feat(code-runtime): execute Node programs through confined processes

This commit is contained in:
Tianyi Cui committed 2026-09-12 15:03:01 +08:00
1 parent f95f7ec8dc
commit 75ed8da3e0
54 files changed
+1933 -2277

No files matched your search

@@ -0,0 +1,23 @@
import { describe, expect, it } from 'vitest'
import { validateBindings } from '../src/bindings.ts'
describe('program binding validation', () => {
it.each(['$name', '', 'while', 'lambda', 'console'])('rejects unusable global %s', (global) => {
expect(() => validateBindings({ program: '', bindings: [{ global, functions: {} }] })).toThrow()
})
it('rejects duplicated globals and accepts arbitrary own member names', () => {
const functions = Object.create(null) as Record<string, () => Promise<string>>
functions.__proto__ = async () => 'own'
expect(validateBindings({ program: '', bindings: [{ global: 'tools', functions }] }).get('tools')?.functions).toBe(functions)
expect(() => validateBindings({ program: '', bindings: [{ global: 'tools', functions }, { global: 'tools', functions }] })).toThrow('duplicate')
})
it.each(['$name', 'while', 'console', 'tools'])('rejects invalid error class name %s', (name) => {
expect(() => validateBindings({ program: '', bindings: [{ global: 'tools', functions: {}, errorClass: { name, memberNameProperty: 'member' } }] })).toThrow()
})
it.each(['', 'name', '__member__'])('rejects invalid error member %s', (memberNameProperty) => {
expect(() => validateBindings({ program: '', bindings: [{ global: 'tools', functions: {}, errorClass: { name: 'ToolError', memberNameProperty } }] })).toThrow()
})
it('rejects two injected constructors with the same name', () => {
expect(() => validateBindings({ program: '', bindings: ['first', 'second'].map(global => ({ global, functions: {}, errorClass: { name: 'ToolError', memberNameProperty: 'member' } })) })).toThrow('duplicate')
})
})
@@ -1,9 +1,9 @@
import { describe, expect, it } from 'vitest'
import { EventEmitter } from 'node:events'
import { LogBuffer, makeBindingErrorClasses, makeConsoleShim, makeNamespaces, captureStreamWrites, prepareCompletion, prepareException, runWorkerMain, wireReplies } from '../src/bootstrap.ts'
import { LogBuffer, makeBindingErrorClasses, makeConsoleShim, makeNamespaces, captureStreamWrites, prepareCompletion, prepareException, runProgram, wireReplies } from '../src/bootstrap.ts'
import type { BootstrapPort, PatchableStream, PendingCall } from '../src/bootstrap.ts'
import type { ReplyMessage, WorkerToHost } from '../src/protocol.ts'
import { decodeWorkerJson, encodeWorkerJson } from '../src/worker-json.ts'
import type { ReplyMessage, ProgramToHost } from '../src/protocol.ts'
import { decodeCodeJsonWire, encodeCodeJsonWire } from '../src/json-wire.ts'
/**
* An in-process stand-in for the worker's parentPort: the test plays the
@@ -12,12 +12,12 @@ import { decodeWorkerJson, encodeWorkerJson } from '../src/worker-json.ts'
* isolate (real-worker behavior is pinned by runtime.spec.ts).
*/
class FakePort implements BootstrapPort {
sent: WorkerToHost[] = []
sent: ProgramToHost[] = []
private readonly emitter = new EventEmitter()
/** Host-scripted responder; return undefined to leave the call pending. */
respond: (message: WorkerToHost) => ReplyMessage | undefined = () => undefined
respond: (message: ProgramToHost) => ReplyMessage | undefined = () => undefined
postMessage(message: WorkerToHost): void {
postMessage(message: ProgramToHost): void {
this.sent.push(message)
const reply = this.respond(message)
if (reply) queueMicrotask(() => this.emitter.emit('message', reply))
@@ -35,13 +35,13 @@ class FakePort implements BootstrapPort {
return this.sent.filter(message => message.type === 'log').map(message => message.text)
}
done(): WorkerToHost | undefined {
done(): ProgramToHost | undefined {
return this.sent.find(message => message.type === 'done')
}
doneValue(): unknown {
const done = this.done()
return done?.type === 'done' && done.value !== undefined ? decodeWorkerJson(done.value) : undefined
return done?.type === 'done' && done.value !== undefined ? decodeCodeJsonWire(done.value) : undefined
}
}
@@ -140,7 +140,7 @@ describe('captureStreamWrites', () => {
describe('prepareCompletion', () => {
it('omits undefined and passes lossless JSON values exactly', () => {
expect(prepareCompletion(undefined, 100)).toEqual({})
expect(prepareCompletion({ a: [1, 'two'] }, 100)).toEqual({ value: encodeWorkerJson({ a: [1, 'two'] }) })
expect(prepareCompletion({ a: [1, 'two'] }, 100)).toEqual({ value: encodeCodeJsonWire({ a: [1, 'two'] }) })
})
it('turns every lossy completion shape into invalid-output', () => {
@@ -162,7 +162,7 @@ describe('prepareCompletion', () => {
})
it('measures the exact JSON serialization at and over the boundary', () => {
expect(prepareCompletion('€', 5)).toEqual({ value: encodeWorkerJson('€') })
expect(prepareCompletion('€', 5)).toEqual({ value: encodeCodeJsonWire('€') })
expect(prepareCompletion('€', 4)).toEqual({
error: { kind: 'output-limit', message: 'outer output exceeded 4 bytes' },
})
@@ -216,7 +216,7 @@ describe('makeNamespaces', () => {
it('exposes prototype-colliding names as ordinary own properties', async () => {
const port = new FakePort()
port.respond = message => message.type === 'call'
? { type: 'reply', id: message.id, ok: true, value: encodeWorkerJson(`${message.name}-ok`) }
? { type: 'reply', id: message.id, ok: true, value: encodeCodeJsonWire(`${message.name}-ok`) }
: undefined
const pending = new Map<number, PendingCall>()
wireReplies(port, pending)
@@ -311,15 +311,15 @@ describe('makeNamespaces', () => {
})
})
describe('runWorkerMain', () => {
describe('runProgram', () => {
it('runs a program end-to-end: bindings, console, return value', async () => {
const port = new FakePort()
port.respond = (message) => {
if (message.type !== 'call') return undefined
const args = decodeWorkerJson(message.args) as { n: number }
return { type: 'reply', id: message.id, ok: true, value: encodeWorkerJson(args.n * 2) }
const args = decodeCodeJsonWire(message.args) as { n: number }
return { type: 'reply', id: message.id, ok: true, value: encodeCodeJsonWire(args.n * 2) }
}
await runWorkerMain(port, {
await runProgram(port, {
...BOOT,
code: 'const doubled = await tools.double({ n: 21 }); console.log("got", doubled); return { doubled };',
namespaces: [{ global: 'tools', names: ['double'] }],
@@ -330,7 +330,7 @@ describe('runWorkerMain', () => {
it('reports worker-side log capture overflow before completing', async () => {
const port = new FakePort()
await runWorkerMain(port, {
await runProgram(port, {
maxOutputBytes: 4,
code: 'console.log("12345"); return null',
namespaces: [],
@@ -345,7 +345,7 @@ describe('runWorkerMain', () => {
it('reports a thrown program error on the done message', async () => {
const port = new FakePort()
await runWorkerMain(port, { ...BOOT, code: 'throw new Error("boom")', namespaces: [] }, fakeStreams())
await runProgram(port, { ...BOOT, code: 'throw new Error("boom")', namespaces: [] }, fakeStreams())
const done = port.done()
expect(done?.type).toBe('done')
expect(done?.type === 'done' ? done.error?.kind : undefined).toBe('exception')
@@ -355,17 +355,17 @@ describe('runWorkerMain', () => {
it('renders non-Error throws and stack-less Errors on the done message', async () => {
const rawPort = new FakePort()
await runWorkerMain(rawPort, { ...BOOT, code: 'throw "raw-throw"', namespaces: [] }, fakeStreams())
await runProgram(rawPort, { ...BOOT, code: 'throw "raw-throw"', namespaces: [] }, fakeStreams())
expect(rawPort.done()).toEqual({ type: 'done', error: { kind: 'exception', message: 'raw-throw' } })
const barePort = new FakePort()
await runWorkerMain(barePort, { ...BOOT, code: 'const e = new Error("bare"); e.stack = undefined; throw e', namespaces: [] }, fakeStreams())
await runProgram(barePort, { ...BOOT, code: 'const e = new Error("bare"); e.stack = undefined; throw e', namespaces: [] }, fakeStreams())
expect(barePort.done()).toEqual({ type: 'done', error: { kind: 'exception', message: 'bare' } })
})
it('replaces giant thrown strings and Error stacks before posting the done message', async () => {
const rawPort = new FakePort()
await runWorkerMain(rawPort, {
await runProgram(rawPort, {
maxOutputBytes: 64,
code: 'throw "x".repeat(1_000_000)',
namespaces: [],
@@ -376,7 +376,7 @@ describe('runWorkerMain', () => {
})
const stackPort = new FakePort()
await runWorkerMain(stackPort, {
await runProgram(stackPort, {
maxOutputBytes: 64,
code: 'throw new Error("x".repeat(1_000_000))',
namespaces: [],
@@ -390,7 +390,7 @@ describe('runWorkerMain', () => {
it('surfaces a host failure reply as a program-side rejection it can catch', async () => {
const port = new FakePort()
port.respond = message => message.type === 'call' ? { type: 'reply', id: message.id, ok: false, message: 'denied by host' } : undefined
await runWorkerMain(port, {
await runProgram(port, {
...BOOT,
code: 'try { await tools.x({}) } catch (error) { return { caught: error instanceof ToolCallError, name: error.name, toolName: error.toolName, message: error.message } }',
namespaces: [toolNamespace(['x'])],
@@ -403,7 +403,7 @@ describe('runWorkerMain', () => {
port.respond = message => message.type === 'call'
? { type: 'reply', id: message.id, ok: false, message: 'helper denied' }
: undefined
await runWorkerMain(port, {
await runProgram(port, {
...BOOT,
code: 'try { await helpers.x({}) } catch (error) { return { caught: error instanceof HelperCallError, name: error.name, helperName: error.helperName, message: error.message } }',
namespaces: [{
@@ -420,10 +420,10 @@ describe('runWorkerMain', () => {
port.respond = (message) => {
if (message.type !== 'call') return undefined
// Deliver a stray reply first; the real one follows.
port.deliver({ type: 'reply', id: 9_999, ok: true, value: encodeWorkerJson('stray') })
return { type: 'reply', id: message.id, ok: true, value: encodeWorkerJson('real') }
port.deliver({ type: 'reply', id: 9_999, ok: true, value: encodeCodeJsonWire('stray') })
return { type: 'reply', id: message.id, ok: true, value: encodeCodeJsonWire('real') }
}
await runWorkerMain(port, {
await runProgram(port, {
...BOOT,
code: 'return await tools.x({})',
namespaces: [{ global: 'tools', names: ['x'] }],
@@ -434,7 +434,7 @@ describe('runWorkerMain', () => {
it('captures raw stream writes through the patched process streams', async () => {
const port = new FakePort()
const streams = fakeStreams()
await runWorkerMain(port, { ...BOOT, code: 'return 1', namespaces: [] }, streams)
await runProgram(port, { ...BOOT, code: 'return 1', namespaces: [] }, streams)
streams.stdout.write('never seen — already restored? no: patch persists in worker')
// The patch stays installed for the worker's lifetime; writes during the
// program landed in order. Here the program wrote nothing via streams, so
@@ -1,95 +0,0 @@
/** Host budget decisions use controlled clocks and ELU samples; worker execution and binding transport stay real. */
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { Context } from '@deepseek-ai/cordis'
import { NodeCodeRuntime } from '@deepseek-ai/dsh-code-runtime-node'
import type { CodeRunResult } from '@deepseek-ai/dsh-code-runtime'
const meter = vi.hoisted(() => ({ sample: vi.fn() }))
vi.mock('node:worker_threads', async (importOriginal) => {
const original = await importOriginal<typeof import('node:worker_threads')>()
return {
...original,
Worker: class extends original.Worker {
constructor(...args: ConstructorParameters<typeof original.Worker>) {
super(...args)
this.performance.eventLoopUtilization = meter.sample
}
},
}
})
describe('worker budgets with controlled ELU samples and real binding transport', () => {
let ctx: Context
let controller: AbortController
let run: Promise<CodeRunResult> | undefined
let release: (() => void) | undefined
beforeEach(() => {
ctx = new Context()
controller = new AbortController()
run = undefined
release = undefined
meter.sample.mockReset().mockReturnValue({ active: 10, idle: 0, utilization: 1 })
// Worker bootstrap and scheduling contribute to ELU active time; only the
// measured input and host deadlines are controlled, not worker execution.
vi.useFakeTimers({ toFake: ['setTimeout', 'clearTimeout', 'setInterval', 'clearInterval'] })
})
afterEach(async () => {
const owned = { ctx, controller, run, release }
try {
owned.controller.abort('test cleanup')
owned.release?.()
} finally {
vi.useRealTimers()
}
try {
await owned.run
} finally {
await owned.ctx.fiber.dispose()
}
})
async function pendingBinding(): Promise<void> {
await ctx.plugin(NodeCodeRuntime, { computeMs: 1_000, maxWallMs: 30_000 })
let entered!: () => void
const ready = new Promise<void>((resolve) => { entered = resolve })
const binding = new Promise<string>((resolve) => { release = () => { resolve('slow-done') } })
run = ctx.codeRuntime.run({
program: 'return await tools.slow({})',
bindings: [{ global: 'tools', functions: { slow: () => { entered(); return binding } } }],
signal: controller.signal,
})
await Promise.race([
ready,
run.then((result) => { throw new Error('Worker settled before binding entry: ' + JSON.stringify(result)) }),
])
}
it('does not charge a binding wait longer than the compute budget', async () => {
await pendingBinding()
const settled = vi.fn()
void run!.then(settled, settled)
meter.sample.mockReturnValue({ active: 10, idle: 1_500, utilization: 10 / 1_510 })
await vi.advanceTimersByTimeAsync(1_500)
expect(meter.sample).toHaveBeenCalled()
expect(settled).not.toHaveBeenCalled()
release!()
expect(await run).toEqual({ logs: [], value: 'slow-done' })
})
it('expires active time even while a binding is pending', async () => {
await pendingBinding()
meter.sample.mockReturnValue({ active: 1_001, idle: 1_500, utilization: 1_001 / 2_501 })
await vi.advanceTimersByTimeAsync(25)
expect(await run).toEqual({ logs: [], error: { kind: 'timeout', message: 'compute budget exhausted (1000ms busy)' } })
})
it('expires the wall ceiling while active time remains below the compute budget', async () => {
await pendingBinding()
meter.sample.mockReturnValue({ active: 10, idle: 30_000, utilization: 10 / 30_010 })
await vi.advanceTimersByTimeAsync(30_000)
expect(await run).toEqual({ logs: [], error: { kind: 'timeout', message: 'wall-clock ceiling reached (30000ms)' } })
})
})
@@ -6,23 +6,29 @@ import { describe, expect, it } from 'vitest'
/**
* Keyless built-artifact smoke: plain Node imports the package by name through its exports map,
* then exercises type stripping, sibling `worker.cjs` loading, bindings, and logs. Unit tests use
* `src/worker.ts`; this pins the downstream `lib/index.js` path. It skips when `lib/` is absent,
* then exercises type stripping, sibling `process.js` loading, bindings, and logs. Unit tests use
* `src/process.ts`; this pins the downstream `lib/index.js` path. It skips when `lib/` is absent,
* and CI runs it after the build.
*/
const pkgDir = fileURLToPath(new URL('..', import.meta.url))
const built = ['lib/index.js', 'lib/worker.cjs'].every(file => existsSync(join(pkgDir, file)))
const built = ['lib/index.js', 'lib/process.js'].every(file => existsSync(join(pkgDir, file)))
&& existsSync(join(pkgDir, '../code-runtime/lib/index.js'))
describe.skipIf(!built)('built lib real load path (plain node)', () => {
it('runs a TypeScript program with a binding through lib/index.js and its lib/worker.cjs entry', async () => {
it('runs a TypeScript program with a binding through lib/index.js and its lib/process.js entry', async () => {
const script = `
const { Context } = await import('@deepseek-ai/cordis')
const { NodeCodeRuntime } = await import('@deepseek-ai/dsh-code-runtime-node')
const ctx = new Context()
for (const name of ['session-projection', 'fs-local', 'subprocess-local', 'sandbox-local']) {
const plugin = await import('@deepseek-ai/dsh-' + name)
await ctx.plugin(plugin.default, {})
}
const { default: SandboxPolicy } = await import('@deepseek-ai/dsh-sandbox-policy')
await ctx.plugin(SandboxPolicy, { mode: 'read-only' })
await ctx.plugin(NodeCodeRuntime, {})
const result = await ctx.codeRuntime.run({
const result = await ctx.codeRuntime.run(ctx.codeRuntime.resolve({
program: 'const doubled: number = await tools.double({ n: 21 }); console.log("halfway", doubled); let failure; try { await tools.fail({}) } catch (error) { failure = { typed: error instanceof ToolCallError, name: error.name, toolName: error.toolName, message: error.message } } return { doubled, failure };',
bindings: [{
global: 'tools',
@@ -32,9 +38,9 @@ describe.skipIf(!built)('built lib real load path (plain node)', () => {
},
errorClass: { name: 'ToolCallError', memberNameProperty: 'toolName' },
}],
})
}))
await ctx.fiber.dispose()
console.log(JSON.stringify(result))
process.exit(0)
`
const { exitCode, stdout, stderr } = await execa(process.execPath, ['--input-type=module', '-e', script], {
cwd: pkgDir,
@@ -0,0 +1,156 @@
import { Duplex, PassThrough } from 'node:stream'
import { describe, expect, it, onTestFinished } from 'vitest'
import { JsonChannel } from '../src/channel.ts'
function pair() {
const left = new PassThrough()
const right = new PassThrough()
const a = Duplex.from({ readable: left, writable: right })
const b = Duplex.from({ readable: right, writable: left })
// Duplex.from forwards peer destruction as ABORT_ERR; channel owners observe their own failures.
a.on('error', () => {})
b.on('error', () => {})
onTestFinished(() => { a.destroy(); b.destroy() })
return { a, b }
}
function frame(value: unknown): Buffer {
const body = Buffer.from(JSON.stringify(value))
const header = Buffer.alloc(4)
header.writeUInt32BE(body.length)
return Buffer.concat([header, body])
}
describe('bounded process frames', () => {
it('reassembles split binary headers and UTF-8 bodies', async () => {
const { a, b } = pair()
const received = Promise.withResolvers<unknown>()
const channel = new JsonChannel(a, 1000, value => received.resolve(value), error => received.reject(error))
onTestFinished(() => channel.close())
const bytes = frame({ text: '你好🙂' })
for (const byte of bytes) b.write(Buffer.from([byte]))
expect(await received.promise).toEqual({ text: '你好🙂' })
})
it('accepts consecutive frames and writes complete responses', async () => {
const { a, b } = pair()
const received: unknown[] = []
const done = Promise.withResolvers<undefined>()
const channel = new JsonChannel(a, 1000, (value) => {
received.push(value)
if (received.length === 2) done.resolve(undefined)
}, error => done.reject(error))
const peer = new JsonChannel(b, 1000, () => {}, error => done.reject(error))
onTestFinished(() => { channel.close(); peer.close() })
await Promise.all([peer.send({ n: 1 }), peer.send({ n: 2 })])
await done.promise
expect(received).toEqual([{ n: 1 }, { n: 2 }])
await peer.drain()
})
it.each([0, 65])('rejects a declared %i-byte frame before accepting its body', async (length) => {
const { a, b } = pair()
const failure = Promise.withResolvers<Error>()
const channel = new JsonChannel(a, 64, () => { throw new Error('must not dispatch') }, error => failure.resolve(error))
onTestFinished(() => channel.close())
const header = Buffer.alloc(4)
header.writeUInt32BE(length)
b.write(header)
expect((await failure.promise).message).toContain('control frame')
})
it('contains malformed JSON, invalid UTF-8 and receiver failures', async () => {
for (const payload of [Buffer.from('{'), Buffer.from([0xff]), Buffer.from('{}')]) {
const { a, b } = pair()
const failure = Promise.withResolvers<Error>()
const channel = new JsonChannel(a, 64, () => { throw new Error('receiver failed') }, error => failure.resolve(error))
const header = Buffer.alloc(4)
header.writeUInt32BE(payload.length)
b.write(Buffer.concat([header, payload]))
expect(await failure.promise).toBeInstanceOf(Error)
channel.close()
}
})
it('rejects oversized writes and closed channels', async () => {
const { a } = pair()
const channel = new JsonChannel(a, 20, () => {}, () => {})
await expect(channel.send({ text: 'x'.repeat(30) })).rejects.toThrow('queued bytes')
channel.close()
channel.close()
await expect(channel.send({})).rejects.toThrow('closed')
})
it('reports peer EOF while a program is active', async () => {
const { a, b } = pair()
const failure = Promise.withResolvers<Error>()
const channel = new JsonChannel(a, 64, () => {}, error => failure.resolve(error))
onTestFinished(() => channel.close())
b.end()
expect((await failure.promise).message).toContain('ended')
})
})
it('retains a partial header and partial payload across distinct stream deliveries', () => {
const { a } = pair()
const values: unknown[] = []
const channel = new JsonChannel(a, 64, (value) => { values.push(value) }, (error) => { throw error })
const bytes = frame({ value: 1 })
a.emit('data', bytes.subarray(0, 2))
a.emit('data', bytes.subarray(2, 6))
a.emit('data', bytes.subarray(6))
expect(values).toEqual([{ value: 1 }])
channel.close()
})
it('contains a receiver throwing a non-Error value', () => {
const { a } = pair()
const failures: string[] = []
const channel = new JsonChannel(a, 64, () => { throw 'receiver failure' }, (error) => { failures.push(error.message) })
a.emit('data', frame({}))
expect(failures).toEqual(['receiver failure'])
channel.close()
})
it('ignores callbacks already captured by an emission when an earlier listener closes the channel', () => {
for (const event of ['data', 'end']) {
const { a } = pair()
let failures = 0
const channel = new JsonChannel(a, 64, () => { throw new Error('closed channel dispatched') }, () => { failures += 1 })
a.prependOnceListener(event, () => channel.close())
a.emit(event, frame({}))
a.emit('error', new Error('late closed stream error'))
expect(failures).toBe(0)
}
})
it('rejects queued writes if the owner closes before they start', async () => {
const { a } = pair()
const channel = new JsonChannel(a, 64, () => {}, () => {})
const pending = channel.send({ n: 1 })
channel.close()
await expect(pending).rejects.toThrow('closed')
await channel.drain()
})
it('bounds queued frames while a receiver is not reading', async () => {
const stream = new Duplex({ read() {}, write(_chunk, _encoding, _callback) {} })
const channel = new JsonChannel(stream, 32, () => {}, () => {})
const pending = channel.send({ value: '1234567890' })
await expect(channel.send({ value: '1234567890' })).rejects.toThrow('queued bytes')
channel.close()
await expect(pending).rejects.toThrow('closed')
})
it.each(['error', 'close'])('settles a blocked write when the stream emits %s', async (event) => {
const entered = Promise.withResolvers<undefined>()
const stream = new Duplex({ read() {}, write(_chunk, _encoding, _callback) { entered.resolve(undefined) } })
let failure: Error | undefined
const channel = new JsonChannel(stream, 64, () => {}, (error) => { failure = error })
const pending = channel.send({ value: 1 })
await entered.promise
stream.emit(event, new Error('transport failed'))
await expect(pending).rejects.toThrow(event === 'error' ? 'transport failed' : 'closed')
if (event === 'error') expect(failure?.message).toBe('transport failed')
channel.close()
})
@@ -1,7 +1,7 @@
import { runInNewContext } from 'node:vm'
import { describe, expect, it } from 'vitest'
import { snapshotJsonValue } from '@deepseek-ai/dsh-util-values'
import { decodeWorkerJson, encodeWorkerJson, snapshotCodeJsonValue } from '../src/worker-json.ts'
import { decodeCodeJsonWire, encodeCodeJsonWire, snapshotCodeJsonValue } from '../src/json-wire.ts'
describe('snapshotCodeJsonValue', () => {
it('matches the canonical scalar boundary', () => {
@@ -176,9 +176,9 @@ describe('flat worker JSON wire', () => {
for (const value of values) {
const snapshot = snapshotCodeJsonValue(value)
expect(snapshot).not.toBeUndefined()
expect(decodeWorkerJson(encodeWorkerJson(snapshot!))).toEqual(snapshot)
expect(decodeCodeJsonWire(encodeCodeJsonWire(snapshot!))).toEqual(snapshot)
}
const decoded = decodeWorkerJson(encodeWorkerJson(snapshotCodeJsonValue(withPrototypeKey)!)) as Record<string, unknown>
const decoded = decodeCodeJsonWire(encodeCodeJsonWire(snapshotCodeJsonValue(withPrototypeKey)!)) as Record<string, unknown>
expect(Object.hasOwn(decoded, '__proto__')).toBe(true)
expect(decoded.__proto__).toEqual({ safe: true })
})
@@ -187,10 +187,10 @@ describe('flat worker JSON wire', () => {
let value: unknown = 'leaf'
for (let depth = 0; depth < 5_000; depth++) value = [value]
const snapshot = snapshotCodeJsonValue(value)!
const wire = encodeWorkerJson(snapshot)
const wire = encodeCodeJsonWire(snapshot)
expect(wire).toHaveLength(5_001)
let cursor = decodeWorkerJson(wire)
let cursor = decodeCodeJsonWire(wire)
for (let depth = 0; depth < 5_000; depth++) {
expect(Array.isArray(cursor)).toBe(true)
cursor = Array.isArray(cursor) ? cursor[0] : undefined
@@ -246,12 +246,12 @@ describe('flat worker JSON wire', () => {
[{ kind: 'object', keys: ['x'] }],
[{ kind: 'object', keys: [], extra: true }],
]) {
expect(decodeWorkerJson(value)).toBeUndefined()
expect(decodeCodeJsonWire(value)).toBeUndefined()
}
})
it('rejects invalid values passed through a forged static type', () => {
expect(() => encodeWorkerJson([undefined] as never)).toThrow(/sparse JSON array/)
expect(() => encodeWorkerJson({ value: undefined } as never)).toThrow(/undefined JSON object property/)
expect(() => encodeCodeJsonWire([undefined] as never)).toThrow(/sparse JSON array/)
expect(() => encodeCodeJsonWire({ value: undefined } as never)).toThrow(/undefined JSON object property/)
})
})
@@ -0,0 +1,66 @@
import { Duplex, PassThrough } from 'node:stream'
import { expect, it, onTestFinished } from 'vitest'
import { JsonChannel } from '../src/channel.ts'
import { runNodeMain } from '../src/process.ts'
import type { ProgramProcess } from '../src/process.ts'
import { decodeCodeJsonWire, encodeCodeJsonWire } from '../src/json-wire.ts'
function endpoints() {
const first = new PassThrough()
const second = new PassThrough()
const child = Duplex.from({ readable: first, writable: second })
const host = Duplex.from({ readable: second, writable: first })
child.on('error', () => {})
host.on('error', () => {})
onTestFinished(() => { child.destroy(); host.destroy() })
return { child, host }
}
function processState(): ProgramProcess { return { env: { FIXTURE_SECRET: 'test' }, stdout: { write: () => true }, stderr: { write: () => true }, exitCode: undefined } }
it('clears process environment, dispatches a binding reply and flushes the terminal frame', async () => {
const { child, host } = endpoints()
const state = processState()
const messages: Record<string, unknown>[] = []
const peer = new JsonChannel(host, 4096, (raw) => {
const message = raw as Record<string, unknown>
messages.push(message)
if (message.type === 'ready') void peer.send({ type: 'boot', data: { code: 'console.log("ready"); return await tools.echo({});', namespaces: [{ global: 'tools', names: ['echo'] }], maxOutputBytes: 1024 } })
if (message.type === 'call') void peer.send({ type: 'reply', id: message.id, ok: true, value: encodeCodeJsonWire(42) })
}, () => {})
onTestFinished(() => peer.close())
await runNodeMain(child, 4096, state)
expect(state.env).toEqual({})
expect(state.exitCode).toBeUndefined()
expect(decodeCodeJsonWire(messages.find(message => message.type === 'done')?.value)).toBe(42)
})
it.each([0, -1, 1.5, 4294967296])('rejects an invalid bootstrap frame limit %i', async (limit) => {
const { child } = endpoints()
await expect(runNodeMain(child, limit, processState())).rejects.toThrow('invalid control message limit')
})
it('rejects an unexpected first control frame', async () => {
const { child, host } = endpoints()
const peer = new JsonChannel(host, 4096, () => { void peer.send({ type: 'reply' }) }, () => {})
onTestFinished(() => peer.close())
await expect(runNodeMain(child, 4096, processState())).rejects.toThrow('expected program boot')
})
it('records an I/O failure before the boot frame', async () => {
const { child, host } = endpoints()
const state = processState()
const peer = new JsonChannel(host, 4096, () => { peer.close() }, () => {})
await expect(runNodeMain(child, 4096, state)).rejects.toBeInstanceOf(Error)
expect(state.exitCode).toBe(1)
})
it('contains program writes that exceed queued control output', async () => {
const { child, host } = endpoints()
const state = processState()
const peer = new JsonChannel(host, 1024, (raw) => {
if ((raw as { type: string }).type === 'ready') void peer.send({ type: 'boot', data: { code: 'for(let i=0;i<30;i++) console.log("x".repeat(100));', namespaces: [], maxOutputBytes: 8000 } })
}, () => {})
onTestFinished(() => peer.close())
await runNodeMain(child, 1024, state)
expect(state.exitCode).toBe(1)
})
@@ -0,0 +1,47 @@
import { spawn } from 'node:child_process'
import { copyFile, mkdtemp, rm } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { pathToFileURL } from 'node:url'
import type { Duplex } from 'node:stream'
import { expect, it, onTestFinished } from 'vitest'
import { JsonChannel } from '../src/channel.ts'
import { decodeCodeJsonWire, encodeCodeJsonWire } from '../src/json-wire.ts'
it('boots an unbuilt source closure outside the workspace and exchanges tool replies', async () => {
const directory = await mkdtemp(join(tmpdir(), 'dsh-node-source-'))
onTestFinished(async () => { await rm(directory, { recursive: true, force: true }) })
for (const file of ['process.ts', 'bootstrap.ts', 'channel.ts', 'json-wire.ts', 'output-json.ts', 'protocol.ts']) {
await copyFile(new URL(`../src/${file}`, import.meta.url), join(directory, file))
}
const source = `import {Socket} from 'node:net';import {runNodeMain} from ${JSON.stringify(pathToFileURL(join(directory, 'process.ts')).href)};await runNodeMain(new Socket({fd:7,readable:true,writable:true}),100000,process);`
const child = spawn(process.execPath, ['--input-type=module', '--eval', source], {
env: { PLACEHOLDER_SECRET: 'fixture-only' },
stdio: ['ignore', 'pipe', 'pipe', 'ignore', 'ignore', 'ignore', 'ignore', 'pipe'],
})
let stderr = ''
child.stderr?.on('data', (chunk: Buffer) => { stderr += chunk.toString('utf8') })
const finished = new Promise<void>((resolve) => { child.once('close', () => { resolve() }) })
const completed = Promise.withResolvers<unknown>()
child.once('error', error => completed.reject(error))
child.once('exit', (code) => { if (code !== 0) completed.reject(new Error(`child exit ${code}: ${stderr}`)) })
const channel = new JsonChannel(child.stdio[7] as Duplex, 100_000, (raw) => {
const message = raw as { type: string; id?: number; args?: unknown; value?: unknown; error?: unknown }
if (message.type === 'ready') {
void channel.send({ type: 'boot', data: {
code: 'const answer = await tools.echo({ n: 21 }); return { answer, env: { ...process.env } }',
namespaces: [{ global: 'tools', names: ['echo'] }],
maxOutputBytes: 10_000,
} }).catch(error => completed.reject(error))
} else if (message.type === 'call') {
expect(decodeCodeJsonWire(message.args)).toEqual({ n: 21 })
void channel.send({ type: 'reply', id: message.id, ok: true, value: encodeCodeJsonWire(42) }).catch(error => completed.reject(error))
} else if (message.type === 'done') {
if (message.error !== undefined) completed.reject(new Error(JSON.stringify(message.error)))
else completed.resolve(decodeCodeJsonWire(message.value))
}
}, error => completed.reject(error))
onTestFinished(async () => { channel.close(); child.kill(); await finished })
expect(await completed.promise).toEqual({ answer: 42, env: {} })
await finished
})
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,19 @@
import { Context } from '@deepseek-ai/cordis'
import FileSystem from '@deepseek-ai/dsh-fs-local'
import Subprocess from '@deepseek-ai/dsh-subprocess-local'
import Sandbox from '@deepseek-ai/dsh-sandbox-local'
import SandboxPolicy from '@deepseek-ai/dsh-sandbox-policy'
import SessionProjections from '@deepseek-ai/dsh-session-projection'
import type { SandboxMode } from '@deepseek-ai/dsh-sandbox'
import NodeRuntime from '../src/index.ts'
import type { Config } from '../src/index.ts'
export async function mountRuntime(ctx: Context, config: Config = {}, policy: { mode?: SandboxMode; workspaceRoot?: string } = {}) {
if (!ctx.get('fs')) await ctx.plugin(FileSystem)
if (!ctx.get('subprocess')) await ctx.plugin(Subprocess)
if (!ctx.get('sandbox')) await ctx.plugin(Sandbox, {})
if (!ctx.get('sessionProjections')) await ctx.plugin(SessionProjections)
if (!ctx.get('sandboxPolicy')) await ctx.plugin(SandboxPolicy, { mode: 'danger-full-access', ...policy })
await ctx.plugin(NodeRuntime, config)
return ctx.codeRuntime as NodeRuntime
}
@@ -1,39 +0,0 @@
import { copyFile, mkdtemp, rm } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { Worker } from 'node:worker_threads'
import { expect, it } from 'vitest'
import { decodeWorkerJson } from '../src/worker-json.ts'
/**
* Prove the unbuilt worker is a self-contained source closure. Copying it out
* of the workspace makes any package runtime import fail even when local
* `lib/` artifacts happen to exist.
*/
it('boots the source worker without workspace package outputs', async () => {
const directory = await mkdtemp(join(tmpdir(), 'dsh-code-source-worker-'))
let worker: Worker | undefined
try {
const files = ['worker.ts', 'bootstrap.ts', 'protocol.ts', 'worker-json.ts', 'output-json.ts']
await Promise.all(files.map(async (file) => {
await copyFile(new URL(`../src/${file}`, import.meta.url), join(directory, file))
}))
worker = new Worker(join(directory, 'worker.ts'), {
workerData: { code: 'return { answer: 42 }', namespaces: [], maxOutputBytes: 65_536 },
env: {},
execArgv: [],
})
const message = await new Promise<unknown>((resolve, reject) => {
worker?.once('message', resolve)
worker?.once('error', reject)
})
expect(message).toMatchObject({ type: 'done' })
const value = typeof message === 'object' && message !== null ? (message as { value?: unknown }).value : undefined
expect(decodeWorkerJson(value)).toEqual({ answer: 42 })
} finally {
if (worker) await worker.terminate()
await rm(directory, { recursive: true, force: true })
}
})