@abelxiaoxing/cadence 1.7.0 → 1.8.1

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.
@@ -1,17 +1,19 @@
1
1
  // Generated by scripts/build-workspace-io.mjs; edit operator-cli-entry.mjs and its source modules.
2
2
  // src/operator-cli-entry.mjs
3
- import path6 from "node:path";
3
+ import path9 from "node:path";
4
4
 
5
5
  // src/operator-tools.ts
6
- import { spawnSync } from "node:child_process";
7
- import { lstatSync as lstatSync6, readdirSync as readdirSync4, readFileSync as readFileSync4, realpathSync as realpathSync4 } from "node:fs";
8
- import path5 from "node:path";
6
+ import { spawnSync as spawnSync2 } from "node:child_process";
7
+ import { lstatSync as lstatSync8, readdirSync as readdirSync4, readFileSync as readFileSync5, realpathSync as realpathSync4 } from "node:fs";
8
+ import path8 from "node:path";
9
9
  import { DatabaseSync } from "node:sqlite";
10
10
 
11
11
  // src/execution-profile.ts
12
+ import path from "node:path";
13
+ import { fileURLToPath } from "node:url";
12
14
  function executionProfile(env = process.env) {
13
15
  const mode = env.ABEL_EXECUTION_MODE ?? "isolated";
14
- if (mode !== "isolated" && mode !== "local-trusted")
16
+ if (mode !== "isolated" && mode !== "local-trusted" && mode !== "host-trusted")
15
17
  throw new Error("execution-mode-invalid");
16
18
  const timeoutMs = Number(env.ABEL_VERIFICATION_TIMEOUT_MS ?? 600000);
17
19
  if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 86400000)
@@ -21,17 +23,22 @@ function executionProfile(env = process.env) {
21
23
  throw new Error("verification-environment-names-invalid");
22
24
  if (mode === "isolated" && inheritEnvironment.length)
23
25
  throw new Error("verification-environment-requires-trusted-mode");
26
+ if (mode === "host-trusted" && env.ABEL_WINDOWS_JOB_HELPER !== undefined && (!env.ABEL_WINDOWS_JOB_HELPER || env.ABEL_WINDOWS_JOB_HELPER.includes("\x00") || !(path.isAbsolute(env.ABEL_WINDOWS_JOB_HELPER) || path.win32.isAbsolute(env.ABEL_WINDOWS_JOB_HELPER))))
27
+ throw new Error("windows-job-helper-path-invalid");
24
28
  return {
25
29
  mode,
26
30
  timeoutMs,
27
31
  bwrapPath: env.ABEL_BWRAP_PATH ?? "/usr/bin/bwrap",
28
- inheritEnvironment
32
+ inheritEnvironment,
33
+ ...mode === "host-trusted" ? {
34
+ windowsHelperPath: env.ABEL_WINDOWS_JOB_HELPER ?? fileURLToPath(new URL("./windows-job.exe", import.meta.url))
35
+ } : {}
29
36
  };
30
37
  }
31
38
 
32
39
  // src/openspec-cli.ts
33
40
  import { lstatSync, readdirSync, readFileSync, realpathSync } from "node:fs";
34
- import path from "node:path";
41
+ import path2 from "node:path";
35
42
  var PACKAGE_NAME = "@fission-ai/openspec";
