@outerlayer/cli 0.8.0 → 0.8.2

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 (91) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +27 -9
  3. package/dist/{agent-setup-C7PLRXZO.js → agent-setup-C5X4356W.js} +10 -10
  4. package/dist/{build-MS34XEGN.js → build-3FSJR5OO.js} +1 -1
  5. package/dist/build-info.json +1 -1
  6. package/dist/{builder-G52A6GW2.js → builder-QZA2Z3M3.js} +3 -3
  7. package/dist/{check-FOF3PUKI.js → check-HQHFAOXW.js} +30 -29
  8. package/dist/{chunk-R7STSBLC.js → chunk-2E2MG3KW.js} +1 -1
  9. package/dist/{chunk-OD2VTIHN.js → chunk-2FWL7CE2.js} +3 -2
  10. package/dist/{chunk-K35RW5HP.js → chunk-33GOCSRO.js} +4 -4
  11. package/dist/{chunk-4A5E2G4E.js → chunk-3J476JZC.js} +11 -11
  12. package/dist/{chunk-MQ3IPHIZ.js → chunk-47RS42OZ.js} +6 -3
  13. package/dist/{chunk-3S4P55NE.js → chunk-4ZZR5SKG.js} +3 -3
  14. package/dist/{chunk-PWBILI5P.js → chunk-7KI224YS.js} +1 -1
  15. package/dist/{chunk-5TNS3PL7.js → chunk-CLURXEWV.js} +4 -3
  16. package/dist/{chunk-7ZR5UPMB.js → chunk-CMFCCIQN.js} +167 -1
  17. package/dist/{chunk-AX23QKHS.js → chunk-CRLAB4PM.js} +1 -1
  18. package/dist/{chunk-CM5QTKM4.js → chunk-EQG7L6LM.js} +311 -29
  19. package/dist/{chunk-OEK4JEB3.js → chunk-ERI5GBNI.js} +18 -3
  20. package/dist/{chunk-ILEA3YHM.js → chunk-EWBM2USM.js} +2 -2
  21. package/dist/{chunk-EV3M3EH7.js → chunk-G54QHBTI.js} +2 -2
  22. package/dist/{chunk-M3SQ4YGD.js → chunk-GXSMDL4M.js} +1 -1
  23. package/dist/{chunk-GAYWY4GI.js → chunk-HN2JBGSI.js} +2 -1
  24. package/dist/{chunk-ZOAEPWGL.js → chunk-HWMYGAKF.js} +1 -1
  25. package/dist/chunk-I2UOAV7X.js +133 -0
  26. package/dist/{chunk-ABIWDUMS.js → chunk-IS4DIGBQ.js} +13 -1
  27. package/dist/{chunk-WVGSB3Z2.js → chunk-J5DPVNL3.js} +2 -2
  28. package/dist/{chunk-VXLJORHL.js → chunk-JXH6LEEH.js} +1 -1
  29. package/dist/{chunk-JPR2WHHF.js → chunk-KI7VPNKC.js} +1 -1
  30. package/dist/{chunk-TUUMW4TC.js → chunk-LSPJHYHM.js} +1 -1
  31. package/dist/{chunk-7VNURP6M.js → chunk-M2F2RAMK.js} +82 -26
  32. package/dist/{chunk-NALVDFIS.js → chunk-MJLUWGUD.js} +2 -2
  33. package/dist/{chunk-5FWNEQNZ.js → chunk-NACLOOXP.js} +5 -5
  34. package/dist/{chunk-U3GOI2B4.js → chunk-NPBLDMSD.js} +1 -1
  35. package/dist/{chunk-BOLTI6LR.js → chunk-PLMTLGYH.js} +1 -1
  36. package/dist/{chunk-IRCBTGWF.js → chunk-QJPO42VI.js} +6 -6
  37. package/dist/{chunk-5QLNZGAQ.js → chunk-R5SPFO6F.js} +6 -6
  38. package/dist/{chunk-M5POMKKW.js → chunk-V7NIVOFD.js} +5 -5
  39. package/dist/{chunk-IJBVPLZZ.js → chunk-VUGDEUMM.js} +71 -143
  40. package/dist/{chunk-4ACZCY4I.js → chunk-WMPO3SKK.js} +3 -3
  41. package/dist/{chunk-TTNFGMHO.js → chunk-Y7UQHKFY.js} +19 -1
  42. package/dist/{chunk-BCLJSQCV.js → chunk-YO4PSLVB.js} +1 -1
  43. package/dist/{chunk-AIL2R4SI.js → chunk-YU4Z2O3W.js} +5 -5
  44. package/dist/{chunk-ZZXLM64C.js → chunk-ZJ3WRVRG.js} +1 -1
  45. package/dist/{cli-AOJPAVFX.js → cli-OWM54BIU.js} +120 -93
  46. package/dist/{command-SY3SVLAI.js → command-LIHTWJHQ.js} +31 -25
  47. package/dist/{config-Z3USDBLU.js → config-OOBPCY7Q.js} +2 -2
  48. package/dist/{connect-cmd-PNZ6IJLJ.js → connect-cmd-NV7WVBY7.js} +3 -3
  49. package/dist/{context-adopt-CE4KDXS3.js → context-adopt-PPCNSN5D.js} +7 -7
  50. package/dist/{context-materialize-QL33HDJN.js → context-materialize-YR3JQCUF.js} +7 -7
  51. package/dist/{dist-3ZTZF2TP.js → dist-2LNAXGPZ.js} +1 -1
  52. package/dist/{docker-676FD3CQ.js → docker-RSALMGPL.js} +4 -4
  53. package/dist/{doctor-WR6TRPSU.js → doctor-PKT42NOJ.js} +59 -25
  54. package/dist/{emit-artifact-cmd-AVM4YXMQ.js → emit-artifact-cmd-BZYXRBCV.js} +8 -8
  55. package/dist/{emit-cmd-2QRMYPYO.js → emit-cmd-IRYNFHPM.js} +3 -3
  56. package/dist/{emit-criteria-cmd-LDBE4YDS.js → emit-criteria-cmd-PSZYZ32B.js} +6 -6
  57. package/dist/{emit-finding-cmd-6R5WBPCH.js → emit-finding-cmd-VTSYDJYZ.js} +6 -6
  58. package/dist/{emit-result-cmd-2DMN6O6U.js → emit-result-cmd-5P7ZFWMZ.js} +8 -8
  59. package/dist/{exec-client-NPYNUAF3.js → exec-client-2UX6NHAO.js} +3 -3
  60. package/dist/{guest-init-JWNNAH2H.js → guest-init-VHQNFCFS.js} +3 -3
  61. package/dist/{hook-fast-XBOZ3N6L.js → hook-fast-OCHVQZTU.js} +1 -1
  62. package/dist/{host-key-KDYZ5ECC.js → host-key-YMLXY6QB.js} +1 -1
  63. package/dist/{import-capture-cmd-T4556ELW.js → import-capture-cmd-2QFLZQFY.js} +3 -3
  64. package/dist/{import-ruler-cmd-SG3UVGI7.js → import-ruler-cmd-BD22GUJS.js} +1 -1
  65. package/dist/index.js +3 -3
  66. package/dist/{init-R2FJ5KNS.js → init-CY4IMZE4.js} +22 -22
  67. package/dist/{init-KDK5QLQZ.js → init-TQUN5RF7.js} +20 -20
  68. package/dist/{init-cmd-BYZYAJ5P.js → init-cmd-F4TZXQJH.js} +11 -11
  69. package/dist/{install-FHTVJZS6.js → install-C33LT5KQ.js} +3 -3
  70. package/dist/{install-cmd-DCF7TVXD.js → install-cmd-5HYMXLEW.js} +2 -1
  71. package/dist/{loop-AG3PLXPO.js → loop-NZP5FAC3.js} +30 -29
  72. package/dist/{machine-LV6WBRRK.js → machine-RUSNVLKO.js} +19 -19
  73. package/dist/{mcp-install-cmd-RLK4SO3N.js → mcp-install-cmd-4AQMSIGY.js} +1 -1
  74. package/dist/{mcp-serve-cmd-N6UD2KBX.js → mcp-serve-cmd-3KVYEUXG.js} +3 -3
  75. package/dist/{real-deps-SU24ZA2K.js → real-deps-TK6JMQSW.js} +3 -2
  76. package/dist/{session-sender-Q5HAMOL2.js → session-sender-EECVKXTC.js} +7 -7
  77. package/dist/{starter-pack-MUXMAU4S.js → starter-pack-VNWPYP5U.js} +3 -3
  78. package/dist/{status-HH4OSOHR.js → status-KCONQLOD.js} +31 -25
  79. package/dist/{sync-cmd-CAEPJR7B.js → sync-cmd-R54ZBHXX.js} +6 -6
  80. package/dist/{watch-LAAZFBHH.js → watch-YTFNBO3S.js} +6 -6
  81. package/dist/{work-claim-cmd-ARIYZARE.js → work-claim-cmd-HRPJ4VUF.js} +10 -10
  82. package/dist/{work-cmd-B3FOXZAL.js → work-cmd-W4GQUUBC.js} +7 -7
  83. package/dist/{work-comment-cmd-O5WGRI6K.js → work-comment-cmd-DDZ4SZ35.js} +7 -7
  84. package/dist/{work-open-pr-cmd-M6CQW7KW.js → work-open-pr-cmd-GXULDTSC.js} +8 -8
  85. package/dist/{work-pr-cmd-C4YEGKD6.js → work-pr-cmd-T6SU4TYN.js} +7 -7
  86. package/package.json +2 -2
  87. package/skill-pack/maintained/amend/SKILL.md +22 -6
  88. package/skill-pack/maintained/outerlayer/SKILL.md +12 -2
  89. package/skill-pack/maintained/outerlayer/references/open-pull-request.md +29 -0
  90. package/skill-pack/template/build/SKILL.md +3 -2
  91. package/skill-pack/template/build/references/release.md +11 -2
