@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,197 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'node:module';
3
+ import { installStarterPack } from './chunk-ZMYPLFG3.js';
4
+ import { runEmit } from './chunk-H5NCNVDA.js';
5
+ import { addStdioMcpEntryIfAbsent } from './chunk-MQ3IPHIZ.js';
6
+ import { C } from './chunk-YRYMNBGQ.js';
7
+ import { resolveGitCommonDir, defaultGitRunner } from './chunk-XDDW4FRS.js';
8
+ import { pinExists, readContextManifest } from './chunk-WIOZAJ2W.js';
9
+ import { loadTemplateFiles, loadMaintainedFiles, skillDir } from './chunk-WO2BXCTQ.js';
10
+ import { generatedHeaderSource } from './chunk-EMY4I27X.js';
11
+ import './chunk-ZJSNLVCE.js';
12
+ import './chunk-BCLJSQCV.js';
13
+ import './chunk-BOLTI6LR.js';
14
+ import './chunk-EABW6AJQ.js';
15
+ import './chunk-RA3O54FC.js';
16
+ import './chunk-M5POMKKW.js';
17
+ import './chunk-ZM2IMMYN.js';
18
+ import './chunk-XCCLFVXM.js';
19
+ import './chunk-BJ3KTMDH.js';
20
+ import './chunk-VNVZDWO3.js';
21
+ import './chunk-Z5GEFMRW.js';
22
+ import { execFileSync } from 'child_process';
23
+ import { existsSync, mkdirSync, writeFileSync } from 'fs';
24
+ import { join, dirname } from 'path';
25
+
26
+ createRequire(import.meta.url);
27
+ var CONFIG_PATH = ".outerlayer/config.json";
28
+ var MCP_OUTPUT_PATH = ".mcp.json";
29
+ var POLICY_PATH = ".outerlayer/policy.yaml";
30
+ var DEFAULT_CONFIG = `${JSON.stringify({ targets: ["claude-code"] }, null, 2)}
31
+ `;
32
+ var errorMessage = (err) => err instanceof Error ? err.message : String(err);
33
+ var skipped = (reason) => ({
34
+ outcome: "skipped",
35
+ reason,
36
+ text: `${C.dim}- Agent setup skipped: ${reason}${C.reset}
37
+ `,
38
+ paths: []
39
+ });
40
+ var GitLookupError = class extends Error {
41
+ };
42
+ function gitTopLevel(cwd) {
43
+ try {
44
+ const top = execFileSync("git", ["rev-parse", "--show-toplevel"], { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
45
+ return top === "" ? void 0 : top;
46
+ } catch (err) {
47
+ const stderr = String(err.stderr ?? "");
48
+ if (/not a git repository/i.test(stderr)) return void 0;
49
+ throw new GitLookupError(`git could not report the repository root: ${(stderr.trim() || errorMessage(err)).split("\n")[0]}`);
50
+ }
51
+ }
52
+ function contextComesFromControlPlane(repoRoot) {
53
+ if (pinExists(repoRoot)) return true;
54
+ const gitCommonDir = resolveGitCommonDir(defaultGitRunner, repoRoot);
55
+ if (gitCommonDir === void 0) throw new GitLookupError("git could not report this repository's git directory");
56
+ return readContextManifest(gitCommonDir) !== void 0;
57
+ }
58
+ function mcpReplacementLosesNothing(existing, content) {
59
+ try {
60
+ const have = JSON.parse(existing);
61
+ const next = JSON.parse(content);
62
+ return Object.entries(have).every(([key, value]) => {
63
+ if (key !== "mcpServers") return JSON.stringify(next[key]) === JSON.stringify(value);
64
+ const nextServers = next.mcpServers ?? {};
65
+ return Object.entries(value ?? {}).every(([name, entry]) => JSON.stringify(nextServers[name]) === JSON.stringify(entry));
66
+ });
67
+ } catch {
68
+ return false;
69
+ }
70
+ }
71
+ function keepHandWritten(createdSources) {
72
+ return (output) => {
73
+ if (output.kind === "copy") return createdSources.has(output.source);
74
+ if (output.path.endsWith("mcp.json")) return !mcpReplacementLosesNothing(output.existing, output.content);
75
+ return generatedHeaderSource(output.existing) === null;
76
+ };
77
+ }
78
+ function runAgentSetup(opts) {
79
+ let repoRoot;
80
+ try {
81
+ repoRoot = gitTopLevel(opts.cwd);
82
+ if (repoRoot === void 0) return skipped("not a git repository");
83
+ if (contextComesFromControlPlane(repoRoot)) {
84
+ return skipped("this repository's context comes from a control plane and arrives at session start");
85
+ }
86
+ } catch (err) {
87
+ return failed(errorMessage(err), /* @__PURE__ */ new Set(), []);
88
+ }
89
+ const written = /* @__PURE__ */ new Set();
90
+ const kept = [];
91
+ const problems = [];
92
+ const keptOutputs = /* @__PURE__ */ new Set();
93
+ const createdSources = new Set(
94
+ [...loadTemplateFiles(), ...loadMaintainedFiles()].map((f) => f.path).filter((p) => !existsSync(join(repoRoot, p)))
95
+ );
96
+ const keep = keepHandWritten(createdSources);
97
+ try {
98
+ const pack = installStarterPack({ repoRoot });
99
+ for (const file of loadTemplateFiles().filter((f) => pack.copied.includes(f.skill))) written.add(file.path);
100
+ for (const path of pack.maintained) written.add(path);
101
+ for (const skill of pack.kept) kept.push(`${skillDir(skill)}/`);
102
+ if (pack.policy === "written") written.add(POLICY_PATH);
103
+ else kept.push(POLICY_PATH);
104
+ if (existsSync(join(repoRoot, CONFIG_PATH))) {
105
+ kept.push(CONFIG_PATH);
106
+ } else {
107
+ mkdirSync(dirname(join(repoRoot, CONFIG_PATH)), { recursive: true });
108
+ writeFileSync(join(repoRoot, CONFIG_PATH), DEFAULT_CONFIG);
109
+ written.add(CONFIG_PATH);
110
+ }
111
+ } catch (err) {
112
+ return failed(`could not install the skills: ${errorMessage(err)}`, written, kept);
113
+ }
114
+ try {
115
+ const mcp = addStdioMcpEntryIfAbsent(repoRoot);
116
+ if (mcp.added) written.add(mcp.path);
117
+ else kept.push(`${mcp.path} (already has an outerlayer server)`);
118
+ } catch (err) {
119
+ problems.push(errorMessage(err));
120
+ }
121
+ let pending;
122
+ try {
123
+ const check = runEmit({ cwd: repoRoot, check: true, quiet: true, keep });
124
+ pending = check.drift ?? [];
125
+ const emitted = runEmit({ cwd: repoRoot, quiet: true, keep });
126
+ for (const path of emitted.kept) keptOutputs.add(path);
127
+ if (emitted.exitCode !== 0) {
128
+ const detail = emitted.errors.map((e) => `${e.path}: ${e.message}`).join("; ");
129
+ problems.push(`context emit failed: ${detail}`);
130
+ } else {
131
+ for (const d of pending) if (d.kind !== "orphaned") written.add(d.path);
132
+ }
133
+ } catch (err) {
134
+ problems.push(`context emit failed: ${errorMessage(err)}`);
135
+ }
136
+ for (const path of [...keptOutputs].sort()) kept.push(`${path} (written by hand, not replaced)`);
137
+ const keptPaths = [...keptOutputs].sort();
138
+ if (problems.length > 0) {
139
+ for (const path of writtenBeforeFailure(repoRoot, pending, keep)) written.add(path);
140
+ return failed(problems.join("; "), written, kept, keptPaths);
141
+ }
142
+ const paths = [...written].sort();
143
+ return {
144
+ outcome: "done",
145
+ reason: paths.length === 0 ? "already set up" : `${paths.length} files written`,
146
+ text: renderDone(paths, kept, keptOutputs.has(MCP_OUTPUT_PATH)),
147
+ paths,
148
+ kept: keptPaths
149
+ };
150
+ }
151
+ function writtenBeforeFailure(repoRoot, pending, keep) {
152
+ if (pending === void 0 || pending.length === 0) return [];
153
+ try {
154
+ const now = new Set((runEmit({ cwd: repoRoot, check: true, quiet: true, keep }).drift ?? []).map((d) => d.path));
155
+ return pending.filter((d) => d.kind !== "orphaned" && !now.has(d.path)).map((d) => d.path);
156
+ } catch {
157
+ return [];
158
+ }
159
+ }
160
+ function failed(reason, written, kept, keptPaths = []) {
161
+ const paths = [...written].sort();
162
+ return {
163
+ outcome: "failed",
164
+ reason,
165
+ text: `${C.red}\u2717${C.reset} Agent setup: ${reason}
166
+ ${paths.length > 0 ? renderPaths(paths, kept) : ""}`,
167
+ paths,
168
+ kept: keptPaths
169
+ };
170
+ }
171
+ function renderPaths(paths, kept) {
172
+ const lines = [];
173
+ if (paths.length > 0) {
174
+ lines.push(" Written, uncommitted:");
175
+ for (const p of paths) lines.push(` ${p}`);
176
+ }
177
+ if (kept.length > 0) {
178
+ lines.push(" Kept as they were:");
179
+ for (const p of kept) lines.push(` ${C.dim}${p}${C.reset}`);
180
+ }
181
+ return `${lines.join("\n")}
182
+ `;
183
+ }
184
+ function renderDone(paths, kept, mcpKept) {
185
+ const mcpNote = mcpKept ? ` ${C.yellow}!${C.reset} The outerlayer entry was not added to .mcp.json, so its MCP server is not set up. Add it to .mcp.json by hand, or move the servers you wrote into .outerlayer/mcp.json and run \`outerlayer context emit\`.
186
+ ` : "";
187
+ const what = mcpKept ? "the OuterLayer skills" : "the OuterLayer skills and MCP server";
188
+ if (paths.length === 0) {
189
+ return `${C.green}\u2713${C.reset} Agent setup: already in place, nothing changed
190
+ ${renderPaths(paths, kept)}${mcpNote}`;
191
+ }
192
+ return `${C.green}\u2713${C.reset} Agent setup: Claude Code has ${what}
193
+ ` + renderPaths(paths, kept) + mcpNote + ` ${C.dim}\`context emit\` compiled every source under .outerlayer/ for the targets in ${CONFIG_PATH}, not only the skills.${C.reset}
194
+ `;
195
+ }
196
+
197
+ export { runAgentSetup };
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'node:module';
3
+ export { buildRepositoryImage } from './chunk-ZNA27WEV.js';
4
+ import './chunk-N5FOE5PS.js';
5
+ export { RecipeError } from './chunk-QOZKPLVJ.js';
6
+ import './chunk-ZM2IMMYN.js';
7
+ import './chunk-Z5GEFMRW.js';
8
+
9
+ createRequire(import.meta.url);
@@ -1 +1 @@
1
- {"id":"2026-09-25T19:18:18.529Z+059710d","root":"/home/runner/_work/outerlayer/outerlayer","pkg":"outerlayer"}
1
+ {"id":"2026-10-03T04:29:29.613Z+d16bdd8","root":"/home/runner/_work/outerlayer/outerlayer","pkg":"outerlayer"}
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'node:module';
3
+ import { probeCredentials } from './chunk-QY22NZUW.js';
4
+ import './chunk-DM3VFDS3.js';
5
+ import './chunk-QDQEUVUF.js';
6
+ import './chunk-ABIWDUMS.js';
7
+ import './chunk-YYYXRJUW.js';
8
+ import './chunk-CBPPJSAR.js';
9
+ import { placeholderQueues, placeholderProblem, needsClaudeCredential, claudeCredentialCheck, claimPermissionProblem } from './chunk-RFUPN6KX.js';
10
+ import './chunk-LYCDNMOH.js';
11
+ import './chunk-TBT347UY.js';
12
+ import './chunk-YFHIIH4B.js';
13
+ import './chunk-3TFPDVTI.js';
14
+ import './chunk-HZRNMGLF.js';
15
+ import './chunk-ZNA27WEV.js';
16
+ import './chunk-N5FOE5PS.js';
17
+ import './chunk-OGFGZQA3.js';
18
+ import './chunk-774HEQL6.js';
19
+ import './chunk-QOZKPLVJ.js';
20
+ import './chunk-W4SQCOZU.js';
21
+ import './chunk-B2G7JHHB.js';
22
+ import './chunk-W5FVUZXV.js';
23
+ import './chunk-BFESLKPP.js';
24
+ import './chunk-WFJ65NUD.js';
25
+ import './chunk-4C4THABW.js';
26
+ import { loadRunnerConfig, RunnerConfigError, checkHooks } from './chunk-5Y3QQRUH.js';
27
+ import './chunk-F6GFZXZ3.js';
28
+ import './chunk-AUFG23AA.js';
29
+ import './chunk-WZBHBH3J.js';
30
+ import './chunk-XDDW4FRS.js';
31
+ import './chunk-WIOZAJ2W.js';
32
+ import './chunk-WO2BXCTQ.js';
33
+ import './chunk-EMY4I27X.js';
34
+ import './chunk-ZJSNLVCE.js';
35
+ import './chunk-BCLJSQCV.js';
36
+ import './chunk-BOLTI6LR.js';
37
+ import './chunk-EABW6AJQ.js';
38
+ import './chunk-RA3O54FC.js';
39
+ import './chunk-M5POMKKW.js';
40
+ import './chunk-ZM2IMMYN.js';
41
+ import './chunk-XCCLFVXM.js';
42
+ import './chunk-BJ3KTMDH.js';
43
+ import './chunk-VNVZDWO3.js';
44
+ import './chunk-Z5GEFMRW.js';
45
+
46
+ createRequire(import.meta.url);
47
+
48
+ // src/runner/check.ts
49
+ async function runRunnerCheck(configPath, deps = {}) {
50
+ let config;
51
+ try {
52
+ config = loadRunnerConfig(configPath);
53
+ } catch (err) {
54
+ return { ok: false, check: "config", message: err instanceof RunnerConfigError ? err.message : String(err) };
55
+ }
56
+ const hookProblem = checkHooks(config, deps.env ?? process.env);
57
+ if (hookProblem) return { ok: false, check: "hooks", message: `${configPath}: ${hookProblem}` };
58
+ const placeholders = placeholderQueues(config);
59
+ if (placeholders.length > 0) {
60
+ return { ok: false, check: "placeholders", message: `${configPath}: ${placeholderProblem(placeholders)}` };
61
+ }
62
+ if (needsClaudeCredential(config.hooks)) {
63
+ const credential = claudeCredentialCheck(deps.env ?? process.env);
64
+ if (credential.status === "fail") {
65
+ return { ok: false, check: "claude credential", message: `${credential.detail}. ${credential.fix}` };
66
+ }
67
+ }
68
+ const probe = await probeCredentials(config, deps.fetchImpl);
69
+ if (!probe.ok && probe.fatal) return { ok: false, check: "credentials", message: probe.reason };
70
+ const claimProblem = await claimPermissionProblem(config, deps.fetchImpl);
71
+ if (claimProblem.status === "lacks") {
72
+ return { ok: false, check: "claim permission", message: `${configPath}: ${claimProblem.problem}` };
73
+ }
74
+ const unverifiedReason = probe.ok ? claimProblem.reason : probe.reason;
75
+ const lines = [
76
+ `config ${configPath}`,
77
+ `host ${config.host}`,
78
+ `queues ${config.queues.join(", ")}`,
79
+ `concurrency ${config.concurrency}`,
80
+ `poll ${config.pollSeconds}s`,
81
+ `time limit ${config.timeLimitMinutes}m`,
82
+ `idle limit ${config.idleLimitMinutes}m`,
83
+ `auto update ${config.autoUpdate ? "on" : "off"}`,
84
+ ...config.queues.map((queue) => `command ${queue.padEnd(11)} ${config.commands[queue] ?? "(none)"}`),
85
+ `hook provision ${config.hooks.provision}`,
86
+ `hook cleanup ${config.hooks.cleanup}`,
87
+ `hook report ${config.hooks.report ?? "(none)"}`,
88
+ `repos include ${config.repos?.include?.join(", ") ?? "(all)"}`,
89
+ `repos exclude ${config.repos?.exclude?.join(", ") ?? "(none)"}`
90
+ ];
91
+ return { ok: true, lines, unverifiedReason, summary: `${config.url} \xB7 app ${config.appId}` };
92
+ }
93
+
94
+ export { runRunnerCheck };
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'node:module';
3
+ import { MIN_ENGINE_MAJOR } from './chunk-HZRNMGLF.js';
4
+ import { firecrackerArch, MICROVM_ARCHES, guestAssetsPinned } from './chunk-ZNA27WEV.js';
5
+ import { spawnSync } from 'child_process';
6
+ import { existsSync, accessSync, constants, statSync, readFileSync } from 'fs';
7
+
8
+ createRequire(import.meta.url);
9
+ function groupName(gid) {
10
+ try {
11
+ for (const line of readFileSync("/etc/group", "utf8").split("\n")) {
12
+ const [name, , id] = line.split(":");
13
+ if (name !== void 0 && Number(id) === gid) return name;
14
+ }
15
+ } catch {
16
+ }
17
+ return String(gid);
18
+ }
19
+ function detectKvm(device) {
20
+ if (!existsSync(device)) return { state: "absent" };
21
+ try {
22
+ accessSync(device, constants.R_OK | constants.W_OK);
23
+ return { state: "usable" };
24
+ } catch {
25
+ return { state: "no-permission", group: groupName(statSync(device).gid) };
26
+ }
27
+ }
28
+ function readProcVersion() {
29
+ try {
30
+ return readFileSync("/proc/version", "utf8");
31
+ } catch {
32
+ return void 0;
33
+ }
34
+ }
35
+ function docker(args) {
36
+ const result = spawnSync("docker", args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 15e3 });
37
+ const out = result.status === 0 ? result.stdout.trim() : "";
38
+ return out === "" ? void 0 : out;
39
+ }
40
+ function dockerFailure() {
41
+ const result = spawnSync("docker", ["version", "--format", "{{.Server.Version}}"], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], timeout: 15e3 });
42
+ if (result.error?.code === "ENOENT") return void 0;
43
+ return /permission denied/i.test(result.stderr ?? "") ? "permission" : "daemon";
44
+ }
45
+ function platformOf(probes) {
46
+ const platform = probes.platform ?? process.platform;
47
+ if (platform === "win32") return "windows";
48
+ if (platform === "darwin") return "darwin";
49
+ if (platform !== "linux") return "other";
50
+ return /microsoft/i.test((probes.procVersion ?? readProcVersion)() ?? "") ? "wsl2" : "linux";
51
+ }
52
+ function detectMachine(probes = {}) {
53
+ const platform = platformOf(probes);
54
+ const kvm = (probes.kvm ?? (() => detectKvm(probes.kvmDevice ?? "/dev/kvm")))();
55
+ const arch = firecrackerArch(probes.nodeArch ?? process.arch);
56
+ const version = (probes.dockerVersion ?? (() => docker(["version", "--format", "{{.Server.Version}}"])))();
57
+ if (version === void 0) {
58
+ const failure = (probes.dockerFailure ?? (probes.dockerVersion === void 0 ? dockerFailure : () => void 0))();
59
+ return { platform, docker: failure === void 0 ? { kind: "none" } : { kind: "unreachable", reason: failure }, kvm, arch };
60
+ }
61
+ const system = (probes.dockerOperatingSystem ?? (() => docker(["info", "--format", "{{.OperatingSystem}}"])))() ?? "";
62
+ return { platform, docker: { kind: /docker desktop/i.test(system) ? "desktop" : "engine", version }, kvm, arch };
63
+ }
64
+ function supportsContainerBuilds(machine) {
65
+ if (machine.platform !== "linux" && machine.platform !== "wsl2") return false;
66
+ return machine.docker.kind === "engine" && Number.parseInt(machine.docker.version ?? "", 10) >= MIN_ENGINE_MAJOR;
67
+ }
68
+ function supportsMicroVmBuilds(machine) {
69
+ return machine.platform === "linux" && machine.kvm?.state === "usable" && supportsContainerBuilds(machine) && microVmAssetsPinned(machine);
70
+ }
71
+ function microVmArchSupported(machine) {
72
+ return MICROVM_ARCHES.includes(machine.arch ?? "");
73
+ }
74
+ function microVmAssetsPinned(machine) {
75
+ return microVmArchSupported(machine) && guestAssetsPinned(machine.arch);
76
+ }
77
+ function reachDockerStep(machine) {
78
+ if (machine.docker.kind !== "unreachable") return void 0;
79
+ return machine.docker.reason === "permission" ? "give this user access to Docker Engine's socket (add it to the docker group, then log in again)" : "start Docker Engine's daemon (for example, sudo systemctl start docker)";
80
+ }
81
+ function describeMachineDocker(machine) {
82
+ const { kind, version, reason } = machine.docker;
83
+ const where = machine.platform === "windows" ? "Windows" : machine.platform === "darwin" ? "macOS" : machine.platform === "wsl2" ? "WSL2" : "Linux";
84
+ if (kind === "desktop") return `Docker Desktop ${version ?? ""} on ${where}, which cannot pass a Unix socket into a container`.replace(" ", " ");
85
+ if (kind === "none") return `no Docker on ${where}`;
86
+ if (kind === "unreachable") {
87
+ const problem = reason === "permission" ? "this user has no permission on its socket" : "its daemon is not running";
88
+ return `a docker command on ${where} that cannot reach Docker Engine (${problem})`;
89
+ }
90
+ if (machine.platform !== "linux" && machine.platform !== "wsl2") return `Docker Engine ${version ?? ""} on ${where}, which cannot isolate a build`.replace(" ", " ");
91
+ return `Docker Engine ${version ?? "of no known version"}, older than the ${MIN_ENGINE_MAJOR} that container builds need`;
92
+ }
93
+
94
+ export { describeMachineDocker, detectMachine, microVmArchSupported, microVmAssetsPinned, reachDockerStep, supportsContainerBuilds, supportsMicroVmBuilds };