@outerlayer/cli 0.2.0 → 0.4.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.
Files changed (141) hide show
  1. package/CHANGELOG.md +132 -0
  2. package/README.md +229 -60
  3. package/dist/agent-setup-IFH3FB5X.js +197 -0
  4. package/dist/build-TZUUTRGH.js +9 -0
  5. package/dist/build-info.json +1 -1
  6. package/dist/check-7MFH6HNC.js +94 -0
  7. package/dist/chunk-3TFPDVTI.js +94 -0
  8. package/dist/{chunk-4TNZMO7V.js → chunk-4C4THABW.js} +5566 -754
  9. package/dist/{chunk-R5KBGVII.js → chunk-5QRP3MVS.js} +30 -14
  10. package/dist/chunk-5Y3QQRUH.js +300 -0
  11. package/dist/chunk-774HEQL6.js +178 -0
  12. package/dist/{work-pr-cmd-AZQPWH4H.js → chunk-7WERLFVR.js} +9 -26
  13. package/dist/chunk-ABIWDUMS.js +104 -0
  14. package/dist/chunk-AUFG23AA.js +534 -0
  15. package/dist/chunk-B2G7JHHB.js +42 -0
  16. package/dist/chunk-BCJNHMZT.js +34 -0
  17. package/dist/chunk-BCLJSQCV.js +56 -0
  18. package/dist/chunk-BFESLKPP.js +61 -0
  19. package/dist/chunk-BJ3KTMDH.js +63 -0
  20. package/dist/chunk-BKO6JEZI.js +47 -0
  21. package/dist/chunk-BOLTI6LR.js +37 -0
  22. package/dist/chunk-CBPPJSAR.js +89 -0
  23. package/dist/chunk-DM3VFDS3.js +1461 -0
  24. package/dist/{chunk-65WXAANR.js → chunk-DVDEBNQJ.js} +17 -2
  25. package/dist/{chunk-TFUIDMOB.js → chunk-EABW6AJQ.js} +12 -1
  26. package/dist/{chunk-KFYJV2ZG.js → chunk-EMY4I27X.js} +1 -1
  27. package/dist/{chunk-NTTPJV35.js → chunk-F47JBIAW.js} +3 -1
  28. package/dist/chunk-F6GFZXZ3.js +60 -0
  29. package/dist/{chunk-YOOSBOKS.js → chunk-F7CU5ABH.js} +1 -1
  30. package/dist/{emit-cmd-TWSYYEZI.js → chunk-H5NCNVDA.js} +68 -17
  31. package/dist/chunk-HZRNMGLF.js +2233 -0
  32. package/dist/chunk-LF6MLJCY.js +38 -0
  33. package/dist/{chunk-U32VSRLO.js → chunk-LT5TZHYH.js} +95 -634
  34. package/dist/chunk-LYCDNMOH.js +83 -0
  35. package/dist/chunk-M5POMKKW.js +1051 -0
  36. package/dist/{mcp-install-cmd-FDQH6SEN.js → chunk-MQ3IPHIZ.js} +35 -18
  37. package/dist/{chunk-D77LS3UI.js → chunk-N5FOE5PS.js} +33 -4
  38. package/dist/chunk-N6LURUEF.js +64 -0
  39. package/dist/chunk-OGFGZQA3.js +23 -0
  40. package/dist/chunk-QDQEUVUF.js +9 -0
  41. package/dist/chunk-QOZKPLVJ.js +226 -0
  42. package/dist/chunk-QY22NZUW.js +1764 -0
  43. package/dist/chunk-RA3O54FC.js +30 -0
  44. package/dist/chunk-RFUPN6KX.js +71 -0
  45. package/dist/chunk-SY4GK4SP.js +55 -0
  46. package/dist/chunk-TBT347UY.js +41 -0
  47. package/dist/chunk-TWNWNS2Q.js +1084 -0
  48. package/dist/{chunk-NXLLURA4.js → chunk-UFFXNXLQ.js} +115 -71
  49. package/dist/chunk-USO2DKBD.js +421 -0
  50. package/dist/chunk-W4SQCOZU.js +206 -0
  51. package/dist/{chunk-Q6CL5THG.js → chunk-W5FVUZXV.js} +208 -61
  52. package/dist/chunk-WFJ65NUD.js +383 -0
  53. package/dist/{chunk-A3WLZX2F.js → chunk-WIOZAJ2W.js} +15 -2
  54. package/dist/chunk-WO2BXCTQ.js +83 -0
  55. package/dist/{chunk-I3ETLSNE.js → chunk-XCCLFVXM.js} +120 -11
  56. package/dist/{context-materialize-6WKT3RBQ.js → chunk-XDDW4FRS.js} +134 -26
  57. package/dist/chunk-Y7KJLXYN.js +232 -0
  58. package/dist/chunk-YFHIIH4B.js +102 -0
  59. package/dist/chunk-YYYXRJUW.js +101 -0
  60. package/dist/chunk-ZM2IMMYN.js +76 -0
  61. package/dist/chunk-ZMYPLFG3.js +71 -0
  62. package/dist/chunk-ZNA27WEV.js +470 -0
  63. package/dist/{cli-G7DILYJY.js → cli-W62AFRSW.js} +616 -878
  64. package/dist/{paths-D2VGWWFI.js → cli-build-K56DK4DS.js} +1 -1
  65. package/dist/config-XVJYZ4IQ.js +9 -0
  66. package/dist/connect-cmd-ZTJONP37.js +16 -0
  67. package/dist/context-adopt-UMA4O5NY.js +105 -0
  68. package/dist/context-materialize-G2FUFC72.js +19 -0
  69. package/dist/dist-44CQVWGT.js +6 -0
  70. package/dist/docker-NEGDLU6D.js +7 -0
  71. package/dist/doctor-53F2PYY7.js +43 -0
  72. package/dist/doctor-ZLGZYMDK.js +426 -0
  73. package/dist/{emit-artifact-cmd-UQVT3OKW.js → emit-artifact-cmd-AKZP7TMF.js} +87 -23
  74. package/dist/emit-cmd-DCERAU27.js +9 -0
  75. package/dist/emit-criteria-cmd-7MXQVCKD.js +166 -0
  76. package/dist/{emit-finding-cmd-FDOU2OPD.js → emit-finding-cmd-N2FTGCVC.js} +34 -31
  77. package/dist/{emit-result-cmd-H2T4C2K3.js → emit-result-cmd-4CPDBMN2.js} +34 -62
  78. package/dist/exec-client-4XGXV3XN.js +8 -0
  79. package/dist/guest-init-GENRHNP7.js +8 -0
  80. package/dist/hook-fast-EYENPK6F.js +9 -0
  81. package/dist/{hook-wrap-fast-KXYNX3AD.js → hook-wrap-fast-NPLHJXFK.js} +2 -2
  82. package/dist/host-key-KDYZ5ECC.js +7 -0
  83. package/dist/import-capture-cmd-AVZ4VIVJ.js +68 -0
  84. package/dist/{import-ruler-cmd-7LH2QOMG.js → import-ruler-cmd-GUHL6IY5.js} +1 -1
  85. package/dist/index.js +4 -4
  86. package/dist/init-55Y4LHWF.js +36 -0
  87. package/dist/init-XXEKWZQP.js +221 -0
  88. package/dist/init-cmd-WLST2F7G.js +135 -0
  89. package/dist/install-cmd-7WXOIFDO.js +55 -0
  90. package/dist/lima-XN65D7GN.js +55 -0
  91. package/dist/login-browser-7XGSV7MA.js +10 -0
  92. package/dist/{config-POF7DEQW.js → logout-cmd-QAUFDJ6J.js} +3 -1
  93. package/dist/{logs-TRNPQM42.js → logs-7RYUH5A6.js} +1 -1
  94. package/dist/loop-JFIBP4B7.js +43 -0
  95. package/dist/machine-ZBPT2J3R.js +35 -0
  96. package/dist/mcp-install-cmd-RLK4SO3N.js +9 -0
  97. package/dist/{mcp-serve-cmd-57EZZOTL.js → mcp-serve-cmd-N6UD2KBX.js} +20 -9
  98. package/dist/paths-OYKMVYJP.js +6 -0
  99. package/dist/{pidfile-PTW76F56.js → pidfile-UZRH774M.js} +2 -3
  100. package/dist/real-deps-SU24ZA2K.js +21 -0
  101. package/dist/relay-L76HDX72.js +46 -0
  102. package/dist/settings-J2652U5N.js +7 -0
  103. package/dist/starter-pack-LIZYMKYQ.js +10 -0
  104. package/dist/{status-I27IST4L.js → status-UJXWZRN5.js} +30 -13
  105. package/dist/{statusline-fast-3C5OXHDD.js → statusline-fast-SVTMBMD7.js} +3 -2
  106. package/dist/sync-cmd-4G4A7EVN.js +28 -0
  107. package/dist/version-BWM6VLDI.js +6 -0
  108. package/dist/{watch-3DTPJETH.js → watch-7SKJKGAA.js} +19 -8
  109. package/dist/{work-claim-cmd-5AUNCLS2.js → work-claim-cmd-5F7VZ42N.js} +34 -21
  110. package/dist/work-cmd-QAUZ7MTD.js +17 -0
  111. package/dist/work-comment-cmd-BNCSBTLH.js +106 -0
  112. package/dist/{work-launch-LT663PB3.js → work-launch-YI4CJDAP.js} +1 -1
  113. package/dist/work-open-pr-cmd-VFYZHIIW.js +156 -0
  114. package/dist/work-pr-cmd-PV32ZLSI.js +16 -0
  115. package/package.json +13 -3
  116. package/skill-pack/maintained/amend/SKILL.md +104 -0
  117. package/skill-pack/maintained/emitting-evidence/SKILL.md +108 -0
  118. package/skill-pack/maintained/emitting-evidence/references/agents-snippet.md +20 -0
  119. package/skill-pack/maintained/outerlayer/SKILL.md +55 -0
  120. package/skill-pack/maintained/reporting-findings/SKILL.md +148 -0
  121. package/skill-pack/template/build/SKILL.md +193 -0
  122. package/skill-pack/template/build/references/agent-briefs.md +243 -0
  123. package/skill-pack/template/build/references/criteria-judge.md +91 -0
  124. package/skill-pack/template/build/references/evidence.md +42 -0
  125. package/skill-pack/template/build/references/release.md +74 -0
  126. package/skill-pack/template/build/references/review-briefs.md +275 -0
  127. package/skill-pack/template/build/references/review-loop.md +158 -0
  128. package/skill-pack/template/build/scripts/record-criteria.mjs +235 -0
  129. package/skill-pack/template/spec/SKILL.md +84 -0
  130. package/skill-pack/template/writing-specs/SKILL.md +134 -0
  131. package/dist/chunk-DCNOXRMV.js +0 -589
  132. package/dist/chunk-JJP7YLMN.js +0 -25
  133. package/dist/chunk-OZ7C3XUE.js +0 -34
  134. package/dist/chunk-WQ6VGRGZ.js +0 -150
  135. package/dist/hook-fast-J5LCDHUJ.js +0 -8
  136. package/dist/import-capture-cmd-EUMBGIV3.js +0 -176
  137. package/dist/init-PTBITAUO.js +0 -103
  138. package/dist/login-cmd-IRX6LZT7.js +0 -62
  139. package/dist/loop-ASZRX3CZ.js +0 -648
  140. package/dist/sync-cmd-BBAUZ5JD.js +0 -17
  141. package/dist/work-cmd-2P4BVX47.js +0 -18
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'node:module';
3
+ import { spawn } from 'child_process';
4
+ import { existsSync } from 'fs';
5
+ import { join, dirname } from 'path';
6
+ import { createHash } from 'crypto';
7
+
8
+ createRequire(import.meta.url);
9
+ var REGISTRY_URL = "https://registry.npmjs.org";
10
+ var PACKAGE_NAME = "@outerlayer/cli";
11
+ var PACKAGE_PATH = "@outerlayer%2fcli";
12
+ var SLSA_PREDICATE = "https://slsa.dev/provenance/v1";
13
+ var REQUEST_TIMEOUT_MS = 6e4;
14
+ function get(url, fetchImpl) {
15
+ return fetchImpl(url, { signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS), headers: { accept: "application/json" } });
16
+ }
17
+ async function fetchVersionMeta(version, fetchImpl) {
18
+ const response = await get(`${REGISTRY_URL}/${PACKAGE_PATH}/${encodeURIComponent(version)}`, fetchImpl);
19
+ if (response.status === 404) return { kind: "not_published" };
20
+ if (!response.ok) throw new Error(`the registry answered HTTP ${response.status} for ${PACKAGE_NAME}@${version}`);
21
+ const body = await response.json();
22
+ const { integrity, tarball } = body.dist ?? {};
23
+ if (typeof integrity !== "string" || typeof tarball !== "string") {
24
+ throw new Error(`the registry's metadata for ${PACKAGE_NAME}@${version} has no dist.integrity or dist.tarball`);
25
+ }
26
+ const nodeRange = body.engines?.node;
27
+ return typeof nodeRange === "string" ? { kind: "found", integrity, tarball, nodeRange } : { kind: "found", integrity, tarball };
28
+ }
29
+ async function fetchTarballDigest(url, fetchImpl) {
30
+ if (!url.startsWith(`${REGISTRY_URL}/`)) throw new Error(`the tarball is not served by ${REGISTRY_URL}: ${url}`);
31
+ const response = await get(url, fetchImpl);
32
+ if (!response.ok) throw new Error(`the registry answered HTTP ${response.status} for the tarball`);
33
+ return createHash("sha512").update(Buffer.from(await response.arrayBuffer())).digest("base64");
34
+ }
35
+ async function fetchProvenance(version, fetchImpl) {
36
+ const response = await get(`${REGISTRY_URL}/-/npm/v1/attestations/${PACKAGE_PATH}@${encodeURIComponent(version)}`, fetchImpl);
37
+ if (response.status === 404) return void 0;
38
+ if (!response.ok) throw new Error(`the registry answered HTTP ${response.status} for the attestations of ${PACKAGE_NAME}@${version}`);
39
+ const body = await response.json();
40
+ const slsa = (body.attestations ?? []).find((a) => a.predicateType === SLSA_PREDICATE);
41
+ const payload = slsa?.bundle?.dsseEnvelope?.payload;
42
+ if (typeof payload !== "string") return void 0;
43
+ const statement = JSON.parse(Buffer.from(payload, "base64").toString("utf8"));
44
+ const workflow = statement.predicate?.buildDefinition?.externalParameters?.workflow;
45
+ const subjectSha512 = statement.subject?.[0]?.digest?.sha512;
46
+ if (typeof workflow?.repository !== "string" || typeof workflow.path !== "string" || typeof subjectSha512 !== "string") return void 0;
47
+ return { repository: workflow.repository, workflowPath: workflow.path, subjectSha512 };
48
+ }
49
+
50
+ // src/runner/update/npm.ts
51
+ function run(file, args, cwd) {
52
+ const COMMAND_TIMEOUT_MS = 10 * 6e4;
53
+ return new Promise((resolve) => {
54
+ const child = spawn(file, args, { cwd, stdio: ["ignore", "pipe", "pipe"], timeout: COMMAND_TIMEOUT_MS });
55
+ let stdout = "";
56
+ let stderr = "";
57
+ child.stdout.on("data", (chunk) => stdout += chunk.toString());
58
+ child.stderr.on("data", (chunk) => stderr += chunk.toString());
59
+ child.on("error", (err) => resolve({ status: 127, stdout, stderr: `${stderr}${err.message}` }));
60
+ child.on("close", (code) => resolve({ status: code ?? 1, stdout, stderr }));
61
+ });
62
+ }
63
+ function npmExecutable(nodePath) {
64
+ const beside = join(dirname(nodePath), "npm");
65
+ return existsSync(beside) ? beside : "npm";
66
+ }
67
+ function realRunNpm(nodePath) {
68
+ const npm = npmExecutable(nodePath);
69
+ return (args, cwd) => run(npm, args, cwd);
70
+ }
71
+ function realRunNode(nodePath) {
72
+ return (args, cwd) => run(nodePath, args, cwd);
73
+ }
74
+ function npmSupportsProvenance(version) {
75
+ const match = /^(\d+)\.(\d+)/.exec(version.trim());
76
+ if (match === null) return false;
77
+ const major = Number(match[1]);
78
+ return major > 9 || major === 9 && Number(match[2]) >= 5;
79
+ }
80
+ async function npmVersion(runNpm, cwd) {
81
+ const result = await runNpm(["--version"], cwd);
82
+ return result.status === 0 ? result.stdout.trim() : void 0;
83
+ }
84
+ async function installVersion(runNpm, stagingDir, version) {
85
+ return runNpm(
86
+ ["install", "--prefix", stagingDir, "--registry", REGISTRY_URL, "--ignore-scripts", "--no-audit", "--no-fund", `@outerlayer/cli@${version}`],
87
+ stagingDir
88
+ );
89
+ }
90
+ async function auditSignatures(runNpm, dir) {
91
+ const result = await runNpm(["audit", "signatures", "--json", "--registry", REGISTRY_URL], dir);
92
+ try {
93
+ const report = JSON.parse(result.stdout);
94
+ if ((report.invalid ?? []).length > 0) return { ok: false, verdict: true, reason: "npm audit signatures reported an invalid signature" };
95
+ if ((report.missing ?? []).some((entry) => entry.name === "@outerlayer/cli")) {
96
+ return { ok: false, verdict: true, reason: "npm audit signatures found no signature for @outerlayer/cli" };
97
+ }
98
+ } catch {
99
+ }
100
+ if (result.status !== 0) return { ok: false, verdict: false, reason: "npm audit signatures failed" };
101
+ return { ok: true };
102
+ }
103
+
104
+ export { auditSignatures, fetchProvenance, fetchTarballDigest, fetchVersionMeta, installVersion, npmSupportsProvenance, npmVersion, realRunNode, realRunNpm };
@@ -0,0 +1,534 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'node:module';
3
+ import { existsSync, unlinkSync, chmodSync } from 'fs';
4
+ import { request as request$1, createServer } from 'http';
5
+ import { request } from 'https';
6
+ import { createServer as createServer$1 } from 'net';
7
+ import { join } from 'path';
8
+
9
+ createRequire(import.meta.url);
10
+
11
+ // src/runner/builtin-destinations.ts
12
+ var GATEWAY_DESTINATION = "gateway";
13
+ var CLAUDE_DESTINATION = "claude";
14
+ var BUILT_IN_NAMES = [GATEWAY_DESTINATION, CLAUDE_DESTINATION];
15
+ var GATEWAY_PORT = 3126;
16
+ var CLAUDE_PORT = 3127;
17
+ var GATEWAY_SOCKET = "gateway.sock";
18
+ var CLAUDE_SOCKET = "claude.sock";
19
+ var CLAUDE_UPSTREAM = "https://api.anthropic.com/";
20
+ var AGENT_CREDENTIAL_MISSING = "agent_credential_missing";
21
+ var OAUTH_PLACEHOLDER = "sk-ant-oat01-placeholder-not-a-real-token";
22
+ var API_KEY_PLACEHOLDER = "sk-ant-api03-placeholder-not-a-real-key";
23
+ var ITEM_KEY_PLACEHOLDER = "olitem_placeholder-not-a-real-key";
24
+ var ITEM_KEY_VARIABLE = "OUTERLAYER_ITEM_KEY";
25
+ var CLAUDE_CREDENTIAL_VARIABLES = ["CLAUDE_CODE_OAUTH_TOKEN", "ANTHROPIC_API_KEY", "ANTHROPIC_AUTH_TOKEN"];
26
+ function claudeCredential(hostEnv) {
27
+ const held = (name) => hostEnv[name] !== void 0 && hostEnv[name] !== "";
28
+ if (held("CLAUDE_CODE_OAUTH_TOKEN")) return { variable: "CLAUDE_CODE_OAUTH_TOKEN", placeholder: OAUTH_PLACEHOLDER };
29
+ if (held("ANTHROPIC_API_KEY")) return { variable: "ANTHROPIC_API_KEY", placeholder: API_KEY_PLACEHOLDER };
30
+ return void 0;
31
+ }
32
+ function gatewayUpstream(gatewayUrl) {
33
+ return `${new URL(gatewayUrl).origin}/`;
34
+ }
35
+ function builtInDestinations(input) {
36
+ const credential = claudeCredential(input.hostEnv);
37
+ const apiKeyMode = credential?.variable === "ANTHROPIC_API_KEY";
38
+ return [
39
+ {
40
+ destination: {
41
+ name: GATEWAY_DESTINATION,
42
+ upstream: gatewayUpstream(input.gatewayUrl),
43
+ header: { name: "Authorization", value: `Bearer \${${ITEM_KEY_VARIABLE}}`, variables: [ITEM_KEY_VARIABLE] },
44
+ env: {}
45
+ },
46
+ hostEnv: input.itemKey === void 0 ? {} : { [ITEM_KEY_VARIABLE]: input.itemKey },
47
+ drop: [],
48
+ socket: GATEWAY_SOCKET
49
+ },
50
+ {
51
+ destination: {
52
+ name: CLAUDE_DESTINATION,
53
+ upstream: input.claudeUpstream ?? CLAUDE_UPSTREAM,
54
+ header: apiKeyMode ? { name: "x-api-key", value: "${ANTHROPIC_API_KEY}", variables: ["ANTHROPIC_API_KEY"] } : { name: "Authorization", value: "Bearer ${CLAUDE_CODE_OAUTH_TOKEN}", variables: ["CLAUDE_CODE_OAUTH_TOKEN"] },
55
+ env: {}
56
+ },
57
+ hostEnv: input.hostEnv,
58
+ // A build's own key in either header would sit beside the runner's, and the upstream takes one of them.
59
+ drop: ["x-api-key", "authorization"],
60
+ socket: CLAUDE_SOCKET
61
+ }
62
+ ];
63
+ }
64
+ function builtInEnvironment(localFor, credential) {
65
+ return {
66
+ OUTERLAYER_URL: localFor(GATEWAY_DESTINATION),
67
+ OUTERLAYER_API_KEY: ITEM_KEY_PLACEHOLDER,
68
+ ANTHROPIC_BASE_URL: localFor(CLAUDE_DESTINATION),
69
+ ...credential === void 0 ? {} : { [credential.variable]: credential.placeholder },
70
+ CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1"
71
+ };
72
+ }
73
+
74
+ // src/runner/destinations.ts
75
+ var MAX_RELEASED_DESTINATIONS = 20;
76
+ var MAX_DESTINATIONS = MAX_RELEASED_DESTINATIONS - BUILT_IN_NAMES.length;
77
+ var DESTINATION_BASE_PORT = 3129;
78
+ var DESTINATION_NAME = /^[a-z0-9][a-z0-9-]{0,62}$/;
79
+ var VARIABLE_NAME = /^[A-Za-z_][A-Za-z0-9_]{0,254}$/;
80
+ var HEADER_NAME = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
81
+ var HEADER_VARIABLE = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
82
+ var DESTINATION_KEYS = ["name", "upstream", "header", "env"];
83
+ var MAX_SOCKET_PATH_BYTES = 100;
84
+ var UPSTREAM_IDLE_MS = 6e5;
85
+ function headerVariables(destinations) {
86
+ return [...new Set((destinations ?? []).flatMap((d) => d.header.variables))];
87
+ }
88
+ function resolveDestinations(given, variables, isReserved, fail) {
89
+ if (!Array.isArray(given)) fail("runner.build.destinations", "must be a list of destinations");
90
+ if (given.length > MAX_DESTINATIONS) {
91
+ fail(
92
+ "runner.build.destinations",
93
+ `lists ${given.length} destinations; a release records at most ${MAX_RELEASED_DESTINATIONS}, and ${BUILT_IN_NAMES.length} of those are the built-in ${BUILT_IN_NAMES.join(" and ")} destinations, so a host lists at most ${MAX_DESTINATIONS}`
94
+ );
95
+ }
96
+ const resolved = [];
97
+ const names = /* @__PURE__ */ new Set();
98
+ const envNames = /* @__PURE__ */ new Set();
99
+ for (const [index, entry] of given.entries()) {
100
+ const at = `runner.build.destinations[${index}]`;
101
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry)) fail(at, "must be an object");
102
+ const record = entry;
103
+ for (const key of Object.keys(record)) {
104
+ if (!DESTINATION_KEYS.includes(key)) {
105
+ fail(`${at}.${key}`, `is not a key a destination accepts; the keys are ${DESTINATION_KEYS.join(", ")}`);
106
+ }
107
+ }
108
+ const name = record.name;
109
+ if (typeof name !== "string" || !DESTINATION_NAME.test(name)) {
110
+ fail(`${at}.name`, "must be 1 to 63 lower-case letters, digits or hyphens, starting with a letter or digit");
111
+ }
112
+ if (BUILT_IN_NAMES.includes(name)) fail(`${at}.name`, `is ${name}, a destination the runner serves itself`);
113
+ if (names.has(name)) fail(`${at}.name`, `repeats the destination ${name}`);
114
+ names.add(name);
115
+ const upstream = parseUpstream(record.upstream, `${at}.upstream`, fail);
116
+ const header = parseHeader(record.header, `${at}.header`, fail);
117
+ for (const variable of header.variables) {
118
+ if (variables.includes(variable)) {
119
+ fail(`${at}.header`, `names ${variable}, which build.variables also passes into the build; a secret a build can read is not a destination's`);
120
+ }
121
+ }
122
+ const env = parseEnv(record.env, `${at}.env`, isReserved, fail);
123
+ for (const variable of Object.keys(env)) {
124
+ if (envNames.has(variable)) fail(`${at}.env`, `sets ${variable}, which another destination sets too`);
125
+ envNames.add(variable);
126
+ if (variables.includes(variable)) fail(`${at}.env`, `sets ${variable}, which build.variables also passes into the build`);
127
+ }
128
+ resolved.push({ name, upstream, header, env });
129
+ }
130
+ for (const [index, destination] of resolved.entries()) {
131
+ for (const variable of destination.header.variables) {
132
+ if (envNames.has(variable)) {
133
+ fail(`runner.build.destinations[${index}].env`, `sets ${variable}, which a destination's header reads as a secret`);
134
+ }
135
+ }
136
+ }
137
+ return resolved;
138
+ }
139
+ function parseUpstream(value, key, fail) {
140
+ if (typeof value !== "string") fail(key, "must be an https:// address");
141
+ let url;
142
+ try {
143
+ url = new URL(value);
144
+ } catch {
145
+ fail(key, `${JSON.stringify(value)} is not an https:// address`);
146
+ }
147
+ if (url.protocol !== "https:") fail(key, `${JSON.stringify(value)} is not an https:// address: a destination's secret is only ever sent over TLS`);
148
+ if (url.username !== "" || url.password !== "") fail(key, "must not carry a login; the credential goes in the header");
149
+ if (url.search !== "" || url.hash !== "") fail(key, "must not carry a query or a fragment");
150
+ return url.href;
151
+ }
152
+ function parseHeader(value, key, fail) {
153
+ if (typeof value !== "string") fail(key, 'must be a string such as "Authorization: Bearer ${NPM_TOKEN}"');
154
+ const colon = value.indexOf(":");
155
+ if (colon === -1) fail(key, 'must be "Name: value", with a colon');
156
+ const name = value.slice(0, colon).trim();
157
+ const text = value.slice(colon + 1).trim();
158
+ if (!HEADER_NAME.test(name)) fail(key, `names the header ${JSON.stringify(name)}, which is not a header name`);
159
+ if (text === "") fail(key, "has an empty value");
160
+ if (/[\r\n\0]/.test(text)) fail(key, "has a value with a line break in it");
161
+ const variables = [...new Set([...text.matchAll(HEADER_VARIABLE)].map((m) => m[1]))];
162
+ return { name, value: text, variables };
163
+ }
164
+ function parseEnv(value, key, isReserved, fail) {
165
+ if (value === void 0) return {};
166
+ if (typeof value !== "object" || value === null || Array.isArray(value)) fail(key, "must be an object of variable names and values");
167
+ const env = {};
168
+ for (const [name, text] of Object.entries(value)) {
169
+ if (!VARIABLE_NAME.test(name)) fail(key, `names ${JSON.stringify(name)}, which is not a variable name`);
170
+ if (isReserved(name)) fail(key, `names ${name}, which the runner sets itself`);
171
+ if (typeof text !== "string") fail(key, `gives ${name} a value that is not a string`);
172
+ env[name] = text;
173
+ }
174
+ return env;
175
+ }
176
+ function destinationEnvironment(destinations, localFor) {
177
+ const env = {};
178
+ for (const [index, destination] of (destinations ?? []).entries()) {
179
+ for (const [name, value] of Object.entries(destination.env)) env[name] = value.split("{local}").join(localFor(index));
180
+ }
181
+ return env;
182
+ }
183
+ function containerLocalAddress(index) {
184
+ return `http://127.0.0.1:${DESTINATION_BASE_PORT + index}/`;
185
+ }
186
+ function destinationSocketPath(buildDir, index) {
187
+ return join(buildDir, `dest-${index}.sock`);
188
+ }
189
+ function respond(res, status, body) {
190
+ if (res.headersSent) {
191
+ res.destroy();
192
+ return;
193
+ }
194
+ res.writeHead(status, { "Content-Type": "text/plain", "Content-Length": Buffer.byteLength(body), Connection: "close" });
195
+ res.end(body);
196
+ }
197
+ function expandHeader(header, env) {
198
+ for (const name of header.variables) {
199
+ if (env[name] === void 0 || env[name] === "") return { missing: name };
200
+ }
201
+ return { value: header.value.replace(HEADER_VARIABLE, (_match, name) => env[name]) };
202
+ }
203
+ async function startDestination(input) {
204
+ const { destination, hostEnv } = input;
205
+ const upstream = new URL(destination.upstream);
206
+ const prefix = upstream.pathname.replace(/\/+$/, "");
207
+ const secure = upstream.protocol === "https:";
208
+ const send = input.request ?? (secure ? request : request$1);
209
+ const headerName = destination.header.name.toLowerCase();
210
+ const dropped = /* @__PURE__ */ new Set(["host", "authorization", "proxy-authorization", "cookie", "connection", "proxy-connection", "keep-alive", "transfer-encoding", "upgrade", ...input.drop ?? []]);
211
+ const handle = (req, res) => {
212
+ const target = req.url ?? "";
213
+ if (!target.startsWith("/")) {
214
+ respond(res, 400, "send an origin-form request path\n");
215
+ return;
216
+ }
217
+ const expanded = expandHeader(destination.header, hostEnv);
218
+ if ("missing" in expanded) {
219
+ respond(res, 502, `the destination ${destination.name} cannot be reached: ${expanded.missing} is not set in the runner's environment
220
+ `);
221
+ return;
222
+ }
223
+ const headers = {};
224
+ for (const [name, value] of Object.entries(req.headers)) {
225
+ if (!dropped.has(name)) headers[name] = value;
226
+ }
227
+ headers.host = upstream.host;
228
+ headers[headerName] = expanded.value;
229
+ let forwarded;
230
+ try {
231
+ forwarded = send(
232
+ {
233
+ protocol: upstream.protocol,
234
+ hostname: upstream.hostname,
235
+ port: upstream.port === "" ? secure ? 443 : 80 : Number(upstream.port),
236
+ method: req.method,
237
+ path: `${prefix}${target}`,
238
+ headers,
239
+ ...input.ca === void 0 ? {} : { ca: input.ca }
240
+ },
241
+ (answer) => {
242
+ res.writeHead(answer.statusCode ?? 502, answer.headers);
243
+ answer.on("error", () => res.destroy());
244
+ answer.on("close", () => {
245
+ if (!answer.complete) res.destroy();
246
+ });
247
+ answer.pipe(res);
248
+ }
249
+ );
250
+ } catch {
251
+ respond(res, 502, `the destination ${destination.name} cannot be reached: its header value was refused
252
+ `);
253
+ return;
254
+ }
255
+ input.onUsed?.(destination.name);
256
+ forwarded.setTimeout(UPSTREAM_IDLE_MS, () => forwarded.destroy());
257
+ res.on("close", () => forwarded.destroy());
258
+ forwarded.on("error", () => respond(res, 502, `the upstream of the destination ${destination.name} could not be reached
259
+ `));
260
+ req.pipe(forwarded);
261
+ };
262
+ const sockets = /* @__PURE__ */ new Set();
263
+ const server = createServer(handle);
264
+ server.on("connection", (socket) => {
265
+ sockets.add(socket);
266
+ socket.on("close", () => sockets.delete(socket));
267
+ });
268
+ const listeners = [server];
269
+ const served = {};
270
+ if (input.socketPath !== void 0) {
271
+ if (Buffer.byteLength(input.socketPath) > MAX_SOCKET_PATH_BYTES) {
272
+ throw new Error(`${input.socketPath} is too long for a Unix socket; keep the runner's config file nearer the root of the filesystem`);
273
+ }
274
+ if (existsSync(input.socketPath)) unlinkSync(input.socketPath);
275
+ await new Promise((resolve, reject) => {
276
+ server.once("error", reject);
277
+ server.listen(input.socketPath, () => resolve());
278
+ });
279
+ chmodSync(input.socketPath, 438);
280
+ served.socketPath = input.socketPath;
281
+ }
282
+ if (input.port !== void 0) {
283
+ const tcp = createServer$1((socket) => server.emit("connection", socket));
284
+ await new Promise((resolve, reject) => {
285
+ tcp.once("error", reject);
286
+ tcp.listen(input.port, "127.0.0.1", () => resolve());
287
+ });
288
+ listeners.push(tcp);
289
+ const address = tcp.address();
290
+ served.port = address.port;
291
+ }
292
+ const close = async () => {
293
+ for (const socket of sockets) socket.destroy();
294
+ await Promise.all(listeners.map((listener) => new Promise((resolve) => listener.close(() => resolve()))));
295
+ };
296
+ return { ...served, close };
297
+ }
298
+ async function startBuildServices(input) {
299
+ const tunnel = await input.startTunnel(join(input.buildDir, "tunnel.sock"), {
300
+ ...input.tunnelOptions,
301
+ ...input.allowHosts === void 0 ? {} : { allowHosts: input.allowHosts },
302
+ recordPath: join(input.jobDir, "tunnel.json")
303
+ });
304
+ const servers = [];
305
+ const closeAll = async () => {
306
+ await Promise.all(servers.map((s) => s.close()));
307
+ await tunnel.close();
308
+ };
309
+ try {
310
+ let proxyPort;
311
+ if (input.proxy === true) proxyPort = (await tunnel.listenTcp({ host: "127.0.0.1", port: input.ports?.proxy ?? 0 })).port;
312
+ const destinationPorts = [];
313
+ const builtInPorts = {};
314
+ for (const [index, destination] of input.destinations.entries()) {
315
+ const server = await startDestination({
316
+ destination,
317
+ ...input.serve === "tcp" ? {} : { socketPath: destinationSocketPath(input.buildDir, index) },
318
+ ...input.serve === "socket" ? {} : { port: input.ports?.destinations?.[index] ?? 0 },
319
+ hostEnv: input.hostEnv,
320
+ onUsed: (name) => tunnel.destinationUsed(name),
321
+ ...input.destinationOptions
322
+ });
323
+ servers.push(server);
324
+ if (server.port !== void 0) destinationPorts.push(server.port);
325
+ }
326
+ const builtIns = input.builtIns === void 0 ? [] : builtInDestinations({ gatewayUrl: input.builtIns.gatewayUrl, itemKey: input.builtIns.itemKey, hostEnv: input.hostEnv, claudeUpstream: input.builtIns.claudeUpstream });
327
+ for (const builtIn of builtIns) {
328
+ const server = await startDestination({
329
+ destination: builtIn.destination,
330
+ ...input.serve === "tcp" ? {} : { socketPath: join(input.buildDir, builtIn.socket) },
331
+ ...input.serve === "socket" ? {} : { port: input.ports?.builtIns?.[builtIn.destination.name] ?? 0 },
332
+ hostEnv: builtIn.hostEnv,
333
+ drop: builtIn.drop,
334
+ onUsed: (name) => tunnel.destinationUsed(name),
335
+ ...input.destinationOptions
336
+ });
337
+ servers.push(server);
338
+ if (server.port !== void 0) builtInPorts[builtIn.destination.name] = server.port;
339
+ }
340
+ return { ...tunnel, proxyPort, destinationPorts, builtInPorts, close: closeAll };
341
+ } catch (err) {
342
+ await closeAll();
343
+ throw err;
344
+ }
345
+ }
346
+
347
+ // src/runner/commit-identity.ts
348
+ var NOREPLY_SUFFIX = "@users.noreply.github.com";
349
+ var MAX_LENGTH = 200;
350
+ function parseIdentity(value) {
351
+ if (typeof value !== "object" || value === null) return void 0;
352
+ const { name, email } = value;
353
+ if (typeof name !== "string" || typeof email !== "string") return void 0;
354
+ if (name.trim() === "" || name.length > MAX_LENGTH || /[\r\n\0<>]/.test(name)) return void 0;
355
+ if (email.length > MAX_LENGTH || !email.endsWith(NOREPLY_SUFFIX) || /[\s\0<>]/.test(email) || email.length === NOREPLY_SUFFIX.length) return void 0;
356
+ return { name, email };
357
+ }
358
+ function parseClaimIdentity(data) {
359
+ const author = parseIdentity(data.commitAuthor);
360
+ if (author === void 0) return void 0;
361
+ const coAuthor = parseIdentity(data.coAuthor);
362
+ return coAuthor === void 0 ? { author } : { author, coAuthor };
363
+ }
364
+ function authorEnvironment(identity) {
365
+ if (identity === void 0) return {};
366
+ return {
367
+ GIT_AUTHOR_NAME: identity.name,
368
+ GIT_AUTHOR_EMAIL: identity.email,
369
+ GIT_COMMITTER_NAME: identity.name,
370
+ GIT_COMMITTER_EMAIL: identity.email
371
+ };
372
+ }
373
+ function configCount(env) {
374
+ const raw = String(env.GIT_CONFIG_COUNT);
375
+ return /^[0-9]+$/.test(raw) ? Number(raw) : 0;
376
+ }
377
+ function withHooksPath(env, dir) {
378
+ const at = configCount(env);
379
+ return {
380
+ ...env,
381
+ GIT_CONFIG_COUNT: String(at + 1),
382
+ [`GIT_CONFIG_KEY_${at}`]: "core.hooksPath",
383
+ [`GIT_CONFIG_VALUE_${at}`]: dir
384
+ };
385
+ }
386
+ function commitIdentityRecord(identity) {
387
+ return {
388
+ author: identity === void 0 ? "host" : "app",
389
+ coAuthor: identity?.coAuthor !== void 0 && identity.hooksPath !== void 0 ? "credited" : "none"
390
+ };
391
+ }
392
+
393
+ // src/runner/container/environment.ts
394
+ var RELAY_PORT = 3128;
395
+ var JOB_MOUNT = "/outerlayer/job";
396
+ var CHECKOUT_DIR = "/build/work/checkout";
397
+ var MAX_HOSTS = 200;
398
+ function tokenVariables() {
399
+ return ["GH_TOKEN", "GITHUB_TOKEN", "GH_ENTERPRISE_TOKEN", "GITHUB_ENTERPRISE_TOKEN"];
400
+ }
401
+ function githubVariables() {
402
+ return /* @__PURE__ */ new Set([...tokenVariables(), "GH_CONFIG_DIR", "GH_REPO", "GH_NO_UPDATE_NOTIFIER"]);
403
+ }
404
+ var PROXY_URL = `http://127.0.0.1:${RELAY_PORT}`;
405
+ var NO_LOCAL = "localhost,127.0.0.1";
406
+ function proxyEnvironment(proxyUrl, alsoNoProxy) {
407
+ const NO_PROXY = alsoNoProxy === void 0 || NO_LOCAL.split(",").includes(alsoNoProxy) ? NO_LOCAL : `${NO_LOCAL},${alsoNoProxy}`;
408
+ return {
409
+ HTTPS_PROXY: proxyUrl,
410
+ HTTP_PROXY: proxyUrl,
411
+ // curl and git read the lowercase forms.
412
+ https_proxy: proxyUrl,
413
+ http_proxy: proxyUrl,
414
+ NO_PROXY,
415
+ no_proxy: NO_PROXY,
416
+ NODE_USE_ENV_PROXY: "1",
417
+ // Yarn 2 and later ignore the variables above and read only their own.
418
+ // Without these, `yarn install` resolves registry names itself, which a
419
+ // build with no network of its own cannot do.
420
+ YARN_HTTPS_PROXY: proxyUrl,
421
+ YARN_HTTP_PROXY: proxyUrl
422
+ };
423
+ }
424
+ function runnerLiterals(repository) {
425
+ return {
426
+ OUTERLAYER_JOB_DIR: JOB_MOUNT,
427
+ HOME: "/build/home",
428
+ TMPDIR: "/tmp",
429
+ ...proxyEnvironment(PROXY_URL),
430
+ GIT_TERMINAL_PROMPT: "0",
431
+ GH_CONFIG_DIR: `${JOB_MOUNT}/gh`,
432
+ GH_NO_UPDATE_NOTIFIER: "1",
433
+ ...repository === void 0 ? {} : { GH_REPO: repository }
434
+ };
435
+ }
436
+ function containerBuiltInAddress(name) {
437
+ return `http://127.0.0.1:${name === GATEWAY_DESTINATION ? GATEWAY_PORT : CLAUDE_PORT}`;
438
+ }
439
+ function reservedNames() {
440
+ return /* @__PURE__ */ new Set([
441
+ ...Object.keys(runnerLiterals("owner/name")),
442
+ ...Object.keys(builtInEnvironment(containerBuiltInAddress, void 0)),
443
+ "PATH",
444
+ ...tokenVariables()
445
+ ]);
446
+ }
447
+ function isReservedVariable(name) {
448
+ return name.startsWith("OUTERLAYER_") || reservedNames().has(name);
449
+ }
450
+ function isRefusedInConfig(name) {
451
+ return isReservedVariable(name) && !githubVariables().has(name);
452
+ }
453
+ function buildEnvironment(input) {
454
+ const authored = authorEnvironment(input.identity?.author);
455
+ const recipe = {};
456
+ for (const [name, value] of Object.entries(input.containerEnv)) {
457
+ if (!isReservedVariable(name) && !(name in authored)) recipe[name] = value;
458
+ }
459
+ const builtIns = input.builtIns === true;
460
+ const passed = {
461
+ ...builtIns ? {} : { OUTERLAYER_URL: input.config.url },
462
+ ...builtIns || input.itemKey === void 0 ? {} : { OUTERLAYER_API_KEY: input.itemKey },
463
+ OUTERLAYER_APP_ID: input.config.appId,
464
+ OUTERLAYER_WORK: String(input.item),
465
+ ...input.branch === void 0 ? {} : { OUTERLAYER_BRANCH: input.branch }
466
+ };
467
+ const variables = [];
468
+ const skippedTokenVariables = [];
469
+ for (const name of input.config.build?.variables ?? []) {
470
+ if (tokenVariables().includes(name)) {
471
+ skippedTokenVariables.push(name);
472
+ continue;
473
+ }
474
+ const value = input.hostEnv[name];
475
+ if (value === void 0 || isReservedVariable(name) || name in authored) continue;
476
+ if (builtIns && CLAUDE_CREDENTIAL_VARIABLES.includes(name)) continue;
477
+ passed[name] = value;
478
+ variables.push(name);
479
+ }
480
+ const destinations = destinationEnvironment(input.config.build?.destinations, containerLocalAddress);
481
+ const builtInLiterals = builtIns ? builtInEnvironment(containerBuiltInAddress, claudeCredential(input.hostEnv)) : {};
482
+ const hooksPath = input.identity?.hooksPath;
483
+ const hooks = {};
484
+ if (hooksPath !== void 0) {
485
+ const extended = withHooksPath({ ...recipe, ...passed }, hooksPath);
486
+ const at = Number(extended.GIT_CONFIG_COUNT) - 1;
487
+ Object.assign(hooks, {
488
+ GIT_CONFIG_COUNT: extended.GIT_CONFIG_COUNT,
489
+ [`GIT_CONFIG_KEY_${at}`]: "core.hooksPath",
490
+ [`GIT_CONFIG_VALUE_${at}`]: hooksPath
491
+ });
492
+ delete passed.GIT_CONFIG_COUNT;
493
+ }
494
+ return {
495
+ literals: { ...recipe, ...destinations, ...runnerLiterals(input.repository), ...builtInLiterals, ...authored, ...hooks },
496
+ passed,
497
+ variables,
498
+ skippedTokenVariables
499
+ };
500
+ }
501
+ function parseIsolation(value) {
502
+ return value === "container" || value === "vm" ? value : "shared-user";
503
+ }
504
+ function capHosts(hosts, alsoReached = 0) {
505
+ const distinct = [...new Set(hosts)];
506
+ const hostsReached = distinct.slice(0, MAX_HOSTS);
507
+ return { hostsReached, hostsReachedMore: distinct.length - hostsReached.length + alsoReached };
508
+ }
509
+ function sharedUserEnvironment(cliVersion) {
510
+ return { isolation: "shared-user", cliVersion };
511
+ }
512
+ var MAX_TEXT = 255;
513
+ var MAX_IGNORED = 500;
514
+ function sendableIgnored(list) {
515
+ if (list === void 0) return void 0;
516
+ const fits = (text) => text.length >= 1 && text.length <= MAX_TEXT;
517
+ return list.filter((entry) => fits(entry.property) && fits(entry.source)).slice(0, MAX_IGNORED);
518
+ }
519
+ function withTunnelRecord(environment, record, onlyWhenUsed = false) {
520
+ const ignoredProperties = sendableIgnored(environment.ignoredProperties);
521
+ const base = ignoredProperties === void 0 ? environment : { ...environment, ignoredProperties };
522
+ if (record === void 0) return base;
523
+ const used = record.hosts.length + record.hostsMore + record.refusals.length + record.refusalsMore + (record.destinations?.length ?? 0) > 0;
524
+ if (onlyWhenUsed && !used) return base;
525
+ return {
526
+ ...base,
527
+ ...capHosts(record.hosts, record.hostsMore),
528
+ refusals: record.refusals.slice(0, MAX_HOSTS),
529
+ refusalsMore: Math.max(0, record.refusals.length - MAX_HOSTS) + record.refusalsMore,
530
+ ...record.destinations === void 0 || record.destinations.length === 0 ? {} : { destinations: record.destinations }
531
+ };
532
+ }
533
+
534
+ export { AGENT_CREDENTIAL_MISSING, BUILT_IN_NAMES, CHECKOUT_DIR, CLAUDE_CREDENTIAL_VARIABLES, CLAUDE_PORT, CLAUDE_SOCKET, DESTINATION_BASE_PORT, GATEWAY_PORT, GATEWAY_SOCKET, JOB_MOUNT, RELAY_PORT, authorEnvironment, buildEnvironment, builtInEnvironment, claudeCredential, commitIdentityRecord, containerLocalAddress, destinationEnvironment, headerVariables, isRefusedInConfig, isReservedVariable, parseClaimIdentity, parseIsolation, proxyEnvironment, resolveDestinations, sharedUserEnvironment, startBuildServices, withHooksPath, withTunnelRecord };
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'node:module';
3
+ import { RELAY_PORT, GATEWAY_SOCKET, GATEWAY_PORT, CLAUDE_SOCKET, CLAUDE_PORT, DESTINATION_BASE_PORT } from './chunk-AUFG23AA.js';
4
+ import { symlinkSync, unlinkSync } from 'fs';
5
+ import { join } from 'path';
6
+
7
+ createRequire(import.meta.url);
8
+ var EXEC_PORT = 1024;
9
+ var GUEST_CID = 3;
10
+ var MAX_SOCKET_PATH = 107;
11
+ function vsockLinks(input) {
12
+ return [
13
+ { port: RELAY_PORT, socket: "tunnel.sock" },
14
+ ...Array.from({ length: input.destinations }, (_unused, index) => ({ port: DESTINATION_BASE_PORT + index, socket: `dest-${index}.sock` })),
15
+ ...input.builtIns ? [
16
+ { port: GATEWAY_PORT, socket: GATEWAY_SOCKET },
17
+ { port: CLAUDE_PORT, socket: CLAUDE_SOCKET }
18
+ ] : []
19
+ ];
20
+ }
21
+ function linkVsockSockets(input) {
22
+ for (const { port, socket } of input.links) {
23
+ const link = `${input.vsockPath}_${port}`;
24
+ removeLink(link);
25
+ symlinkSync(join(input.buildDir, socket), link);
26
+ }
27
+ }
28
+ function removeLink(link) {
29
+ try {
30
+ unlinkSync(link);
31
+ } catch (err) {
32
+ if (err.code !== "ENOENT") throw err;
33
+ }
34
+ }
35
+ var LONGEST_SUFFIX = "/v.sock_65535";
36
+ function socketPathProblem(buildDir) {
37
+ const longest = Buffer.byteLength(`${buildDir}${LONGEST_SUFFIX}`);
38
+ if (longest <= MAX_SOCKET_PATH) return void 0;
39
+ return `the build directory ${buildDir} is too deep for the microVM's sockets: a socket path holds at most ${MAX_SOCKET_PATH} bytes and the longest here is ${longest}; move the runner's config file closer to the root`;
40
+ }
41
+
42
+ export { EXEC_PORT, GUEST_CID, linkVsockSockets, socketPathProblem, vsockLinks };