upstream-radar 0.43.4 → 0.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README-zh-CN.md +92 -77
  2. package/README.md +125 -94
  3. package/dist/src/cli.js +177 -5
  4. package/dist/src/cli.js.map +1 -1
  5. package/dist/src/dsh-compatibility-ledger.d.ts +2 -0
  6. package/dist/src/dsh-compatibility-ledger.d.ts.map +1 -1
  7. package/dist/src/dsh-compatibility-ledger.js +5 -0
  8. package/dist/src/dsh-compatibility-ledger.js.map +1 -1
  9. package/dist/src/dsh-directory-feed.d.ts +7 -3
  10. package/dist/src/dsh-directory-feed.d.ts.map +1 -1
  11. package/dist/src/dsh-directory-feed.js +59 -25
  12. package/dist/src/dsh-directory-feed.js.map +1 -1
  13. package/dist/src/dsh-headless-agent-plan.d.ts +7 -0
  14. package/dist/src/dsh-headless-agent-plan.d.ts.map +1 -1
  15. package/dist/src/dsh-headless-agent-plan.js +20 -1
  16. package/dist/src/dsh-headless-agent-plan.js.map +1 -1
  17. package/dist/src/dsh-install-observation.d.ts +6 -0
  18. package/dist/src/dsh-install-observation.d.ts.map +1 -1
  19. package/dist/src/dsh-install-observation.js +2 -2
  20. package/dist/src/dsh-install-observation.js.map +1 -1
  21. package/dist/src/dsh-install-plan.d.ts +7 -0
  22. package/dist/src/dsh-install-plan.d.ts.map +1 -1
  23. package/dist/src/dsh-install-plan.js +8 -2
  24. package/dist/src/dsh-install-plan.js.map +1 -1
  25. package/dist/src/dsh-surface-observation.d.ts +160 -0
  26. package/dist/src/dsh-surface-observation.d.ts.map +1 -0
  27. package/dist/src/dsh-surface-observation.js +1031 -0
  28. package/dist/src/dsh-surface-observation.js.map +1 -0
  29. package/dist/src/dsh-surface.d.ts +127 -0
  30. package/dist/src/dsh-surface.d.ts.map +1 -0
  31. package/dist/src/dsh-surface.js +667 -0
  32. package/dist/src/dsh-surface.js.map +1 -0
  33. package/dist/src/index.d.ts +2 -0
  34. package/dist/src/index.d.ts.map +1 -1
  35. package/dist/src/index.js +2 -0
  36. package/dist/src/index.js.map +1 -1
  37. package/dist/src/upstream-observer.d.ts +6 -0
  38. package/dist/src/upstream-observer.d.ts.map +1 -1
  39. package/dist/src/upstream-observer.js +206 -127
  40. package/dist/src/upstream-observer.js.map +1 -1
  41. package/dist/src/version.d.ts +1 -1
  42. package/dist/src/version.js +1 -1
  43. package/docs/README.zh-CN.md +19 -19
  44. package/package.json +1 -1