36
43
  var OPENSPEC_LIMITS = Object.freeze({
37
44
  timeoutMs: 30000,
@@ -57,8 +64,8 @@ function record(value) {
57
64
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : undefined;
58
65
  }
59
66
  function inside(root, target) {
60
- const relative = path.relative(root, target);
61
- return relative === "" || relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
67
+ const relative = path2.relative(root, target);
68
+ return relative === "" || relative !== ".." && !relative.startsWith(`..${path2.sep}`) && !path2.isAbsolute(relative);
62
69
  }
63
70
  function envValue(environment, name) {
64
71
  const key = Object.keys(environment).sort().find((candidate) => process.platform === "win32" ? candidate.toUpperCase() === name.toUpperCase() : candidate === name);
@@ -66,15 +73,15 @@ function envValue(environment, name) {
66
73
  }
67
74
  function packageEntry(root) {
68
75
  const canonicalRoot = realpathSync(root);
69
- const manifestPath = realpathSync(path.join(canonicalRoot, "package.json"));
76
+ const manifestPath = realpathSync(path2.join(canonicalRoot, "package.json"));
70
77
  const manifestStat = lstatSync(manifestPath);
71
78
  if (!inside(canonicalRoot, manifestPath) || !manifestStat.isFile() || manifestStat.size > 1024 * 1024)
72
79
  throw new Error("manifest-limit");
73
80
  const manifest = record(JSON.parse(readFileSync(manifestPath, "utf8")));
74
81
  const bin = typeof manifest?.bin === "string" ? manifest.bin : record(manifest?.bin)?.openspec;
75
- if (manifest?.name !== PACKAGE_NAME || typeof bin !== "string" || path.isAbsolute(bin))
82
+ if (manifest?.name !== PACKAGE_NAME || typeof bin !== "string" || path2.isAbsolute(bin))
76
83
  throw new Error("manifest-invalid");
77
- const entry = realpathSync(path.resolve(canonicalRoot, bin));
84
+ const entry = realpathSync(path2.resolve(canonicalRoot, bin));
78
85
  if (!inside(canonicalRoot, entry) || !/\.(?:cjs|mjs|js)$/iu.test(entry) || !lstatSync(entry).isFile())
79
86
  throw new Error("entry-invalid");
80
87
  return entry;
@@ -99,7 +106,7 @@ function commandIn(directory) {
99
106
  if (matches.length > 1)
100
107
  throw new Error("ambiguous-command");
101
108
  if (matches.length === 1) {
102
- const candidate = path.join(directory, matches[0]);
109
+ const candidate = path2.join(directory, matches[0]);
103
110
  if (lstatSync(candidate, { throwIfNoEntry: false }))
104
111
  return candidate;
105
112
  }
@@ -112,7 +119,7 @@ function resolveOpenSpecInvocation(consumerRoot, environment = process.env) {
112
119
  const node = configuredNode ?? (process.release.name === "node" && !process.versions.bun ? process.execPath : undefined);
113
120
  let executable;
114
121
  try {
115
- if (!node || !path.isAbsolute(node) || /\.(?:cmd|bat|ps1)$/iu.test(node)) {
122
+ if (!node || !path2.isAbsolute(node) || /\.(?:cmd|bat|ps1)$/iu.test(node)) {
116
123
  throw new Error("runtime-invalid");
117
124
  }
118
125
  executable = realpathSync(node);
@@ -123,7 +130,7 @@ function resolveOpenSpecInvocation(consumerRoot, environment = process.env) {
123
130
  }
124
131
  if (configuredRoot !== undefined) {
125
132
  try {
126
- if (!path.isAbsolute(configuredRoot))
133
+ if (!path2.isAbsolute(configuredRoot))
127
134
  throw new Error("root-invalid");
128
135
  return { executable, entry: packageEntry(configuredRoot) };
129
136
  } catch {
@@ -131,9 +138,9 @@ function resolveOpenSpecInvocation(consumerRoot, environment = process.env) {
131
138
  }
132
139
  }
133
140
  const consumer = canonicalConsumerRoot(consumerRoot);
134
- const directories = (envValue(environment, "PATH") ?? "").split(path.delimiter);
141
+ const directories = (envValue(environment, "PATH") ?? "").split(path2.delimiter);
135
142
  for (const directory of directories) {
136
- if (!path.isAbsolute(directory))
143
+ if (!path2.isAbsolute(directory))
137
144
  continue;
138
145
  let command;
139
146
  let canonicalDirectory;
@@ -153,14 +160,14 @@ function resolveOpenSpecInvocation(consumerRoot, environment = process.env) {
153
160
  throw new OpenSpecCliError("resolve", "resolution", "unsafe-installation");
154
161
  }
155
162
  const layoutRoots = [
156
- path.join(canonicalDirectory, "node_modules", PACKAGE_NAME),
157
- path.resolve(canonicalDirectory, "../lib/node_modules", PACKAGE_NAME)
163
+ path2.join(canonicalDirectory, "node_modules", PACKAGE_NAME),
164
+ path2.resolve(canonicalDirectory, "../lib/node_modules", PACKAGE_NAME)
158
165
  ];
159
166
  const roots = [];
160
- let ancestor = path.dirname(target);
167
+ let ancestor = path2.dirname(target);
161
168
  for (let depth = 0;depth < 8; depth++) {
162
169
  roots.push(ancestor);
163
- const parent = path.dirname(ancestor);
170
+ const parent = path2.dirname(ancestor);
164
171
  if (parent === ancestor)
165
172
  break;
166
173
  ancestor = parent;
@@ -194,7 +201,7 @@ function resolveOpenSpecInvocation(consumerRoot, environment = process.env) {
194
201
  import { createHash } from "node:crypto";
195
202
  import { chmodSync, lstatSync as lstatSync2, mkdirSync, realpathSync as realpathSync2 } from "node:fs";
196
203
  import os from "node:os";
197
- import path2 from "node:path";
204
+ import path3 from "node:path";
198
205
  class StateRootError extends Error {
199
206
  code;
200
207
  constructor(code) {
@@ -204,15 +211,15 @@ class StateRootError extends Error {
204
211
  }
205
212
  }
206
213
  function isContainedBy(parent, candidate) {
207
- const relative = path2.relative(parent, candidate);
208
- return relative === "" || !relative.startsWith("..") && !path2.isAbsolute(relative);
214
+ const relative = path3.relative(parent, candidate);
215
+ return relative === "" || !relative.startsWith("..") && !path3.isAbsolute(relative);
209
216
  }
210
217
  function inspectExistingComponents(absolutePath) {
211
- const parsed = path2.parse(absolutePath);
212
- const relativeParts = absolutePath.slice(parsed.root.length).split(path2.sep).filter(Boolean);
218
+ const parsed = path3.parse(absolutePath);
219
+ const relativeParts = absolutePath.slice(parsed.root.length).split(path3.sep).filter(Boolean);
213
220
  let current = parsed.root;
214
221
  for (const part of relativeParts) {
215
- current = path2.join(current, part);
222
+ current = path3.join(current, part);
216
223
  let stat;
217
224
  try {
218
225
  stat = lstatSync2(current);
@@ -230,37 +237,37 @@ function inspectExistingComponents(absolutePath) {
230
237
  }
231
238
  }
232
239
  function canonicalFuturePath(input) {
233
- const absolute = path2.resolve(input);
240
+ const absolute = path3.resolve(input);
234
241
  inspectExistingComponents(absolute);
235
242
  let cursor = absolute;
236
243
  const missing = [];
237
244
  for (;; ) {
238
245
  try {
239
- return path2.join(realpathSync2.native(cursor), ...missing.reverse());
246
+ return path3.join(realpathSync2.native(cursor), ...missing.reverse());
240
247
  } catch (error) {
241
248
  if (error.code !== "ENOENT")
242
249
  throw error;
243
- const parent = path2.dirname(cursor);
250
+ const parent = path3.dirname(cursor);
244
251
  if (parent === cursor)
245
252
  throw error;
246
- missing.push(path2.basename(cursor));
253
+ missing.push(path3.basename(cursor));
247
254
  cursor = parent;
248
255
  }
249
256
  }
250
257
  }
251
258
  function requireAbsoluteDirectoryBase(xdgStateHome, homeDir) {
252
- if (xdgStateHome && path2.isAbsolute(xdgStateHome)) {
259
+ if (xdgStateHome && path3.isAbsolute(xdgStateHome)) {
253
260
  return canonicalFuturePath(xdgStateHome);
254
261
  }
255
- if (!path2.isAbsolute(homeDir)) {
262
+ if (!path3.isAbsolute(homeDir)) {
256
263
  throw new StateRootError("invalid-state-base");
257
264
  }
258
- return canonicalFuturePath(path2.join(homeDir, ".local", "state"));
265
+ return canonicalFuturePath(path3.join(homeDir, ".local", "state"));
259
266
  }
260
267
  function resolveStateRoot(options) {
261
268
  let consumerRoot;
262
269
  try {
263
- consumerRoot = realpathSync2.native(path2.resolve(options.consumerRoot));
270
+ consumerRoot = realpathSync2.native(path3.resolve(options.consumerRoot));
264
271
  if (!lstatSync2(consumerRoot).isDirectory()) {
265
272
  throw new StateRootError("consumer-root-unavailable");
266
273
  }
@@ -271,9 +278,9 @@ function resolveStateRoot(options) {
271
278
  }
272
279
  const stateBase = requireAbsoluteDirectoryBase(options.xdgStateHome, options.homeDir ?? os.homedir());
273
280
  const consumerRootHash = createHash("sha256").update(consumerRoot).digest("hex");
274
- const productRoot = canonicalFuturePath(path2.join(stateBase, "abel-cadence"));
275
- const rootsDir = canonicalFuturePath(path2.join(productRoot, "roots"));
276
- const rootDir = canonicalFuturePath(path2.join(rootsDir, consumerRootHash));
281
+ const productRoot = canonicalFuturePath(path3.join(stateBase, "abel-cadence"));
282
+ const rootsDir = canonicalFuturePath(path3.join(productRoot, "roots"));
283
+ const rootDir = canonicalFuturePath(path3.join(rootsDir, consumerRootHash));
277
284
  if (isContainedBy(consumerRoot, rootDir)) {
278
285
  throw new StateRootError("state-root-inside-consumer");
279
286
  }
@@ -284,10 +291,10 @@ function resolveStateRoot(options) {
284
291
  productRoot,
285
292
  rootsDir,
286
293
  rootDir,
287
- databasePath: path2.join(rootDir, "control.sqlite3"),
288
- artifactsDir: path2.join(rootDir, "artifacts"),
289
- workspacesDir: path2.join(rootDir, "workspaces"),
290
- transactionsDir: path2.join(rootDir, "transactions")
294
+ databasePath: path3.join(rootDir, "control.sqlite3"),
295
+ artifactsDir: path3.join(rootDir, "artifacts"),
296
+ workspacesDir: path3.join(rootDir, "workspaces"),
297
+ transactionsDir: path3.join(rootDir, "transactions")
291
298
  };
292
299
  }
293
300
 
@@ -318,7 +325,7 @@ import {
318
325
  readFileSync as readFileSync3,
319
326
  realpathSync as realpathSync3
320
327
  } from "node:fs";
321
- import path4 from "node:path";
328
+ import path5 from "node:path";
322
329
 
323
330
  // src/control-contracts.ts
324
331
  var CONTROL_SCHEMA_PROPERTIES = {
@@ -609,13 +616,13 @@ import { join, resolve } from "node:path";
609
616
 
610
617
  // src/safe-path.ts
611
618
  import { lstatSync as lstatSync3 } from "node:fs";
612
- import path3 from "node:path";
619
+ import path4 from "node:path";
613
620
  function observeSafePath(root, relative) {
614
621
  if (!isValidRelativePath(relative)) {
615
622
  return { kind: "unsafe", reason: "invalid-path" };
616
623
  }
617
624
  try {
618
- const resolvedRoot = path3.resolve(root);
625
+ const resolvedRoot = path4.resolve(root);
619
626
  const rootStat = lstatSync3(resolvedRoot, { throwIfNoEntry: false });
620
627
  if (!rootStat?.isDirectory() || rootStat.isSymbolicLink()) {
621
628
  return { kind: "unsafe", reason: "root-unavailable" };
@@ -625,8 +632,8 @@ function observeSafePath(root, relative) {
625
632
  let current = resolvedRoot;
626
633
  const parts = relative.split("/");
627
634
  for (const [index, part] of parts.entries()) {
628
- current = path3.join(current, part);
629
- if (!current.startsWith(`${resolvedRoot}${path3.sep}`)) {
635
+ current = path4.join(current, part);
636
+ if (!current.startsWith(`${resolvedRoot}${path4.sep}`)) {
630
637
  return { kind: "unsafe", reason: "invalid-path" };
631
638
  }
632
639
  const stat = lstatSync3(current, { throwIfNoEntry: false });
@@ -716,11 +723,11 @@ function regularFile(root, relative) {
716
723
  return fileStatus(root, relative) === "regular";
717
724
  }
718
725
  function localExecutable(root, executable, dependencyOwner = root, environment = {}) {
719
- const dependencyRoot = path4.resolve(dependencyOwner, "node_modules");
726
+ const dependencyRoot = path5.resolve(dependencyOwner, "node_modules");
720
727
  try {
721
728
  const canonicalDependencyRoot = realpathSync3(dependencyRoot);
722
729
  for (const name of localExecutableNames(executable, environment)) {
723
- const unresolved = resolveCandidateName(path4.resolve(dependencyRoot, ".bin"), name, environment.platform ?? process.platform);
730
+ const unresolved = resolveCandidateName(path5.resolve(dependencyRoot, ".bin"), name, environment.platform ?? process.platform);
724
731
  if (!unresolved)
725
732
  continue;
726
733
  const canonical = realpathSync3(unresolved);
@@ -736,11 +743,11 @@ function localExecutable(root, executable, dependencyOwner = root, environment =
736
743
  }
737
744
  function pathApi(platform, ...values) {
738
745
  if (platform !== "win32")
739
- return path4.posix;
746
+ return path5.posix;
740
747
  if (process.platform === "win32" || values.some((value) => /^[a-z]:[\\/]/iu.test(value) || value.startsWith("\\\\"))) {
741
- return path4.win32;
748
+ return path5.win32;
742
749
  }
743
- return path4.posix;
750
+ return path5.posix;
744
751
  }
745
752
  function pathWithin(root, candidate, platform) {
746
753
  const platformPath = pathApi(platform, root, candidate);
@@ -770,7 +777,7 @@ function runnerNames(command, environment) {
770
777
  if ((environment.platform ?? process.platform) !== "win32") {
771
778
  return [command];
772
779
  }
773
- const extension = path4.extname(command).toLowerCase();
780
+ const extension = path5.extname(command).toLowerCase();
774
781
  if (extension) {
775
782
  return WINDOWS_EXECUTABLE_EXTENSIONS.has(extension) ? [command] : [];
776
783
  }
@@ -782,7 +789,7 @@ function localExecutableNames(executable, environment) {
782
789
  if ((environment.platform ?? process.platform) !== "win32") {
783
790
  return [executable];
784
791
  }
785
- const extension = path4.extname(executable).toLowerCase();
792
+ const extension = path5.extname(executable).toLowerCase();
786
793
  if (extension) {
787
794
  return WINDOWS_EXECUTABLE_EXTENSIONS.has(extension) ? [executable] : [];
788
795
  }
@@ -792,18 +799,18 @@ function localExecutableNames(executable, environment) {
792
799
  ];
793
800
  }
794
801
  function resolveCandidateName(directory, name, platform) {
795
- const exact = path4.join(directory, name);
802
+ const exact = path5.join(directory, name);
796
803
  if (platform !== "win32")
797
804
  return exact;
798
805
  try {
799
806
  const matches = readdirSync3(directory).filter((entry) => entry.toLowerCase() === name.toLowerCase());
800
- return matches.length === 1 ? path4.join(directory, matches[0]) : null;
807
+ return matches.length === 1 ? path5.join(directory, matches[0]) : null;
801
808
  } catch {
802
809
  return null;
803
810
  }
804
811
  }
805
812
  function within(root, candidate) {
806
- return candidate === root || candidate.startsWith(`${root}${path4.sep}`);
813
+ return candidate === root || candidate.startsWith(`${root}${path5.sep}`);
807
814
  }
808
815
  function commonDirectory(left, right, platform = process.platform) {
809
816
  const platformPath = pathApi(platform, left, right);
@@ -830,15 +837,15 @@ function commonDirectory(left, right, platform = process.platform) {
830
837
  function unsafeMountSource(source) {
831
838
  if (BROAD_MOUNT_ROOTS.has(source))
832
839
  return true;
833
- if (path4.resolve(source) === path4.parse(path4.resolve(source)).root)
840
+ if (path5.resolve(source) === path5.parse(path5.resolve(source)).root)
834
841
  return true;
835
- const parts = path4.relative(path4.parse(path4.resolve(source)).root, path4.resolve(source)).split(path4.sep).filter(Boolean).map((part) => part.toLowerCase());
842
+ const parts = path5.relative(path5.parse(path5.resolve(source)).root, path5.resolve(source)).split(path5.sep).filter(Boolean).map((part) => part.toLowerCase());
836
843
  return parts[0] === "home" && parts.length <= 2 || parts[0] === "users" && parts.length <= 2 || parts[0] === "root" && parts.length <= 1;
837
844
  }
838
845
  function safeRunnerSource(paths, platform) {
839
- let source = path4.dirname(paths[0]);
846
+ let source = path5.dirname(paths[0]);
840
847
  for (const candidate of paths.slice(1)) {
841
- source = commonDirectory(source, path4.dirname(candidate), platform);
848
+ source = commonDirectory(source, path5.dirname(candidate), platform);
842
849
  }
843
850
  if (unsafeMountSource(source))
844
851
  return null;
@@ -856,18 +863,18 @@ function resolvedRunnerEnvironment(environment) {
856
863
  platform: environment.platform ?? process.platform,
857
864
  path: environment.path ?? process.env.PATH ?? "",
858
865
  pathExt: environment.pathExt ?? process.env.PATHEXT ?? DEFAULT_WINDOWS_PATH_EXT,
859
- pathDelimiter: environment.pathDelimiter ?? path4.delimiter
866
+ pathDelimiter: environment.pathDelimiter ?? path5.delimiter
860
867
  };
861
868
  }
862
869
  function windowsShimBinding(command, launcherPath, environment) {
863
- const name = path4.basename(command, path4.extname(command)).toLowerCase();
870
+ const name = path5.basename(command, path5.extname(command)).toLowerCase();
864
871
  if (name !== "npm" && name !== "npx")
865
872
  return null;
866
873
  const node = resolveVerificationRunner("node", environment);
867
- if (!node || node.fixedArgs || path4.extname(node.executablePath).toLowerCase() !== ".exe") {
874
+ if (!node || node.fixedArgs || path5.extname(node.executablePath).toLowerCase() !== ".exe") {
868
875
  return null;
869
876
  }
870
- const cli = path4.join(path4.dirname(launcherPath), "node_modules", "npm", "bin", `${name}-cli.js`);
877
+ const cli = path5.join(path5.dirname(launcherPath), "node_modules", "npm", "bin", `${name}-cli.js`);
871
878
  try {
872
879
  const canonicalLauncher = realpathSync3(launcherPath);
873
880
  const canonicalCli = realpathSync3(cli);
@@ -892,14 +899,14 @@ function localPackageDirectories(dependencyRoot) {
892
899
  for (const entry of readdirSync3(dependencyRoot, { withFileTypes: true })) {
893
900
  if (!entry.isDirectory() || entry.name.startsWith("."))
894
901
  continue;
895
- const candidate = path4.join(dependencyRoot, entry.name);
902
+ const candidate = path5.join(dependencyRoot, entry.name);
896
903
  if (!entry.name.startsWith("@")) {
897
904
  packages.push(candidate);
898
905
  continue;
899
906
  }
900
907
  for (const scoped of readdirSync3(candidate, { withFileTypes: true })) {
901
908
  if (scoped.isDirectory() && !scoped.name.startsWith(".")) {
902
- packages.push(path4.join(candidate, scoped.name));
909
+ packages.push(path5.join(candidate, scoped.name));
903
910
  }
904
911
  }
905
912
  }
@@ -909,7 +916,7 @@ function localPackageDirectories(dependencyRoot) {
909
916
  return packages;
910
917
  }
911
918
  function localPackageBinTargets(dependencyOwner, executable, platform) {
912
- const dependencyRoot = path4.resolve(dependencyOwner, "node_modules");
919
+ const dependencyRoot = path5.resolve(dependencyOwner, "node_modules");
913
920
  let canonicalDependencyRoot;
914
921
  try {
915
922
  canonicalDependencyRoot = realpathSync3(dependencyRoot);
@@ -927,7 +934,7 @@ function localPackageBinTargets(dependencyOwner, executable, platform) {
927
934
  if (!pathWithin(canonicalDependencyRoot, canonicalPackage, platform) || canonicalPackage === canonicalDependencyRoot) {
928
935
  continue;
929
936
  }
930
- const manifestPath = path4.join(canonicalPackage, "package.json");
937
+ const manifestPath = path5.join(canonicalPackage, "package.json");
931
938
  const manifestStat = lstatSync5(manifestPath);
932
939
  if (!manifestStat.isFile() || manifestStat.isSymbolicLink())
933
940
  continue;
@@ -949,7 +956,7 @@ function localPackageBinTargets(dependencyOwner, executable, platform) {
949
956
  if (typeof relative !== "string" || relative.length === 0 || relative.includes("\x00")) {
950
957
  continue;
951
958
  }
952
- const unresolved = path4.resolve(canonicalPackage, relative);
959
+ const unresolved = path5.resolve(canonicalPackage, relative);
953
960
  if (!pathWithin(canonicalPackage, unresolved, platform))
954
961
  continue;
955
962
  const canonical = realpathSync3(unresolved);
@@ -969,7 +976,7 @@ function resolveLocalVerificationRunner(dependencyOwner, executable, runnerEnvir
969
976
  if (targets.length !== 1)
970
977
  return null;
971
978
  const target = targets[0];
972
- const extension = path4.extname(target).toLowerCase();
979
+ const extension = path5.extname(target).toLowerCase();
973
980
  if (extension === ".exe") {
974
981
  return { command: executable, executablePath: target };
975
982
  }
@@ -991,7 +998,7 @@ function resolveVerificationRunner(command, runnerEnvironment = {}) {
991
998
  const candidates = [
992
999
  ...command === "bun" && process.versions.bun && environment.platform === process.platform ? [process.execPath] : [],
993
1000
  ...directories.flatMap((directory) => {
994
- const resolvedDirectory = path4.resolve(directory);
1001
+ const resolvedDirectory = path5.resolve(directory);
995
1002
  const key = environment.platform === "win32" ? resolvedDirectory.toLowerCase() : resolvedDirectory;
996
1003
  if (seenDirectories.has(key))
997
1004
  return [];
@@ -1005,7 +1012,7 @@ function resolveVerificationRunner(command, runnerEnvironment = {}) {
1005
1012
  const seenCandidates = new Set;
1006
1013
  for (const candidate of candidates) {
1007
1014
  try {
1008
- const executablePath = path4.resolve(candidate);
1015
+ const executablePath = path5.resolve(candidate);
1009
1016
  const candidateKey = environment.platform === "win32" ? executablePath.toLowerCase() : executablePath;
1010
1017
  if (seenCandidates.has(candidateKey))
1011
1018
  continue;
@@ -1015,7 +1022,7 @@ function resolveVerificationRunner(command, runnerEnvironment = {}) {
1015
1022
  if (!regularExecutableFile(stat, environment.platform))
1016
1023
  continue;
1017
1024
  if (environment.platform === "win32") {
1018
- const extension = path4.extname(executablePath).toLowerCase();
1025
+ const extension = path5.extname(executablePath).toLowerCase();
1019
1026
  if (extension === ".cmd") {
1020
1027
  const shim = windowsShimBinding(command, executablePath, environment);
1021
1028
  if (shim)
@@ -1032,7 +1039,7 @@ function resolveVerificationRunner(command, runnerEnvironment = {}) {
1032
1039
  const systemVisible = SYSTEM_MOUNT_ROOTS.some((root) => within(root, executablePath) && within(root, canonical));
1033
1040
  if (systemVisible)
1034
1041
  return { command, executablePath };
1035
- const mountSource = commonDirectory(path4.dirname(executablePath), path4.dirname(canonical), environment.platform);
1042
+ const mountSource = commonDirectory(path5.dirname(executablePath), path5.dirname(canonical), environment.platform);
1036
1043
  if (unsafeMountSource(mountSource))
1037
1044
  continue;
1038
1045
  const sourceStat = lstatSync5(mountSource);
@@ -1047,7 +1054,7 @@ function packageManifest(root) {
1047
1054
  if (!regularFile(root, "package.json"))
1048
1055
  return null;
1049
1056
  try {
1050
- const value = JSON.parse(readFileSync3(path4.join(root, "package.json"), "utf8"));
1057
+ const value = JSON.parse(readFileSync3(path5.join(root, "package.json"), "utf8"));
1051
1058
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
1052
1059
  } catch {
1053
1060
  return null;
@@ -1169,7 +1176,7 @@ function validateAtomicCapability(root, step, dependencyOwner, runnerEnvironment
1169
1176
  "pnpm-workspace.yaml"
1170
1177
  ].includes(relative))
1171
1178
  continue;
1172
- if (lstatSync5(path4.join(root, relative)).size > 64 * 1024 || executionProfile().mode !== "local-trusted" && unsupportedExecutionConfiguration(readFileSync3(path4.join(root, relative), "utf8"))) {
1179
+ if (lstatSync5(path5.join(root, relative)).size > 64 * 1024 || executionProfile().mode === "isolated" && unsupportedExecutionConfiguration(readFileSync3(path5.join(root, relative), "utf8"))) {
1173
1180
  return adapterFailure(step.id, "verification-config-unsafe", "Execution configuration requires unsupported credentials or host configuration");
1174
1181
  }
1175
1182
  }
@@ -1243,6 +1250,549 @@ function validateAcceptedVerificationCapability(root, verification, dependencyOw
1243
1250
  return capabilitySuccess(verification.id, runnerBindings);
1244
1251
  }
1245
1252
 
1253
+ // src/windows-job-backend.ts
1254
+ import { spawn as spawn2, spawnSync } from "node:child_process";
1255
+ import { createHash as createHash3 } from "node:crypto";
1256
+ import { lstatSync as lstatSync7, readFileSync as readFileSync4 } from "node:fs";
1257
+ import path7 from "node:path";
1258
+
1259
+ // src/isolation-backend.ts
1260
+ import { spawn } from "node:child_process";
1261
+ import { accessSync, constants, lstatSync as lstatSync6 } from "node:fs";
1262
+ import path6 from "node:path";
1263
+ var ISOLATION_RUN_LIMITS = Object.freeze({
1264
+ timeoutMs: 10 * 60000,
1265
+ maxOutputBytes: 256 * 1024,
1266
+ terminateGraceMs: 1000
1267
+ });
1268
+ function defaultProbe(executable) {
1269
+ try {
1270
+ accessSync(executable, constants.X_OK);
1271
+ return true;
1272
+ } catch {
1273
+ return false;
1274
+ }
1275
+ }
1276
+ function validEnvironmentName(name) {
1277
+ return /^[A-Za-z_][A-Za-z0-9_]*$/u.test(name);
1278
+ }
1279
+
1280
+ class BoundedLog {
1281
+ bytes = 0;
1282
+ #head = Buffer.alloc(0);
1283
+ #tail = Buffer.alloc(0);
1284
+ limit;
1285
+ #witness;
1286
+ #witnessTail = Buffer.alloc(0);
1287
+ witnessMatched = false;
1288
+ constructor(limit, witness) {
1289
+ this.limit = limit;
1290
+ if (witness)
1291
+ this.#witness = Buffer.from(witness);
1292
+ }
1293
+ append(chunk) {
1294
+ const input = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
1295
+ this.bytes += input.byteLength;
1296
+ if (this.#witness && !this.witnessMatched) {
1297
+ const searchable = Buffer.concat([this.#witnessTail, input]);
1298
+ this.witnessMatched = searchable.includes(this.#witness);
1299
+ this.#witnessTail = this.witnessMatched ? Buffer.alloc(0) : Buffer.from(searchable.subarray(Math.max(0, searchable.length - this.#witness.length + 1)));
1300
+ }
1301
+ const headLimit = Math.ceil(this.limit / 2);
1302
+ const take = Math.min(input.length, headLimit - this.#head.length);
1303
+ if (take > 0)
1304
+ this.#head = Buffer.concat([this.#head, input.subarray(0, take)]);
1305
+ const rest = input.subarray(take);
1306
+ const tailLimit = this.limit - headLimit;
1307
+ if (rest.length && tailLimit) {
1308
+ this.#tail = rest.length >= tailLimit ? Buffer.from(rest.subarray(-tailLimit)) : Buffer.concat([
1309
+ this.#tail.subarray(Math.max(0, this.#tail.length + rest.length - tailLimit)),
1310
+ rest
1311
+ ]);
1312
+ }
1313
+ }
1314
+ text() {
1315
+ return Buffer.concat([this.#head, this.#tail]).toString("utf8");
1316
+ }
1317
+ metadata() {
1318
+ return { bytes: this.bytes, truncated: this.bytes > this.limit };
1319
+ }
1320
+ }
1321
+
1322
+ class BubblewrapIsolationBackend {
1323
+ #localTrusted;
1324
+ #bwrapPath;
1325
+ #probe;
1326
+ #spawn;
1327
+ #timeoutMs;
1328
+ #maxOutputBytes;
1329
+ #terminateGraceMs;
1330
+ constructor(options = {}) {
1331
+ this.#localTrusted = options.localTrusted === true;
1332
+ this.#bwrapPath = options.bwrapPath ?? "/usr/bin/bwrap";
1333
+ this.#probe = options.probe ?? defaultProbe;
1334
+ this.#spawn = options.spawnProcess ?? spawn;
1335
+ this.#timeoutMs = options.timeoutMs ?? ISOLATION_RUN_LIMITS.timeoutMs;
1336
+ this.#maxOutputBytes = options.maxOutputBytes ?? ISOLATION_RUN_LIMITS.maxOutputBytes;
1337
+ this.#terminateGraceMs = options.terminateGraceMs ?? ISOLATION_RUN_LIMITS.terminateGraceMs;
1338
+ if (!Number.isSafeInteger(this.#timeoutMs) || this.#timeoutMs < 1 || !Number.isSafeInteger(this.#maxOutputBytes) || this.#maxOutputBytes < 1 || !Number.isSafeInteger(this.#terminateGraceMs) || this.#terminateGraceMs < 1) {
1339
+ throw new Error("isolation-run-limits-invalid");
1340
+ }
1341
+ }
1342
+ async available() {
1343
+ try {
1344
+ if (this.#localTrusted && process.platform !== "linux")
1345
+ return false;
1346
+ return Boolean(await this.#probe(this.#bwrapPath));
1347
+ } catch {
1348
+ return false;
1349
+ }
1350
+ }
1351
+ async run(input) {
1352
+ if (input.outputWitness !== undefined && (typeof input.outputWitness !== "string" || input.outputWitness.length < 1 || input.outputWitness.length > 512))
1353
+ throw new Error("isolation-output-witness-invalid");
1354
+ if (!await this.available()) {
1355
+ return {
1356
+ ok: false,
1357
+ state: "paused",
1358
+ code: "isolation-backend-unavailable"
1359
+ };
1360
+ }
1361
+ if (input.signal?.aborted) {
1362
+ return { ok: false, state: "cancelled", code: "cancelled" };
1363
+ }
1364
+ const root = path6.resolve(input.root);
1365
+ const rootStat = lstatSync6(root, { throwIfNoEntry: false });
1366
+ if (!rootStat?.isDirectory() || rootStat.isSymbolicLink()) {
1367
+ throw new Error("isolation-root-invalid");
1368
+ }
1369
+ const argv = this.#localTrusted ? [
1370
+ "--die-with-parent",
1371
+ "--new-session",
1372
+ "--unshare-pid",
1373
+ "--bind",
1374
+ "/",
1375
+ "/",
1376
+ "--proc",
1377
+ "/proc",
1378
+ "--dev",
1379
+ "/dev",
1380
+ "--chdir",
1381
+ root,
1382
+ "--clearenv"
1383
+ ] : [
1384
+ "--die-with-parent",
1385
+ "--new-session",
1386
+ "--unshare-all",
1387
+ "--proc",
1388
+ "/proc",
1389
+ "--dev",
1390
+ "/dev",
1391
+ "--tmpfs",
1392
+ "/tmp",
1393
+ "--bind",
1394
+ root,
1395
+ "/workspace",
1396
+ "--chdir",
1397
+ "/workspace",
1398
+ "--clearenv",
1399
+ "--setenv",
1400
+ "PATH",
1401
+ "/usr/local/bin:/usr/bin:/bin"
1402
+ ];
1403
+ if (!this.#localTrusted) {
1404
+ for (const systemRoot of ["/usr", "/bin", "/lib", "/lib64"]) {
1405
+ if (lstatSync6(systemRoot, { throwIfNoEntry: false }))
1406
+ argv.push("--ro-bind", systemRoot, systemRoot);
1407
+ }
1408
+ for (const mount of input.mounts ?? []) {
1409
+ if (!path6.isAbsolute(mount.source) || !path6.isAbsolute(mount.target))
1410
+ throw new Error("isolation-mount-invalid");
1411
+ argv.push(mount.writable ? "--bind" : "--ro-bind", mount.source, mount.target);
1412
+ }
1413
+ }
1414
+ for (const [name, value] of Object.entries(input.environment ?? {}).sort()) {
1415
+ if (!validEnvironmentName(name) || value.includes("\x00"))
1416
+ throw new Error("isolation-environment-invalid");
1417
+ argv.push("--setenv", name, value);
1418
+ }
1419
+ argv.push("--", input.executable, ...input.args ?? []);
1420
+ return new Promise((resolve2) => {
1421
+ let settled = false;
1422
+ const stdout = new BoundedLog(this.#maxOutputBytes, input.outputWitness);
1423
+ const stderr = new BoundedLog(this.#maxOutputBytes, input.outputWitness);
1424
+ let child;
1425
+ let executionTimer;
1426
+ let killTimer;
1427
+ let terminationResult;
1428
+ const settle = (result) => {
1429
+ if (settled)
1430
+ return;
1431
+ settled = true;
1432
+ if (executionTimer)
1433
+ clearTimeout(executionTimer);
1434
+ input.signal?.removeEventListener("abort", abort);
1435
+ resolve2(result);
1436
+ };
1437
+ const kill = (signal) => {
1438
+ child?.kill(signal);
1439
+ };
1440
+ const terminate = (result) => {
1441
+ if (settled || terminationResult)
1442
+ return;
1443
+ terminationResult = result;
1444
+ if (executionTimer)
1445
+ clearTimeout(executionTimer);
1446
+ killTimer = setTimeout(() => kill("SIGKILL"), this.#terminateGraceMs);
1447
+ killTimer.unref?.();
1448
+ kill("SIGTERM");
1449
+ };
1450
+ const abort = () => {
1451
+ terminate({ ok: false, state: "cancelled", code: "cancelled" });
1452
+ };
1453
+ try {
1454
+ child = this.#spawn(this.#bwrapPath, argv, {
1455
+ cwd: root,
1456
+ shell: false,
1457
+ stdio: ["ignore", "pipe", "pipe"]
1458
+ });
1459
+ } catch {
1460
+ settle({
1461
+ ok: false,
1462
+ state: "paused",
1463
+ code: "isolation-backend-launch-failed"
1464
+ });
1465
+ return;
1466
+ }
1467
+ const capture = (destination, chunk) => {
1468
+ if (!settled && !terminationResult)
1469
+ destination.append(chunk);
1470
+ };
1471
+ child.stdout?.on("data", (chunk) => {
1472
+ capture(stdout, chunk);
1473
+ });
1474
+ child.stderr?.on("data", (chunk) => {
1475
+ capture(stderr, chunk);
1476
+ });
1477
+ child.once("error", () => {
1478
+ if (terminationResult)
1479
+ return;
1480
+ settle({
1481
+ ok: false,
1482
+ state: "paused",
1483
+ code: "isolation-backend-launch-failed"
1484
+ });
1485
+ });
1486
+ child.once("close", (code) => {
1487
+ if (killTimer)
1488
+ clearTimeout(killTimer);
1489
+ settle(terminationResult ?? {
1490
+ ok: true,
1491
+ state: "completed",
1492
+ exitCode: code ?? 1,
1493
+ stdout: stdout.text(),
1494
+ stderr: stderr.text(),
1495
+ logs: { stdout: stdout.metadata(), stderr: stderr.metadata() },
1496
+ ...input.outputWitness ? {
1497
+ outputWitnessMatched: stdout.witnessMatched || stderr.witnessMatched
1498
+ } : {}
1499
+ });
1500
+ });
1501
+ input.signal?.addEventListener("abort", abort, { once: true });
1502
+ if (input.signal?.aborted) {
1503
+ abort();
1504
+ return;
1505
+ }
1506
+ executionTimer = setTimeout(() => terminate({
1507
+ ok: false,
1508
+ state: "paused",
1509
+ code: "isolation-execution-timeout"
1510
+ }), this.#timeoutMs);
1511
+ executionTimer.unref?.();
1512
+ });
1513
+ }
1514
+ }
1515
+
1516
+ // src/windows-job-backend.ts
1517
+ var SHUTDOWN_MS = 5000;
1518
+ var invalid = () => {
1519
+ throw new Error("windows-job-request-invalid");
1520
+ };
1521
+ var text = (value) => typeof value === "string" && value.length <= 16384 && !value.includes("\x00") && !/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/u.test(value);
1522
+ var absolute = (value) => text(value) && /^[A-Za-z]:\\/u.test(value) && path7.win32.isAbsolute(value);
1523
+ function encodeWindowsJobRequest(input, timeoutMs) {
1524
+ if (!absolute(input.root) || !absolute(input.executable) || !input.executable.toLowerCase().endsWith(".exe") || input.mounts?.length || !Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 86400000)
1525
+ invalid();
1526
+ const args = input.args ?? [];
1527
+ if (args.length > 128 || !args.every(text))
1528
+ invalid();
1529
+ const seen = new Set;
1530
+ const env = Object.entries(input.environment ?? {});
1531
+ for (const [name, value] of env) {
1532
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/u.test(name) || !text(value) || seen.has(name.toUpperCase()))
1533
+ invalid();
1534
+ seen.add(name.toUpperCase());
1535
+ }
1536
+ const quote = (value) => `"${value.replace(/(\\*)"/gu, "$1$1\\\"").replace(/(\\+)$/gu, "$1$1")}"`;
1537
+ const command = [input.executable, ...args].map(quote).join(" ");
1538
+ if (command.length >= 32767)
1539
+ invalid();
1540
+ const environment = `${env.sort(([a], [b]) => a.toUpperCase() < b.toUpperCase() ? -1 : 1).map(([k, v]) => `${k}=${v}`).join("\x00")}\x00\x00`;
1541
+ const parts = [
1542
+ `${input.executable}\x00`,
1543
+ `${command}\x00`,
1544
+ `${input.root}\x00`,
1545
+ environment
1546
+ ].map((s) => Buffer.from(s, "utf16le"));
1547
+ const header = Buffer.alloc(36);
1548
+ [2, timeoutMs, SHUTDOWN_MS, 0, ...parts.map((p) => p.length), 0].forEach((n, i) => {
1549
+ header.writeUInt32LE(n, i * 4);
1550
+ });
1551
+ const wire = Buffer.concat([header, ...parts]);
1552
+ if (wire.length > 65536 || parts.some((p) => p.length > 60000))
1553
+ invalid();
1554
+ return wire;
1555
+ }
1556
+ function verifyWindowsJobHelper(helper) {
1557
+ try {
1558
+ if (!path7.isAbsolute(helper))
1559
+ return false;
1560
+ const files = [helper, `${helper}.json`];
1561
+ if (files.some((file) => {
1562
+ const s = lstatSync7(file);
1563
+ return !s.isFile() || s.isSymbolicLink() || s.size > 8 * 1024 * 1024;
1564
+ }))
1565
+ return false;
1566
+ const bytes = readFileSync4(helper);
1567
+ const pe = bytes.length >= 64 ? bytes.readUInt32LE(60) : -1;
1568
+ if (bytes.toString("ascii", 0, 2) !== "MZ" || pe < 64 || pe + 6 > bytes.length || bytes.readUInt32LE(pe) !== 17744 || bytes.readUInt16LE(pe + 4) !== 34404)
1569
+ return false;
1570
+ const hash = (data) => createHash3("sha256").update(data).digest("hex");
1571
+ const manifest = JSON.parse(readFileSync4(`${helper}.json`, "utf8"));
1572
+ return manifest.version === 2 && manifest.arch === "x64" && manifest.helperSha256 === hash(bytes) && manifest.sourceSha256 === hash(readFileSync4(new URL("./windows-job.c", import.meta.url)));
1573
+ } catch {
1574
+ return false;
1575
+ }
1576
+ }
1577
+ function observation(value) {
1578
+ if (!value || typeof value !== "object")
1579
+ return;
1580
+ const v = value;
1581
+ if (Object.keys(v).sort().join(",") !== "exitCode,managedSettled,outcome,reason,rootExited,version" || v.version !== 2 || !["complete", "failed", "uncertain"].includes(v.outcome) || ![
1582
+ "exit",
1583
+ "timeout",
1584
+ "cancelled",
1585
+ "launch-failed",
1586
+ "termination-unconfirmed"
1587
+ ].includes(v.reason) || typeof v.rootExited !== "boolean" || typeof v.managedSettled !== "boolean" || !(v.exitCode === null || Number.isInteger(v.exitCode) && v.exitCode >= 0 && v.exitCode <= 4294967295))
1588
+ return;
1589
+ if (v.outcome === "complete" && (v.reason !== "exit" || !v.rootExited || !v.managedSettled || v.exitCode === null))
1590
+ return;
1591
+ if (v.outcome === "failed" && (!v.managedSettled || !["timeout", "cancelled", "launch-failed"].includes(v.reason)))
1592
+ return;
1593
+ return v;
1594
+ }
1595
+ var uncertain = () => ({
1596
+ ok: false,
1597
+ state: "paused",
1598
+ code: "isolation-termination-unconfirmed"
1599
+ });
1600
+ function classify(v, helperExit, output, cancelled) {
1601
+ if (!v?.managedSettled || v.outcome === "uncertain" || helperExit !== 0)
1602
+ return uncertain();
1603
+ if (cancelled || v.reason === "cancelled")
1604
+ return { ok: false, state: "cancelled", code: "cancelled" };
1605
+ if (v.reason === "timeout")
1606
+ return { ok: false, state: "paused", code: "isolation-execution-timeout" };
1607
+ if (v.reason === "launch-failed")
1608
+ return {
1609
+ ok: false,
1610
+ state: "paused",
1611
+ code: "isolation-backend-launch-failed"
1612
+ };
1613
+ if (v.outcome !== "complete" || v.exitCode === null)
1614
+ return uncertain();
1615
+ return {
1616
+ ok: true,
1617
+ state: "completed",
1618
+ exitCode: v.exitCode,
1619
+ stdout: output.text(),
1620
+ stderr: "",
1621
+ outputWitnessMatched: output.witnessMatched,
1622
+ logs: { stdout: output.metadata(), stderr: { bytes: 0, truncated: false } }
1623
+ };
1624
+ }
1625
+
1626
+ class WindowsJobBackend {
1627
+ #options;
1628
+ constructor(options) {
1629
+ for (const [value, maximum] of [
1630
+ [options.timeoutMs ?? ISOLATION_RUN_LIMITS.timeoutMs, 86400000],
1631
+ [
1632
+ options.maxOutputBytes ?? ISOLATION_RUN_LIMITS.maxOutputBytes,
1633
+ 1024 * 1024
1634
+ ]
1635
+ ])
1636
+ if (!Number.isSafeInteger(value) || value < 1 || value > maximum)
1637
+ throw new Error("isolation-run-limits-invalid");
1638
+ this.#options = options;
1639
+ }
1640
+ available() {
1641
+ return (this.#options.platform ?? process.platform) === "win32" && (this.#options.arch ?? process.arch) === "x64" && (this.#options.probe ?? verifyWindowsJobHelper)(this.#options.helperPath);
1642
+ }
1643
+ async run(input) {
1644
+ if (!this.available())
1645
+ return {
1646
+ ok: false,
1647
+ state: "paused",
1648
+ code: "isolation-backend-unavailable"
1649
+ };
1650
+ if (input.signal?.aborted)
1651
+ return { ok: false, state: "cancelled", code: "cancelled" };
1652
+ if (input.outputWitness !== undefined && (!text(input.outputWitness) || input.outputWitness.length < 1 || input.outputWitness.length > 512))
1653
+ return {
1654
+ ok: false,
1655
+ state: "paused",
1656
+ code: "isolation-backend-launch-failed"
1657
+ };
1658
+ const timeoutMs = this.#options.timeoutMs ?? ISOLATION_RUN_LIMITS.timeoutMs;
1659
+ let wire;
1660
+ try {
1661
+ wire = encodeWindowsJobRequest(input, timeoutMs);
1662
+ } catch {
1663
+ return {
1664
+ ok: false,
1665
+ state: "paused",
1666
+ code: "isolation-backend-launch-failed"
1667
+ };
1668
+ }
1669
+ const output = new BoundedLog(this.#options.maxOutputBytes ?? ISOLATION_RUN_LIMITS.maxOutputBytes, input.outputWitness);
1670
+ return new Promise((resolve2) => {
1671
+ let child;
1672
+ let line = "", final, bad = false, closed = false, launchFailed = false;
1673
+ let timer;
1674
+ let stopping = false;
1675
+ const armDeadline = (duration) => {
1676
+ clearTimeout(timer);
1677
+ timer = setTimeout(() => {
1678
+ bad = true;
1679
+ child.kill();
1680
+ finish(uncertain());
1681
+ }, duration);
1682
+ timer.unref?.();
1683
+ };
1684
+ const cancel = () => {
1685
+ if (stopping || closed)
1686
+ return;
1687
+ stopping = true;
1688
+ child.stdin?.end();
1689
+ armDeadline(SHUTDOWN_MS + 5000);
1690
+ };
1691
+ const finish = (result) => {
1692
+ if (closed)
1693
+ return;
1694
+ closed = true;
1695
+ clearTimeout(timer);
1696
+ input.signal?.removeEventListener("abort", cancel);
1697
+ resolve2(result);
1698
+ };
1699
+ try {
1700
+ child = (this.#options.spawnProcess ?? spawn2)(this.#options.helperPath, [], {
1701
+ cwd: input.root,
1702
+ shell: false,
1703
+ windowsHide: true,
1704
+ stdio: ["pipe", "pipe", "pipe"]
1705
+ });
1706
+ } catch {
1707
+ finish({
1708
+ ok: false,
1709
+ state: "paused",
1710
+ code: "isolation-backend-launch-failed"
1711
+ });
1712
+ return;
1713
+ }
1714
+ const malformed = () => {
1715
+ bad = true;
1716
+ cancel();
1717
+ };
1718
+ child.stdout?.on("data", (chunk) => {
1719
+ if (closed || bad)
1720
+ return;
1721
+ line += chunk.toString("utf8");
1722
+ while (line.includes(`
1723
+ `)) {
1724
+ const end = line.indexOf(`
1725
+ `);
1726
+ const row = line.slice(0, end);
1727
+ line = line.slice(end + 1);
1728
+ if (row.length > 4096 || final) {
1729
+ malformed();
1730
+ return;
1731
+ }
1732
+ try {
1733
+ const value = JSON.parse(row);
1734
+ if (Object.keys(value).length === 1 && typeof value.outputHex === "string" && /^(?:[a-f0-9]{2}){1,1024}$/u.test(value.outputHex))
1735
+ output.append(Buffer.from(value.outputHex, "hex"));
1736
+ else {
1737
+ final = observation(value);
1738
+ if (!final) {
1739
+ malformed();
1740
+ return;
1741
+ }
1742
+ }
1743
+ } catch {
1744
+ malformed();
1745
+ return;
1746
+ }
1747
+ }
1748
+ if (line.length > 4096)
1749
+ malformed();
1750
+ });
1751
+ child.stderr?.on("data", () => {});
1752
+ child.stdin?.on("error", () => {
1753
+ bad = true;
1754
+ });
1755
+ child.once("error", () => {
1756
+ bad = true;
1757
+ launchFailed = child.pid === undefined;
1758
+ });
1759
+ child.once("close", (code) => finish(launchFailed ? {
1760
+ ok: false,
1761
+ state: "paused",
1762
+ code: "isolation-backend-launch-failed"
1763
+ } : bad || line.length ? uncertain() : classify(final, code, output, input.signal?.aborted === true)));
1764
+ armDeadline(timeoutMs + SHUTDOWN_MS + 1e4);
1765
+ input.signal?.addEventListener("abort", cancel, { once: true });
1766
+ child.stdin?.write(wire);
1767
+ if (input.signal?.aborted)
1768
+ cancel();
1769
+ });
1770
+ }
1771
+ }
1772
+ function probeWindowsJob(helper) {
1773
+ if (process.platform !== "win32" || process.arch !== "x64" || !verifyWindowsJobHelper(helper))
1774
+ return false;
1775
+ const wire = encodeWindowsJobRequest({
1776
+ root: process.cwd(),
1777
+ executable: process.execPath,
1778
+ args: ["-e", "process.exit(0)"],
1779
+ environment: { SystemRoot: process.env.SystemRoot ?? "C:\\Windows" }
1780
+ }, 5000);
1781
+ const result = spawnSync(helper, ["--probe"], {
1782
+ input: wire,
1783
+ shell: false,
1784
+ encoding: "utf8",
1785
+ timeout: 15000,
1786
+ maxBuffer: 65536
1787
+ });
1788
+ try {
1789
+ const v = observation(JSON.parse(result.stdout.trim()));
1790
+ return !result.error && result.status === 0 && v?.outcome === "complete" && v.exitCode === 0;
1791
+ } catch {
1792
+ return false;
1793
+ }
1794
+ }
1795
+
1246
1796
  // src/operator-tools.ts
1247
1797
  var PACKAGE_LOCKS = {
1248
1798
  bun: ["bun.lock", "bun.lockb"],
@@ -1252,7 +1802,7 @@ var PACKAGE_LOCKS = {
1252
1802
  };
1253
1803
  function packageManagerSelection(root, declaration) {
1254
1804
  const managers = Object.keys(PACKAGE_LOCKS);
1255
- const lockfiles = managers.flatMap((manager) => PACKAGE_LOCKS[manager].filter((file) => lstatSync6(path5.join(root, file), { throwIfNoEntry: false })?.isFile()));
1805
+ const lockfiles = managers.flatMap((manager) => PACKAGE_LOCKS[manager].filter((file) => lstatSync8(path8.join(root, file), { throwIfNoEntry: false })?.isFile()));
1256
1806
  const inferred = managers.filter((manager) => PACKAGE_LOCKS[manager].some((file) => lockfiles.includes(file)));
1257
1807
  const declared = typeof declaration === "string" ? managers.find((manager) => new RegExp(`^${manager}@[0-9]+\\.[0-9]+\\.[0-9]+(?:[-+][a-z0-9._+-]+)?$`, "iu").test(declaration)) : undefined;
1258
1808
  const selectedPackageManager = declaration !== undefined ? declared ?? null : inferred.length === 1 ? inferred[0] ?? null : inferred.length > 1 ? null : "npm";
@@ -1285,7 +1835,7 @@ function inspectConsumer(root) {
1285
1835
  return process.versions.node;
1286
1836
  }, "Install Node >=22.13.");
1287
1837
  check("git", () => {
1288
- const result = spawnSync("git", ["rev-parse", "--show-toplevel"], {
1838
+ const result = spawnSync2("git", ["rev-parse", "--show-toplevel"], {
1289
1839
  cwd: root,
1290
1840
  encoding: "utf8",
1291
1841
  timeout: 5000,
@@ -1301,9 +1851,14 @@ function inspectConsumer(root) {
1301
1851
  }, "Install @fission-ai/openspec@1.5.0, or configure ABEL_OPENSPEC_PACKAGE_ROOT / ABEL_OPENSPEC_NODE before launching the host.");
1302
1852
  check("execution", () => {
1303
1853
  const profile = executionProfile();
1854
+ if (profile.mode === "host-trusted") {
1855
+ if (!probeWindowsJob(profile.windowsHelperPath ?? ""))
1856
+ throw new Error("windows-job-backend-unavailable");
1857
+ return "host-trusted: Windows x64 Job probe passed; host files/network accessible";
1858
+ }
1304
1859
  if (process.platform !== "linux")
1305
1860
  throw new Error("linux-pid-isolation-required");
1306
- const result = spawnSync(profile.bwrapPath, [
1861
+ const result = spawnSync2(profile.bwrapPath, [
1307
1862
  "--ro-bind",
1308
1863
  "/",
1309
1864
  "/",
@@ -1314,45 +1869,45 @@ function inspectConsumer(root) {
1314
1869
  if (result.status !== 0)
1315
1870
  throw new Error("bubblewrap-probe-failed");
1316
1871
  return `${profile.mode}: Bubblewrap PID namespace probe passed`;
1317
- }, "On Linux install/enable Bubblewrap; set ABEL_BWRAP_PATH for a custom install. Trusted Linux projects may explicitly set ABEL_EXECUTION_MODE=local-trusted; Windows should use WSL.");
1872
+ }, "On Linux install/enable Bubblewrap; set ABEL_BWRAP_PATH for a custom install. Trusted Linux projects may explicitly set ABEL_EXECUTION_MODE=local-trusted; Windows x64 trusted projects may explicitly select ABEL_EXECUTION_MODE=host-trusted and install the matching native Job helper (ABEL_WINDOWS_JOB_HELPER).");
1318
1873
  check("request-budgets", () => JSON.stringify(requestBounds()), "Set ABEL_FIRST_PROGRESS_MS and ABEL_STREAM_IDLE_MS to positive milliseconds <=1200000.");
1319
1874
  check("state-root", () => resolveStateRoot({
1320
1875
  consumerRoot: root,
1321
1876
  xdgStateHome: process.env.XDG_STATE_HOME
1322
1877
  }).rootDir, "Use an absolute repository-external XDG_STATE_HOME.");
1323
1878
  check("workspace-dependencies", () => {
1324
- const dependencies = path5.join(root, "node_modules");
1325
- if (!lstatSync6(dependencies, { throwIfNoEntry: false }))
1879
+ const dependencies = path8.join(root, "node_modules");
1880
+ if (!lstatSync8(dependencies, { throwIfNoEntry: false }))
1326
1881
  return "no node_modules; prepare dependencies before verification";
1327
- const names = readdirSync4(dependencies).flatMap((name) => name.startsWith("@") && lstatSync6(path5.join(dependencies, name)).isDirectory() ? readdirSync4(path5.join(dependencies, name)).map((child) => `${name}/${child}`) : [name]);
1882
+ const names = readdirSync4(dependencies).flatMap((name) => name.startsWith("@") && lstatSync8(path8.join(dependencies, name)).isDirectory() ? readdirSync4(path8.join(dependencies, name)).map((child) => `${name}/${child}`) : [name]);
1328
1883
  if (names.length > 20000)
1329
1884
  throw new Error("dependency-view-too-large");
1330
1885
  let links = 0;
1331
1886
  for (const name of names) {
1332
- const file = path5.join(dependencies, name);
1333
- if (!lstatSync6(file).isSymbolicLink())
1887
+ const file = path8.join(dependencies, name);
1888
+ if (!lstatSync8(file).isSymbolicLink())
1334
1889
  continue;
1335
1890
  const resolved = realpathSync4(file);
1336
- const relative = path5.relative(root, resolved);
1337
- if (relative === ".." || relative.startsWith(`..${path5.sep}`) || path5.isAbsolute(relative))
1891
+ const relative = path8.relative(root, resolved);
1892
+ if (relative === ".." || relative.startsWith(`..${path8.sep}`) || path8.isAbsolute(relative))
1338
1893
  throw new Error("dependency-link-outside-consumer");
1339
- if (!resolved.startsWith(`${dependencies}${path5.sep}`))
1894
+ if (!resolved.startsWith(`${dependencies}${path8.sep}`))
1340
1895
  links++;
1341
1896
  }
1342
1897
  return `${links} repository workspace link(s); their targets must be included in the candidate baseline`;
1343
1898
  }, "Repair dangling/external workspace links and ensure local package sources are tracked or admitted in the candidate.");
1344
- const manifest = path5.join(root, "package.json");
1899
+ const manifest = path8.join(root, "package.json");
1345
1900
  let selection = packageManagerSelection(root, undefined);
1346
- if (lstatSync6(manifest, { throwIfNoEntry: false })?.isFile()) {
1901
+ if (lstatSync8(manifest, { throwIfNoEntry: false })?.isFile()) {
1347
1902
  check("manifest", () => {
1348
- const parsed2 = JSON.parse(readFileSync4(manifest, "utf8"));
1903
+ const parsed2 = JSON.parse(readFileSync5(manifest, "utf8"));
1349
1904
  if (!parsed2 || typeof parsed2 !== "object")
1350
1905
  throw new Error("manifest-invalid");
1351
1906
  return Object.keys(parsed2.scripts ?? {}).join(", ") || "no package scripts";
1352
1907
  }, "Repair package.json.");
1353
1908
  const parsed = (() => {
1354
1909
  try {
1355
- const value = JSON.parse(readFileSync4(manifest, "utf8"));
1910
+ const value = JSON.parse(readFileSync5(manifest, "utf8"));
1356
1911
  return value && typeof value === "object" && !Array.isArray(value) ? value : {};
1357
1912
  } catch {
1358
1913
  return {};
@@ -1395,7 +1950,7 @@ function storageUsage(root, maximum = 1e5) {
1395
1950
  truncated = true;
1396
1951
  return;
1397
1952
  }
1398
- const stat = lstatSync6(file, { throwIfNoEntry: false });
1953
+ const stat = lstatSync8(file, { throwIfNoEntry: false });
1399
1954
  if (!stat)
1400
1955
  return;
1401
1956
  entries++;
@@ -1403,7 +1958,7 @@ function storageUsage(root, maximum = 1e5) {
1403
1958
  bytes += stat.size;
1404
1959
  else if (stat.isDirectory())
1405
1960
  for (const name of readdirSync4(file)) {
1406
- visit(path5.join(file, name));
1961
+ visit(path8.join(file, name));
1407
1962
  if (truncated)
1408
1963
  break;
1409
1964
  }
@@ -1417,7 +1972,7 @@ function inspectRuns(root) {
1417
1972
  xdgStateHome: process.env.XDG_STATE_HOME
1418
1973
  });
1419
1974
  const usage = storageUsage(state.rootDir);
1420
- if (!lstatSync6(state.databasePath, { throwIfNoEntry: false }))
1975
+ if (!lstatSync8(state.databasePath, { throwIfNoEntry: false }))
1421
1976
  return { stateRoot: state.rootDir, usage, runs: [], truncated: false };
1422
1977
  const db = new DatabaseSync(state.databasePath, { readOnly: true });
1423
1978
  try {
@@ -1438,7 +1993,7 @@ var [command = "doctor", root = process.cwd()] = process.argv.slice(2);
1438
1993
  try {
1439
1994
  if (!["doctor", "runs"].includes(command))
1440
1995
  throw new Error("Usage: node --experimental-strip-types <cadence>/src/operator-cli.mjs doctor|runs [consumer-root]");
1441
- const result = command === "doctor" ? inspectConsumer(path6.resolve(root)) : inspectRuns(path6.resolve(root));
1996
+ const result = command === "doctor" ? inspectConsumer(path9.resolve(root)) : inspectRuns(path9.resolve(root));
1442
1997
  console.log(JSON.stringify(result, null, 2));
1443
1998
  if (command === "doctor" && !result.ok)
1444
1999
  process.exitCode = 1;