@outerlayer/cli 0.11.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +16 -7
  3. package/dist/{agent-setup-YEFN74Y4.js → agent-setup-QE6PSCEZ.js} +8 -8
  4. package/dist/build-info.json +1 -1
  5. package/dist/{builder-PP436VGZ.js → builder-UUS4ORDH.js} +3 -3
  6. package/dist/{check-YDXOLVJ5.js → check-63IDCEEV.js} +31 -30
  7. package/dist/{chunk-XR3BJN33.js → chunk-22RMZBGW.js} +20 -1
  8. package/dist/{chunk-TB7EP24C.js → chunk-2M63GG56.js} +1 -1
  9. package/dist/{chunk-Y6P5XX2B.js → chunk-3D4OYFCD.js} +4 -4
  10. package/dist/{chunk-2GBCTEN4.js → chunk-3KTA5UU2.js} +1 -1
  11. package/dist/{chunk-USO2DKBD.js → chunk-4KSQKQXE.js} +8 -2
  12. package/dist/{chunk-UES67HLJ.js → chunk-4QVZCVWT.js} +5 -5
  13. package/dist/{chunk-I263FZ3A.js → chunk-6S3H3J6H.js} +3 -3
  14. package/dist/{chunk-BHOBM3IZ.js → chunk-C6IV5BV7.js} +10 -10
  15. package/dist/{chunk-VDI5POCC.js → chunk-CNFVHDHG.js} +1 -1
  16. package/dist/{chunk-EOWWTFUU.js → chunk-DPKX26LT.js} +92 -16
  17. package/dist/{chunk-SUMWRA7K.js → chunk-DXQ5MHCO.js} +1 -1
  18. package/dist/{chunk-ZQ5JGHUO.js → chunk-EFZLAYPE.js} +2 -2
  19. package/dist/{chunk-RWQWPZB4.js → chunk-GI4XXD4I.js} +3 -3
  20. package/dist/{chunk-YU5GL7QS.js → chunk-HDTVTA6I.js} +2 -2
  21. package/dist/{chunk-BFESLKPP.js → chunk-IP7BIBXJ.js} +19 -1
  22. package/dist/{chunk-YT35WGIP.js → chunk-J5M7BSJQ.js} +99 -7
  23. package/dist/{chunk-IU4NZROR.js → chunk-JXYVA45C.js} +1 -1
  24. package/dist/{chunk-BMX3L7CR.js → chunk-LCEXHCHD.js} +1 -1
  25. package/dist/{chunk-7C4T5BGN.js → chunk-MJXXRX4M.js} +54 -12
  26. package/dist/{chunk-E5DC4VZL.js → chunk-MZN7AQOB.js} +8 -8
  27. package/dist/{chunk-CJGQQZWB.js → chunk-NX57IBNK.js} +1 -1
  28. package/dist/{chunk-DTDZXDQ3.js → chunk-P4MM66C6.js} +31 -18
  29. package/dist/{chunk-BEZSECPA.js → chunk-P6FX4D43.js} +1 -1
  30. package/dist/{chunk-A56PYPDF.js → chunk-R7OQTKWT.js} +2 -1
  31. package/dist/{chunk-TFIOXPZT.js → chunk-RLYZLUQ7.js} +67 -5
  32. package/dist/chunk-RMWLBUCJ.js +50 -0
  33. package/dist/{chunk-4HMXH2OA.js → chunk-SFFHGX3G.js} +1 -1
  34. package/dist/{chunk-3YFCWYBU.js → chunk-SVEP7PVH.js} +14 -14
  35. package/dist/chunk-T5VPP6H7.js +65 -0
  36. package/dist/{chunk-BGWTYRSS.js → chunk-TCIW27FM.js} +1 -1
  37. package/dist/{chunk-R7BJYFD2.js → chunk-TOPDNXHZ.js} +5 -5
  38. package/dist/{chunk-HKUH5AR2.js → chunk-XJKL4BNU.js} +3 -3
  39. package/dist/{cli-RQZXTOEI.js → cli-WSBIAVZU.js} +176 -88
  40. package/dist/{command-U6UGIBVT.js → command-N7BAB4XJ.js} +21 -22
  41. package/dist/{config-MKBX2ZQD.js → config-N3C6DFQA.js} +1 -1
  42. package/dist/{connect-cmd-5MIUNYAY.js → connect-cmd-FGYRLLXL.js} +5 -5
  43. package/dist/{context-adopt-A77G67CP.js → context-adopt-B3PWGFHL.js} +5 -5
  44. package/dist/{context-materialize-KQZYATQZ.js → context-materialize-7S6NLLRO.js} +5 -5
  45. package/dist/{dist-3M3ZMEZF.js → dist-LKDWQWZ7.js} +2 -2
  46. package/dist/{docker-WVRRMK6R.js → docker-BZ55BMBJ.js} +4 -4
  47. package/dist/{doctor-ZF4PYWVN.js → doctor-3H25Q7WT.js} +23 -20
  48. package/dist/{emit-artifact-cmd-VKPW577P.js → emit-artifact-cmd-IQCRXOH7.js} +9 -9
  49. package/dist/{emit-cmd-SU62J2PB.js → emit-cmd-KDSM5VAO.js} +4 -4
  50. package/dist/{emit-criteria-cmd-U2TL6FYY.js → emit-criteria-cmd-EYUXME26.js} +7 -7
  51. package/dist/emit-finding-cmd-P4RKBPBB.js +465 -0
  52. package/dist/{emit-result-cmd-GKBVPMU4.js → emit-result-cmd-6XYENU6H.js} +9 -9
  53. package/dist/{hook-fast-AQCAQIA6.js → hook-fast-PXWKQPN6.js} +1 -1
  54. package/dist/{import-capture-cmd-5NJVL2HN.js → import-capture-cmd-2BKM5I2N.js} +3 -3
  55. package/dist/{import-ruler-cmd-TUK3MRPC.js → import-ruler-cmd-IXR7YK47.js} +2 -2
  56. package/dist/index.js +3 -3
  57. package/dist/{init-PUY2BUHH.js → init-2LWVS4AH.js} +21 -17
  58. package/dist/{init-M5IBAGVE.js → init-ZL7ORPTF.js} +53 -32
  59. package/dist/{init-cmd-NPNR23UL.js → init-cmd-JXOBCOIB.js} +16 -14
  60. package/dist/{install-HKOVISUP.js → install-HDBIPHTX.js} +2 -4
  61. package/dist/{login-browser-7XGSV7MA.js → login-browser-QZCS3WW6.js} +2 -1
  62. package/dist/{loop-3FNROLW7.js → loop-XOAE6ZIY.js} +32 -30
  63. package/dist/{machine-RWDDJRWB.js → machine-G6PY2J2J.js} +16 -16
  64. package/dist/{mcp-install-cmd-XZARXNYT.js → mcp-install-cmd-VXVIH7PH.js} +1 -1
  65. package/dist/runner-env-XTC53CHN.js +15 -0
  66. package/dist/runner-key-TDTLNAKJ.js +154 -0
  67. package/dist/{session-sender-E3UHGSNV.js → session-sender-XVGZFWRK.js} +7 -7
  68. package/dist/{starter-pack-I35VNYAR.js → starter-pack-B7JPGEDO.js} +4 -4
  69. package/dist/{status-2OW5AKWL.js → status-MRTPG34B.js} +22 -22
  70. package/dist/{sync-cmd-ULKXCHO2.js → sync-cmd-UBUZMMDN.js} +6 -6
  71. package/dist/{watch-IU6NHB4K.js → watch-STNPEWD6.js} +6 -6
  72. package/dist/{work-claim-cmd-LXXSNEGF.js → work-claim-cmd-CWGYEIJE.js} +11 -11
  73. package/dist/{work-cmd-ZJY3NA2G.js → work-cmd-X7NMJDFL.js} +8 -8
  74. package/dist/{work-comment-cmd-34FTLPUO.js → work-comment-cmd-FI2TMAYQ.js} +8 -8
  75. package/dist/{work-open-pr-cmd-YZAENDP6.js → work-open-pr-cmd-OVHJ5I5H.js} +9 -9
  76. package/dist/{work-pr-cmd-EXTOGBJC.js → work-pr-cmd-Y5TDMQ4O.js} +8 -8
  77. package/package.json +1 -1
  78. package/skill-pack/maintained/reporting-findings/SKILL.md +84 -5
  79. package/skill-pack/template/build/SKILL.md +60 -33
  80. package/skill-pack/template/build/references/agent-briefs.md +4 -2
  81. package/skill-pack/template/build/references/release.md +1 -1
  82. package/skill-pack/template/build/references/review-loop.md +6 -2
  83. package/skill-pack/template/build/scripts/record-criteria.mjs +109 -143
  84. package/skill-pack/validators/code-review.yaml +4 -2
  85. package/dist/chunk-FBTE2A2X.js +0 -38
  86. package/dist/chunk-JKIWMRFH.js +0 -27
  87. package/dist/chunk-T6OIJQRT.js +0 -31
  88. package/dist/emit-finding-cmd-TMOOKHPD.js +0 -251
