dsh 0.2.0-rc.2: in-container update (experimental channel), path-agnostic compose, upstream patches adapted to 0.1.x/0.2.x

This commit is contained in:
hermes committed 2026-10-01 19:21:19 +00:00
commit e6dcef9bf5
302 files changed
+75178

No files matched your search

@@ -0,0 +1,10 @@
[
{
"insert": [
{
"id": "dsh-docker.platform-management.plugin",
"name": "@dsh-docker/platform-management"
}
]
}
]
@@ -0,0 +1,128 @@
import { readFileSync, watch } from 'node:fs'
import { basename, dirname } from 'node:path'
import { installProviderRouting } from './provider-routing.js'
export const name = '@dsh-docker/platform-management'
export const inject = ['subprocess']
const PROXY_KEYS = Object.freeze([
'HTTP_PROXY', 'HTTPS_PROXY', 'http_proxy', 'https_proxy',
'NO_PROXY', 'no_proxy', 'ALL_PROXY', 'all_proxy',
])
const CONNECTION_PROXY_KEYS = Object.freeze([
'HTTP_PROXY', 'HTTPS_PROXY', 'http_proxy', 'https_proxy', 'ALL_PROXY', 'all_proxy',
])
const SHARED_DSH_PROXY_URL = 'http://127.0.0.1:17898'
const ROUTING_STATE_PATH = `${process.env.DSH_PLATFORM_RUN ?? '/run/dsh-platform'}/outbound-proxy-routing.json`
function routingState(path = ROUTING_STATE_PATH) {
try {
const value = JSON.parse(readFileSync(path, 'utf8'))
if (value?.schema !== 1 || typeof value.enabled !== 'boolean' || typeof value.scopes?.agentNetwork !== 'boolean') return null
return value
} catch {
return null
}
}
function agentNetworkEnvironment(environment = process.env) {
const proxy = environment.DSH_PLATFORM_AGENT_PROXY_URL
if (typeof proxy !== 'string' || proxy === '') return null
const noProxy = environment.NO_PROXY ?? environment.no_proxy ?? ''
const allProxy = environment.ALL_PROXY !== undefined || environment.all_proxy !== undefined
return {
HTTP_PROXY: proxy,
HTTPS_PROXY: proxy,
http_proxy: proxy,
https_proxy: proxy,
NO_PROXY: noProxy,
no_proxy: noProxy,
...(allProxy ? { ALL_PROXY: proxy, all_proxy: proxy } : {}),
}
}
function sharedDshProxyEnabled(state) {
return state !== null && state.enabled
&& (state.scopes?.dshCore === true || state.scopes?.dshPlugins === true)
}
function synchronizeSharedDshEnvironment(environment, state) {
if (state === null) return
for (const key of CONNECTION_PROXY_KEYS) delete environment[key]
const noProxy = [...new Set([
...(state.noProxy?.system ?? []),
...(state.noProxy?.user ?? []),
])].join(',')
environment.NO_PROXY = noProxy
environment.no_proxy = noProxy
if (!sharedDshProxyEnabled(state)) return
for (const key of ['HTTP_PROXY', 'HTTPS_PROXY', 'http_proxy', 'https_proxy']) environment[key] = SHARED_DSH_PROXY_URL
if (state.environment?.allProxy === 'scope-proxy') {
environment.ALL_PROXY = SHARED_DSH_PROXY_URL
environment.all_proxy = SHARED_DSH_PROXY_URL
}
}
function watchSharedDshEnvironment(path = ROUTING_STATE_PATH, environment = process.env) {
const original = Object.fromEntries(PROXY_KEYS.map(key => [key, environment[key]]))
const synchronize = () => synchronizeSharedDshEnvironment(environment, routingState(path))
synchronize()
let watcher
try {
watcher = watch(dirname(path), (_event, filename) => {
if (filename === null || filename.toString() === basename(path)) synchronize()
})
watcher.on('error', () => {})
} catch {}
return () => {
watcher?.close()
for (const key of PROXY_KEYS) {
if (original[key] === undefined) delete environment[key]
else environment[key] = original[key]
}
}
}
function managedSubprocessSpec(spec, environment = process.env, routingPath = ROUTING_STATE_PATH) {
if (spec?.env?.DSH_SHELL !== '1') return spec
const env = { ...spec.env }
for (const key of PROXY_KEYS) delete env[key]
const state = routingState(routingPath)
if (state !== null && (!state.enabled || !state.scopes.agentNetwork)) {
for (const key of PROXY_KEYS) env[key] = undefined
return { ...spec, env }
}
const proxy = agentNetworkEnvironment(environment)
if (proxy === null) return spec
return { ...spec, env: { ...env, ...proxy } }
}
export function apply(ctx) {
const removeSharedDshEnvironment = watchSharedDshEnvironment()
const removeProviderRouting = installProviderRouting(ctx)
const service = ctx.subprocess
const spawn = service.spawn
const spawnTerminal = service.spawnTerminal
const managedSpawn = function (spec) {
return spawn.call(this, managedSubprocessSpec(spec))
}
const managedSpawnTerminal = function (spec) {
return spawnTerminal.call(this, managedSubprocessSpec(spec))
}
service.spawn = managedSpawn
service.spawnTerminal = managedSpawnTerminal
ctx.effect(() => () => {
removeSharedDshEnvironment()
removeProviderRouting()
if (service.spawn === managedSpawn) service.spawn = spawn
if (service.spawnTerminal === managedSpawnTerminal) service.spawnTerminal = spawnTerminal
}, 'dsh-docker managed network scopes')
}
export const managedNetworkInternals = Object.freeze({
agentNetworkEnvironment,
managedSubprocessSpec,
routingState,
sharedDshProxyEnabled,
synchronizeSharedDshEnvironment,
})
@@ -0,0 +1,248 @@
import { AsyncLocalStorage } from 'node:async_hooks'
import { readFileSync } from 'node:fs'
import { request } from 'node:http'
import { Agent, fetch as proxyFetch, ProxyAgent } from 'undici'
const PROVIDER_ID_PATTERN = /^[a-z0-9][a-z0-9._-]{0,127}$/
const FETCH_ROUTER = Symbol.for('dsh-docker.platform-management.provider-fetch-router')
const MAX_CONTROL_RESPONSE_BYTES = 64 * 1024
const DEFAULT_SOCKET = '/run/dsh-platform/outbound-proxy.sock'
const DEFAULT_PROXY_URL = 'http://127.0.0.1:17897'
const DEFAULT_SHARED_PROXY_URL = 'http://127.0.0.1:17898'
const DEFAULT_AGENT_PROXY_URL = 'http://127.0.0.1:17895'
const DEFAULT_ROUTING_STATE = '/run/dsh-platform/outbound-proxy-routing.json'
function routingState(path) {
try {
const value = JSON.parse(readFileSync(path, 'utf8'))
if (value?.schema !== 1 || typeof value.enabled !== 'boolean' || value.modelApi === null || typeof value.modelApi !== 'object') return null
return value
} catch {
return null
}
}
function providerProxyEnabled(state, providerId) {
if (state === null || !state.enabled) return false
const policy = state.modelApi.providers?.[providerId] ?? state.modelApi.default
return policy?.proxyEnabled === true
}
function providerRoutingCapability(options) {
return options?.routingCapability === 'shared-dsh' || options?.supportsIndependentRouting === false
? 'shared-dsh'
: 'provider'
}
function sharedDshProxyEnabled(state) {
return state !== null && state.enabled
&& (state.scopes?.dshCore === true || state.scopes?.dshPlugins === true)
}
function agentNetworkProxyEnabled(state) {
return state !== null && state.enabled && state.scopes?.agentNetwork === true
}
function controlRequest(socketPath, method, path, body) {
return new Promise((resolve, reject) => {
const bytes = body === undefined ? undefined : Buffer.from(JSON.stringify(body))
const outgoing = request({
socketPath,
method,
path,
headers: bytes === undefined ? {} : {
'content-type': 'application/json',
'content-length': bytes.byteLength,
},
signal: AbortSignal.timeout(5_000),
}, response => {
const chunks = []
let size = 0
response.on('data', chunk => {
size += chunk.byteLength
if (size > MAX_CONTROL_RESPONSE_BYTES) outgoing.destroy(new Error('Outbound Proxy response is too large'))
else chunks.push(chunk)
})
response.on('end', () => {
let value
try { value = JSON.parse(Buffer.concat(chunks).toString('utf8')) } catch {
reject(new Error('Outbound Proxy response is invalid'))
return
}
if ((response.statusCode ?? 500) >= 400) {
const error = new Error(value?.error?.message ?? `Outbound Proxy returned HTTP ${String(response.statusCode)}`)
error.statusCode = response.statusCode
error.code = value?.error?.code
reject(error)
} else resolve(value)
})
})
outgoing.once('error', reject)
outgoing.end(bytes)
})
}
async function issueProviderHandle(providerId, socketPath) {
for (let attempt = 0; attempt < 2; attempt += 1) {
const configuration = await controlRequest(socketPath, 'GET', '/v1/configuration')
try {
return await controlRequest(socketPath, 'POST', '/v1/provider-handles', {
providerId,
policyRevision: configuration.revision,
})
} catch (error) {
if (error?.code !== 'REVISION_CONFLICT' || attempt > 0) throw error
}
}
throw new Error('Provider policy changed repeatedly')
}
function installFetchRouter(store, routedFetch = proxyFetch, resolveDefaultRoute = () => undefined) {
const owner = Object.freeze({})
let registry = globalThis[FETCH_ROUTER]
if (registry === undefined) {
const original = globalThis.fetch
registry = { original, owner, store, routedFetch, resolveDefaultRoute }
const wrapper = function (input, init) {
const route = registry.store.getStore() ?? registry.resolveDefaultRoute()
if (route === undefined || (init !== undefined && Object.hasOwn(init, 'dispatcher'))) {
return registry.original.call(this, input, init)
}
return registry.routedFetch(input, { ...init, dispatcher: route.dispatcher })
}
registry.wrapper = wrapper
globalThis[FETCH_ROUTER] = registry
globalThis.fetch = wrapper
} else {
registry.owner = owner
registry.store = store
registry.routedFetch = routedFetch
registry.resolveDefaultRoute = resolveDefaultRoute
}
return () => {
if (registry.owner !== owner) return
if (globalThis.fetch === registry.wrapper) globalThis.fetch = registry.original
delete globalThis[FETCH_ROUTER]
}
}
function routedIterator(next, providerId, store, { resolveRoute }) {
let iterator
let routePromise
let closed = false
async function route() {
if (routePromise === undefined) {
routePromise = resolveRoute(providerId)
}
return routePromise
}
async function invoke(method, value) {
const context = await route()
if (iterator === undefined) iterator = store.run(context, () => next()[Symbol.asyncIterator]())
if (typeof iterator[method] !== 'function') {
await close(context)
if (method === 'throw') throw value
return { value, done: true }
}
try {
const result = await store.run(context, () => iterator[method](value))
if (result.done) await close(context)
return result
} catch (error) {
await close(context)
throw error
}
}
async function close(context) {
if (closed) return
closed = true
if (context.owned) await context.dispatcher.close()
}
return {
next: value => invoke('next', value),
return: value => invoke('return', value),
throw: value => invoke('throw', value),
[Symbol.asyncIterator]() { return this },
}
}
export function installProviderRouting(ctx, {
socketPath = process.env.DSH_PLATFORM_RUN === undefined
? DEFAULT_SOCKET
: `${process.env.DSH_PLATFORM_RUN}/outbound-proxy.sock`,
proxyUrl = DEFAULT_PROXY_URL,
routingStatePath = process.env.DSH_PLATFORM_RUN === undefined
? DEFAULT_ROUTING_STATE
: `${process.env.DSH_PLATFORM_RUN}/outbound-proxy-routing.json`,
createDispatcher = options => new ProxyAgent(options),
createSharedDispatcher = options => new ProxyAgent(options),
createAgentDispatcher = options => new ProxyAgent(options),
createDirectDispatcher = () => new Agent(),
routedFetch = proxyFetch,
} = {}) {
const store = new AsyncLocalStorage()
const directDispatcher = createDirectDispatcher()
const sharedDispatcher = createSharedDispatcher({ uri: DEFAULT_SHARED_PROXY_URL })
const agentDispatcher = createAgentDispatcher({ uri: DEFAULT_AGENT_PROXY_URL })
const resolveDefaultRoute = () => {
const state = routingState(routingStatePath)
if (state === null) return undefined
return { dispatcher: sharedDshProxyEnabled(state) ? sharedDispatcher : directDispatcher }
}
const removeFetchRouter = installFetchRouter(store, routedFetch, resolveDefaultRoute)
const disposeTool = ctx.on('tools/execute', (_execution, next) => {
const state = routingState(routingStatePath)
return store.run({
dispatcher: agentNetworkProxyEnabled(state) ? agentDispatcher : directDispatcher,
owned: false,
}, next)
}, { global: true, prepend: true })
const disposeStream = ctx.on('llm/stream', (options, next) => {
if (!PROVIDER_ID_PATTERN.test(options.provider)) return next()
const capability = providerRoutingCapability(options)
return routedIterator(next, options.provider, store, {
resolveRoute: async providerId => {
const state = routingState(routingStatePath)
if (state !== null && !providerProxyEnabled(state, providerId)) {
return { dispatcher: directDispatcher, owned: false }
}
if (capability === 'shared-dsh') {
return {
dispatcher: sharedDshProxyEnabled(state) ? sharedDispatcher : directDispatcher,
owned: false,
}
}
const { handle } = await issueProviderHandle(providerId, socketPath)
return {
dispatcher: createDispatcher({ uri: proxyUrl, token: `DSH-Provider ${handle}` }),
owned: true,
}
},
})
}, { global: true, prepend: true })
return () => {
disposeTool?.()
disposeStream?.()
removeFetchRouter()
void directDispatcher.close()
void sharedDispatcher.close()
void agentDispatcher.close()
}
}
export const providerRoutingInternals = Object.freeze({
FETCH_ROUTER,
controlRequest,
installFetchRouter,
issueProviderHandle,
agentNetworkProxyEnabled,
providerProxyEnabled,
providerRoutingCapability,
sharedDshProxyEnabled,
routingState,
routedIterator,
})
@@ -0,0 +1,32 @@
{
"name": "@dsh-docker/platform-management",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@dsh-docker/platform-management",
"version": "1.0.0",
"dependencies": {
"undici": "8.10.0"
},
"peerDependencies": {
"@deepseek-ai/cordis": "*",
"@deepseek-ai/dsh-client-connection": "*",
"@deepseek-ai/dsh-client-locale": "*",
"@deepseek-ai/dsh-client-ui-primitives": "*",
"@deepseek-ai/dsh-client-ui-settings": "*",
"react": "^18.2.0"
}
},
"node_modules/undici": {
"version": "8.10.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-8.10.0.tgz",
"integrity": "sha512-HvltHd7avK13QIw/oLe4qoOLyoVSoafqJ2jYOrtMRBkbYT31eiBQ8O0ehRKZiEZCMEyLFQNIADpgCWC5fALvYQ==",
"license": "MIT",
"engines": {
"node": ">=22.19.0"
}
}
}
}
@@ -0,0 +1,39 @@
{
"name": "@dsh-docker/platform-management",
"version": "1.0.0",
"type": "module",
"main": "lib/index.js",
"exports": {
".": "./lib/index.js",
"./client": "./lib/client.bundle.js",
"./package.json": "./package.json"
},
"dependencies": {
"undici": "8.10.0"
},
"dshDocker": {
"description": {
"zh": "管理 DSH Docker 更新、运行维护、系统资源与登录会话。",
"en": "Manage DSH Docker updates, runtime maintenance, system resources, and login sessions."
}
},
"dsh": {
"client": {
"inject": [
"@deepseek-ai/dsh-client-connection",
"@deepseek-ai/dsh-client-locale",
"@deepseek-ai/dsh-client-ui-primitives",
"@deepseek-ai/dsh-client-ui-settings"
],
"platform": "web"
}
},
"peerDependencies": {
"@deepseek-ai/cordis": "*",
"@deepseek-ai/dsh-client-connection": "*",
"@deepseek-ai/dsh-client-locale": "*",
"@deepseek-ai/dsh-client-ui-primitives": "*",
"@deepseek-ai/dsh-client-ui-settings": "*",
"react": "^18.2.0"
}
}