import assert from 'node:assert/strict' import { EventEmitter } from 'node:events' import { request as httpRequest } from 'node:http' import { createConnection } from 'node:net' import { appendFile, lstat, mkdir, mkdtemp, readFile, rm, symlink, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import { join } from 'node:path' import { PassThrough } from 'node:stream' import test from 'node:test' import { JsonlLogManager } from '../../control-plane/modules/log-manager/index.mjs' import { parseCli, readSecret, recoverImageBaseline, resetTrust, runCli } from '../../control-plane/services/management/cli.mjs' import { API_PREFIX, createManagementServer, listenManagement } from '../../control-plane/services/management/server.mjs' import { LocalApiClient } from '../../control-plane/modules/updater/lib/client.mjs' import { UpdateConflictError } from '../../control-plane/modules/updater/lib/coordinator.mjs' import { UpdateScheduler } from '../../control-plane/modules/updater/lib/scheduler.mjs' import { SettingsDocumentStore } from '../../control-plane/services/management/settings-document.mjs' import { UserPluginInventory } from '../../control-plane/modules/plugin-manager/user-inventory.mjs' import { UserPluginJournal } from '../../control-plane/modules/plugin-manager/user-journal.mjs' import { UserPluginSnapshots } from '../../control-plane/modules/plugin-manager/user-snapshots.mjs' import { UserPluginSelectionStore } from '../../control-plane/modules/plugin-manager/user-state.mjs' import { UserPluginTransactionManager } from '../../control-plane/modules/plugin-manager/user-transaction.mjs' import { FileInventory, FileSearchManager } from '../../control-plane/modules/file-manager/index.mjs' import { FileTransferManager } from '../../control-plane/modules/file-manager/transfers.mjs' import { FileTaskManager } from '../../control-plane/modules/file-manager/tasks.mjs' import { AtomicFileEditor } from '../../control-plane/modules/file-manager/editor.mjs' const ASYNC_POLL_ATTEMPTS = 2_000 const ASYNC_POLL_INTERVAL_MS = 5 class Coordinator extends EventEmitter { constructor() { super() this.value = { status: 'idle', progress: 0 } this.state = { read: async () => this.value } } async publicStatus() { return { update: this.value, updateChannel: 'experimental', holds: [], experimentalBlocked: null, rollbackPlan: { planId: 'plan-a' } } } hasActiveTask() { return this.running === true } async check(source) { this.checkSource = source return { value: { targetSequence: 2, desired: { dsh: { version: 'rc.8' } } } } } start() { if (this.running) throw new UpdateConflictError('busy') this.running = true return { taskId: 'task-one', completion: new Promise(() => {}) } } startReconcile() { return this.start() } rollbackPlan() { return Promise.resolve({ planId: 'plan-a' }) } setChannel(channel) { return Promise.resolve({ updateChannel: channel, holds: [], experimentalBlocked: null }) } retryHold(id) { return Promise.resolve({ retried: id }) } startCompleteRollback(planId) { assert.equal(planId, 'plan-a') return { taskId: 'rollback-task', completion: Promise.resolve() } } } function rawRequest(socketPath, path, method = 'GET') { return new Promise((resolve, reject) => { const request = httpRequest({ socketPath, path, method }, response => { const chunks = [] response.on('data', chunk => chunks.push(chunk)) response.on('end', () => resolve({ status: response.statusCode, headers: response.headers, body: Buffer.concat(chunks).toString('utf8'), })) }) request.once('error', reject) request.end() }) } function rawUpgrade(socketPath, path) { return new Promise((resolve, reject) => { const socket = createConnection(socketPath) const chunks = [] socket.once('connect', () => socket.write([ `GET ${path} HTTP/1.1`, 'Host: management.internal', 'Connection: Upgrade', 'Upgrade: websocket', 'Sec-WebSocket-Key: dGVzdC1rZXk=', 'Sec-WebSocket-Version: 13', '', '', ].join('\r\n'))) socket.on('data', chunk => chunks.push(chunk)) socket.once('error', reject) socket.once('end', () => resolve(Buffer.concat(chunks).toString('utf8'))) }) } test('management preserves capability backend failures for HTTP and WebSocket requests', async () => { const root = await mkdtemp(join(tmpdir(), 'dsh-management-capability-')) const logs = new JsonlLogManager({ root: join(root, 'logs') }) const authorizeInternal = async () => { throw Object.assign(new Error('Access Manager unavailable'), { statusCode: 503 }) } const server = createManagementServer({ coordinator: new Coordinator(), logs, authorizeInternal, terminalSessions: { upgrade() { assert.fail('unauthorized terminal upgrade reached execution point') } }, }) const socketPath = join(root, 'management.sock') await listenManagement(server, socketPath) try { const response = await rawRequest(socketPath, `${API_PREFIX}status`) assert.equal(response.status, 503) assert.equal(JSON.parse(response.body).error, 'Access Manager unavailable') assert.match(await rawUpgrade(socketPath, `${API_PREFIX}terminal/sessions/00000000-0000-4000-8000-000000000000/stream`), /^HTTP\/1\.1 503 Service Unavailable/m) } finally { await new Promise(resolve => server.close(resolve)) await rm(root, { recursive: true, force: true }) } }) test('management socket exposes status, check, update, logs, and local rollback', async () => { const root = await mkdtemp(join(tmpdir(), 'dsh-management-')) const coordinator = new Coordinator() const originTransitions = [] const totpEnrollments = [] const logs = new JsonlLogManager({ root: join(root, 'logs') }) await logs.append('gateway', 'stdout', 'ready') const server = createManagementServer({ coordinator, logs, platformStatus: async () => ({ environment: 'one' }), updateAutomaticCheck: async value => value, getAuthenticationSettings: async () => ({ account: { username: 'admin' } }), updateAuthenticationSettings: async value => ({ account: { username: value.username } }), beginTotpEnrollment: async value => { totpEnrollments.push({ action: 'begin', value }) return { enrollmentToken: 'dshte_test', qrCode: 'data:image/svg+xml,test', secret: 'SECRET' } }, confirmTotpEnrollment: async value => { totpEnrollments.push({ action: 'confirm', value }) return { confirmed: true } }, cancelTotpEnrollment: async value => { totpEnrollments.push({ action: 'cancel', value }) return { canceled: true } }, beginTotpDisableConfirmation: async value => { totpEnrollments.push({ action: 'disable-begin', value }) return { confirmationToken: 'dshtd_test' } }, confirmTotpDisable: async value => { totpEnrollments.push({ action: 'disable-confirm', value }) return { confirmed: true } }, cancelTotpDisableConfirmation: async value => { totpEnrollments.push({ action: 'disable-cancel', value }) return { canceled: true } }, revokeAuthenticationSessions: async value => ({ revoked: value.scope === 'all' ? 2 : 1 }), createManagementOriginTransition: async value => { originTransitions.push({ stage: 'create', value }) return { transition: { transitionId: 'transition-one', nonce: 'nonce-one' } } }, commitManagementOriginTransition: async value => { originTransitions.push({ stage: 'commit', value }) return { account: { managementAccess: { mode: 'isolated' } } } }, reportManagementOriginTransitionFailure: async value => { originTransitions.push({ stage: 'failure', value }) return { reported: true } }, }) const socketPath = join(root, 'run', 'management.sock') await listenManagement(server, socketPath) const client = new LocalApiClient(socketPath) try { const status = await client.request('GET', '/_dsh_platform/api/v1/status') assert.equal(status.environment, 'one') assert.equal((await client.request('POST', '/_dsh_platform/api/v1/check', { source: 'page-open' })).targetSequence, 2) assert.equal(coordinator.checkSource, 'page-open') assert.deepEqual(await client.request('POST', '/_dsh_platform/api/v1/update'), { taskId: 'task-one' }) await assert.rejects(client.request('POST', '/_dsh_platform/api/v1/update'), error => error.statusCode === 409) const rejected = await logs.query({ sources: ['platform-management'] }) assert.equal(rejected.some(entry => entry.message === 'management.request.failed' && entry.pathname === '/_dsh_platform/api/v1/update' && entry.level === 'warning'), true) assert.equal((await client.request('GET', '/_dsh_platform/api/v1/logs?source=gateway')).entries[0].message, 'ready') assert.equal((await client.request('PUT', '/_dsh_platform/api/v1/channel', { channel: 'experimental' })).updateChannel, 'experimental') assert.deepEqual(await client.request('PUT', '/_dsh_platform/api/v1/automatic-check', { enabled: true, intervalSeconds: 21_600, notificationsEnabled: false, }), { enabled: true, intervalSeconds: 21_600, notificationsEnabled: false }) assert.equal((await client.request('GET', '/_dsh_platform/api/v1/auth-settings')).account.username, 'admin') assert.equal((await client.request('PUT', '/_dsh_platform/api/v1/auth-settings', { username: 'operator' })).account.username, 'operator') assert.equal((await client.request('POST', '/_dsh_platform/api/v1/auth-totp/enrollments', { currentPassword: 'correct password', })).enrollmentToken, 'dshte_test') assert.equal((await client.request('POST', '/_dsh_platform/api/v1/auth-totp/enrollments/confirm', { enrollmentToken: 'dshte_test', totpCode: '123456', })).confirmed, true) assert.equal((await client.request('POST', '/_dsh_platform/api/v1/auth-totp/enrollments/cancel', { enrollmentToken: 'dshte_test', })).canceled, true) assert.equal((await client.request('POST', '/_dsh_platform/api/v1/auth-totp/disable-confirmations')).confirmationToken, 'dshtd_test') assert.equal((await client.request('POST', '/_dsh_platform/api/v1/auth-totp/disable-confirmations/confirm', { confirmationToken: 'dshtd_test', totpCode: '123456', })).confirmed, true) assert.equal((await client.request('POST', '/_dsh_platform/api/v1/auth-totp/disable-confirmations/cancel', { confirmationToken: 'dshtd_test', })).canceled, true) assert.deepEqual(totpEnrollments, [ { action: 'begin', value: { currentPassword: 'correct password' } }, { action: 'confirm', value: { enrollmentToken: 'dshte_test', totpCode: '123456' } }, { action: 'cancel', value: { enrollmentToken: 'dshte_test' } }, { action: 'disable-begin', value: {} }, { action: 'disable-confirm', value: { confirmationToken: 'dshtd_test', totpCode: '123456' } }, { action: 'disable-cancel', value: { confirmationToken: 'dshtd_test' } }, ]) assert.equal((await client.request('POST', '/_dsh_platform/api/v1/auth-sessions/revoke', { sessionId: 'session-two', })).revoked, 1) assert.equal((await client.request('POST', '/_dsh_platform/api/v1/management-origin/transitions', { mode: 'isolated', isolatedEntry: { kind: 'public', managementPublicOrigin: 'https://manage.example' }, })).transition.transitionId, 'transition-one') assert.equal((await client.request('POST', '/_dsh_platform/api/v1/management-origin/transitions/commit', { transitionId: 'transition-one', proof: 'proof-one', })).account.managementAccess.mode, 'isolated') assert.equal((await client.request('POST', '/_dsh_platform/api/v1/management-origin/transitions/failure', { stage: 'browser-probe', mode: 'isolated', })).reported, true) assert.deepEqual(originTransitions.map(value => value.stage), ['create', 'commit', 'failure']) assert.equal((await client.request('POST', '/_dsh_platform/api/v1/holds/retry', { id: 'hold-a' })).retried, 'hold-a') assert.equal((await client.request('GET', '/_dsh_platform/api/v1/rollback-plan')).plan.planId, 'plan-a') coordinator.running = false assert.deepEqual(await client.request('POST', '/_dsh_platform/api/v1/rollback', { planId: 'plan-a' }), { taskId: 'rollback-task' }) const audit = await logs.query({ sources: ['audit'] }) assert.equal(audit.some(entry => entry.message === 'update.channel.changed' && entry.updateChannel === 'experimental'), true) assert.equal(audit.some(entry => entry.message === 'update.automatic-check.configured' && entry.intervalSeconds === 21_600), true) assert.equal(audit.some(entry => entry.message === 'update.hold.retried' && entry.holdId === 'hold-a'), true) } finally { await new Promise(resolve => server.close(resolve)) } }) test('management proxies sanitized outbound configuration, Provider inventory, and structured conflicts', async () => { const root = await mkdtemp(join(tmpdir(), 'dsh-management-outbound-proxy-')) const logs = new JsonlLogManager({ root: join(root, 'logs') }) const view = { schema: 1, enabled: false, revision: 'revision-one', componentReady: true, proxy: { protocol: 'http', host: '', port: null, username: '', passwordConfigured: false, remoteDns: true }, scopes: {}, environment: { allProxy: null }, modelApi: { default: { proxyEnabled: false }, providers: {} }, noProxy: { system: ['localhost'], user: [] }, bypass: { additional: [] }, routeHealth: {}, lastTest: null, scopeCatalog: { schema: 1, entries: [] }, } const providers = { schema: 1, source: 'live', providers: [{ id: 'deepseek', routingCapability: 'shared-dsh' }] } const updates = [] const server = createManagementServer({ coordinator: new Coordinator(), logs, getProxyConfiguration: async () => view, listProxyProviders: async () => providers, updateProxyConfiguration: async value => { updates.push(value) if (value.baseRevision !== view.revision) { const error = new Error('proxy configuration changed') error.statusCode = 409 error.code = 'REVISION_CONFLICT' error.stage = 'activate' error.retryable = true error.proxyError = { code: error.code, message: error.message, stage: error.stage, retryable: error.retryable, } throw error } return { ...view, revision: 'revision-two' } }, }) const socketPath = join(root, 'run', 'management.sock') await listenManagement(server, socketPath) const client = new LocalApiClient(socketPath) try { assert.deepEqual(await client.request('GET', `${API_PREFIX}proxy`), view) assert.deepEqual(await client.request('GET', `${API_PREFIX}proxy/provider-inventory`), providers) const configured = await client.request('PUT', `${API_PREFIX}proxy`, { baseRevision: view.revision, value: { password: 'must-not-log' }, }) assert.equal(configured.revision, 'revision-two') assert.deepEqual(updates, [{ baseRevision: view.revision, value: { password: 'must-not-log' } }]) const bytes = Buffer.from(JSON.stringify({ baseRevision: 'stale', value: {} })) const conflict = await new Promise((resolve, reject) => { const outgoing = httpRequest({ socketPath, method: 'PUT', path: `${API_PREFIX}proxy`, headers: { 'content-type': 'application/json', 'content-length': bytes.byteLength }, }, response => { const chunks = [] response.on('data', chunk => chunks.push(chunk)) response.on('end', () => resolve({ status: response.statusCode, body: JSON.parse(Buffer.concat(chunks).toString()) })) }) outgoing.once('error', reject) outgoing.end(bytes) }) assert.equal(conflict.status, 409) assert.deepEqual(conflict.body, { error: { code: 'REVISION_CONFLICT', message: 'proxy configuration changed', stage: 'activate', retryable: true, } }) const audit = await logs.query({ sources: ['audit'] }) assert.equal(audit.some(entry => entry.message === 'proxy.configuration.update.completed' && entry.revision === 'revision-two'), true) assert.equal(audit.some(entry => entry.message === 'proxy.configuration.update.failed' && entry.code === 'REVISION_CONFLICT'), true) assert.equal(JSON.stringify(audit).includes('must-not-log'), false) } finally { await new Promise(resolve => server.close(resolve)) } }) test('management exposes one reconnectable and cancellable outbound proxy test task', async () => { const root = await mkdtemp(join(tmpdir(), 'dsh-management-proxy-test-')) const logs = new JsonlLogManager({ root: join(root, 'logs') }) const taskId = '123e4567-e89b-42d3-a456-426614174000' let status = 'running' let cancelled = false const state = () => ({ schema: 1, taskId, status, baseRevision: 'revision-one', candidateHash: `sha256:${'a'.repeat(64)}`, mode: 'proxy', currentStage: status === 'running' ? 'proxy-connect' : null, stages: [{ stage: 'proxy-connect', status: status === 'running' ? 'running' : 'success', durationMs: 5, errorCode: null, detail: null }], error: null, createdAt: '2026-08-25T00:00:00.000Z', updatedAt: new Date().toISOString(), }) const server = createManagementServer({ coordinator: new Coordinator(), logs, startProxyTest: async () => state(), getProxyTest: async requested => { assert.equal(requested, taskId) return state() }, cancelProxyTest: async requested => { assert.equal(requested, taskId) cancelled = true status = 'cancelled' return state() }, }) const socketPath = join(root, 'run', 'management.sock') await listenManagement(server, socketPath) const client = new LocalApiClient(socketPath) try { assert.deepEqual(await client.request('POST', `${API_PREFIX}proxy/test`, { baseRevision: 'revision-one', value: {}, }), { taskId }) assert.equal((await client.request('GET', `${API_PREFIX}status`)).proxyTestOperation.taskId, taskId) assert.equal((await client.request('GET', `${API_PREFIX}proxy/test/tasks/${taskId}`)).status, 'running') assert.equal((await client.request('DELETE', `${API_PREFIX}proxy/test/tasks/${taskId}`)).status, 'cancelled') assert.equal(cancelled, true) let audit for (let attempt = 0; attempt < ASYNC_POLL_ATTEMPTS; attempt += 1) { audit = await logs.query({ sources: ['audit'] }) if (audit.some(entry => entry.message === 'proxy.test.cancelled' && entry.taskId === taskId)) break await new Promise(resolve => setTimeout(resolve, ASYNC_POLL_INTERVAL_MS)) } assert.equal(audit.some(entry => entry.message === 'proxy.test.started' && entry.taskId === taskId), true) assert.equal(audit.some(entry => entry.message === 'proxy.test.stage.changed' && entry.taskId === taskId && entry.stage === 'proxy-connect' && entry.status === 'running'), true) assert.equal(audit.some(entry => entry.message === 'proxy.test.stage.changed' && entry.taskId === taskId && entry.stage === 'proxy-connect' && entry.status === 'success'), true) assert.equal(audit.some(entry => entry.message === 'proxy.test.cancel-requested' && entry.taskId === taskId), true) assert.equal(audit.some(entry => entry.message === 'proxy.test.cancelled' && entry.taskId === taskId), true) } finally { await new Promise(resolve => server.close(resolve)) } }) test('management exposes audited System Skill tasks through the shared runtime mutex', async () => { const root = await mkdtemp(join(tmpdir(), 'dsh-management-system-skills-')) const logs = new JsonlLogManager({ root: join(root, 'logs') }) let complete const pending = new Promise(resolve => { complete = resolve }) const calls = [] const skills = [{ id: 'dsh-docker-operations', sha256: 'a'.repeat(64), description: { zh: '容器操作手册', en: 'Container operations guide' }, installed: true, enabled: true, }] const server = createManagementServer({ coordinator: new Coordinator(), logs, listSystemSkills: async () => skills, configureSystemSkill: async (skillId, action) => { calls.push({ skillId, action }); await pending }, }) const socketPath = join(root, 'run', 'management.sock') await listenManagement(server, socketPath) const client = new LocalApiClient(socketPath) try { assert.deepEqual((await client.request('GET', `${API_PREFIX}system-skills`)).skills, skills) const task = await client.request('POST', `${API_PREFIX}system-skills/action`, { skillId: 'dsh-docker-operations', action: 'disable', }) assert.match(task.taskId, /^[0-9a-f-]{36}$/) await assert.rejects(client.request('POST', `${API_PREFIX}restart-dsh`), error => error.statusCode === 409) await assert.rejects(client.request('POST', `${API_PREFIX}system-skills/action`, { skillId: 'dsh-docker-operations', action: 'disable', unexpected: true, }), error => error.statusCode === 400) complete() let status for (let attempt = 0; attempt < ASYNC_POLL_ATTEMPTS; attempt += 1) { status = await client.request('GET', `${API_PREFIX}status`) if (status.systemSkillOperation.status === 'success') break await new Promise(resolve => setTimeout(resolve, ASYNC_POLL_INTERVAL_MS)) } assert.equal(status.systemSkillOperation.status, 'success') assert.deepEqual(calls, [{ skillId: 'dsh-docker-operations', action: 'disable' }]) const audit = await logs.query({ sources: ['audit'] }) assert.equal(audit.some(entry => entry.message === 'system-skill.disable.started'), true) assert.equal(audit.some(entry => entry.message === 'system-skill.disable.completed'), true) } finally { complete() await new Promise(resolve => server.close(resolve)) } }) test('management exposes validated User Skill tasks only on the standalone API', async () => { const root = await mkdtemp(join(tmpdir(), 'dsh-management-user-skills-')) const logs = new JsonlLogManager({ root: join(root, 'logs') }) let complete const pending = new Promise(resolve => { complete = resolve }) const revision = `sha256:${'a'.repeat(64)}` const entryId = `sha256:${'b'.repeat(64)}` const inventory = { schema: 1, revision, skills: [{ entryId, entryName: 'local-guide', source: 'user-dsh', kind: 'directory', name: 'local-guide', description: 'Local guide', enabled: true, damaged: false, metadataError: null, symbolicLink: false }], } const calls = [] const server = createManagementServer({ coordinator: new Coordinator(), logs, listUserSkills: async () => inventory, validateUserSkillAction: async value => { if (value.revision !== revision) { const error = new Error('User Skill state changed') error.statusCode = 409 throw error } }, configureUserSkill: async value => { calls.push(value) if (value.action === 'delete') throw new Error('permission denied') await pending }, }) const socketPath = join(root, 'run', 'management.sock') await listenManagement(server, socketPath) const client = new LocalApiClient(socketPath) try { assert.deepEqual(await client.request('GET', `${API_PREFIX}user-skills`), inventory) await assert.rejects(client.request('POST', `${API_PREFIX}user-skills/action`, { entryId, revision: `sha256:${'c'.repeat(64)}`, action: 'disable', }), error => error.statusCode === 409) await assert.rejects(client.request('POST', `${API_PREFIX}user-skills/action`, { entryId, revision, action: 'disable', unexpected: true, }), error => error.statusCode === 400) const task = await client.request('POST', `${API_PREFIX}user-skills/action`, { entryId, revision, action: 'disable' }) assert.match(task.taskId, /^[0-9a-f-]{36}$/) await assert.rejects(client.request('POST', `${API_PREFIX}restart-dsh`), error => error.statusCode === 409) await assert.rejects(client.request('POST', `${API_PREFIX}user-skills/action`, { entryId, revision, action: 'disable', }), error => error.statusCode === 409) complete() let status for (let attempt = 0; attempt < ASYNC_POLL_ATTEMPTS; attempt += 1) { status = await client.request('GET', `${API_PREFIX}status`) if (status.userSkillOperation.status === 'success') break await new Promise(resolve => setTimeout(resolve, ASYNC_POLL_INTERVAL_MS)) } assert.equal(status.userSkillOperation.status, 'success') const failedTask = await client.request('POST', `${API_PREFIX}user-skills/action`, { entryId, revision, action: 'delete' }) for (let attempt = 0; attempt < ASYNC_POLL_ATTEMPTS; attempt += 1) { status = await client.request('GET', `${API_PREFIX}status`) if (status.userSkillOperation.taskId === failedTask.taskId && status.userSkillOperation.status === 'failed') break await new Promise(resolve => setTimeout(resolve, ASYNC_POLL_INTERVAL_MS)) } assert.equal(status.userSkillOperation.status, 'failed') assert.equal(status.userSkillOperation.error, 'permission denied') assert.deepEqual(calls, [{ entryId, revision, action: 'disable' }, { entryId, revision, action: 'delete' }]) const audit = await logs.query({ sources: ['audit'] }) assert.equal(audit.some(entry => entry.message === 'user-skill.disable.started'), true) assert.equal(audit.some(entry => entry.message === 'user-skill.disable.completed'), true) assert.equal(audit.some(entry => entry.message === 'user-skill.delete.started'), true) assert.equal(audit.some(entry => entry.message === 'user-skill.delete.failed' && entry.error === 'permission denied'), true) } finally { complete() await new Promise(resolve => server.close(resolve)) } }) test('management live logs preserve the requested source filter', async () => { const root = await mkdtemp(join(tmpdir(), 'dsh-management-log-stream-')) const logs = new JsonlLogManager({ root: join(root, 'logs') }) await logs.append('gateway', 'stdout', 'initial') const server = createManagementServer({ coordinator: new Coordinator(), logs }) const socketPath = join(root, 'run', 'management.sock') await listenManagement(server, socketPath) let request let response try { ({ request, response } = await new Promise((resolve, reject) => { const nextRequest = httpRequest({ socketPath, path: `${API_PREFIX}logs/stream?source=gateway&limit=10` }, nextResponse => { resolve({ request: nextRequest, response: nextResponse }) }) nextRequest.once('error', reject) nextRequest.end() })) let body = '' const live = new Promise(resolve => { response.on('data', chunk => { body += chunk.toString('utf8') if (body.includes('must-stream')) resolve() }) }) await logs.append('updater', 'stdout', 'must-not-stream') await new Promise(resolve => setTimeout(resolve, 300)) await appendFile(logs.currentPath('gateway'), `${JSON.stringify({ timestamp: new Date().toISOString(), source: 'gateway', stream: 'stdout', level: 'info', message: 'must-stream', })}\n`) await Promise.race([ live, new Promise((_, reject) => setTimeout(() => reject(new Error('timed out waiting for live log')), 1_000)), ]) assert.match(body, /must-stream/) assert.doesNotMatch(body, /must-not-stream/) assert.equal(response.headers['x-accel-buffering'], 'no') assert.equal(response.headers['cache-control'], 'no-cache, no-transform') } finally { request?.destroy() response?.destroy() await new Promise(resolve => server.close(resolve)) } }) test('management state events keep idle proxy connections alive', async () => { const root = await mkdtemp(join(tmpdir(), 'dsh-management-state-stream-')) const server = createManagementServer({ coordinator: new Coordinator(), logs: new JsonlLogManager({ root: join(root, 'logs') }), stateHeartbeatMs: 10, }) const socketPath = join(root, 'run', 'management.sock') await listenManagement(server, socketPath) let request let response try { ({ request, response } = await new Promise((resolve, reject) => { const nextRequest = httpRequest({ socketPath, path: `${API_PREFIX}events` }, nextResponse => { resolve({ request: nextRequest, response: nextResponse }) }) nextRequest.once('error', reject) nextRequest.end() })) let body = '' await Promise.race([ new Promise(resolve => { response.on('data', chunk => { body += chunk.toString('utf8') if (body.includes('event: heartbeat')) resolve() }) }), new Promise((_, reject) => setTimeout(() => reject(new Error('timed out waiting for state heartbeat')), 1_000)), ]) assert.match(body, /event: state/) assert.match(body, /event: heartbeat/) assert.equal(response.headers['x-accel-buffering'], 'no') assert.equal(response.headers['cache-control'], 'no-cache, no-transform') } finally { request?.destroy() response?.destroy() await new Promise(resolve => server.close(resolve)) } }) test('management state fan-out supports many tabs and releases every listener', async () => { const root = await mkdtemp(join(tmpdir(), 'dsh-management-state-fanout-')) const coordinator = new Coordinator() const server = createManagementServer({ coordinator, logs: new JsonlLogManager({ root: join(root, 'logs') }), }) const socketPath = join(root, 'run', 'management.sock') await listenManagement(server, socketPath) const connections = [] try { for (let index = 0; index < 12; index += 1) { connections.push(await new Promise((resolve, reject) => { const request = httpRequest({ socketPath, path: `${API_PREFIX}events` }, response => { response.once('data', () => resolve({ request, response })) }) request.once('error', reject) request.end() })) } assert.equal(coordinator.getMaxListeners(), 0) assert.equal(server.getMaxListeners(), 0) assert.equal(coordinator.listenerCount('state'), 12) assert.equal(server.listenerCount('management-state'), 12) for (const connection of connections) { connection.request.destroy() connection.response.destroy() } for (let attempt = 0; attempt < 100 && coordinator.listenerCount('state') !== 0; attempt += 1) { await new Promise(resolve => setTimeout(resolve, 5)) } assert.equal(coordinator.listenerCount('state'), 0) assert.equal(server.listenerCount('management-state'), 0) } finally { for (const connection of connections) { connection.request.destroy() connection.response.destroy() } await new Promise(resolve => server.close(resolve)) } }) test('management reports unpublished development metadata without an HTTP error', async () => { const root = await mkdtemp(join(tmpdir(), 'dsh-management-unpublished-')) const coordinator = new Coordinator() coordinator.check = async () => ({ unavailable: true, upstream: { version: 'rc.10' } }) const server = createManagementServer({ coordinator, logs: new JsonlLogManager({ root: join(root, 'logs') }), }) const socketPath = join(root, 'run', 'management.sock') await listenManagement(server, socketPath) try { assert.deepEqual(await new LocalApiClient(socketPath).request('POST', '/_dsh_platform/api/v1/check'), { available: false, upstream: { version: 'rc.10' }, }) } finally { await new Promise(resolve => server.close(resolve)) } }) test('management treats a page-open check during an update as an accepted no-op', async () => { const root = await mkdtemp(join(tmpdir(), 'dsh-management-busy-page-open-')) const coordinator = new Coordinator() coordinator.check = async () => ({ busy: true }) const server = createManagementServer({ coordinator, logs: new JsonlLogManager({ root: join(root, 'logs') }), }) const socketPath = join(root, 'run', 'management.sock') await listenManagement(server, socketPath) try { const response = await rawRequest(socketPath, `${API_PREFIX}check`, 'POST') assert.equal(response.status, 202) assert.deepEqual(JSON.parse(response.body), { busy: true }) } finally { await new Promise(resolve => server.close(resolve)) } }) test('management edits only the fixed settings document with optimistic revisions', async () => { const root = await mkdtemp(join(tmpdir(), 'dsh-management-settings-document-')) const dshHome = join(root, 'dsh') const settingsDocument = new SettingsDocumentStore(dshHome) const logs = new JsonlLogManager({ root: join(root, 'logs') }) const server = createManagementServer({ coordinator: new Coordinator(), logs, settingsDocument }) const socketPath = join(root, 'management.sock') await listenManagement(server, socketPath) const client = new LocalApiClient(socketPath) try { const empty = await client.request('GET', `${API_PREFIX}settings-document`) assert.equal(empty.content, '') assert.equal(empty.exists, false) assert.match(empty.revision, /^[a-f0-9]{64}$/) const saved = await client.request('PUT', `${API_PREFIX}settings-document`, { content: 'language: zh\n', revision: empty.revision, }) assert.equal(saved.exists, true) assert.equal(await readFile(join(dshHome, 'settings.yaml'), 'utf8'), 'language: zh\n') assert.equal((await lstat(join(dshHome, 'settings.yaml'))).isFile(), true) await assert.rejects(client.request('PUT', `${API_PREFIX}settings-document`, { content: 'language: en\n', revision: empty.revision, path: '/etc/passwd', }), error => error.statusCode === 400) await assert.rejects(client.request('PUT', `${API_PREFIX}settings-document`, { content: 'language: en\n', revision: empty.revision, }), error => error.statusCode === 409) assert.equal(await readFile(join(dshHome, 'settings.yaml'), 'utf8'), 'language: zh\n') assert.deepEqual((await logs.query({ sources: ['audit'] })).map(entry => entry.message), ['settings-document.saved']) } finally { await new Promise(resolve => server.close(resolve)) } }) test('settings document editing rejects symbolic links, invalid UTF-8, and oversized content', async () => { const root = await mkdtemp(join(tmpdir(), 'dsh-management-settings-unsafe-')) const dshHome = join(root, 'dsh') await import('node:fs/promises').then(({ mkdir }) => mkdir(dshHome)) const target = join(root, 'outside.yaml') await writeFile(target, 'outside: true\n') await symlink(target, join(dshHome, 'settings.yaml')) const store = new SettingsDocumentStore(dshHome, { maxBytes: 8 }) await assert.rejects(store.read(), /regular file/) await import('node:fs/promises').then(({ rm }) => rm(join(dshHome, 'settings.yaml'))) await writeFile(join(dshHome, 'settings.yaml'), Buffer.from([0xc3, 0x28])) await assert.rejects(store.read(), /not valid UTF-8/) await assert.rejects(store.write('123456789', '0'.repeat(64)), /too large/) assert.equal(await readFile(target, 'utf8'), 'outside: true\n') }) test('management restarts only DSH as an audited task and excludes update activation', async () => { const root = await mkdtemp(join(tmpdir(), 'dsh-management-restart-')) const coordinator = new Coordinator() const logs = new JsonlLogManager({ root: join(root, 'logs') }) let finishRestart const restartCompletion = new Promise(resolve => { finishRestart = resolve }) let finishLoadedStateCapture const loadedStateCaptureCompletion = new Promise(resolve => { finishLoadedStateCapture = resolve }) let restarts = 0 let loadedStateCaptures = 0 const server = createManagementServer({ coordinator, logs, restartDelayMs: 0, restartDsh: async () => { restarts += 1 await restartCompletion }, markUserPluginsLoaded: async () => { loadedStateCaptures += 1 await loadedStateCaptureCompletion }, }) const socketPath = join(root, 'run', 'management.sock') await listenManagement(server, socketPath) const client = new LocalApiClient(socketPath) try { const task = await client.request('POST', '/_dsh_platform/api/v1/restart-dsh') assert.equal(typeof task.taskId, 'string') for (let attempt = 0; attempt < ASYNC_POLL_ATTEMPTS && restarts === 0; attempt += 1) { await new Promise(resolve => setTimeout(resolve, ASYNC_POLL_INTERVAL_MS)) } assert.equal(restarts, 1) assert.equal((await client.request('GET', '/_dsh_platform/api/v1/status')).dshLifecycle.state, 'restarting') await assert.rejects( client.request('POST', '/_dsh_platform/api/v1/restart-dsh'), error => error.statusCode === 409, ) await assert.rejects( client.request('POST', '/_dsh_platform/api/v1/update'), error => error.statusCode === 409, ) finishRestart() await new Promise(resolve => setImmediate(resolve)) await logs.queue const status = await client.request('GET', '/_dsh_platform/api/v1/status') assert.equal(status.dshLifecycle.state, 'running') assert.equal(status.dshLifecycle.taskId, task.taskId) assert.equal(loadedStateCaptures, 1) finishLoadedStateCapture() assert.deepEqual( (await logs.query({ sources: ['audit'] })).map(entry => entry.message), ['dsh.restart.started', 'dsh.restart.completed'], ) } finally { await new Promise(resolve => server.close(resolve)) } }) test('management rejects DSH restart while an update task is active', async () => { const root = await mkdtemp(join(tmpdir(), 'dsh-management-restart-conflict-')) const coordinator = new Coordinator() coordinator.running = true const server = createManagementServer({ coordinator, logs: new JsonlLogManager({ root: join(root, 'logs') }), restartDelayMs: 0, restartDsh: async () => {}, }) const socketPath = join(root, 'run', 'management.sock') await listenManagement(server, socketPath) try { await assert.rejects( new LocalApiClient(socketPath).request('POST', '/_dsh_platform/api/v1/restart-dsh'), error => error.statusCode === 409, ) } finally { await new Promise(resolve => server.close(resolve)) } }) test('management returns the restart task before the configured restart delay elapses', async () => { const root = await mkdtemp(join(tmpdir(), 'dsh-management-restart-delay-')) let restarted = false const server = createManagementServer({ coordinator: new Coordinator(), logs: new JsonlLogManager({ root: join(root, 'logs') }), restartDelayMs: 40, restartDsh: async () => { restarted = true }, }) const socketPath = join(root, 'run', 'management.sock') await listenManagement(server, socketPath) const client = new LocalApiClient(socketPath) try { const task = await client.request('POST', `${API_PREFIX}restart-dsh`) assert.equal(typeof task.taskId, 'string') assert.equal(restarted, false) await new Promise(resolve => setTimeout(resolve, 60)) assert.equal(restarted, true) } finally { await new Promise(resolve => server.close(resolve)) } }) test('management starts and stops DSH through the unified lifecycle state', async () => { const root = await mkdtemp(join(tmpdir(), 'dsh-management-lifecycle-')) const calls = [] const logs = new JsonlLogManager({ root: join(root, 'logs') }) const server = createManagementServer({ coordinator: new Coordinator(), logs, startDsh: async () => { calls.push('start') }, stopDsh: async () => { calls.push('stop') }, }) const socketPath = join(root, 'run', 'management.sock') await listenManagement(server, socketPath) const client = new LocalApiClient(socketPath) try { const stopped = await client.request('POST', `${API_PREFIX}stop-dsh`) let status for (let attempt = 0; attempt < ASYNC_POLL_ATTEMPTS; attempt += 1) { status = await client.request('GET', `${API_PREFIX}status`) if (status.dshLifecycle.taskId === stopped.taskId && status.dshLifecycle.state === 'stopped') break await new Promise(resolve => setTimeout(resolve, ASYNC_POLL_INTERVAL_MS)) } assert.equal(status.dshLifecycle.state, 'stopped') assert.equal(status.dshLifecycle.action, null) const started = await client.request('POST', `${API_PREFIX}start-dsh`) for (let attempt = 0; attempt < ASYNC_POLL_ATTEMPTS; attempt += 1) { status = await client.request('GET', `${API_PREFIX}status`) if (status.dshLifecycle.taskId === started.taskId && status.dshLifecycle.state === 'running') break await new Promise(resolve => setTimeout(resolve, ASYNC_POLL_INTERVAL_MS)) } assert.equal(status.dshLifecycle.state, 'running') assert.deepEqual(calls, ['stop', 'start']) assert.deepEqual((await logs.query({ sources: ['audit'] })).map(entry => entry.message), [ 'dsh.stop.started', 'dsh.stop.completed', 'dsh.start.started', 'dsh.start.completed', ]) } finally { await new Promise(resolve => server.close(resolve)) } }) test('management preserves a completed lifecycle task until Bootstrap publishes a newer state', async () => { const root = await mkdtemp(join(tmpdir(), 'dsh-management-lifecycle-merge-')) const logs = new JsonlLogManager({ root: join(root, 'logs') }) let bootstrapLifecycle = { state: 'running', action: null, taskId: null, attempt: 0, maxAttempts: 3, error: null, updatedAt: '2026-08-22T00:00:00.000Z', } const server = createManagementServer({ coordinator: new Coordinator(), logs, platformStatus: async () => ({ dshLifecycle: bootstrapLifecycle }), startDsh: async () => {}, }) const socketPath = join(root, 'run', 'management.sock') await listenManagement(server, socketPath) const client = new LocalApiClient(socketPath) try { const started = await client.request('POST', `${API_PREFIX}start-dsh`) let status for (let attempt = 0; attempt < ASYNC_POLL_ATTEMPTS; attempt += 1) { status = await client.request('GET', `${API_PREFIX}status`) if (status.dshLifecycle.taskId === started.taskId && status.dshLifecycle.state === 'running') break await new Promise(resolve => setTimeout(resolve, ASYNC_POLL_INTERVAL_MS)) } assert.equal(status.dshLifecycle.taskId, started.taskId) bootstrapLifecycle = { ...bootstrapLifecycle, state: 'recovering', action: 'auto-recover', attempt: 1, updatedAt: '2099-08-22T00:00:00.000Z', } status = await client.request('GET', `${API_PREFIX}status`) assert.equal(status.dshLifecycle.state, 'recovering') assert.equal(status.dshLifecycle.attempt, 1) } finally { await new Promise(resolve => server.close(resolve)) } }) test('management resets the current Runtime as an audited exclusive task', async () => { const root = await mkdtemp(join(tmpdir(), 'dsh-management-runtime-reset-')) const coordinator = new Coordinator() const logs = new JsonlLogManager({ root: join(root, 'logs') }) let finishReset const completion = new Promise(resolve => { finishReset = resolve }) let resets = 0 const server = createManagementServer({ coordinator, logs, resetRuntime: async () => { resets += 1 await completion }, }) const socketPath = join(root, 'run', 'management.sock') await listenManagement(server, socketPath) const client = new LocalApiClient(socketPath) try { const task = await client.request('POST', '/_dsh_platform/api/v1/runtime/reset') assert.equal(typeof task.taskId, 'string') for (let attempt = 0; attempt < ASYNC_POLL_ATTEMPTS && resets === 0; attempt += 1) { await new Promise(resolve => setTimeout(resolve, ASYNC_POLL_INTERVAL_MS)) } assert.equal(resets, 1) assert.equal((await client.request('GET', '/_dsh_platform/api/v1/status')).runtimeReset.status, 'resetting') for (const path of ['runtime/reset', 'restart-dsh', 'update']) { await assert.rejects( client.request('POST', `/_dsh_platform/api/v1/${path}`), error => error.statusCode === 409, ) } finishReset() await new Promise(resolve => setImmediate(resolve)) await logs.queue const status = await client.request('GET', '/_dsh_platform/api/v1/status') assert.equal(status.runtimeReset.status, 'success') assert.equal(status.runtimeReset.taskId, task.taskId) assert.deepEqual( (await logs.query({ sources: ['audit'] })).map(entry => entry.message), ['runtime.reset.started', 'runtime.reset.completed'], ) } finally { await new Promise(resolve => server.close(resolve)) } }) test('management exposes recoverable User Plugin tasks under the shared runtime mutex', async () => { const root = await mkdtemp(join(tmpdir(), 'dsh-management-user-plugins-')) const coordinator = new Coordinator() const logs = new JsonlLogManager({ root: join(root, 'logs') }) let finish const completion = new Promise(resolve => { finish = resolve }) const calls = [] const inventory = { schema: 1, profile: 'web', revision: `sha256:${'a'.repeat(64)}`, plugins: [{ name: 'faulty-plugin', enabled: true, damaged: false }], } const server = createManagementServer({ coordinator, logs, listUserPlugins: async () => inventory, validateUserPluginActions: async value => { calls.push(['validate', value]) if (value.revision !== inventory.revision) { const error = new Error('revision changed') error.code = 'REVISION_CONFLICT' throw error } return value }, applyUserPluginActions: async value => { calls.push(['apply', value.taskId, value.revision, value.actions]) value.onProgress({ phase: 'paused' }) await completion value.onProgress({ phase: 'completed' }) }, }) const socketPath = join(root, 'management.sock') await listenManagement(server, socketPath) const client = new LocalApiClient(socketPath) try { assert.deepEqual(await client.request('GET', `${API_PREFIX}user-plugins`), inventory) const request = { profile: 'web', revision: inventory.revision, actions: [{ name: 'faulty-plugin', action: 'disable' }], } const started = await client.request('POST', `${API_PREFIX}user-plugins/apply`, request) assert.match(started.taskId, /^[0-9a-f-]{36}$/) let status for (let attempt = 0; attempt < ASYNC_POLL_ATTEMPTS; attempt += 1) { status = await client.request('GET', `${API_PREFIX}status`) if (status.userPluginOperation.phase === 'paused') break await new Promise(resolve => setTimeout(resolve, ASYNC_POLL_INTERVAL_MS)) } assert.equal(status.userPluginOperation.status, 'running') assert.equal(status.userPluginOperation.phase, 'paused') assert.equal((await client.request('GET', `${API_PREFIX}user-plugins/task/${started.taskId}`)).status, 'running') for (const [path, body] of [ ['restart-dsh', undefined], ['update', undefined], ['bundled-plugins/action', { id: 'diagnostics', action: 'disable' }], ['user-plugins/apply', request], ]) { await assert.rejects(client.request('POST', `${API_PREFIX}${path}`, body), error => error.statusCode === 409) } finish() for (let attempt = 0; attempt < ASYNC_POLL_ATTEMPTS; attempt += 1) { status = await client.request('GET', `${API_PREFIX}status`) if (status.userPluginOperation.status === 'success') break await new Promise(resolve => setTimeout(resolve, ASYNC_POLL_INTERVAL_MS)) } assert.equal(status.userPluginOperation.phase, 'completed') assert.equal((await client.request('GET', `${API_PREFIX}user-plugins/task/${started.taskId}`)).status, 'success') assert.deepEqual(calls[0], ['validate', { revision: inventory.revision, actions: request.actions }]) assert.deepEqual(calls[1].slice(0, 3), ['apply', started.taskId, inventory.revision]) await assert.rejects(client.request('POST', `${API_PREFIX}user-plugins/apply`, { ...request, revision: `sha256:${'b'.repeat(64)}`, }), error => error.statusCode === 409) await assert.rejects( client.request('GET', `${API_PREFIX}user-plugins/task/123e4567-e89b-42d3-a456-426614174000`), error => error.statusCode === 404, ) await logs.queue assert.deepEqual((await logs.query({ sources: ['audit'] })).map(entry => entry.message), [ 'user-plugin.apply.started', 'user-plugin.apply.completed', ]) } finally { await new Promise(resolve => server.close(resolve)) } }) test('management resumes a persisted User Plugin transaction after its socket is available', async () => { const root = await mkdtemp(join(tmpdir(), 'dsh-management-user-plugin-resume-')) const logs = new JsonlLogManager({ root: join(root, 'logs') }) let recover const recovery = new Promise(resolve => { recover = resolve }) const server = createManagementServer({ coordinator: new Coordinator(), logs, recoverUserPluginTransaction: async () => recovery, }) const socketPath = join(root, 'management.sock') await listenManagement(server, socketPath) const client = new LocalApiClient(socketPath) try { await assert.rejects(client.request('POST', `${API_PREFIX}restart-dsh`), error => error.statusCode === 409) recover({ taskId: 'resume-task', phase: 'failed', error: 'interrupted change restored', recoveryResult: 'success', }) let status for (let attempt = 0; attempt < ASYNC_POLL_ATTEMPTS; attempt += 1) { status = await client.request('GET', `${API_PREFIX}status`) if (status.userPluginOperation.taskId === 'resume-task') break await new Promise(resolve => setTimeout(resolve, ASYNC_POLL_INTERVAL_MS)) } assert.equal(status.userPluginOperation.status, 'failed') assert.equal(status.userPluginOperation.error, 'interrupted change restored') await logs.queue assert.equal((await logs.query({ sources: ['audit'] })).at(-1).message, 'user-plugin.recovery.completed') } finally { await new Promise(resolve => server.close(resolve)) } }) test('standalone Management disables a startup-faulting Bundle while DSH is already down', async () => { const root = await mkdtemp(join(tmpdir(), 'dsh-management-fault-plugin-')) const dshHome = join(root, 'dsh') const profileRoot = join(dshHome, 'profiles/web') const pluginRoot = join(profileRoot, 'node_modules/startup-fault') const selectionPath = join(root, 'platform/state/management/user-plugins.json') await mkdir(pluginRoot, { recursive: true }) await writeFile(join(profileRoot, 'package.json'), JSON.stringify({ name: 'dsh-profile-web', dependencies: { 'startup-fault': '1.0.0' }, dsh: { profile: { bundles: ['startup-fault'] } }, })) await writeFile(join(profileRoot, 'pnpm-lock.yaml'), 'lockfileVersion: 9\n') await writeFile(join(pluginRoot, 'package.json'), JSON.stringify({ name: 'startup-fault', version: '1.0.0', dsh: { bundle: { patch: './cordis.patch.yml' } }, })) await writeFile(join(pluginRoot, 'cordis.patch.yml'), '- insert: [{ id: startup-fault, name: startup-fault }]\n') const inventory = new UserPluginInventory({ dshHome, selectionPath }) const logs = new JsonlLogManager({ root: join(root, 'logs') }) let paused = 0 let restarted = 0 const transactions = new UserPluginTransactionManager({ inventory, selectionStore: new UserPluginSelectionStore(selectionPath), snapshots: new UserPluginSnapshots({ root: join(root, 'platform/store/snapshots/user-plugins'), profileRoot }), journal: new UserPluginJournal(join(root, 'platform/state/management/user-plugin-transaction.json')), pauseDsh: async () => { paused += 1 }, restartDelayMs: 0, restartDsh: async () => { restarted += 1 const manifest = JSON.parse(await readFile(join(profileRoot, 'package.json'), 'utf8')) if (manifest.dsh.profile.bundles.includes('startup-fault')) throw new Error('startup-fault crashed DSH') }, report: (message, fields) => logs.diagnostic('user-plugin-manager', message, fields), }) const server = createManagementServer({ coordinator: new Coordinator(), logs, platformStatus: async () => ({ recoveryMode: 'startup-fault crashed DSH' }), listUserPlugins: () => inventory.read(), validateUserPluginActions: value => transactions.validate(value), applyUserPluginActions: value => transactions.apply(value), }) const socketPath = join(root, 'management.sock') await listenManagement(server, socketPath) const client = new LocalApiClient(socketPath) try { const listed = await client.request('GET', `${API_PREFIX}user-plugins`) assert.deepEqual(listed.plugins.map(plugin => [plugin.name, plugin.enabled]), [['startup-fault', true]]) assert.equal((await client.request('GET', `${API_PREFIX}status`)).recoveryMode, 'startup-fault crashed DSH') const task = await client.request('POST', `${API_PREFIX}user-plugins/apply`, { profile: 'web', revision: listed.revision, actions: [{ name: 'startup-fault', action: 'disable' }], }) let state for (let attempt = 0; attempt < ASYNC_POLL_ATTEMPTS; attempt += 1) { state = await client.request('GET', `${API_PREFIX}user-plugins/task/${task.taskId}`) if (state.status !== 'running') break await new Promise(resolve => setTimeout(resolve, ASYNC_POLL_INTERVAL_MS)) } assert.equal(state.status, 'success') assert.equal(state.strategy, 'next-start') assert.equal(paused, 0) assert.equal(restarted, 0) const manifest = JSON.parse(await readFile(join(profileRoot, 'package.json'), 'utf8')) assert.deepEqual(manifest.dsh.profile.bundles, []) await logs.queue assert.deepEqual((await logs.query({ sources: ['user-plugin-manager'] })).map(entry => entry.message), [ 'user-plugin.transaction.started', 'user-plugin.transaction.completed', ]) } finally { await new Promise(resolve => server.close(resolve)) } }) test('management changes a bundled plugin as an audited task and excludes runtime changes', async () => { const root = await mkdtemp(join(tmpdir(), 'dsh-management-plugin-action-')) const coordinator = new Coordinator() const logs = new JsonlLogManager({ root: join(root, 'logs') }) let finish const completion = new Promise(resolve => { finish = resolve }) const calls = [] const server = createManagementServer({ coordinator, logs, listBundledPlugins: async () => [{ id: 'diagnostics', installed: true, enabled: true, protected: false }], configureBundledPlugin: async (id, action) => { calls.push([id, action]) await completion }, restartDelayMs: 0, restartDsh: async () => {}, }) const socketPath = join(root, 'run', 'management.sock') await listenManagement(server, socketPath) const client = new LocalApiClient(socketPath) try { assert.deepEqual((await client.request('GET', '/_dsh_platform/api/v1/bundled-plugins')).plugins, [{ id: 'diagnostics', installed: true, enabled: true, protected: false, }]) const task = await client.request('POST', '/_dsh_platform/api/v1/bundled-plugins/action', { id: 'diagnostics', action: 'disable', }) assert.equal(typeof task.taskId, 'string') for (let attempt = 0; attempt < ASYNC_POLL_ATTEMPTS && calls.length === 0; attempt += 1) { await new Promise(resolve => setTimeout(resolve, ASYNC_POLL_INTERVAL_MS)) } assert.deepEqual(calls, [['diagnostics', 'disable']]) assert.equal((await client.request('GET', '/_dsh_platform/api/v1/status')).systemPluginOperation.status, 'running') assert.equal((await client.request('GET', `/_dsh_platform/api/v1/bundled-plugins/task/${task.taskId}`)).status, 'running') await assert.rejects( client.request('GET', '/_dsh_platform/api/v1/bundled-plugins/task/123e4567-e89b-42d3-a456-426614174000'), error => error.statusCode === 404, ) for (const [method, path, body] of [ ['POST', '/_dsh_platform/api/v1/bundled-plugins/action', { id: 'diagnostics', action: 'uninstall' }], ['POST', '/_dsh_platform/api/v1/restart-dsh'], ['POST', '/_dsh_platform/api/v1/update'], ]) { await assert.rejects(client.request(method, path, body), error => error.statusCode === 409) } finish() await new Promise(resolve => setImmediate(resolve)) await logs.queue const changed = await client.request('GET', '/_dsh_platform/api/v1/status') assert.equal(changed.systemPluginOperation.status, 'success') assert.equal(changed.systemPluginOperation.restartRequired, true) assert.equal((await client.request('GET', `/_dsh_platform/api/v1/bundled-plugins/task/${task.taskId}`)).status, 'success') assert.deepEqual( (await logs.query({ sources: ['audit'] })).map(entry => entry.message), ['system-plugin.disable.started', 'system-plugin.disable.completed'], ) await client.request('POST', '/_dsh_platform/api/v1/restart-dsh') let restarted for (let attempt = 0; attempt < ASYNC_POLL_ATTEMPTS; attempt += 1) { restarted = await client.request('GET', '/_dsh_platform/api/v1/status') if (restarted.dshLifecycle.state === 'running') break await new Promise(resolve => setTimeout(resolve, ASYNC_POLL_INTERVAL_MS)) } assert.equal(restarted.dshLifecycle.state, 'running') assert.equal(restarted.systemPluginOperation.restartRequired, false) } finally { await new Promise(resolve => server.close(resolve)) } }) test('management completes System Plugin changes for the next start while DSH is unavailable', async () => { const root = await mkdtemp(join(tmpdir(), 'dsh-management-system-plugin-offline-')) let configured = false const server = createManagementServer({ coordinator: new Coordinator(), logs: new JsonlLogManager({ root: join(root, 'logs') }), platformStatus: async () => ({ recoveryMode: 'startup failed', dshLifecycle: { state: 'failed' }, }), configureBundledPlugin: async () => { configured = true }, }) const socketPath = join(root, 'run', 'management.sock') await listenManagement(server, socketPath) const client = new LocalApiClient(socketPath) try { await client.request('POST', `${API_PREFIX}bundled-plugins/action`, { id: 'diagnostics', action: 'disable', }) let status for (let attempt = 0; attempt < ASYNC_POLL_ATTEMPTS; attempt += 1) { status = await client.request('GET', `${API_PREFIX}status`) if (status.systemPluginOperation.status === 'success') break await new Promise(resolve => setTimeout(resolve, ASYNC_POLL_INTERVAL_MS)) } assert.equal(configured, true) assert.equal(status.systemPluginOperation.strategy, 'next-start') assert.equal(status.systemPluginOperation.restartRequired, false) } finally { await new Promise(resolve => server.close(resolve)) } }) test('management toggle endpoint accepts only enable and disable actions', async () => { const root = await mkdtemp(join(tmpdir(), 'dsh-management-plugin-toggle-')) const calls = [] const server = createManagementServer({ coordinator: new Coordinator(), logs: new JsonlLogManager({ root: join(root, 'logs') }), configureBundledPlugin: async (id, action) => { calls.push([id, action]) }, }) const socketPath = join(root, 'run', 'management.sock') await listenManagement(server, socketPath) const client = new LocalApiClient(socketPath) try { await assert.rejects(client.request('POST', `${API_PREFIX}bundled-plugins/toggle`, { id: 'diagnostics', action: 'install', }), error => error.statusCode === 400) const task = await client.request('POST', `${API_PREFIX}bundled-plugins/toggle`, { id: 'diagnostics', action: 'disable', }) assert.equal(typeof task.taskId, 'string') for (let attempt = 0; attempt < ASYNC_POLL_ATTEMPTS && calls.length === 0; attempt += 1) { await new Promise(resolve => setTimeout(resolve, ASYNC_POLL_INTERVAL_MS)) } assert.deepEqual(calls, [['diagnostics', 'disable']]) } finally { await new Promise(resolve => server.close(resolve)) } }) test('management discards unapplied System Plugin changes and clears restart state', async () => { const root = await mkdtemp(join(tmpdir(), 'dsh-management-plugin-discard-')) let discarded = 0 const logs = new JsonlLogManager({ root: join(root, 'logs') }) const server = createManagementServer({ coordinator: new Coordinator(), logs, configureBundledPlugin: async () => {}, discardBundledPluginChanges: async () => { discarded += 1 return { plugins: [{ id: 'diagnostics', pendingRestart: false }] } }, }) const socketPath = join(root, 'run', 'management.sock') await listenManagement(server, socketPath) const client = new LocalApiClient(socketPath) try { await client.request('POST', `${API_PREFIX}bundled-plugins/action`, { id: 'diagnostics', action: 'disable', }) for (let attempt = 0; attempt < ASYNC_POLL_ATTEMPTS; attempt += 1) { if ((await client.request('GET', `${API_PREFIX}status`)).systemPluginOperation.restartRequired) break await new Promise(resolve => setTimeout(resolve, ASYNC_POLL_INTERVAL_MS)) } let result for (let attempt = 0; attempt < ASYNC_POLL_ATTEMPTS; attempt += 1) { try { result = await client.request('POST', `${API_PREFIX}bundled-plugins/discard`) break } catch (error) { if (error.statusCode !== 409) throw error await new Promise(resolve => setTimeout(resolve, ASYNC_POLL_INTERVAL_MS)) } } assert.notEqual(result, undefined) assert.equal(discarded, 1) assert.deepEqual(result, { plugins: [{ id: 'diagnostics', pendingRestart: false }] }) assert.equal((await client.request('GET', `${API_PREFIX}status`)).systemPluginOperation.restartRequired, false) assert.deepEqual((await logs.query({ sources: ['audit'] })).map(entry => entry.message), [ 'system-plugin.disable.started', 'system-plugin.disable.completed', 'system-plugin.changes.discarded', ]) } finally { await new Promise(resolve => server.close(resolve)) } }) test('management completes the System Plugin change when DSH restart fails', async () => { const root = await mkdtemp(join(tmpdir(), 'dsh-management-plugin-restart-failure-')) const logs = new JsonlLogManager({ root: join(root, 'logs') }) const server = createManagementServer({ coordinator: new Coordinator(), logs, configureBundledPlugin: async () => {}, restartDelayMs: 0, restartDsh: async () => { throw new Error('restart failed') }, }) const socketPath = join(root, 'run', 'management.sock') await listenManagement(server, socketPath) const client = new LocalApiClient(socketPath) try { await client.request('POST', '/_dsh_platform/api/v1/bundled-plugins/action', { id: 'diagnostics', action: 'disable', }) let status for (let attempt = 0; attempt < ASYNC_POLL_ATTEMPTS; attempt += 1) { status = await client.request('GET', '/_dsh_platform/api/v1/status') if (status.systemPluginOperation.status === 'success') break await new Promise(resolve => setTimeout(resolve, ASYNC_POLL_INTERVAL_MS)) } assert.equal(status.systemPluginOperation.restartRequired, true) await client.request('POST', '/_dsh_platform/api/v1/restart-dsh') for (let attempt = 0; attempt < ASYNC_POLL_ATTEMPTS; attempt += 1) { status = await client.request('GET', '/_dsh_platform/api/v1/status') if (status.dshLifecycle.state === 'failed') break await new Promise(resolve => setTimeout(resolve, ASYNC_POLL_INTERVAL_MS)) } assert.equal(status.dshLifecycle.state, 'failed') assert.equal(status.systemPluginOperation.strategy, 'next-start') assert.equal(status.systemPluginOperation.activationError, 'restart failed') assert.equal(status.systemPluginOperation.restartRequired, false) await logs.queue assert.equal((await logs.query({ sources: ['audit'] })).find(entry => entry.message === 'dsh.restart.failed').level, 'error') } finally { await new Promise(resolve => server.close(resolve)) } }) test('management audit failures do not block a completed System Plugin operation', async () => { const root = await mkdtemp(join(tmpdir(), 'dsh-management-audit-failure-')) let configured = false const logs = { diagnostic: () => { throw new Error('audit storage unavailable') }, query: async () => [], on: () => {}, off: () => {}, } const server = createManagementServer({ coordinator: new Coordinator(), logs, configureBundledPlugin: async () => { configured = true }, }) const socketPath = join(root, 'run', 'management.sock') await listenManagement(server, socketPath) const client = new LocalApiClient(socketPath) try { await client.request('POST', `${API_PREFIX}bundled-plugins/action`, { id: 'diagnostics', action: 'disable', }) let status for (let attempt = 0; attempt < ASYNC_POLL_ATTEMPTS; attempt += 1) { status = await client.request('GET', `${API_PREFIX}status`) if (status.systemPluginOperation.status === 'success') break await new Promise(resolve => setTimeout(resolve, ASYNC_POLL_INTERVAL_MS)) } assert.equal(configured, true) assert.equal(status.systemPluginOperation.status, 'success') assert.equal(status.systemPluginOperation.restartRequired, true) } finally { await new Promise(resolve => server.close(resolve)) } }) test('management exposes a dedicated recovery action only for Platform Management', async () => { const root = await mkdtemp(join(tmpdir(), 'dsh-management-plugin-recovery-')) const calls = [] const logs = new JsonlLogManager({ root: join(root, 'logs') }) const server = createManagementServer({ coordinator: new Coordinator(), logs, recoverBundledPlugin: async (id, action) => { calls.push([id, action]) }, }) const socketPath = join(root, 'run', 'management.sock') await listenManagement(server, socketPath) const client = new LocalApiClient(socketPath) try { await assert.rejects(client.request('POST', `${API_PREFIX}bundled-plugins/recovery-action`, { id: 'diagnostics', action: 'uninstall', }), error => error.statusCode === 400) const task = await client.request('POST', `${API_PREFIX}bundled-plugins/recovery-action`, { id: 'platform-management', action: 'disable', }) assert.equal(typeof task.taskId, 'string') for (let attempt = 0; attempt < ASYNC_POLL_ATTEMPTS && calls.length === 0; attempt += 1) { await new Promise(resolve => setTimeout(resolve, ASYNC_POLL_INTERVAL_MS)) } assert.deepEqual(calls, [['platform-management', 'disable']]) await new Promise(resolve => setImmediate(resolve)) await logs.queue assert.deepEqual( (await logs.query({ sources: ['audit'] })).map(entry => entry.message), ['system-plugin.recovery.disable.started', 'system-plugin.recovery.disable.completed'], ) } finally { await new Promise(resolve => server.close(resolve)) } }) test('management separates a known Stable target from update availability', async () => { const root = await mkdtemp(join(tmpdir(), 'dsh-management-current-')) const coordinator = new Coordinator() coordinator.check = async () => ({ value: { targetSequence: 2, desired: { dsh: { version: 'rc.7' } } }, updateAvailable: false, }) const server = createManagementServer({ coordinator, logs: new JsonlLogManager({ root: join(root, 'logs') }), }) const socketPath = join(root, 'run', 'management.sock') await listenManagement(server, socketPath) try { assert.deepEqual(await new LocalApiClient(socketPath).request('POST', '/_dsh_platform/api/v1/check'), { available: false, targetSequence: 2, desired: { dsh: { version: 'rc.7' } }, }) } finally { await new Promise(resolve => server.close(resolve)) } }) test('management records a completion audit for a successful update task', async () => { const root = await mkdtemp(join(tmpdir(), 'dsh-management-audit-')) const coordinator = new Coordinator() coordinator.startReconcile = () => ({ taskId: 'task-success', completion: Promise.resolve() }) const logs = new JsonlLogManager({ root: join(root, 'logs') }) const server = createManagementServer({ coordinator, logs }) const socketPath = join(root, 'run', 'management.sock') await listenManagement(server, socketPath) try { const client = new LocalApiClient(socketPath) await client.request('POST', '/_dsh_platform/api/v1/update') await new Promise(resolve => setImmediate(resolve)) await logs.queue assert.deepEqual( (await logs.query({ sources: ['audit'] })).map(entry => [entry.message, entry.taskId]), [['update.started', 'task-success'], ['update.completed', 'task-success']], ) } finally { await new Promise(resolve => server.close(resolve)) } }) test('management records correlated rollback and return-stable audit lifecycles', async () => { const root = await mkdtemp(join(tmpdir(), 'dsh-management-rollback-audit-')) const coordinator = new Coordinator() coordinator.startCompleteRollback = (planId, options) => { assert.equal(planId, 'plan-a') if (options.requireConfirmation) { return { taskId: 'return-stable-failure', completion: Promise.reject(new Error('restore failed')) } } return { taskId: 'rollback-success', completion: Promise.resolve() } } const logs = new JsonlLogManager({ root: join(root, 'logs') }) const server = createManagementServer({ coordinator, logs }) const socketPath = join(root, 'run', 'management.sock') await listenManagement(server, socketPath) try { const client = new LocalApiClient(socketPath) await client.request('POST', `${API_PREFIX}rollback`, { planId: 'plan-a' }) await client.request('POST', `${API_PREFIX}return-stable`, { planId: 'plan-a', confirmDataLoss: true, }) await new Promise(resolve => setImmediate(resolve)) await logs.queue assert.deepEqual( (await logs.query({ sources: ['audit'] })).map(entry => [entry.message, entry.taskId]), [ ['rollback.started', 'rollback-success'], ['rollback.completed', 'rollback-success'], ['return-stable.started', 'return-stable-failure'], ['return-stable.failed', 'return-stable-failure'], ], ) } finally { await new Promise(resolve => server.close(resolve)) } }) test('management serves only the fixed persistent Platform Management assets', async () => { const root = await mkdtemp(join(tmpdir(), 'dsh-management-console-')) const coordinator = new Coordinator() const logs = new JsonlLogManager({ root: join(root, 'logs') }) const server = createManagementServer({ coordinator, logs }) const socketPath = join(root, 'management.sock') await listenManagement(server, socketPath) try { const redirect = await rawRequest(socketPath, '/_dsh_platform/console') assert.equal(redirect.status, 308) assert.equal(redirect.headers.location, '/_dsh_platform/console/') const page = await rawRequest(socketPath, '/_dsh_platform/console/') assert.equal(page.status, 200) assert.match(page.headers['content-type'], /^text\/html/) assert.match(page.body.toString('utf8'), / match[1])) const references = [...script.matchAll(/elements(?:\.([A-Za-z][A-Za-z0-9]*)|\[['"]([a-z0-9-]+)['"]\])/g)] .map(match => match[1] ?? match[2]) assert.equal(references.length > 0, true) for (const id of references) assert.equal(ids.has(id), true, `Console element ${id} is not declared`) assert.doesNotMatch(script, /innerHTML|outerHTML|insertAdjacentHTML/) assert.match(script, /selectTab\(window\.location\.hash === '#auth-settings' \? 'auth-settings' : 'maintenance'\)/) }) test('standalone file task queue stays below file actions and scrolls within a fixed height', async () => { const publicRoot = new URL('../../control-plane/services/management/public/', import.meta.url) const html = await readFile(new URL('index.html', publicRoot), 'utf8') const script = await readFile(new URL('app.js', publicRoot), 'utf8') const style = await readFile(new URL('style.css', publicRoot), 'utf8') const browserOffset = html.indexOf('class="file-browser"') const selectionOffset = html.indexOf('class="file-selection"') const taskOffset = html.indexOf('id="file-task-state"') const archiveOffset = html.indexOf('id="file-archive-panel"') assert.equal(browserOffset < selectionOffset && selectionOffset < taskOffset && taskOffset < archiveOffset, true) assert.match(style, /\.file-task-list\s*\{[^}]*max-height:\s*180px;[^}]*overflow-y:\s*auto;/) assert.match(style, /\.file-main\s*\{[^}]*display:\s*flex;[^}]*flex-direction:\s*column;/) assert.match(style, /\.file-pagination\s*\{[^}]*margin-top:\s*auto;/) assert.match(script, /task\.status === 'failed' && Date\.parse\(task\.updatedAt\) >= recentCutoff/) assert.doesNotMatch(script, /\['success', 'failed', 'cancelled'\]/) assert.match(script, /fileUploadQueue = files\.map/) assert.match(script, /request\.upload\.onprogress/) assert.match(script, /\.\.\.fileUploadQueue, \.\.\.backend/) assert.match(script, /task\.local === true\) task\.cancel\(\)/) assert.match(script, /item\.webkitGetAsEntry\?\.\(\)/) assert.match(script, /collectDroppedEntry\(child, relativePath\)/) assert.match(script, /fileDropTarget\.addEventListener\('drop'/) assert.match(html, /class="file-drop-overlay"/) assert.match(style, /\.file-main\.file-dragging \.file-drop-overlay\s*\{[^}]*display:\s*grid;/) assert.match(style, /\.proxy-provider-list \{[^}]*max-height: 320px;[^}]*overflow-y: auto;/) assert.match(html, /id="proxy-advanced"[\s\S]*id="proxy-remote-dns-row"[^>]*hidden[\s\S]*id="proxy-remote-dns"/) assert.doesNotMatch(style, /\.proxy-check-row/) assert.match(style, /\.proxy-scope-description \{[^}]*-webkit-line-clamp: 1;/) assert.match(script, /function refreshProxyDescriptions\(\)[\s\S]*preserveScrollableAncestors/) assert.match(script, /function renderProxyProviders\(\)[\s\S]*displayName[\s\S]*info\.className = 'proxy-provider-info'/) assert.match(script, /proxyConfiguration = await api\('proxy'\)[\s\S]*renderProxyConfiguration\(\)[\s\S]*void refreshProxyProviders\(\)/) assert.match(script, /if \(tab === 'proxy'\) \{[\s\S]*if \(proxyLoaded\) void refreshProxyProviders\(\)[\s\S]*else void loadProxy\(\)/) assert.match(script, /function refreshVisibleProxyProviders\(\)[\s\S]*proxyLoaded[\s\S]*panel-proxy[\s\S]*refreshProxyProviders\(\)/) assert.match(script, /window\.addEventListener\('focus', refreshVisibleProxyProviders\)/) assert.doesNotMatch(script, /Promise\.all\(\[api\('proxy'\), api\('proxy\/provider-inventory'\)\]\)/) assert.match(script, /proxy-provider-search[\s\S]*addEventListener\('input', renderProxyProviders\)/) assert.match(script, /proxy-clear-password[\s\S]*getAttribute\('aria-pressed'\) === 'true'/) assert.match(script, /route\.className = 'proxy-provider-route'[\s\S]*input\.dataset\.providerPolicy = provider\.id[\s\S]*input\.checked = policy\.proxyEnabled === true/) assert.match(html, /class="proxy-scope-card"[\s\S]*class="toggle"[\s\S]*data-proxy-scope="updates"/) assert.match(html, /id="proxy-clear-password"[^>]*aria-pressed="false"/) assert.match(html, /class="proxy-setting-row[^"]*"[\s\S]*class="toggle"[\s\S]*id="proxy-all-proxy"/) assert.match(html, /id="proxy-provider-search"[^>]*type="search"/) assert.match(html, /id="proxy-test-dialog"[\s\S]*id="proxy-test-stages"/) assert.doesNotMatch(html, /id="proxy-test-close"/) assert.match(html, /