@outerlayer/cli 0.20.0 → 0.22.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.
- package/CHANGELOG.md +33 -0
- package/README.md +15 -5
- package/dist/{agent-setup-AM42SA2D.js → agent-setup-LTSLXPS3.js} +13 -12
- package/dist/build-6WRNZPVR.js +12 -0
- package/dist/build-command-ND2ENDBS.js +50 -0
- package/dist/build-info.json +1 -1
- package/dist/{builder-C4QNTYMS.js → builder-G3MLDMTI.js} +7 -5
- package/dist/{check-BR5D27CX.js → check-UYKEMQNR.js} +48 -50
- package/dist/{chunk-AMFWYESY.js → chunk-276D7BJ7.js} +1 -1
- package/dist/{chunk-X5H2WYMB.js → chunk-2Q3PZ42G.js} +1 -1
- package/dist/{chunk-3CWUOFV6.js → chunk-3AZYLAJC.js} +13 -8
- package/dist/{chunk-LRG3ZMZD.js → chunk-3V5C64Y5.js} +4 -4
- package/dist/{chunk-QOZKPLVJ.js → chunk-3W333NFO.js} +40 -1
- package/dist/{chunk-7YP7T6GV.js → chunk-4AVDD7VX.js} +2 -2
- package/dist/{chunk-KYS3TQAG.js → chunk-56X5TOJY.js} +4 -4
- package/dist/{chunk-3ZR7Y23Y.js → chunk-5NJIWKD4.js} +650 -70
- package/dist/{chunk-NNLA7LLQ.js → chunk-5OCIHFGE.js} +29 -5
- package/dist/{chunk-EAEELQVQ.js → chunk-6YBLN4TR.js} +5 -5
- package/dist/{chunk-AD3EYI7P.js → chunk-7FEY33MY.js} +259 -56
- package/dist/{chunk-QNLX477Q.js → chunk-AEKSX4EY.js} +165 -72
- package/dist/{chunk-FBTGMSQF.js → chunk-BET347JZ.js} +16 -9
- package/dist/{chunk-JHGEBPLE.js → chunk-BOMRFYTZ.js} +3 -3
- package/dist/{chunk-MTP2QEVO.js → chunk-CQAEV3Z5.js} +1 -1
- package/dist/{chunk-XZ4H4X5F.js → chunk-CUFFDEF3.js} +15 -8
- package/dist/{chunk-BM63HB2M.js → chunk-EAQSOE62.js} +2 -2
- package/dist/{chunk-VYAL7PUE.js → chunk-EFB3FGLC.js} +1 -1
- package/dist/{chunk-GEMQLBMW.js → chunk-ENTHEUKT.js} +6 -6
- package/dist/{chunk-B6SCN5NY.js → chunk-FOCML5FI.js} +175 -44
- package/dist/{chunk-LW7G5TJB.js → chunk-FRNYCJ4W.js} +2 -2
- package/dist/chunk-GISNHSYL.js +41 -0
- package/dist/{chunk-G2YNJIGF.js → chunk-HCVLP7XA.js} +10 -1
- package/dist/{chunk-N2PXGQRL.js → chunk-IHNQWF6F.js} +1 -1
- package/dist/{chunk-CG36IONK.js → chunk-JRCSET55.js} +2 -2
- package/dist/{chunk-MLAAT4S6.js → chunk-JV4YGAAW.js} +3 -3
- package/dist/{chunk-XNGMQ27R.js → chunk-JZGFDTNC.js} +2 -2
- package/dist/{chunk-GGOMT6KF.js → chunk-NYTZ7HVN.js} +1 -1
- package/dist/{chunk-CGGPS7SI.js → chunk-O6RJ3QMU.js} +5 -5
- package/dist/{chunk-QEFKA2QD.js → chunk-OBCFK4QD.js} +2 -2
- package/dist/{chunk-HOKXGFDR.js → chunk-ORNJJ7XN.js} +22 -16
- package/dist/{chunk-M2ZWWTWZ.js → chunk-OW52QLK3.js} +1 -1
- package/dist/{chunk-PSN33ERW.js → chunk-QC72I5GQ.js} +3 -3
- package/dist/{chunk-FVMYKL6L.js → chunk-QF7WZUU6.js} +1 -1
- package/dist/{chunk-IF2OSI5T.js → chunk-QOALB6ZD.js} +2 -2
- package/dist/{chunk-ZX4I2MEK.js → chunk-TPKYTORP.js} +1 -1
- package/dist/{chunk-S5PCA3SN.js → chunk-UF5L4BAI.js} +1 -1
- package/dist/{chunk-R22NZSGA.js → chunk-URAKVDSI.js} +9 -9
- package/dist/{chunk-BQXFBI7T.js → chunk-URWYRAIC.js} +32 -7
- package/dist/{chunk-7UWVLZXD.js → chunk-USWVXQIH.js} +1 -1
- package/dist/{chunk-P53VEAOF.js → chunk-V5CG75NX.js} +1 -1
- package/dist/{chunk-6X4SQ5MY.js → chunk-V7SAQ645.js} +1 -1
- package/dist/{chunk-TWF3RICI.js → chunk-VSUXCMMH.js} +110 -11
- package/dist/{chunk-Z7K3L4FC.js → chunk-W3SWJ2TD.js} +2 -2
- package/dist/{chunk-ECODQ7II.js → chunk-XACXXKQ5.js} +1 -1
- package/dist/{chunk-5VML45HH.js → chunk-XCRZTXRL.js} +2 -2
- package/dist/chunk-XR56WEA5.js +718 -0
- package/dist/chunk-ZNPE7FW5.js +137 -0
- package/dist/{chunk-PIZRPNKR.js → chunk-ZPSLJFGL.js} +5 -5
- package/dist/claude-launcher-TBWV3YL3.js +267 -0
- package/dist/{cli-MKF7TU7X.js → cli-U253V422.js} +161 -120
- package/dist/{command-ZQNEPDS6.js → command-IQTPFSXY.js} +32 -34
- package/dist/{config-CLSGABD5.js → config-H4QS332G.js} +3 -2
- package/dist/{connect-cmd-UNYKGDAL.js → connect-cmd-ZFOK6HEL.js} +5 -5
- package/dist/{context-cleanup-CHEWNYU6.js → context-cleanup-DSA3HGQU.js} +1 -1
- package/dist/{control-plane-launch-34LNRJWR.js → control-plane-launch-OWSNH6YA.js} +62 -28
- package/dist/{dist-WO7NOJNG.js → dist-7IH56ELA.js} +1 -1
- package/dist/{docker-BFU3AQGY.js → docker-JU422JL7.js} +7 -7
- package/dist/{doctor-DHUM5DBB.js → doctor-FZTS57ZO.js} +35 -36
- package/dist/{emit-artifact-cmd-XBHM647X.js → emit-artifact-cmd-YLZ5WLSF.js} +19 -18
- package/dist/{emit-cmd-W55AEBCA.js → emit-cmd-3W5IO5ET.js} +3 -3
- package/dist/{emit-criteria-cmd-UNMWHOUY.js → emit-criteria-cmd-MF6TVE7C.js} +13 -12
- package/dist/{emit-finding-cmd-7JHVRYOP.js → emit-finding-cmd-S3YMFTZT.js} +13 -12
- package/dist/{emit-result-cmd-5NZMLCJK.js → emit-result-cmd-JHEUFTPX.js} +19 -18
- package/dist/{emit-step-cmd-347XNGPQ.js → emit-step-cmd-TUI2T3DS.js} +14 -13
- package/dist/{exec-client-7UMESIE2.js → exec-client-ATPF2DDB.js} +4 -3
- package/dist/{governing-source-HAA4OW57.js → governing-source-XSEIRSBE.js} +19 -18
- package/dist/{guest-init-KPBQOFE5.js → guest-init-DF7V5D2R.js} +4 -3
- package/dist/{hook-fast-RL5E5OXE.js → hook-fast-O3UUN6SO.js} +2 -2
- package/dist/host-key-ATB23GI3.js +9 -0
- package/dist/{import-capture-cmd-TK4XY7RD.js → import-capture-cmd-Q56DDZVO.js} +1 -1
- package/dist/index.js +5 -5
- package/dist/init-VZE72CR4.js +52 -0
- package/dist/{init-V6FOMD2V.js → init-ZZZPYGUH.js} +32 -33
- package/dist/{init-cmd-Z4O4PPLZ.js → init-cmd-W5VV62ZN.js} +25 -23
- package/dist/{init-update-7DVAKWW3.js → init-update-YFPIQS5G.js} +69 -24
- package/dist/{install-4SEFIIW2.js → install-SFACZU7E.js} +5 -4
- package/dist/{install-cmd-QDOTR4OM.js → install-cmd-6HTP7I43.js} +6 -5
- package/dist/{login-browser-DX35MARZ.js → login-browser-BN5UKPHS.js} +3 -3
- package/dist/{logout-cmd-5V5MMBSY.js → logout-cmd-RZFWTNYD.js} +2 -2
- package/dist/{logs-AMLKC3VP.js → logs-N3ZX7HFV.js} +3 -2
- package/dist/loop-6WENRS5S.js +75 -0
- package/dist/machine-M4MQBV5A.js +47 -0
- package/dist/{mcp-install-cmd-GGWB3JJ2.js → mcp-install-cmd-GD4QUP7Z.js} +2 -2
- package/dist/{mcp-serve-cmd-N3UTY6EE.js → mcp-serve-cmd-SBW7HYKD.js} +6 -5
- package/dist/{paths-7XP63OAX.js → paths-AKX2OOLF.js} +3 -2
- package/dist/pending-7SGWZCEH.js +9 -0
- package/dist/{pidfile-3DM4OBKO.js → pidfile-G2J5WBQS.js} +4 -3
- package/dist/receive-files-T2BIUBQL.js +254 -0
- package/dist/{record-3WP6V2K5.js → record-Q4IEYIVV.js} +4 -3
- package/dist/relay-PSZ4LGMW.js +210 -0
- package/dist/relay-protocol-K7KP2SPI.js +6 -0
- package/dist/{runner-env-FRZFFHDF.js → runner-env-GZHXM27A.js} +6 -5
- package/dist/runner-identity-FTHZFMGY.js +14 -0
- package/dist/{runner-key-5FOWCW7J.js → runner-key-QT7GKGEE.js} +32 -33
- package/dist/{session-sender-U2BBUWNO.js → session-sender-BK6TGNVO.js} +20 -19
- package/dist/{spec-cmd-FVLJOFGC.js → spec-cmd-ZNJ3GXWC.js} +13 -12
- package/dist/{starter-pack-6LQVLBIA.js → starter-pack-5EQYGSJF.js} +2 -2
- package/dist/{status-BINH7MBG.js → status-JOO7MGIC.js} +36 -38
- package/dist/{statusline-fast-KN3BX67H.js → statusline-fast-2OK3DVAA.js} +2 -2
- package/dist/{sync-cmd-VXOUFS4I.js → sync-cmd-W2FPY3T5.js} +19 -18
- package/dist/{watch-OH645CV2.js → watch-WLH5ZUZU.js} +19 -18
- package/dist/{work-claim-cmd-DRQCHCOK.js → work-claim-cmd-4PLDTEZR.js} +17 -16
- package/dist/{work-cmd-5I3J2XQQ.js → work-cmd-KWHAYUYB.js} +13 -12
- package/dist/{work-comment-cmd-UCCPWUSR.js → work-comment-cmd-4CUI7ZDE.js} +13 -12
- package/dist/{work-logs-cmd-7352CJPG.js → work-logs-cmd-AF253WLM.js} +13 -12
- package/dist/{work-open-pr-cmd-LSH4VHJM.js → work-open-pr-cmd-JETE6HRL.js} +15 -14
- package/dist/{work-pr-cmd-H4OQR26N.js → work-pr-cmd-V5G4CPMG.js} +14 -13
- package/dist/{work-stop-cmd-CRDHXQVW.js → work-stop-cmd-FIEKUDGE.js} +13 -12
- package/package.json +2 -2
- package/skill-pack/maintained/outerlayer-amend/SKILL.md +17 -6
- package/skill-pack/maintained/outerlayer-build/SKILL.md +347 -0
- package/skill-pack/{template/build → maintained/outerlayer-build}/references/evidence.md +2 -2
- package/skill-pack/{template/build → maintained/outerlayer-build}/references/release.md +4 -4
- package/skill-pack/{template/build → maintained/outerlayer-build}/references/review-loop.md +3 -1
- package/skill-pack/shipped.json +2 -0
- package/skill-pack/template/build/SKILL.md +4 -253
- package/skill-pack/validators/acceptance-criteria.yaml +1 -1
- package/dist/build-TQ6X5PYN.js +0 -12
- package/dist/chunk-3UZTIRLF.js +0 -320
- package/dist/chunk-CX22S5BA.js +0 -45
- package/dist/chunk-LLM3I5YU.js +0 -89
- package/dist/chunk-SADUQSO4.js +0 -50
- package/dist/chunk-VMJPFQNE.js +0 -261
- package/dist/claude-launcher-J5M44PFS.js +0 -115
- package/dist/host-key-U4B5QETR.js +0 -8
- package/dist/init-XG47KCI4.js +0 -53
- package/dist/loop-EAZQMAE2.js +0 -77
- package/dist/machine-TRHBT5NE.js +0 -48
- package/dist/pending-532ABZYC.js +0 -8
- package/dist/relay-L76HDX72.js +0 -46
- package/dist/{chunk-YVZJ6GKF.js → chunk-DIB2WLBL.js} +1 -1
- package/dist/{chunk-GOSFARMS.js → chunk-F35VMF5M.js} +1 -1
- package/dist/{chunk-DSF2FWAZ.js → chunk-IZSVXRDR.js} +1 -1
- /package/skill-pack/{template/build → maintained/outerlayer-build}/references/agent-briefs.md +0 -0
- /package/skill-pack/{template/build → maintained/outerlayer-build}/references/criteria-judge.md +0 -0
- /package/skill-pack/{template/build → maintained/outerlayer-build}/references/review-briefs.md +0 -0
- /package/skill-pack/{template/build → maintained/outerlayer-build}/scripts/criterion-citations.mjs +0 -0
- /package/skill-pack/{template/build → maintained/outerlayer-build}/scripts/record-criteria.mjs +0 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { rewriteControlPlanePaths, rewriteAgentsFilePaths, SUPPORTED_CONTROL_PLANE_LAYOUT, rewriteHookCommand } from './chunk-HCVLP7XA.js';
|
|
4
|
+
import { MAINTAINED_SKILLS } from './chunk-5OCIHFGE.js';
|
|
5
|
+
import { existsSync, mkdirSync, copyFileSync, lstatSync, readdirSync, writeFileSync, readFileSync } from 'fs';
|
|
6
|
+
import { join, dirname } from 'path';
|
|
7
|
+
|
|
8
|
+
createRequire(import.meta.url);
|
|
9
|
+
function deliverySection(dir) {
|
|
10
|
+
return `This repository's control plane is not in the checkout. Its files are in \`${dir}\`. When the instructions name a file under \`.agents/\` that this directory holds, or a path under \`.outerlayer/\` or \`.claude/\`, read it under \`${dir}\`.`;
|
|
11
|
+
}
|
|
12
|
+
function isFile(path) {
|
|
13
|
+
try {
|
|
14
|
+
return lstatSync(path).isFile();
|
|
15
|
+
} catch {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function filesUnder(dir, base = dir) {
|
|
20
|
+
if (!existsSync(dir) || !lstatSync(dir).isDirectory()) return [];
|
|
21
|
+
const out = [];
|
|
22
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
23
|
+
const abs = join(dir, entry.name);
|
|
24
|
+
if (entry.isDirectory()) out.push(...filesUnder(abs, base));
|
|
25
|
+
else if (entry.isFile()) out.push(abs.slice(base.length + 1));
|
|
26
|
+
}
|
|
27
|
+
return out.sort();
|
|
28
|
+
}
|
|
29
|
+
function readText(path) {
|
|
30
|
+
if (!isFile(path)) return void 0;
|
|
31
|
+
const content = readFileSync(path);
|
|
32
|
+
return content.includes(0) ? void 0 : content.toString("utf8");
|
|
33
|
+
}
|
|
34
|
+
function deliveredSettings(raw, dir) {
|
|
35
|
+
if (raw === void 0) return "{}\n";
|
|
36
|
+
const rewrite = (value) => {
|
|
37
|
+
if (typeof value === "string") return rewriteHookCommand(value, dir);
|
|
38
|
+
if (Array.isArray(value)) return value.map(rewrite);
|
|
39
|
+
if (typeof value === "object" && value !== null) return Object.fromEntries(Object.entries(value).map(([key, inner]) => [key, rewrite(inner)]));
|
|
40
|
+
return value;
|
|
41
|
+
};
|
|
42
|
+
try {
|
|
43
|
+
return `${JSON.stringify(rewrite(JSON.parse(raw)), null, 2)}
|
|
44
|
+
`;
|
|
45
|
+
} catch {
|
|
46
|
+
return rewriteHookCommand(raw, dir);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function composeDelivery(input) {
|
|
50
|
+
const { tree, dir } = input;
|
|
51
|
+
const added = [];
|
|
52
|
+
const put = (rel, content) => {
|
|
53
|
+
mkdirSync(dirname(join(tree, rel)), { recursive: true });
|
|
54
|
+
writeFileSync(join(tree, rel), content);
|
|
55
|
+
added.push(rel);
|
|
56
|
+
};
|
|
57
|
+
for (const skill of MAINTAINED_SKILLS) {
|
|
58
|
+
for (const rel of filesUnder(join(tree, ".outerlayer/skills", skill))) {
|
|
59
|
+
const target = `.claude/skills/${skill}/${rel}`;
|
|
60
|
+
if (existsSync(join(tree, target))) continue;
|
|
61
|
+
mkdirSync(dirname(join(tree, target)), { recursive: true });
|
|
62
|
+
copyFileSync(join(tree, ".outerlayer/skills", skill, rel), join(tree, target));
|
|
63
|
+
added.push(target);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const agentSkills = join(tree, ".agents/skills");
|
|
67
|
+
if (existsSync(agentSkills) && lstatSync(agentSkills).isDirectory()) {
|
|
68
|
+
for (const entry of readdirSync(agentSkills, { withFileTypes: true })) {
|
|
69
|
+
if (!entry.isDirectory() || existsSync(join(tree, ".claude/skills", entry.name))) continue;
|
|
70
|
+
for (const rel of filesUnder(join(agentSkills, entry.name))) {
|
|
71
|
+
const target = `.claude/skills/${entry.name}/${rel}`;
|
|
72
|
+
mkdirSync(dirname(join(tree, target)), { recursive: true });
|
|
73
|
+
copyFileSync(join(agentSkills, entry.name, rel), join(tree, target));
|
|
74
|
+
added.push(target);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
for (const rel of [...filesUnder(join(tree, ".claude/skills"), tree), ...filesUnder(join(tree, ".claude/agents"), tree)]) {
|
|
79
|
+
const text = readText(join(tree, rel));
|
|
80
|
+
if (text === void 0) continue;
|
|
81
|
+
const rewritten = rewriteControlPlanePaths(text, dir);
|
|
82
|
+
if (rewritten !== text) writeFileSync(join(tree, rel), rewritten);
|
|
83
|
+
}
|
|
84
|
+
const agents = readText(join(tree, "AGENTS.md"));
|
|
85
|
+
const instructions = agents === void 0 ? void 0 : rewriteAgentsFilePaths(rewriteControlPlanePaths(agents, dir), dir, (path) => isFile(join(tree, path)));
|
|
86
|
+
put("CLAUDE.md", instructions === void 0 ? `${deliverySection(dir)}
|
|
87
|
+
` : `${deliverySection(dir)}
|
|
88
|
+
|
|
89
|
+
${instructions}`);
|
|
90
|
+
put("settings.json", deliveredSettings(readText(join(tree, ".claude/settings.json")), dir));
|
|
91
|
+
return added;
|
|
92
|
+
}
|
|
93
|
+
function sharedSkillNames(tree) {
|
|
94
|
+
const dir = join(tree, ".claude/skills");
|
|
95
|
+
if (!existsSync(dir)) return [];
|
|
96
|
+
return readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
|
|
97
|
+
}
|
|
98
|
+
function skillConflictMessage(input) {
|
|
99
|
+
return `${input.folder}/${input.skill} in ${input.repository} has the same name as the shared skill ${input.skill} from ${input.controlPlane}`;
|
|
100
|
+
}
|
|
101
|
+
var UNPARSABLE_CONFIG = "unparsable";
|
|
102
|
+
function isNewerLayout(declared) {
|
|
103
|
+
return /^[1-9][0-9]*$/.test(declared) && Number(declared) > SUPPORTED_CONTROL_PLANE_LAYOUT;
|
|
104
|
+
}
|
|
105
|
+
function layoutProblem(repository, declared, upgrade) {
|
|
106
|
+
if (declared === UNPARSABLE_CONFIG) {
|
|
107
|
+
return { what: `the control plane ${repository} has an .outerlayer/config.json that is not valid JSON`, advice: "fix the file" };
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
what: `the control plane ${repository} declares layout ${declared}`,
|
|
111
|
+
advice: isNewerLayout(declared) ? `${upgrade} to a version that reads layout ${declared}` : `set "layout" in .outerlayer/config.json to 1 or ${SUPPORTED_CONTROL_PLANE_LAYOUT}`
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function unsupportedLayout(root) {
|
|
115
|
+
const text = readText(join(root, ".outerlayer/config.json"));
|
|
116
|
+
if (text === void 0) return void 0;
|
|
117
|
+
let declared;
|
|
118
|
+
try {
|
|
119
|
+
declared = JSON.parse(text)?.layout;
|
|
120
|
+
} catch {
|
|
121
|
+
return UNPARSABLE_CONFIG;
|
|
122
|
+
}
|
|
123
|
+
if (declared === void 0) return void 0;
|
|
124
|
+
if (typeof declared === "number" && Number.isInteger(declared) && declared >= 1 && declared <= SUPPORTED_CONTROL_PLANE_LAYOUT) return void 0;
|
|
125
|
+
return JSON.stringify(declared);
|
|
126
|
+
}
|
|
127
|
+
function layoutRefusalMessage(repository, declared) {
|
|
128
|
+
const { what, advice } = layoutProblem(repository, declared, "upgrade the CLI");
|
|
129
|
+
return `outerlayer claude: ${what}, and this CLI reads layouts 1 and ${SUPPORTED_CONTROL_PLANE_LAYOUT}; ${advice}
|
|
130
|
+
`;
|
|
131
|
+
}
|
|
132
|
+
function layoutRefusalReason(repository, declared) {
|
|
133
|
+
const { what, advice } = layoutProblem(repository, declared, "upgrade the CLI on the runner host");
|
|
134
|
+
return `context_layout_unsupported: ${what}, and this runner's CLI reads layouts 1 and ${SUPPORTED_CONTROL_PLANE_LAYOUT}; ${advice}`;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export { composeDelivery, layoutRefusalMessage, layoutRefusalReason, sharedSkillNames, skillConflictMessage, unsupportedLayout };
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { hasStoredCredential, AGENT_COMMANDS, storeClaudeCredential, credentialFromStdin, isAgentName } from './chunk-
|
|
4
|
-
import { detectMachine, supportsContainerBuilds, reachDockerStep, describeMachineDocker, supportsMicroVmBuilds } from './chunk-
|
|
5
|
-
import { MIN_ENGINE_MAJOR } from './chunk-
|
|
6
|
-
import { runnerCredentialProblem } from './chunk-
|
|
7
|
-
import { runnerDir, RUNNER_DIR_MODE, RUNNER_FILE_MODE } from './chunk-
|
|
3
|
+
import { hasStoredCredential, AGENT_COMMANDS, storeClaudeCredential, credentialFromStdin, isAgentName } from './chunk-4AVDD7VX.js';
|
|
4
|
+
import { detectMachine, supportsContainerBuilds, reachDockerStep, describeMachineDocker, supportsMicroVmBuilds } from './chunk-FRNYCJ4W.js';
|
|
5
|
+
import { MIN_ENGINE_MAJOR } from './chunk-5NJIWKD4.js';
|
|
6
|
+
import { runnerCredentialProblem } from './chunk-V5CG75NX.js';
|
|
7
|
+
import { runnerDir, RUNNER_DIR_MODE, RUNNER_FILE_MODE } from './chunk-IHNQWF6F.js';
|
|
8
8
|
import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'fs';
|
|
9
9
|
import { dirname } from 'path';
|
|
10
10
|
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { sharedSkillNames } from './chunk-ZNPE7FW5.js';
|
|
4
|
+
import { GOVERNANCE_VARIABLE, versionAtLeast, MINIMUM_CLAUDE_VERSION, SHIM_SCRIPT, CONTROL_PLANE_DIR_VARIABLE } from './chunk-HCVLP7XA.js';
|
|
5
|
+
export { MINIMUM_CLAUDE_VERSION } from './chunk-HCVLP7XA.js';
|
|
6
|
+
import { findGitDirs } from './chunk-BJ3KTMDH.js';
|
|
7
|
+
import './chunk-5OCIHFGE.js';
|
|
8
|
+
import './chunk-WFSUUUAN.js';
|
|
9
|
+
import './chunk-7VUK5RFS.js';
|
|
10
|
+
import { homeFromEnv } from './chunk-VNVZDWO3.js';
|
|
11
|
+
import './chunk-RA3O54FC.js';
|
|
12
|
+
import './chunk-Z5GEFMRW.js';
|
|
13
|
+
import { spawnSync, execFileSync, spawn } from 'child_process';
|
|
14
|
+
import { existsSync, accessSync, constants, statSync, readFileSync, readdirSync, lstatSync, rmSync, mkdirSync, realpathSync, symlinkSync, renameSync, readlinkSync, writeFileSync } from 'fs';
|
|
15
|
+
import { join, delimiter, isAbsolute, resolve } from 'path';
|
|
16
|
+
import { createHash, randomBytes } from 'crypto';
|
|
17
|
+
|
|
18
|
+
createRequire(import.meta.url);
|
|
19
|
+
function ownSkillsRoot(home) {
|
|
20
|
+
return join(home, ".outerlayer", "context", "own");
|
|
21
|
+
}
|
|
22
|
+
function folderNames(dir) {
|
|
23
|
+
try {
|
|
24
|
+
return readdirSync(dir).filter((name) => {
|
|
25
|
+
try {
|
|
26
|
+
return statSync(join(dir, name)).isDirectory();
|
|
27
|
+
} catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
} catch {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function wantedLinks(repoRoot, shared) {
|
|
36
|
+
const provided = new Set(folderNames(join(repoRoot, ".claude", "skills")));
|
|
37
|
+
const wanted = /* @__PURE__ */ new Map();
|
|
38
|
+
for (const name of folderNames(join(repoRoot, ".agents", "skills")).sort()) {
|
|
39
|
+
if (provided.has(name) || shared.has(name)) continue;
|
|
40
|
+
wanted.set(name, join(repoRoot, ".agents", "skills", name));
|
|
41
|
+
}
|
|
42
|
+
return wanted;
|
|
43
|
+
}
|
|
44
|
+
function holdsLinks(dir, wanted) {
|
|
45
|
+
const skills = join(dir, ".claude", "skills");
|
|
46
|
+
let names;
|
|
47
|
+
try {
|
|
48
|
+
names = readdirSync(skills);
|
|
49
|
+
} catch {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
if (names.length !== wanted.size) return false;
|
|
53
|
+
return names.every((name) => {
|
|
54
|
+
try {
|
|
55
|
+
return readlinkSync(join(skills, name)) === wanted.get(name);
|
|
56
|
+
} catch {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
function removeQuietly(path) {
|
|
62
|
+
rmSync(path, { recursive: true, force: true });
|
|
63
|
+
}
|
|
64
|
+
var REPLACE_ATTEMPTS = 3;
|
|
65
|
+
function ensureOwnSkillsDir(input) {
|
|
66
|
+
const wanted = wantedLinks(input.repoRoot, new Set(input.shared));
|
|
67
|
+
if (wanted.size === 0) return void 0;
|
|
68
|
+
const root = ownSkillsRoot(input.home);
|
|
69
|
+
mkdirSync(root, { recursive: true });
|
|
70
|
+
const checkout = realpathSync(input.repoRoot);
|
|
71
|
+
const key = createHash("sha256").update(checkout).digest("hex").slice(0, 16);
|
|
72
|
+
const final = join(root, key);
|
|
73
|
+
const record = join(root, `${key}.json`);
|
|
74
|
+
const writeRecord = () => {
|
|
75
|
+
const temporary2 = join(root, `.tmp-${randomBytes(6).toString("hex")}.json`);
|
|
76
|
+
writeFileSync(temporary2, `${JSON.stringify({ checkout })}
|
|
77
|
+
`);
|
|
78
|
+
renameSync(temporary2, record);
|
|
79
|
+
};
|
|
80
|
+
if (holdsLinks(final, wanted)) {
|
|
81
|
+
if (!existsSync(record)) writeRecord();
|
|
82
|
+
return final;
|
|
83
|
+
}
|
|
84
|
+
const temporary = join(root, `.tmp-${randomBytes(6).toString("hex")}`);
|
|
85
|
+
mkdirSync(join(temporary, ".claude", "skills"), { recursive: true });
|
|
86
|
+
for (const [name, target] of wanted) symlinkSync(target, join(temporary, ".claude", "skills", name));
|
|
87
|
+
try {
|
|
88
|
+
for (let attempt = 0; attempt < REPLACE_ATTEMPTS; attempt += 1) {
|
|
89
|
+
try {
|
|
90
|
+
renameSync(temporary, final);
|
|
91
|
+
writeRecord();
|
|
92
|
+
return final;
|
|
93
|
+
} catch {
|
|
94
|
+
if (holdsLinks(final, wanted)) {
|
|
95
|
+
writeRecord();
|
|
96
|
+
return final;
|
|
97
|
+
}
|
|
98
|
+
if (attempt === REPLACE_ATTEMPTS - 1) break;
|
|
99
|
+
const old = join(root, `.old-${randomBytes(6).toString("hex")}`);
|
|
100
|
+
try {
|
|
101
|
+
renameSync(final, old);
|
|
102
|
+
removeQuietly(old);
|
|
103
|
+
} catch {
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return void 0;
|
|
108
|
+
} finally {
|
|
109
|
+
removeQuietly(temporary);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
var LEFTOVER_AGE_MS = 10 * 6e4;
|
|
113
|
+
function removeStaleOwnSkillsDirs(home, now = Date.now()) {
|
|
114
|
+
const root = ownSkillsRoot(home);
|
|
115
|
+
let entries;
|
|
116
|
+
try {
|
|
117
|
+
entries = readdirSync(root);
|
|
118
|
+
} catch {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
for (const entry of entries) {
|
|
122
|
+
const path = join(root, entry);
|
|
123
|
+
if (entry.startsWith(".")) {
|
|
124
|
+
try {
|
|
125
|
+
if (now - lstatSync(path).mtimeMs > LEFTOVER_AGE_MS) removeQuietly(path);
|
|
126
|
+
} catch {
|
|
127
|
+
}
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (!entry.endsWith(".json")) continue;
|
|
131
|
+
let checkout;
|
|
132
|
+
try {
|
|
133
|
+
checkout = JSON.parse(readFileSync(path, "utf8")).checkout;
|
|
134
|
+
} catch {
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (typeof checkout !== "string" || existsSync(checkout)) continue;
|
|
138
|
+
removeQuietly(join(root, entry.slice(0, -".json".length)));
|
|
139
|
+
removeQuietly(path);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// src/claude-launcher.ts
|
|
144
|
+
function readDeliveryRecord(cwd) {
|
|
145
|
+
let common;
|
|
146
|
+
try {
|
|
147
|
+
common = execFileSync("git", ["rev-parse", "--git-common-dir"], { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 5e3 }).trim();
|
|
148
|
+
} catch {
|
|
149
|
+
return { kind: "none" };
|
|
150
|
+
}
|
|
151
|
+
const path = join(isAbsolute(common) ? common : resolve(cwd, common), "outerlayer", "delivery.json");
|
|
152
|
+
if (!existsSync(path)) return { kind: "none" };
|
|
153
|
+
try {
|
|
154
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
155
|
+
if (typeof parsed.dir === "string" && isAbsolute(parsed.dir) && typeof parsed.repository === "string" && typeof parsed.sha === "string") {
|
|
156
|
+
return { kind: "found", record: { repository: parsed.repository, sha: parsed.sha, dir: parsed.dir, launcher: parsed.launcher === true } };
|
|
157
|
+
}
|
|
158
|
+
} catch {
|
|
159
|
+
}
|
|
160
|
+
return { kind: "invalid", path };
|
|
161
|
+
}
|
|
162
|
+
function findClaude(path) {
|
|
163
|
+
for (const entry of (path ?? "").split(delimiter)) {
|
|
164
|
+
if (entry === "") continue;
|
|
165
|
+
const candidate = join(entry, "claude");
|
|
166
|
+
try {
|
|
167
|
+
accessSync(candidate, constants.X_OK);
|
|
168
|
+
if (statSync(candidate).size === Buffer.byteLength(SHIM_SCRIPT) && readFileSync(candidate, "utf8") === SHIM_SCRIPT) continue;
|
|
169
|
+
} catch {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
return candidate;
|
|
173
|
+
}
|
|
174
|
+
return void 0;
|
|
175
|
+
}
|
|
176
|
+
var SIGNALS = ["SIGINT", "SIGTERM", "SIGHUP"];
|
|
177
|
+
var SIGNAL_NUMBERS = { SIGHUP: 1, SIGINT: 2, SIGTERM: 15 };
|
|
178
|
+
function run(program, argv, env, cwd, stderr) {
|
|
179
|
+
return new Promise((done) => {
|
|
180
|
+
const child = spawn(program, argv, { cwd, env, stdio: "inherit" });
|
|
181
|
+
const forward = (signal) => () => {
|
|
182
|
+
child.kill(signal);
|
|
183
|
+
};
|
|
184
|
+
const handlers = SIGNALS.map((signal) => [signal, forward(signal)]);
|
|
185
|
+
for (const [signal, handler] of handlers) process.on(signal, handler);
|
|
186
|
+
const finish = (code) => {
|
|
187
|
+
for (const [signal, handler] of handlers) process.off(signal, handler);
|
|
188
|
+
done(code);
|
|
189
|
+
};
|
|
190
|
+
child.on("error", (err) => {
|
|
191
|
+
stderr(`outerlayer claude: could not start ${program}: ${err.message}
|
|
192
|
+
`);
|
|
193
|
+
finish(127);
|
|
194
|
+
});
|
|
195
|
+
child.on("exit", (code, signal) => finish(code ?? 128 + (signal === null ? 0 : SIGNAL_NUMBERS[signal] ?? 0)));
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
function ownSkillsDirFor(cwd, home, shared, stderr) {
|
|
199
|
+
const root = findGitDirs(cwd)?.repoRoot;
|
|
200
|
+
if (root === void 0) return void 0;
|
|
201
|
+
try {
|
|
202
|
+
return ensureOwnSkillsDir({ home, repoRoot: root, shared });
|
|
203
|
+
} catch (err) {
|
|
204
|
+
stderr(`outerlayer claude: the checkout's .agents/skills/ skills are not attached: ${err instanceof Error ? err.message : String(err)}
|
|
205
|
+
`);
|
|
206
|
+
return void 0;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
async function runClaudeLauncher(args, deps = {}) {
|
|
210
|
+
const cwd = deps.cwd ?? process.cwd();
|
|
211
|
+
const env = deps.env ?? process.env;
|
|
212
|
+
const stderr = deps.stderr ?? ((text) => void process.stderr.write(text));
|
|
213
|
+
const claude = findClaude(env.PATH);
|
|
214
|
+
if (claude === void 0) {
|
|
215
|
+
stderr("outerlayer claude: no claude program found on PATH\n");
|
|
216
|
+
return 127;
|
|
217
|
+
}
|
|
218
|
+
const home = homeFromEnv(env);
|
|
219
|
+
try {
|
|
220
|
+
removeStaleOwnSkillsDirs(home);
|
|
221
|
+
} catch {
|
|
222
|
+
}
|
|
223
|
+
const found = readDeliveryRecord(cwd);
|
|
224
|
+
const build = found.kind === "found" && found.record.launcher !== true ? found.record : void 0;
|
|
225
|
+
if (found.kind === "invalid" && env.OUTERLAYER_JOB_DIR !== void 0) {
|
|
226
|
+
stderr(`outerlayer claude: ${found.path} is not a readable delivery record, so the control plane cannot be attached
|
|
227
|
+
`);
|
|
228
|
+
return 1;
|
|
229
|
+
}
|
|
230
|
+
let dir;
|
|
231
|
+
if (build !== void 0) {
|
|
232
|
+
dir = build.dir;
|
|
233
|
+
if (!existsSync(dir)) {
|
|
234
|
+
stderr(`outerlayer claude: the control plane of ${build.repository} is not at ${dir}, so it cannot be attached
|
|
235
|
+
`);
|
|
236
|
+
return 1;
|
|
237
|
+
}
|
|
238
|
+
} else {
|
|
239
|
+
const { resolveLaunchPlane } = await import('./control-plane-launch-OWSNH6YA.js');
|
|
240
|
+
const plane = env[GOVERNANCE_VARIABLE] === "self" ? { kind: "none" } : await resolveLaunchPlane({ cwd, env, home, fetchImpl: deps.fetchImpl, stderr });
|
|
241
|
+
if (plane.kind === "none") {
|
|
242
|
+
const own2 = ownSkillsDirFor(cwd, home, [], stderr);
|
|
243
|
+
return run(claude, own2 === void 0 ? args : ["--add-dir", own2, ...args], env, cwd, stderr);
|
|
244
|
+
}
|
|
245
|
+
if (plane.kind === "refused") {
|
|
246
|
+
stderr(plane.message);
|
|
247
|
+
return 1;
|
|
248
|
+
}
|
|
249
|
+
dir = plane.dir;
|
|
250
|
+
}
|
|
251
|
+
const reported = spawnSync(claude, ["--version"], { env, encoding: "utf8", timeout: 15e3, stdio: ["ignore", "pipe", "ignore"] }).stdout ?? "";
|
|
252
|
+
if (/\d+\.\d+\.\d+/.test(reported) && !versionAtLeast(reported)) {
|
|
253
|
+
stderr(`outerlayer claude: Claude Code ${reported.trim().split(/\s+/)[0]} is older than ${MINIMUM_CLAUDE_VERSION}, the oldest version known to load a control plane's instructions for subagents; the minimum version is ${MINIMUM_CLAUDE_VERSION}
|
|
254
|
+
`);
|
|
255
|
+
return 1;
|
|
256
|
+
}
|
|
257
|
+
const own = ownSkillsDirFor(cwd, home, sharedSkillNames(dir), stderr);
|
|
258
|
+
return run(
|
|
259
|
+
claude,
|
|
260
|
+
["--add-dir", dir, ...own === void 0 ? [] : ["--add-dir", own], "--settings", join(dir, "settings.json"), ...args],
|
|
261
|
+
{ ...env, CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD: "1", [CONTROL_PLANE_DIR_VARIABLE]: dir },
|
|
262
|
+
cwd,
|
|
263
|
+
stderr
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export { runClaudeLauncher };
|