@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
@@ -1,150 +0,0 @@
1
- #!/usr/bin/env node
2
- import { createRequire } from 'node:module';
3
- import { homeFromEnv } from './chunk-VNVZDWO3.js';
4
- import { existsSync, statSync, accessSync, constants, readFileSync } from 'fs';
5
- import { hostname } from 'os';
6
- import { join } from 'path';
7
-
8
- createRequire(import.meta.url);
9
- var RUNNER_QUEUES = ["implement", "amend"];
10
- var RunnerConfigError = class extends Error {
11
- };
12
- var DEFAULT_CONCURRENCY = 1;
13
- var DEFAULT_POLL_SECONDS = 60;
14
- var DEFAULT_TIME_LIMIT_MINUTES = 480;
15
- var DEFAULT_IDLE_LIMIT_MINUTES = 30;
16
- function defaultRunnerConfigPath(home = homeFromEnv()) {
17
- return join(home, ".outerlayer", "config.json");
18
- }
19
- function fail(configPath, key, reason) {
20
- throw new RunnerConfigError(`${configPath}: ${key} ${reason}`);
21
- }
22
- function readConfigFile(configPath) {
23
- if (!existsSync(configPath)) return {};
24
- const text = readFileSync(configPath, "utf8");
25
- try {
26
- return JSON.parse(text);
27
- } catch (err) {
28
- throw new RunnerConfigError(
29
- `${configPath}: is not valid JSON: ${err instanceof Error ? err.message : String(err)}`
30
- );
31
- }
32
- }
33
- var RUNNER_KEYS = [
34
- "host",
35
- "queues",
36
- "concurrency",
37
- "pollSeconds",
38
- "timeLimitMinutes",
39
- "idleLimitMinutes",
40
- "commands",
41
- "hooks"
42
- ];
43
- var RUNNER_NUMBERS = [
44
- { key: "concurrency", min: 1 },
45
- // Below five seconds a runner is asking the gateway a question it has
46
- // just been answered.
47
- { key: "pollSeconds", min: 5 },
48
- { key: "timeLimitMinutes", min: 1 },
49
- { key: "idleLimitMinutes", min: 1 }
50
- ];
51
- function nearestKey(unknown, accepted) {
52
- const lower = unknown.toLowerCase();
53
- let best;
54
- for (const key of accepted) {
55
- const distance = editDistance(lower, key.toLowerCase());
56
- if (distance <= Math.max(1, Math.floor(key.length / 4)) && (!best || distance < best.distance)) {
57
- best = { key, distance };
58
- }
59
- }
60
- return best?.key;
61
- }
62
- function editDistance(a, b) {
63
- const rows = Array.from({ length: a.length + 1 }, (_unused, i) => [i, ...Array(b.length).fill(0)]);
64
- for (let j = 0; j <= b.length; j++) rows[0][j] = j;
65
- for (let i = 1; i <= a.length; i++) {
66
- for (let j = 1; j <= b.length; j++) {
67
- rows[i][j] = Math.min(
68
- rows[i - 1][j] + 1,
69
- rows[i][j - 1] + 1,
70
- rows[i - 1][j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1)
71
- );
72
- }
73
- }
74
- return rows[a.length][b.length];
75
- }
76
- function checkHooks(config) {
77
- const named = [
78
- ["runner.hooks.provision", config.hooks.provision],
79
- ["runner.hooks.cleanup", config.hooks.cleanup],
80
- ["runner.hooks.report", config.hooks.report]
81
- ];
82
- for (const [key, path] of named) {
83
- if (path === void 0) continue;
84
- if (path.includes("$") || path.startsWith("~")) {
85
- return `${key}: ${path} \u2014 hooks are executable paths, not shell lines; $HOME and ~ are not expanded`;
86
- }
87
- if (!existsSync(path)) return `${key}: ${path} does not exist`;
88
- if (!statSync(path).isFile()) return `${key}: ${path} is not a file`;
89
- try {
90
- accessSync(path, constants.X_OK);
91
- } catch {
92
- return `${key}: ${path} is not executable`;
93
- }
94
- }
95
- return void 0;
96
- }
97
- function loadRunnerConfig(configPath) {
98
- const config = readConfigFile(configPath);
99
- if (!config.url || !config.apiKey || !config.appId) {
100
- fail(configPath, "url/apiKey/appId", "missing \u2014 run `outerlayer login` against this config file first");
101
- }
102
- const runner = config.runner;
103
- if (!runner) {
104
- fail(configPath, "runner", "block is missing");
105
- }
106
- for (const key of Object.keys(runner)) {
107
- if (RUNNER_KEYS.includes(key)) continue;
108
- const near = nearestKey(key, RUNNER_KEYS);
109
- fail(configPath, `runner.${key}`, near ? `is not a key this block accepts; did you mean ${near}?` : "is not a key this block accepts");
110
- }
111
- for (const { key, min } of RUNNER_NUMBERS) {
112
- const value = runner[key];
113
- if (value === void 0) continue;
114
- if (typeof value !== "number" || !Number.isInteger(value) || value < min) {
115
- fail(configPath, `runner.${key}`, `must be a whole number of at least ${min}`);
116
- }
117
- }
118
- const queues = runner.queues && runner.queues.length > 0 ? runner.queues : [...RUNNER_QUEUES];
119
- for (const queue of queues) {
120
- if (!RUNNER_QUEUES.includes(queue)) {
121
- fail(configPath, "runner.queues", `names an unknown queue "${queue}" \u2014 must be implement or amend`);
122
- }
123
- if (!runner.commands?.[queue]) {
124
- fail(configPath, `runner.commands.${queue}`, "is required for an enabled queue");
125
- }
126
- }
127
- if (!runner.hooks?.provision) fail(configPath, "runner.hooks.provision", "is required");
128
- if (!runner.hooks?.cleanup) fail(configPath, "runner.hooks.cleanup", "is required");
129
- return {
130
- configPath,
131
- url: config.url,
132
- apiKey: config.apiKey,
133
- appId: config.appId,
134
- repos: config.repos,
135
- host: runner.host ?? hostname(),
136
- queues,
137
- concurrency: runner.concurrency ?? DEFAULT_CONCURRENCY,
138
- pollSeconds: runner.pollSeconds ?? DEFAULT_POLL_SECONDS,
139
- timeLimitMinutes: runner.timeLimitMinutes ?? DEFAULT_TIME_LIMIT_MINUTES,
140
- idleLimitMinutes: runner.idleLimitMinutes ?? DEFAULT_IDLE_LIMIT_MINUTES,
141
- commands: runner.commands ?? {},
142
- hooks: {
143
- provision: runner.hooks.provision,
144
- cleanup: runner.hooks.cleanup,
145
- report: runner.hooks.report
146
- }
147
- };
148
- }
149
-
150
- export { RunnerConfigError, checkHooks, defaultRunnerConfigPath, loadRunnerConfig };
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env node
2
- import { createRequire } from 'node:module';
3
- export { AUTO_SYNC_DEBOUNCE_MS, WORK_ADD_RETRIES, contextPossiblyGoverned, handleWorkLaunch, hookFiredMarkerPath, maybeAutoSync, runHookFast, spoolDir } from './chunk-NXLLURA4.js';
4
- import './chunk-I3ETLSNE.js';
5
- import './chunk-VNVZDWO3.js';
6
- import './chunk-Z5GEFMRW.js';
7
-
8
- createRequire(import.meta.url);
@@ -1,176 +0,0 @@
1
- #!/usr/bin/env node
2
- import { createRequire } from 'node:module';
3
- import './chunk-Z5GEFMRW.js';
4
- import { statSync, existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
5
- import { join, dirname } from 'path';
6
-
7
- createRequire(import.meta.url);
8
- var GREEN = "\x1B[32m";
9
- var DIM = "\x1B[2m";
10
- var YELLOW = "\x1B[33m";
11
- var RESET = "\x1B[0m";
12
- var ImportCaptureError = class extends Error {
13
- };
14
- var SKILL_PATH = ".outerlayer/skills/emitting-evidence/SKILL.md";
15
- var SNIPPET_PATH = ".outerlayer/skills/emitting-evidence/references/agents-snippet.md";
16
- var AGENTS_MD_PATH = ".outerlayer/AGENTS.md";
17
- var AGENTS_MD_MARKER = "<!-- outerlayer:capture-pack -->";
18
- var SKILL_CONTENT = `---
19
- name: emitting-evidence
20
- description: >
21
- Emit artifacts \u2014 screenshots, recordings, reports, logs \u2014 as proof that a
22
- change works, bound to the pull request via \`outerlayer emit artifact\`.
23
- Use when finishing work a spec criterion or tracked requirement covers,
24
- when asked to "provide evidence", "attach a screenshot", or "prove it
25
- works", and in CI steps that produce verifiable output.
26
- ---
27
-
28
- An artifact is an exhibit: evidence *of* a specific change, anchored to the
29
- pull request and rendered in its evidence comment. Emit the few exhibits the
30
- spec asks for \u2014 a reviewer should see the change working, not a gallery.
31
-
32
- ## When to capture
33
-
34
- Capture AFTER the state exists, never before: run the app, the test, or the
35
- flow first, and shoot the working result. A screenshot of code is not
36
- evidence; a screenshot of the rendered page is. If the state takes setup
37
- (seed data, a logged-in user), finish the setup, verify by eye, then capture.
38
-
39
- ## Per-kind mechanics
40
-
41
- Kind is inferred from the file's media type \u2014 name files honestly. Every
42
- artifact shares one upload cap of 8 MiB, whatever its kind:
43
-
44
- - **screenshot** (\`.png\`, \`.jpg\`) \u2014 one focused window or region showing the
45
- proven state. Crop noise; keep enough chrome (URL bar, test summary line)
46
- to show it is real.
47
- - **video** (\`.webm\`, \`.mp4\`) \u2014 a short recording of the flow, start to
48
- outcome. Video is the kind most likely to hit the 8 MiB cap: trim dead
49
- time, prefer webm.
50
- - **report** (\`.html\`, \`.pdf\`) \u2014 generated reports: coverage, benchmark,
51
- audit output. Emit the file the tool produced, unedited.
52
- - **log** (\`.txt\`, \`.log\`) \u2014 command output proving a run happened: test
53
- runs, migrations, gate output. Pipe to a file and emit that file.
54
-
55
- Anything else uploads as plain \`file\` \u2014 it is never guessed into a stronger
56
- kind, so a \`.mov\` will NOT count where a video is required; convert first.
57
-
58
- ## Captions
59
-
60
- One sentence, present tense, saying what the exhibit shows and what that
61
- proves: "Signup blocked for a disallowed domain \u2014 the 403 page renders."
62
- Never put secrets, tokens, or personal data in the caption \u2014 or the pixels.
63
-
64
- ## Binding with --for
65
-
66
- When a criterion is the reason you captured, bind it:
67
-
68
- outerlayer emit artifact shot.png --caption "\u2026" --for LOGIN-01
69
-
70
- The id comes from your team's own spec or tracker \u2014 wherever criteria for
71
- this change are written down. The PR comment's Evidence section shows the
72
- id next to the artifact, and the dashboard's Floor lists it against the
73
- work item \u2014 but deciding whether a given criterion still needs proof, and
74
- of what kind, is your team's own check to write, typically a custom
75
- validator recorded with \`outerlayer emit\`.
76
-
77
- ## The noise rule
78
-
79
- Satisfy the declared proofs; don't document everything. One exhibit per
80
- criterion is the norm. An unrequested artifact is worth emitting only when
81
- it would change how a reviewer reads the diff. When in doubt, leave it out \u2014
82
- evidence works because there is little of it.
83
-
84
- ## Mechanics
85
-
86
- Inside a recorded session, just run the command \u2014 the artifact spools
87
- locally and uploads with the next \`outerlayer sync\`, bound to this session
88
- and turn. In CI, run it after the step that produced the file (repo and PR
89
- come from the CI environment). From a plain machine it anchors through the
90
- git checkout, or pass \`--pr <n>\` explicitly. If there is nothing to attach
91
- to, the command refuses \u2014 emit from the work, not from nowhere.
92
- `;
93
- var AGENTS_SNIPPET_CONTENT = `${AGENTS_MD_MARKER}
94
- ## Emitting evidence
95
-
96
- When you finish work a spec criterion or tracked requirement covers,
97
- capture the working state and emit it:
98
-
99
- outerlayer emit artifact <file> --caption "what it shows" [--for <criterion-id>] [--pr <n>]
100
-
101
- Rules: capture AFTER the state exists (run it, then shoot it); kind comes
102
- from the file type (png/jpg screenshot, webm/mp4 video, html/pdf report,
103
- txt/log log \u2014 anything else is a plain file); every artifact caps at 8
104
- MiB, and video is the kind most likely to hit it; captions are one
105
- present-tense sentence with no secrets; bind an artifact to the id of the
106
- requirement it proves \u2014 \`--for <id>\`, from the team's own spec or
107
- tracker \u2014 and pick the kind that shows the state best; the platform
108
- records the kind and the id and shows them next to the artifact; satisfy
109
- what's asked for and stop \u2014 don't document everything. Inside a recorded
110
- session the artifact uploads on the next \`outerlayer sync\`; in CI it
111
- anchors via the CI environment; otherwise the git checkout or \`--pr\`
112
- anchors it, and with nothing to attach to the command refuses.
113
- `;
114
- function writeOutFile(repoRoot, relPath, content) {
115
- const abs = join(repoRoot, relPath);
116
- mkdirSync(dirname(abs), { recursive: true });
117
- writeFileSync(abs, content);
118
- }
119
- function runImportCapture(opts = {}) {
120
- const cwd = opts.cwd ?? process.cwd();
121
- let cwdStat;
122
- try {
123
- cwdStat = statSync(cwd);
124
- } catch {
125
- throw new ImportCaptureError(`no such directory: ${cwd}`);
126
- }
127
- if (!cwdStat.isDirectory()) throw new ImportCaptureError(`not a directory: ${cwd}`);
128
- const skillAbs = join(cwd, SKILL_PATH);
129
- if (existsSync(skillAbs) && readFileSync(skillAbs, "utf8") !== SKILL_CONTENT) {
130
- throw new ImportCaptureError(
131
- `refusing to import \u2014 ${SKILL_PATH} already exists with local edits (never overwritten; remove it first to reinstall)`
132
- );
133
- }
134
- const files = [];
135
- for (const [relPath, content] of [
136
- [SKILL_PATH, SKILL_CONTENT],
137
- [SNIPPET_PATH, AGENTS_SNIPPET_CONTENT]
138
- ]) {
139
- if (existsSync(join(cwd, relPath))) continue;
140
- writeOutFile(cwd, relPath, content);
141
- files.push(relPath);
142
- }
143
- let appendedAgentsMd = false;
144
- const agentsMdAbs = join(cwd, AGENTS_MD_PATH);
145
- if (existsSync(agentsMdAbs)) {
146
- const existing = readFileSync(agentsMdAbs, "utf8");
147
- if (!existing.includes(AGENTS_MD_MARKER)) {
148
- writeFileSync(agentsMdAbs, existing + "\n" + AGENTS_SNIPPET_CONTENT);
149
- appendedAgentsMd = true;
150
- }
151
- }
152
- let output;
153
- if (opts.json) {
154
- output = JSON.stringify({ files, appendedAgentsMd });
155
- } else {
156
- const lines = files.map((f) => `${GREEN}\u2713${RESET} wrote ${f}`);
157
- if (appendedAgentsMd) lines.push(`${GREEN}\u2713${RESET} appended the evidence snippet to ${AGENTS_MD_PATH}`);
158
- if (files.length === 0 && !appendedAgentsMd) {
159
- lines.push(`${DIM}capture pack already installed \u2014 nothing to write${RESET}`);
160
- }
161
- lines.push(`Run ${YELLOW}\`outerlayer emit\`${RESET} to compile the pack into your agent's native files.`);
162
- output = lines.join("\n");
163
- }
164
- if (!opts.quiet) {
165
- process.stdout.write(output + "\n");
166
- if (!opts.json && !appendedAgentsMd && !existsSync(agentsMdAbs)) {
167
- process.stdout.write(
168
- `${DIM} no ${AGENTS_MD_PATH} to append to \u2014 paste ${SNIPPET_PATH} into your instructions file if your harness reads no skills${RESET}
169
- `
170
- );
171
- }
172
- }
173
- return { files, appendedAgentsMd, output };
174
- }
175
-
176
- export { ImportCaptureError, runImportCapture };
@@ -1,103 +0,0 @@
1
- #!/usr/bin/env node
2
- import { createRequire } from 'node:module';
3
- import { runnerDir, RUNNER_DIR_MODE, RUNNER_FILE_MODE } from './chunk-JJP7YLMN.js';
4
- import './chunk-Z5GEFMRW.js';
5
- import { existsSync, readFileSync, mkdirSync, writeFileSync, chmodSync } from 'fs';
6
- import { join, dirname } from 'path';
7
-
8
- createRequire(import.meta.url);
9
- var RunnerInitError = class extends Error {
10
- };
11
- var PLACEHOLDER_COMMAND = "echo 'edit runner.commands in this config file: it must start your agent on item $OUTERLAYER_WORK' >&2; exit 1";
12
- var PROVISION = `#!/bin/sh
13
- # provision.sh \u2014 makes the place this job runs in.
14
- # Runs in $OUTERLAYER_JOB_DIR, before the command. Exit 75 to decline the
15
- # item for now; any other non-zero exit fails the job.
16
- set -e
17
-
18
- if ! df -Pk . | awk 'NR==2 {exit ($4 < 6*1024*1024)}'; then
19
- exit 75 # not enough disk free right now
20
- fi
21
-
22
- repo=$(outerlayer work status --item "$OUTERLAYER_WORK" --json \\
23
- | jq -r '.trackerKey | split("#")[0]')
24
- workdir="$OUTERLAYER_JOB_DIR/work"
25
- git clone --depth 1 "https://github.com/$repo.git" "$workdir"
26
-
27
- echo "workdir=$workdir" > "$OUTERLAYER_JOB_DIR/provision.out"
28
- `;
29
- var CLEANUP = `#!/bin/sh
30
- # cleanup.sh \u2014 runs after the command, however it ended, and on recovery.
31
- # $OUTERLAYER_OUTCOME is the attempt's fate, not a verdict on the work: a
32
- # command that ran and exited says "ok" whatever its exit status.
33
- workdir="$OUTERLAYER_JOB_DIR/work"
34
-
35
- if grep -q "pull request opened" "$OUTERLAYER_LOG" 2>/dev/null; then
36
- rm -rf "$workdir"
37
- else
38
- echo "kept $workdir for inspection (outcome: $OUTERLAYER_OUTCOME)"
39
- fi
40
- `;
41
- var REPORT = `#!/bin/sh
42
- # report.sh \u2014 optional. Runs once at the start of a job and once at the end.
43
- # At the start there is no working directory yet and $OUTERLAYER_LOG is
44
- # unset; the command's log is always $OUTERLAYER_JOB_DIR/command.log.
45
- case "$OUTERLAYER_PHASE" in
46
- start) echo "starting #$OUTERLAYER_WORK" ;;
47
- end) echo "#$OUTERLAYER_WORK ended: $OUTERLAYER_OUTCOME" ;;
48
- esac
49
- `;
50
- function runRunnerInit(configPath) {
51
- if (!existsSync(configPath)) {
52
- throw new RunnerInitError(
53
- `${configPath}: no config file. Run \`outerlayer login --url <your gateway>\` first, then this command.`
54
- );
55
- }
56
- let config;
57
- try {
58
- config = JSON.parse(readFileSync(configPath, "utf8"));
59
- } catch (err) {
60
- throw new RunnerInitError(`${configPath}: not valid JSON (${err instanceof Error ? err.message : String(err)})`);
61
- }
62
- if (config.runner !== void 0) {
63
- throw new RunnerInitError(`${configPath}: a runner block is already here. Edit it, or remove it and run this again.`);
64
- }
65
- const hooksDir = join(runnerDir(configPath), "hooks");
66
- mkdirSync(hooksDir, { recursive: true, mode: RUNNER_DIR_MODE });
67
- const written = [];
68
- for (const [name, body] of [
69
- ["provision.sh", PROVISION],
70
- ["cleanup.sh", CLEANUP],
71
- ["report.sh", REPORT]
72
- ]) {
73
- const path = join(hooksDir, name);
74
- if (existsSync(path)) continue;
75
- writeFileSync(path, body, { mode: 448 });
76
- chmodSync(path, 448);
77
- written.push(path);
78
- }
79
- config.runner = {
80
- queues: ["implement", "amend"],
81
- concurrency: 1,
82
- pollSeconds: 60,
83
- timeLimitMinutes: 480,
84
- idleLimitMinutes: 30,
85
- // A placeholder that fails loudly. A command line that quietly did
86
- // nothing would claim an item, run, exit zero, and record `ok` —
87
- // every attempt reading as a success that produced nothing.
88
- commands: {
89
- implement: PLACEHOLDER_COMMAND,
90
- amend: PLACEHOLDER_COMMAND
91
- },
92
- hooks: {
93
- provision: join(hooksDir, "provision.sh"),
94
- cleanup: join(hooksDir, "cleanup.sh")
95
- }
96
- };
97
- mkdirSync(dirname(configPath), { recursive: true });
98
- writeFileSync(configPath, `${JSON.stringify(config, null, 2)}
99
- `, { mode: RUNNER_FILE_MODE });
100
- return { configPath, hooksDir, hooks: written };
101
- }
102
-
103
- export { RunnerInitError, runRunnerInit };
@@ -1,62 +0,0 @@
1
- #!/usr/bin/env node
2
- import { createRequire } from 'node:module';
3
- import { writeCloudConfig, readCloudConfig, cloudConfigPath } from './chunk-TFUIDMOB.js';
4
- import { homeFromEnv } from './chunk-VNVZDWO3.js';
5
- import './chunk-Z5GEFMRW.js';
6
- import { createInterface } from 'readline';
7
- import { Writable } from 'stream';
8
-
9
- createRequire(import.meta.url);
10
- var LoginError = class extends Error {
11
- };
12
- function runLogin(opts) {
13
- const apiKey = opts.apiKey.trim();
14
- if (apiKey.length === 0) throw new LoginError("no API key given \u2014 pipe it on stdin or enter it at the prompt");
15
- if (/\s/.test(apiKey)) throw new LoginError("the API key must be a single token with no spaces or line breaks");
16
- const url = opts.url?.trim() || void 0;
17
- const appId = opts.appId?.trim() || void 0;
18
- const home = opts.home ?? homeFromEnv();
19
- writeCloudConfig(
20
- {
21
- apiKey,
22
- ...url ? { url } : {},
23
- ...appId ? { appId } : {}
24
- },
25
- home
26
- );
27
- const saved = readCloudConfig(home);
28
- return { path: cloudConfigPath(home), url: saved.url, appId: saved.appId };
29
- }
30
- async function readAllStdin(stdin) {
31
- const chunks = [];
32
- for await (const chunk of stdin) chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
33
- return Buffer.concat(chunks).toString("utf8");
34
- }
35
- function promptHidden(stdin, stdout, question) {
36
- return new Promise((resolve, reject) => {
37
- const muted = new Writable({
38
- write(_chunk, _encoding, callback) {
39
- callback();
40
- }
41
- });
42
- const rl = createInterface({ input: stdin, output: muted, terminal: true });
43
- stdout.write(question);
44
- rl.question("", (answer) => {
45
- rl.close();
46
- stdout.write("\n");
47
- resolve(answer);
48
- });
49
- rl.on("error", reject);
50
- });
51
- }
52
- async function readApiKey(input = {}) {
53
- const stdin = input.stdin ?? process.stdin;
54
- const stdout = input.stdout ?? process.stdout;
55
- if (!stdin.isTTY) return (await readAllStdin(stdin)).trim();
56
- if (input.noInput) {
57
- throw new LoginError('no API key on stdin and --no-input forbids prompting \u2014 pipe the key: echo "$KEY" | outerlayer login');
58
- }
59
- return (await promptHidden(stdin, stdout, "API key (input hidden): ")).trim();
60
- }
61
-
62
- export { LoginError, readApiKey, runLogin };