@outerlayer/cli 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +124 -0
- package/README.md +226 -59
- package/dist/agent-setup-IFH3FB5X.js +197 -0
- package/dist/build-TZUUTRGH.js +9 -0
- package/dist/build-info.json +1 -1
- package/dist/check-WQP2CZDW.js +94 -0
- package/dist/chunk-2E54Z3P5.js +232 -0
- package/dist/chunk-3TT7RQYB.js +55 -0
- package/dist/{chunk-U32VSRLO.js → chunk-3YPKXE5L.js} +90 -632
- package/dist/{chunk-Q6CL5THG.js → chunk-5DLBAXY5.js} +208 -61
- package/dist/{chunk-R5KBGVII.js → chunk-5QRP3MVS.js} +30 -14
- package/dist/chunk-5Y3QQRUH.js +300 -0
- package/dist/chunk-6Y63SXFO.js +383 -0
- package/dist/chunk-774HEQL6.js +178 -0
- package/dist/chunk-7A6RVXPZ.js +2233 -0
- package/dist/chunk-7YIOOFVJ.js +71 -0
- package/dist/chunk-ABIWDUMS.js +104 -0
- package/dist/chunk-AUFG23AA.js +534 -0
- package/dist/chunk-B2G7JHHB.js +42 -0
- package/dist/chunk-BCJNHMZT.js +34 -0
- package/dist/chunk-BCLJSQCV.js +56 -0
- package/dist/chunk-BFESLKPP.js +61 -0
- package/dist/chunk-BJ3KTMDH.js +63 -0
- package/dist/chunk-BKO6JEZI.js +47 -0
- package/dist/chunk-BOLTI6LR.js +37 -0
- package/dist/chunk-CBPPJSAR.js +89 -0
- package/dist/chunk-CBZB6XY6.js +102 -0
- package/dist/{chunk-65WXAANR.js → chunk-DVDEBNQJ.js} +17 -2
- package/dist/{work-pr-cmd-AZQPWH4H.js → chunk-DVYHCMB2.js} +9 -26
- package/dist/{chunk-TFUIDMOB.js → chunk-EABW6AJQ.js} +12 -1
- package/dist/{chunk-KFYJV2ZG.js → chunk-EMY4I27X.js} +1 -1
- package/dist/{chunk-NTTPJV35.js → chunk-F47JBIAW.js} +3 -1
- package/dist/chunk-F6GFZXZ3.js +60 -0
- package/dist/{chunk-YOOSBOKS.js → chunk-F7CU5ABH.js} +1 -1
- package/dist/chunk-FAJSS2WN.js +1357 -0
- package/dist/chunk-FS2VYGZT.js +1764 -0
- package/dist/chunk-FV2CCGXK.js +9 -0
- package/dist/{chunk-4TNZMO7V.js → chunk-G7FSV7JX.js} +3452 -751
- package/dist/{emit-cmd-TWSYYEZI.js → chunk-H5NCNVDA.js} +68 -17
- package/dist/chunk-HE2D4EY5.js +83 -0
- package/dist/chunk-L2DBRKRA.js +94 -0
- package/dist/chunk-LF6MLJCY.js +38 -0
- package/dist/chunk-M5POMKKW.js +1051 -0
- package/dist/{mcp-install-cmd-FDQH6SEN.js → chunk-MQ3IPHIZ.js} +35 -18
- package/dist/{chunk-D77LS3UI.js → chunk-N5FOE5PS.js} +33 -4
- package/dist/chunk-N6LURUEF.js +64 -0
- package/dist/chunk-NYC54VBD.js +1084 -0
- package/dist/chunk-OGFGZQA3.js +23 -0
- package/dist/chunk-QOZKPLVJ.js +226 -0
- package/dist/chunk-RA3O54FC.js +30 -0
- package/dist/chunk-TBT347UY.js +41 -0
- package/dist/{chunk-NXLLURA4.js → chunk-UFFXNXLQ.js} +115 -71
- package/dist/chunk-USO2DKBD.js +421 -0
- package/dist/chunk-W4SQCOZU.js +206 -0
- package/dist/{chunk-A3WLZX2F.js → chunk-WIOZAJ2W.js} +15 -2
- package/dist/chunk-WO2BXCTQ.js +83 -0
- package/dist/{chunk-I3ETLSNE.js → chunk-XCCLFVXM.js} +120 -11
- package/dist/{context-materialize-6WKT3RBQ.js → chunk-XDDW4FRS.js} +134 -26
- package/dist/chunk-YYYXRJUW.js +101 -0
- package/dist/chunk-ZM2IMMYN.js +76 -0
- package/dist/chunk-ZMYPLFG3.js +71 -0
- package/dist/chunk-ZNA27WEV.js +470 -0
- package/dist/{cli-G7DILYJY.js → cli-NHUXABYH.js} +590 -876
- package/dist/{paths-D2VGWWFI.js → cli-build-K56DK4DS.js} +1 -1
- package/dist/config-XVJYZ4IQ.js +9 -0
- package/dist/connect-cmd-OOG4TPU7.js +16 -0
- package/dist/context-adopt-UMA4O5NY.js +105 -0
- package/dist/context-materialize-G2FUFC72.js +19 -0
- package/dist/dist-RW4UPPC2.js +6 -0
- package/dist/docker-NEGDLU6D.js +7 -0
- package/dist/doctor-53F2PYY7.js +43 -0
- package/dist/doctor-UFVL4PZY.js +426 -0
- package/dist/{emit-artifact-cmd-UQVT3OKW.js → emit-artifact-cmd-KTYW2TN2.js} +24 -20
- package/dist/emit-cmd-DCERAU27.js +9 -0
- package/dist/emit-criteria-cmd-VAUUSAYD.js +166 -0
- package/dist/{emit-finding-cmd-FDOU2OPD.js → emit-finding-cmd-UR4ID45Z.js} +34 -31
- package/dist/{emit-result-cmd-H2T4C2K3.js → emit-result-cmd-FDTUKGKE.js} +34 -62
- package/dist/exec-client-4XGXV3XN.js +8 -0
- package/dist/guest-init-GENRHNP7.js +8 -0
- package/dist/hook-fast-EYENPK6F.js +9 -0
- package/dist/{hook-wrap-fast-KXYNX3AD.js → hook-wrap-fast-NPLHJXFK.js} +2 -2
- package/dist/host-key-KDYZ5ECC.js +7 -0
- package/dist/import-capture-cmd-AVZ4VIVJ.js +68 -0
- package/dist/{import-ruler-cmd-7LH2QOMG.js → import-ruler-cmd-GUHL6IY5.js} +1 -1
- package/dist/index.js +4 -4
- package/dist/init-DXBCOAJK.js +36 -0
- package/dist/init-YEXK35CF.js +221 -0
- package/dist/init-cmd-NHR7PRSQ.js +135 -0
- package/dist/install-cmd-7WXOIFDO.js +55 -0
- package/dist/lima-XN65D7GN.js +55 -0
- package/dist/login-browser-7XGSV7MA.js +10 -0
- package/dist/{config-POF7DEQW.js → logout-cmd-QAUFDJ6J.js} +3 -1
- package/dist/{logs-TRNPQM42.js → logs-7RYUH5A6.js} +1 -1
- package/dist/loop-VZOLL4WQ.js +43 -0
- package/dist/machine-WSG52J75.js +35 -0
- package/dist/mcp-install-cmd-RLK4SO3N.js +9 -0
- package/dist/{mcp-serve-cmd-57EZZOTL.js → mcp-serve-cmd-JPTP5FMS.js} +20 -9
- package/dist/paths-OYKMVYJP.js +6 -0
- package/dist/{pidfile-PTW76F56.js → pidfile-UZRH774M.js} +2 -3
- package/dist/real-deps-SU24ZA2K.js +21 -0
- package/dist/relay-L76HDX72.js +46 -0
- package/dist/settings-J2652U5N.js +7 -0
- package/dist/starter-pack-LIZYMKYQ.js +10 -0
- package/dist/{status-I27IST4L.js → status-2UZKKNB7.js} +30 -13
- package/dist/{statusline-fast-3C5OXHDD.js → statusline-fast-SVTMBMD7.js} +3 -2
- package/dist/sync-cmd-VPPYCNNM.js +28 -0
- package/dist/version-BWM6VLDI.js +6 -0
- package/dist/{watch-3DTPJETH.js → watch-5C4ZBBGO.js} +19 -8
- package/dist/{work-claim-cmd-5AUNCLS2.js → work-claim-cmd-LSO2B2EX.js} +33 -20
- package/dist/work-cmd-UOQO2AD4.js +17 -0
- package/dist/work-comment-cmd-WF3EOLAE.js +106 -0
- package/dist/{work-launch-LT663PB3.js → work-launch-YI4CJDAP.js} +1 -1
- package/dist/work-open-pr-cmd-4SSQZMYG.js +156 -0
- package/dist/work-pr-cmd-6AN6RRQJ.js +16 -0
- package/package.json +13 -3
- package/skill-pack/maintained/amend/SKILL.md +104 -0
- package/skill-pack/maintained/emitting-evidence/SKILL.md +99 -0
- package/skill-pack/maintained/emitting-evidence/references/agents-snippet.md +20 -0
- package/skill-pack/maintained/outerlayer/SKILL.md +55 -0
- package/skill-pack/maintained/reporting-findings/SKILL.md +148 -0
- package/skill-pack/template/build/SKILL.md +193 -0
- package/skill-pack/template/build/references/agent-briefs.md +243 -0
- package/skill-pack/template/build/references/criteria-judge.md +91 -0
- package/skill-pack/template/build/references/evidence.md +42 -0
- package/skill-pack/template/build/references/release.md +74 -0
- package/skill-pack/template/build/references/review-briefs.md +275 -0
- package/skill-pack/template/build/references/review-loop.md +158 -0
- package/skill-pack/template/build/scripts/record-criteria.mjs +235 -0
- package/skill-pack/template/spec/SKILL.md +84 -0
- package/skill-pack/template/writing-specs/SKILL.md +134 -0
- package/dist/chunk-DCNOXRMV.js +0 -589
- package/dist/chunk-JJP7YLMN.js +0 -25
- package/dist/chunk-OZ7C3XUE.js +0 -34
- package/dist/chunk-WQ6VGRGZ.js +0 -150
- package/dist/hook-fast-J5LCDHUJ.js +0 -8
- package/dist/import-capture-cmd-EUMBGIV3.js +0 -176
- package/dist/init-PTBITAUO.js +0 -103
- package/dist/login-cmd-IRX6LZT7.js +0 -62
- package/dist/loop-ASZRX3CZ.js +0 -648
- package/dist/sync-cmd-BBAUZ5JD.js +0 -17
- package/dist/work-cmd-2P4BVX47.js +0 -18
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { splitRawFile } from './chunk-EMY4I27X.js';
|
|
4
|
+
import { resolvePackageVersion } from './chunk-RA3O54FC.js';
|
|
5
|
+
import { existsSync, readFileSync, mkdirSync, writeFileSync, readdirSync } from 'fs';
|
|
6
|
+
import { join, dirname } from 'path';
|
|
7
|
+
import { fileURLToPath } from 'url';
|
|
8
|
+
|
|
9
|
+
createRequire(import.meta.url);
|
|
10
|
+
var TEMPLATE_SKILLS = ["spec", "writing-specs", "build"];
|
|
11
|
+
var MAINTAINED_SKILLS = ["emitting-evidence", "amend", "reporting-findings", "outerlayer"];
|
|
12
|
+
var TEMPLATE_CLASS_LINE = "<!-- yours to edit. Copied once from the default template by outerlayer init; never overwritten. -->";
|
|
13
|
+
function maintainedClassLine(version) {
|
|
14
|
+
return `<!-- maintained by OuterLayer ${version}. Edits are overwritten on the next outerlayer context emit. -->`;
|
|
15
|
+
}
|
|
16
|
+
function resolvePackRoot(entryPath = fileURLToPath(import.meta.url)) {
|
|
17
|
+
return join(dirname(dirname(entryPath)), "skill-pack");
|
|
18
|
+
}
|
|
19
|
+
function skillDir(skill) {
|
|
20
|
+
return `.outerlayer/skills/${skill}`;
|
|
21
|
+
}
|
|
22
|
+
function renderSkillFile(skillClass, content, version) {
|
|
23
|
+
const line = skillClass === "maintained" ? maintainedClassLine(version) : TEMPLATE_CLASS_LINE;
|
|
24
|
+
const { frontmatterInner, body } = splitRawFile(content);
|
|
25
|
+
if (frontmatterInner === null) return `${line}
|
|
26
|
+
${content}`;
|
|
27
|
+
const block = content.slice(0, content.length - body.length);
|
|
28
|
+
return `${block}${block.endsWith("\n") ? "" : "\n"}${line}
|
|
29
|
+
${body}`;
|
|
30
|
+
}
|
|
31
|
+
function filesUnder(dir, base = dir) {
|
|
32
|
+
const out = [];
|
|
33
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
34
|
+
const abs = join(dir, entry.name);
|
|
35
|
+
if (entry.isDirectory()) out.push(...filesUnder(abs, base));
|
|
36
|
+
else if (entry.isFile()) out.push(abs.slice(base.length + 1).split("\\").join("/"));
|
|
37
|
+
}
|
|
38
|
+
return out.sort();
|
|
39
|
+
}
|
|
40
|
+
function orderSkillFiles(relPaths) {
|
|
41
|
+
return [...relPaths.filter((p) => p === "SKILL.md"), ...relPaths.filter((p) => p !== "SKILL.md")];
|
|
42
|
+
}
|
|
43
|
+
function loadClass(packRoot, skillClass, names, version) {
|
|
44
|
+
const files = [];
|
|
45
|
+
for (const skill of names) {
|
|
46
|
+
const dir = join(packRoot, skillClass, skill);
|
|
47
|
+
if (!existsSync(dir)) throw new Error(`skill pack is missing ${skillClass}/${skill} under ${packRoot}`);
|
|
48
|
+
for (const rel of orderSkillFiles(filesUnder(dir))) {
|
|
49
|
+
const raw = readFileSync(join(dir, rel), "utf8");
|
|
50
|
+
files.push({
|
|
51
|
+
skill,
|
|
52
|
+
skillClass,
|
|
53
|
+
path: `${skillDir(skill)}/${rel}`,
|
|
54
|
+
content: rel === "SKILL.md" ? renderSkillFile(skillClass, raw, version) : raw
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return files;
|
|
59
|
+
}
|
|
60
|
+
function loadTemplateFiles(packRoot = resolvePackRoot(), version = resolvePackageVersion()) {
|
|
61
|
+
return loadClass(packRoot, "template", TEMPLATE_SKILLS, version);
|
|
62
|
+
}
|
|
63
|
+
function loadMaintainedFiles(packRoot = resolvePackRoot(), version = resolvePackageVersion()) {
|
|
64
|
+
return loadClass(packRoot, "maintained", MAINTAINED_SKILLS, version);
|
|
65
|
+
}
|
|
66
|
+
function writeMaintainedFiles(repoRoot, files) {
|
|
67
|
+
const created = [];
|
|
68
|
+
const rewritten = [];
|
|
69
|
+
for (const file of files) {
|
|
70
|
+
const abs = join(repoRoot, file.path);
|
|
71
|
+
const exists = existsSync(abs);
|
|
72
|
+
if (exists && readFileSync(abs, "utf8") === file.content) continue;
|
|
73
|
+
mkdirSync(dirname(abs), { recursive: true });
|
|
74
|
+
writeFileSync(abs, file.content);
|
|
75
|
+
(exists ? rewritten : created).push(file.path);
|
|
76
|
+
}
|
|
77
|
+
return { created, rewritten };
|
|
78
|
+
}
|
|
79
|
+
function maintainedFilesForExistingSkills(repoRoot, files) {
|
|
80
|
+
return files.filter((file) => existsSync(join(repoRoot, skillDir(file.skill))));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export { MAINTAINED_SKILLS, TEMPLATE_SKILLS, loadMaintainedFiles, loadTemplateFiles, maintainedFilesForExistingSkills, resolvePackRoot, skillDir, writeMaintainedFiles };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
3
|
import { homeFromEnv } from './chunk-VNVZDWO3.js';
|
|
4
|
-
import {
|
|
4
|
+
import { mkdirSync, chmodSync, writeFileSync, readFileSync, readdirSync, existsSync, unlinkSync } from 'fs';
|
|
5
5
|
import { join, isAbsolute, dirname, basename, resolve } from 'path';
|
|
6
6
|
import { execFileSync } from 'child_process';
|
|
7
7
|
|
|
@@ -74,8 +74,103 @@ function contextExcludesPresent(gitCommonDir) {
|
|
|
74
74
|
if (beginIdx === -1) return false;
|
|
75
75
|
return BLOCK_LINES.every((l, i) => lines[beginIdx + i] === l);
|
|
76
76
|
}
|
|
77
|
-
var
|
|
77
|
+
var HOOKS_DIR_NAME = "git-hooks";
|
|
78
|
+
var CO_AUTHOR_FILE = "co-author";
|
|
79
|
+
var HOOKS = [
|
|
80
|
+
"applypatch-msg",
|
|
81
|
+
"pre-applypatch",
|
|
82
|
+
"post-applypatch",
|
|
83
|
+
"pre-commit",
|
|
84
|
+
"pre-merge-commit",
|
|
85
|
+
"prepare-commit-msg",
|
|
86
|
+
"commit-msg",
|
|
87
|
+
"post-commit",
|
|
88
|
+
"pre-rebase",
|
|
89
|
+
"post-checkout",
|
|
90
|
+
"post-merge",
|
|
91
|
+
"pre-push",
|
|
92
|
+
"pre-receive",
|
|
93
|
+
"update",
|
|
94
|
+
"post-receive",
|
|
95
|
+
"post-update",
|
|
96
|
+
"reference-transaction",
|
|
97
|
+
"pre-auto-gc",
|
|
98
|
+
"post-rewrite",
|
|
99
|
+
"sendemail-validate",
|
|
100
|
+
"post-index-change"
|
|
101
|
+
];
|
|
102
|
+
var READS_STDIN = /* @__PURE__ */ new Set(["pre-push", "pre-receive", "post-receive", "reference-transaction", "post-rewrite"]);
|
|
78
103
|
var PRE_COMMIT_GUARD_MARKER = "# outerlayer materialized-context commit guard";
|
|
104
|
+
var RUN_GUARD = `common=$(git rev-parse --git-common-dir 2>/dev/null)
|
|
105
|
+
guard=$common/hooks/pre-commit
|
|
106
|
+
if [ "$guard" != "$hook" ] && [ -f "$guard" ] && [ -x "$guard" ] &&
|
|
107
|
+
sed -n 2p "$guard" | grep -q '^${PRE_COMMIT_GUARD_MARKER}'; then
|
|
108
|
+
"$guard" "$@" || exit $?
|
|
109
|
+
fi
|
|
110
|
+
`;
|
|
111
|
+
var ADD_TRAILER = `
|
|
112
|
+
# Every commit of the build names the requester once: a trailer already there is left alone.
|
|
113
|
+
file=\${1-}
|
|
114
|
+
if [ -n "$file" ] && [ -f "$file" ]; then
|
|
115
|
+
git interpret-trailers --in-place --if-exists addIfDifferent --trailer "Co-authored-by: $(cat "$here/${CO_AUTHOR_FILE}")" "$file" ||
|
|
116
|
+
echo "outerlayer: could not add the co-author line to the commit message" >&2
|
|
117
|
+
fi
|
|
118
|
+
`;
|
|
119
|
+
var HOOK_WRAPPER_MARKER = "# Written by the OuterLayer runner.";
|
|
120
|
+
function script(name) {
|
|
121
|
+
const adds = name === "prepare-commit-msg";
|
|
122
|
+
return `#!/bin/sh
|
|
123
|
+
${HOOK_WRAPPER_MARKER} Runs the repository's own ${name} hook, then
|
|
124
|
+
# ${adds ? "adds the co-author line to the commit message" : "hands its status on"}.
|
|
125
|
+
here=$(dirname "$0")
|
|
126
|
+
|
|
127
|
+
# The hooks path git would use without this directory: cut the list of configuration
|
|
128
|
+
# entries the runner passes just before its own core.hooksPath entry, then ask git.
|
|
129
|
+
n=\${GIT_CONFIG_COUNT-0}
|
|
130
|
+
case $n in '' | *[!0-9]*) n=0 ;; esac
|
|
131
|
+
cut=$n
|
|
132
|
+
i=$n
|
|
133
|
+
while [ "$i" -gt 0 ]; do
|
|
134
|
+
i=$((i - 1))
|
|
135
|
+
eval "key=\\\${GIT_CONFIG_KEY_$i-}"
|
|
136
|
+
eval "value=\\\${GIT_CONFIG_VALUE_$i-}"
|
|
137
|
+
if [ "$key" = core.hooksPath ] && { [ "$value" = "$here" ] || [ "$value" -ef "$here" ]; }; then
|
|
138
|
+
cut=$i
|
|
139
|
+
break
|
|
140
|
+
fi
|
|
141
|
+
done
|
|
142
|
+
path=$(GIT_CONFIG_COUNT=$cut git config --type=path core.hooksPath 2>/dev/null) || path=
|
|
143
|
+
if [ -z "$path" ]; then
|
|
144
|
+
path=$(git rev-parse --git-common-dir 2>/dev/null)/hooks
|
|
145
|
+
fi
|
|
146
|
+
|
|
147
|
+
hook=$path/${name}
|
|
148
|
+
${name === "pre-commit" ? RUN_GUARD : ""}status=0
|
|
149
|
+
if [ "$path" -ef "$here" ] || [ ! -f "$hook" ] || [ ! -x "$hook" ]; then
|
|
150
|
+
${READS_STDIN.has(name) ? " cat >/dev/null\n" : ""} :
|
|
151
|
+
else
|
|
152
|
+
${adds ? ` "$hook" "$@" || status=$?` : ` exec "$hook" "$@"`}
|
|
153
|
+
fi
|
|
154
|
+
${adds ? `[ "$status" -eq 0 ] || exit "$status"
|
|
155
|
+
${ADD_TRAILER}` : ""}exit 0
|
|
156
|
+
`;
|
|
157
|
+
}
|
|
158
|
+
function writeCommitHooks(buildDir, coAuthor) {
|
|
159
|
+
const dir = join(buildDir, HOOKS_DIR_NAME);
|
|
160
|
+
mkdirSync(dir, { recursive: true });
|
|
161
|
+
chmodSync(dir, 493);
|
|
162
|
+
for (const name of HOOKS) {
|
|
163
|
+
const path = join(dir, name);
|
|
164
|
+
writeFileSync(path, script(name));
|
|
165
|
+
chmodSync(path, 493);
|
|
166
|
+
}
|
|
167
|
+
const data = join(dir, CO_AUTHOR_FILE);
|
|
168
|
+
writeFileSync(data, `${coAuthor.name} <${coAuthor.email}>
|
|
169
|
+
`);
|
|
170
|
+
chmodSync(data, 420);
|
|
171
|
+
return dir;
|
|
172
|
+
}
|
|
173
|
+
var JOURNAL_HOOK_MARKER = "# outerlayer post-rewrite journal";
|
|
79
174
|
var PRE_COMMIT_GUARD_VERSION = `${PRE_COMMIT_GUARD_MARKER} v1`;
|
|
80
175
|
var PRE_COMMIT_GUARD_BODY_SIGNATURE = "outerlayer: refusing to commit materialized control-plane context";
|
|
81
176
|
var PRE_COMMIT_GUARD_SCRIPT = `#!/bin/sh
|
|
@@ -114,8 +209,8 @@ exit 1
|
|
|
114
209
|
function managedHookScripts() {
|
|
115
210
|
return [["pre-commit", printableHookScript(PRE_COMMIT_GUARD_SCRIPT)]];
|
|
116
211
|
}
|
|
117
|
-
function printableHookScript(
|
|
118
|
-
const lines =
|
|
212
|
+
function printableHookScript(script2) {
|
|
213
|
+
const lines = script2.split("\n");
|
|
119
214
|
return [lines[0], ...lines.slice(2)].join("\n");
|
|
120
215
|
}
|
|
121
216
|
function git(cwd, args) {
|
|
@@ -141,7 +236,7 @@ function commonHooksDir(cwd) {
|
|
|
141
236
|
if (rel === void 0) return void 0;
|
|
142
237
|
return join(isAbsolute(rel) ? rel : join(cwd, rel), "hooks");
|
|
143
238
|
}
|
|
144
|
-
function installHookFile(dir, name,
|
|
239
|
+
function installHookFile(dir, name, script2, marker) {
|
|
145
240
|
const path = join(dir, name);
|
|
146
241
|
let existing;
|
|
147
242
|
try {
|
|
@@ -151,7 +246,7 @@ function installHookFile(dir, name, script, marker) {
|
|
|
151
246
|
}
|
|
152
247
|
if (existing !== void 0) {
|
|
153
248
|
if (!isOwnRecognitionLine(existing, marker)) return "skipped-foreign";
|
|
154
|
-
if (existing ===
|
|
249
|
+
if (existing === script2) {
|
|
155
250
|
try {
|
|
156
251
|
chmodSync(path, 493);
|
|
157
252
|
} catch {
|
|
@@ -160,7 +255,7 @@ function installHookFile(dir, name, script, marker) {
|
|
|
160
255
|
}
|
|
161
256
|
}
|
|
162
257
|
mkdirSync(dir, { recursive: true });
|
|
163
|
-
writeFileSync(path,
|
|
258
|
+
writeFileSync(path, script2);
|
|
164
259
|
chmodSync(path, 493);
|
|
165
260
|
return existing === void 0 ? "installed" : "updated";
|
|
166
261
|
}
|
|
@@ -237,15 +332,29 @@ function expandHooksPathTilde(hooksPath) {
|
|
|
237
332
|
}
|
|
238
333
|
function ensurePreCommitGuardHook(repoRoot, gitCommonDir) {
|
|
239
334
|
const hooksPath = git(repoRoot, ["config", "core.hooksPath"]) ?? "";
|
|
240
|
-
if (hooksPath !== "") {
|
|
335
|
+
if (hooksPath !== "" && !isRunnerHookWrapper(hooksPath, repoRoot)) {
|
|
241
336
|
const expanded = expandHooksPathTilde(hooksPath);
|
|
242
337
|
const effectivePath = isAbsolute(expanded) ? join(expanded, "pre-commit") : join(repoRoot, expanded, "pre-commit");
|
|
243
338
|
return { status: "skipped-custom-hooks-path", adopted: guardAdoptedAtEffectiveHook(effectivePath) };
|
|
244
339
|
}
|
|
340
|
+
return installPreCommitGuardInGitDir(gitCommonDir);
|
|
341
|
+
}
|
|
342
|
+
function installPreCommitGuardInGitDir(gitCommonDir) {
|
|
245
343
|
const path = join(gitCommonDir, "hooks", "pre-commit");
|
|
246
344
|
const status = installHookFile(join(gitCommonDir, "hooks"), "pre-commit", PRE_COMMIT_GUARD_SCRIPT, PRE_COMMIT_GUARD_MARKER);
|
|
247
345
|
return { status, adopted: status === "skipped-foreign" ? guardBodyPresent(path) : true };
|
|
248
346
|
}
|
|
347
|
+
function isRunnerHookWrapper(hooksPath, repoRoot) {
|
|
348
|
+
const dir = resolve(repoRoot, expandHooksPathTilde(hooksPath));
|
|
349
|
+
if (basename(dir) !== HOOKS_DIR_NAME) return false;
|
|
350
|
+
try {
|
|
351
|
+
const script2 = readFileSync(join(dir, "pre-commit"), "utf8");
|
|
352
|
+
const secondLine = script2.indexOf("\n") + 1;
|
|
353
|
+
return secondLine > 0 && script2.startsWith(HOOK_WRAPPER_MARKER, secondLine);
|
|
354
|
+
} catch {
|
|
355
|
+
return false;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
249
358
|
function deadHooksState(cwd) {
|
|
250
359
|
const inRepo = git(cwd, ["rev-parse", "--is-inside-work-tree"]);
|
|
251
360
|
if (inRepo !== "true") return { dead: false };
|
|
@@ -282,11 +391,11 @@ function preparePackageManagerCommand(repoRoot) {
|
|
|
282
391
|
function huskyPrepareScript(repoRoot) {
|
|
283
392
|
try {
|
|
284
393
|
const pkg = JSON.parse(readFileSync(join(repoRoot, "package.json"), "utf8"));
|
|
285
|
-
const
|
|
286
|
-
if (typeof
|
|
394
|
+
const script2 = pkg.scripts?.prepare;
|
|
395
|
+
if (typeof script2 === "string" && /husky/i.test(script2)) return script2;
|
|
287
396
|
} catch {
|
|
288
397
|
}
|
|
289
398
|
return null;
|
|
290
399
|
}
|
|
291
400
|
|
|
292
|
-
export { contextExcludesPresent, deadHooksState, ensureContextExcludes, ensurePreCommitGuardHook, huskyPrepareScript, installPreCommitGuardHook, managedHookScripts, preparePackageManagerCommand, removeJournalHooks, removePreCommitGuardHook };
|
|
401
|
+
export { HOOKS_DIR_NAME, contextExcludesPresent, deadHooksState, ensureContextExcludes, ensurePreCommitGuardHook, huskyPrepareScript, installPreCommitGuardHook, installPreCommitGuardInGitDir, managedHookScripts, preparePackageManagerCommand, removeJournalHooks, removePreCommitGuardHook, writeCommitHooks };
|
|
@@ -1,17 +1,55 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { readPin, pinExists, writeDegraded, writeSourceCache, SOURCE_CACHE_TTL_RETRY_MS, SOURCE_CACHE_TTL_SUCCESS_MS,
|
|
4
|
-
import {
|
|
3
|
+
import { readPin, pinExists, readBuildHold, readContextManifest, manifestFilesPresent, writeDegraded, writeSourceCache, SOURCE_CACHE_TTL_RETRY_MS, SOURCE_CACHE_TTL_SUCCESS_MS, isLastCheckFresh, readDegraded, touchLastCheck, clearDegraded, writeContextManifest, writePinIfChanged, readSourceCache, isSourceCacheFresh } from './chunk-WIOZAJ2W.js';
|
|
4
|
+
import { loadMaintainedFiles, writeMaintainedFiles } from './chunk-WO2BXCTQ.js';
|
|
5
|
+
import { parseGoverns, normalizeRepoName, CONTEXT_SOURCE_CONFIG_PATH, parseOuterlayerConfig, classifyTree, emitTree } from './chunk-EMY4I27X.js';
|
|
5
6
|
import { appendContextProvenance } from './chunk-ZJSNLVCE.js';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
7
|
+
import { defaultFetch } from './chunk-BCLJSQCV.js';
|
|
8
|
+
import { resolveGatewayCredentials } from './chunk-BOLTI6LR.js';
|
|
9
|
+
import { ensureContextExcludes, ensurePreCommitGuardHook } from './chunk-XCCLFVXM.js';
|
|
8
10
|
import { homeFromEnv } from './chunk-VNVZDWO3.js';
|
|
9
|
-
import './chunk-Z5GEFMRW.js';
|
|
10
11
|
import { execFileSync } from 'child_process';
|
|
11
|
-
import { existsSync, mkdirSync, writeFileSync, chmodSync, unlinkSync, readdirSync, rmSync,
|
|
12
|
-
import {
|
|
12
|
+
import { lstatSync, existsSync, mkdirSync, writeFileSync, chmodSync, unlinkSync, readdirSync, rmSync, readFileSync, copyFileSync } from 'fs';
|
|
13
|
+
import { join, isAbsolute, dirname, relative } from 'path';
|
|
13
14
|
|
|
14
15
|
createRequire(import.meta.url);
|
|
16
|
+
var CONFIG_PATH = ".outerlayer/config.json";
|
|
17
|
+
var CONTENT_KINDS = /* @__PURE__ */ new Set(["skill", "skill-reference"]);
|
|
18
|
+
function overlayMaintainedSkills(repoRoot, unsafe) {
|
|
19
|
+
const sources = loadMaintainedFiles().filter((file) => !unsafe(file.path));
|
|
20
|
+
writeMaintainedFiles(repoRoot, sources);
|
|
21
|
+
const paths = sources.map((file) => file.path);
|
|
22
|
+
const configAbs = join(repoRoot, CONFIG_PATH);
|
|
23
|
+
if (!existsSync(configAbs)) return paths;
|
|
24
|
+
const config = parseOuterlayerConfig(readFileSync(configAbs, "utf8"));
|
|
25
|
+
if (!config.ok || !config.config) return paths;
|
|
26
|
+
const { entries } = classifyTree(sources.map((file) => file.path));
|
|
27
|
+
const input = {
|
|
28
|
+
entries,
|
|
29
|
+
contents: new Map(entries.filter((e) => CONTENT_KINDS.has(e.kind)).map((e) => [e.path, sources.find((f) => f.path === e.path).content])),
|
|
30
|
+
assetPaths: [],
|
|
31
|
+
targets: config.config.targets
|
|
32
|
+
};
|
|
33
|
+
const compiled = emitTree(input);
|
|
34
|
+
if (compiled.errors.length > 0) return paths;
|
|
35
|
+
for (const file of compiled.files) {
|
|
36
|
+
if (unsafe(file.path)) continue;
|
|
37
|
+
const abs = join(repoRoot, file.path);
|
|
38
|
+
mkdirSync(dirname(abs), { recursive: true });
|
|
39
|
+
if (!existsSync(abs) || readFileSync(abs, "utf8") !== file.content) writeFileSync(abs, file.content);
|
|
40
|
+
paths.push(file.path);
|
|
41
|
+
}
|
|
42
|
+
for (const copy of compiled.copies) {
|
|
43
|
+
if (unsafe(copy.toPath)) continue;
|
|
44
|
+
const abs = join(repoRoot, copy.toPath);
|
|
45
|
+
mkdirSync(dirname(abs), { recursive: true });
|
|
46
|
+
copyFileSync(join(repoRoot, copy.fromSourcePath), abs);
|
|
47
|
+
paths.push(copy.toPath);
|
|
48
|
+
}
|
|
49
|
+
return paths;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// src/context-materialize.ts
|
|
15
53
|
var defaultGitRunner = (args, opts) => {
|
|
16
54
|
try {
|
|
17
55
|
const stdout = execFileSync("git", args, {
|
|
@@ -54,8 +92,11 @@ function gitText(git, cwd, args, timeoutMs) {
|
|
|
54
92
|
var NOT_GOVERNED_MESSAGE = "this repository's control plane no longer governs it";
|
|
55
93
|
var ADOPTION_MESSAGE = "committed context files shadow the control plane \u2014 remove them in a commit";
|
|
56
94
|
var CANNOT_IDENTIFY_MESSAGE = "this repository is governed, but the control plane cannot be identified \u2014 check OuterLayer connectivity/config";
|
|
57
|
-
|
|
95
|
+
function designationAuthRejectedMessage(credential) {
|
|
96
|
+
return credential.startsWith("olu_") ? "the OuterLayer login was rejected (401/403) \u2014 run `outerlayer login` to sign in again" : "the OuterLayer API key was rejected (401/403) \u2014 check apiKey in ~/.outerlayer/config.json";
|
|
97
|
+
}
|
|
58
98
|
var PARTIAL_WRITE_MESSAGE = "control-plane content could not be fully written \u2014 running on previous context";
|
|
99
|
+
var OVERLAY_FAILED_MESSAGE = "control-plane content was written but the maintained skills could not be refreshed \u2014 retrying next session";
|
|
59
100
|
var COMMIT_GUARD_FOREIGN_NOT_INSTALLED_MESSAGE = "the materialized-context commit guard is NOT installed \u2014 a pre-commit hook this clone did not write occupies that slot \u2014 resolve the conflict or run `outerlayer hooks install-git`";
|
|
60
101
|
var COMMIT_GUARD_HOOKSPATH_NOT_INSTALLED_MESSAGE = "the materialized-context commit guard is NOT installed \u2014 hooks are managed via core.hooksPath, which this clone never writes to automatically \u2014 adopt it yourself: `outerlayer hooks install-git --print`";
|
|
61
102
|
function guardNotInstalledMessage(guard) {
|
|
@@ -100,7 +141,7 @@ async function materializeContext(deps) {
|
|
|
100
141
|
const home = deps.home ?? homeFromEnv();
|
|
101
142
|
const now = deps.now ?? Date.now;
|
|
102
143
|
const git = deps.git ?? defaultGitRunner;
|
|
103
|
-
const fetchImpl = deps.fetchImpl ??
|
|
144
|
+
const fetchImpl = deps.fetchImpl ?? defaultFetch;
|
|
104
145
|
const repoRoot = gitText(git, deps.cwd, ["rev-parse", "--show-toplevel"], 2e3);
|
|
105
146
|
if (repoRoot === void 0) {
|
|
106
147
|
return { status: "not-a-repo", sha: null, verification: "none" };
|
|
@@ -125,34 +166,53 @@ async function materializeContext(deps) {
|
|
|
125
166
|
clearDegraded(gitCommonDir);
|
|
126
167
|
return { status: "self", sha: null, verification: "none" };
|
|
127
168
|
};
|
|
169
|
+
const hold = readBuildHold(gitCommonDir);
|
|
170
|
+
if (hold !== void 0) {
|
|
171
|
+
const heldManifest = readContextManifest(gitCommonDir);
|
|
172
|
+
if (heldManifest !== void 0 && heldManifest.sha === hold.sha && pin?.sha === hold.sha && manifestFilesPresent(repoRoot, heldManifest)) {
|
|
173
|
+
ensureContextExcludes(gitCommonDir);
|
|
174
|
+
recordMaintainedOverlay(repoRoot, gitCommonDir, heldManifest);
|
|
175
|
+
const guardMessage2 = guardNotInstalledMessage(ensurePreCommitGuardHook(repoRoot, gitCommonDir));
|
|
176
|
+
record(hold.sha, "verified", hold.repository, hold.ref);
|
|
177
|
+
return {
|
|
178
|
+
status: "debounced",
|
|
179
|
+
sha: hold.sha,
|
|
180
|
+
verification: "verified",
|
|
181
|
+
...guardMessage2 ? { message: guardMessage2 } : {}
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
}
|
|
128
185
|
const ownRepoName = deriveOwnRepoName(git, repoRoot);
|
|
129
186
|
if (ownRepoName === void 0) {
|
|
130
187
|
return selfOutcome();
|
|
131
188
|
}
|
|
132
|
-
const designation = await resolveDesignation({ git, fetchImpl, home, now, gitCommonDir, ownRepoName });
|
|
189
|
+
const designation = await resolveDesignation({ git, fetchImpl, home, cloud: deps.cloud, now, gitCommonDir, ownRepoName, repoRoot });
|
|
190
|
+
const answered = { designation: "answered" };
|
|
133
191
|
if (designation.status === "self") {
|
|
134
|
-
return selfOutcome();
|
|
192
|
+
return { ...selfOutcome(), ...answered };
|
|
135
193
|
}
|
|
136
194
|
if (designation.status === "unknown") {
|
|
137
|
-
const message = designation.authRejected
|
|
195
|
+
const message = designation.authRejected ?? CANNOT_IDENTIFY_MESSAGE;
|
|
138
196
|
if (hasPin) {
|
|
139
197
|
writeDegraded(gitCommonDir, { at: new Date(now()).toISOString(), kind: "designation-unverified", message });
|
|
140
198
|
record(null, "none");
|
|
141
|
-
return { status: "designation-unverified", message, sha: null, verification: "none" };
|
|
199
|
+
return { status: "designation-unverified", message, sha: null, verification: "none", designation: "unknown" };
|
|
142
200
|
}
|
|
143
201
|
if (designation.authRejected) {
|
|
144
202
|
writeDegraded(gitCommonDir, { at: new Date(now()).toISOString(), kind: "designation-unverified", message });
|
|
145
203
|
}
|
|
146
204
|
writeSourceCache(gitCommonDir, { checkedAt: new Date(now()).toISOString(), status: "self", ttlMs: SOURCE_CACHE_TTL_RETRY_MS });
|
|
147
|
-
return { status: "self", sha: null, verification: "none" };
|
|
205
|
+
return { status: "self", sha: null, verification: "none", designation: "unknown", ...designation.authRejected ? { authRejected: true } : {} };
|
|
148
206
|
}
|
|
149
207
|
let repository;
|
|
150
208
|
let ref;
|
|
151
209
|
let unverifiedIdentity;
|
|
152
210
|
let preFetched;
|
|
211
|
+
let unreadableCandidates = [];
|
|
153
212
|
if (designation.status === "candidates") {
|
|
154
213
|
const sortedCandidates = [...designation.candidates].sort((a, b) => a.repository.localeCompare(b.repository));
|
|
155
214
|
const claimants = [];
|
|
215
|
+
const unfetched = [];
|
|
156
216
|
for (const candidate of sortedCandidates) {
|
|
157
217
|
const candidateUrl = deps.resolveControlPlaneUrl ? deps.resolveControlPlaneUrl(candidate.repository, candidate.ref, repoRoot) : buildControlPlaneUrl(git, repoRoot, candidate.repository);
|
|
158
218
|
const candidateHost = parseRemoteHost(candidateUrl).host || "unknown-host";
|
|
@@ -162,9 +222,15 @@ async function materializeContext(deps) {
|
|
|
162
222
|
cwd: candidateCacheDir,
|
|
163
223
|
timeoutMs: 1e4
|
|
164
224
|
});
|
|
165
|
-
if (!candidateFetch.ok)
|
|
225
|
+
if (!candidateFetch.ok) {
|
|
226
|
+
unfetched.push(candidate.repository);
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
166
229
|
const candidateSha = gitText(git, candidateCacheDir, ["rev-parse", candidateLocalRef], 2e3);
|
|
167
|
-
if (candidateSha === void 0)
|
|
230
|
+
if (candidateSha === void 0) {
|
|
231
|
+
unfetched.push(candidate.repository);
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
168
234
|
const candidateConfig = readBlobAtPath(git, candidateCacheDir, candidateSha, CONTEXT_SOURCE_CONFIG_PATH);
|
|
169
235
|
const candidateGoverns = candidateConfig === void 0 ? { governs: [] } : parseGoverns(candidateConfig);
|
|
170
236
|
const candidateGovernsList = "error" in candidateGoverns ? [] : candidateGoverns.governs;
|
|
@@ -174,7 +240,7 @@ async function materializeContext(deps) {
|
|
|
174
240
|
}
|
|
175
241
|
if (claimants.length === 0) {
|
|
176
242
|
writeSourceCache(gitCommonDir, { checkedAt: new Date(now()).toISOString(), status: "self", ttlMs: SOURCE_CACHE_TTL_SUCCESS_MS });
|
|
177
|
-
return selfOutcome();
|
|
243
|
+
return { ...selfOutcome(), ...answered, ...unfetched.length > 0 ? { unfetched } : {} };
|
|
178
244
|
}
|
|
179
245
|
if (claimants.length > 1) {
|
|
180
246
|
const names = claimants.map((c) => c.repository).sort();
|
|
@@ -186,6 +252,7 @@ async function materializeContext(deps) {
|
|
|
186
252
|
record(lastGood, verification);
|
|
187
253
|
return { status: "conflict", message, sha: lastGood, verification };
|
|
188
254
|
}
|
|
255
|
+
unreadableCandidates = unfetched;
|
|
189
256
|
const winner = claimants[0];
|
|
190
257
|
writeSourceCache(gitCommonDir, {
|
|
191
258
|
checkedAt: new Date(now()).toISOString(),
|
|
@@ -227,6 +294,7 @@ async function materializeContext(deps) {
|
|
|
227
294
|
const degraded = readDegraded(gitCommonDir);
|
|
228
295
|
const verification = degraded ? "unverified" : "verified";
|
|
229
296
|
ensureContextExcludes(gitCommonDir);
|
|
297
|
+
recordMaintainedOverlay(repoRoot, gitCommonDir, manifest);
|
|
230
298
|
const guardMessage2 = guardNotInstalledMessage(ensurePreCommitGuardHook(repoRoot, gitCommonDir));
|
|
231
299
|
record(manifest.sha, verification, repository, ref);
|
|
232
300
|
return {
|
|
@@ -255,6 +323,7 @@ async function materializeContext(deps) {
|
|
|
255
323
|
touchLastCheck(gitCommonDir, now());
|
|
256
324
|
clearOrFlagIdentity();
|
|
257
325
|
ensureContextExcludes(gitCommonDir);
|
|
326
|
+
recordMaintainedOverlay(repoRoot, gitCommonDir, manifest);
|
|
258
327
|
const guardMessage2 = guardNotInstalledMessage(ensurePreCommitGuardHook(repoRoot, gitCommonDir));
|
|
259
328
|
writeSourceCache(gitCommonDir, { checkedAt: new Date(now()).toISOString(), status: "governed", repository, ref, ttlMs: SOURCE_CACHE_TTL_SUCCESS_MS });
|
|
260
329
|
record(remoteSha, "verified", repository, ref);
|
|
@@ -335,14 +404,36 @@ async function materializeContext(deps) {
|
|
|
335
404
|
if (!writeResult.ok) {
|
|
336
405
|
return partialWrite();
|
|
337
406
|
}
|
|
338
|
-
|
|
407
|
+
let overlayPaths = [];
|
|
408
|
+
let overlayFailed = false;
|
|
409
|
+
try {
|
|
410
|
+
overlayPaths = overlayMaintainedSkills(repoRoot, (rel) => hasSymlinkComponent(repoRoot, rel));
|
|
411
|
+
} catch {
|
|
412
|
+
overlayFailed = true;
|
|
413
|
+
}
|
|
414
|
+
const writtenPaths = [.../* @__PURE__ */ new Set([...writeResult.writtenPaths, ...overlayPaths])];
|
|
415
|
+
const deleteResult = manifest ? deleteRemovedFiles(repoRoot, manifest.files, writtenPaths) : { skipped: [] };
|
|
416
|
+
if (overlayFailed && deleteResult.skipped.length === 0) {
|
|
417
|
+
writeContextManifest(gitCommonDir, { schemaVersion: 1, sha, files: writtenPaths, materializedAt: new Date(now()).toISOString() });
|
|
418
|
+
writeDegraded(gitCommonDir, {
|
|
419
|
+
at: new Date(now()).toISOString(),
|
|
420
|
+
kind: "unreachable",
|
|
421
|
+
message: OVERLAY_FAILED_MESSAGE,
|
|
422
|
+
repository,
|
|
423
|
+
ref,
|
|
424
|
+
lastGoodSha: sha
|
|
425
|
+
});
|
|
426
|
+
if (!evidenced) return { status: "unreachable", sha: null, verification: "none" };
|
|
427
|
+
record(sha, "unverified", repository, ref);
|
|
428
|
+
return { status: "unreachable", message: OVERLAY_FAILED_MESSAGE, sha, verification: "unverified" };
|
|
429
|
+
}
|
|
339
430
|
if (deleteResult.skipped.length > 0) {
|
|
340
431
|
return partialWrite();
|
|
341
432
|
}
|
|
342
433
|
const newManifest = {
|
|
343
434
|
schemaVersion: 1,
|
|
344
435
|
sha,
|
|
345
|
-
files:
|
|
436
|
+
files: writtenPaths,
|
|
346
437
|
materializedAt: new Date(now()).toISOString()
|
|
347
438
|
};
|
|
348
439
|
writeContextManifest(gitCommonDir, newManifest);
|
|
@@ -357,23 +448,40 @@ async function materializeContext(deps) {
|
|
|
357
448
|
status: "materialized",
|
|
358
449
|
sha,
|
|
359
450
|
verification: "verified",
|
|
360
|
-
...guardMessage ? { message: guardMessage } : {}
|
|
451
|
+
...guardMessage ? { message: guardMessage } : {},
|
|
452
|
+
...unreadableCandidates.length > 0 ? { unfetched: unreadableCandidates } : {}
|
|
361
453
|
};
|
|
362
454
|
}
|
|
455
|
+
function recordMaintainedOverlay(repoRoot, gitCommonDir, manifest) {
|
|
456
|
+
let overlayPaths;
|
|
457
|
+
try {
|
|
458
|
+
overlayPaths = overlayMaintainedSkills(repoRoot, (rel) => hasSymlinkComponent(repoRoot, rel));
|
|
459
|
+
} catch {
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
const known = new Set(manifest.files);
|
|
463
|
+
const added = overlayPaths.filter((path) => !known.has(path));
|
|
464
|
+
if (added.length > 0) writeContextManifest(gitCommonDir, { ...manifest, files: [...manifest.files, ...added] });
|
|
465
|
+
}
|
|
363
466
|
async function resolveDesignation(ctx) {
|
|
364
467
|
const cache = readSourceCache(ctx.gitCommonDir);
|
|
365
468
|
if (cache && isSourceCacheFresh(cache, ctx.now())) {
|
|
366
469
|
return cache.status === "governed" && cache.repository && cache.ref ? { status: "governed", repository: cache.repository, ref: cache.ref } : { status: "self" };
|
|
367
470
|
}
|
|
368
|
-
let authRejected
|
|
369
|
-
const
|
|
370
|
-
|
|
471
|
+
let authRejected;
|
|
472
|
+
const destination = resolveGatewayCredentials({
|
|
473
|
+
home: ctx.home,
|
|
474
|
+
env: {},
|
|
475
|
+
cwd: ctx.repoRoot,
|
|
476
|
+
...ctx.cloud === void 0 ? {} : { url: ctx.cloud.url, apiKey: ctx.cloud.apiKey, appId: ctx.cloud.appId }
|
|
477
|
+
});
|
|
478
|
+
if (destination.ok) {
|
|
371
479
|
const sources = await fetchContextSource(
|
|
372
|
-
|
|
480
|
+
{ url: destination.url, apiKey: destination.credential, appId: destination.appId },
|
|
373
481
|
ctx.ownRepoName,
|
|
374
482
|
ctx.fetchImpl,
|
|
375
483
|
() => {
|
|
376
|
-
authRejected =
|
|
484
|
+
authRejected = designationAuthRejectedMessage(destination.credential);
|
|
377
485
|
}
|
|
378
486
|
);
|
|
379
487
|
if (sources !== void 0) {
|
|
@@ -586,4 +694,4 @@ function pruneEmptyDirs(dir, stopAt) {
|
|
|
586
694
|
}
|
|
587
695
|
}
|
|
588
696
|
|
|
589
|
-
export { defaultGitRunner, materializeContext, parseOwnerRepo, parseRemoteHost };
|
|
697
|
+
export { defaultGitRunner, hasSymlinkComponent, materializeContext, parseOwnerRepo, parseRemoteHost, resolveGitCommonDir };
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { runnerDir, RUNNER_DIR_MODE, RUNNER_FILE_MODE } from './chunk-ZM2IMMYN.js';
|
|
4
|
+
import { readlinkSync, realpathSync, mkdirSync, unlinkSync, symlinkSync, renameSync, readFileSync, writeFileSync, rmSync, existsSync, readdirSync } from 'fs';
|
|
5
|
+
import { join, basename, sep, resolve } from 'path';
|
|
6
|
+
|
|
7
|
+
createRequire(import.meta.url);
|
|
8
|
+
var PACKAGE_PATH = join("node_modules", "@outerlayer", "cli");
|
|
9
|
+
var STAGING_PREFIX = ".staging-";
|
|
10
|
+
function versionsDir(configPath) {
|
|
11
|
+
return join(runnerDir(configPath), "versions");
|
|
12
|
+
}
|
|
13
|
+
function versionDir(configPath, version) {
|
|
14
|
+
return join(versionsDir(configPath), version);
|
|
15
|
+
}
|
|
16
|
+
function stagingDir(configPath, version) {
|
|
17
|
+
return join(versionsDir(configPath), `${STAGING_PREFIX}${version}`);
|
|
18
|
+
}
|
|
19
|
+
function currentLink(configPath) {
|
|
20
|
+
return join(runnerDir(configPath), "current");
|
|
21
|
+
}
|
|
22
|
+
function markerPath(configPath) {
|
|
23
|
+
return join(runnerDir(configPath), "update-marker.json");
|
|
24
|
+
}
|
|
25
|
+
function packageRootIn(dir) {
|
|
26
|
+
return join(dir, PACKAGE_PATH);
|
|
27
|
+
}
|
|
28
|
+
function entryIn(dir) {
|
|
29
|
+
return join(packageRootIn(dir), "dist", "index.js");
|
|
30
|
+
}
|
|
31
|
+
function currentVersion(configPath) {
|
|
32
|
+
try {
|
|
33
|
+
return basename(readlinkSync(currentLink(configPath)));
|
|
34
|
+
} catch {
|
|
35
|
+
return void 0;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function isInLayout(configPath, entryPath) {
|
|
39
|
+
try {
|
|
40
|
+
const versions = realpathSync(versionsDir(configPath));
|
|
41
|
+
return realpathSync(entryPath).startsWith(`${versions}${sep}`);
|
|
42
|
+
} catch {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function startedThroughCurrent(configPath, entryPath) {
|
|
47
|
+
const parts = resolve(entryPath).split(sep);
|
|
48
|
+
const at = parts.lastIndexOf("current");
|
|
49
|
+
if (at <= 0 || parts.slice(at + 1).join(sep) !== join(PACKAGE_PATH, "dist", "index.js")) return false;
|
|
50
|
+
try {
|
|
51
|
+
return realpathSync(parts.slice(0, at).join(sep) || sep) === realpathSync(runnerDir(configPath));
|
|
52
|
+
} catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function switchCurrent(configPath, version) {
|
|
57
|
+
mkdirSync(runnerDir(configPath), { recursive: true, mode: RUNNER_DIR_MODE });
|
|
58
|
+
const temporary = `${currentLink(configPath)}.new`;
|
|
59
|
+
try {
|
|
60
|
+
unlinkSync(temporary);
|
|
61
|
+
} catch (error) {
|
|
62
|
+
if (error.code !== "ENOENT") throw error;
|
|
63
|
+
}
|
|
64
|
+
symlinkSync(versionDir(configPath, version), temporary);
|
|
65
|
+
renameSync(temporary, currentLink(configPath));
|
|
66
|
+
}
|
|
67
|
+
function readMarker(configPath) {
|
|
68
|
+
try {
|
|
69
|
+
const parsed = JSON.parse(readFileSync(markerPath(configPath), "utf8"));
|
|
70
|
+
if (typeof parsed.from !== "string" || typeof parsed.to !== "string") return void 0;
|
|
71
|
+
return { from: parsed.from, to: parsed.to, starts: typeof parsed.starts === "number" && Number.isInteger(parsed.starts) ? parsed.starts : 0 };
|
|
72
|
+
} catch {
|
|
73
|
+
return void 0;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function writeMarker(configPath, marker) {
|
|
77
|
+
mkdirSync(runnerDir(configPath), { recursive: true, mode: RUNNER_DIR_MODE });
|
|
78
|
+
const temporary = `${markerPath(configPath)}.tmp`;
|
|
79
|
+
writeFileSync(temporary, `${JSON.stringify(marker)}
|
|
80
|
+
`, { mode: RUNNER_FILE_MODE });
|
|
81
|
+
renameSync(temporary, markerPath(configPath));
|
|
82
|
+
}
|
|
83
|
+
function removeMarker(configPath) {
|
|
84
|
+
rmSync(markerPath(configPath), { force: true });
|
|
85
|
+
}
|
|
86
|
+
function cleanVersions(configPath, keep) {
|
|
87
|
+
const dir = versionsDir(configPath);
|
|
88
|
+
if (!existsSync(dir)) return;
|
|
89
|
+
for (const name of readdirSync(dir)) {
|
|
90
|
+
if (keep.includes(name)) continue;
|
|
91
|
+
rmSync(join(dir, name), { recursive: true, force: true });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function execStartCommand(configPath, nodePath) {
|
|
95
|
+
return `${nodePath} ${join(currentLink(configPath), "node_modules", "@outerlayer", "cli", "dist", "index.js")} runner start --config ${configPath}`;
|
|
96
|
+
}
|
|
97
|
+
function execStartLine(configPath, nodePath) {
|
|
98
|
+
return `ExecStart=${execStartCommand(configPath, nodePath)}`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export { cleanVersions, currentVersion, entryIn, execStartCommand, execStartLine, isInLayout, packageRootIn, readMarker, removeMarker, stagingDir, startedThroughCurrent, switchCurrent, versionDir, writeMarker };
|