@@ -1,26 +1,30 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { readDefaultBranchHead, cloneRepository, buildRepositoryImage } from './chunk-TTNFGMHO.js';
3
+ import { readDefaultBranchHead, cloneRepository, buildRepositoryImage } from './chunk-Y7UQHKFY.js';
4
4
  import { startTimeOf, isSameProcess } from './chunk-N5FOE5PS.js';
5
5
  import { currentCliBuild } from './chunk-OGFGZQA3.js';
6
- import { createDockerHost, childEnvironment, spawnRun } from './chunk-U3GOI2B4.js';
6
+ import { createDockerHost, childEnvironment, spawnRun } from './chunk-NPBLDMSD.js';
7
7
  import { RecipeError } from './chunk-QOZKPLVJ.js';
8
- import { builderSpec, runnerLabelValue, RUNNER_LABEL, BUILD_LABEL, prepareRunArgs, containerRunArgs, containerExecArgs } from './chunk-5TNS3PL7.js';
8
+ import { builderSpec, runnerLabelValue, RUNNER_LABEL, BUILD_LABEL, prepareRunArgs, containerRunArgs, containerExecArgs } from './chunk-CLURXEWV.js';
9
9
  import { CLAUDE_CODE_VERSION, firecrackerArch, MICROVM_ARCHES, DEFAULT_BASE_IMAGE, GUEST_HELPER_SHA256, GUEST_ASSETS_BASE_URL, GUEST_KERNEL_SHA256, FIRECRACKER_BINARY_SHA256, FIRECRACKER_SHA256, FIRECRACKER_VERSION, GUEST_ASSETS_VERSION } from './chunk-CX22S5BA.js';
10
- import { execCapture } from './chunk-ZOAEPWGL.js';
11
- import { GUEST_CID } from './chunk-R7STSBLC.js';
12
- import { WorkCommandError, postJson } from './chunk-5QLNZGAQ.js';
10
+ import { execCapture } from './chunk-HWMYGAKF.js';
11
+ import { GUEST_CID } from './chunk-2E2MG3KW.js';
12
+ import { WorkCommandError, postJson } from './chunk-R5SPFO6F.js';
13
+ import { setCustomScrubbers, setLocalIdentity, setRepoRoot, parseTranscript, scrubDeep, scrubText } from './chunk-HN2JBGSI.js';
13
14
  import { hostAllowed } from './chunk-UXYLWVTQ.js';
14
15
  import { parseDuKilobytes } from './chunk-7RQ73DLO.js';
15
- import { RELAY_PORT, CHECKOUT_DIR, JOB_MOUNT, claudeCredential, buildEnvironment, startBuildServices, checkEnvironment, AGENT_CREDENTIAL_MISSING } from './chunk-OD2VTIHN.js';
16
- import { defaultGitRunner, materializeContext } from './chunk-5FWNEQNZ.js';
17
- import { readContextManifest, readSourceCache } from './chunk-ZZXLM64C.js';
18
- import { signingFetchFor } from './chunk-BCLJSQCV.js';
19
- import { signerFor } from './chunk-M5POMKKW.js';
16
+ import { RELAY_PORT, CHECKOUT_DIR, JOB_MOUNT, claudeCredential, buildEnvironment, startBuildServices, checkEnvironment, AGENT_CREDENTIAL_MISSING } from './chunk-2FWL7CE2.js';
17
+ import { defaultGitRunner, materializeContext } from './chunk-NACLOOXP.js';
18
+ import { readContextManifest, readSourceCache } from './chunk-ZJ3WRVRG.js';
19
+ import { orderSessions, extractFacts, sessionsToTimeline } from './chunk-CMFCCIQN.js';
20
+ import { signingFetchFor } from './chunk-YO4PSLVB.js';
21
+ import { downconvertSession } from './chunk-YNJEOGDL.js';
22
+ import { signerFor } from './chunk-V7NIVOFD.js';
20
23
  import { rootfsPathFor, diskTemplatesDir, RUNNER_FILE_MODE, stagedContextDir, contextHomeDir, ghConfigDir, runnerDirOfBuild, spoolDir, mirrorDir, buildDir, RUNNER_DIR_MODE, vmAssetsDir } from './chunk-ZM2IMMYN.js';