@@ -0,0 +1,1031 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { spawn } from 'node:child_process';
3
+ import { createRequire } from 'node:module';
4
+ import { constants } from 'node:fs';
5
+ import { chmod, mkdir, mkdtemp, open, rm, writeFile } from 'node:fs/promises';
6
+ import { tmpdir } from 'node:os';
7
+ import { basename, join, relative, resolve, sep } from 'node:path';
8
+ import { extractPnpmRequiredDependencyBuilds } from './dsh-install-observation.js';
9
+ import { parseNpmSpec } from './npm.js';
10
+ import { parseNpmTarball } from './tar.js';
11
+ import { TOOL_VERSION } from './version.js';
12
+ export const DSH_SURFACE_OBSERVATION_SCHEMA = 'upstream-radar.dsh-surface-observation/v1alpha1';
13
+ const EXACT_VERSION = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
14
+ const CASE_ID = /^[a-z0-9][a-z0-9._-]{0,63}$/;
15
+ const FINGERPRINT = /^sha256:[a-f0-9]{64}$/;
16
+ const BARE_SHA256 = /^[a-f0-9]{64}$/;
17
+ const PROFILE_NAME = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,63}$/;
18
+ const DEFAULT_TIMEOUT_MS = 300_000;
19
+ const MAX_COMMAND_OUTPUT_BYTES = 256 * 1024;
20
+ const MAX_ARTIFACT_BYTES = 64 * 1024 * 1024;
21
+ const MAX_ARTIFACT_UNPACKED_BYTES = 192 * 1024 * 1024;
22
+ const MAX_SURFACE_ERRORS = 32;
23
+ const MAX_ALLOWED_BUILDS = 32;
24
+ const MAX_EVIDENCE_TEXT = 2_048;
25
+ const MAX_TUI_BYTES = 256 * 1024;
26
+ const WEB_PORT = 30_880;
27
+ function bounded(value, maximum = MAX_EVIDENCE_TEXT) {
28
+ return value.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f-\u009f]/g, character => (`\\u${character.codePointAt(0)?.toString(16).padStart(4, '0') ?? '0000'}`)).slice(0, maximum);
29
+ }
30
+ function boundedList(values) {
31
+ return values.slice(0, MAX_SURFACE_ERRORS).map(value => bounded(value, 512));
32
+ }
33
+ export function evaluateDshWebEvidence(input) {
34
+ if (!input.driverAvailable) {
35
+ return { result: 'environment-unsupported', failedStage: 'surface', reason: 'the isolated runner has no usable Chromium/Playwright driver' };
36
+ }
37
+ if (!input.hostStarted) {
38
+ return { result: 'surface-incompatible', failedStage: 'host', reason: 'the exact DSH Web profile exited before exposing an HTTP surface' };
39
+ }
40
+ if (input.httpStatus === undefined || input.httpStatus < 200 || input.httpStatus >= 400) {
41
+ return { result: 'surface-incompatible', failedStage: 'host', reason: `the DSH Web endpoint returned HTTP ${input.httpStatus ?? 'unknown'}` };
42
+ }
43
+ if (!input.rootMounted) {
44
+ return { result: 'surface-incompatible', failedStage: 'surface', reason: 'the Web document loaded but the DSH root did not mount' };
45
+ }
46
+ if (!input.bootManifestPresent) {
47
+ return { result: 'surface-incompatible', failedStage: 'surface', reason: 'the Web document did not expose a valid DSH boot manifest' };
48
+ }
49
+ if (!input.pluginEntryPresent) {
50
+ return { result: 'surface-incompatible', failedStage: 'surface', reason: 'the declared plugin client entry is absent from the DSH boot manifest' };
51
+ }
52
+ if (input.pluginBundleStatus === undefined || input.pluginBundleStatus < 200 || input.pluginBundleStatus >= 400) {
53
+ return { result: 'surface-incompatible', failedStage: 'surface', reason: `the declared plugin client bundle returned HTTP ${input.pluginBundleStatus ?? 'unknown'}` };
54
+ }
55
+ if (!input.applicationMounted) {
56
+ const detail = input.bootFailureText === undefined ? '' : `: ${bounded(input.bootFailureText, 512)}`;
57
+ return { result: 'surface-incompatible', failedStage: 'surface', reason: `DSH Web did not hand off from its boot page to the assembled application${detail}` };
58
+ }
59
+ if (!input.pluginMaterialized) {
60
+ return { result: 'surface-incompatible', failedStage: 'surface', reason: 'DSH mounted the application but could not establish activation of the declared plugin client entry' };
61
+ }
62
+ if (input.pageErrors.length > 0) {
63
+ return { result: 'surface-incompatible', failedStage: 'interaction', reason: `the browser observed ${input.pageErrors.length} uncaught page error(s) after plugin materialization` };
64
+ }
65
+ return {
66
+ result: 'compatible',
67
+ failedStage: undefined,
68
+ reason: 'the Web host mounted and the declared plugin client entry was published, fetched, and materialized',
69
+ };
70
+ }
71
+ export function evaluateDshTuiEvidence(input) {
72
+ if (!input.driverAvailable) {
73
+ return { result: 'environment-unsupported', failedStage: 'surface', reason: 'the isolated runner has no usable pseudo-terminal driver' };
74
+ }
75
+ if (!input.frameObserved) {
76
+ return { result: 'surface-incompatible', failedStage: 'surface', reason: 'the DSH TUI exited or timed out before producing a terminal frame' };
77
+ }
78
+ if (!input.inputSent) {
79
+ return { result: 'unknown', failedStage: 'interaction', reason: 'a TUI frame was visible but the observer could not send bounded terminal input' };
80
+ }
81
+ if (!input.exitedAfterShutdown) {
82
+ return { result: 'surface-incompatible', failedStage: 'shutdown', reason: 'the TUI produced a frame but did not stop after the bounded shutdown input' };
83
+ }
84
+ return {
85
+ result: 'compatible',
86
+ failedStage: undefined,
87
+ reason: 'the TUI produced a real PTY frame, accepted input, and completed controlled shutdown',
88
+ };
89
+ }
90
+ export function dshSurfaceProfileStrategy(plane) {
91
+ return plane === 'web' ? 'initialize-stock-profile' : 'create-with-plugin-add';
92
+ }
93
+ function skippedStages() {
94
+ return {
95
+ runtime: { status: 'skipped' },
96
+ artifact: { status: 'skipped' },
97
+ profile: { status: 'skipped' },
98
+ install: { status: 'skipped' },
99
+ registration: { status: 'skipped' },
100
+ host: { status: 'skipped' },
101
+ surface: { status: 'skipped' },
102
+ interaction: { status: 'skipped' },
103
+ shutdown: { status: 'skipped' },
104
+ };
105
+ }
106
+ function emptyEvidence(plane) {
107
+ return plane === 'web'
108
+ ? {
109
+ plane: 'web',
110
+ url: `http://127.0.0.1:${WEB_PORT}/`,
111
+ rootMounted: false,
112
+ bootManifestPresent: false,
113
+ bootEntryIds: [],
114
+ pluginEntryPresent: false,
115
+ applicationMounted: false,
116
+ pluginMaterialized: false,
117
+ consoleErrors: [],
118
+ pageErrors: [],
119
+ failedRequests: [],
120
+ }
121
+ : {
122
+ plane: 'tui',
123
+ terminal: 'xterm-256color',
124
+ columns: 100,
125
+ rows: 32,
126
+ frameObserved: false,
127
+ inputSent: false,
128
+ exitedAfterShutdown: false,
129
+ normalizedFrame: '',
130
+ capturedBytes: 0,
131
+ truncated: false,
132
+ };
133
+ }
134
+ function finish(report, result, reason) {
135
+ report.completedAt = new Date().toISOString();
136
+ report.result = result;
137
+ report.reason = bounded(reason);
138
+ return report;
139
+ }
140
+ function commandStage(result) {
141
+ if (result.code === 0 && !result.timedOut && !result.outputExceeded && result.launchError === undefined) {
142
+ return { status: 'passed', code: 0 };
143
+ }
144
+ const detail = [result.launchError, result.stderr, result.stdout].filter(value => value !== undefined && value !== '').join('\n').trim();
145
+ const summary = result.timedOut
146
+ ? 'command timed out'
147
+ : result.outputExceeded
148
+ ? 'command exceeded the output budget'
149
+ : result.launchError !== undefined
150
+ ? 'command could not start'
151
+ : `command exited with ${result.code}`;
152
+ return {
153
+ status: 'failed',
154
+ code: result.code,
155
+ detail: bounded(detail === '' ? summary : `${summary}: ${detail}`),
156
+ ...(result.timedOut ? { timedOut: true } : {}),
157
+ ...(result.outputExceeded ? { outputExceeded: true } : {}),
158
+ };
159
+ }
160
+ function runCommand(command, args, cwd, env, timeoutMs) {
161
+ return new Promise(resolveResult => {
162
+ const child = spawn(command, args, { cwd, env, stdio: ['ignore', 'pipe', 'pipe'], shell: false, detached: true });
163
+ const stdout = [];
164
+ const stderr = [];
165
+ let bytes = 0;
166
+ let outputExceeded = false;
167
+ let timedOut = false;
168
+ let settled = false;
169
+ let launchError;
170
+ const terminate = () => {
171
+ try {
172
+ if (child.pid !== undefined)
173
+ process.kill(-child.pid, 'SIGKILL');
174
+ else
175
+ child.kill('SIGKILL');
176
+ }
177
+ catch {
178
+ child.kill('SIGKILL');
179
+ }
180
+ };
181
+ const finishResult = (code) => {
182
+ if (settled)
183
+ return;
184
+ settled = true;
185
+ clearTimeout(timer);
186
+ resolveResult({
187
+ code,
188
+ stdout: Buffer.concat(stdout).toString('utf8'),
189
+ stderr: Buffer.concat(stderr).toString('utf8'),
190
+ timedOut,
191
+ outputExceeded,
192
+ ...(launchError === undefined ? {} : { launchError }),
193
+ });
194
+ };
195
+ const collect = (target, chunk) => {
196
+ if (bytes + chunk.length > MAX_COMMAND_OUTPUT_BYTES) {
197
+ outputExceeded = true;
198
+ terminate();
199
+ return;
200
+ }
201
+ bytes += chunk.length;
202
+ target.push(chunk);
203
+ };
204
+ child.stdout.on('data', (chunk) => collect(stdout, chunk));
205
+ child.stderr.on('data', (chunk) => collect(stderr, chunk));
206
+ child.once('error', error => {
207
+ launchError = bounded(error.message);
208
+ finishResult(null);
209
+ });
210
+ child.once('close', code => finishResult(code));
211
+ const timer = setTimeout(() => {
212
+ timedOut = true;
213
+ terminate();
214
+ }, timeoutMs);
215
+ });
216
+ }
217
+ function controlledEnvironment(root, host) {
218
+ const env = {};
219
+ for (const key of ['PATH', 'LANG', 'LC_ALL', 'SSL_CERT_FILE', 'SSL_CERT_DIR', 'NODE_EXTRA_CA_CERTS']) {
220
+ if (host[key] !== undefined)
221
+ env[key] = host[key];
222
+ }
223
+ env.HOME = join(root, 'home');
224
+ env.DSH_HOME = join(root, 'dsh-home');
225
+ env.TMPDIR = join(root, 'tmp');
226
+ env.XDG_CACHE_HOME = join(root, 'cache');
227
+ env.XDG_CONFIG_HOME = join(root, 'config');
228
+ env.XDG_DATA_HOME = join(root, 'data');
229
+ env.NPM_CONFIG_CACHE = join(root, 'npm-cache');
230
+ env.NPM_CONFIG_USERCONFIG = join(root, 'controlled.npmrc');
231
+ env.NPM_CONFIG_GLOBALCONFIG = join(root, 'controlled-global.npmrc');
232
+ env.NPM_CONFIG_AUDIT = 'false';
233
+ env.NPM_CONFIG_FUND = 'false';
234
+ env.NPM_CONFIG_UPDATE_NOTIFIER = 'false';
235
+ env.PNPM_HOME = join(root, 'pnpm-home');
236
+ env.COREPACK_HOME = join(root, 'corepack-home');
237
+ env.COREPACK_ENABLE_DOWNLOAD_PROMPT = '0';
238
+ env.GIT_CONFIG_NOSYSTEM = '1';
239
+ env.GIT_CONFIG_GLOBAL = join(root, 'controlled.gitconfig');
240
+ env.GIT_TERMINAL_PROMPT = '0';
241
+ env.DSH_PERMISSION_MODE = 'read-only';
242
+ env.DSH_TELEMETRY_MODE = 'DISABLED';
243
+ env.CI = 'true';
244
+ env.NO_COLOR = '1';
245
+ return env;
246
+ }
247
+ function scriptPolicy(environment, enabled) {
248
+ const value = enabled ? 'false' : 'true';
249
+ return {
250
+ ...environment,
251
+ NPM_CONFIG_IGNORE_SCRIPTS: value,
252
+ npm_config_ignore_scripts: value,
253
+ PNPM_CONFIG_IGNORE_SCRIPTS: value,
254
+ };
255
+ }
256
+ function normalizeAllowedBuilds(values) {
257
+ if (values === undefined)
258
+ return [];
259
+ if (values.length > MAX_ALLOWED_BUILDS)
260
+ throw new Error(`DSH surface observation accepts at most ${MAX_ALLOWED_BUILDS} approved dependency builds`);
261
+ const names = new Set();
262
+ for (const value of values) {
263
+ if (value.length > 214 || !/^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/.test(value)) {
264
+ throw new Error(`invalid approved dependency build package name: ${JSON.stringify(value)}`);
265
+ }
266
+ names.add(value);
267
+ }
268
+ return [...names].sort();
269
+ }
270
+ function pnpmSurfaceBuildApproval(approvedPackage, artifact, artifactName, profileDirectory) {
271
+ if (approvedPackage !== artifactName)
272
+ return approvedPackage;
273
+ const artifactPath = relative(profileDirectory, artifact.path).split(sep).join('/');
274
+ return `${artifactName}@file:${artifactPath}`;
275
+ }
276
+ function dshArgs(dshVersion, args) {
277
+ return ['dlx', `--package=@deepseek-ai/dsh@${dshVersion}`, 'dsh', ...args];
278
+ }
279
+ async function readRegularFile(path, maximum) {
280
+ const handle = await open(path, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
281
+ try {
282
+ const metadata = await handle.stat();
283
+ if (!metadata.isFile())
284
+ throw new Error(`${basename(path)} is not a regular file`);
285
+ if (metadata.size > maximum)
286
+ throw new Error(`${basename(path)} exceeds ${maximum} bytes`);
287
+ const buffer = Buffer.alloc(metadata.size);
288
+ let offset = 0;
289
+ while (offset < buffer.length) {
290
+ const current = await handle.read(buffer, offset, buffer.length - offset, offset);
291
+ if (current.bytesRead === 0)
292
+ break;
293
+ offset += current.bytesRead;
294
+ }
295
+ if (offset !== buffer.length)
296
+ throw new Error(`${basename(path)} changed while it was read`);
297
+ return buffer;
298
+ }
299
+ finally {
300
+ await handle.close();
301
+ }
302
+ }
303
+ async function packedArtifact(result, directory, expectedName, expectedVersion) {
304
+ if (commandStage(result).status !== 'passed')
305
+ throw new Error(commandStage(result).detail ?? 'npm pack failed');
306
+ let output;
307
+ try {
308
+ output = JSON.parse(result.stdout);
309
+ }
310
+ catch {
311
+ throw new Error('npm pack did not return valid JSON');
312
+ }
313
+ if (!Array.isArray(output) || output.length !== 1 || typeof output[0] !== 'object' || output[0] === null) {
314
+ throw new Error('npm pack returned an unexpected result');
315
+ }
316
+ const item = output[0];
317
+ const filename = item.filename;
318
+ if (typeof filename !== 'string' || filename !== basename(filename) || !filename.endsWith('.tgz')) {
319
+ throw new Error('npm pack returned an unsafe artifact filename');
320
+ }
321
+ const path = resolve(directory, filename);
322
+ if (!path.startsWith(`${resolve(directory)}${sep}`))
323
+ throw new Error('npm pack artifact escaped its directory');
324
+ const bytes = await readRegularFile(path, MAX_ARTIFACT_BYTES);
325
+ const tarball = parseNpmTarball(bytes, { maxFileBytes: MAX_ARTIFACT_BYTES, maxUnpackedBytes: MAX_ARTIFACT_UNPACKED_BYTES });
326
+ const manifestEntry = tarball.entries.find(entry => entry.path === 'package.json' && entry.type === 'file');
327
+ if (manifestEntry?.contents === undefined)
328
+ throw new Error('packed artifact has no package.json');
329
+ let manifest;
330
+ try {
331
+ const parsed = JSON.parse(manifestEntry.contents.toString('utf8'));
332
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
333
+ throw new Error('not an object');
334
+ manifest = parsed;
335
+ }
336
+ catch {
337
+ throw new Error('packed artifact package.json is not valid JSON');
338
+ }
339
+ if (manifest.name !== expectedName || manifest.version !== expectedVersion) {
340
+ throw new Error(`packed artifact identity does not match ${expectedName}@${expectedVersion}`);
341
+ }
342
+ const dsh = typeof manifest.dsh === 'object' && manifest.dsh !== null && !Array.isArray(manifest.dsh)
343
+ ? manifest.dsh
344
+ : undefined;
345
+ const bundle = typeof dsh?.bundle === 'object' && dsh.bundle !== null && !Array.isArray(dsh.bundle)
346
+ ? dsh.bundle
347
+ : undefined;
348
+ if (typeof bundle?.patch !== 'string' || bundle.patch.trim() === '')
349
+ throw new Error('packed artifact does not declare dsh.bundle.patch');
350
+ return {
351
+ path,
352
+ filename,
353
+ sha256: createHash('sha256').update(bytes).digest('hex'),
354
+ bytes: bytes.length,
355
+ ...(typeof item.integrity === 'string' ? { integrity: bounded(item.integrity, 1_024) } : {}),
356
+ };
357
+ }
358
+ async function registeredBundle(dshHome, profileName, packageName) {
359
+ try {
360
+ const contents = await readRegularFile(join(dshHome, 'profiles', profileName, 'package.json'), 4 * 1024 * 1024);
361
+ const manifest = JSON.parse(contents.toString('utf8'));
362
+ const dsh = typeof manifest.dsh === 'object' && manifest.dsh !== null && !Array.isArray(manifest.dsh)
363
+ ? manifest.dsh
364
+ : undefined;
365
+ const profile = typeof dsh?.profile === 'object' && dsh.profile !== null && !Array.isArray(dsh.profile)
366
+ ? dsh.profile
367
+ : undefined;
368
+ return Array.isArray(profile?.bundles) && profile.bundles.includes(packageName);
369
+ }
370
+ catch {
371
+ return false;
372
+ }
373
+ }
374
+ function safeArtifactName(caseId, suffix) {
375
+ return `${caseId}.${suffix}`;
376
+ }
377
+ function isLoopbackUrl(value) {
378
+ try {
379
+ const url = new URL(value);
380
+ return ['data:', 'blob:', 'about:'].includes(url.protocol)
381
+ || ['127.0.0.1', 'localhost', '[::1]', '::1'].includes(url.hostname);
382
+ }
383
+ catch {
384
+ return false;
385
+ }
386
+ }
387
+ function startHost(command, args, cwd, env) {
388
+ const child = spawn(command, args, { cwd, env, stdio: ['ignore', 'pipe', 'pipe'], shell: false, detached: true });
389
+ const chunks = [];
390
+ let bytes = 0;
391
+ let exceeded = false;
392
+ let didExit = false;
393
+ let exitCode = null;
394
+ let errorMessage;
395
+ const collect = (chunk) => {
396
+ if (exceeded)
397
+ return;
398
+ if (bytes + chunk.length > MAX_COMMAND_OUTPUT_BYTES) {
399
+ exceeded = true;
400
+ return;
401
+ }
402
+ bytes += chunk.length;
403
+ chunks.push(chunk);
404
+ };
405
+ child.stdout?.on('data', collect);
406
+ child.stderr?.on('data', collect);
407
+ child.once('error', error => { errorMessage = bounded(error.message); });
408
+ child.once('close', code => {
409
+ didExit = true;
410
+ exitCode = code;
411
+ });
412
+ return {
413
+ child,
414
+ output: () => Buffer.concat(chunks).toString('utf8'),
415
+ outputExceeded: () => exceeded,
416
+ exited: () => didExit,
417
+ code: () => exitCode,
418
+ launchError: () => errorMessage,
419
+ stop: () => new Promise(resolveStopped => {
420
+ if (didExit) {
421
+ resolveStopped(true);
422
+ return;
423
+ }
424
+ const timer = setTimeout(() => {
425
+ try {
426
+ if (child.pid !== undefined)
427
+ process.kill(-child.pid, 'SIGKILL');
428
+ else
429
+ child.kill('SIGKILL');
430
+ }
431
+ catch {
432
+ child.kill('SIGKILL');
433
+ }
434
+ resolveStopped(false);
435
+ }, 5_000);
436
+ child.once('close', () => {
437
+ clearTimeout(timer);
438
+ resolveStopped(true);
439
+ });
440
+ try {
441
+ if (child.pid !== undefined)
442
+ process.kill(-child.pid, 'SIGTERM');
443
+ else
444
+ child.kill('SIGTERM');
445
+ }
446
+ catch {
447
+ child.kill('SIGTERM');
448
+ }
449
+ }),
450
+ };
451
+ }
452
+ async function waitForHttp(url, host, timeoutMs) {
453
+ const deadline = Date.now() + timeoutMs;
454
+ while (Date.now() < deadline && !host.exited() && host.launchError() === undefined && !host.outputExceeded()) {
455
+ try {
456
+ const response = await fetch(url, { redirect: 'manual', signal: AbortSignal.timeout(2_000) });
457
+ return response.status;
458
+ }
459
+ catch {
460
+ await new Promise(resolveWait => setTimeout(resolveWait, 250));
461
+ }
462
+ }
463
+ return undefined;
464
+ }
465
+ function loadDriver(root, packageName) {
466
+ if (root === undefined)
467
+ return undefined;
468
+ try {
469
+ const require = createRequire(join(resolve(root), 'package.json'));
470
+ return require(packageName);
471
+ }
472
+ catch {
473
+ return undefined;
474
+ }
475
+ }
476
+ async function observeWebSurface(input) {
477
+ const evidence = input.report.evidence;
478
+ const playwright = loadDriver(input.driverRoot, 'playwright-core');
479
+ if (playwright === undefined) {
480
+ input.report.stages.surface = { status: 'failed', detail: 'playwright-core is unavailable' };
481
+ return evaluateDshWebEvidence({
482
+ driverAvailable: false,
483
+ hostStarted: false,
484
+ rootMounted: false,
485
+ bootManifestPresent: false,
486
+ pluginEntryPresent: false,
487
+ applicationMounted: false,
488
+ pluginMaterialized: false,
489
+ consoleErrors: [],
490
+ pageErrors: [],
491
+ failedRequests: [],
492
+ });
493
+ }
494
+ const host = startHost(input.pnpmCommand, dshArgs(input.report.dshVersion, [
495
+ '--profile', input.report.profile,
496
+ '--host', '127.0.0.1',
497
+ '--port', String(WEB_PORT),
498
+ ]), input.cwd, input.env);
499
+ const hostLogPath = join(input.artifactsDirectory, safeArtifactName(input.report.caseId, 'host.log'));
500
+ evidence.hostLog = basename(hostLogPath);
501
+ let browser;
502
+ let context;
503
+ let traceStarted = false;
504
+ try {
505
+ const httpStatus = await waitForHttp(evidence.url, host, Math.min(input.timeoutMs, 120_000));
506
+ if (httpStatus !== undefined)
507
+ evidence.httpStatus = httpStatus;
508
+ const hostStarted = httpStatus !== undefined;
509
+ input.report.stages.host = hostStarted
510
+ ? { status: 'passed' }
511
+ : {
512
+ status: 'failed',
513
+ code: host.code(),
514
+ detail: bounded(host.launchError() ?? (host.output() || 'DSH Web did not expose an HTTP endpoint')),
515
+ ...(host.outputExceeded() ? { outputExceeded: true } : {}),
516
+ };
517
+ if (!hostStarted) {
518
+ return evaluateDshWebEvidence({
519
+ driverAvailable: true,
520
+ hostStarted: false,
521
+ rootMounted: false,
522
+ bootManifestPresent: false,
523
+ pluginEntryPresent: false,
524
+ applicationMounted: false,
525
+ pluginMaterialized: false,
526
+ consoleErrors: [],
527
+ pageErrors: [],
528
+ failedRequests: [],
529
+ });
530
+ }
531
+ browser = await playwright.chromium.launch({
532
+ headless: true,
533
+ ...(input.chromiumExecutable === undefined ? {} : { executablePath: input.chromiumExecutable }),
534
+ args: ['--no-sandbox', '--disable-dev-shm-usage', '--disable-crashpad', '--disable-crash-reporter'],
535
+ env: Object.fromEntries(Object.entries(input.env).filter((entry) => typeof entry[1] === 'string')),
536
+ });
537
+ context = await browser.newContext({ serviceWorkers: 'block' });
538
+ const tracePath = join(input.artifactsDirectory, safeArtifactName(input.report.caseId, 'trace.zip'));
539
+ await context.tracing.start({ screenshots: true, snapshots: true, sources: false });
540
+ traceStarted = true;
541
+ const page = await context.newPage();
542
+ const consoleErrors = [];
543
+ const pageErrors = [];
544
+ const failedRequests = [];
545
+ const blockedExternalRequests = [];
546
+ page.on('console', message => {
547
+ if (message.type() === 'error')
548
+ consoleErrors.push(message.text());
549
+ });
550
+ page.on('pageerror', error => pageErrors.push(error.message));
551
+ page.on('requestfailed', request => failedRequests.push(`${request.url()}: ${request.failure()?.errorText ?? 'failed'}`));
552
+ await page.route('**/*', async (route) => {
553
+ const url = route.request().url();
554
+ if (isLoopbackUrl(url))
555
+ await route.continue();
556
+ else {
557
+ blockedExternalRequests.push(url);
558
+ await route.abort('blockedbyclient');
559
+ }
560
+ });
561
+ const response = await page.goto(evidence.url, { waitUntil: 'domcontentloaded', timeout: Math.min(input.timeoutMs, 90_000) });
562
+ if (response !== null)
563
+ evidence.httpStatus = response.status();
564
+ evidence.title = bounded(await page.title(), 256);
565
+ const initial = await page.evaluate(runtimeId => {
566
+ const value = globalThis;
567
+ const entries = Array.isArray(value.__DSH_BOOT__?.entries) ? value.__DSH_BOOT__?.entries ?? [] : [];
568
+ const entry = entries.find(item => item.id === runtimeId);
569
+ const ids = entries.map(item => typeof item.id === 'string' ? item.id : '').filter(id => id !== '');
570
+ return {
571
+ rootMounted: (value.document?.querySelector('#root')?.childElementCount ?? 0) > 0,
572
+ bootManifestPresent: entries.length > 0,
573
+ // Put community modules first so the bounded diagnostic list is still
574
+ // useful when DSH contributes dozens of built-in entries.
575
+ bootEntryIds: [...new Set([...ids.filter(id => !id.startsWith('@deepseek-ai/')), ...ids])].slice(0, 32),
576
+ pluginEntryPresent: entry !== undefined,
577
+ pluginBundleUrl: typeof entry?.url === 'string' ? entry.url : undefined,
578
+ };
579
+ }, input.report.runtimeId);
580
+ evidence.rootMounted = initial.rootMounted;
581
+ evidence.bootManifestPresent = initial.bootManifestPresent;
582
+ evidence.bootEntryIds = boundedList(initial.bootEntryIds);
583
+ evidence.pluginEntryPresent = initial.pluginEntryPresent;
584
+ if (initial.pluginBundleUrl !== undefined) {
585
+ evidence.pluginBundleUrl = new URL(initial.pluginBundleUrl, evidence.url).href;
586
+ try {
587
+ evidence.pluginBundleStatus = (await fetch(evidence.pluginBundleUrl, { signal: AbortSignal.timeout(10_000) })).status;
588
+ }
589
+ catch {
590
+ evidence.pluginBundleStatus = 0;
591
+ }
592
+ }
593
+ if (initial.pluginEntryPresent) {
594
+ try {
595
+ await page.waitForFunction(() => {
596
+ const value = globalThis;
597
+ const root = value.document?.querySelector('#root');
598
+ return (root?.childElementCount ?? 0) > 0
599
+ && root?.querySelector(':scope > [data-dsh-boot]') == null;
600
+ }, undefined, { timeout: Math.min(input.timeoutMs, 60_000) });
601
+ evidence.applicationMounted = true;
602
+ // DSH's Web boot contract audits every graph entry as ACTIVE before
603
+ // the UI renderer replaces [data-dsh-boot]. This is a public,
604
+ // observable boundary; the old __DSH_MODULES__ page global no longer
605
+ // exists in current DSH releases.
606
+ evidence.pluginMaterialized = true;
607
+ }
608
+ catch {
609
+ evidence.applicationMounted = false;
610
+ evidence.pluginMaterialized = false;
611
+ evidence.bootFailureText = bounded(await page.evaluate(() => {
612
+ const value = globalThis;
613
+ return value.document?.querySelector('#root > [data-dsh-boot]')?.textContent ?? '';
614
+ }, undefined), 512);
615
+ }
616
+ }
617
+ evidence.consoleErrors = boundedList(consoleErrors);
618
+ evidence.pageErrors = boundedList(pageErrors);
619
+ evidence.failedRequests = boundedList(failedRequests);
620
+ evidence.blockedExternalRequests = boundedList(blockedExternalRequests);
621
+ const screenshotPath = join(input.artifactsDirectory, safeArtifactName(input.report.caseId, 'png'));
622
+ await page.screenshot({ path: screenshotPath, fullPage: true });
623
+ await chmod(screenshotPath, 0o644);
624
+ evidence.screenshot = basename(screenshotPath);
625
+ await context.tracing.stop({ path: tracePath });
626
+ await chmod(tracePath, 0o644);
627
+ traceStarted = false;
628
+ evidence.trace = basename(tracePath);
629
+ const evaluation = evaluateDshWebEvidence({
630
+ driverAvailable: true,
631
+ hostStarted: true,
632
+ ...(evidence.httpStatus === undefined ? {} : { httpStatus: evidence.httpStatus }),
633
+ rootMounted: evidence.rootMounted,
634
+ bootManifestPresent: evidence.bootManifestPresent,
635
+ pluginEntryPresent: evidence.pluginEntryPresent,
636
+ ...(evidence.pluginBundleStatus === undefined ? {} : { pluginBundleStatus: evidence.pluginBundleStatus }),
637
+ applicationMounted: evidence.applicationMounted,
638
+ pluginMaterialized: evidence.pluginMaterialized,
639
+ ...(evidence.bootFailureText === undefined ? {} : { bootFailureText: evidence.bootFailureText }),
640
+ consoleErrors: evidence.consoleErrors,
641
+ pageErrors: evidence.pageErrors,
642
+ failedRequests: evidence.failedRequests,
643
+ });
644
+ input.report.stages.surface = evaluation.failedStage === 'surface'
645
+ ? { status: 'failed', detail: evaluation.reason }
646
+ : { status: 'passed' };
647
+ input.report.stages.interaction = evaluation.failedStage === 'interaction'
648
+ ? { status: 'failed', detail: evaluation.reason }
649
+ : { status: 'passed' };
650
+ return evaluation;
651
+ }
652
+ catch (error) {
653
+ const reason = `the browser driver failed while observing the Web surface: ${bounded(error instanceof Error ? error.message : String(error))}`;
654
+ input.report.stages.surface = { status: 'failed', detail: reason };
655
+ return { result: 'unknown', failedStage: 'surface', reason };
656
+ }
657
+ finally {
658
+ if (traceStarted && context !== undefined) {
659
+ await context.tracing.stop({ path: join(input.artifactsDirectory, safeArtifactName(input.report.caseId, 'trace.zip')) }).catch(() => undefined);
660
+ }
661
+ await context?.close().catch(() => undefined);
662
+ await browser?.close().catch(() => undefined);
663
+ const stopped = await host.stop();
664
+ input.report.stages.shutdown = stopped
665
+ ? { status: 'passed' }
666
+ : { status: 'failed', detail: 'DSH Web required a forced shutdown' };
667
+ await writeFile(hostLogPath, bounded(host.output(), MAX_COMMAND_OUTPUT_BYTES), { mode: 0o644 }).catch(() => undefined);
668
+ }
669
+ }
670
+ function normalizeTerminalFrame(raw) {
671
+ return raw
672
+ .replace(/\u001b\][^\u0007]*(?:\u0007|\u001b\\)/g, '')
673
+ .replace(/\u001b\[[0-?]*[ -/]*[@-~]/g, '')
674
+ .replace(/\r/g, '\n')
675
+ .replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, '')
676
+ .split('\n')
677
+ .map(line => line.trimEnd())
678
+ .filter(line => line.trim() !== '')
679
+ .slice(-32)
680
+ .join('\n')
681
+ .slice(0, 8_192);
682
+ }
683
+ async function observeTuiSurface(input) {
684
+ const evidence = input.report.evidence;
685
+ const driver = loadDriver(input.driverRoot, 'node-pty');
686
+ if (driver === undefined) {
687
+ input.report.stages.surface = { status: 'failed', detail: 'node-pty is unavailable' };
688
+ return evaluateDshTuiEvidence({ driverAvailable: false, frameObserved: false, inputSent: false, exitedAfterShutdown: false });
689
+ }
690
+ input.report.stages.host = { status: 'passed' };
691
+ const terminalEnvironment = Object.fromEntries(Object.entries({
692
+ ...input.env,
693
+ TERM: evidence.terminal,
694
+ COLORTERM: 'truecolor',
695
+ FORCE_COLOR: '3',
696
+ CI: 'false',
697
+ NO_COLOR: undefined,
698
+ }).filter((entry) => typeof entry[1] === 'string'));
699
+ let terminal;
700
+ try {
701
+ terminal = driver.spawn(input.pnpmCommand, dshArgs(input.report.dshVersion, ['--profile', input.report.profile]), {
702
+ name: evidence.terminal,
703
+ cols: evidence.columns,
704
+ rows: evidence.rows,
705
+ cwd: input.cwd,
706
+ env: terminalEnvironment,
707
+ });
708
+ }
709
+ catch (error) {
710
+ input.report.stages.host = { status: 'failed', detail: bounded(error instanceof Error ? error.message : String(error)) };
711
+ return { result: 'unknown', failedStage: 'host', reason: 'the PTY driver could not start the exact DSH profile' };
712
+ }
713
+ let raw = '';
714
+ let capturedBytes = 0;
715
+ let truncated = false;
716
+ let inputSent = false;
717
+ let shutdownRequested = false;
718
+ let exitCode;
719
+ let signal;
720
+ let exited = false;
721
+ let forced = false;
722
+ const transcriptPath = join(input.artifactsDirectory, safeArtifactName(input.report.caseId, 'ansi'));
723
+ evidence.transcript = basename(transcriptPath);
724
+ await new Promise(resolveObservation => {
725
+ let settled = false;
726
+ let shutdownTimer;
727
+ const settle = () => {
728
+ if (settled)
729
+ return;
730
+ settled = true;
731
+ clearTimeout(deadline);
732
+ if (shutdownTimer !== undefined)
733
+ clearTimeout(shutdownTimer);
734
+ resolveObservation();
735
+ };
736
+ const requestShutdown = () => {
737
+ if (shutdownRequested)
738
+ return;
739
+ shutdownRequested = true;
740
+ terminal.write('\u0003');
741
+ // dsh-TUI intentionally requires a double Ctrl-C: the first press clears
742
+ // input/arms exit and the second performs the graceful shutdown. Keep the
743
+ // pair close enough to exercise that public interaction contract.
744
+ setTimeout(() => {
745
+ if (exited)
746
+ return;
747
+ try {
748
+ terminal.write('\u0003');
749
+ }
750
+ catch { /* the PTY exited between taps */ }
751
+ }, 150);
752
+ shutdownTimer = setTimeout(() => {
753
+ forced = true;
754
+ try {
755
+ terminal.kill('SIGKILL');
756
+ }
757
+ catch {
758
+ settle();
759
+ }
760
+ }, 5_000);
761
+ };
762
+ terminal.onData(data => {
763
+ const dataBytes = Buffer.byteLength(data);
764
+ if (capturedBytes + dataBytes <= MAX_TUI_BYTES) {
765
+ raw += data;
766
+ capturedBytes += dataBytes;
767
+ }
768
+ else {
769
+ truncated = true;
770
+ }
771
+ const normalized = normalizeTerminalFrame(raw);
772
+ const hasTerminalControl = /\u001b\[[0-?]*[ -/]*[@-~]/.test(raw);
773
+ if (!evidence.frameObserved && hasTerminalControl && normalized.replace(/\s/g, '').length >= 40) {
774
+ evidence.frameObserved = true;
775
+ terminal.write('\u000c');
776
+ inputSent = true;
777
+ setTimeout(requestShutdown, 750);
778
+ }
779
+ });
780
+ terminal.onExit(event => {
781
+ exited = true;
782
+ exitCode = event.exitCode;
783
+ signal = event.signal;
784
+ settle();
785
+ });
786
+ const deadline = setTimeout(() => {
787
+ requestShutdown();
788
+ setTimeout(settle, 5_250);
789
+ }, Math.min(input.timeoutMs, 120_000));
790
+ });
791
+ evidence.inputSent = inputSent;
792
+ evidence.exitedAfterShutdown = exited && shutdownRequested && !forced;
793
+ if (exitCode !== undefined)
794
+ evidence.exitCode = exitCode;
795
+ if (signal !== undefined)
796
+ evidence.signal = signal;
797
+ evidence.normalizedFrame = normalizeTerminalFrame(raw);
798
+ evidence.capturedBytes = capturedBytes;
799
+ evidence.truncated = truncated;
800
+ await writeFile(transcriptPath, raw, { mode: 0o644 });
801
+ const evaluation = evaluateDshTuiEvidence({
802
+ driverAvailable: true,
803
+ frameObserved: evidence.frameObserved,
804
+ inputSent: evidence.inputSent,
805
+ exitedAfterShutdown: evidence.exitedAfterShutdown,
806
+ ...(evidence.exitCode === undefined ? {} : { exitCode: evidence.exitCode }),
807
+ });
808
+ input.report.stages.surface = evaluation.failedStage === 'surface'
809
+ ? { status: 'failed', detail: evaluation.reason }
810
+ : { status: 'passed' };
811
+ input.report.stages.interaction = evaluation.failedStage === 'interaction'
812
+ ? { status: 'failed', detail: evaluation.reason }
813
+ : evidence.inputSent ? { status: 'passed' } : { status: 'skipped' };
814
+ input.report.stages.shutdown = evaluation.failedStage === 'shutdown'
815
+ ? { status: 'failed', detail: evaluation.reason }
816
+ : evidence.exitedAfterShutdown ? { status: 'passed' } : { status: 'skipped' };
817
+ return evaluation;
818
+ }
819
+ function validateObservationOptions(options) {
820
+ const plugin = parseNpmSpec(options.packageSpec);
821
+ if (!EXACT_VERSION.test(options.dshVersion))
822
+ throw new Error('DSH surface observation requires an exact DSH version');
823
+ if (!CASE_ID.test(options.caseId) || !CASE_ID.test(options.sourceCaseId))
824
+ throw new Error('DSH surface observation case ids must be short lowercase labels');
825
+ if (!FINGERPRINT.test(options.sourceFingerprint) || !FINGERPRINT.test(options.contractFingerprint))
826
+ throw new Error('DSH surface observation fingerprints must be sha256 digests');
827
+ if (!BARE_SHA256.test(options.expectedArtifactSha256))
828
+ throw new Error('DSH surface observation requires the expected artifact SHA-256');
829
+ if (!PROFILE_NAME.test(options.profile))
830
+ throw new Error('DSH surface observation profile must be a short safe profile name');
831
+ if (options.runtimeId.trim() === '' || options.runtimeId.length > 214)
832
+ throw new Error('DSH surface observation runtimeId must be a bounded package id');
833
+ if (options.plane === 'web' && options.profile !== 'web')
834
+ throw new Error('Web surface observations must use the official web profile');
835
+ if (options.plane === 'web' && options.runtimeId !== plugin.name) {
836
+ throw new Error('Web runtimeId must equal the exact npm package name; Cordis loader row ids are not browser module ids');
837
+ }
838
+ if (options.plane === 'tui' && options.profile === 'web')
839
+ throw new Error('TUI surface observations cannot use the reserved web profile');
840
+ if (!options.allowExecution)
841
+ throw new Error('DSH surface observation requires explicit execution consent');
842
+ if (!['github-actions-hosted-runner', 'firecracker', 'other'].includes(options.isolationProvider))
843
+ throw new Error('unsupported isolation provider');
844
+ }
845
+ export async function observeDshPluginSurface(options) {
846
+ validateObservationOptions(options);
847
+ const allowedBuilds = normalizeAllowedBuilds(options.allowedBuilds);
848
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
849
+ if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 30_000 || timeoutMs > 600_000) {
850
+ throw new Error('DSH surface observation timeout must be between 30000 and 600000 milliseconds');
851
+ }
852
+ const hostEnvironment = options.hostEnvironment ?? process.env;
853
+ if (hostEnvironment.UPSTREAM_RADAR_ISOLATED_RUNNER !== '1') {
854
+ throw new Error('DSH surface observation requires UPSTREAM_RADAR_ISOLATED_RUNNER=1');
855
+ }
856
+ const parsedSpec = parseNpmSpec(options.packageSpec);
857
+ const startedAt = new Date().toISOString();
858
+ const nodeVersion = process.version.replace(/^v/, '');
859
+ const nodeMajor = Number(nodeVersion.split('.')[0]);
860
+ const report = {
861
+ schema: DSH_SURFACE_OBSERVATION_SCHEMA,
862
+ tool: { name: 'upstream-radar', version: TOOL_VERSION },
863
+ probe: 'dsh-surface',
864
+ scope: 'surface-runtime-behavior',
865
+ startedAt,
866
+ completedAt: startedAt,
867
+ caseId: options.caseId,
868
+ sourceCaseId: options.sourceCaseId,
869
+ sourceFingerprint: options.sourceFingerprint,
870
+ contractFingerprint: options.contractFingerprint,
871
+ plugin: `${parsedSpec.name}@${parsedSpec.version}`,
872
+ dshVersion: options.dshVersion,
873
+ plane: options.plane,
874
+ profile: options.profile,
875
+ runtimeId: options.runtimeId,
876
+ runtime: { nodeMajor, nodeVersion, platform: process.platform, architecture: process.arch },
877
+ artifact: {},
878
+ stages: skippedStages(),
879
+ evidence: emptyEvidence(options.plane),
880
+ result: 'unknown',
881
+ reason: 'surface observation did not complete',
882
+ boundary: {
883
+ isolationProviderClaim: options.isolationProvider,
884
+ isolationVerifiedByRadar: false,
885
+ disposableEnvironmentRequired: true,
886
+ inheritedHostSecrets: false,
887
+ externalBrowserRequestsBlocked: options.plane === 'web',
888
+ approvedDependencyBuilds: allowedBuilds,
889
+ note: 'The caller supplies a disposable VM and restricted container. Radar passes no repository or model secrets, binds the run to exact artifact bytes, blocks non-loopback browser requests, and collects bounded smoke evidence. This is compatibility evidence, not a malicious-code safety certificate.',
890
+ },
891
+ };
892
+ const sandboxRoot = await mkdtemp(join(tmpdir(), 'upstream-radar-dsh-surface-'));
893
+ const artifactDirectory = join(sandboxRoot, 'artifact');
894
+ const artifactsDirectory = resolve(options.artifactsDirectory ?? join(sandboxRoot, 'evidence'));
895
+ const environment = controlledEnvironment(sandboxRoot, hostEnvironment);
896
+ const noScriptsEnvironment = scriptPolicy(environment, false);
897
+ const scriptsEnvironment = scriptPolicy(environment, true);
898
+ const npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm';
899
+ const pnpmCommand = process.platform === 'win32' ? 'pnpm.cmd' : 'pnpm';
900
+ try {
901
+ await Promise.all([
902
+ mkdir(artifactDirectory, { recursive: true, mode: 0o700 }),
903
+ mkdir(artifactsDirectory, { recursive: true, mode: 0o700 }),
904
+ mkdir(environment.HOME, { recursive: true, mode: 0o700 }),
905
+ mkdir(environment.DSH_HOME, { recursive: true, mode: 0o700 }),
906
+ mkdir(environment.TMPDIR, { recursive: true, mode: 0o700 }),
907
+ mkdir(environment.XDG_CACHE_HOME, { recursive: true, mode: 0o700 }),
908
+ mkdir(environment.XDG_CONFIG_HOME, { recursive: true, mode: 0o700 }),
909
+ mkdir(environment.XDG_DATA_HOME, { recursive: true, mode: 0o700 }),
910
+ ]);
911
+ await Promise.all([
912
+ writeFile(join(sandboxRoot, 'controlled.npmrc'), 'registry=https://registry.npmjs.org/\naudit=false\nfund=false\nupdate-notifier=false\n', { mode: 0o600 }),
913
+ writeFile(join(sandboxRoot, 'controlled-global.npmrc'), '', { mode: 0o600 }),
914
+ writeFile(join(sandboxRoot, 'controlled.gitconfig'), '', { mode: 0o600 }),
915
+ ]);
916
+ const runtime = await runCommand(pnpmCommand, ['--version'], sandboxRoot, noScriptsEnvironment, timeoutMs);
917
+ report.stages.runtime = commandStage(runtime);
918
+ const pnpmVersion = runtime.stdout.trim();
919
+ if (report.stages.runtime.status !== 'passed' || !EXACT_VERSION.test(pnpmVersion)) {
920
+ report.stages.runtime = { ...report.stages.runtime, status: 'failed', detail: report.stages.runtime.detail ?? 'pnpm did not return an exact version' };
921
+ return finish(report, 'unknown', 'the package-manager runtime could not be established');
922
+ }
923
+ report.runtime.pnpmVersion = pnpmVersion;
924
+ const packed = await runCommand(npmCommand, [
925
+ 'pack', report.plugin, '--ignore-scripts', '--pack-destination', '.', '--json', '--silent',
926
+ ], artifactDirectory, noScriptsEnvironment, timeoutMs);
927
+ let artifact;
928
+ try {
929
+ artifact = await packedArtifact(packed, artifactDirectory, parsedSpec.name, parsedSpec.version);
930
+ }
931
+ catch (error) {
932
+ report.stages.artifact = { ...commandStage(packed), status: 'failed', detail: bounded(error instanceof Error ? error.message : String(error)) };
933
+ return finish(report, 'unknown', 'the exact npm artifact could not be established');
934
+ }
935
+ report.artifact = {
936
+ sha256: artifact.sha256,
937
+ bytes: artifact.bytes,
938
+ ...(artifact.integrity === undefined ? {} : { integrity: artifact.integrity }),
939
+ };
940
+ report.stages.artifact = { status: 'passed', code: packed.code };
941
+ if (artifact.sha256 !== options.expectedArtifactSha256) {
942
+ report.stages.artifact = { status: 'failed', detail: `artifact sha256:${artifact.sha256} does not match scheduled sha256:${options.expectedArtifactSha256}` };
943
+ return finish(report, 'unknown', 'the downloaded artifact bytes do not match the source observation');
944
+ }
945
+ const profileStrategy = dshSurfaceProfileStrategy(report.plane);
946
+ if (profileStrategy === 'initialize-stock-profile') {
947
+ const profile = await runCommand(pnpmCommand, dshArgs(report.dshVersion, ['--profile', report.profile, '--help']), artifactDirectory, noScriptsEnvironment, timeoutMs);
948
+ report.stages.profile = commandStage(profile);
949
+ if (report.stages.profile.status !== 'passed')
950
+ return finish(report, 'unknown', 'the exact DSH runtime could not initialize the declared profile');
951
+ }
952
+ else {
953
+ report.stages.profile = { status: 'skipped', detail: 'the custom TUI profile must be created by dsh plugin add' };
954
+ }
955
+ const install = await runCommand(pnpmCommand, dshArgs(report.dshVersion, [
956
+ 'plugin', '--profile', report.profile, 'add', artifact.path,
957
+ ...allowedBuilds.map(name => `--allow-build=${pnpmSurfaceBuildApproval(name, artifact, parsedSpec.name, join(environment.DSH_HOME, 'profiles', report.profile))}`),
958
+ ]), artifactDirectory, scriptsEnvironment, timeoutMs);
959
+ report.stages.install = commandStage(install);
960
+ if (install.timedOut || install.outputExceeded || install.launchError !== undefined) {
961
+ return finish(report, 'unknown', 'the profile install did not produce a bounded result');
962
+ }
963
+ if (install.code !== 0) {
964
+ const requiredBuilds = extractPnpmRequiredDependencyBuilds(`${install.stderr}\n${install.stdout}`, parsedSpec.name);
965
+ if (requiredBuilds.length > 0) {
966
+ return finish(report, 'environment-unsupported', `the declared ${report.plane} environment still requires explicit dependency-build approval: ${requiredBuilds.join(', ')}`);
967
+ }
968
+ return finish(report, 'surface-incompatible', `the exact plugin could not be installed into the declared ${report.plane} profile`);
969
+ }
970
+ if (profileStrategy === 'create-with-plugin-add') {
971
+ report.stages.profile = { status: 'passed', detail: 'dsh plugin add created the custom TUI profile' };
972
+ }
973
+ const registered = await registeredBundle(environment.DSH_HOME, report.profile, parsedSpec.name);
974
+ report.stages.registration = registered
975
+ ? { status: 'passed' }
976
+ : { status: 'failed', detail: `the profile did not register ${parsedSpec.name}` };
977
+ if (!registered)
978
+ return finish(report, 'surface-incompatible', 'DSH accepted the install command but did not register the plugin in the declared profile');
979
+ const evaluation = report.plane === 'web'
980
+ ? await observeWebSurface({
981
+ report,
982
+ pnpmCommand,
983
+ cwd: artifactDirectory,
984
+ env: scriptsEnvironment,
985
+ timeoutMs,
986
+ artifactsDirectory,
987
+ ...(options.driverRoot === undefined ? {} : { driverRoot: options.driverRoot }),
988
+ ...(options.chromiumExecutable === undefined ? {} : { chromiumExecutable: options.chromiumExecutable }),
989
+ })
990
+ : await observeTuiSurface({
991
+ report,
992
+ pnpmCommand,
993
+ cwd: artifactDirectory,
994
+ env: scriptsEnvironment,
995
+ timeoutMs,
996
+ artifactsDirectory,
997
+ ...(options.driverRoot === undefined ? {} : { driverRoot: options.driverRoot }),
998
+ });
999
+ return finish(report, evaluation.result, evaluation.reason);
1000
+ }
1001
+ catch (error) {
1002
+ return finish(report, 'unknown', `the bounded surface observer failed: ${bounded(error instanceof Error ? error.message : String(error))}`);
1003
+ }
1004
+ finally {
1005
+ await rm(sandboxRoot, { recursive: true, force: true, maxRetries: 2 }).catch(() => undefined);
1006
+ }
1007
+ }
1008
+ export function renderDshSurfaceObservation(report) {
1009
+ const lines = [
1010
+ 'DSH execution-plane observation',
1011
+ `Case: ${report.caseId}`,
1012
+ `Plugin: ${report.plugin}`,
1013
+ `DSH: ${report.dshVersion}`,
1014
+ `Plane: ${report.plane} (profile ${report.profile}, runtime id ${report.runtimeId})`,
1015
+ `Artifact: ${report.artifact.sha256 === undefined ? 'not established' : `sha256:${report.artifact.sha256}`}`,
1016
+ `Result: ${report.result.toUpperCase()} — ${report.reason}`,
1017
+ '',
1018
+ ];
1019
+ for (const [name, stage] of Object.entries(report.stages)) {
1020
+ lines.push(` ${name}: ${stage.status}${stage.detail === undefined ? '' : ` (${stage.detail})`}`);
1021
+ }
1022
+ if (report.evidence.plane === 'web') {
1023
+ lines.push('', `Web: HTTP ${report.evidence.httpStatus ?? 'unknown'}, root ${report.evidence.rootMounted ? 'mounted' : 'missing'}, entry ${report.evidence.pluginEntryPresent ? 'present' : 'missing'}, app ${report.evidence.applicationMounted ? 'mounted' : 'still booting'}, module ${report.evidence.pluginMaterialized ? 'activated' : 'not activated'}`, `Browser errors: ${report.evidence.pageErrors.length} page, ${report.evidence.consoleErrors.length} console, ${report.evidence.failedRequests.length} failed request(s)`);
1024
+ }
1025
+ else {
1026
+ lines.push('', `TUI: frame ${report.evidence.frameObserved ? 'observed' : 'missing'}, input ${report.evidence.inputSent ? 'sent' : 'not sent'}, shutdown ${report.evidence.exitedAfterShutdown ? 'controlled' : 'not controlled'}`, `Captured: ${report.evidence.capturedBytes} byte(s)${report.evidence.truncated ? ' (truncated)' : ''}`);
1027
+ }
1028
+ lines.push('', report.boundary.note);
1029
+ return `${lines.join('\n')}\n`;
1030
+ }
1031
+ //# sourceMappingURL=dsh-surface-observation.js.map