0sec-cli 0.22.1 → 0.22.4

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 (107) hide show
  1. package/0.js +56 -45
  2. package/README.md +12 -0
  3. package/chunks/{adapt-loop-J6QCZZTB.js → adapt-loop-3CCOWE43.js} +4 -4
  4. package/chunks/{artifact-scraper-MFKT6OI4.js → artifact-scraper-Z4UUIOH3.js} +2 -2
  5. package/chunks/{assumption-mining-646LYSJY.js → assumption-mining-2UAKQB6V.js} +18 -14
  6. package/chunks/{chunk-OHHCGDJ2.js → chunk-2WWAPKAW.js} +126 -1089
  7. package/chunks/{chunk-XDKB2P5X.js → chunk-3BPX75OR.js} +1 -1
  8. package/chunks/chunk-4ZYYX5GY.js +74 -0
  9. package/chunks/{chunk-3E2KALZ5.js → chunk-5JNTMWPK.js} +2 -2
  10. package/chunks/{chunk-CPWA6RWE.js → chunk-5WAJGLCW.js} +3 -3
  11. package/chunks/{chunk-KAU6NLGP.js → chunk-6KFB6AYN.js} +515 -20
  12. package/chunks/{chunk-XA2O4WXA.js → chunk-6WQDXWQC.js} +169 -15
  13. package/chunks/chunk-6YZE552K.js +5603 -0
  14. package/chunks/chunk-BGX53365.js +309 -0
  15. package/chunks/{chunk-X7LAEEUW.js → chunk-C6JPA32H.js} +1 -1
  16. package/chunks/chunk-CF3MNS3O.js +990 -0
  17. package/chunks/{chunk-4VGLO2YS.js → chunk-CPCHLL6A.js} +5 -2
  18. package/chunks/{chunk-ZKQ2I3S2.js → chunk-DPUXXMJW.js} +1 -1
  19. package/chunks/{chunk-XCISCI4T.js → chunk-E3OOBVOK.js} +30 -12
  20. package/chunks/{chunk-4G7OVYJX.js → chunk-E74LXQFY.js} +1 -1
  21. package/chunks/{chunk-OSBPGWTE.js → chunk-ELI3BVIK.js} +3 -3
  22. package/chunks/chunk-FFHRANYC.js +547 -0
  23. package/chunks/{chunk-WSDDGFA6.js → chunk-G2BE2FC7.js} +1 -1
  24. package/chunks/chunk-GQ3DGHWE.js +1659 -0
  25. package/chunks/{chunk-V36HVTUL.js → chunk-GVY3S5E5.js} +21 -18
  26. package/chunks/{chunk-5BTJMINS.js → chunk-H4DH7COV.js} +192 -40
  27. package/chunks/{chunk-HHPWRY2M.js → chunk-H6Q2QAIY.js} +23475 -22747
  28. package/chunks/{chunk-7XCKYNLO.js → chunk-IHPTOTLL.js} +1 -1
  29. package/chunks/chunk-IXAHQICR.js +31 -0
  30. package/chunks/{chunk-TH2LW437.js → chunk-KCA35IL3.js} +2 -190
  31. package/chunks/{chunk-VWITLWXB.js → chunk-L2CCO4KE.js} +282 -34
  32. package/chunks/{chunk-6YNKRUMC.js → chunk-NJLKFC66.js} +321 -5
  33. package/chunks/{chunk-W6UWZFKL.js → chunk-O6KPVYOI.js} +100 -5
  34. package/chunks/{chunk-LGCASF2C.js → chunk-OAS5SAVZ.js} +6767 -2100
  35. package/chunks/chunk-PS5E7LUQ.js +277 -0
  36. package/chunks/{chunk-AV2LG2L7.js → chunk-QVIRWJ45.js} +257 -145
  37. package/chunks/{chunk-TV27OWIW.js → chunk-QWK4R3DB.js} +9 -9
  38. package/chunks/{chunk-WZRVS7SB.js → chunk-RCRTSKQN.js} +1 -1
  39. package/chunks/chunk-RS4MAN3Q.js +73 -0
  40. package/chunks/{chunk-V6GHX34G.js → chunk-RTBZ36BD.js} +8 -274
  41. package/chunks/{chunk-YL6YAGLL.js → chunk-SCWWLABR.js} +1 -1
  42. package/chunks/chunk-VDOWARMJ.js +63 -0
  43. package/chunks/{chunk-IFME676H.js → chunk-WBRGXCU2.js} +79 -18
  44. package/chunks/{chunk-IVQV574Y.js → chunk-WHUAUFSX.js} +1 -1
  45. package/chunks/{chunk-GOEA3NW6.js → chunk-XODDLNBI.js} +1534 -1305
  46. package/chunks/chunk-YAZARMJY.js +355 -0
  47. package/chunks/{chunk-NLH4LMWH.js → chunk-YED25PA4.js} +1 -1
  48. package/chunks/{codex-models-NHQW45SO.js → codex-models-Z43V3427.js} +9 -3
  49. package/chunks/{commands-N6GN5SBF.js → commands-EUGF4FUZ.js} +5842 -4093
  50. package/chunks/{db-5IWEJUYV.js → db-NVAW4WWR.js} +3 -3
  51. package/chunks/{dist-L3XJWWZ6.js → dist-27BNG6WD.js} +193 -112
  52. package/chunks/{dist-FSS7MBDF.js → dist-7RUOKPRJ.js} +12 -2
  53. package/chunks/{dist-KEULE5IQ.js → dist-LNXGATR2.js} +2 -2
  54. package/chunks/{dist-XZB53PS5.js → dist-M2X367VW.js} +2 -2
  55. package/chunks/{dist-BS4CY4RG.js → dist-NG2HT5XU.js} +27 -1
  56. package/chunks/{eval-runner-EMEGF24P.js → eval-runner-6Q3H52SP.js} +13 -9
  57. package/chunks/{exploit-agent-KW4EB7BE.js → exploit-agent-FCCR2HYX.js} +1 -1
  58. package/chunks/{exploit-autoclimb-CXJ2ERRO.js → exploit-autoclimb-X5ZS5FAO.js} +1 -1
  59. package/chunks/{exploit-climb-TJWLY4XC.js → exploit-climb-3JHGEO36.js} +2 -2
  60. package/chunks/{fix-UUKPXCFB.js → fix-NSNPU33B.js} +12 -6
  61. package/chunks/{harness-C5HOKIEV.js → harness-KXQOVIDQ.js} +3 -3
  62. package/chunks/{hunt-scan-KZZ444V2.js → hunt-scan-ELTKGKC4.js} +16 -12
  63. package/chunks/{kernel-primitive-Z77IR7YS.js → kernel-primitive-MT7YZSIG.js} +2 -2
  64. package/chunks/{kernel-vm-runner-JW77P276.js → kernel-vm-runner-YFRHYC6G.js} +2 -2
  65. package/chunks/{native-loop-P6P5GYVJ.js → native-loop-OFJDRHPI.js} +18 -8
  66. package/chunks/orchestrate-DRZ2SVCP.js +62 -0
  67. package/chunks/{prepare-KYHGFWCJ.js → prepare-72GWTRVI.js} +6 -2
  68. package/chunks/{process-N245QIQG.js → process-MJYJ2K5E.js} +2 -1
  69. package/chunks/{run-GAFKBCTN.js → run-WTJCZZHV.js} +1901 -4816
  70. package/chunks/{runtime-H7YBR5IZ.js → runtime-FCKOUB7V.js} +10 -4
  71. package/chunks/{runtime-XFWJTBUO.js → runtime-LCJ4XSKE.js} +5 -1
  72. package/chunks/{scan-stream-BMOCPKQX.js → scan-stream-GT6GKQ3U.js} +7 -1
  73. package/chunks/{scope-QWY4VQZ7.js → scope-E5QZUOOH.js} +2 -2
  74. package/chunks/service-plugins-MSK2EQP6.js +62 -0
  75. package/chunks/{session-store-XIDFGG7K.js → session-store-H3Q7KCYG.js} +6 -4
  76. package/chunks/{variant-candidates-7D4F5E4H.js → variant-candidates-SZKDCOLK.js} +9 -3
  77. package/chunks/{web-recon-prepass-VXXBPDAV.js → web-recon-prepass-IO3GXQMW.js} +11 -6
  78. package/dashboard/apple-touch-icon.png +0 -0
  79. package/dashboard/assets/operations-BvB5Qpmk.css +1 -0
  80. package/dashboard/assets/operations-D8Ii01SV.js +114 -0
  81. package/dashboard/assets/zero-peek-Dzm3MT3X.png +0 -0
  82. package/dashboard/favicon.ico +0 -0
  83. package/dashboard/favicon.svg +10 -0
  84. package/dashboard/index.html +5 -3
  85. package/package.json +1 -1
  86. package/chunks/chunk-6GHSR47F.js +0 -147
  87. package/chunks/chunk-BCSRRQG5.js +0 -3514
  88. package/chunks/chunk-BOUGKEKA.js +0 -193
  89. package/chunks/chunk-GHWODZMR.js +0 -16
  90. package/chunks/orchestrate-DWWUT5RC.js +0 -58
  91. package/dashboard/assets/bot-Bg1Tp9_d.js +0 -1
  92. package/dashboard/assets/dist-B_dDCPbu.js +0 -1
  93. package/dashboard/assets/findings-page-mayGhNx6.js +0 -5
  94. package/dashboard/assets/format--susmHX7.js +0 -1
  95. package/dashboard/assets/input-TuVPR3Zl.js +0 -1
  96. package/dashboard/assets/jsx-runtime-MGseXjj5.js +0 -3
  97. package/dashboard/assets/live-page-HWWHX99U.js +0 -1
  98. package/dashboard/assets/meta-tile-ckHo-w7k.js +0 -1
  99. package/dashboard/assets/operations-BxZ0OoXO.js +0 -10
  100. package/dashboard/assets/operations-CMFVrXEe.css +0 -2
  101. package/dashboard/assets/operations-app-D3d35rgo.js +0 -46
  102. package/dashboard/assets/overview-page-W_4IteEr.js +0 -1
  103. package/dashboard/assets/page-header-Bw9AJgBV.js +0 -1
  104. package/dashboard/assets/scans-page-CgzVXHnn.js +0 -1
  105. package/dashboard/assets/siren-KQj8QAnJ.js +0 -1
  106. package/dashboard/assets/table-8EDklulN.js +0 -1
  107. package/dashboard/assets/tabs-CFHPOwg8.js +0 -1
@@ -5,9 +5,11 @@ import {
5
5
  redactAuthHeaders
6
6
  } from "./chunk-UELXHK2Y.js";