21
24
  import { relative, join, dirname, basename } from 'path';
22
25
  import { readFileSync, existsSync, mkdirSync, chmodSync, truncateSync, rmSync, writeFileSync, accessSync, constants, readdirSync, statSync, rmdirSync, renameSync, lstatSync, unlinkSync, openSync, closeSync, mkdtempSync, createWriteStream, createReadStream } from 'fs';
23
26
  import { spawnSync, spawn } from 'child_process';
27
+ import { availableParallelism, networkInterfaces } from 'os';
24
28
  import { randomBytes, createHash } from 'crypto';
25
29
  import { createServer, request } from 'http';
26
30
  import { request as request$1 } from 'https';
@@ -29,7 +33,6 @@ import { pipeline, finished } from 'stream/promises';
29
33
  import { createGunzip, gunzipSync, constants as constants$1 } from 'zlib';
30
34
  import { isIP, connect, createServer as createServer$1 } from 'net';
31
35
  import { lookup } from 'dns/promises';
32
- import { networkInterfaces } from 'os';
33
36
 
34
37
  createRequire(import.meta.url);
35
38
  function logStamp(at) {
@@ -430,7 +433,11 @@ async function prepareBranch(opts) {
430
433
  throw new BranchPrepareError(`the pull request's branch ${branch} is not on the remote`);
431
434
  }
432
435
  await mustGit(git2, workdir, `checking out ${branch}`, ["checkout", "--quiet", "-B", branch, start]);
433
- return { branch, head: await mustGit(git2, workdir, "reading the checked-out commit", ["rev-parse", "HEAD"]) };
436
+ return {
437
+ branch,
438
+ head: await mustGit(git2, workdir, "reading the checked-out commit", ["rev-parse", "HEAD"]),
439
+ startCommit: await mustGit(git2, workdir, `reading the commit of ${defaultBranch}`, ["rev-parse", `origin/${defaultBranch}`])
440
+ };
434
441
  }
