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.
- package/README-zh-CN.md +92 -77
- package/README.md +125 -94
- package/dist/src/cli.js +177 -5
- package/dist/src/cli.js.map +1 -1
- package/dist/src/dsh-compatibility-ledger.d.ts +2 -0
- package/dist/src/dsh-compatibility-ledger.d.ts.map +1 -1
- package/dist/src/dsh-compatibility-ledger.js +5 -0
- package/dist/src/dsh-compatibility-ledger.js.map +1 -1
- package/dist/src/dsh-directory-feed.d.ts +7 -3
- package/dist/src/dsh-directory-feed.d.ts.map +1 -1
- package/dist/src/dsh-directory-feed.js +59 -25
- package/dist/src/dsh-directory-feed.js.map +1 -1
- package/dist/src/dsh-headless-agent-plan.d.ts +7 -0
- package/dist/src/dsh-headless-agent-plan.d.ts.map +1 -1
- package/dist/src/dsh-headless-agent-plan.js +20 -1
- package/dist/src/dsh-headless-agent-plan.js.map +1 -1
- package/dist/src/dsh-install-observation.d.ts +6 -0
- package/dist/src/dsh-install-observation.d.ts.map +1 -1
- package/dist/src/dsh-install-observation.js +2 -2
- package/dist/src/dsh-install-observation.js.map +1 -1
- package/dist/src/dsh-install-plan.d.ts +7 -0
- package/dist/src/dsh-install-plan.d.ts.map +1 -1
- package/dist/src/dsh-install-plan.js +8 -2
- package/dist/src/dsh-install-plan.js.map +1 -1
- package/dist/src/dsh-surface-observation.d.ts +160 -0
- package/dist/src/dsh-surface-observation.d.ts.map +1 -0
- package/dist/src/dsh-surface-observation.js +1031 -0
- package/dist/src/dsh-surface-observation.js.map +1 -0
- package/dist/src/dsh-surface.d.ts +127 -0
- package/dist/src/dsh-surface.d.ts.map +1 -0
- package/dist/src/dsh-surface.js +667 -0
- package/dist/src/dsh-surface.js.map +1 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/upstream-observer.d.ts +6 -0
- package/dist/src/upstream-observer.d.ts.map +1 -1
- package/dist/src/upstream-observer.js +206 -127
- package/dist/src/upstream-observer.js.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/docs/README.zh-CN.md +19 -19
- package/package.json +1 -1
|
@@ -0,0 +1,667 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { parseDshCompatibilityLedger } from './dsh-compatibility-ledger.js';
|
|
3
|
+
import { DSH_SURFACE_OBSERVATION_SCHEMA, } from './dsh-surface-observation.js';
|
|
4
|
+
import { parseNpmSpec } from './npm.js';
|
|
5
|
+
export const DSH_SURFACE_TARGETS_SCHEMA = 'upstream-radar.dsh-surface-targets/v1alpha1';
|
|
6
|
+
export const DSH_SURFACE_LEDGER_SCHEMA = 'upstream-radar.dsh-surface-ledger/v1alpha1';
|
|
7
|
+
export const DSH_SURFACE_IR_SCHEMA = 'upstream-radar.dsh-surface-ir/v1alpha1';
|
|
8
|
+
const EXACT_VERSION = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
|
|
9
|
+
const CASE_ID = /^[a-z0-9][a-z0-9._-]{0,63}$/;
|
|
10
|
+
const PROFILE_NAME = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,63}$/;
|
|
11
|
+
const FINGERPRINT = /^sha256:[a-f0-9]{64}$/;
|
|
12
|
+
const BARE_SHA256 = /^[a-f0-9]{64}$/;
|
|
13
|
+
const RESULTS = new Set(['compatible', 'surface-incompatible', 'environment-unsupported', 'unknown']);
|
|
14
|
+
const STAGE_STATUS = new Set(['passed', 'failed', 'skipped']);
|
|
15
|
+
const DEFAULT_REFRESH_AFTER_HOURS = 7 * 24;
|
|
16
|
+
const MAX_TARGETS = 32;
|
|
17
|
+
const MAX_LEDGER_ENTRIES = 128;
|
|
18
|
+
const SURFACE_CONTRACT_REVISION = 'dsh-surface-contract/4';
|
|
19
|
+
function record(value, label) {
|
|
20
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
21
|
+
throw new Error(`${label} must be an object`);
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
function boundedString(value, label, maximum) {
|
|
25
|
+
if (typeof value !== 'string' || value.trim() === '' || value.length > maximum) {
|
|
26
|
+
throw new Error(`${label} must be a non-empty string no longer than ${maximum} characters`);
|
|
27
|
+
}
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
function caseId(value, label) {
|
|
31
|
+
const parsed = boundedString(value, label, 64);
|
|
32
|
+
if (!CASE_ID.test(parsed))
|
|
33
|
+
throw new Error(`${label} must be a short lowercase label`);
|
|
34
|
+
return parsed;
|
|
35
|
+
}
|
|
36
|
+
function profileName(value, label) {
|
|
37
|
+
const parsed = boundedString(value, label, 64);
|
|
38
|
+
if (!PROFILE_NAME.test(parsed))
|
|
39
|
+
throw new Error(`${label} must be a short safe DSH profile name`);
|
|
40
|
+
return parsed;
|
|
41
|
+
}
|
|
42
|
+
function fingerprint(value, label) {
|
|
43
|
+
const parsed = boundedString(value, label, 71);
|
|
44
|
+
if (!FINGERPRINT.test(parsed))
|
|
45
|
+
throw new Error(`${label} must be a sha256 fingerprint`);
|
|
46
|
+
return parsed;
|
|
47
|
+
}
|
|
48
|
+
function bareSha256(value, label) {
|
|
49
|
+
const parsed = boundedString(value, label, 64);
|
|
50
|
+
if (!BARE_SHA256.test(parsed))
|
|
51
|
+
throw new Error(`${label} must be a SHA-256 hex digest`);
|
|
52
|
+
return parsed;
|
|
53
|
+
}
|
|
54
|
+
function exactSpec(value, label) {
|
|
55
|
+
const parsed = parseNpmSpec(boundedString(value, label, 512));
|
|
56
|
+
if (!EXACT_VERSION.test(parsed.version))
|
|
57
|
+
throw new Error(`${label} must be an exact npm package coordinate`);
|
|
58
|
+
return `${parsed.name}@${parsed.version}`;
|
|
59
|
+
}
|
|
60
|
+
function exactVersion(value, label) {
|
|
61
|
+
const parsed = boundedString(value, label, 256);
|
|
62
|
+
if (!EXACT_VERSION.test(parsed))
|
|
63
|
+
throw new Error(`${label} must be an exact semantic version`);
|
|
64
|
+
return parsed;
|
|
65
|
+
}
|
|
66
|
+
function executionPlane(value, label) {
|
|
67
|
+
if (value !== 'web' && value !== 'tui')
|
|
68
|
+
throw new Error(`${label} must be web or tui`);
|
|
69
|
+
return value;
|
|
70
|
+
}
|
|
71
|
+
function nodeMajor(value, label) {
|
|
72
|
+
if (!Number.isSafeInteger(value) || value < 16 || value > 40) {
|
|
73
|
+
throw new Error(`${label} must be a supported Node.js major version`);
|
|
74
|
+
}
|
|
75
|
+
return value;
|
|
76
|
+
}
|
|
77
|
+
function isoDate(value, label) {
|
|
78
|
+
const parsed = boundedString(value, label, 64);
|
|
79
|
+
if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/.test(parsed) || Number.isNaN(Date.parse(parsed))) {
|
|
80
|
+
throw new Error(`${label} must be an ISO timestamp`);
|
|
81
|
+
}
|
|
82
|
+
return parsed;
|
|
83
|
+
}
|
|
84
|
+
export function parseDshSurfaceTargets(input) {
|
|
85
|
+
const root = record(input, 'DSH surface targets');
|
|
86
|
+
if (root.schema !== DSH_SURFACE_TARGETS_SCHEMA)
|
|
87
|
+
throw new Error(`DSH surface targets schema must be ${DSH_SURFACE_TARGETS_SCHEMA}`);
|
|
88
|
+
const refreshAfterHours = root.refreshAfterHours === undefined ? DEFAULT_REFRESH_AFTER_HOURS : Number(root.refreshAfterHours);
|
|
89
|
+
if (!Number.isSafeInteger(refreshAfterHours) || refreshAfterHours < 1 || refreshAfterHours > 90 * 24) {
|
|
90
|
+
throw new Error('DSH surface target refreshAfterHours must be an integer between 1 and 2160');
|
|
91
|
+
}
|
|
92
|
+
if (!Array.isArray(root.surfaces) || root.surfaces.length === 0 || root.surfaces.length > MAX_TARGETS) {
|
|
93
|
+
throw new Error(`DSH surface targets must contain between 1 and ${MAX_TARGETS} surfaces`);
|
|
94
|
+
}
|
|
95
|
+
const ids = new Set();
|
|
96
|
+
const pairs = new Set();
|
|
97
|
+
const surfaces = root.surfaces.map((value, index) => {
|
|
98
|
+
const item = record(value, `surfaces[${index}]`);
|
|
99
|
+
const id = caseId(item.id, `surfaces[${index}].id`);
|
|
100
|
+
if (ids.has(id))
|
|
101
|
+
throw new Error(`duplicate DSH surface target id: ${id}`);
|
|
102
|
+
ids.add(id);
|
|
103
|
+
const sourceCaseId = caseId(item.sourceCaseId, `surfaces[${index}].sourceCaseId`);
|
|
104
|
+
const plane = executionPlane(item.plane, `surfaces[${index}].plane`);
|
|
105
|
+
const profile = profileName(item.profile, `surfaces[${index}].profile`);
|
|
106
|
+
if (plane === 'web' && profile !== 'web')
|
|
107
|
+
throw new Error('a Web target must use the official web profile');
|
|
108
|
+
if (plane === 'tui' && profile === 'web')
|
|
109
|
+
throw new Error('a TUI target cannot use the reserved web profile');
|
|
110
|
+
const runtimeId = boundedString(item.runtimeId, `surfaces[${index}].runtimeId`, 214);
|
|
111
|
+
const reason = boundedString(item.reason, `surfaces[${index}].reason`, 2_048);
|
|
112
|
+
const pair = `${sourceCaseId}\u0000${plane}`;
|
|
113
|
+
if (pairs.has(pair))
|
|
114
|
+
throw new Error(`duplicate DSH surface plane for source case: ${sourceCaseId} ${plane}`);
|
|
115
|
+
pairs.add(pair);
|
|
116
|
+
return { id, sourceCaseId, plane, profile, runtimeId, reason };
|
|
117
|
+
});
|
|
118
|
+
surfaces.sort((left, right) => left.id.localeCompare(right.id));
|
|
119
|
+
return { schema: DSH_SURFACE_TARGETS_SCHEMA, refreshAfterHours, surfaces };
|
|
120
|
+
}
|
|
121
|
+
function digest(value) {
|
|
122
|
+
return `sha256:${createHash('sha256').update(JSON.stringify(value)).digest('hex')}`;
|
|
123
|
+
}
|
|
124
|
+
function sourceFingerprint(entry) {
|
|
125
|
+
return digest({
|
|
126
|
+
caseId: entry.caseId,
|
|
127
|
+
plugin: entry.plugin,
|
|
128
|
+
dshVersion: entry.dshVersion,
|
|
129
|
+
nodeMajor: entry.runtime.nodeMajor,
|
|
130
|
+
artifactSha256: entry.artifact.sha256,
|
|
131
|
+
staticFingerprint: entry.staticFingerprint,
|
|
132
|
+
contractFingerprint: entry.contractFingerprint,
|
|
133
|
+
approvedDependencyBuilds: entry.approvedDependencyBuilds ?? [],
|
|
134
|
+
observerSchema: entry.observer.schema,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
function contractFingerprint(target, entry, runtimeId) {
|
|
138
|
+
return digest({
|
|
139
|
+
revision: SURFACE_CONTRACT_REVISION,
|
|
140
|
+
plane: target.plane,
|
|
141
|
+
profile: target.profile,
|
|
142
|
+
runtimeId,
|
|
143
|
+
nodeMajor: entry.runtime.nodeMajor,
|
|
144
|
+
approvedDependencyBuilds: entry.approvedDependencyBuilds ?? [],
|
|
145
|
+
web: target.plane === 'web'
|
|
146
|
+
? { browser: 'chromium', root: '#root', manifest: '__DSH_BOOT__', bootHandoff: '[data-dsh-boot] removed after graph activation', externalRequests: 'blocked' }
|
|
147
|
+
: undefined,
|
|
148
|
+
tui: target.plane === 'tui'
|
|
149
|
+
? { terminal: 'xterm-256color', columns: 100, rows: 32, frame: 'ansi-and-printable', interaction: 'ctrl-l', shutdown: 'double-ctrl-c' }
|
|
150
|
+
: undefined,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
function desiredCase(target, source) {
|
|
154
|
+
// The headless result may legitimately remain unknown when the unresolved
|
|
155
|
+
// edges belong to the Web or TUI host that this target is about to provide.
|
|
156
|
+
// An explicit surface target needs only exact artifact provenance here; the
|
|
157
|
+
// plane observer independently reinstalls the package, verifies this digest,
|
|
158
|
+
// and establishes its own result.
|
|
159
|
+
if (source.artifact.sha256 === undefined || !BARE_SHA256.test(source.artifact.sha256))
|
|
160
|
+
return undefined;
|
|
161
|
+
if (source.result === 'build-approval-required' || source.result === 'runtime-incompatible'
|
|
162
|
+
|| source.result === 'install-failed' || source.result === 'load-failed')
|
|
163
|
+
return undefined;
|
|
164
|
+
if (source.result === 'unknown' && source.resolution?.runtimeGraph?.digest === undefined)
|
|
165
|
+
return undefined;
|
|
166
|
+
// DSH's browser manifest is keyed by the client package name. The Cordis
|
|
167
|
+
// patch row id is a different namespace and may remain stable while a
|
|
168
|
+
// package migrates. Deriving this value from the exact observed coordinate
|
|
169
|
+
// prevents a stale loader id from becoming a false incompatibility.
|
|
170
|
+
const runtimeId = target.plane === 'web' ? parseNpmSpec(source.plugin).name : target.runtimeId;
|
|
171
|
+
return {
|
|
172
|
+
id: target.id,
|
|
173
|
+
sourceCaseId: source.caseId,
|
|
174
|
+
plugin: source.plugin,
|
|
175
|
+
dshVersion: source.dshVersion,
|
|
176
|
+
nodeMajor: source.runtime.nodeMajor,
|
|
177
|
+
plane: target.plane,
|
|
178
|
+
profile: target.profile,
|
|
179
|
+
runtimeId,
|
|
180
|
+
artifactSha256: source.artifact.sha256,
|
|
181
|
+
allowedBuilds: [...(source.approvedDependencyBuilds ?? [])].sort().join(','),
|
|
182
|
+
sourceFingerprint: sourceFingerprint(source),
|
|
183
|
+
contractFingerprint: contractFingerprint(target, source, runtimeId),
|
|
184
|
+
reasons: [],
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
export function emptyDshSurfaceLedger() {
|
|
188
|
+
return { schema: DSH_SURFACE_LEDGER_SCHEMA, entries: [] };
|
|
189
|
+
}
|
|
190
|
+
function parseStage(value, label) {
|
|
191
|
+
const item = record(value, label);
|
|
192
|
+
if (!STAGE_STATUS.has(item.status))
|
|
193
|
+
throw new Error(`${label}.status is unsupported`);
|
|
194
|
+
const status = item.status;
|
|
195
|
+
const code = item.code === undefined || item.code === null
|
|
196
|
+
? item.code
|
|
197
|
+
: Number.isSafeInteger(item.code) ? item.code : (() => { throw new Error(`${label}.code must be an integer or null`); })();
|
|
198
|
+
const detail = item.detail === undefined ? undefined : boundedString(item.detail, `${label}.detail`, 2_048);
|
|
199
|
+
return {
|
|
200
|
+
status,
|
|
201
|
+
...(code === undefined ? {} : { code }),
|
|
202
|
+
...(detail === undefined ? {} : { detail }),
|
|
203
|
+
...(item.timedOut === true ? { timedOut: true } : {}),
|
|
204
|
+
...(item.outputExceeded === true ? { outputExceeded: true } : {}),
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
function parseStages(value, label) {
|
|
208
|
+
const item = record(value, label);
|
|
209
|
+
return {
|
|
210
|
+
runtime: parseStage(item.runtime, `${label}.runtime`),
|
|
211
|
+
artifact: parseStage(item.artifact, `${label}.artifact`),
|
|
212
|
+
profile: parseStage(item.profile, `${label}.profile`),
|
|
213
|
+
install: parseStage(item.install, `${label}.install`),
|
|
214
|
+
registration: parseStage(item.registration, `${label}.registration`),
|
|
215
|
+
host: parseStage(item.host, `${label}.host`),
|
|
216
|
+
surface: parseStage(item.surface, `${label}.surface`),
|
|
217
|
+
interaction: parseStage(item.interaction, `${label}.interaction`),
|
|
218
|
+
shutdown: parseStage(item.shutdown, `${label}.shutdown`),
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
function stringArray(value, label) {
|
|
222
|
+
if (!Array.isArray(value) || value.length > 32)
|
|
223
|
+
throw new Error(`${label} must be an array of at most 32 strings`);
|
|
224
|
+
return value.map((item, index) => boundedString(item, `${label}[${index}]`, 512));
|
|
225
|
+
}
|
|
226
|
+
function packageNames(value, label) {
|
|
227
|
+
if (!Array.isArray(value) || value.length > 32)
|
|
228
|
+
throw new Error(`${label} must be an array of at most 32 package names`);
|
|
229
|
+
const names = value.map((item, index) => {
|
|
230
|
+
const name = boundedString(item, `${label}[${index}]`, 214);
|
|
231
|
+
if (!/^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/.test(name))
|
|
232
|
+
throw new Error(`${label}[${index}] is not a package name`);
|
|
233
|
+
return name;
|
|
234
|
+
});
|
|
235
|
+
if (new Set(names).size !== names.length)
|
|
236
|
+
throw new Error(`${label} contains duplicate package names`);
|
|
237
|
+
return names.sort();
|
|
238
|
+
}
|
|
239
|
+
function optionalInteger(value, label) {
|
|
240
|
+
if (value === undefined)
|
|
241
|
+
return undefined;
|
|
242
|
+
if (!Number.isSafeInteger(value) || value < 0 || value > 1_000_000_000)
|
|
243
|
+
throw new Error(`${label} must be a bounded non-negative integer`);
|
|
244
|
+
return value;
|
|
245
|
+
}
|
|
246
|
+
function parseEvidence(value, plane, label) {
|
|
247
|
+
const item = record(value, label);
|
|
248
|
+
if (item.plane !== plane)
|
|
249
|
+
throw new Error(`${label}.plane does not match the report plane`);
|
|
250
|
+
if (plane === 'web') {
|
|
251
|
+
const booleanKeys = ['rootMounted', 'bootManifestPresent', 'pluginEntryPresent', 'pluginMaterialized'];
|
|
252
|
+
for (const key of booleanKeys)
|
|
253
|
+
if (typeof item[key] !== 'boolean')
|
|
254
|
+
throw new Error(`${label}.${key} must be boolean`);
|
|
255
|
+
return {
|
|
256
|
+
plane: 'web',
|
|
257
|
+
url: boundedString(item.url, `${label}.url`, 2_048),
|
|
258
|
+
...(optionalInteger(item.httpStatus, `${label}.httpStatus`) === undefined ? {} : { httpStatus: optionalInteger(item.httpStatus, `${label}.httpStatus`) }),
|
|
259
|
+
...(item.title === undefined ? {} : { title: boundedString(item.title, `${label}.title`, 256) }),
|
|
260
|
+
rootMounted: item.rootMounted,
|
|
261
|
+
bootManifestPresent: item.bootManifestPresent,
|
|
262
|
+
...(item.bootEntryIds === undefined ? {} : { bootEntryIds: stringArray(item.bootEntryIds, `${label}.bootEntryIds`) }),
|
|
263
|
+
pluginEntryPresent: item.pluginEntryPresent,
|
|
264
|
+
...(item.pluginBundleUrl === undefined ? {} : { pluginBundleUrl: boundedString(item.pluginBundleUrl, `${label}.pluginBundleUrl`, 2_048) }),
|
|
265
|
+
...(optionalInteger(item.pluginBundleStatus, `${label}.pluginBundleStatus`) === undefined ? {} : { pluginBundleStatus: optionalInteger(item.pluginBundleStatus, `${label}.pluginBundleStatus`) }),
|
|
266
|
+
applicationMounted: typeof item.applicationMounted === 'boolean'
|
|
267
|
+
? item.applicationMounted
|
|
268
|
+
: item.pluginMaterialized,
|
|
269
|
+
pluginMaterialized: item.pluginMaterialized,
|
|
270
|
+
...(item.bootFailureText === undefined ? {} : { bootFailureText: boundedString(item.bootFailureText, `${label}.bootFailureText`, 512) }),
|
|
271
|
+
consoleErrors: stringArray(item.consoleErrors, `${label}.consoleErrors`),
|
|
272
|
+
pageErrors: stringArray(item.pageErrors, `${label}.pageErrors`),
|
|
273
|
+
failedRequests: stringArray(item.failedRequests, `${label}.failedRequests`),
|
|
274
|
+
...(item.blockedExternalRequests === undefined ? {} : { blockedExternalRequests: stringArray(item.blockedExternalRequests, `${label}.blockedExternalRequests`) }),
|
|
275
|
+
...(item.screenshot === undefined ? {} : { screenshot: boundedString(item.screenshot, `${label}.screenshot`, 256) }),
|
|
276
|
+
...(item.trace === undefined ? {} : { trace: boundedString(item.trace, `${label}.trace`, 256) }),
|
|
277
|
+
...(item.hostLog === undefined ? {} : { hostLog: boundedString(item.hostLog, `${label}.hostLog`, 256) }),
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
for (const key of ['frameObserved', 'inputSent', 'exitedAfterShutdown', 'truncated']) {
|
|
281
|
+
if (typeof item[key] !== 'boolean')
|
|
282
|
+
throw new Error(`${label}.${key} must be boolean`);
|
|
283
|
+
}
|
|
284
|
+
if (item.terminal !== 'xterm-256color')
|
|
285
|
+
throw new Error(`${label}.terminal must be xterm-256color`);
|
|
286
|
+
return {
|
|
287
|
+
plane: 'tui',
|
|
288
|
+
terminal: 'xterm-256color',
|
|
289
|
+
columns: optionalInteger(item.columns, `${label}.columns`) ?? 0,
|
|
290
|
+
rows: optionalInteger(item.rows, `${label}.rows`) ?? 0,
|
|
291
|
+
frameObserved: item.frameObserved,
|
|
292
|
+
inputSent: item.inputSent,
|
|
293
|
+
exitedAfterShutdown: item.exitedAfterShutdown,
|
|
294
|
+
...(optionalInteger(item.exitCode, `${label}.exitCode`) === undefined ? {} : { exitCode: optionalInteger(item.exitCode, `${label}.exitCode`) }),
|
|
295
|
+
...(optionalInteger(item.signal, `${label}.signal`) === undefined ? {} : { signal: optionalInteger(item.signal, `${label}.signal`) }),
|
|
296
|
+
...(item.transcript === undefined ? {} : { transcript: boundedString(item.transcript, `${label}.transcript`, 256) }),
|
|
297
|
+
normalizedFrame: typeof item.normalizedFrame === 'string' && item.normalizedFrame.length <= 8_192 ? item.normalizedFrame : (() => { throw new Error(`${label}.normalizedFrame must be a bounded string`); })(),
|
|
298
|
+
capturedBytes: optionalInteger(item.capturedBytes, `${label}.capturedBytes`) ?? 0,
|
|
299
|
+
truncated: item.truncated,
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
function parseRuntime(value, label) {
|
|
303
|
+
const item = record(value, label);
|
|
304
|
+
return {
|
|
305
|
+
nodeMajor: nodeMajor(item.nodeMajor, `${label}.nodeMajor`),
|
|
306
|
+
nodeVersion: exactVersion(item.nodeVersion, `${label}.nodeVersion`),
|
|
307
|
+
platform: boundedString(item.platform, `${label}.platform`, 64),
|
|
308
|
+
architecture: boundedString(item.architecture, `${label}.architecture`, 64),
|
|
309
|
+
...(item.pnpmVersion === undefined ? {} : { pnpmVersion: exactVersion(item.pnpmVersion, `${label}.pnpmVersion`) }),
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
function parseReport(input) {
|
|
313
|
+
const root = record(input, 'DSH surface report');
|
|
314
|
+
if (root.schema !== DSH_SURFACE_OBSERVATION_SCHEMA || root.probe !== 'dsh-surface' || root.scope !== 'surface-runtime-behavior') {
|
|
315
|
+
throw new Error(`report schema must be ${DSH_SURFACE_OBSERVATION_SCHEMA}`);
|
|
316
|
+
}
|
|
317
|
+
const tool = record(root.tool, 'report.tool');
|
|
318
|
+
if (tool.name !== 'upstream-radar')
|
|
319
|
+
throw new Error('report.tool.name must be upstream-radar');
|
|
320
|
+
const plane = executionPlane(root.plane, 'report.plane');
|
|
321
|
+
const artifact = record(root.artifact, 'report.artifact');
|
|
322
|
+
const result = boundedString(root.result, 'report.result', 64);
|
|
323
|
+
if (!RESULTS.has(result))
|
|
324
|
+
throw new Error('report.result is unsupported');
|
|
325
|
+
const boundary = record(root.boundary, 'report.boundary');
|
|
326
|
+
return {
|
|
327
|
+
schema: DSH_SURFACE_OBSERVATION_SCHEMA,
|
|
328
|
+
tool: { name: 'upstream-radar', version: boundedString(tool.version, 'report.tool.version', 64) },
|
|
329
|
+
probe: 'dsh-surface',
|
|
330
|
+
scope: 'surface-runtime-behavior',
|
|
331
|
+
startedAt: isoDate(root.startedAt, 'report.startedAt'),
|
|
332
|
+
completedAt: isoDate(root.completedAt, 'report.completedAt'),
|
|
333
|
+
caseId: caseId(root.caseId, 'report.caseId'),
|
|
334
|
+
sourceCaseId: caseId(root.sourceCaseId, 'report.sourceCaseId'),
|
|
335
|
+
sourceFingerprint: fingerprint(root.sourceFingerprint, 'report.sourceFingerprint'),
|
|
336
|
+
contractFingerprint: fingerprint(root.contractFingerprint, 'report.contractFingerprint'),
|
|
337
|
+
plugin: exactSpec(root.plugin, 'report.plugin'),
|
|
338
|
+
dshVersion: exactVersion(root.dshVersion, 'report.dshVersion'),
|
|
339
|
+
plane,
|
|
340
|
+
profile: profileName(root.profile, 'report.profile'),
|
|
341
|
+
runtimeId: boundedString(root.runtimeId, 'report.runtimeId', 214),
|
|
342
|
+
runtime: parseRuntime(root.runtime, 'report.runtime'),
|
|
343
|
+
artifact: {
|
|
344
|
+
...(artifact.sha256 === undefined ? {} : { sha256: bareSha256(artifact.sha256, 'report.artifact.sha256') }),
|
|
345
|
+
...(optionalInteger(artifact.bytes, 'report.artifact.bytes') === undefined ? {} : { bytes: optionalInteger(artifact.bytes, 'report.artifact.bytes') }),
|
|
346
|
+
...(artifact.integrity === undefined ? {} : { integrity: boundedString(artifact.integrity, 'report.artifact.integrity', 1_024) }),
|
|
347
|
+
},
|
|
348
|
+
stages: parseStages(root.stages, 'report.stages'),
|
|
349
|
+
evidence: parseEvidence(root.evidence, plane, 'report.evidence'),
|
|
350
|
+
result,
|
|
351
|
+
reason: boundedString(root.reason, 'report.reason', 2_048),
|
|
352
|
+
boundary: {
|
|
353
|
+
isolationProviderClaim: (() => {
|
|
354
|
+
const value = boundary.isolationProviderClaim;
|
|
355
|
+
if (value !== 'github-actions-hosted-runner' && value !== 'firecracker' && value !== 'other')
|
|
356
|
+
throw new Error('report.boundary.isolationProviderClaim is unsupported');
|
|
357
|
+
return value;
|
|
358
|
+
})(),
|
|
359
|
+
isolationVerifiedByRadar: false,
|
|
360
|
+
disposableEnvironmentRequired: true,
|
|
361
|
+
inheritedHostSecrets: false,
|
|
362
|
+
externalBrowserRequestsBlocked: boundary.externalBrowserRequestsBlocked === true,
|
|
363
|
+
approvedDependencyBuilds: packageNames(boundary.approvedDependencyBuilds ?? [], 'report.boundary.approvedDependencyBuilds'),
|
|
364
|
+
note: boundedString(boundary.note, 'report.boundary.note', 2_048),
|
|
365
|
+
},
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
export function parseDshSurfaceLedger(input) {
|
|
369
|
+
const root = record(input, 'DSH surface ledger');
|
|
370
|
+
if (root.schema !== DSH_SURFACE_LEDGER_SCHEMA)
|
|
371
|
+
throw new Error(`DSH surface ledger schema must be ${DSH_SURFACE_LEDGER_SCHEMA}`);
|
|
372
|
+
if (!Array.isArray(root.entries) || root.entries.length > MAX_LEDGER_ENTRIES)
|
|
373
|
+
throw new Error(`DSH surface ledger must contain at most ${MAX_LEDGER_ENTRIES} entries`);
|
|
374
|
+
const ids = new Set();
|
|
375
|
+
const entries = root.entries.map((value, index) => {
|
|
376
|
+
const item = record(value, `entries[${index}]`);
|
|
377
|
+
const parsedCaseId = caseId(item.caseId, `entries[${index}].caseId`);
|
|
378
|
+
if (ids.has(parsedCaseId))
|
|
379
|
+
throw new Error(`duplicate DSH surface ledger case: ${parsedCaseId}`);
|
|
380
|
+
ids.add(parsedCaseId);
|
|
381
|
+
const plane = executionPlane(item.plane, `entries[${index}].plane`);
|
|
382
|
+
const artifact = record(item.artifact, `entries[${index}].artifact`);
|
|
383
|
+
const observer = record(item.observer, `entries[${index}].observer`);
|
|
384
|
+
if (observer.schema !== DSH_SURFACE_OBSERVATION_SCHEMA)
|
|
385
|
+
throw new Error(`entries[${index}].observer.schema is unsupported`);
|
|
386
|
+
const result = boundedString(item.result, `entries[${index}].result`, 64);
|
|
387
|
+
if (!RESULTS.has(result))
|
|
388
|
+
throw new Error(`entries[${index}].result is unsupported`);
|
|
389
|
+
return {
|
|
390
|
+
caseId: parsedCaseId,
|
|
391
|
+
sourceCaseId: caseId(item.sourceCaseId, `entries[${index}].sourceCaseId`),
|
|
392
|
+
plugin: exactSpec(item.plugin, `entries[${index}].plugin`),
|
|
393
|
+
dshVersion: exactVersion(item.dshVersion, `entries[${index}].dshVersion`),
|
|
394
|
+
plane,
|
|
395
|
+
profile: profileName(item.profile, `entries[${index}].profile`),
|
|
396
|
+
runtimeId: boundedString(item.runtimeId, `entries[${index}].runtimeId`, 214),
|
|
397
|
+
...(item.approvedDependencyBuilds === undefined ? {} : { approvedDependencyBuilds: packageNames(item.approvedDependencyBuilds, `entries[${index}].approvedDependencyBuilds`) }),
|
|
398
|
+
sourceFingerprint: fingerprint(item.sourceFingerprint, `entries[${index}].sourceFingerprint`),
|
|
399
|
+
contractFingerprint: fingerprint(item.contractFingerprint, `entries[${index}].contractFingerprint`),
|
|
400
|
+
observedAt: isoDate(item.observedAt, `entries[${index}].observedAt`),
|
|
401
|
+
runtime: parseRuntime(item.runtime, `entries[${index}].runtime`),
|
|
402
|
+
artifact: {
|
|
403
|
+
sha256: bareSha256(artifact.sha256, `entries[${index}].artifact.sha256`),
|
|
404
|
+
...(optionalInteger(artifact.bytes, `entries[${index}].artifact.bytes`) === undefined ? {} : { bytes: optionalInteger(artifact.bytes, `entries[${index}].artifact.bytes`) }),
|
|
405
|
+
...(artifact.integrity === undefined ? {} : { integrity: boundedString(artifact.integrity, `entries[${index}].artifact.integrity`, 1_024) }),
|
|
406
|
+
},
|
|
407
|
+
stages: parseStages(item.stages, `entries[${index}].stages`),
|
|
408
|
+
evidence: parseEvidence(item.evidence, plane, `entries[${index}].evidence`),
|
|
409
|
+
result,
|
|
410
|
+
reason: boundedString(item.reason, `entries[${index}].reason`, 2_048),
|
|
411
|
+
observer: {
|
|
412
|
+
schema: DSH_SURFACE_OBSERVATION_SCHEMA,
|
|
413
|
+
version: boundedString(observer.version, `entries[${index}].observer.version`, 64),
|
|
414
|
+
},
|
|
415
|
+
};
|
|
416
|
+
});
|
|
417
|
+
entries.sort((left, right) => left.caseId.localeCompare(right.caseId));
|
|
418
|
+
return { schema: DSH_SURFACE_LEDGER_SCHEMA, entries };
|
|
419
|
+
}
|
|
420
|
+
export function buildDshSurfacePlan(targetsInput, sourceLedgerInput, surfaceLedgerInput, now = new Date()) {
|
|
421
|
+
const targets = parseDshSurfaceTargets(targetsInput);
|
|
422
|
+
const sourceLedger = parseDshCompatibilityLedger(sourceLedgerInput);
|
|
423
|
+
const surfaceLedger = parseDshSurfaceLedger(surfaceLedgerInput);
|
|
424
|
+
const sourceById = new Map(sourceLedger.entries.map(entry => [entry.caseId, entry]));
|
|
425
|
+
const currentById = new Map(surfaceLedger.entries.map(entry => [entry.caseId, entry]));
|
|
426
|
+
const include = [];
|
|
427
|
+
const blocked = [];
|
|
428
|
+
const staleBefore = now.getTime() - targets.refreshAfterHours * 60 * 60 * 1_000;
|
|
429
|
+
for (const target of targets.surfaces) {
|
|
430
|
+
const source = sourceById.get(target.sourceCaseId);
|
|
431
|
+
if (source === undefined) {
|
|
432
|
+
blocked.push({ id: target.id, reason: `source compatibility case ${target.sourceCaseId} is missing` });
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
435
|
+
const desired = desiredCase(target, source);
|
|
436
|
+
if (desired === undefined) {
|
|
437
|
+
const hasExactArtifact = source.artifact.sha256 !== undefined && BARE_SHA256.test(source.artifact.sha256);
|
|
438
|
+
blocked.push({
|
|
439
|
+
id: target.id,
|
|
440
|
+
reason: hasExactArtifact
|
|
441
|
+
? `source compatibility case ${target.sourceCaseId} is ${source.result}; its headless environment must be resolved before entering ${target.plane}`
|
|
442
|
+
: `source compatibility case ${target.sourceCaseId} has no exact artifact bytes`,
|
|
443
|
+
});
|
|
444
|
+
continue;
|
|
445
|
+
}
|
|
446
|
+
const current = currentById.get(target.id);
|
|
447
|
+
if (current === undefined)
|
|
448
|
+
desired.reasons.push('missing-evidence');
|
|
449
|
+
else {
|
|
450
|
+
if (current.sourceFingerprint !== desired.sourceFingerprint || current.artifact.sha256 !== desired.artifactSha256
|
|
451
|
+
|| current.plugin !== desired.plugin || current.dshVersion !== desired.dshVersion || current.runtime.nodeMajor !== desired.nodeMajor) {
|
|
452
|
+
desired.reasons.push('source-evidence-changed');
|
|
453
|
+
}
|
|
454
|
+
if (current.contractFingerprint !== desired.contractFingerprint || current.plane !== desired.plane
|
|
455
|
+
|| current.profile !== desired.profile || current.runtimeId !== desired.runtimeId) {
|
|
456
|
+
desired.reasons.push('surface-contract-changed');
|
|
457
|
+
}
|
|
458
|
+
if (Date.parse(current.observedAt) < staleBefore)
|
|
459
|
+
desired.reasons.push('stale-evidence');
|
|
460
|
+
}
|
|
461
|
+
if (desired.reasons.length > 0)
|
|
462
|
+
include.push(desired);
|
|
463
|
+
}
|
|
464
|
+
include.sort((left, right) => left.id.localeCompare(right.id));
|
|
465
|
+
blocked.sort((left, right) => left.id.localeCompare(right.id));
|
|
466
|
+
return {
|
|
467
|
+
run: include.length > 0,
|
|
468
|
+
matrix: { include },
|
|
469
|
+
blocked,
|
|
470
|
+
reason: include.length > 0
|
|
471
|
+
? `${include.length} execution-plane observation(s) need fresh exact evidence`
|
|
472
|
+
: blocked.length > 0
|
|
473
|
+
? `no runnable execution-plane observations; ${blocked.length} source evidence gap(s) remain`
|
|
474
|
+
: 'all configured execution planes have fresh exact evidence',
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
function mismatch(expected, report) {
|
|
478
|
+
const comparisons = [
|
|
479
|
+
['case id', expected.id, report.caseId],
|
|
480
|
+
['source case id', expected.sourceCaseId, report.sourceCaseId],
|
|
481
|
+
['plugin', expected.plugin, report.plugin],
|
|
482
|
+
['DSH version', expected.dshVersion, report.dshVersion],
|
|
483
|
+
['Node major', expected.nodeMajor, report.runtime.nodeMajor],
|
|
484
|
+
['plane', expected.plane, report.plane],
|
|
485
|
+
['profile', expected.profile, report.profile],
|
|
486
|
+
['runtime id', expected.runtimeId, report.runtimeId],
|
|
487
|
+
['approved dependency builds', expected.allowedBuilds, report.boundary.approvedDependencyBuilds.join(',')],
|
|
488
|
+
['artifact SHA-256', expected.artifactSha256, report.artifact.sha256],
|
|
489
|
+
['source fingerprint', expected.sourceFingerprint, report.sourceFingerprint],
|
|
490
|
+
['contract fingerprint', expected.contractFingerprint, report.contractFingerprint],
|
|
491
|
+
];
|
|
492
|
+
const changed = comparisons.find(([, left, right]) => left !== right);
|
|
493
|
+
return changed === undefined ? undefined : `${changed[0]} mismatch: expected ${String(changed[1])}, observed ${String(changed[2])}`;
|
|
494
|
+
}
|
|
495
|
+
function transition(previous, current) {
|
|
496
|
+
const infrastructure = current.result === 'environment-unsupported' || current.result === 'unknown';
|
|
497
|
+
let status;
|
|
498
|
+
if (previous === undefined) {
|
|
499
|
+
status = current.result === 'compatible' ? 'compatible' : infrastructure ? 'new-infrastructure-gap' : 'new-incompatibility';
|
|
500
|
+
}
|
|
501
|
+
else if (current.result === 'compatible') {
|
|
502
|
+
status = previous.result === 'compatible' ? 'compatible' : 'resolved-incompatibility';
|
|
503
|
+
}
|
|
504
|
+
else if (infrastructure) {
|
|
505
|
+
status = previous.result === current.result && previous.reason === current.reason ? 'persisting-infrastructure-gap' : 'new-infrastructure-gap';
|
|
506
|
+
}
|
|
507
|
+
else {
|
|
508
|
+
status = previous.result === current.result && previous.reason === current.reason ? 'persisting-incompatibility' : 'changed-incompatibility';
|
|
509
|
+
}
|
|
510
|
+
return {
|
|
511
|
+
caseId: current.caseId,
|
|
512
|
+
status,
|
|
513
|
+
result: current.result,
|
|
514
|
+
reason: current.reason,
|
|
515
|
+
...(previous === undefined ? {} : { previousResult: previous.result }),
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
export function mergeDshSurfaceLedger(input) {
|
|
519
|
+
const ledger = parseDshSurfaceLedger(input.ledger);
|
|
520
|
+
if (input.expected.length > MAX_TARGETS)
|
|
521
|
+
throw new Error(`surface reconciliation accepts at most ${MAX_TARGETS} expected cases`);
|
|
522
|
+
if (input.reports.length > MAX_TARGETS)
|
|
523
|
+
throw new Error(`surface reconciliation accepts at most ${MAX_TARGETS} reports`);
|
|
524
|
+
const expectedById = new Map();
|
|
525
|
+
for (const value of input.expected) {
|
|
526
|
+
const id = caseId(value.id, 'expected.id');
|
|
527
|
+
if (expectedById.has(id))
|
|
528
|
+
throw new Error(`duplicate expected DSH surface case: ${id}`);
|
|
529
|
+
const allowedBuilds = value.allowedBuilds === ''
|
|
530
|
+
? []
|
|
531
|
+
: packageNames(value.allowedBuilds.split(','), `expected ${id}.allowedBuilds`);
|
|
532
|
+
expectedById.set(id, {
|
|
533
|
+
id,
|
|
534
|
+
sourceCaseId: caseId(value.sourceCaseId, `expected ${id}.sourceCaseId`),
|
|
535
|
+
plugin: exactSpec(value.plugin, `expected ${id}.plugin`),
|
|
536
|
+
dshVersion: exactVersion(value.dshVersion, `expected ${id}.dshVersion`),
|
|
537
|
+
nodeMajor: nodeMajor(value.nodeMajor, `expected ${id}.nodeMajor`),
|
|
538
|
+
plane: executionPlane(value.plane, `expected ${id}.plane`),
|
|
539
|
+
profile: profileName(value.profile, `expected ${id}.profile`),
|
|
540
|
+
runtimeId: boundedString(value.runtimeId, `expected ${id}.runtimeId`, 214),
|
|
541
|
+
artifactSha256: bareSha256(value.artifactSha256, `expected ${id}.artifactSha256`),
|
|
542
|
+
allowedBuilds: allowedBuilds.join(','),
|
|
543
|
+
sourceFingerprint: fingerprint(value.sourceFingerprint, `expected ${id}.sourceFingerprint`),
|
|
544
|
+
contractFingerprint: fingerprint(value.contractFingerprint, `expected ${id}.contractFingerprint`),
|
|
545
|
+
reasons: Array.isArray(value.reasons) ? value.reasons.map((reason, index) => boundedString(reason, `expected ${id}.reasons[${index}]`, 128)) : [],
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
const previousById = new Map(ledger.entries.map(entry => [entry.caseId, entry]));
|
|
549
|
+
const accepted = new Map();
|
|
550
|
+
const rejectedReports = [];
|
|
551
|
+
for (const inputReport of input.reports) {
|
|
552
|
+
let report;
|
|
553
|
+
try {
|
|
554
|
+
report = parseReport(inputReport);
|
|
555
|
+
}
|
|
556
|
+
catch (error) {
|
|
557
|
+
rejectedReports.push(`invalid report: ${error instanceof Error ? error.message : String(error)}`);
|
|
558
|
+
continue;
|
|
559
|
+
}
|
|
560
|
+
const expected = expectedById.get(report.caseId);
|
|
561
|
+
if (expected === undefined) {
|
|
562
|
+
rejectedReports.push(`${report.caseId}: report was not scheduled`);
|
|
563
|
+
continue;
|
|
564
|
+
}
|
|
565
|
+
if (accepted.has(report.caseId)) {
|
|
566
|
+
rejectedReports.push(`${report.caseId}: duplicate report`);
|
|
567
|
+
accepted.delete(report.caseId);
|
|
568
|
+
continue;
|
|
569
|
+
}
|
|
570
|
+
const difference = mismatch(expected, report);
|
|
571
|
+
if (difference !== undefined) {
|
|
572
|
+
rejectedReports.push(`${report.caseId}: ${difference}`);
|
|
573
|
+
continue;
|
|
574
|
+
}
|
|
575
|
+
if (report.artifact.sha256 === undefined) {
|
|
576
|
+
rejectedReports.push(`${report.caseId}: report did not establish exact artifact bytes`);
|
|
577
|
+
continue;
|
|
578
|
+
}
|
|
579
|
+
accepted.set(report.caseId, {
|
|
580
|
+
caseId: report.caseId,
|
|
581
|
+
sourceCaseId: report.sourceCaseId,
|
|
582
|
+
plugin: report.plugin,
|
|
583
|
+
dshVersion: report.dshVersion,
|
|
584
|
+
plane: report.plane,
|
|
585
|
+
profile: report.profile,
|
|
586
|
+
runtimeId: report.runtimeId,
|
|
587
|
+
...(report.boundary.approvedDependencyBuilds.length === 0 ? {} : { approvedDependencyBuilds: report.boundary.approvedDependencyBuilds }),
|
|
588
|
+
sourceFingerprint: report.sourceFingerprint,
|
|
589
|
+
contractFingerprint: report.contractFingerprint,
|
|
590
|
+
observedAt: report.completedAt,
|
|
591
|
+
runtime: report.runtime,
|
|
592
|
+
artifact: {
|
|
593
|
+
sha256: report.artifact.sha256,
|
|
594
|
+
...(report.artifact.bytes === undefined ? {} : { bytes: report.artifact.bytes }),
|
|
595
|
+
...(report.artifact.integrity === undefined ? {} : { integrity: report.artifact.integrity }),
|
|
596
|
+
},
|
|
597
|
+
stages: report.stages,
|
|
598
|
+
evidence: report.evidence,
|
|
599
|
+
result: report.result,
|
|
600
|
+
reason: report.reason,
|
|
601
|
+
observer: { schema: DSH_SURFACE_OBSERVATION_SCHEMA, version: report.tool.version },
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
const next = new Map(previousById);
|
|
605
|
+
for (const [id, entry] of accepted)
|
|
606
|
+
next.set(id, entry);
|
|
607
|
+
const entries = [...next.values()].sort((left, right) => left.caseId.localeCompare(right.caseId));
|
|
608
|
+
if (entries.length > MAX_LEDGER_ENTRIES)
|
|
609
|
+
throw new Error(`surface ledger exceeds ${MAX_LEDGER_ENTRIES} entries`);
|
|
610
|
+
const acceptedCaseIds = [...accepted.keys()].sort();
|
|
611
|
+
const missingCaseIds = [...expectedById.keys()].filter(id => !accepted.has(id)).sort();
|
|
612
|
+
const transitions = acceptedCaseIds.map(id => transition(previousById.get(id), accepted.get(id)));
|
|
613
|
+
rejectedReports.sort();
|
|
614
|
+
return {
|
|
615
|
+
ledger: { schema: DSH_SURFACE_LEDGER_SCHEMA, entries },
|
|
616
|
+
transitions,
|
|
617
|
+
acceptedCaseIds,
|
|
618
|
+
missingCaseIds,
|
|
619
|
+
rejectedReports,
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
export function buildDshSurfaceIR(ledgerInput) {
|
|
623
|
+
const ledger = parseDshSurfaceLedger(ledgerInput);
|
|
624
|
+
return {
|
|
625
|
+
schema: DSH_SURFACE_IR_SCHEMA,
|
|
626
|
+
generatedAt: ledger.entries.reduce((latest, entry) => entry.observedAt > latest ? entry.observedAt : latest, '1970-01-01T00:00:00.000Z'),
|
|
627
|
+
cells: ledger.entries.map(entry => ({
|
|
628
|
+
id: entry.caseId,
|
|
629
|
+
sourceCaseId: entry.sourceCaseId,
|
|
630
|
+
plane: entry.plane,
|
|
631
|
+
profile: entry.profile,
|
|
632
|
+
runtimeId: entry.runtimeId,
|
|
633
|
+
plugin: { spec: entry.plugin, artifactSha256: entry.artifact.sha256 },
|
|
634
|
+
upstream: { package: '@deepseek-ai/dsh', dshVersion: entry.dshVersion },
|
|
635
|
+
runtime: entry.runtime,
|
|
636
|
+
observation: {
|
|
637
|
+
observedAt: entry.observedAt,
|
|
638
|
+
result: entry.result,
|
|
639
|
+
reason: entry.reason,
|
|
640
|
+
stages: entry.stages,
|
|
641
|
+
evidence: entry.evidence,
|
|
642
|
+
},
|
|
643
|
+
})),
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
export function renderDshSurfaceLedgerMerge(merge) {
|
|
647
|
+
const lines = [
|
|
648
|
+
'# DSH execution-plane reconciliation',
|
|
649
|
+
'',
|
|
650
|
+
`Accepted: ${merge.acceptedCaseIds.length}`,
|
|
651
|
+
`Missing: ${merge.missingCaseIds.length}`,
|
|
652
|
+
`Rejected: ${merge.rejectedReports.length}`,
|
|
653
|
+
'',
|
|
654
|
+
];
|
|
655
|
+
for (const transition of merge.transitions) {
|
|
656
|
+
lines.push(`- ${transition.caseId}: **${transition.status}** — ${transition.result}: ${transition.reason}`);
|
|
657
|
+
}
|
|
658
|
+
if (merge.missingCaseIds.length > 0)
|
|
659
|
+
lines.push('', `Missing cases: ${merge.missingCaseIds.join(', ')}`);
|
|
660
|
+
if (merge.rejectedReports.length > 0) {
|
|
661
|
+
lines.push('', 'Rejected reports:');
|
|
662
|
+
for (const item of merge.rejectedReports)
|
|
663
|
+
lines.push(`- ${item}`);
|
|
664
|
+
}
|
|
665
|
+
return `${lines.join('\n')}\n`;
|
|
666
|
+
}
|
|
667
|
+
//# sourceMappingURL=dsh-surface.js.map
|