7
7
  import {
8
- ScopePolicy,
9
- isScopeEnforcementEnabled
10
- } from "./chunk-X7LAEEUW.js";
8
+ eventBus
9
+ } from "./chunk-4ZYYX5GY.js";
10
+ import {
11
+ ScopePolicy
12
+ } from "./chunk-C6JPA32H.js";
11
13
  import {
12
14
  allowlistedChildEnv,
13
15
  sanitizedEnv
@@ -15,172 +17,12 @@ import {
15
17
  import {
16
18
  VERSION,
17
19
  cloudStateDir,
18
- createPresentationEvent,
19
20
  homeStateDir
20
- } from "./chunk-KAU6NLGP.js";
21
+ } from "./chunk-6KFB6AYN.js";
21
22
  import {
22
23
  external_exports
23
24
  } from "./chunk-VIT5ALXF.js";
24
25
 
25
- // packages/core/dist/bench/improvement-promotion.js
26
- import { createHash } from "node:crypto";
27
- var DEFAULT_IMPROVEMENT_PROMOTION_POLICY = Object.freeze({
28
- minimumCases: 10,
29
- minimumDevelopmentLift: 0.05,
30
- minimumHeldOutLift: 0.03,
31
- maximumNegativeControlFpDelta: 0.02,
32
- maximumCostMultiplier: 1.5
33
- });
34
- function canonicalJson(value) {
35
- if (value === null || typeof value !== "object")
36
- return JSON.stringify(value);
37
- if (Array.isArray(value))
38
- return `[${value.map(canonicalJson).join(",")}]`;
39
- const record = value;
40
- return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`).join(",")}}`;
41
- }
42
- function digest(value) {
43
- return `sha256:${createHash("sha256").update(canonicalJson(value)).digest("hex")}`;
44
- }
45
- function isSha256(value) {
46
- return /^sha256:[a-f0-9]{64}$/i.test(value);
47
- }
48
- function metricSampleIsSufficient(result, minimumCases) {
49
- return result.development.champion.cases >= minimumCases && result.development.challenger.cases >= minimumCases && result.heldOut.champion.cases >= minimumCases && result.heldOut.challenger.cases >= minimumCases && result.negativeControls.champion.cases >= minimumCases && result.negativeControls.challenger.cases >= minimumCases;
50
- }
51
- function resultRatesAreFinite(result) {
52
- return [
53
- result.development.champion.successRate,
54
- result.development.challenger.successRate,
55
- result.heldOut.champion.successRate,
56
- result.heldOut.challenger.successRate,
57
- result.negativeControls.champion.falsePositiveRate,
58
- result.negativeControls.challenger.falsePositiveRate
59
- ].every((value) => Number.isFinite(value) && value >= 0 && value <= 1);
60
- }
61
- function makeDecision(candidateId, status, checks) {
62
- const unsigned = {
63
- schemaVersion: 1,
64
- candidateId,
65
- status,
66
- checks
67
- };
68
- return {
69
- ...unsigned,
70
- decisionDigest: digest(unsigned)
71
- };
72
- }
73
- function evaluateImprovementPromotion(candidate, policy = DEFAULT_IMPROVEMENT_PROMOTION_POLICY) {
74
- const result = candidate.result;
75
- const checks = [];
76
- checks.push({
77
- id: "candidate_identity",
78
- passed: candidate.candidateId.length > 0 && candidate.candidateId === result.candidateId,
79
- detail: "candidate id must be non-empty and match the sealed research result"
80
- });
81
- checks.push({
82
- id: "artifact_digests",
83
- passed: isSha256(candidate.baseArtifactDigest) && isSha256(candidate.candidateArtifactDigest) && candidate.baseArtifactDigest !== candidate.candidateArtifactDigest,
84
- detail: "base and candidate artifacts must be distinct sha256 digests"
85
- });
86
- checks.push({
87
- id: "ci",
88
- passed: result.ciPassed,
89
- detail: "candidate evaluation must carry a passing CI attestation"
90
- });
91
- checks.push({
92
- id: "evaluator_stability",
93
- passed: isSha256(result.evaluatorDigestBefore) && result.evaluatorDigestBefore === result.evaluatorDigestAfter,
94
- detail: "the evaluator digest must remain stable across the candidate evaluation"
95
- });
96
- checks.push({
97
- id: "evidence",
98
- passed: result.evidenceRefs.length > 0,
99
- detail: "candidate evaluation must retain at least one evidence reference"
100
- });
101
- checks.push({
102
- id: "sample_size",
103
- passed: resultRatesAreFinite(result) && metricSampleIsSufficient(result, policy.minimumCases),
104
- detail: `every sealed lane must contain at least ${policy.minimumCases} finite observations`
105
- });
106
- const developmentLift = result.development.challenger.successRate - result.development.champion.successRate;
107
- checks.push({
108
- id: "development_lift",
109
- passed: developmentLift >= policy.minimumDevelopmentLift,
110
- detail: `development success lift ${developmentLift.toFixed(4)} must be at least ${policy.minimumDevelopmentLift.toFixed(4)}`
111
- });
112
- const heldOutLift = result.heldOut.challenger.successRate - result.heldOut.champion.successRate;
113
- checks.push({
114
- id: "held_out_lift",
115
- passed: heldOutLift >= policy.minimumHeldOutLift,
116
- detail: `held-out success lift ${heldOutLift.toFixed(4)} must be at least ${policy.minimumHeldOutLift.toFixed(4)}`
117
- });
118
- const fpDelta = result.negativeControls.challenger.falsePositiveRate - result.negativeControls.champion.falsePositiveRate;
119
- checks.push({
120
- id: "negative_control_precision",
121
- passed: fpDelta <= policy.maximumNegativeControlFpDelta,
122
- detail: `negative-control FP delta ${fpDelta.toFixed(4)} must not exceed ${policy.maximumNegativeControlFpDelta.toFixed(4)}`
123
- });
124
- const championCost = result.heldOut.champion.costPerSuccessUsd;
125
- const challengerCost = result.heldOut.challenger.costPerSuccessUsd;
126
- const zeroSuccessBaseline = championCost === null && result.heldOut.champion.successRate === 0 && result.heldOut.champion.inconclusiveRate === 0;
127
- const costPasses = challengerCost !== null && Number.isFinite(challengerCost) && challengerCost >= 0 && (zeroSuccessBaseline || championCost !== null && Number.isFinite(championCost) && championCost >= 0 && challengerCost <= championCost * policy.maximumCostMultiplier);
128
- checks.push({
129
- id: "cost_discipline",
130
- passed: costPasses,
131
- detail: `held-out challenger cost must be finite and nonnegative; at most ${policy.maximumCostMultiplier.toFixed(2)}x champion cost unless the fully observed champion has zero successes`
132
- });
133
- if (checks.some((check) => !check.passed)) {
134
- return makeDecision(candidate.candidateId, "rejected", checks);
135
- }
136
- return makeDecision(candidate.candidateId, candidate.kind === "source" ? "requires_human_approval" : "eligible_for_canary", checks);
137
- }
138
- function appendImprovementLedgerEntry(entries, event) {
139
- const verification = verifyImprovementLedger(entries);
140
- if (!verification.valid)
141
- throw new Error(`cannot append to invalid improvement ledger: ${verification.reason}`);
142
- if (!isSha256(event.payloadDigest))
143
- throw new Error("ledger payloadDigest must be a sha256 digest");
144
- const previousDigest = entries.length === 0 ? null : entries[entries.length - 1].entryDigest;
145
- const unsigned = {
146
- schemaVersion: 1,
147
- sequence: entries.length,
148
- occurredAt: event.occurredAt,
149
- type: event.type,
150
- candidateId: event.candidateId,
151
- payloadDigest: event.payloadDigest,
152
- previousDigest
153
- };
154
- return {
155
- ...unsigned,
156
- entryDigest: digest(unsigned)
157
- };
158
- }
159
- function verifyImprovementLedger(entries) {
160
- let previousDigest = null;
161
- for (const [sequence, entry] of entries.entries()) {
162
- if (entry.sequence !== sequence)
163
- return { valid: false, reason: `unexpected sequence at ${sequence}` };
164
- if (entry.previousDigest !== previousDigest)
165
- return { valid: false, reason: `broken predecessor at ${sequence}` };
166
- if (!isSha256(entry.payloadDigest))
167
- return { valid: false, reason: `invalid payload digest at ${sequence}` };
168
- const unsigned = {
169
- schemaVersion: entry.schemaVersion,
170
- sequence: entry.sequence,
171
- occurredAt: entry.occurredAt,
172
- type: entry.type,
173
- candidateId: entry.candidateId,
174
- payloadDigest: entry.payloadDigest,
175
- previousDigest: entry.previousDigest
176
- };
177
- if (entry.entryDigest !== digest(unsigned))
178
- return { valid: false, reason: `invalid entry digest at ${sequence}` };
179
- previousDigest = entry.entryDigest;
180
- }
181
- return { valid: true };
182
- }
183
-
184
26
  // packages/core/dist/telemetry/analytics-level.js
185
27
  var LEVEL_ORDER = {
186
28
  off: 0,
@@ -217,69 +59,6 @@ function resolveAnalyticsLevel(env = process.env) {
217
59
  return isAnalyticsLevel(normalized) ? normalized : "off";
218
60
  }
219
61
 
220
- // packages/core/dist/events/bus.js
221
- function presentationEventSink(sink, options = {}) {
222
- let sequence = 0;
223
- const source = options.source ?? "core";
224
- const now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
225
- return {
226
- emit(type, payload) {
227
- const scanId = typeof payload["scan_id"] === "string" ? payload["scan_id"] : typeof payload["scanId"] === "string" ? payload["scanId"] : void 0;
228
- const sessionId = typeof payload["session_id"] === "string" ? payload["session_id"] : typeof payload["sessionId"] === "string" ? payload["sessionId"] : void 0;
229
- sink.emit(createPresentationEvent({
230
- source,
231
- sequence: ++sequence,
232
- at: now(),
233
- eventType: type,
234
- payload,
235
- ...scanId ? { scanId } : {},
236
- ...sessionId ? { sessionId } : {}
237
- }));
238
- }
239
- };
240
- }
241
- var EventBus = class {
242
- sinks = [];
243
- /** Subscribe a sink. Returns an unsubscribe function. */
244
- subscribe(sink) {
245
- this.sinks.push(sink);
246
- return () => {
247
- const idx = this.sinks.indexOf(sink);
248
- if (idx >= 0)
249
- this.sinks.splice(idx, 1);
250
- };
251
- }
252
- /** Remove every subscriber — primarily useful in tests. */
253
- clear() {
254
- this.sinks = [];
255
- }
256
- /** Returns a read-only snapshot of the current sinks (for tests). */
257
- get size() {
258
- return this.sinks.length;
259
- }
260
- /**
261
- * Fan out a single event to every subscribed sink. Exceptions thrown by
262
- * any sink are swallowed — one misbehaving consumer must never abort a
263
- * scan. Diagnostic is written to stderr so the failure is visible.
264
- */
265
- emit(type, payload) {
266
- const snapshot2 = this.sinks.slice();
267
- for (const sink of snapshot2) {
268
- try {
269
- sink.emit(type, payload);
270
- } catch (err) {
271
- const msg = err instanceof Error ? err.message : String(err);
272
- try {
273
- process.stderr.write(`[0 event-bus] sink threw on ${type}: ${msg}
274
- `);
275
- } catch {
276
- }
277
- }
278
- }
279
- }
280
- };
281
- var eventBus = new EventBus();
282
-
283
62
  // packages/core/dist/cloud/credentials.js
284
63
  import { readFileSync, statSync } from "node:fs";
285
64
  import { join } from "node:path";
@@ -1205,7 +984,7 @@ function maybeSubscribeAnalyticsPipeline() {
1205
984
 
1206
985
  // packages/core/dist/telemetry/run-contribution.js
1207
986
  import { AsyncLocalStorage } from "node:async_hooks";
1208
- import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
987
+ import { createHash, randomUUID as randomUUID2 } from "node:crypto";
1209
988
  import { appendFileSync as appendFileSync2, closeSync, existsSync, fsyncSync, lstatSync, mkdirSync as mkdirSync3, openSync, readFileSync as readFileSync3, readdirSync, renameSync, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
1210
989
  import { isAbsolute, join as join4 } from "node:path";
1211
990
  var CONTRIBUTION_ENDPOINT = "/api/run-contributions";
@@ -1280,7 +1059,7 @@ var contributionClientPolicySchema = external_exports.object({
1280
1059
  }).strict();
1281
1060
  var PROCESS_INSTANCE = randomUUID2();
1282
1061
  var context = new AsyncLocalStorage();
1283
- var hash = (value) => createHash2("sha256").update(value).digest("hex");
1062
+ var hash = (value) => createHash("sha256").update(value).digest("hex");
1284
1063
  function syncDirectory(path) {
1285
1064
  const fd = openSync(path, "r");
1286
1065
  try {
@@ -1957,7 +1736,7 @@ function reportUnsupportedContributionMode(mode) {
1957
1736
 
1958
1737
  // packages/core/dist/runtime/smolvm-provision.js
1959
1738
  import { execFile } from "node:child_process";
1960
- import { createHash as createHash3, randomBytes } from "node:crypto";
1739
+ import { createHash as createHash2, randomBytes } from "node:crypto";
1961
1740
  import { constants, createWriteStream } from "node:fs";
1962
1741
  import { chmod, copyFile, lstat, mkdir, open, readFile, rename, rm, stat, writeFile } from "node:fs/promises";
1963
1742
  import { homedir } from "node:os";
@@ -2287,13 +2066,13 @@ async function privateSmolvmDirectory(path) {
2287
2066
  throw new Error(`SmolVM state must be a private operator-owned directory: ${path}`);
2288
2067
  }
2289
2068
  }
2290
- function smolvmExec(binary, argv2, options = {}) {
2291
- const { promise, resolve: resolve7, reject } = Promise.withResolvers();
2292
- execFile(binary, argv2, { env: { PATH: "/usr/bin:/bin:/usr/sbin:/sbin" }, ...options, timeout: 12e4, maxBuffer: 4 * 1024 * 1024 }, (error, stdout, stderr) => {
2069
+ function smolvmExec(binary, argv, options = {}) {
2070
+ const { promise, resolve: resolve3, reject } = Promise.withResolvers();
2071
+ execFile(binary, argv, { env: { PATH: "/usr/bin:/bin:/usr/sbin:/sbin" }, ...options, timeout: 12e4, maxBuffer: 4 * 1024 * 1024 }, (error, stdout, stderr) => {
2293
2072
  if (error)
2294
2073
  reject(new Error(`${binary} failed: ${stderr.trim() || error.message}`));
2295
2074
  else
2296
- resolve7({ stdout, stderr });
2075
+ resolve3({ stdout, stderr });
2297
2076
  });
2298
2077
  return promise;
2299
2078
  }
@@ -2301,9 +2080,9 @@ async function smolvmArchiveDigest(path, signal) {
2301
2080
  const file = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
2302
2081
  try {
2303
2082
  const info = await file.stat();
2304
- if (!info.isFile() || info.size <= 0 || info.size > 16 * 1024 ** 3)
2305
- throw new Error("SmolVM image must be a regular nonempty archive no larger than 16 GiB");
2306
- const hash3 = createHash3("sha256");
2083
+ if (!info.isFile() || info.size <= 0 || info.size > 8 * 1024 ** 3)
2084
+ throw new Error("SmolVM image must be a regular nonempty archive no larger than 8 GiB");
2085
+ const hash3 = createHash2("sha256");
2307
2086
  for await (const chunk of file.createReadStream({ autoClose: false, signal }))
2308
2087
  hash3.update(chunk);
2309
2088
  return `sha256:${hash3.digest("hex")}`;
@@ -2313,7 +2092,7 @@ async function smolvmArchiveDigest(path, signal) {
2313
2092
  }
2314
2093
  function runtimePaths(root) {
2315
2094
  const bundleRoot = join5(root, "runtime", BUNDLE_NAME);
2316
- const sourceDigest = createHash3("sha256").update(SMOLVM_DARWIN_SUPERVISOR_SOURCE).digest("hex").slice(0, 16);
2095
+ const sourceDigest = createHash2("sha256").update(SMOLVM_DARWIN_SUPERVISOR_SOURCE).digest("hex").slice(0, 16);
2317
2096
  return { version: VERSION2, binary: join5(bundleRoot, "smolvm"), bundleRoot, supervisor: join5(root, "runtime", `supervisor-${sourceDigest}`) };
2318
2097
  }
2319
2098
  async function verifyBundle(runtime, signal) {
@@ -2349,7 +2128,7 @@ async function resolveSmolvmRuntime(options = {}) {
2349
2128
  throw new Error(`Pinned SmolVM download failed (HTTP ${response.status})`);
2350
2129
  const archive = join5(staging, "bundle.tar.gz");
2351
2130
  let bytes = 0;
2352
- const hash3 = createHash3("sha256");
2131
+ const hash3 = createHash2("sha256");
2353
2132
  const source = Readable.fromWeb(response.body);
2354
2133
  source.on("data", (chunk) => {
2355
2134
  bytes += chunk.length;
@@ -2396,21 +2175,21 @@ async function approveSmolvmWorkbenchImage(options) {
2396
2175
  await privateSmolvmDirectory(options.stateRoot);
2397
2176
  const images = join5(options.stateRoot, "images");
2398
2177
  await privateSmolvmDirectory(images);
2399
- const digest2 = await smolvmArchiveDigest(options.imageArchive, options.signal);
2400
- const destination = join5(images, `${digest2.slice(7)}.tar`);
2178
+ const digest = await smolvmArchiveDigest(options.imageArchive, options.signal);
2179
+ const destination = join5(images, `${digest.slice(7)}.tar`);
2401
2180
  if (options.imageArchive !== destination) {
2402
2181
  const staging = join5(images, `.approve-${randomBytes(8).toString("hex")}.tar`);
2403
2182
  try {
2404
2183
  await copyFile(options.imageArchive, staging, constants.COPYFILE_EXCL | constants.COPYFILE_FICLONE);
2405
2184
  await chmod(staging, 384);
2406
- if (await smolvmArchiveDigest(staging, options.signal) !== digest2)
2185
+ if (await smolvmArchiveDigest(staging, options.signal) !== digest)
2407
2186
  throw new Error("Image changed during approval");
2408
2187
  await rename(staging, destination);
2409
2188
  } finally {
2410
2189
  await rm(staging, { force: true });
2411
2190
  }
2412
2191
  }
2413
- const image = { path: destination, digest: digest2 };
2192
+ const image = { path: destination, digest };
2414
2193
  await writeFile(`${destination}.json`, JSON.stringify({ schemaVersion: 1, ...image }), { flag: "w", mode: 384 });
2415
2194
  return image;
2416
2195
  }
@@ -2456,7 +2235,7 @@ async function getSmolvmWorkbenchStatus(options) {
2456
2235
  }
2457
2236
 
2458
2237
  // packages/core/dist/runtime/smolvm-broker.js
2459
- import { createHash as createHash5, randomBytes as randomBytes3 } from "node:crypto";
2238
+ import { createHash as createHash4, randomBytes as randomBytes3 } from "node:crypto";
2460
2239
  import { lookup as lookup2 } from "node:dns/promises";
2461
2240
  import { constants as constants4, closeSync as closeSync2, fstatSync, lstatSync as lstatSync2, openSync as openSync2, readFileSync as readFileSync4 } from "node:fs";
2462
2241
  import { chmod as chmod2, copyFile as copyFile3, lstat as lstat3, mkdir as mkdir4, mkdtemp as mkdtemp4, open as open3, readdir as readdir2, realpath as realpath3, rename as rename2, rm as rm5, writeFile as writeFile2 } from "node:fs/promises";
@@ -2484,7 +2263,7 @@ var ALLOWED_SCANNER_BINARIES = /* @__PURE__ */ new Set([
2484
2263
  "ffuf",
2485
2264
  "nuclei"
2486
2265
  ]);
2487
- function launchAllowlistedScanner(bin, argv2, env) {
2266
+ function launchAllowlistedScanner(bin, argv, env) {
2488
2267
  const spawnOpts = {
2489
2268
  env,
2490
2269
  stdio: ["ignore", "pipe", "pipe"],
@@ -2493,13 +2272,13 @@ function launchAllowlistedScanner(bin, argv2, env) {
2493
2272
  };
2494
2273
  switch (bin) {
2495
2274
  case "sqlmap":
2496
- return spawn("sqlmap", argv2, spawnOpts);
2275
+ return spawn("sqlmap", argv, spawnOpts);
2497
2276
  case "nmap":
2498
- return spawn("nmap", argv2, spawnOpts);
2277
+ return spawn("nmap", argv, spawnOpts);
2499
2278
  case "ffuf":
2500
- return spawn("ffuf", argv2, spawnOpts);
2279
+ return spawn("ffuf", argv, spawnOpts);
2501
2280
  case "nuclei":
2502
- return spawn("nuclei", argv2, spawnOpts);
2281
+ return spawn("nuclei", argv, spawnOpts);
2503
2282
  default:
2504
2283
  return null;
2505
2284
  }
@@ -2574,7 +2353,7 @@ function superviseChild(child, effectiveTimeoutMs, startedAt) {
2574
2353
  });
2575
2354
  });
2576
2355
  }
2577
- async function runScannerProcess(bin, argv2, opts) {
2356
+ async function runScannerProcess(bin, argv, opts) {
2578
2357
  const startedAt = Date.now();
2579
2358
  const effectiveTimeout = Math.min(Math.max(1, opts.timeoutMs), Math.max(1, opts.ceilingMs));
2580
2359
  if (!ALLOWED_SCANNER_BINARIES.has(bin)) {
@@ -2586,7 +2365,7 @@ async function runScannerProcess(bin, argv2, opts) {
2586
2365
  }
2587
2366
  let child;
2588
2367
  try {
2589
- child = launchAllowlistedScanner(bin, argv2, opts.env);
2368
+ child = launchAllowlistedScanner(bin, argv, opts.env);
2590
2369
  } catch (err) {
2591
2370
  return {
2592
2371
  kind: "error",
@@ -2613,68 +2392,68 @@ function sanitizeToken(value, allowed) {
2613
2392
  return allowed.test(value) ? value : null;
2614
2393
  }
2615
2394
  function buildSqlmapArgv(opts) {
2616
- const argv2 = ["-u", opts.url, "--batch", "--disable-coloring"];
2395
+ const argv = ["-u", opts.url, "--batch", "--disable-coloring"];
2617
2396
  if (typeof opts.data === "string" && opts.data.length > 0) {
2618
- argv2.push("--data", opts.data);
2397
+ argv.push("--data", opts.data);
2619
2398
  }
2620
- argv2.push("--level", String(clampInt(opts.level, 1, 5, 1)));
2621
- argv2.push("--risk", String(clampInt(opts.risk, 1, 3, 1)));
2399
+ argv.push("--level", String(clampInt(opts.level, 1, 5, 1)));
2400
+ argv.push("--risk", String(clampInt(opts.risk, 1, 3, 1)));
2622
2401
  if (opts.technique) {
2623
2402
  const t = sanitizeToken(opts.technique.toUpperCase(), /^[BEUSTQ]{1,6}$/);
2624
2403
  if (t)
2625
- argv2.push("--technique", t);
2404
+ argv.push("--technique", t);
2626
2405
  }
2627
2406
  if (opts.dbms) {
2628
2407
  const d = sanitizeToken(opts.dbms, /^[A-Za-z0-9 _.-]{1,32}$/);
2629
2408
  if (d)
2630
- argv2.push("--dbms", d);
2409
+ argv.push("--dbms", d);
2631
2410
  }
2632
- argv2.push("--threads", String(clampInt(opts.threads, 1, 10, 1)));
2411
+ argv.push("--threads", String(clampInt(opts.threads, 1, 10, 1)));
2633
2412
  if (opts.enumerateDbs)
2634
- argv2.push("--dbs");
2413
+ argv.push("--dbs");
2635
2414
  if (opts.dump)
2636
- argv2.push("--dump");
2637
- return argv2;
2415
+ argv.push("--dump");
2416
+ return argv;
2638
2417
  }
2639
2418
  function buildNmapArgv(opts) {
2640
- const argv2 = [];
2419
+ const argv = [];
2641
2420
  if (opts.skipPing !== false)
2642
- argv2.push("-Pn");
2421
+ argv.push("-Pn");
2643
2422
  if (opts.serviceDetection)
2644
- argv2.push("-sV");
2423
+ argv.push("-sV");
2645
2424
  if (opts.ports) {
2646
2425
  const p = sanitizeToken(opts.ports, /^[0-9,\-]{1,64}$/);
2647
2426
  if (p)
2648
- argv2.push("-p", p);
2427
+ argv.push("-p", p);
2649
2428
  } else if (opts.topPorts) {
2650
- argv2.push("--top-ports", String(clampInt(opts.topPorts, 1, 65535, 100)));
2429
+ argv.push("--top-ports", String(clampInt(opts.topPorts, 1, 65535, 100)));
2651
2430
  }
2652
- argv2.push(opts.target);
2653
- return argv2;
2431
+ argv.push(opts.target);
2432
+ return argv;
2654
2433
  }
2655
2434
  function buildFfufArgv(opts) {
2656
- const argv2 = ["-u", opts.url, "-w", opts.wordlist, "-of", "json", "-o", "/dev/stdout", "-s"];
2435
+ const argv = ["-u", opts.url, "-w", opts.wordlist, "-of", "json", "-o", "/dev/stdout", "-s"];
2657
2436
  if (opts.matchStatus) {
2658
2437
  const m = sanitizeToken(opts.matchStatus, /^[0-9,\-]{1,64}$/);
2659
2438
  if (m)
2660
- argv2.push("-mc", m);
2439
+ argv.push("-mc", m);
2661
2440
  }
2662
- argv2.push("-t", String(clampInt(opts.threads, 1, 50, 10)));
2663
- return argv2;
2441
+ argv.push("-t", String(clampInt(opts.threads, 1, 50, 10)));
2442
+ return argv;
2664
2443
  }
2665
2444
  function buildNucleiArgv(opts) {
2666
- const argv2 = ["-u", opts.target, "-jsonl", "-silent"];
2445
+ const argv = ["-u", opts.target, "-jsonl", "-silent"];
2667
2446
  if (opts.severity) {
2668
2447
  const s = sanitizeToken(opts.severity.toLowerCase(), /^[a-z,]{1,40}$/);
2669
2448
  if (s)
2670
- argv2.push("-severity", s);
2449
+ argv.push("-severity", s);
2671
2450
  }
2672
2451
  if (opts.tags) {
2673
2452
  const t = sanitizeToken(opts.tags.toLowerCase(), /^[a-z0-9,_-]{1,60}$/);
2674
2453
  if (t)
2675
- argv2.push("-tags", t);
2454
+ argv.push("-tags", t);
2676
2455
  }
2677
- return argv2;
2456
+ return argv;
2678
2457
  }
2679
2458
  function tail(text, n = 1200) {
2680
2459
  return text.length > n ? text.slice(text.length - n) : text;
@@ -3155,6 +2934,30 @@ function parseRepositoryAcquisition(command) {
3155
2934
  return null;
3156
2935
  }
3157
2936
  }
2937
+ function normalizeRepositoryIdentity(value) {
2938
+ try {
2939
+ const url = new URL(value.trim());
2940
+ if (url.protocol !== "https:" || url.username || url.password || url.search || url.hash || url.port && url.port !== "443" || url.pathname === "/")
2941
+ return null;
2942
+ url.hostname = url.hostname.replace(/\.+$/, "").toLowerCase();
2943
+ let pathname = url.pathname;
2944
+ if (pathname.endsWith("/"))
2945
+ pathname = pathname.slice(0, -1);
2946
+ if (pathname.endsWith(".git"))
2947
+ pathname = pathname.slice(0, -4);
2948
+ if (!pathname || pathname === "/")
2949
+ return null;
2950
+ url.pathname = pathname;
2951
+ return url.href;
2952
+ } catch {
2953
+ return null;
2954
+ }
2955
+ }
2956
+ function repositoryIdentityMatchesTarget(target, cloneUrl) {
2957
+ const targetIdentity = normalizeRepositoryIdentity(target);
2958
+ const cloneIdentity = normalizeRepositoryIdentity(cloneUrl);
2959
+ return targetIdentity !== null && targetIdentity === cloneIdentity;
2960
+ }
3158
2961
  function repositoryAcquisitionAllowed(plan, scope) {
3159
2962
  if (!scope)
3160
2963
  return true;
@@ -3242,7 +3045,7 @@ async function runRepositoryAcquisition(plan, command, timeoutMs, ceilingMs, sco
3242
3045
  const address = proxy.address();
3243
3046
  if (!address || typeof address === "string")
3244
3047
  throw new Error("Cannot open source acquisition tunnel");
3245
- const argv2 = [
3048
+ const argv = [
3246
3049
  "-C",
3247
3050
  plan.cwd,
3248
3051
  "-c",
@@ -3265,7 +3068,7 @@ async function runRepositoryAcquisition(plan, command, timeoutMs, ceilingMs, sco
3265
3068
  ...plan.cloneArgs
3266
3069
  ];
3267
3070
  const startedAt = Date.now();
3268
- const child = spawn2("git", argv2, {
3071
+ const child = spawn2("git", argv, {
3269
3072
  shell: false,
3270
3073
  detached: true,
3271
3074
  stdio: ["ignore", "pipe", "pipe"],
@@ -3308,7 +3111,7 @@ async function runRepositoryAcquisition(plan, command, timeoutMs, ceilingMs, sco
3308
3111
 
3309
3112
  // packages/core/dist/runtime/smolvm.js
3310
3113
  import { execFile as execFile2, spawn as spawn4 } from "node:child_process";
3311
- import { createHash as createHash4 } from "node:crypto";
3114
+ import { createHash as createHash3 } from "node:crypto";
3312
3115
  import { constants as constants3, createWriteStream as createWriteStream2 } from "node:fs";
3313
3116
  import { mkdir as mkdir3, mkdtemp as mkdtemp3, open as open2, readdir, readFile as readFile2, readlink, realpath as realpath2, rm as rm4 } from "node:fs/promises";
3314
3117
  import { tmpdir as tmpdir2 } from "node:os";
@@ -3372,8 +3175,8 @@ async function runSmolvmDarwinProgram(options, network, ownership) {
3372
3175
  throw new Error("SmolVM image requires a nonempty regular archive no larger than 8 GiB");
3373
3176
  const archive = join7(root, "image.tar");
3374
3177
  await copyFile2(options.imageArchive, archive, constants2.COPYFILE_EXCL | constants2.COPYFILE_FICLONE);
3375
- const digest2 = await smolvmArchiveDigest(archive, lifetime.signal);
3376
- if (options.imageDigest && digest2 !== options.imageDigest)
3178
+ const digest = await smolvmArchiveDigest(archive, lifetime.signal);
3179
+ if (options.imageDigest && digest !== options.imageDigest)
3377
3180
  throw new Error("SmolVM archive identity mismatch");
3378
3181
  const args = [
3379
3182
  "machine",
@@ -3559,7 +3362,7 @@ async function hashArchive(path, destination, signal) {
3559
3362
  if (!stat2.isFile() || stat2.size === 0 || stat2.size > MAX_IMAGE_BYTES) {
3560
3363
  throw new Error("smolvm image must be a nonempty regular archive no larger than 8 GiB");
3561
3364
  }
3562
- const hash3 = createHash4("sha256");
3365
+ const hash3 = createHash3("sha256");
3563
3366
  const source = file.createReadStream({ autoClose: false, signal });
3564
3367
  if (destination) {
3565
3368
  source.on("data", (chunk) => hash3.update(chunk));
@@ -3618,14 +3421,14 @@ function launcherEnvironment(root) {
3618
3421
  return env;
3619
3422
  }
3620
3423
  function version(binary, cwd, env, signal) {
3621
- return new Promise((resolve7, reject) => {
3424
+ return new Promise((resolve3, reject) => {
3622
3425
  execFile2(binary, ["--version"], { cwd, env, signal, timeout: 5e3, killSignal: "SIGKILL", maxBuffer: 4096 }, (error, stdout, stderr) => {
3623
3426
  if (error)
3624
3427
  reject(new Error(`smolvm runtime unavailable: ${stderr.trim() || error.message}`));
3625
3428
  else if (stdout.trim() !== "smolvm 1.14.6")
3626
3429
  reject(new Error("smolvm 1.14.6 is required; other versions need lifecycle qualification"));
3627
3430
  else
3628
- resolve7();
3431
+ resolve3();
3629
3432
  });
3630
3433
  });
3631
3434
  }
@@ -3691,8 +3494,8 @@ async function runSmolvm(options) {
3691
3494
  const binary = options.binary ?? "smolvm";
3692
3495
  await version(binary, root, env, controller.signal);
3693
3496
  const archive = join8(root, "image.tar");
3694
- const digest2 = await hashArchive(options.imageArchive, archive, controller.signal);
3695
- if (options.imageDigest && digest2 !== options.imageDigest)
3497
+ const digest = await hashArchive(options.imageArchive, archive, controller.signal);
3498
+ if (options.imageDigest && digest !== options.imageDigest)
3696
3499
  throw new Error("smolvm archive identity mismatch");
3697
3500
  const args = [
3698
3501
  "--pdeathsig",
@@ -3726,7 +3529,7 @@ async function runSmolvm(options) {
3726
3529
  }
3727
3530
  args.push("--", ...options.command);
3728
3531
  controller.signal.throwIfAborted();
3729
- await new Promise((resolve7) => {
3532
+ await new Promise((resolve3) => {
3730
3533
  const child = spawn4("setpriv", args, { cwd: root, env, stdio: ["pipe", "pipe", "pipe"], detached: true });
3731
3534
  const stdout = [], stderr = [];
3732
3535
  let stdoutBytes = 0, stderrBytes = 0;
@@ -3799,7 +3602,7 @@ async function runSmolvm(options) {
3799
3602
  result.stderr = Buffer.concat(stderr, stderrBytes).toString("utf8");
3800
3603
  if (!banner || signal)
3801
3604
  result.error ??= `smolvm launcher failed${signal ? ` (${signal})` : " before guest execution"}`;
3802
- resolve7();
3605
+ resolve3();
3803
3606
  });
3804
3607
  controller.signal.addEventListener("abort", abort, { once: true });
3805
3608
  if (controller.signal.aborted)
@@ -3841,6 +3644,7 @@ async function runSmolvm(options) {
3841
3644
  // packages/core/dist/runtime/smolvm-broker.js
3842
3645
  var ADMISSION = "/run/0-workbench/admission.json";
3843
3646
  var GUEST_ROOT = "/run/0-workbench/broker";
3647
+ var WORKBENCH_BROKER_WORKSPACE = "/tmp/0-workspace";
3844
3648
  var DIGEST = /^sha256:[a-f0-9]{64}$/;
3845
3649
  var REFERENCE = /^(?:sha256:[a-f0-9]{64}|[a-zA-Z0-9][a-zA-Z0-9_.:/-]*@sha256:[a-f0-9]{64})$/;
3846
3650
  var POLL_MS = 15;
@@ -3860,7 +3664,7 @@ var DEFAULT_WORKBENCH_BROKER_LIMITS = Object.freeze({
3860
3664
  maxInputBytes: 1024 * 1024
3861
3665
  });
3862
3666
  function hash2(data) {
3863
- return `sha256:${createHash5("sha256").update(data).digest("hex")}`;
3667
+ return `sha256:${createHash4("sha256").update(data).digest("hex")}`;
3864
3668
  }
3865
3669
  function problem(error) {
3866
3670
  return error instanceof Error ? error.message : String(error);
@@ -3905,8 +3709,8 @@ function admission() {
3905
3709
  if (marker.schemaVersion !== 1 || marker.profile !== "smolvm-workbench" || !broker || broker.protocol !== 1 || broker.root !== GUEST_ROOT || typeof marker.runtimeId !== "string" || !/^[a-f0-9]{64}$/.test(marker.runtimeId) || broker.runtimeId !== marker.runtimeId || !DIGEST.test(broker.imageDigest) || typeof broker.allowHttp !== "boolean" || !broker.approvedImages || typeof broker.approvedImages !== "object")
3906
3710
  throw new Error("Workbench broker admission identity mismatch");
3907
3711
  broker.limits = Object.freeze(checkedLimits(broker.limits));
3908
- for (const [reference, digest2] of Object.entries(broker.approvedImages))
3909
- if (!REFERENCE.test(reference) || !DIGEST.test(digest2))
3712
+ for (const [reference, digest] of Object.entries(broker.approvedImages))
3713
+ if (!REFERENCE.test(reference) || !DIGEST.test(digest))
3910
3714
  throw new Error("Invalid admitted image catalog");
3911
3715
  return immutableAdmission = broker;
3912
3716
  } finally {
@@ -3935,10 +3739,10 @@ function resolveWorkbenchBrokerImage(reference) {
3935
3739
  const grant = admission();
3936
3740
  if (reference === void 0 || reference === grant.imageDigest)
3937
3741
  return grant.imageDigest;
3938
- const digest2 = Object.hasOwn(grant.approvedImages, reference) ? grant.approvedImages[reference] : void 0;
3939
- if (!digest2)
3742
+ const digest = Object.hasOwn(grant.approvedImages, reference) ? grant.approvedImages[reference] : void 0;
3743
+ if (!digest)
3940
3744
  throw new Error(`Sandbox image is not explicitly approved: ${reference}`);
3941
- return digest2;
3745
+ return digest;
3942
3746
  }
3943
3747
  async function regularBytes(path, maximum) {
3944
3748
  const file = await open3(path, constants4.O_RDONLY | constants4.O_NOFOLLOW | constants4.O_NONBLOCK);
@@ -3987,8 +3791,8 @@ async function snapshot(root, limits) {
3987
3791
  if ((entries += names.length) > limits.maxFiles * 4)
3988
3792
  throw new Error("Broker workspace has too many filesystem entries");
3989
3793
  for (const name of names.sort()) {
3990
- const path = join9(directory, name), relativePath2 = relative(root, path).split(sep).join("/");
3991
- if (!safePath(relativePath2))
3794
+ const path = join9(directory, name), relativePath = relative(root, path).split(sep).join("/");
3795
+ if (!safePath(relativePath))
3992
3796
  throw new Error("Invalid broker workspace path");
3993
3797
  const info = await lstat3(path);
3994
3798
  if (info.isDirectory()) {
@@ -4001,7 +3805,7 @@ async function snapshot(root, limits) {
4001
3805
  throw new Error("Broker workspace exceeds its file/byte limits");
4002
3806
  const content = await regularBytes(path, limits.maxWorkspaceBytes - bytes);
4003
3807
  bytes += content.length;
4004
- files.push({ path: relativePath2, digest: hash2(content), data: content.toString("base64"), mode: info.mode & 73 ? 448 : 384 });
3808
+ files.push({ path: relativePath, digest: hash2(content), data: content.toString("base64"), mode: info.mode & 73 ? 448 : 384 });
4005
3809
  }
4006
3810
  }
4007
3811
  await walk(root);
@@ -4083,25 +3887,25 @@ async function httpCommand(request, signal) {
4083
3887
  const host = url.hostname.replace(/^\[|\]$/g, "");
4084
3888
  if (!["http:", "https:"].includes(url.protocol) || url.username || url.password || !host || /^(?:localhost|.*\.localhost)$/i.test(host))
4085
3889
  throw new Error("HTTP broker requires a public HTTP(S) URL without userinfo");
4086
- const argv2 = request.command;
3890
+ const argv = request.command;
4087
3891
  const prefix = ["curl", "--disable", "--globoff", "--silent", "--show-error", "--request"];
4088
- if (!prefix.every((arg, index2) => argv2[index2] === arg) || !/^[A-Za-z]{1,32}$/.test(argv2[6] ?? ""))
3892
+ if (!prefix.every((arg, index2) => argv[index2] === arg) || !/^[A-Za-z]{1,32}$/.test(argv[6] ?? ""))
4089
3893
  throw new Error("HTTP broker permits only declarative curl requests");
4090
- const fixed = ["--max-time", argv2[8], "--proto", "=http,https", "--proto-redir", "=http,https", "--max-redirs", "0", "--noproxy", "*", "--output", "-", "--write-out", "%{stderr}\n__ZERO_HTTP_STATUS__:%{http_code}\n"];
4091
- if (!fixed.every((arg, index2) => argv2[index2 + 7] === arg) || !/^[0-9]+(?:\.[0-9]+)?$/.test(argv2[8] ?? "") || Number(argv2[8]) <= 0 || Number(argv2[8]) > request.timeoutMs / 1e3)
3894
+ const fixed = ["--max-time", argv[8], "--proto", "=http,https", "--proto-redir", "=http,https", "--max-redirs", "0", "--noproxy", "*", "--output", "-", "--write-out", "%{stderr}\n__ZERO_HTTP_STATUS__:%{http_code}\n"];
3895
+ if (!fixed.every((arg, index2) => argv[index2 + 7] === arg) || !/^[0-9]+(?:\.[0-9]+)?$/.test(argv[8] ?? "") || Number(argv[8]) <= 0 || Number(argv[8]) > request.timeoutMs / 1e3)
4092
3896
  throw new Error("HTTP broker curl policy mismatch");
4093
3897
  let index = 21, headerBytes = 0, headers = 0;
4094
- while (argv2[index] === "--header") {
4095
- const header = argv2[index + 1];
3898
+ while (argv[index] === "--header") {
3899
+ const header = argv[index + 1];
4096
3900
  if (typeof header !== "string" || !/^[!#$%&'*+.^_`|~0-9A-Za-z-]+:[^\r\n\0]*$/.test(header) || ++headers > 64 || (headerBytes += Buffer.byteLength(header)) > 65536)
4097
3901
  throw new Error("Invalid bounded HTTP header");
4098
3902
  index += 2;
4099
3903
  }
4100
- if (argv2[index] === "--data-binary" && argv2[index + 1] === "@-")
3904
+ if (argv[index] === "--data-binary" && argv[index + 1] === "@-")
4101
3905
  index += 2;
4102
- if (argv2[index] !== "--" || argv2[index + 1] !== request.httpTarget || argv2.length !== index + 2)
3906
+ if (argv[index] !== "--" || argv[index + 1] !== request.httpTarget || argv.length !== index + 2)
4103
3907
  throw new Error("HTTP broker rejects additional curl flags, files, redirects or destinations");
4104
- const addresses = isIP2(host) ? [{ address: host, family: isIP2(host) }] : await new Promise((resolve7, reject) => {
3908
+ const addresses = isIP2(host) ? [{ address: host, family: isIP2(host) }] : await new Promise((resolve3, reject) => {
4105
3909
  let settled = false;
4106
3910
  const finish = (error, result) => {
4107
3911
  if (settled)
@@ -4112,7 +3916,7 @@ async function httpCommand(request, signal) {
4112
3916
  if (error)
4113
3917
  reject(error);
4114
3918
  else
4115
- resolve7(result);
3919
+ resolve3(result);
4116
3920
  };
4117
3921
  const timer = setTimeout(() => finish(new Error("HTTP broker DNS resolution exceeded its deadline")), Math.min(5e3, request.timeoutMs));
4118
3922
  const abort = () => finish(new Error("HTTP broker DNS resolution cancelled"));
@@ -4125,18 +3929,21 @@ async function httpCommand(request, signal) {
4125
3929
  if (!addresses.length || addresses.some(({ address: address2 }) => !isPublicRepositoryAddress(address2)))
4126
3930
  throw new Error("HTTP broker refuses private, loopback, reserved or mixed DNS destinations");
4127
3931
  if (isIP2(host))
4128
- return [...argv2];
3932
+ return [...argv];
4129
3933
  const address = addresses[0];
4130
3934
  const port = url.port || (url.protocol === "https:" ? "443" : "80");
4131
3935
  const pinned = address.family === 6 ? `[${address.address}]` : address.address;
4132
- return [...argv2.slice(0, index), "--resolve", `${host}:${port}:${pinned}`, ...argv2.slice(index)];
3936
+ return [...argv.slice(0, index), "--resolve", `${host}:${port}:${pinned}`, ...argv.slice(index)];
4133
3937
  }
4134
3938
  var SIBLING_TRANSPORT = String.raw`
4135
3939
  const fs = require('node:fs'), p = require('node:path'), crypto = require('node:crypto'), cp = require('node:child_process');
4136
3940
  const cfg = JSON.parse(fs.readFileSync('/control/program.json', 'utf8'));
4137
3941
  const send = value => process.stdout.write(JSON.stringify(value) + '\n');
4138
3942
  const digest = b => 'sha256:' + crypto.createHash('sha256').update(b).digest('hex');
4139
- const workspace = fs.mkdtempSync('/tmp/0-workspace-');
3943
+ const workspace = ${JSON.stringify(WORKBENCH_BROKER_WORKSPACE)};
3944
+ // A fresh guest must not contain this path, including a dangling symlink.
3945
+ fs.mkdirSync(workspace,{mode:0o700});
3946
+ const workspaceInfo = fs.lstatSync(workspace);
4140
3947
  for (const file of cfg.files) {
4141
3948
  const destination = p.join(workspace,file.path);
4142
3949
  fs.mkdirSync(p.dirname(destination),{recursive:true,mode:0o700});
@@ -4155,6 +3962,8 @@ child.on('spawn',()=>send({type:'ready'}));
4155
3962
  child.on('close',(code,signal)=>{
4156
3963
  let bytes=0,files=0,entries=0;
4157
3964
  try {
3965
+ const current = fs.lstatSync(workspace);
3966
+ if(!current.isDirectory()||current.dev!==workspaceInfo.dev||current.ino!==workspaceInfo.ino)throw Error('sandbox workspace was replaced');
4158
3967
  const walk=dir=>{const names=fs.readdirSync(dir).sort();entries+=names.length;if(entries>cfg.maxFiles*4)throw Error('sandbox artifact entry limit');for(const name of names){const path=p.join(dir,name), rel=p.relative(workspace,path),st=fs.lstatSync(path);if(st.isDirectory()){walk(path);continue;}if(!st.isFile()||st.nlink!==1)throw Error('sandbox artifact is not a regular unlinked file');if(++files>cfg.maxFiles||bytes+st.size>cfg.maxWorkspaceBytes)throw Error('sandbox artifact limit');const fd=fs.openSync(path,fs.constants.O_RDONLY|fs.constants.O_NOFOLLOW|fs.constants.O_NONBLOCK);let b;try{const a=fs.fstatSync(fd);b=fs.readFileSync(fd);const z=fs.fstatSync(fd);if(!a.isFile()||a.nlink!==1||a.size!==z.size||a.mtimeMs!==z.mtimeMs||b.length!==a.size)throw Error('sandbox artifact changed');}finally{fs.closeSync(fd);}bytes+=b.length;send({type:'file',file:{path:rel,digest:digest(b),data:b.toString('base64'),mode:st.mode&0o111?448:384}});}};
4159
3968
  walk(workspace); send({type:'done',exitCode:code,error:failed?'sandbox transport failed':signal?'sandbox process signalled '+signal:undefined});
4160
3969
  } catch(e) { send({type:'done',exitCode:code,error:e.message}); }
@@ -4188,8 +3997,8 @@ async function startWorkbenchBroker(options) {
4188
3997
  let polling;
4189
3998
  try {
4190
3999
  await mkdir4(guestRoot, { mode: 448 });
4191
- async function approve(archive, digest2, index) {
4192
- if (!DIGEST.test(digest2) || !isAbsolute5(archive) || resolve2(archive) !== await realpath3(archive))
4000
+ async function approve(archive, digest, index) {
4001
+ if (!DIGEST.test(digest) || !isAbsolute5(archive) || resolve2(archive) !== await realpath3(archive))
4193
4002
  throw new Error("Invalid trusted broker image archive");
4194
4003
  const file = join9(privateRoot, `image-${index}.tar`);
4195
4004
  const info = await lstat3(archive);
@@ -4197,7 +4006,7 @@ async function startWorkbenchBroker(options) {
4197
4006
  throw new Error("Invalid approved broker archive file");
4198
4007
  await copyFile3(archive, file, constants4.COPYFILE_EXCL | constants4.COPYFILE_FICLONE);
4199
4008
  await chmod2(file, 256);
4200
- if (await resolveSmolvmImage(file) !== digest2)
4009
+ if (await resolveSmolvmImage(file) !== digest)
4201
4010
  throw new Error("Approved broker archive copy identity mismatch");
4202
4011
  return file;
4203
4012
  }
@@ -4678,770 +4487,7 @@ async function runWorkbenchBrokerProgram(options, signal, channel) {
4678
4487
  }
4679
4488
  }
4680
4489
 
4681
- // packages/core/dist/improvement/config.js
4682
- import { existsSync as existsSync2, readFileSync as readFileSync5, realpathSync } from "node:fs";
4683
- import { homedir as homedir3 } from "node:os";
4684
- import { isAbsolute as isAbsolute6, resolve as resolve3, sep as sep2 } from "node:path";
4685
- var relativePath = external_exports.string().min(1).max(512).refine((value) => !isAbsolute6(value) && !value.includes("\\") && !value.includes("\0") && value !== "." && value.split("/").every((part) => part !== ".." && part !== "" && part !== "."), "must be a normalized relative file or directory path (not the entire workspace)");
4686
- var argv = external_exports.array(external_exports.string().max(8192).refine((value) => !value.includes("\0"))).min(1).max(128).refine((value) => value[0].length > 0, "executable must not be empty");
4687
- var finitePositive = external_exports.number().finite().positive();
4688
- var schema = external_exports.object({
4689
- schemaVersion: external_exports.literal(1),
4690
- sourceRoot: external_exports.string().min(1),
4691
- storePath: external_exports.string().min(1),
4692
- image: external_exports.string().min(1).max(512).regex(/^[a-zA-Z0-9][a-zA-Z0-9_./:@-]*$/),
4693
- backend: external_exports.enum(["docker", "smolvm"]).optional(),
4694
- imageArchive: external_exports.string().min(1).max(4096).refine((value) => !value.includes("\0")).optional(),
4695
- sourcePaths: external_exports.array(relativePath).min(1).max(256),
4696
- editablePaths: external_exports.array(relativePath).min(1).max(256),
4697
- kind: external_exports.enum(["source", "skill", "router", "lens"]).default("source"),
4698
- command: argv,
4699
- buildCommand: argv.optional(),
4700
- cases: external_exports.array(external_exports.object({
4701
- id: external_exports.string().min(1).max(128),
4702
- lane: external_exports.enum(["development", "held-out", "negative-control"]),
4703
- input: external_exports.unknown(),
4704
- expected: external_exports.unknown()
4705
- }).strict()).min(3).max(1e3),
4706
- repeats: external_exports.number().int().min(2).max(20).default(3),
4707
- maxIterations: external_exports.number().int().min(1).max(100).default(3),
4708
- maxModelTurns: external_exports.number().int().min(1).max(64).default(12),
4709
- maxModelCostUsd: finitePositive.max(1e3).default(5),
4710
- maxEvaluationCostUsd: finitePositive.max(1e3).default(5),
4711
- computeUsdPerSecond: finitePositive.max(10),
4712
- timeoutMs: external_exports.number().int().min(100).max(6e5).default(6e4),
4713
- memoryMb: external_exports.number().int().min(32).max(16384).default(1024),
4714
- cpus: finitePositive.max(16).default(1),
4715
- maxOutputBytes: external_exports.number().int().min(256).max(16 * 1024 * 1024).default(64 * 1024),
4716
- maxSourceBytes: external_exports.number().int().min(1024).max(512 * 1024 * 1024).default(64 * 1024 * 1024),
4717
- maxChangedBytes: external_exports.number().int().min(128).max(4 * 1024 * 1024).default(256 * 1024),
4718
- model: external_exports.string().min(1).optional(),
4719
- objective: external_exports.string().min(1).max(16e3),
4720
- allowModelSourceAccess: external_exports.boolean().default(false),
4721
- autoPromote: external_exports.boolean().default(false),
4722
- canaryTrials: external_exports.number().int().min(1).max(20).default(2),
4723
- promotionPolicy: external_exports.object({
4724
- minimumCases: external_exports.number().int().min(3).max(1e3).default(DEFAULT_IMPROVEMENT_PROMOTION_POLICY.minimumCases),
4725
- minimumDevelopmentLift: finitePositive.max(1).default(DEFAULT_IMPROVEMENT_PROMOTION_POLICY.minimumDevelopmentLift),
4726
- minimumHeldOutLift: finitePositive.max(1).default(DEFAULT_IMPROVEMENT_PROMOTION_POLICY.minimumHeldOutLift),
4727
- maximumNegativeControlFpDelta: external_exports.literal(0).default(0),
4728
- maximumCostMultiplier: finitePositive.max(10).default(DEFAULT_IMPROVEMENT_PROMOTION_POLICY.maximumCostMultiplier)
4729
- }).strict().default({}),
4730
- maxAlternativeParents: external_exports.number().int().min(0).max(10).optional()
4731
- }).strict();
4732
- function canonicalEvolutionJson(value) {
4733
- if (value === null || typeof value === "string" || typeof value === "boolean")
4734
- return JSON.stringify(value);
4735
- if (typeof value === "number" && Number.isFinite(value))
4736
- return JSON.stringify(value);
4737
- if (Array.isArray(value))
4738
- return `[${Array.from(value, canonicalEvolutionJson).join(",")}]`;
4739
- if (typeof value === "object" && value !== null && Object.getPrototypeOf(value) === Object.prototype) {
4740
- return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${canonicalEvolutionJson(value[key])}`).join(",")}}`;
4741
- }
4742
- throw new Error("evolution cases must contain finite JSON values only");
4743
- }
4744
- function parseEvolutionConfig(raw, baseDir = process.cwd()) {
4745
- const parsed = schema.parse(raw);
4746
- const admitted = isAdmittedSmolvmWorkbench();
4747
- if (admitted) {
4748
- if (parsed.imageArchive)
4749
- throw new Error("Admitted workbench images must use the host-approved catalog, not guest archive paths");
4750
- parsed.backend = "smolvm";
4751
- }
4752
- if (parsed.backend === "smolvm") {
4753
- if (!parsed.imageArchive && !admitted)
4754
- throw new Error("smolvm requires a local imageArchive");
4755
- if (!Number.isInteger(parsed.cpus))
4756
- throw new Error("smolvm cpus must be an integer");
4757
- if (parsed.imageArchive)
4758
- parsed.imageArchive = resolve3(baseDir, parsed.imageArchive);
4759
- } else if (parsed.imageArchive !== void 0) {
4760
- throw new Error("imageArchive is only valid with backend smolvm");
4761
- }
4762
- if (parsed.cases.length * parsed.repeats * 4 * parsed.maxOutputBytes > 64 * 1024 * 1024) {
4763
- throw new Error("evaluation stream retention exceeds 64 MiB; reduce cases, repeats or maxOutputBytes");
4764
- }
4765
- const seenIds = /* @__PURE__ */ new Set();
4766
- const inputs = /* @__PURE__ */ new Map();
4767
- for (const entry of parsed.cases) {
4768
- if (seenIds.has(entry.id))
4769
- throw new Error(`duplicate evolution case id: ${entry.id}`);
4770
- seenIds.add(entry.id);
4771
- const input = canonicalEvolutionJson(entry.input);
4772
- canonicalEvolutionJson(entry.expected);
4773
- if (inputs.has(input))
4774
- throw new Error(`duplicate evaluation input: ${entry.id}; cases and lanes must be independent`);
4775
- inputs.set(input, entry.lane);
4776
- }
4777
- for (const lane of ["development", "held-out", "negative-control"]) {
4778
- if (parsed.cases.filter((entry) => entry.lane === lane).length < parsed.promotionPolicy.minimumCases) {
4779
- throw new Error(`${lane} requires at least ${parsed.promotionPolicy.minimumCases} distinct cases`);
4780
- }
4781
- }
4782
- for (const edit of parsed.editablePaths) {
4783
- if (!parsed.sourcePaths.some((source) => edit === source || edit.startsWith(`${source}/`))) {
4784
- throw new Error(`editable path ${edit} is not inside a selected source path`);
4785
- }
4786
- }
4787
- const sourceRoot = resolve3(baseDir, parsed.sourceRoot === "~" ? homedir3() : parsed.sourceRoot.startsWith("~/") ? resolve3(homedir3(), parsed.sourceRoot.slice(2)) : parsed.sourceRoot);
4788
- const storePath = resolve3(baseDir, parsed.storePath === "~" ? homedir3() : parsed.storePath.startsWith("~/") ? resolve3(homedir3(), parsed.storePath.slice(2)) : parsed.storePath);
4789
- if (sourceRoot === storePath)
4790
- throw new Error("evolution store cannot be the source workspace");
4791
- for (const selected of parsed.sourcePaths) {
4792
- const source = resolve3(sourceRoot, selected);
4793
- if (storePath === source || storePath.startsWith(`${source}/`) || source.startsWith(`${storePath}/`)) {
4794
- throw new Error("evolution store and selected source paths must not overlap");
4795
- }
4796
- }
4797
- return JSON.parse(JSON.stringify({ ...parsed, sourceRoot, storePath }));
4798
- }
4799
- function loadEvolutionConfigFile(path) {
4800
- const absPath = resolve3(path);
4801
- if (!existsSync2(absPath))
4802
- throw new Error(`config file not found: ${absPath}`);
4803
- const configFile = realpathSync(absPath);
4804
- const raw = JSON.parse(readFileSync5(absPath, "utf8"));
4805
- const config = parseEvolutionConfig(raw, resolve3(path, ".."));
4806
- for (const source of config.sourcePaths) {
4807
- const sourcePath = resolve3(config.sourceRoot, source);
4808
- const selected = existsSync2(sourcePath) ? realpathSync(sourcePath) : sourcePath;
4809
- if (configFile === selected || configFile.startsWith(`${selected}${sep2}`)) {
4810
- throw new Error("evolution config contains private answers and must be outside selected source paths");
4811
- }
4812
- }
4813
- return config;
4814
- }
4815
-
4816
- // packages/core/dist/verification-spec/spec.js
4817
- import { execFile as execFile3 } from "node:child_process";
4818
- import { promises as fs } from "node:fs";
4819
- import { tmpdir as tmpdir3 } from "node:os";
4820
- import { join as join10, resolve as resolve4, isAbsolute as isAbsolute7, normalize, sep as sep3 } from "node:path";
4821
- import { promisify } from "node:util";
4822
- var execFileAsync = promisify(execFile3);
4823
- var MAX_GIT_DIFF_BYTES = 1e6;
4824
- var GIT_CHECK_TIMEOUT_MS = 5e3;
4825
- var FULL_GIT_OBJECT_ID_RE = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/i;
4826
- var MAX_PATTERN_LENGTH = 512;
4827
- var MAX_FILE_BYTES = 1e6;
4828
- function resolveRepoPath(repoRoot, file) {
4829
- if (typeof file !== "string" || file.length === 0)
4830
- return null;
4831
- if (isAbsolute7(file))
4832
- return null;
4833
- const root = resolve4(repoRoot);
4834
- const candidate = resolve4(root, file);
4835
- const normalized = normalize(candidate);
4836
- if (normalized !== root && !normalized.startsWith(root + sep3)) {
4837
- return null;
4838
- }
4839
- return normalized;
4840
- }
4841
- async function checkRepoBoundary(repoRoot, absPath) {
4842
- try {
4843
- await fs.lstat(absPath);
4844
- } catch {
4845
- return "missing";
4846
- }
4847
- try {
4848
- const [realRoot, realTarget] = await Promise.all([
4849
- fs.realpath(repoRoot),
4850
- fs.realpath(absPath)
4851
- ]);
4852
- if (realTarget === realRoot || realTarget.startsWith(realRoot + sep3)) {
4853
- return "inside";
4854
- }
4855
- return "outside";
4856
- } catch {
4857
- return "outside";
4858
- }
4859
- }
4860
- function safeRegex(pattern, flags) {
4861
- if (typeof pattern !== "string")
4862
- return null;
4863
- if (pattern.length > MAX_PATTERN_LENGTH)
4864
- return null;
4865
- try {
4866
- return new RegExp(pattern, flags);
4867
- } catch {
4868
- return null;
4869
- }
4870
- }
4871
- async function readFileSafe(absPath) {
4872
- try {
4873
- const stat2 = await fs.stat(absPath);
4874
- if (!stat2.isFile())
4875
- return null;
4876
- if (stat2.size > MAX_FILE_BYTES)
4877
- return null;
4878
- return await fs.readFile(absPath, "utf8");
4879
- } catch {
4880
- return null;
4881
- }
4882
- }
4883
- async function evaluateGitDiffApplies(predicate, repoRoot) {
4884
- if (predicate.diff.trim().length === 0) {
4885
- return { predicate, passed: false, reason: "git diff is empty" };
4886
- }
4887
- if (Buffer.byteLength(predicate.diff, "utf8") > MAX_GIT_DIFF_BYTES) {
4888
- return {
4889
- predicate,
4890
- passed: false,
4891
- reason: `git diff exceeds ${MAX_GIT_DIFF_BYTES} bytes`
4892
- };
4893
- }
4894
- if (!FULL_GIT_OBJECT_ID_RE.test(predicate.baseCommit)) {
4895
- return { predicate, passed: false, reason: "invalid git base commit" };
4896
- }
4897
- let head;
4898
- try {
4899
- const result = await execFileAsync("git", ["rev-parse", "--verify", "HEAD"], {
4900
- cwd: repoRoot,
4901
- timeout: GIT_CHECK_TIMEOUT_MS,
4902
- maxBuffer: 4096
4903
- });
4904
- head = result.stdout.trim();
4905
- } catch {
4906
- return {
4907
- predicate,
4908
- passed: false,
4909
- reason: "git repository HEAD unavailable"
4910
- };
4911
- }
4912
- if (head.toLowerCase() !== predicate.baseCommit.toLowerCase()) {
4913
- return {
4914
- predicate,
4915
- passed: false,
4916
- reason: "git HEAD does not match diff base commit"
4917
- };
4918
- }
4919
- let tempDir;
4920
- try {
4921
- tempDir = await fs.mkdtemp(join10(tmpdir3(), "0-verify-diff-"));
4922
- const patchPath = join10(tempDir, "evidence.patch");
4923
- await fs.writeFile(patchPath, predicate.diff, { encoding: "utf8", mode: 384 });
4924
- await execFileAsync("git", ["apply", "--check", "--whitespace=nowarn", "--", patchPath], {
4925
- cwd: repoRoot,
4926
- timeout: GIT_CHECK_TIMEOUT_MS,
4927
- maxBuffer: 65536
4928
- });
4929
- return {
4930
- predicate,
4931
- passed: true,
4932
- reason: "git diff applies cleanly"
4933
- };
4934
- } catch {
4935
- return {
4936
- predicate,
4937
- passed: false,
4938
- reason: "git diff does not apply cleanly"
4939
- };
4940
- } finally {
4941
- if (tempDir) {
4942
- try {
4943
- await fs.rm(tempDir, { recursive: true, force: true });
4944
- } catch {
4945
- }
4946
- }
4947
- }
4948
- }
4949
- async function evaluateOne(predicate, repoRoot) {
4950
- switch (predicate.kind) {
4951
- case "file-exists": {
4952
- const abs = resolveRepoPath(repoRoot, predicate.file);
4953
- if (!abs) {
4954
- return {
4955
- predicate,
4956
- passed: false,
4957
- reason: `path escapes repo root or is invalid: ${predicate.file}`
4958
- };
4959
- }
4960
- const outcome = await checkRepoBoundary(repoRoot, abs);
4961
- if (outcome === "outside") {
4962
- return {
4963
- predicate,
4964
- passed: false,
4965
- reason: `path resolves outside repo root: ${predicate.file}`
4966
- };
4967
- }
4968
- const ok = outcome === "inside";
4969
- return {
4970
- predicate,
4971
- passed: ok,
4972
- reason: ok ? "file exists" : `file not found: ${predicate.file}`
4973
- };
4974
- }
4975
- case "file-contains": {
4976
- const abs = resolveRepoPath(repoRoot, predicate.file);
4977
- if (!abs) {
4978
- return {
4979
- predicate,
4980
- passed: false,
4981
- reason: `path escapes repo root or is invalid: ${predicate.file}`
4982
- };
4983
- }
4984
- const outcome = await checkRepoBoundary(repoRoot, abs);
4985
- if (outcome === "outside") {
4986
- return {
4987
- predicate,
4988
- passed: false,
4989
- reason: `path resolves outside repo root: ${predicate.file}`
4990
- };
4991
- }
4992
- if (outcome === "missing") {
4993
- return {
4994
- predicate,
4995
- passed: false,
4996
- reason: `file not found or unreadable: ${predicate.file}`
4997
- };
4998
- }
4999
- const content = await readFileSafe(abs);
5000
- if (content === null) {
5001
- return {
5002
- predicate,
5003
- passed: false,
5004
- reason: `file not found or unreadable: ${predicate.file}`
5005
- };
5006
- }
5007
- const re = safeRegex(predicate.pattern, predicate.flags);
5008
- if (!re) {
5009
- return {
5010
- predicate,
5011
- passed: false,
5012
- reason: `invalid regex: /${predicate.pattern}/${predicate.flags ?? ""}`
5013
- };
5014
- }
5015
- const matched = re.test(content);
5016
- return {
5017
- predicate,
5018
- passed: matched,
5019
- reason: matched ? `pattern matched in ${predicate.file}` : `pattern not found in ${predicate.file}`
5020
- };
5021
- }
5022
- case "file-missing-pattern": {
5023
- const abs = resolveRepoPath(repoRoot, predicate.file);
5024
- if (!abs) {
5025
- return {
5026
- predicate,
5027
- passed: false,
5028
- reason: `path escapes repo root or is invalid: ${predicate.file}`
5029
- };
5030
- }
5031
- const outcome = await checkRepoBoundary(repoRoot, abs);
5032
- if (outcome === "outside") {
5033
- return {
5034
- predicate,
5035
- passed: false,
5036
- reason: `path resolves outside repo root: ${predicate.file}`
5037
- };
5038
- }
5039
- if (outcome === "missing") {
5040
- return {
5041
- predicate,
5042
- passed: false,
5043
- reason: `file not found or unreadable: ${predicate.file}`
5044
- };
5045
- }
5046
- const content = await readFileSafe(abs);
5047
- if (content === null) {
5048
- return {
5049
- predicate,
5050
- passed: false,
5051
- reason: `file not found or unreadable: ${predicate.file}`
5052
- };
5053
- }
5054
- const re = safeRegex(predicate.pattern, predicate.flags);
5055
- if (!re) {
5056
- return {
5057
- predicate,
5058
- passed: false,
5059
- reason: `invalid regex: /${predicate.pattern}/${predicate.flags ?? ""}`
5060
- };
5061
- }
5062
- const matched = re.test(content);
5063
- return {
5064
- predicate,
5065
- passed: !matched,
5066
- reason: matched ? `pattern unexpectedly present in ${predicate.file}` : `pattern absent in ${predicate.file}`
5067
- };
5068
- }
5069
- case "git-diff-applies":
5070
- return evaluateGitDiffApplies(predicate, repoRoot);
5071
- case "ast-shape": {
5072
- return {
5073
- predicate,
5074
- passed: false,
5075
- reason: "ast-shape predicates are not yet implemented in the OSS verifier"
5076
- };
5077
- }
5078
- default: {
5079
- const _exhaustive = predicate;
5080
- void _exhaustive;
5081
- return {
5082
- predicate,
5083
- passed: false,
5084
- reason: "unknown predicate kind"
5085
- };
5086
- }
5087
- }
5088
- }
5089
- async function evaluateVerificationSpec(spec, repoRoot) {
5090
- const results = [];
5091
- for (const predicate of spec.code) {
5092
- results.push(await evaluateOne(predicate, repoRoot));
5093
- }
5094
- const failedPredicates = results.filter((r) => !r.passed);
5095
- const hasVulnerabilityPredicate = spec.code.some((predicate) => predicate.kind !== "git-diff-applies");
5096
- if (!hasVulnerabilityPredicate && !spec.behavior) {
5097
- return {
5098
- passed: false,
5099
- failedPredicates,
5100
- reason: spec.code.length === 0 ? "no predicates" : "no vulnerability predicates"
5101
- };
5102
- }
5103
- const codePassed = failedPredicates.length === 0 && hasVulnerabilityPredicate;
5104
- if (spec.behavior) {
5105
- return {
5106
- passed: codePassed,
5107
- failedPredicates,
5108
- reason: "behavior eval not yet supported"
5109
- };
5110
- }
5111
- return {
5112
- passed: codePassed,
5113
- failedPredicates
5114
- };
5115
- }
5116
-
5117
- // packages/core/dist/improvement/artifacts.js
5118
- import { randomUUID as randomUUID3 } from "node:crypto";
5119
- import { closeSync as closeSync3, constants as constants5, fchmodSync, fstatSync as fstatSync2, fsyncSync as fsyncSync2, linkSync, lstatSync as lstatSync3, mkdirSync as mkdirSync4, openSync as openSync3, readFileSync as readFileSync6, unlinkSync, writeFileSync as writeFileSync3 } from "node:fs";
5120
- import { dirname as dirname4, isAbsolute as isAbsolute8, join as join11, parse, resolve as resolve5, sep as sep4 } from "node:path";
5121
- var MAX_ARTIFACT_BYTES = 128 * 1024 * 1024;
5122
- function hasCode(error, code) {
5123
- return error instanceof Error && "code" in error && error.code === code;
5124
- }
5125
- function inspectDirectories(path, create) {
5126
- if (!isAbsolute8(path))
5127
- throw new Error("evolution storage paths must be absolute");
5128
- const absolute = resolve5(path);
5129
- const root = parse(absolute).root;
5130
- let cursor = root;
5131
- for (const component of absolute.slice(root.length).split(sep4).filter(Boolean)) {
5132
- cursor = join11(cursor, component);
5133
- if (create) {
5134
- try {
5135
- mkdirSync4(cursor, { mode: 448 });
5136
- } catch (error) {
5137
- if (!hasCode(error, "EEXIST"))
5138
- throw error;
5139
- }
5140
- }
5141
- const stat2 = lstatSync3(cursor);
5142
- if (!stat2.isDirectory() || stat2.isSymbolicLink())
5143
- throw new Error(`unsafe evolution directory: ${cursor}`);
5144
- }
5145
- const leaf = lstatSync3(absolute);
5146
- if (typeof process.getuid === "function" && leaf.uid !== process.getuid() || (leaf.mode & 63) !== 0) {
5147
- throw new Error(`evolution storage directory must be owner-only: ${absolute}`);
5148
- }
5149
- return absolute;
5150
- }
5151
- function ensureEvolutionDirectory(path) {
5152
- return inspectDirectories(path, true);
5153
- }
5154
- function readEvolutionArtifact(path) {
5155
- inspectDirectories(dirname4(path), false);
5156
- const fd = openSync3(path, constants5.O_RDONLY | constants5.O_NOFOLLOW);
5157
- try {
5158
- const stat2 = fstatSync2(fd);
5159
- if (!stat2.isFile() || stat2.size > MAX_ARTIFACT_BYTES || (stat2.mode & 63) !== 0 || typeof process.getuid === "function" && stat2.uid !== process.getuid()) {
5160
- throw new Error(`unsafe evolution artifact: ${path}`);
5161
- }
5162
- const value = JSON.parse(readFileSync6(fd, "utf8"));
5163
- canonicalEvolutionJson(value);
5164
- return value;
5165
- } finally {
5166
- closeSync3(fd);
5167
- }
5168
- }
5169
- function publishEvolutionArtifact(path, value) {
5170
- const content = canonicalEvolutionJson(value);
5171
- if (Buffer.byteLength(content) > MAX_ARTIFACT_BYTES)
5172
- throw new Error("evolution artifact exceeds retention limit");
5173
- const directory = ensureEvolutionDirectory(dirname4(path));
5174
- const temporary = join11(directory, `.artifact-${randomUUID3()}.tmp`);
5175
- const fd = openSync3(temporary, constants5.O_WRONLY | constants5.O_CREAT | constants5.O_EXCL | constants5.O_NOFOLLOW, 384);
5176
- try {
5177
- try {
5178
- writeFileSync3(fd, content, "utf8");
5179
- fchmodSync(fd, 256);
5180
- fsyncSync2(fd);
5181
- } finally {
5182
- closeSync3(fd);
5183
- }
5184
- try {
5185
- linkSync(temporary, path);
5186
- } catch (error) {
5187
- if (!hasCode(error, "EEXIST"))
5188
- throw error;
5189
- if (canonicalEvolutionJson(readEvolutionArtifact(path)) !== content) {
5190
- throw new Error(`immutable evolution artifact collision: ${path}`);
5191
- }
5192
- }
5193
- const directoryFd = openSync3(directory, constants5.O_RDONLY | constants5.O_DIRECTORY | constants5.O_NOFOLLOW);
5194
- try {
5195
- fsyncSync2(directoryFd);
5196
- } finally {
5197
- closeSync3(directoryFd);
5198
- }
5199
- } finally {
5200
- unlinkSync(temporary);
5201
- }
5202
- }
5203
-
5204
- // packages/core/dist/agent/apply-patch.js
5205
- import { existsSync as existsSync3, mkdirSync as mkdirSync5, readFileSync as readFileSync7, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "node:fs";
5206
- import { dirname as dirname5 } from "node:path";
5207
- var BEGIN = "*** Begin Patch";
5208
- var END = "*** End Patch";
5209
- function parsePatch(input) {
5210
- const text = input.replace(/\r\n/g, "\n");
5211
- const lines = text.split("\n");
5212
- if (lines.length > 0 && lines[lines.length - 1] === "") {
5213
- lines.pop();
5214
- }
5215
- if (lines.length === 0 || lines[0].trim() !== BEGIN) {
5216
- throw new Error(`apply_patch: envelope must start with "${BEGIN}" (got: ${lines[0] ?? "<empty>"})`);
5217
- }
5218
- if (lines[lines.length - 1].trim() !== END) {
5219
- throw new Error(`apply_patch: envelope must end with "${END}"`);
5220
- }
5221
- const body = lines.slice(1, -1);
5222
- const ops = [];
5223
- let i = 0;
5224
- while (i < body.length) {
5225
- const line = body[i];
5226
- const trimmed = line.trim();
5227
- if (trimmed === "") {
5228
- i += 1;
5229
- continue;
5230
- }
5231
- const addMatch = /^\*\*\* Add File: (.+)$/.exec(trimmed);
5232
- const replaceMatch = /^\*\*\* Replace File: (.+)$/.exec(trimmed);
5233
- const updateMatch = /^\*\*\* Update File: (.+)$/.exec(trimmed);
5234
- const deleteMatch = /^\*\*\* Delete File: (.+)$/.exec(trimmed);
5235
- if (addMatch || replaceMatch) {
5236
- const path = (addMatch?.[1] ?? replaceMatch[1]).trim();
5237
- const overwrite = replaceMatch !== null;
5238
- i += 1;
5239
- const contentLines = [];
5240
- while (i < body.length && !isOpHeader(body[i])) {
5241
- const ln = body[i];
5242
- if (ln.length === 0) {
5243
- contentLines.push("");
5244
- } else if (ln[0] === "+") {
5245
- contentLines.push(ln.slice(1));
5246
- } else {
5247
- throw new Error(`apply_patch: Add/Replace body lines must start with "+" (got: ${JSON.stringify(ln)})`);
5248
- }
5249
- i += 1;
5250
- }
5251
- ops.push({
5252
- kind: "add",
5253
- path,
5254
- contents: contentLines.join("\n"),
5255
- overwrite
5256
- });
5257
- continue;
5258
- }
5259
- if (deleteMatch) {
5260
- ops.push({ kind: "delete", path: deleteMatch[1].trim() });
5261
- i += 1;
5262
- continue;
5263
- }
5264
- if (updateMatch) {
5265
- const path = updateMatch[1].trim();
5266
- i += 1;
5267
- const hunks = [];
5268
- while (i < body.length && !isOpHeader(body[i])) {
5269
- const hunkLine = body[i];
5270
- if (!hunkLine.startsWith("@@")) {
5271
- throw new Error(`apply_patch: Update File body must begin with "@@ <anchor>" hunk header (got: ${JSON.stringify(hunkLine)})`);
5272
- }
5273
- const anchor = hunkLine.slice(2).trim();
5274
- i += 1;
5275
- const hunkBody = [];
5276
- while (i < body.length && !isOpHeader(body[i]) && !body[i].startsWith("@@")) {
5277
- const ln = body[i];
5278
- if (ln === "") {
5279
- hunkBody.push({ kind: "context", text: "" });
5280
- } else if (ln[0] === " ") {
5281
- hunkBody.push({ kind: "context", text: ln.slice(1) });
5282
- } else if (ln[0] === "+") {
5283
- hunkBody.push({ kind: "add", text: ln.slice(1) });
5284
- } else if (ln[0] === "-") {
5285
- hunkBody.push({ kind: "del", text: ln.slice(1) });
5286
- } else {
5287
- throw new Error(`apply_patch: Update hunk body lines must start with " ", "+", or "-" (got: ${JSON.stringify(ln)})`);
5288
- }
5289
- i += 1;
5290
- }
5291
- hunks.push({ anchor, body: hunkBody });
5292
- }
5293
- if (hunks.length === 0) {
5294
- throw new Error(`apply_patch: "Update File: ${path}" requires at least one "@@" hunk`);
5295
- }
5296
- ops.push({ kind: "update", path, hunks });
5297
- continue;
5298
- }
5299
- throw new Error(`apply_patch: unknown directive (got: ${JSON.stringify(trimmed)})`);
5300
- }
5301
- if (ops.length === 0) {
5302
- throw new Error("apply_patch: envelope contains no operations");
5303
- }
5304
- return ops;
5305
- }
5306
- function isOpHeader(line) {
5307
- return line.startsWith("*** Add File:") || line.startsWith("*** Replace File:") || line.startsWith("*** Update File:") || line.startsWith("*** Delete File:");
5308
- }
5309
- function applyPatchOps(ops, pathResolver) {
5310
- const applied = [];
5311
- for (const op of ops) {
5312
- const abs = pathResolver(op.path);
5313
- if (op.kind === "add") {
5314
- if (!op.overwrite && existsSync3(abs)) {
5315
- throw new Error(`apply_patch: Add File "${op.path}" failed \u2014 file already exists. Use "*** Replace File: ${op.path}" to overwrite.`);
5316
- }
5317
- mkdirSync5(dirname5(abs), { recursive: true });
5318
- writeFileSync4(abs, op.contents, "utf-8");
5319
- applied.push({ kind: "add", path: op.path });
5320
- continue;
5321
- }
5322
- if (op.kind === "delete") {
5323
- if (!existsSync3(abs)) {
5324
- throw new Error(`apply_patch: Delete File "${op.path}" failed \u2014 file does not exist`);
5325
- }
5326
- unlinkSync2(abs);
5327
- applied.push({ kind: "delete", path: op.path });
5328
- continue;
5329
- }
5330
- if (!existsSync3(abs)) {
5331
- throw new Error(`apply_patch: Update File "${op.path}" failed \u2014 file does not exist`);
5332
- }
5333
- const original = readFileSync7(abs, "utf-8");
5334
- const updated = applyUpdateHunks(original, op.path, op.hunks);
5335
- writeFileSync4(abs, updated, "utf-8");
5336
- applied.push({ kind: "update", path: op.path });
5337
- }
5338
- return { applied };
5339
- }
5340
- function applyUpdateHunks(source, logicalPath, hunks) {
5341
- const hadTrailingNewline = source.endsWith("\n");
5342
- const sourceBody = hadTrailingNewline ? source.slice(0, -1) : source;
5343
- let lines = sourceBody.split("\n");
5344
- for (const hunk of hunks) {
5345
- const anchorIdx = locateAnchor(lines, hunk.anchor, logicalPath);
5346
- const { newLines, consumed } = applyOneHunk(lines, anchorIdx, hunk, logicalPath);
5347
- lines = [
5348
- ...lines.slice(0, anchorIdx),
5349
- ...newLines,
5350
- ...lines.slice(anchorIdx + consumed)
5351
- ];
5352
- }
5353
- return lines.join("\n") + (hadTrailingNewline ? "\n" : "");
5354
- }
5355
- function locateAnchor(lines, anchor, logicalPath) {
5356
- if (anchor === "") {
5357
- return 0;
5358
- }
5359
- const matches = [];
5360
- for (let i = 0; i < lines.length; i++) {
5361
- if (lines[i].includes(anchor)) {
5362
- matches.push(i);
5363
- }
5364
- }
5365
- if (matches.length === 0) {
5366
- throw new Error(`apply_patch: anchor "${anchor}" not found in ${logicalPath}; refine the @@ anchor to a line that exists in the file`);
5367
- }
5368
- if (matches.length > 1) {
5369
- throw new Error(`apply_patch: context "${anchor}" matches ${matches.length} locations in ${logicalPath}; refine the @@ anchor to be unique`);
5370
- }
5371
- return matches[0];
5372
- }
5373
- function applyOneHunk(lines, anchorIdx, hunk, logicalPath) {
5374
- const newLines = [];
5375
- let cursor = anchorIdx;
5376
- for (const entry of hunk.body) {
5377
- if (entry.kind === "context") {
5378
- const observed = lines[cursor];
5379
- if (observed === void 0 || observed !== entry.text) {
5380
- throw new Error(`apply_patch: context mismatch in ${logicalPath} at line ${cursor + 1}: expected ${JSON.stringify(entry.text)}, got ${JSON.stringify(observed ?? "<eof>")}`);
5381
- }
5382
- newLines.push(entry.text);
5383
- cursor += 1;
5384
- continue;
5385
- }
5386
- if (entry.kind === "del") {
5387
- const observed = lines[cursor];
5388
- if (observed === void 0 || observed !== entry.text) {
5389
- throw new Error(`apply_patch: removed line mismatch in ${logicalPath} at line ${cursor + 1}: expected ${JSON.stringify(entry.text)}, got ${JSON.stringify(observed ?? "<eof>")}`);
5390
- }
5391
- cursor += 1;
5392
- continue;
5393
- }
5394
- newLines.push(entry.text);
5395
- }
5396
- return { newLines, consumed: cursor - anchorIdx };
5397
- }
5398
-
5399
- // packages/core/dist/agent/tools/scope-path.js
5400
- import { lstatSync as lstatSync4, realpathSync as realpathSync2 } from "node:fs";
5401
- import { basename, dirname as dirname6, isAbsolute as isAbsolute9, resolve as resolve6, sep as sep5 } from "node:path";
5402
- function pathEntryExists(path) {
5403
- try {
5404
- lstatSync4(path);
5405
- return true;
5406
- } catch (error) {
5407
- if (error.code === "ENOENT")
5408
- return false;
5409
- throw error;
5410
- }
5411
- }
5412
- function canonicalizePath(path) {
5413
- const missingSegments = [];
5414
- let existingPath = path;
5415
- while (!pathEntryExists(existingPath)) {
5416
- const parent = dirname6(existingPath);
5417
- if (parent === existingPath) {
5418
- throw new Error(`Path does not have an existing parent: ${path}`);
5419
- }
5420
- missingSegments.unshift(basename(existingPath));
5421
- existingPath = parent;
5422
- }
5423
- return resolve6(realpathSync2(existingPath), ...missingSegments);
5424
- }
5425
- function isScopedPath(scopePath, inputPath) {
5426
- const root = realpathSync2(scopePath);
5427
- const logicalCandidate = isAbsolute9(inputPath) ? resolve6(inputPath) : resolve6(root, inputPath);
5428
- const candidate = canonicalizePath(logicalCandidate);
5429
- return candidate === root || candidate.startsWith(root + sep5);
5430
- }
5431
- function resolveScopedPath(scopePath, inputPath) {
5432
- if (isScopeEnforcementEnabled() && (!scopePath || !isScopedPath(scopePath, inputPath))) {
5433
- throw new Error(`Path escapes the allowed scope: ${inputPath}`);
5434
- }
5435
- const root = scopePath ? realpathSync2(scopePath) : process.cwd();
5436
- const logicalCandidate = isAbsolute9(inputPath) ? resolve6(inputPath) : resolve6(root, inputPath);
5437
- return canonicalizePath(logicalCandidate);
5438
- }
5439
-
5440
4490
  export {
5441
- DEFAULT_IMPROVEMENT_PROMOTION_POLICY,
5442
- evaluateImprovementPromotion,
5443
- appendImprovementLedgerEntry,
5444
- verifyImprovementLedger,
5445
4491
  superviseChild,
5446
4492
  runScannerProcess,
5447
4493
  buildSqlmapArgv,
@@ -5457,9 +4503,8 @@ export {
5457
4503
  shellTokens,
5458
4504
  ANALYTICS_LEVEL_ENV,
5459
4505
  levelAtLeast,
4506
+ analyticsOptedOut,
5460
4507
  resolveAnalyticsLevel,
5461
- presentationEventSink,
5462
- eventBus,
5463
4508
  DEFAULT_CLOUD_HOST,
5464
4509
  CloudAuthMissingError,
5465
4510
  CloudAuthError,
@@ -5493,6 +4538,7 @@ export {
5493
4538
  truncateMiddle,
5494
4539
  formatTruncated,
5495
4540
  parseRepositoryAcquisition,
4541
+ repositoryIdentityMatchesTarget,
5496
4542
  repositoryAcquisitionAllowed,
5497
4543
  runRepositoryAcquisition,
5498
4544
  assertSmolvmWorkbenchPlatform,
@@ -5504,20 +4550,11 @@ export {
5504
4550
  getSmolvmWorkbenchStatus,
5505
4551
  resolveSmolvmImage,
5506
4552
  runSmolvm,
4553
+ WORKBENCH_BROKER_WORKSPACE,
5507
4554
  DEFAULT_WORKBENCH_BROKER_LIMITS,
5508
4555
  isAdmittedSmolvmWorkbench,
5509
4556
  getWorkbenchBrokerLimits,
5510
4557
  resolveWorkbenchBrokerImage,
5511
4558
  startWorkbenchBroker,
5512
- runWorkbenchBrokerProgram,
5513
- canonicalEvolutionJson,
5514
- parseEvolutionConfig,
5515
- loadEvolutionConfigFile,
5516
- ensureEvolutionDirectory,
5517
- readEvolutionArtifact,
5518
- publishEvolutionArtifact,
5519
- evaluateVerificationSpec,
5520
- parsePatch,
5521
- applyPatchOps,
5522
- resolveScopedPath
4559
+ runWorkbenchBrokerProgram
5523
4560
  };