435
442
  async function isMerged(git2, workdir, branch, defaultBranch) {
436
443
  const result = await git2(workdir, ["merge-base", "--is-ancestor", `origin/${branch}`, `origin/${defaultBranch}`]);
@@ -469,6 +476,63 @@ function lifecycleSteps(lifecycle) {
469
476
  function lifecycleFailureReason(step, exitCode) {
470
477
  return `lifecycle command ${step.name} (${step.source}) exited ${exitCode}`;
471
478
  }
479
+ function parseCpuList(text) {
480
+ const cores = /* @__PURE__ */ new Set();
481
+ for (const part of text.trim().split(",")) {
482
+ const match = /^(\d+)(?:-(\d+))?$/.exec(part.trim());
483
+ if (match === null) return void 0;
484
+ const low = Number(match[1]);
485
+ const high = match[2] === void 0 ? low : Number(match[2]);
486
+ if (high < low) return void 0;
487
+ for (let core = low; core <= high; core++) cores.add(core);
488
+ }
489
+ return [...cores].sort((a, b) => a - b);
490
+ }
491
+ function formatCpuList(cores) {
492
+ const sorted = [...new Set(cores)].sort((a, b) => a - b);
493
+ const parts = [];
494
+ for (let i = 0; i < sorted.length; i++) {
495
+ const start = sorted[i];
496
+ while (sorted[i + 1] === sorted[i] + 1) i++;
497
+ parts.push(start === sorted[i] ? String(start) : `${start}-${sorted[i]}`);
498
+ }
499
+ return parts.join(",");
500
+ }
501
+ function usableCpus(deps = {}) {
502
+ try {
503
+ const status = (deps.readStatus ?? (() => readFileSync("/proc/self/status", "utf8")))();
504
+ const cores = parseCpuList(/^Cpus_allowed_list:\s*(.+)$/m.exec(status)?.[1] ?? "");
505
+ if (cores !== void 0) return cores;
506
+ } catch {
507
+ }
508
+ return Array.from({ length: Math.max(1, (deps.parallelism ?? availableParallelism)()) }, (_unused, core) => core);
509
+ }
510
+ function createCpuAllocator(cores) {
511
+ const usable = [...new Set(cores)].sort((a, b) => a - b);
512
+ const held = /* @__PURE__ */ new Map();
513
+ const holders = (core) => {
514
+ let count = 0;
515
+ for (const set of held.values()) if (set.includes(core)) count++;
516
+ return count;
517
+ };
518
+ return {
519
+ acquire(buildId, cpus) {
520
+ const existing = held.get(buildId);
521
+ if (existing !== void 0) return existing;
522
+ const wanted = Math.min(Math.ceil(cpus), usable.length);
523
+ const ranked = usable.map((core) => ({ core, count: holders(core) })).sort((a, b) => a.count - b.count);
524
+ const chosen = ranked.slice(0, wanted).map(({ core }) => core).sort((a, b) => a - b);
525
+ held.set(buildId, chosen);
526
+ return chosen;
527
+ },
528
+ hold(buildId, set) {
529
+ held.set(buildId, [...set]);
530
+ },
531
+ release(buildId) {
532
+ held.delete(buildId);
533
+ }
534
+ };
535
+ }
472
536
  var AssetError = class extends Error {
473
537
  constructor(file, message) {
474
538
  super(message);
@@ -583,7 +647,7 @@ function templateScript() {
583
647
  ].join("\n");
584
648
  }
585
649
  var ids = () => ({ uid: String(process.getuid?.() ?? 0), gid: String(process.getgid?.() ?? 0) });
586
- function converterArgs(dest, extra, script) {
650
+ function converterArgs(dest, memoryBytes, extra, script) {
587
651
  const { uid, gid } = ids();
588
652
  return [
589
653
  "run",
@@ -591,6 +655,10 @@ function converterArgs(dest, extra, script) {
591
655
  "-i",
592
656
  "--network",
593
657
  "none",
658
+ "--memory",
659
+ String(memoryBytes),
660
+ "--memory-swap",
661
+ String(memoryBytes),
594
662
  "--mount",
595
663
  `type=bind,src=${dirname(dest)},dst=/out`,
596
664
  "-e",
@@ -636,18 +704,18 @@ function exportInto(container, converter) {
636
704
  function createRootfsHost() {
637
705
  const env = () => childEnvironment(process.env);
638
706
  return {
639
- async convert({ imageId, dest }) {
707
+ async convert({ imageId, dest, memoryBytes }) {
640
708
  const created = await spawnRun("docker", ["create", imageId, "none"], { env: env() });
641
709
  if (created.status !== 0) throw new Error(`docker create ${imageId} failed: ${firstLine2(created.stderr)}`);
642
710
  const container = created.stdout.trim();
643
711
  try {
644
- await exportInto(container, converterArgs(dest, [], converterScript()));
712
+ await exportInto(container, converterArgs(dest, memoryBytes, [], converterScript()));
645
713
  } finally {
646
714
  await spawnRun("docker", ["rm", "--force", "--volumes", container], { env: env() });
647
715
  }
648
716
  },
649
- async makeTemplate({ bytes, dest }) {
650
- const made = await spawnRun("docker", converterArgs(dest, ["-e", `BYTES=${bytes}`], templateScript()), { env: env() });
717
+ async makeTemplate({ bytes, dest, memoryBytes }) {
718
+ const made = await spawnRun("docker", converterArgs(dest, memoryBytes, ["-e", `BYTES=${bytes}`], templateScript()), { env: env() });
651
719
  if (made.status !== 0) throw new Error(`making the empty build disk failed: ${firstLine2(made.stderr)}`);
652
720
  },
653
721
  async imageConfig(imageId) {
@@ -696,14 +764,14 @@ async function ensureRootfs(input) {
696
764
  const path = rootfsPathFor(input.runnerDir, input.imageId);
697
765
  if (existsSync(path)) return path;
698
766
  const { uid, gid } = ids();
699
- await madeAtomically(path, (temp) => input.host.convert({ imageId: input.imageId, dest: temp, uid: Number(uid), gid: Number(gid) }));
767
+ await madeAtomically(path, (temp) => input.host.convert({ imageId: input.imageId, dest: temp, uid: Number(uid), gid: Number(gid), memoryBytes: input.memoryBytes }));
700
768
  return path;
701
769
  }
702
770
  async function createBuildDisk(input) {
703
771
  const template = join(diskTemplatesDir(input.runnerDir), `${input.bytes}.ext4`);
704
772
  if (!existsSync(template)) {
705
773
  const { uid, gid } = ids();
706
- await madeAtomically(template, (temp) => input.host.makeTemplate({ bytes: input.bytes, dest: temp, uid: Number(uid), gid: Number(gid) }));
774
+ await madeAtomically(template, (temp) => input.host.makeTemplate({ bytes: input.bytes, dest: temp, uid: Number(uid), gid: Number(gid), memoryBytes: input.memoryBytes }));
707
775
  }
708
776
  mkdirSync(dirname(input.dest), { recursive: true });
709
777
  if (input.host.copyTemplate) input.host.copyTemplate(template, input.dest);
@@ -831,6 +899,7 @@ function adoptionFailureReason(result) {
831
899
  }
832
900
  async function runBuildSteps(input) {
833
901
  const { exec, log, tag, repository } = input;
902
+ let startCommit = "";
834
903
  const rewrite = await exec(["git", "config", "--global", `url.http://127.0.0.1:${RELAY_PORT}/github.com/.insteadOf`, "https://github.com/"], "/build/work");
835
904
  if (rewrite.status !== 0) return { ok: false, reason: `configuring the build's git remote failed (exit ${rewrite.status})` };
836
905
  const clone = await exec(["git", "clone", "--filter=blob:none", "--single-branch", `https://github.com/${repository}.git`, CHECKOUT_DIR], "/build/work");
@@ -847,6 +916,7 @@ async function runBuildSteps(input) {
847
916
  return { status: ran.status, stdout: ran.stdout.trim(), stderr: ran.stderr.trim() };
848
917
  }
849
918
  });
919
+ startCommit = prepared.startCommit;
850
920
  log.line(`${tag} on ${prepared.branch} at ${prepared.head.slice(0, 8)}`);
851
921
  } catch (err) {
852
922
  if (err instanceof BranchPrepareError) return { ok: false, reason: err.message };
@@ -873,7 +943,200 @@ async function runBuildSteps(input) {
873
943
  }
874
944
  if (input.aborted()) return aborted();
875
945
  }
876
- return { ok: true };
946
+ return { ok: true, startCommit };
947
+ }
948
+ var EVAL_INPUT_NAME = "eval-input";
949
+ var TRANSCRIPT_ROOT = "/build/home/.claude/projects";
950
+ var MAX_TRANSCRIPT_BYTES = 7 * 1024 * 1024;
951
+ var MAX_TRANSCRIPTS = 500;
952
+ var MAX_TOTAL_TRANSCRIPT_BYTES = 64 * 1024 * 1024;
953
+ var TRANSCRIPT_COPY_MS = 2 * 60 * 1e3;
954
+ var BUILD_HOME = "/build/home";
955
+ function firstLine4(text) {
956
+ return text.trim().split("\n")[0] ?? "";
957
+ }
958
+ async function copyTranscripts(run, now = Date.now) {
959
+ const deadline = now() + TRANSCRIPT_COPY_MS;
960
+ let listed;
961
+ try {
962
+ listed = await run(["find", TRANSCRIPT_ROOT, "-type", "f", "-name", "*.jsonl", "-print0"]);
963
+ } catch (err) {
964
+ return { ok: false, reason: describeCallFailure(err) };
965
+ }
966
+ if (listed.status !== 0 && !/No such file or directory/.test(listed.stderr)) {
967
+ return { ok: false, reason: `listing the transcripts failed (exit ${listed.status})${firstLine4(listed.stderr) === "" ? "" : `: ${firstLine4(listed.stderr)}`}` };
968
+ }
969
+ const paths = listed.stdout.split("\0").filter((entry) => entry.startsWith(`${TRANSCRIPT_ROOT}/`) && !entry.includes("\n") && !entry.slice(TRANSCRIPT_ROOT.length + 1).split("/").includes("..")).map((entry) => entry.slice(TRANSCRIPT_ROOT.length + 1)).sort();
970
+ const copied = [];
971
+ const missed = [];
972
+ let total = 0;
973
+ for (const [index, path] of paths.entries()) {
974
+ if (index >= MAX_TRANSCRIPTS) {
975
+ missed.push({ path, reason: `the build wrote more than ${MAX_TRANSCRIPTS} transcripts` });
976
+ continue;
977
+ }
978
+ if (now() >= deadline) {
979
+ missed.push({ path, reason: `copying the transcripts took longer than ${TRANSCRIPT_COPY_MS / 1e3} seconds` });
980
+ continue;
981
+ }
982
+ try {
983
+ const read = await run(["head", "-c", String(MAX_TRANSCRIPT_BYTES + 1), `${TRANSCRIPT_ROOT}/${path}`]);
984
+ if (read.status !== 0) missed.push({ path, reason: `reading it failed (exit ${read.status})${firstLine4(read.stderr) === "" ? "" : `: ${firstLine4(read.stderr)}`}` });
985
+ else if (Buffer.byteLength(read.stdout) > MAX_TRANSCRIPT_BYTES) missed.push({ path, reason: `it is larger than ${MAX_TRANSCRIPT_BYTES / (1024 * 1024)} MiB` });
986
+ else if (total + Buffer.byteLength(read.stdout) > MAX_TOTAL_TRANSCRIPT_BYTES) missed.push({ path, reason: `copying it would take the transcripts past ${MAX_TOTAL_TRANSCRIPT_BYTES / (1024 * 1024)} MiB` });
987
+ else {
988
+ total += Buffer.byteLength(read.stdout);
989
+ copied.push({ path, content: read.stdout });
990
+ }
991
+ } catch (err) {
992
+ missed.push({ path, reason: describeCallFailure(err) });
993
+ }
994
+ }
995
+ return { ok: true, copied, missed };
996
+ }
997
+ function traceIdForSession(sessionId) {
998
+ const stripped = sessionId.replace(/-/g, "").toLowerCase();
999
+ if (/^[0-9a-f]{32}$/.test(stripped)) return stripped;
1000
+ return createHash("sha256").update(sessionId).digest("hex").slice(0, 32);
1001
+ }
1002
+ function sessionOptionsOf(path) {
1003
+ const segments = path.split("/");
1004
+ const stem = (segments.at(-1) ?? path).replace(/\.jsonl$/, "");
1005
+ const at = segments.lastIndexOf("subagents");
1006
+ if (at <= 0) return { fallbackId: stem };
1007
+ const parent = segments[at - 1];
1008
+ return {
1009
+ fallbackId: stem,
1010
+ isSubagent: true,
1011
+ ...parent ? { parentSessionId: parent } : {},
1012
+ ownId: stem,
1013
+ ...segments[at + 1] === "workflows" && segments[at + 2] ? { workflowRunId: segments[at + 2] } : {}
1014
+ };
1015
+ }
1016
+ function scrubbing(scrub, author, work) {
1017
+ setCustomScrubbers(scrub, () => void 0);
1018
+ setLocalIdentity({ home: BUILD_HOME, ...author?.name === void 0 ? {} : { name: author.name }, ...author?.email === void 0 ? {} : { email: author.email } });
1019
+ setRepoRoot(CHECKOUT_DIR);
1020
+ try {
1021
+ return work();
1022
+ } finally {
1023
+ setCustomScrubbers(void 0, () => void 0);
1024
+ setLocalIdentity({});
1025
+ setRepoRoot(void 0);
1026
+ }
1027
+ }
1028
+ function scrubLine(line) {
1029
+ try {
1030
+ return JSON.stringify(scrubDeep(JSON.parse(line)));
1031
+ } catch {
1032
+ return scrubText(line);
1033
+ }
1034
+ }
1035
+ function writeReadOnly(path, text) {
1036
+ writeFileSync(path, text);
1037
+ chmodSync(path, 292);
1038
+ }
1039
+ function diffBase(job) {
1040
+ const recorded = (job.container ?? job.microvm)?.startCommit;
1041
+ if (recorded !== void 0 && recorded !== "") return { commit: recorded, source: "build" };
1042
+ const recipe = job.environment?.recipe?.sourceCommit;
1043
+ return recipe === void 0 || recipe === "" ? void 0 : { commit: recipe, source: "recipe" };
1044
+ }
1045
+ async function writeEvalInput(request) {
1046
+ const { job, dir, log } = request;
1047
+ const tag = `#${job.item}`;
1048
+ rmSync(dir, { recursive: true, force: true });
1049
+ mkdirSync(join(dir, "sessions"), { recursive: true });
1050
+ chmodSync(dir, 493);
1051
+ chmodSync(join(dir, "sessions"), 493);
1052
+ let workItem;
1053
+ try {
1054
+ const data = await request.readWorkItem();
1055
+ writeReadOnly(join(dir, "work-item.json"), `${JSON.stringify(data, null, 2)}
1056
+ `);
1057
+ workItem = { status: "written" };
1058
+ } catch (err) {
1059
+ workItem = { status: "unavailable", reason: describeCallFailure(err) };
1060
+ log.error(`${tag} the work item could not be read for the checks' input: ${workItem.reason}`);
1061
+ }
1062
+ const patch = join(dir, "diff.patch");
1063
+ const base = diffBase(job);
1064
+ let diff;
1065
+ if (base === void 0) {
1066
+ diff = { status: "unavailable", reason: "the build recorded no commit it started from" };
1067
+ } else {
1068
+ const fetched = await request.fetchDiff({ remote: request.remote, dir: join(request.scratch, "diff"), base: base.commit, head: request.branchHead, token: request.readToken, output: patch });
1069
+ diff = fetched.ok ? { status: "written", base: base.commit, head: request.branchHead, baseSource: base.source } : { status: "unavailable", base: base.commit, head: request.branchHead, baseSource: base.source, reason: fetched.reason };
1070
+ if (!fetched.ok) log.error(`${tag} the diff could not be read for the checks' input: ${fetched.reason}`);
1071
+ }
1072
+ if (diff.status === "unavailable") writeFileSync(patch, "");
1073
+ chmodSync(patch, 292);
1074
+ const sessions = [];
1075
+ const parsed = [];
1076
+ const copiedByTrace = /* @__PURE__ */ new Map();
1077
+ let sessionsUnavailable;
1078
+ if (!request.transcripts.ok) {
1079
+ sessionsUnavailable = request.transcripts.reason;
1080
+ log.error(`${tag} the sessions could not be copied for the checks' input: ${sessionsUnavailable}`);
1081
+ } else {
1082
+ const { copied, missed } = request.transcripts;
1083
+ scrubbing(request.scrub, request.job.commitIdentity?.author, () => {
1084
+ for (const transcript of copied) {
1085
+ const options = sessionOptionsOf(transcript.path);
1086
+ const subagent = options.isSubagent === true;
1087
+ const stem = options.fallbackId ?? transcript.path;
1088
+ let session = null;
1089
+ try {
1090
+ session = parseTranscript(transcript.content, options).session;
1091
+ } catch {
1092
+ }
1093
+ const traceId = traceIdForSession(session?.id ?? stem);
1094
+ if (session === null) {
1095
+ sessions.push({ traceId, subagent, status: "missing", reason: "the transcript holds no session" });
1096
+ log.error(`${tag} the transcript ${transcript.path} holds no session, so the checks' input leaves it out`);
1097
+ continue;
1098
+ }
1099
+ const kept = copiedByTrace.get(traceId);
1100
+ if (kept !== void 0) {
1101
+ kept.duplicates = (kept.duplicates ?? 0) + 1;
1102
+ log.error(`${tag} the transcript ${transcript.path} repeats session ${traceId}, so the checks' input leaves it out`);
1103
+ continue;
1104
+ }
1105
+ const file = `sessions/${traceId}.jsonl`;
1106
+ const text = transcript.content.split("\n").filter((line) => line.trim() !== "").map(scrubLine).join("\n");
1107
+ writeReadOnly(join(dir, file), `${text}
1108
+ `);
1109
+ scrubDeep(session);
1110
+ parsed.push({ session: downconvertSession(session, "full"), traceId, subagent });
1111
+ const entry = { traceId, subagent, file, status: "copied" };
1112
+ copiedByTrace.set(traceId, entry);
1113
+ sessions.push(entry);
1114
+ }
1115
+ for (const miss of missed) {
1116
+ const options = sessionOptionsOf(miss.path);
1117
+ sessions.push({ traceId: traceIdForSession(options.fallbackId ?? miss.path), subagent: options.isSubagent === true, status: "missing", reason: miss.reason });
1118
+ log.error(`${tag} the transcript ${miss.path} could not be copied for the checks' input: ${miss.reason}`);
1119
+ }
1120
+ });
1121
+ }
1122
+ const ordered = orderSessions(parsed.map((entry) => entry.session));
1123
+ const traceOf = new Map(parsed.map((entry) => [entry.session.id, entry.traceId]));
1124
+ const facts = extractFacts(sessionsToTimeline(parsed.map((entry) => entry.session)));
1125
+ const traceAt = (index) => traceOf.get(ordered[index].id);
1126
+ const recorded = { commands: facts.coverage.has("commands"), edits: facts.coverage.has("edits") };
1127
+ const activity = {
1128
+ schemaVersion: 1,
1129
+ sessions: sessions.sort((a, b) => a.traceId.localeCompare(b.traceId)),
1130
+ // Left out of the file when undefined.
1131
+ sessionsUnavailable,
1132
+ workItem,
1133
+ diff,
1134
+ recorded,
1135
+ commands: recorded.commands ? facts.runs.map((run) => ({ seq: run.seq, traceId: traceAt(run.sessionIndex), turn: run.turnIndex, command: run.normalized, kind: run.kind, status: run.status })) : null,
1136
+ edits: recorded.edits ? facts.edits.map((edit) => ({ seq: edit.seq, traceId: traceAt(edit.sessionIndex), turn: edit.turnIndex, file: edit.file, status: edit.status })) : null
1137
+ };
1138
+ writeReadOnly(join(dir, "activity.json"), `${JSON.stringify(activity, null, 2)}
1139
+ `);
877
1140
  }
878
1141
 
879
1142
  // src/runner/container/agent-user.ts
@@ -1880,6 +2143,7 @@ function createContainerRuntime(opts = {}) {
1880
2143
  const { cli, remoteFor } = opts;
1881
2144
  return {
1882
2145
  host: opts.host ?? createContainerHost(),
2146
+ cpus: opts.cpus ?? createCpuAllocator(usableCpus()),
1883
2147
  tunnels: opts.tunnels ?? /* @__PURE__ */ new Map(),
1884
2148
  buildImage: opts.buildImage ?? ((build) => buildRepositoryImage(
1885
2149
  {
@@ -1934,7 +2198,7 @@ function gitRemoteFor(runtime, input) {
1934
2198
  ...input.readOnly === true ? { readOnly: true } : {}
1935
2199
  });
1936
2200
  }
1937
- function firstLine4(text) {
2201
+ function firstLine5(text) {
1938
2202
  return text.trim().split("\n")[0] ?? "";
1939
2203
  }
1940
2204
  var NO_ENV = { literals: {}, passed: {} };
@@ -2027,7 +2291,8 @@ async function provisionContainer(input) {
2027
2291
  imageId: report.imageId,
2028
2292
  containerEnv: report.apply.containerEnv,
2029
2293
  builtIns: true,
2030
- lifecycle: report.apply.lifecycle
2294
+ lifecycle: report.apply.lifecycle,
2295
+ cpuset: formatCpuList(runtime.cpus.acquire(buildId, config.build?.cpus ?? 4))
2031
2296
  };
2032
2297
  const env = buildEnvironment({
2033
2298
  config,
@@ -2058,7 +2323,7 @@ async function provisionContainer(input) {
2058
2323
  }
2059
2324
  return aborted2 ? { ok: false, reason: "ended before provision finished", aborted: true } : fail(reason);
2060
2325
  };
2061
- const dockerFailed = (what, result) => bail(`${what} failed (exit ${result.status})${firstLine4(result.stderr) === "" ? "" : `: ${firstLine4(result.stderr)}`}`);
2326
+ const dockerFailed = (what, result) => bail(`${what} failed (exit ${result.status})${firstLine5(result.stderr) === "" ? "" : `: ${firstLine5(result.stderr)}`}`);
2062
2327
  if (input.aborted()) return bail("", true);
2063
2328
  mkdirSync(buildDir2, { recursive: true });
2064
2329
  chmodSync(buildDir2, 493);
@@ -2079,7 +2344,7 @@ async function provisionContainer(input) {
2079
2344
  } catch (err) {
2080
2345
  return bail(`the tunnel could not start: ${describeCallFailure(err)}`);
2081
2346
  }
2082
- const limits2 = { memory: config.build?.memory ?? "8g", cpus: config.build?.cpus ?? 4, pids: config.build?.pids ?? 4096 };
2347
+ const limits2 = { memory: config.build?.memory ?? "8g", cpus: config.build?.cpus ?? 4, pids: config.build?.pids ?? 4096, cpuset: state.cpuset };
2083
2348
  const runnerId = runnerLabelValue(config.configPath);
2084
2349
  const volume = await host.createVolume(name, { [BUILD_LABEL]: buildId, [RUNNER_LABEL]: runnerId });
2085
2350
  if (volume.status !== 0) return dockerFailed("creating the build's volume", volume);
@@ -2143,6 +2408,7 @@ async function provisionContainer(input) {
2143
2408
  removeStagedContext: () => removeStagedContext(buildDir2)
2144
2409
  });
2145
2410
  if (!steps.ok) return bail(steps.reason, steps.aborted === true);
2411
+ state.startCommit = steps.startCommit;
2146
2412
  return { ok: true, workdir: CHECKOUT_DIR, state, environment };
2147
2413
  }
2148
2414
  function agentVersionOf(toolsJson) {
@@ -2278,6 +2544,18 @@ async function syncInContainer(input) {
2278
2544
  return { ok: false, reason: describeCallFailure(err) };
2279
2545
  }
2280
2546
  }
2547
+ async function copyTranscriptsFromContainer(runtime, state) {
2548
+ try {
2549
+ if (state.stopped) {
2550
+ const started = await runtime.host.start(state.container);
2551
+ if (started.status !== 0) return { ok: false, reason: `the build's container could not be started again (exit ${started.status})` };
2552
+ state.stopped = false;
2553
+ }
2554
+ } catch (err) {
2555
+ return { ok: false, reason: describeCallFailure(err) };
2556
+ }
2557
+ return copyTranscripts((argv) => runtime.host.exec({ container: state.container, user: state.agentUser ?? ADDED_USER, env: NO_ENV, argv }));
2558
+ }
2281
2559
  var HOOK_ERRORS_IN_CONTAINER = "/build/home/.outerlayer/spool/hook-errors.log";
2282
2560
  async function copyHookErrors(runtime, state, jobDir) {
2283
2561
  try {
@@ -2310,6 +2588,7 @@ async function cleanupContainer(runtime, state, jobDir) {
2310
2588
  await tunnel.close();
2311
2589
  }
2312
2590
  rmSync(state.buildDir, { recursive: true, force: true });
2591
+ runtime.cpus.release(state.buildId);
2313
2592
  if (failures.length > 0) throw new Error(failures.join("; "));
2314
2593
  }
2315
2594
  async function startCheckContainer(input) {
@@ -2325,7 +2604,8 @@ async function startCheckContainer(input) {
2325
2604
  const name = `outerlayer-check-${buildId}`;
2326
2605
  const buildDir2 = buildDir(config.configPath, buildId);
2327
2606
  const agentUser = built.agentUser ?? ADDED_USER;
2328
- const state = { buildId, container: name, volume: name, buildDir: buildDir2, repository, imageId: built.imageId, agentUser };
2607
+ const cpuset = built.cpuset ?? formatCpuList(runtime.cpus.acquire(buildId, config.build?.cpus ?? 4));
2608
+ const state = { buildId, container: name, volume: name, buildDir: buildDir2, repository, imageId: built.imageId, agentUser, cpuset };
2329
2609
  const checksDir = join(jobDir, "checks");
2330
2610
  mkdirSync(checksDir, { recursive: true });
2331
2611
  const logPath = join(checksDir, "environment.log");
@@ -2338,7 +2618,7 @@ async function startCheckContainer(input) {
2338
2618
  }
2339
2619
  return { ok: false, reason };
2340
2620
  };
2341
- const dockerFailed = (what, result) => fail(`${what} failed (exit ${result.status})${firstLine4(result.stderr) === "" ? "" : `: ${firstLine4(result.stderr)}`}`);
2621
+ const dockerFailed = (what, result) => fail(`${what} failed (exit ${result.status})${firstLine5(result.stderr) === "" ? "" : `: ${firstLine5(result.stderr)}`}`);
2342
2622
  mkdirSync(buildDir2, { recursive: true });
2343
2623
  chmodSync(buildDir2, 493);
2344
2624
  try {
@@ -2347,7 +2627,7 @@ async function startCheckContainer(input) {
2347
2627
  } catch (err) {
2348
2628
  return fail(`the tunnel could not start: ${describeCallFailure(err)}`);
2349
2629
  }
2350
- const limits2 = { memory: config.build?.memory ?? "8g", cpus: config.build?.cpus ?? 4, pids: config.build?.pids ?? 4096 };
2630
+ const limits2 = { memory: config.build?.memory ?? "8g", cpus: config.build?.cpus ?? 4, pids: config.build?.pids ?? 4096, cpuset };
2351
2631
  const runnerId = runnerLabelValue(config.configPath);
2352
2632
  const volume = await host.createVolume(name, { [BUILD_LABEL]: buildId, [RUNNER_LABEL]: runnerId });
2353
2633
  if (volume.status !== 0) return dockerFailed("creating the check environment's volume", volume);
@@ -2373,6 +2653,8 @@ async function startCheckContainer(input) {
2373
2653
  ok: true,
2374
2654
  container: {
2375
2655
  state,
2656
+ // The directory the container sees read-only at `JOB_MOUNT` is the build's directory on the host, so a folder written there is already in place.
2657
+ evalInput: { dir: join(buildDir2, EVAL_INPUT_NAME), deliver: async () => void 0 },
2376
2658
  exec: (argv, workdir, opts) => host.exec({ container: name, user: agentUser, workdir, env, argv }, opts?.logPath === void 0 ? void 0 : { logPath: opts.logPath }),
2377
2659
  restart: async () => {
2378
2660
  await host.stop(name);
@@ -2383,4 +2665,4 @@ async function startCheckContainer(input) {
2383
2665
  };
2384
2666
  }
2385
2667
 
2386
- export { ADDED_USER, AgentUserError, AssetError, BuildTokens, GH_CONFIG_YML, MIN_ENGINE_MAJOR, classifyRefusal, cleanupContainer, containerCommandLine, containerSenderCommand, createBuildDisk, createContainerRuntime, createRunnerLog, deleteBuildDisk, describeCallFailure, ensureRootfs, ensureTunnel, formatClock, formatDuration, ghHostsYml, gitRemoteFor, isLiteralAddress, keepGhConfigCurrent, lifecycleFailureReason, lifecycleSteps, machineConfig, measureDisk, obtainImage, outOfMemoryReason, prepareBranch, provisionContainer, refusalCode, relativeToRunner, removeStagedContext, resolveAgentUser, retrySend, runBuildSteps, senderSignalScript, setUpRunnerCgroup, shellQuote, shortClaim, signalInContainer, stageGovernedContext, startCheckContainer, startServicesFor, stopContainer, syncInContainer };
2668
+ export { ADDED_USER, AgentUserError, AssetError, BuildTokens, EVAL_INPUT_NAME, GH_CONFIG_YML, MIN_ENGINE_MAJOR, classifyRefusal, cleanupContainer, containerCommandLine, containerSenderCommand, copyTranscripts, copyTranscriptsFromContainer, createBuildDisk, createContainerRuntime, createRunnerLog, deleteBuildDisk, describeCallFailure, ensureRootfs, ensureTunnel, formatClock, formatDuration, ghHostsYml, gitRemoteFor, isLiteralAddress, keepGhConfigCurrent, lifecycleFailureReason, lifecycleSteps, machineConfig, measureDisk, obtainImage, outOfMemoryReason, parseCpuList, prepareBranch, provisionContainer, refusalCode, relativeToRunner, removeStagedContext, resolveAgentUser, retrySend, runBuildSteps, senderSignalScript, setUpRunnerCgroup, shellQuote, shortClaim, signalInContainer, stageGovernedContext, startCheckContainer, startServicesFor, stopContainer, syncInContainer, writeEvalInput };
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { writeLauncher, LauncherWriteError, pathNotes, launcherPath } from './chunk-PTW6W5NR.js';
4
- import { isInLayout, packageRootIn, versionDir, switchCurrent, currentLink, execStartLine } from './chunk-TI7B7UEO.js';
3
+ import { compareVersions } from './chunk-I2UOAV7X.js';
4
+ import { launcherPath, writeLauncher, LauncherWriteError, pathNotes } from './chunk-PTW6W5NR.js';
5
+ import { currentVersion, currentLink, isInLayout, packageRootIn, versionDir, switchCurrent, execStartLine } from './chunk-TI7B7UEO.js';
5
6
  import { RUNNER_DIR_MODE } from './chunk-ZM2IMMYN.js';
6
7
  import { realpathSync, readFileSync, mkdirSync, rmSync, cpSync, existsSync } from 'fs';
7
8
  import { dirname, sep, join } from 'path';
@@ -9,6 +10,8 @@ import { dirname, sep, join } from 'path';
9
10
  createRequire(import.meta.url);
10
11
  var RunnerInstallError = class extends Error {
11
12
  };
13
+ var RunnerDowngradeError = class extends RunnerInstallError {
14
+ };
12
15
  function runRunnerInstall(args) {
13
16
  const { configPath, nodePath } = args;
14
17
  let entry;
@@ -32,6 +35,13 @@ function runRunnerInstall(args) {
32
35
  if (manifest.name !== "@outerlayer/cli") throw new RunnerInstallError(`${packageRoot} is not the @outerlayer/cli package`);
33
36
  const version = manifest.version;
34
37
  if (typeof version !== "string" || version === "") throw new RunnerInstallError(`${packageRoot}/package.json has no version`);
38
+ const installed = currentVersion(configPath);
39
+ if (args.allowDowngrade !== true && installed !== void 0 && (compareVersions(version, installed) ?? 0) < 0) {
40
+ const command = args.command ?? "runner install";
41
+ throw new RunnerDowngradeError(
42
+ `this CLI is ${version}, older than ${installed}, the version the runner runs from ${currentLink(configPath)}. Installing it would move the runner back to ${version}. Run \`${launcherPath(args.home)} ${command}\` instead, which runs ${installed}. To install ${version} anyway, run \`outerlayer ${command} --allow-downgrade\``
43
+ );
44
+ }
35
45
  if (!isInLayout(configPath, entry) && !sameBuildInstalled(packageRoot, packageRootIn(versionDir(configPath, version)))) {
36
46
  const target = packageRootIn(versionDir(configPath, version));
37
47
  mkdirSync(dirname(target), { recursive: true, mode: RUNNER_DIR_MODE });
@@ -49,6 +59,7 @@ function runRunnerInstall(args) {
49
59
  }
50
60
  return {
51
61
  version,
62
+ previous: installed,
52
63
  execStart: execStartLine(configPath, nodePath),
53
64
  launcher: {
54
65
  path: launcherPath(args.home),
@@ -79,5 +90,9 @@ function launcherSummary(launcher) {
79
90
  if (launcher.state === "foreign") return void 0;
80
91
  return `${launcher.state === "unchanged" ? "outerlayer command already at" : "wrote the outerlayer command at"} ${launcher.path}, which runs the CLI that ${launcher.follows} names`;
81
92
  }
93
+ function restartNote(result) {
94
+ if (result.previous === void 0 || result.previous === result.version) return void 0;
95
+ return `current moved from ${result.previous} to ${result.version}: a runner its service started takes no new work, waits for its running builds to finish, then restarts on ${result.version}. A runner started by hand keeps running until you restart it`;
96
+ }
82
97
 
83
- export { RunnerInstallError, launcherSummary, runRunnerInstall };
98
+ export { RunnerDowngradeError, RunnerInstallError, launcherSummary, restartNote, runRunnerInstall };
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
3
  import { sanitizeTerminal } from './chunk-ZYHKJKSU.js';
4
- import { remoteLookupFailed } from './chunk-GAYWY4GI.js';
4
+ import { remoteLookupFailed } from './chunk-HN2JBGSI.js';
5
5
  import { DEFAULT_DASHBOARD } from './chunk-USO2DKBD.js';
6
- import { resolveGatewayCredentials } from './chunk-BOLTI6LR.js';
6
+ import { resolveGatewayCredentials } from './chunk-PLMTLGYH.js';
7
7
  import { readCloudConfig } from './chunk-EABW6AJQ.js';
8
8
  import { writeRepoConnection, findGitDirs, readRepoConnection } from './chunk-BJ3KTMDH.js';
9
9
  import { createInterface } from 'readline/promises';
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { maintainedFilesForExistingSkills, loadMaintainedFiles, writeMaintainedFiles } from './chunk-VXLJORHL.js';
4
- import { emitTree, parseOuterlayerConfig, classifyTree, generatedHeaderSource, CONTEXT_PIN_FILENAME, normalizePath } from './chunk-7ZR5UPMB.js';
3
+ import { maintainedFilesForExistingSkills, loadMaintainedFiles, writeMaintainedFiles } from './chunk-JXH6LEEH.js';
4
+ import { emitTree, parseOuterlayerConfig, classifyTree, generatedHeaderSource, CONTEXT_PIN_FILENAME, normalizePath } from './chunk-CMFCCIQN.js';
5
5
  import { existsSync, readFileSync, mkdirSync, writeFileSync, copyFileSync, readdirSync } from 'fs';
6
6
  import { join, dirname, relative, sep } from 'path';
7
7
 
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
3
  import { C } from './chunk-YRYMNBGQ.js';
4
- import { resolvePackRoot, loadTemplateFiles, loadMaintainedFiles, TEMPLATE_SKILLS, skillDir, writeMaintainedFiles, MAINTAINED_SKILLS } from './chunk-VXLJORHL.js';
4
+ import { resolvePackRoot, loadTemplateFiles, loadMaintainedFiles, TEMPLATE_SKILLS, skillDir, writeMaintainedFiles, MAINTAINED_SKILLS } from './chunk-JXH6LEEH.js';
5
5
  import { resolvePackageVersion } from './chunk-RA3O54FC.js';
6
6
  import { existsSync, mkdirSync, writeFileSync } from 'fs';
7
7
  import { join, dirname } from 'path';
@@ -21663,6 +21663,7 @@ function buildTokenPricingDictionary(models) {
21663
21663
  }
21664
21664
  var CACHE_CREATE_1H_INPUT_MULTIPLIER = 2;
21665
21665
  var isCount = (n) => typeof n === "number" && Number.isFinite(n) && n > 0;
21666
+ var tokensOf = (n) => isCount(n) ? n : 0;
21666
21667
  var hasTokens = (usage) => isCount(usage.in) || isCount(usage.out) || isCount(usage.cacheRead) || isCount(usage.cacheCreate5m) || isCount(usage.cacheCreate1h);
21667
21668
  function selectRates(price, context = {}) {
21668
21669
  var _a5, _b, _c;
@@ -21704,7 +21705,7 @@ function priceUsage(usage, price, context = {}) {
21704
21705
  if (isCount(usage.cacheCreate5m) && rates.cacheCreate === void 0) unpriced.push("cacheCreate5m");
21705
21706
  if (unpriced.length > 0) return { costUsd: null, unpricedClasses: unpriced };
21706
21707
  return {
21707
- costUsd: rates.in * usage.in + rates.out * usage.out + ((_a5 = rates.cacheRead) != null ? _a5 : 0) * usage.cacheRead + ((_b = rates.cacheCreate) != null ? _b : 0) * usage.cacheCreate5m + ((_c = rates.cacheCreate1h) != null ? _c : rates.in * CACHE_CREATE_1H_INPUT_MULTIPLIER) * usage.cacheCreate1h
21708
+ costUsd: rates.in * tokensOf(usage.in) + rates.out * tokensOf(usage.out) + ((_a5 = rates.cacheRead) != null ? _a5 : 0) * tokensOf(usage.cacheRead) + ((_b = rates.cacheCreate) != null ? _b : 0) * tokensOf(usage.cacheCreate5m) + ((_c = rates.cacheCreate1h) != null ? _c : rates.in * CACHE_CREATE_1H_INPUT_MULTIPLIER) * tokensOf(usage.cacheCreate1h)
21708
21709
  };
21709
21710
  }
21710
21711
  function priceBuckets(buckets, dict) {
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { EXEC_PORT } from './chunk-R7STSBLC.js';
3
+ import { EXEC_PORT } from './chunk-2E2MG3KW.js';
4
4
  import { createConnection } from 'net';
5
5
 
6
6
  createRequire(import.meta.url);