@@ -1,13 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { autoConnect } from './chunk-ZQ5JGHUO.js';
4
- import { detectActiveSession } from './chunk-BFESLKPP.js';
5
- import { sanitizeTerminal } from './chunk-VDI5POCC.js';
6
- import { resolveRepoIdentity } from './chunk-IU4NZROR.js';
3
+ import { autoConnect } from './chunk-EFZLAYPE.js';
4
+ import { detectActiveSession } from './chunk-IP7BIBXJ.js';
5
+ import { workPageUrl } from './chunk-4QVZCVWT.js';
6
+ import { sanitizeTerminal } from './chunk-CNFVHDHG.js';
7
+ import { resolveRepoIdentity } from './chunk-JXYVA45C.js';
8
+ import { C } from './chunk-YRYMNBGQ.js';
7
9
  import { parseTrackerNumber, trackerNumberError } from './chunk-WZBHBH3J.js';
8
10
  import { defaultFetch } from './chunk-YO4PSLVB.js';
9
11
  import { resolveGatewayCredentials, unauthorizedMessage } from './chunk-PLMTLGYH.js';
10
12
  import { signRunnerRequest } from './chunk-V7NIVOFD.js';
13
+ import { readRepoConnection } from './chunk-BJ3KTMDH.js';
11
14
  import { homeFromEnv } from './chunk-VNVZDWO3.js';
12
15
 
13
16
  createRequire(import.meta.url);
@@ -39,10 +42,6 @@ function isSignatureRefusal(code) {
39
42
  }
40
43
 
41
44
  // src/work-cmd.ts
