import { spawnSync } from 'node:child_process'; import { randomUUID } from 'node:crypto'; import { setTimeout as delay } from 'node:timers/promises'; const image = process.argv[2] ?? 'dsh-web:0.2.0-rc.2-1'; let interrupted = false; for (const signal of ['SIGINT', 'SIGTERM']) process.on(signal, () => { interrupted = true; }); function docker(args, input) { const result = spawnSync('docker', args, { input, encoding: 'utf8', timeout: 20000, maxBuffer: 16 * 1024 * 1024 }); if (result.error || result.status !== 0) throw new Error(`Docker ${args[0]} failed (output suppressed)`); return result.stdout + (args[0] === 'logs' ? result.stderr : ''); } function check(condition, message) { if (!condition) throw new Error(message); } async function probe(publicURL) { const name = `dsh-runtime-probe-${randomUUID()}`; let created = false; try { const id = docker(['image', 'inspect', '--format', '{{.Id}}', image]).trim(); docker(['create', '--name', name, '--pull', 'never', '--network', 'none', '--read-only', '--cap-drop', 'ALL', '--security-opt', 'no-new-privileges', '--user', '1000:1000', '--memory', '2g', '--cpus', '2', '--pids-limit', '256', '--tmpfs', '/tmp:rw,exec,nosuid,nodev,size=256m', '--tmpfs', '/data/dsh-home:rw,nosuid,nodev,uid=1000,gid=1000,size=512m', '--tmpfs', '/workspace:rw,nosuid,nodev,uid=1000,gid=1000,size=64m', id, 'web', '--no-open', '--port', '3080', ...(publicURL ? ['--public-url', publicURL, '--trusted-host', new URL(publicURL).host] : [])]); created = true; docker(['start', name]); let startupURL; for (let attempt = 0; attempt < 60; attempt++) { check(!interrupted, 'Interrupted'); const logs = docker(['logs', name]); const match = logs.match(/dsh web:\s*(https?:\/\/[^\s\x1b]+[?&]token=[^\s\x1b]+)/); if (match) { startupURL = new URL(match[1]); break; } if (docker(['inspect', '--format', '{{.State.Running}}', name]).trim() !== 'true') { if (logs.includes("unknown option '--public-url'")) console.log(JSON.stringify({mode: 'public-url', supported: false, reason: "unknown option '--public-url'"})); throw new Error('Runtime exited before readiness'); } await delay(1000); } check(startupURL?.searchParams.get('token'), 'No startup token found (logs suppressed)'); if (publicURL) check(startupURL.origin + startupURL.pathname === publicURL, 'Public startup URL differs'); const input = JSON.stringify({ token: startupURL.searchParams.get('token'), publicURL }); const source = String.raw` const fs = require('node:fs'); const path = require('node:path'); const { token, publicURL } = JSON.parse(fs.readFileSync(0, 'utf8')); const headers = publicURL ? {host: new URL(publicURL).host, 'x-forwarded-proto': 'https'} : {}; const request = (suffix, extra = {}) => fetch('http://127.0.0.1:8080' + suffix, {headers: {...headers, ...extra}, redirect: 'manual', signal: AbortSignal.timeout(10000)}); (async () => { const anonymous = await request('/'); const exchange = await request('/?token=' + encodeURIComponent(token)); const cookies = exchange.headers.getSetCookie(); const cookie = cookies.map(value => value.split(';')[0]).join('; '); const home = await request('/', {cookie}); const html = await home.text(); const scripts = [...html.matchAll(/]*src=["']([^"']+)/g)].map(match => match[1]); const evidence = []; function scan(dir) { for (const entry of fs.readdirSync(dir, {withFileTypes: true})) { const file = path.join(dir, entry.name); if (entry.isDirectory()) scan(file); else if (/\.(js|mjs|html)$/.test(file) && fs.statSync(file).size < 20000000) { const text = fs.readFileSync(file, 'utf8'); for (const needle of ['new WebSocket(', 'function remoteStreamUrl', 'REMOTE_STREAM_MUX_PATH =']) { let index = text.indexOf(needle); if (index >= 0) evidence.push({file, needle, line: text.slice(0,index).split('\n').length, context: text.slice(Math.max(0,index-180),index+260)}); } } } } scan('/usr/local/lib/node_modules/@deepseek-ai/dsh/node_modules/@deepseek-ai/dsh-api-gateway/lib'); const result = {anonymousStatus: anonymous.status, exchangeStatus: exchange.status, redirect: exchange.headers.get('location'), cookies: cookies.map(value => ({name: value.split('=')[0], attributes: value.split(';').slice(1).map(v => v.trim())})), authenticatedStatus: home.status, htmlConfig: [...html.matchAll(/.{0,120}(?:WebSocket|websocket|endpoint|base href|wsPath).{0,240}/g)].map(m => m[0]), scripts, evidence}; // Never serialize raw credentials, even if an upstream response reflects them. let output = JSON.stringify(result); for (const secret of [token, ...cookies.map(value => value.split(';')[0].slice(value.indexOf('=')+1))]) if (secret) output = output.split(secret).join('[REDACTED]'); console.log(output); })().catch(() => {console.error('In-container probe failed (details suppressed)'); process.exit(1);}); `; const result = JSON.parse(docker(['exec', '-i', name, 'node', '-e', source], input)); console.log(JSON.stringify({mode: publicURL ? 'public-url' : 'default', startupURL: startupURL.origin + startupURL.pathname + '?token=[REDACTED]', ...result}, null, 2)); check(result.anonymousStatus === 401, 'Expected anonymous HTTP 401'); check(result.cookies.length > 0, 'Token exchange did not set cookie'); check(result.authenticatedStatus === 200, 'Authenticated homepage was not HTTP 200'); check(result.evidence.some(item => item.needle === 'REMOTE_STREAM_MUX_PATH ='), 'WebSocket path constant not found'); } finally { if (created) { docker(['rm', '-f', name]); console.log(JSON.stringify({cleanup: 'removed', container: name})); } } } try { await probe(); // Optional capability check: current npm rc.2 intentionally fails this mode. if (process.argv.includes('--public-url')) await probe('https://probe.invalid/dsh/probe/'); } catch (error) { console.error(error.message); process.exitCode = 1; }