42
- var GREEN = "\x1B[32m";
43
- var YELLOW = "\x1B[33m";
44
- var DIM = "\x1B[2m";
45
- var RESET = "\x1B[0m";
46
45
  async function signatureHeaders(method, endpoint, payload, signer) {
47
46
  if (signer === void 0) return {};
48
47
  return { ...await signRunnerRequest({ method, url: endpoint, body: new TextEncoder().encode(payload), signer }) };
@@ -298,7 +297,19 @@ function tokenWarning(data) {
298
297
  if (tokens === void 0 || tokens === null || tokens.earnable !== false) return "";
299
298
  const why = typeof tokens.message === "string" ? sanitizeTerminal(tokens.message) : "the gateway named no reason";
300
299
  return `
301
- ${YELLOW}!${RESET} a container host will fail this build: ${why}`;
300
+ ${C.yellow}!${C.reset} a container host will fail this build: ${why}`;
301
+ }
302
+ function itemLabel(data) {
303
+ return data.number != null ? `item #${data.number}` : `item ${sanitizeTerminal(String(data.workItemId))}`;
304
+ }
305
+ function issueLabel(subject) {
306
+ if ("issue" in subject) return `issue ${sanitizeTerminal(subject.repository)}#${subject.issue}`;
307
+ return `issue ${sanitizeTerminal(subject.key)}`;
308
+ }
309
+ function itemPageUrl(home, cwd, appId, workItemId) {
310
+ const connection = readRepoConnection(cwd);
311
+ if (connection === void 0 || connection.appId !== appId || typeof workItemId !== "string" || workItemId === "") return void 0;
312
+ return `${workPageUrl(home, connection.organization.name, connection.factory.name)}/${encodeURIComponent(workItemId)}`;
302
313
  }
303
314
  async function runWorkBuild(opts) {
304
315
  const cwd = opts.cwd ?? process.cwd();
@@ -326,7 +337,7 @@ async function runWorkBuild(opts) {
326
337
  const label = data2.number != null ? `#${data2.number}` : String(data2.workItemId);
327
338
  const launch = data2.number != null ? `OUTERLAYER_WORK=${data2.number} claude` : null;
328
339
  const branch = typeof data2.branch === "string" && data2.branch !== "" ? data2.branch : null;
329
- const output2 = opts.json ? JSON.stringify({ ...data2, ...launch !== null ? { launchCommand: launch } : {} }) : `${GREEN}\u2713${RESET} on Work for you to build ${DIM}\xB7${RESET} ${label} ${DIM}\xB7${RESET} no host will take it` + (branch !== null ? `
340
+ const output2 = opts.json ? JSON.stringify({ ...data2, ...launch !== null ? { launchCommand: launch } : {} }) : `${C.green}\u2713${C.reset} on Work for you to build ${C.dim}\xB7${C.reset} ${label} ${C.dim}\xB7${C.reset} no host will take it` + (branch !== null ? `
330
341
  branch: ${branch}` : "") + (launch !== null ? `
331
342
  start your session with: ${launch}` : "");
332
343
  if (!opts.quiet) process.stdout.write(output2 + "\n");
@@ -338,7 +349,9 @@ async function runWorkBuild(opts) {
338
349
  ...facts
339
350
  };
340
351
  const { data } = await postJsonWithRetry(url, "/v1/work-items/build", apiKey, appId, body, fetchImpl, retries, sleep);
341
- const output = opts.json ? JSON.stringify(data) : `${GREEN}\u2713${RESET} build requested ${DIM}\xB7${RESET} ${data.number != null ? `#${data.number}` : data.workItemId} ${DIM}\xB7${RESET} by ${data.attestor}` + tokenWarning(data);
352
+ const page = itemPageUrl(home, cwd, appId, data.workItemId);
353
+ const output = opts.json ? JSON.stringify(data) : `${C.green}\u2713${C.reset} build requested ${C.dim}\xB7${C.reset} ${itemLabel(data)}${"subject" in body ? ` (${issueLabel(body.subject)})` : ""} ${C.dim}\xB7${C.reset} by ${data.attestor}` + (page !== void 0 ? `
354
+ ${page}` : "") + tokenWarning(data);
342
355
  if (!opts.quiet) process.stdout.write(output + "\n");
343
356
  return { data, output };
344
357
  }
@@ -347,7 +360,7 @@ async function runWorkLinkSession(opts) {
347
360
  return await linkSession(opts);
348
361
  } catch (err) {
349
362
  if (opts.logFailures) {
350
- const { logHookError } = await import('./hook-fast-AQCAQIA6.js');
363
+ const { logHookError } = await import('./hook-fast-PXWKQPN6.js');
351
364
  const reason = err instanceof Error ? err.message : String(err);
352
365
  logHookError(opts.home ?? homeFromEnv(opts.env ?? process.env), new Error(`work link-session #${opts.number} failed: ${reason}`));
353
366
  }
@@ -387,7 +400,7 @@ async function linkSession(opts) {
387
400
  }
388
401
  const heldBy = typeof data.heldBy === "string" ? data.heldBy : void 0;
389
402
  const linked = data.linked === true;
390
- const output = opts.json ? JSON.stringify(data) : opts.ended ? linked ? `${GREEN}\u2713${RESET} ended ${DIM}\xB7${RESET} session ${sessionId} ${DIM}\xB7${RESET} #${number}` : `${DIM}\xB7${RESET} session ${sessionId} was not linked to #${number}` : heldBy ? `${DIM}\xB7${RESET} session ${sessionId} linked to #${number}, held by ${sanitizeTerminal(heldBy)} \u2014 no lease taken` : `${GREEN}\u2713${RESET} linked ${DIM}\xB7${RESET} session ${sessionId} ${DIM}\xB7${RESET} #${number}`;
403
+ const output = opts.json ? JSON.stringify(data) : opts.ended ? linked ? `${C.green}\u2713${C.reset} ended ${C.dim}\xB7${C.reset} session ${sessionId} ${C.dim}\xB7${C.reset} #${number}` : `${C.dim}\xB7${C.reset} session ${sessionId} was not linked to #${number}` : heldBy ? `${C.dim}\xB7${C.reset} session ${sessionId} linked to #${number}, held by ${sanitizeTerminal(heldBy)} \u2014 no lease taken` : `${C.green}\u2713${C.reset} linked ${C.dim}\xB7${C.reset} session ${sessionId} ${C.dim}\xB7${C.reset} #${number}`;
391
404
  if (!opts.quiet) process.stdout.write(output + "\n");
392
405
  return { data, output };
393
406
  }
@@ -411,7 +424,7 @@ async function runWorkRemove(opts) {
411
424
  { reason: opts.reason, ...environmentFacts({ cwd, env, home }) },
412
425
  fetchImpl
413
426
  );
414
- const output2 = opts.json ? JSON.stringify(data2) : `${GREEN}\u2713${RESET} removed ${DIM}\xB7${RESET} ${data2.workItemId} ${DIM}\xB7${RESET} withdrawn ${data2.itemWithdrawn}`;
427
+ const output2 = opts.json ? JSON.stringify(data2) : `${C.green}\u2713${C.reset} removed ${C.dim}\xB7${C.reset} ${data2.workItemId} ${C.dim}\xB7${C.reset} withdrawn ${data2.itemWithdrawn}`;
415
428
  if (!opts.quiet) process.stdout.write(output2 + "\n");
416
429
  return { data: data2, output: output2 };
417
430
  }
@@ -421,7 +434,7 @@ async function runWorkRemove(opts) {
421
434
  const item = await findLiveItem(creds, repository, subject, fetchImpl);
422
435
  if (!item) {
423
436
  const data2 = { workItemId: null, removed: true, itemWithdrawn: false, noLiveAddition: true };
424
- const output2 = opts.json ? JSON.stringify(data2) : `${GREEN}\u2713${RESET} nothing to remove ${DIM}\xB7${RESET} no live addition on ${repository}#${number}`;
437
+ const output2 = opts.json ? JSON.stringify(data2) : `${C.green}\u2713${C.reset} nothing to remove ${C.dim}\xB7${C.reset} no live addition on ${repository}#${number}`;
425
438
  if (!opts.quiet) process.stdout.write(output2 + "\n");
426
439
  return { data: data2, output: output2 };
427
440
  }
@@ -433,7 +446,7 @@ async function runWorkRemove(opts) {
433
446
  { reason: opts.reason, ...environmentFacts({ cwd, env, home }) },
434
447
  fetchImpl
435
448
  );
436
- const output = opts.json ? JSON.stringify(data) : `${GREEN}\u2713${RESET} removed ${DIM}\xB7${RESET} ${data.workItemId} ${DIM}\xB7${RESET} withdrawn ${data.itemWithdrawn}`;
449
+ const output = opts.json ? JSON.stringify(data) : `${C.green}\u2713${C.reset} removed ${C.dim}\xB7${C.reset} ${data.workItemId} ${C.dim}\xB7${C.reset} withdrawn ${data.itemWithdrawn}`;
437
450
  if (!opts.quiet) process.stdout.write(output + "\n");
438
451
  return { data, output };
439
452
  }
@@ -496,10 +509,10 @@ async function runWorkList(opts) {
496
509
  const claimSuffix = (claim) => {
497
510
  if (!claim) return "";
498
511
  const holder = claim.holderKind === "session" ? `${claim.memberName || "a member"} (local session` : `${sanitizeTerminal(claim.host)} (${claim.kind}`;
499
- return ` ${DIM}\xB7${RESET} claimed by ${holder}, since ${claim.createdAt}, until ${claim.expiresAt})`;
512
+ return ` ${C.dim}\xB7${C.reset} claimed by ${holder}, since ${claim.createdAt}, until ${claim.expiresAt})`;
500
513
  };
501
514
  const output = opts.json ? JSON.stringify(data) : items.length === 0 ? "no live work items" : items.map(
502
- (i) => `${i.trackerKey} ${DIM}\xB7${RESET} ${i.stage} ${DIM}\xB7${RESET} ${i.section} ${DIM}\xB7${RESET} ${i.title}${claimSuffix(i.claim)}`
515
+ (i) => `${i.trackerKey} ${C.dim}\xB7${C.reset} ${i.stage} ${C.dim}\xB7${C.reset} ${i.section} ${C.dim}\xB7${C.reset} ${i.title}${claimSuffix(i.claim)}`
503
516
  ).join("\n");
504
517
  if (!opts.quiet) process.stdout.write(output + "\n");
505
518
  return { data, output };
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { MIN_ENGINE_MAJOR } from './chunk-E5DC4VZL.js';
3
+ import { MIN_ENGINE_MAJOR } from './chunk-MZN7AQOB.js';
4
4
  import { firecrackerArch, MICROVM_ARCHES, guestAssetsPinned } from './chunk-CX22S5BA.js';
5
5
  import { spawnSync } from 'child_process';
6
6
  import { existsSync, accessSync, constants, statSync, readFileSync } from 'fs';
@@ -62,8 +62,9 @@ function addMcpEntryIfAbsent(repoRoot, name, entry) {
62
62
  `);
63
63
  return { path: relPath, added: true };
64
64
  }
65
+ var INIT_STDIO_ENTRY = { type: "stdio", command: "npx", args: ["-y", "@outerlayer/cli", "mcp", "serve"] };
65
66
  function addStdioMcpEntryIfAbsent(repoRoot, name = "outerlayer") {
66
- return addMcpEntryIfAbsent(repoRoot, name, buildStdioServerEntry(DEFAULT_MCP_COMMAND, void 0, void 0));
67
+ return addMcpEntryIfAbsent(repoRoot, name, { ...INIT_STDIO_ENTRY, args: [...INIT_STDIO_ENTRY.args] });
67
68
  }
68
69
  var DOCS_MCP_SERVER_NAME = "outerlayer-docs";
69
70
  var DOCS_MCP_URL = "https://docs.outerlayer.ai/mcp";
@@ -1,13 +1,64 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { runnerUnit, UNIT_MARKER } from './chunk-FBTE2A2X.js';
4
- import { checkFit } from './chunk-JKIWMRFH.js';
5
- import { builtinKind } from './chunk-XR3BJN33.js';
3
+ import { builtinKind } from './chunk-22RMZBGW.js';
4
+ import { parseSize } from './chunk-7RQ73DLO.js';
6
5
  import { runnerDir } from './chunk-ZM2IMMYN.js';
7
6
  import { dirname } from 'path';
8
7
  import { spawnSync } from 'child_process';
9
8
 
10
9
  createRequire(import.meta.url);
10
+
11
+ // src/runner/vm/service.ts
12
+ var VM_SERVICE_NAME = "outerlayer-runner";
13
+ var UNIT_MARKER = "# Written by outerlayer runner install --service; edit the runner config, not this file.";
14
+ var STOP_MARGIN_MINUTES = 10;
15
+ function escapeUnitPath(path) {
16
+ return path.replace(/%/g, "%%").replace(/ /g, "\\x20");
17
+ }
18
+ function runnerUnit(unit) {
19
+ return `${UNIT_MARKER}
20
+ [Unit]
21
+ Description=OuterLayer runner
22
+ After=network-online.target${unit.docker ? " docker.service" : ""}
23
+ Wants=network-online.target
24
+ ${unit.docker ? "Requires=docker.service\n" : ""}
25
+ [Service]
26
+ User=${unit.user}
27
+ ExecStart=${unit.execStart}
28
+ EnvironmentFile=-${escapeUnitPath(`${unit.configDir}/runner.env`)}
29
+ Restart=on-failure
30
+ RestartForceExitStatus=75
31
+ RestartSec=30
32
+ KillSignal=SIGTERM
33
+ KillMode=mixed
34
+ TimeoutStopSec=${unit.timeLimitMinutes + STOP_MARGIN_MINUTES}min
35
+ OOMPolicy=continue
36
+ Delegate=yes
37
+
38
+ [Install]
39
+ WantedBy=multi-user.target
40
+ `;
41
+ }
42
+
43
+ // src/runner/service/fit.ts
44
+ var GIB = 1024 ** 3;
45
+ var RUNNER_RESERVE_BYTES = 2 * GIB;
46
+ function gib(bytes) {
47
+ return `${Number((bytes / GIB).toFixed(1))} GiB`;
48
+ }
49
+ function checkFit(input) {
50
+ const perBuild = parseSize(input.buildMemory);
51
+ if (perBuild === void 0) return { fits: true };
52
+ const neededBytes = input.concurrency * perBuild;
53
+ const availableBytes = Math.max(0, input.usableBytes - RUNNER_RESERVE_BYTES);
54
+ if (neededBytes <= availableBytes) return { fits: true };
55
+ return {
56
+ fits: false,
57
+ neededBytes,
58
+ availableBytes,
59
+ message: `runner.concurrency ${input.concurrency} times runner.build.memory ${input.buildMemory} needs ${gib(neededBytes)}, and ${gib(availableBytes)} is available (${gib(input.usableBytes)} of memory less ${gib(RUNNER_RESERVE_BYTES)} for the runner and the system)`
60
+ };
61
+ }
11
62
  function withSystemd(conf) {
12
63
  const lines = (conf ?? "").split("\n");
13
64
  if (lines.at(-1) === "") lines.pop();
@@ -130,7 +181,18 @@ function installService(request, deps) {
130
181
  if (deps.platform === "wsl2") lines.push(...ensureLoginTask(deps, unitName));
131
182
  };
132
183
  if (existing === unit) {
133
- if (!running() && !asRoot(deps, lines, say, ["enable", "--now", unitName])) return outcome(false, "refused", lines);
184
+ const wasRunning = running();
185
+ if (!wasRunning && !asRoot(deps, lines, say, ["enable", "--now", unitName])) return outcome(false, "refused", lines);
186
+ if (wasRunning && request.restart === true) {
187
+ ensureTask();
188
+ if (deps.buildsRunning(runnerDir(request.configPath))) {
189
+ lines.push(`the service ${unitName} is current; a build is running, so it was not restarted and reads runner.env at its next restart`);
190
+ return outcome(true, "pending-restart", lines);
191
+ }
192
+ if (!asRoot(deps, lines, say, ["restart", unitName])) return outcome(false, "refused", lines);
193
+ lines.push(`restarted the service ${unitName}, so it reads runner.env`);
194
+ return outcome(true, "updated", lines);
195
+ }
134
196
  lines.push(`the service ${unitName} is current: the unit already matches the config, so nothing was changed`);
135
197
  ensureTask();
136
198
  return outcome(true, "current", lines);
@@ -212,4 +274,4 @@ function uninstallService(request, deps) {
212
274
  return outcome(true, "removed", lines);
213
275
  }
214
276
 
215
- export { DEFAULT_UNIT_NAME, installService, realTaskScheduler, uninstallService, unitPathFor };
277
+ export { DEFAULT_UNIT_NAME, RUNNER_RESERVE_BYTES, VM_SERVICE_NAME, checkFit, installService, realTaskScheduler, uninstallService, unitPathFor };
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'node:module';
3
+ import { RUNNER_DIR_MODE, RUNNER_FILE_MODE } from './chunk-ZM2IMMYN.js';
4
+ import { existsSync, readFileSync, mkdirSync, chmodSync, writeFileSync } from 'fs';
5
+ import { dirname, join } from 'path';
6
+
7
+ createRequire(import.meta.url);
8
+ var ITEM_KEY_FILE = "item-key";
9
+ function itemKeyPath(jobPath) {
10
+ return join(dirname(jobPath), ITEM_KEY_FILE);
11
+ }
12
+ function withItemKey(jobPath, job) {
13
+ let key;
14
+ try {
15
+ key = readFileSync(itemKeyPath(jobPath), "utf8").trim();
16
+ } catch {
17
+ return job;
18
+ }
19
+ return key === "" ? job : { ...job, itemKey: key };
20
+ }
21
+ function writeOwnerOnly(path, content) {
22
+ if (existsSync(path)) chmodSync(path, RUNNER_FILE_MODE);
23
+ writeFileSync(path, content, { mode: RUNNER_FILE_MODE });
24
+ }
25
+ function readJobFile(path) {
26
+ if (!existsSync(path)) return null;
27
+ try {
28
+ return withItemKey(path, JSON.parse(readFileSync(path, "utf8")));
29
+ } catch {
30
+ return null;
31
+ }
32
+ }
33
+ function readJobFileChecked(path) {
34
+ if (!existsSync(path)) return { kind: "missing" };
35
+ try {
36
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
37
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return { kind: "unreadable" };
38
+ return { kind: "ok", job: withItemKey(path, parsed) };
39
+ } catch {
40
+ return { kind: "unreadable" };
41
+ }
42
+ }
43
+ function writeJobFile(path, job) {
44
+ mkdirSync(dirname(path), { recursive: true, mode: RUNNER_DIR_MODE });
45
+ const { itemKey, ...rest } = job;
46
+ if (itemKey !== void 0) writeOwnerOnly(itemKeyPath(path), itemKey);
47
+ writeOwnerOnly(path, JSON.stringify(rest, null, 2));
48
+ }
49
+
50
+ export { readJobFile, readJobFileChecked, writeJobFile };
@@ -634,7 +634,7 @@ async function runHookFast(event, home = homeFromEnv(), read = readStdin, env =
634
634
  recordHookErrorsSeen(home, hookErrorsBytes);
635
635
  if (contextPossiblyGoverned(sessionCwd, home)) {
636
636
  try {
637
- const { materializeContext: materialize } = await import('./context-materialize-KQZYATQZ.js');
637
+ const { materializeContext: materialize } = await import('./context-materialize-7S6NLLRO.js');
638
638
  const sessionId = typeof payload.session_id === "string" ? payload.session_id : "unknown";
639
639
  const result = await materialize({ cwd: sessionCwd, sessionId, home });
640
640
  materializeMessage = result.message;
@@ -1,24 +1,24 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
+ import { LEASE_SECONDS, releaseLease, isUnfinished, setStage, jobEnv, runHook, describeHookEnd, provisionMicroVm, deliverCommitHooks, provisionProcess, provisionEnvironment, readProvisionOut, resolveHostBuild, workdirProblem, listenOnBroker, hostEnvironment, hostCommandEnvironment, execArgv, microVmCommandLine, runCommand, execCommandLine, startSender, startHostServices, finishJob, isGroupAlive, outcomeFromExitCode, isTimedOut, readCpuUsec, isIdle, stopOrphanedMicroVms, unfinishedJobs, listAdoptableJobs, retryFailedReleases, recoverLostJobs, hookLimitsFor, needsRenew, renewLease, RENEW_INTERVAL_SECONDS, ALREADY_RELEASED } from './chunk-C6IV5BV7.js';
4
+ import { writePendingUpdate } from './chunk-O765JR4W.js';
3
5
  import { readHousekeepingRecord, writeHousekeepingRecord } from './chunk-UGP76IZ5.js';
4
6
  import { decideInstalledSwitch } from './chunk-3JVYMD4G.js';
5
- import { compareVersions, satisfiesRange } from './chunk-I2UOAV7X.js';
6
7
  import { fetchVersionMeta, npmVersion, npmSupportsProvenance, fetchTarballDigest, fetchProvenance, installVersion, auditSignatures } from './chunk-YETTWTAY.js';
7
- import { LEASE_SECONDS, releaseLease, isUnfinished, setStage, jobEnv, runHook, describeHookEnd, provisionMicroVm, deliverCommitHooks, provisionProcess, provisionEnvironment, readProvisionOut, resolveHostBuild, workdirProblem, listenOnBroker, hostEnvironment, hostCommandEnvironment, execArgv, microVmCommandLine, runCommand, execCommandLine, startSender, startHostServices, finishJob, isGroupAlive, outcomeFromExitCode, isTimedOut, readCpuUsec, isIdle, stopOrphanedMicroVms, unfinishedJobs, listAdoptableJobs, retryFailedReleases, recoverLostJobs, hookLimitsFor, needsRenew, renewLease, RENEW_INTERVAL_SECONDS, ALREADY_RELEASED } from './chunk-BHOBM3IZ.js';
8
- import { writeJobFile, readJobFile, readJobFileChecked } from './chunk-T6OIJQRT.js';
8
+ import { writeJobFile, readJobFile, readJobFileChecked } from './chunk-RMWLBUCJ.js';
9
+ import { compareVersions, satisfiesRange } from './chunk-I2UOAV7X.js';
9
10
  import { RUNNER_PROTOCOL } from './chunk-FI5UYJMQ.js';
10
- import { writePendingUpdate } from './chunk-O765JR4W.js';
11
- import { placeholderQueues, placeholderProblem } from './chunk-R7BJYFD2.js';
12
- import { writeMarker, readMarker, currentVersion, removeMarker, entryIn, versionDir, startedThroughCurrent, switchCurrent, cleanVersions, isInLayout, stagingDir } from './chunk-TI7B7UEO.js';
11
+ import { placeholderQueues, placeholderProblem } from './chunk-TOPDNXHZ.js';
13
12
  import { freeBytes, formatBytes } from './chunk-YYYRBYWG.js';
13
+ import { writeMarker, readMarker, currentVersion, removeMarker, entryIn, versionDir, startedThroughCurrent, switchCurrent, cleanVersions, isInLayout, stagingDir } from './chunk-TI7B7UEO.js';
14
14
  import { RepoFilter } from './chunk-TBT347UY.js';
15
- import { describeCallFailure, retrySend, shortClaim, relativeToRunner, formatDuration, provisionContainer, lifecycleSteps, lifecycleFailureReason, prepareBranch, containerCommandLine, BuildTokens, classifyRefusal, refusalCode, measureDisk, createRunnerLog, setUpRunnerCgroup, parseCpuList, ensureTunnel, keepGhConfigCurrent, createContainerRuntime, formatClock } from './chunk-E5DC4VZL.js';
16
- import { checkFit } from './chunk-JKIWMRFH.js';
15
+ import { describeCallFailure, retrySend, shortClaim, relativeToRunner, formatDuration, provisionContainer, lifecycleSteps, lifecycleFailureReason, prepareBranch, containerCommandLine, BuildTokens, classifyRefusal, refusalCode, measureDisk, createRunnerLog, setUpRunnerCgroup, parseCpuList, ensureTunnel, keepGhConfigCurrent, createContainerRuntime, formatClock } from './chunk-MZN7AQOB.js';
16
+ import { checkFit } from './chunk-RLYZLUQ7.js';
17
17
  import { startTimeOf, claimPidFile, readPidFile, isSameProcess, releasePidFile } from './chunk-N5FOE5PS.js';
18
- import { spawnRun } from './chunk-2GBCTEN4.js';
19
- import { builderName, runnerLabelValue, ensureFailureCount, builderSpec, ensureBuilder, BUILD_LABEL, RUNNER_LABEL } from './chunk-CJGQQZWB.js';
20
- import { isSignatureRefusal, WorkCommandError, getJson, postJson } from './chunk-DTDZXDQ3.js';
21
- import { isBuiltinContainer, isBuiltinVm, isBuiltinProcess, loadRunnerConfig, checkHooks, builtinKind, DEFAULT_UPDATE_MAX_WAIT_HOURS } from './chunk-XR3BJN33.js';
18
+ import { spawnRun } from './chunk-3KTA5UU2.js';
19
+ import { builderName, runnerLabelValue, ensureFailureCount, builderSpec, ensureBuilder, BUILD_LABEL, RUNNER_LABEL } from './chunk-NX57IBNK.js';
20
+ import { isSignatureRefusal, WorkCommandError, getJson, postJson } from './chunk-P4MM66C6.js';
21
+ import { isBuiltinContainer, isBuiltinVm, isBuiltinProcess, loadRunnerConfig, checkHooks, builtinKind, DEFAULT_UPDATE_MAX_WAIT_HOURS } from './chunk-22RMZBGW.js';
22
22
  import { exceedsDisk } from './chunk-7RQ73DLO.js';
23
23
  import { parseClaimedRepositories, repositoriesOf, primaryOf, sharedUserEnvironment, JOB_MOUNT, claudeCredential } from './chunk-4Q7OMKAT.js';
24
24
  import { parseClaimIdentity } from './chunk-BYT5O5RV.js';
@@ -1315,7 +1315,7 @@ var CLAIM_REFUSAL_CODES = new Set(
1315
1315
  "pull_request_from_fork pull_request_on_default_branch pull_request_ambiguous pull_request_repository_not_connected branch_claimed_elsewhere".split(" ")
1316
1316
  );
1317
1317
  async function tryClaimAndStart(state, item, queue) {
1318
- const { postJson: postJson2 } = await import('./work-cmd-ZJY3NA2G.js');
1318
+ const { postJson: postJson2 } = await import('./work-cmd-X7NMJDFL.js');
1319
1319
  const log = logOf(state);
1320
1320
  const hostKey = await loadHostKey(state.config.configPath);
1321
1321
  const claim = await retrySend(
@@ -2072,7 +2072,7 @@ async function start(configPath, deps = { fetchImpl: fetch }) {
2072
2072
  const outcome = await onStartUnderMarker({
2073
2073
  configPath,
2074
2074
  own: ownVersionOf(deps),
2075
- check: update.check ? () => update.check(configPath) : async () => (await import('./check-YDXOLVJ5.js')).runRunnerCheck(configPath, { fetchImpl: deps.fetchImpl }),
2075
+ check: update.check ? () => update.check(configPath) : async () => (await import('./check-63IDCEEV.js')).runRunnerCheck(configPath, { fetchImpl: deps.fetchImpl }),
2076
2076
  now: () => (deps.now ?? (() => /* @__PURE__ */ new Date()))().getTime(),
2077
2077
  log
2078
2078
  });
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'node:module';
3
+ import { DEFAULT_UNIT_NAME } from './chunk-RLYZLUQ7.js';
4
+ import { parseRunnerEnv } from './chunk-6MZSX4M7.js';
5
+ import { RUNNER_FILE_MODE } from './chunk-ZM2IMMYN.js';
6
+ import { existsSync, readFileSync, writeFileSync, renameSync, rmSync } from 'fs';
7
+ import { join, dirname } from 'path';
8
+
9
+ createRequire(import.meta.url);
10
+ var AGENT_COMMANDS = {
11
+ claude: {
12
+ implement: 'claude -p "/build" --permission-mode bypassPermissions',
13
+ amend: 'claude -p "/amend" --permission-mode bypassPermissions'
14
+ }
15
+ };
16
+ function isAgentName(name) {
17
+ return Object.hasOwn(AGENT_COMMANDS, name);
18
+ }
19
+ var CLAUDE_CREDENTIAL_NAMES = ["CLAUDE_CODE_OAUTH_TOKEN", "ANTHROPIC_API_KEY"];
20
+ var RunnerEnvError = class extends Error {
21
+ };
22
+ var CREDENTIAL_SHAPE = /^sk-ant-(oat|api)[0-9]*-[A-Za-z0-9_-]+$/;
23
+ var SETS = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)=/;
24
+ function runnerEnvPath(configPath) {
25
+ return join(dirname(configPath), "runner.env");
26
+ }
27
+ function credentialFromStdin(text) {
28
+ const value = (text ?? "").trim();
29
+ if (value === "") return void 0;
30
+ const kind = CREDENTIAL_SHAPE.exec(value)?.[1];
31
+ if (kind === void 0) {
32
+ throw new RunnerEnvError(
33
+ "the value on stdin is not a Claude credential, so nothing was written. Pipe one value: a token from `claude setup-token`, which starts sk-ant-oat, or an Anthropic API key, which starts sk-ant-api."
34
+ );
35
+ }
36
+ return { variable: kind === "oat" ? "CLAUDE_CODE_OAUTH_TOKEN" : "ANTHROPIC_API_KEY", value };
37
+ }
38
+ function hasStoredCredential(configPath) {
39
+ const path = runnerEnvPath(configPath);
40
+ if (!existsSync(path)) return false;
41
+ const env = parseRunnerEnv(readFileSync(path, "utf8"));
42
+ return CLAUDE_CREDENTIAL_NAMES.some((name) => env[name]);
43
+ }
44
+ function storeClaudeCredential(configPath, credential) {
45
+ const path = runnerEnvPath(configPath);
46
+ const kept = existsSync(path) ? readFileSync(path, "utf8").split("\n").filter((line) => !CLAUDE_CREDENTIAL_NAMES.includes(SETS.exec(line)?.[1] ?? "")) : [];
47
+ while (kept[kept.length - 1] === "") kept.pop();
48
+ const text = `${[...kept, `${credential.variable}=${credential.value}`].join("\n")}
49
+ `;
50
+ const temporary = `${path}.${process.pid}.tmp`;
51
+ try {
52
+ writeFileSync(temporary, text, { mode: RUNNER_FILE_MODE, flag: "wx" });
53
+ renameSync(temporary, path);
54
+ } catch (err) {
55
+ rmSync(temporary, { force: true });
56
+ throw err;
57
+ }
58
+ return path;
59
+ }
60
+ function restartForCredential(state, unitName = DEFAULT_UNIT_NAME) {
61
+ if (state !== "pending-restart" && state !== "differs") return void 0;
62
+ return `The service ${unitName} reads runner.env only when it starts. Restart it so it uses the stored credential: sudo systemctl restart ${unitName}`;
63
+ }
64
+
65
+ export { AGENT_COMMANDS, RunnerEnvError, credentialFromStdin, hasStoredCredential, isAgentName, restartForCredential, storeClaudeCredential };
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { splitRawFile } from './chunk-TB7EP24C.js';
3
+ import { splitRawFile } from './chunk-2M63GG56.js';
4
4
  import { resolvePackageVersion } from './chunk-RA3O54FC.js';
5
5
  import { existsSync, readFileSync, mkdirSync, writeFileSync, readdirSync } from 'fs';
6
6
  import { join, dirname } from 'path';
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { isPlaceholderCommand } from './chunk-7C4T5BGN.js';
4
- import { describeCallFailure } from './chunk-E5DC4VZL.js';
5
- import { WorkCommandError, getJson } from './chunk-DTDZXDQ3.js';
6
- import { sanitizeTerminal } from './chunk-VDI5POCC.js';
7
- import { builtinKind } from './chunk-XR3BJN33.js';
3
+ import { isPlaceholderCommand } from './chunk-MJXXRX4M.js';
4
+ import { describeCallFailure } from './chunk-MZN7AQOB.js';
5
+ import { WorkCommandError, getJson } from './chunk-P4MM66C6.js';
6
+ import { sanitizeTerminal } from './chunk-CNFVHDHG.js';
7
+ import { builtinKind } from './chunk-22RMZBGW.js';
8
8
  import { CLAUDE_CREDENTIAL_VARIABLES, claudeCredential } from './chunk-4Q7OMKAT.js';
9
9
  import { signerFor } from './chunk-V7NIVOFD.js';
10
10
 
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { LiveUploader } from './chunk-Y6P5XX2B.js';
4
- import { CaptureDaemon } from './chunk-IU4NZROR.js';
3
+ import { LiveUploader } from './chunk-3D4OYFCD.js';
4
+ import { CaptureDaemon } from './chunk-JXYVA45C.js';
5
5
  import { parseWorkItemEnv } from './chunk-GQD22KKN.js';
6
- import { spoolDir } from './chunk-4HMXH2OA.js';
6
+ import { spoolDir } from './chunk-SFFHGX3G.js';
7
7
  import { homeFromEnv } from './chunk-VNVZDWO3.js';
8
8
  import { mkdirSync, writeFileSync, rmSync } from 'fs';
9
9
  import { join } from 'path';