@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,23 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { readFileSync } from 'fs';
|
|
4
|
+
import { dirname, join } from 'path';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
|
|
7
|
+
createRequire(import.meta.url);
|
|
8
|
+
function currentCliBuild(entryPath = fileURLToPath(import.meta.url)) {
|
|
9
|
+
const distDir = dirname(entryPath);
|
|
10
|
+
const read = (path) => {
|
|
11
|
+
try {
|
|
12
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
13
|
+
} catch {
|
|
14
|
+
return {};
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
const id = read(join(distDir, "build-info.json")).id;
|
|
18
|
+
if (typeof id === "string" && id !== "") return { distDir, buildId: id };
|
|
19
|
+
const version = read(join(dirname(distDir), "package.json")).version;
|
|
20
|
+
return { distDir, buildId: `${typeof version === "string" ? version : "unknown"}+unstamped` };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export { currentCliBuild };
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { existsSync, readdirSync, readFileSync, lstatSync, realpathSync, writeFileSync } from 'fs';
|
|
4
|
+
import { join, resolve, relative, sep, basename, dirname } from 'path';
|
|
5
|
+
|
|
6
|
+
createRequire(import.meta.url);
|
|
7
|
+
|
|
8
|
+
// src/runner/image/properties.ts
|
|
9
|
+
var LIFECYCLE = ["onCreateCommand", "updateContentCommand", "postCreateCommand", "postStartCommand"];
|
|
10
|
+
var HONOURED = ["image", "build", "features", "containerEnv", "remoteUser", ...LIFECYCLE];
|
|
11
|
+
var IGNORED_BUILD_KEYS = ["options"];
|
|
12
|
+
var HOST_REFERENCE = /\$\{(?:localEnv|env)[:}]/;
|
|
13
|
+
var DROP = /* @__PURE__ */ Symbol("drop");
|
|
14
|
+
function isHonoured(property) {
|
|
15
|
+
return HONOURED.includes(property);
|
|
16
|
+
}
|
|
17
|
+
function isObject(value) {
|
|
18
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
19
|
+
}
|
|
20
|
+
function featureName(id) {
|
|
21
|
+
const slash = id.lastIndexOf("/");
|
|
22
|
+
const cut = id.slice(slash + 1).search(/[:@]/);
|
|
23
|
+
return cut === -1 ? id : id.slice(0, slash + 1 + cut);
|
|
24
|
+
}
|
|
25
|
+
var DOCKER_FEATURE = "ghcr.io/devcontainers/features/docker-in-docker";
|
|
26
|
+
function asksForDocker(recipe, metadata) {
|
|
27
|
+
const isDocker = (id) => featureName(id) === DOCKER_FEATURE;
|
|
28
|
+
if (isObject(recipe.features) && Object.keys(recipe.features).some(isDocker)) return true;
|
|
29
|
+
return metadata.some((entry) => typeof entry.id === "string" && isDocker(entry.id));
|
|
30
|
+
}
|
|
31
|
+
function sameValue(a, b) {
|
|
32
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
33
|
+
}
|
|
34
|
+
function isFilesOwnEntry(entry, recipe) {
|
|
35
|
+
if (typeof entry.id === "string") return false;
|
|
36
|
+
const keys = Object.keys(entry);
|
|
37
|
+
return keys.length > 0 && keys.every((key) => key in recipe && sameValue(entry[key], recipe[key]));
|
|
38
|
+
}
|
|
39
|
+
function withoutHostReferences(value, path, dropped) {
|
|
40
|
+
if (typeof value === "string") {
|
|
41
|
+
if (!HOST_REFERENCE.test(value)) return value;
|
|
42
|
+
dropped.push(path);
|
|
43
|
+
return DROP;
|
|
44
|
+
}
|
|
45
|
+
if (Array.isArray(value)) {
|
|
46
|
+
if (!HOST_REFERENCE.test(JSON.stringify(value))) return value;
|
|
47
|
+
dropped.push(path);
|
|
48
|
+
return DROP;
|
|
49
|
+
}
|
|
50
|
+
if (isObject(value)) {
|
|
51
|
+
const kept = {};
|
|
52
|
+
for (const [key, item] of Object.entries(value)) {
|
|
53
|
+
if (HOST_REFERENCE.test(key)) {
|
|
54
|
+
dropped.push(`${path}.${key}`);
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const clean = withoutHostReferences(item, `${path}.${key}`, dropped);
|
|
58
|
+
if (clean !== DROP) kept[key] = clean;
|
|
59
|
+
}
|
|
60
|
+
return kept;
|
|
61
|
+
}
|
|
62
|
+
return value;
|
|
63
|
+
}
|
|
64
|
+
function honouredWithoutHostReferences(entry) {
|
|
65
|
+
const kept = {};
|
|
66
|
+
const dropped = [];
|
|
67
|
+
for (const property of HONOURED) {
|
|
68
|
+
if (!(property in entry)) continue;
|
|
69
|
+
const value = entry[property];
|
|
70
|
+
const clean = withoutHostReferences(
|
|
71
|
+
property === "build" && isObject(value) ? withoutIgnoredBuildKeys(value) : value,
|
|
72
|
+
property,
|
|
73
|
+
dropped
|
|
74
|
+
);
|
|
75
|
+
if (clean !== DROP) kept[property] = clean;
|
|
76
|
+
}
|
|
77
|
+
return { kept, dropped };
|
|
78
|
+
}
|
|
79
|
+
function withoutIgnoredBuildKeys(build) {
|
|
80
|
+
return Object.fromEntries(Object.entries(build).filter(([key]) => !IGNORED_BUILD_KEYS.includes(key)));
|
|
81
|
+
}
|
|
82
|
+
function ignoredIn(entry, source) {
|
|
83
|
+
const found = [];
|
|
84
|
+
for (const property of Object.keys(entry)) {
|
|
85
|
+
if (property === "id" || property.startsWith("$") || isHonoured(property)) continue;
|
|
86
|
+
found.push({ property, source });
|
|
87
|
+
}
|
|
88
|
+
if (isObject(entry.build)) {
|
|
89
|
+
for (const key of IGNORED_BUILD_KEYS) {
|
|
90
|
+
if (key in entry.build) found.push({ property: `build.${key}`, source });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
for (const property of honouredWithoutHostReferences(entry).dropped) found.push({ property, source });
|
|
94
|
+
return found;
|
|
95
|
+
}
|
|
96
|
+
function emptyApply() {
|
|
97
|
+
return { containerEnv: {}, lifecycle: { onCreateCommand: [], updateContentCommand: [], postCreateCommand: [], postStartCommand: [] } };
|
|
98
|
+
}
|
|
99
|
+
function applyEntry(apply, raw, source) {
|
|
100
|
+
const entry = honouredWithoutHostReferences(raw).kept;
|
|
101
|
+
if (typeof entry.remoteUser === "string") apply.remoteUser = entry.remoteUser;
|
|
102
|
+
if (isObject(entry.containerEnv)) {
|
|
103
|
+
for (const [key, value] of Object.entries(entry.containerEnv)) {
|
|
104
|
+
if (typeof value === "string") apply.containerEnv[key] = value;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
for (const name of LIFECYCLE) {
|
|
108
|
+
if (entry[name] !== void 0) apply.lifecycle[name].push({ source, command: entry[name] });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function classifyProperties(recipe, metadata) {
|
|
112
|
+
const ignored = ignoredIn(recipe, "file");
|
|
113
|
+
const apply = emptyApply();
|
|
114
|
+
let fileApplied = false;
|
|
115
|
+
const sanitized = sanitizeRecipe(recipe);
|
|
116
|
+
for (const entry of metadata) {
|
|
117
|
+
if (isFilesOwnEntry(entry, sanitized)) {
|
|
118
|
+
applyEntry(apply, entry, "file");
|
|
119
|
+
fileApplied = true;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
const source = typeof entry.id === "string" ? `feature ${featureName(entry.id)}` : "image label";
|
|
123
|
+
ignored.push(...ignoredIn(entry, source));
|
|
124
|
+
applyEntry(apply, entry, source);
|
|
125
|
+
}
|
|
126
|
+
if (!fileApplied) applyEntry(apply, recipe, "file");
|
|
127
|
+
return { docker: asksForDocker(recipe, metadata), honoured: HONOURED.filter((property) => property in recipe), ignored, apply };
|
|
128
|
+
}
|
|
129
|
+
function sanitizeRecipe(recipe) {
|
|
130
|
+
return honouredWithoutHostReferences(recipe).kept;
|
|
131
|
+
}
|
|
132
|
+
var RecipeError = class extends Error {
|
|
133
|
+
constructor(code, message, logPath) {
|
|
134
|
+
super(message);
|
|
135
|
+
this.code = code;
|
|
136
|
+
this.logPath = logPath;
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
var DIR_RECIPE = ".devcontainer/devcontainer.json";
|
|
140
|
+
var ROOT_RECIPE = ".devcontainer.json";
|
|
141
|
+
function locateRecipe(checkoutDir) {
|
|
142
|
+
if (existsSync(join(checkoutDir, DIR_RECIPE))) return { kind: "file", path: DIR_RECIPE };
|
|
143
|
+
if (existsSync(join(checkoutDir, ROOT_RECIPE))) return { kind: "file", path: ROOT_RECIPE };
|
|
144
|
+
const named = namedConfigurations(checkoutDir);
|
|
145
|
+
if (named.length > 0) {
|
|
146
|
+
throw new RecipeError(
|
|
147
|
+
"recipe_ambiguous",
|
|
148
|
+
`the repository has only named devcontainer configurations (${named.join(", ")}); add ${DIR_RECIPE} so the runner knows which to build`
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
return { kind: "default" };
|
|
152
|
+
}
|
|
153
|
+
function namedConfigurations(checkoutDir) {
|
|
154
|
+
const dir = join(checkoutDir, ".devcontainer");
|
|
155
|
+
if (!existsSync(dir)) return [];
|
|
156
|
+
return readdirSync(dir).sort().map((entry) => `.devcontainer/${entry}/devcontainer.json`).filter((path) => existsSync(join(checkoutDir, path)));
|
|
157
|
+
}
|
|
158
|
+
var COMMENTS = /("(?:[^"\\]|\\.)*")|\/\/[^\n]*|\/\*[\s\S]*?(?:\*\/|$)/g;
|
|
159
|
+
var TRAILING_COMMAS = /("(?:[^"\\]|\\.)*")|,(\s*[}\]])/g;
|
|
160
|
+
function parseJsonc(text) {
|
|
161
|
+
const withoutComments = text.replace(COMMENTS, (_match, str) => str ?? "");
|
|
162
|
+
return JSON.parse(withoutComments.replace(TRAILING_COMMAS, (_match, str, close) => str ?? close ?? ""));
|
|
163
|
+
}
|
|
164
|
+
function readRecipe(checkoutDir, location) {
|
|
165
|
+
if (location.kind === "default") return {};
|
|
166
|
+
const parsed = parseJsonc(readFileSync(join(checkoutDir, location.path), "utf8"));
|
|
167
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
168
|
+
throw new Error(`${location.path} is not a JSON object`);
|
|
169
|
+
}
|
|
170
|
+
return parsed;
|
|
171
|
+
}
|
|
172
|
+
function within(dir, path) {
|
|
173
|
+
const rel = relative(dir, path);
|
|
174
|
+
return rel !== ".." && !rel.startsWith(`..${sep}`);
|
|
175
|
+
}
|
|
176
|
+
function realish(path) {
|
|
177
|
+
let existing = path;
|
|
178
|
+
let rest = "";
|
|
179
|
+
while (!existsSync(existing)) {
|
|
180
|
+
rest = join(basename(existing), rest);
|
|
181
|
+
existing = dirname(existing);
|
|
182
|
+
}
|
|
183
|
+
return join(realpathSync(existing), rest);
|
|
184
|
+
}
|
|
185
|
+
function assertBuildable(checkoutDir, location, recipe) {
|
|
186
|
+
if (location.kind === "default") return;
|
|
187
|
+
const devcontainerDir = join(checkoutDir, ".devcontainer");
|
|
188
|
+
if (existsSync(devcontainerDir) && lstatSync(devcontainerDir).isSymbolicLink()) {
|
|
189
|
+
throw new RecipeError(
|
|
190
|
+
"recipe_context_outside",
|
|
191
|
+
".devcontainer is a symbolic link, so the build could read files outside the repository; commit a real directory"
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
if (recipe.dockerComposeFile !== void 0) {
|
|
195
|
+
throw new RecipeError(
|
|
196
|
+
"recipe_needs_docker",
|
|
197
|
+
"the recipe uses dockerComposeFile; its services need Docker, which a container build does not have"
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
const build = recipe.build;
|
|
201
|
+
if (typeof build !== "object" || build === null) return;
|
|
202
|
+
const { dockerfile, context } = build;
|
|
203
|
+
const base = resolve(checkoutDir, location.path, "..");
|
|
204
|
+
const allowed = join(realpathSync(checkoutDir), ".devcontainer");
|
|
205
|
+
const contextPath = resolve(base, typeof context === "string" ? context : ".");
|
|
206
|
+
const paths = [contextPath];
|
|
207
|
+
if (typeof dockerfile === "string") paths.push(resolve(base, dockerfile));
|
|
208
|
+
for (const path of paths) {
|
|
209
|
+
if (!within(devcontainerDir, path) || !within(allowed, realish(path))) {
|
|
210
|
+
throw new RecipeError(
|
|
211
|
+
"recipe_context_outside",
|
|
212
|
+
`the recipe's build reads ${relative(checkoutDir, path) || "."}, outside .devcontainer/; move the build files into .devcontainer/`
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
function writeSanitizedRecipe(checkoutDir, location, recipe) {
|
|
218
|
+
if (location.kind === "default") throw new Error("the default image has no recipe to sanitize");
|
|
219
|
+
const path = location.path === ROOT_RECIPE ? join(checkoutDir, ".outerlayer-recipe.json") : join(checkoutDir, ".devcontainer", SANITIZED_NAME);
|
|
220
|
+
writeFileSync(path, `${JSON.stringify(sanitizeRecipe(recipe), null, 2)}
|
|
221
|
+
`);
|
|
222
|
+
return path;
|
|
223
|
+
}
|
|
224
|
+
var SANITIZED_NAME = "outerlayer-recipe.json";
|
|
225
|
+
|
|
226
|
+
export { RecipeError, SANITIZED_NAME, assertBuildable, classifyProperties, locateRecipe, readRecipe, writeSanitizedRecipe };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { existsSync, readFileSync } from 'fs';
|
|
4
|
+
import { dirname, join } from 'path';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
|
|
7
|
+
createRequire(import.meta.url);
|
|
8
|
+
var FALLBACK_VERSION = "0.1.0";
|
|
9
|
+
function readJson(path) {
|
|
10
|
+
if (!existsSync(path)) return null;
|
|
11
|
+
try {
|
|
12
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
13
|
+
} catch {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function resolveVersion(entryPath = fileURLToPath(import.meta.url)) {
|
|
18
|
+
const distDir = dirname(entryPath);
|
|
19
|
+
const pkg = readJson(join(dirname(distDir), "package.json"));
|
|
20
|
+
const version = typeof pkg?.version === "string" ? pkg.version : FALLBACK_VERSION;
|
|
21
|
+
const buildInfo = readJson(join(distDir, "build-info.json"));
|
|
22
|
+
const buildId = typeof buildInfo?.id === "string" ? buildInfo.id : null;
|
|
23
|
+
return buildId ? `${version} (build ${buildId})` : version;
|
|
24
|
+
}
|
|
25
|
+
function resolvePackageVersion(entryPath = fileURLToPath(import.meta.url)) {
|
|
26
|
+
const pkg = readJson(join(dirname(dirname(entryPath)), "package.json"));
|
|
27
|
+
return typeof pkg?.version === "string" ? pkg.version : FALLBACK_VERSION;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export { resolvePackageVersion, resolveVersion };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
|
|
4
|
+
createRequire(import.meta.url);
|
|
5
|
+
|
|
6
|
+
// src/repo-filter.ts
|
|
7
|
+
function toMatcher(pattern) {
|
|
8
|
+
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
9
|
+
return new RegExp(`^${escaped}$`, "i");
|
|
10
|
+
}
|
|
11
|
+
var RepoFilter = class {
|
|
12
|
+
constructor(config) {
|
|
13
|
+
/** Sessions dropped, keyed by repo (or "(no repo)") — for the sync summary. */
|
|
14
|
+
this.skipped = /* @__PURE__ */ new Map();
|
|
15
|
+
this.include = (config?.include ?? []).map(toMatcher);
|
|
16
|
+
this.exclude = (config?.exclude ?? []).map(toMatcher);
|
|
17
|
+
}
|
|
18
|
+
get active() {
|
|
19
|
+
return this.include.length > 0 || this.exclude.length > 0;
|
|
20
|
+
}
|
|
21
|
+
/** True when the session should sync. Records skips for reporting. */
|
|
22
|
+
allows(repo) {
|
|
23
|
+
const label = repo || "(no repo)";
|
|
24
|
+
if (repo && this.exclude.some((m) => m.test(repo))) {
|
|
25
|
+
this.skipped.set(label, (this.skipped.get(label) ?? 0) + 1);
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
if (this.include.length > 0 && !(repo && this.include.some((m) => m.test(repo)))) {
|
|
29
|
+
this.skipped.set(label, (this.skipped.get(label) ?? 0) + 1);
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
get skippedTotal() {
|
|
35
|
+
let n = 0;
|
|
36
|
+
for (const c of this.skipped.values()) n += c;
|
|
37
|
+
return n;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export { RepoFilter };
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { deadHooksState, huskyPrepareScript, preparePackageManagerCommand } from './chunk-
|
|
3
|
+
import { deadHooksState, huskyPrepareScript, preparePackageManagerCommand } from './chunk-XCCLFVXM.js';
|
|
4
|
+
import { readRepoConnection, findGitDirs } from './chunk-BJ3KTMDH.js';
|
|
4
5
|
import { homeFromEnv } from './chunk-VNVZDWO3.js';
|
|
5
6
|
import { readFileSync, mkdirSync, writeFileSync, renameSync, existsSync, statSync, appendFileSync, openSync, opendirSync, unlinkSync } from 'fs';
|
|
6
7
|
import { spawn, execFileSync } from 'child_process';
|
|
7
8
|
import { createHash } from 'crypto';
|
|
8
|
-
import { join, dirname,
|
|
9
|
+
import { join, dirname, resolve } from 'path';
|
|
9
10
|
|
|
10
11
|
createRequire(import.meta.url);
|
|
11
12
|
var SYNC_STALE_AFTER_MS = 6 * 60 * 60 * 1e3;
|
|
@@ -354,11 +355,17 @@ function appendFd(path) {
|
|
|
354
355
|
return "ignore";
|
|
355
356
|
}
|
|
356
357
|
}
|
|
357
|
-
var spawnDetached = (command, args, stderrPath) => {
|
|
358
|
-
const child = spawn(command, args, {
|
|
358
|
+
var spawnDetached = (command, args, stderrPath, cwd) => {
|
|
359
|
+
const child = spawn(command, args, {
|
|
360
|
+
detached: true,
|
|
361
|
+
stdio: ["ignore", "ignore", appendFd(stderrPath)],
|
|
362
|
+
...cwd !== void 0 ? { cwd } : {}
|
|
363
|
+
});
|
|
364
|
+
child.on("error", () => {
|
|
365
|
+
});
|
|
359
366
|
child.unref();
|
|
360
367
|
};
|
|
361
|
-
function maybeAutoSync(event, home = homeFromEnv(), spawnImpl = spawnDetached, now = Date.now()) {
|
|
368
|
+
function maybeAutoSync(event, home = homeFromEnv(), spawnImpl = spawnDetached, now = Date.now(), cwd = process.cwd()) {
|
|
362
369
|
try {
|
|
363
370
|
if (event !== "SessionEnd" && event !== "Stop") return;
|
|
364
371
|
const marker = join(spoolDir(home), "last-auto-sync");
|
|
@@ -370,55 +377,114 @@ function maybeAutoSync(event, home = homeFromEnv(), spawnImpl = spawnDetached, n
|
|
|
370
377
|
if (!existsSync(configPath)) return;
|
|
371
378
|
const config = JSON.parse(readFileSync(configPath, "utf8"));
|
|
372
379
|
if (config.autoSync === false) return;
|
|
373
|
-
if (!config.url
|
|
380
|
+
if (!config.url) return;
|
|
381
|
+
if (config.apiKey) {
|
|
382
|
+
if (!config.appId) return;
|
|
383
|
+
} else if (!config.accountToken || readRepoConnection(cwd) === void 0) return;
|
|
374
384
|
writeFileSync(marker, String(now));
|
|
375
|
-
spawnImpl(process.execPath, [process.argv[1], "sync", "--quiet"]);
|
|
385
|
+
spawnImpl(process.execPath, [process.argv[1], "sync", "--quiet"], void 0, cwd);
|
|
376
386
|
} catch {
|
|
377
387
|
}
|
|
378
388
|
}
|
|
379
|
-
var
|
|
389
|
+
var WORK_LINK_RETRIES = 4;
|
|
380
390
|
function hookErrorsContext(home, writtenBytes, seenBytes) {
|
|
381
391
|
if (writtenBytes === 0) return null;
|
|
382
392
|
if (writtenBytes === seenBytes) return null;
|
|
383
393
|
const path = join(spoolDir(home), "hook-errors.log");
|
|
384
|
-
return `OuterLayer recorded a hook failure since your last session started \u2014 a refused OUTERLAYER_WORK value, or a work-item session link that could not reach
|
|
394
|
+
return `OuterLayer recorded a hook failure since your last session started \u2014 a refused OUTERLAYER_WORK value, or a work-item session link that could not reach Work. The log is ${path}.`;
|
|
385
395
|
}
|
|
386
|
-
async function handleWorkLaunch(sessionId, home, env, spawnImpl = spawnDetached) {
|
|
396
|
+
async function handleWorkLaunch(sessionId, home, env, spawnImpl = spawnDetached, cwd = process.cwd()) {
|
|
387
397
|
if (!sessionId || env.OUTERLAYER_WORK === void 0) return void 0;
|
|
388
|
-
const { parseWorkItemEnv, appendLaunchRecord } = await import('./work-launch-
|
|
398
|
+
const { parseWorkItemEnv, appendLaunchRecord } = await import('./work-launch-YI4CJDAP.js');
|
|
389
399
|
const parsed = parseWorkItemEnv(env.OUTERLAYER_WORK);
|
|
390
400
|
if ("reason" in parsed) {
|
|
391
401
|
logHookError(home, new Error(`OUTERLAYER_WORK ignored: ${parsed.reason}`));
|
|
392
402
|
return `OuterLayer ignored this session's OUTERLAYER_WORK value: ${sanitizeForContext(parsed.reason)}. The session is not attached to a work item and will not upload. Hook failures are logged to ${join(spoolDir(home), "hook-errors.log")}.`;
|
|
393
403
|
}
|
|
394
404
|
appendLaunchRecord({ sessionId, item: parsed.number }, home);
|
|
405
|
+
const args = [
|
|
406
|
+
process.argv[1],
|
|
407
|
+
"work",
|
|
408
|
+
"link-session",
|
|
409
|
+
String(parsed.number),
|
|
410
|
+
"--session-id",
|
|
411
|
+
sessionId,
|
|
412
|
+
// The ordinary failure is a gateway that is briefly unreachable — a
|
|
413
|
+
// laptop that has not joined the network yet, a restart. Nothing else
|
|
414
|
+
// ever retries this link: the launch record is already written, so
|
|
415
|
+
// the session uploads whether or not Work ever hears about the
|
|
416
|
+
// link. Retrying costs nothing here (this process is detached; the
|
|
417
|
+
// hook has already returned) and cannot double-link, because the
|
|
418
|
+
// link is keyed by the session id passed above.
|
|
419
|
+
"--retry",
|
|
420
|
+
String(WORK_LINK_RETRIES)
|
|
421
|
+
];
|
|
422
|
+
if (env.OUTERLAYER_JOB_DIR !== void 0) args.push("--no-lease");
|
|
395
423
|
spawnImpl(
|
|
396
424
|
process.execPath,
|
|
397
|
-
|
|
398
|
-
process.argv[1],
|
|
399
|
-
"work",
|
|
400
|
-
"link-session",
|
|
401
|
-
String(parsed.number),
|
|
402
|
-
"--session-id",
|
|
403
|
-
sessionId,
|
|
404
|
-
// The ordinary failure is a gateway that is briefly unreachable — a
|
|
405
|
-
// laptop that has not joined the network yet, a restart. Nothing else
|
|
406
|
-
// ever retries this link: the launch record is already written, so
|
|
407
|
-
// the session uploads whether or not the Floor ever hears about the
|
|
408
|
-
// link. Retrying costs nothing here (this process is detached; the
|
|
409
|
-
// hook has already returned) and cannot double-link, because the
|
|
410
|
-
// link is keyed by the session id passed above.
|
|
411
|
-
"--retry",
|
|
412
|
-
String(WORK_ADD_RETRIES)
|
|
413
|
-
],
|
|
425
|
+
args,
|
|
414
426
|
// The link runs after this hook has exited, so its own failure
|
|
415
427
|
// (offline, revoked key, unknown item number) has nowhere else to be
|
|
416
|
-
// seen: without this the session uploads later with no
|
|
428
|
+
// seen: without this the session uploads later with no Work link and
|
|
417
429
|
// no trace of why.
|
|
418
|
-
join(spoolDir(home), "hook-errors.log")
|
|
430
|
+
join(spoolDir(home), "hook-errors.log"),
|
|
431
|
+
cwd
|
|
419
432
|
);
|
|
420
433
|
return void 0;
|
|
421
434
|
}
|
|
435
|
+
async function missingRenewalHookContext(home, cwd) {
|
|
436
|
+
try {
|
|
437
|
+
const { settingsPath, readSettings, isOurHookCommand } = await import('./settings-J2652U5N.js');
|
|
438
|
+
const userSettings = readSettings(settingsPath("user", { home }));
|
|
439
|
+
const projectSettings = readSettings(settingsPath("project", { cwd }));
|
|
440
|
+
if (userSettings === null && projectSettings === null) return void 0;
|
|
441
|
+
const registeredIn = (settings) => (settings.hooks?.PostToolUse ?? []).some((block) => (block.hooks ?? []).some((h) => isOurHookCommand(h)));
|
|
442
|
+
const registered = userSettings !== null && registeredIn(userSettings) || projectSettings !== null && registeredIn(projectSettings);
|
|
443
|
+
if (registered) return void 0;
|
|
444
|
+
return "This install has no PostToolUse hook, so this session's work-item lease will never be renewed while it works and may be lost to another worker mid-session. Run `outerlayer init` to register it.";
|
|
445
|
+
} catch {
|
|
446
|
+
return void 0;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
var WORK_LEASE_RENEW_DEBOUNCE_MS = 5 * 60 * 1e3;
|
|
450
|
+
async function maybeRenewWorkLease(event, sessionId, home = homeFromEnv(), env = process.env, spawnImpl = spawnDetached, now = Date.now(), cwd = process.cwd()) {
|
|
451
|
+
try {
|
|
452
|
+
if (event !== "PostToolUse") return;
|
|
453
|
+
if (!sessionId || env.OUTERLAYER_WORK === void 0 || env.OUTERLAYER_JOB_DIR !== void 0) return;
|
|
454
|
+
const marker = join(spoolDir(home), `work-lease-renew-${sessionId}`);
|
|
455
|
+
try {
|
|
456
|
+
if (now - statSync(marker).mtimeMs < WORK_LEASE_RENEW_DEBOUNCE_MS) return;
|
|
457
|
+
} catch {
|
|
458
|
+
}
|
|
459
|
+
const { parseWorkItemEnv } = await import('./work-launch-YI4CJDAP.js');
|
|
460
|
+
const parsed = parseWorkItemEnv(env.OUTERLAYER_WORK);
|
|
461
|
+
if ("reason" in parsed) return;
|
|
462
|
+
writeFileSync(marker, String(now));
|
|
463
|
+
spawnImpl(
|
|
464
|
+
process.execPath,
|
|
465
|
+
[process.argv[1], "work", "link-session", String(parsed.number), "--session-id", sessionId],
|
|
466
|
+
join(spoolDir(home), "hook-errors.log"),
|
|
467
|
+
cwd
|
|
468
|
+
);
|
|
469
|
+
} catch {
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
async function handleWorkSessionEnd(event, sessionId, home = homeFromEnv(), env = process.env, spawnImpl = spawnDetached, cwd = process.cwd()) {
|
|
473
|
+
try {
|
|
474
|
+
if (event !== "SessionEnd") return;
|
|
475
|
+
if (!sessionId || env.OUTERLAYER_WORK === void 0) return;
|
|
476
|
+
const { parseWorkItemEnv } = await import('./work-launch-YI4CJDAP.js');
|
|
477
|
+
const parsed = parseWorkItemEnv(env.OUTERLAYER_WORK);
|
|
478
|
+
if ("reason" in parsed) return;
|
|
479
|
+
spawnImpl(
|
|
480
|
+
process.execPath,
|
|
481
|
+
[process.argv[1], "work", "link-session", String(parsed.number), "--session-id", sessionId, "--ended"],
|
|
482
|
+
join(spoolDir(home), "hook-errors.log"),
|
|
483
|
+
cwd
|
|
484
|
+
);
|
|
485
|
+
} catch {
|
|
486
|
+
}
|
|
487
|
+
}
|
|
422
488
|
function syncHealthContext(home) {
|
|
423
489
|
if (!existsSync(join(home, ".outerlayer", "config.json"))) return null;
|
|
424
490
|
const health = assessSyncHealth(readSyncStatus(home));
|
|
@@ -479,7 +545,7 @@ function clearHealFailureMarker(home, repoRoot) {
|
|
|
479
545
|
}
|
|
480
546
|
}
|
|
481
547
|
function deadHooksAutoHealContext(cwd, home) {
|
|
482
|
-
if (
|
|
548
|
+
if (findGitDirs(cwd) === void 0) return null;
|
|
483
549
|
const state = deadHooksState(cwd);
|
|
484
550
|
if (!state.dead || state.repoRoot === void 0 || state.hooksDir === void 0) return null;
|
|
485
551
|
const hooksDirLabel = sanitizeForContext(state.hooksDir);
|
|
@@ -510,41 +576,9 @@ var PIN_FILENAME = ".outerlayer-pin.json";
|
|
|
510
576
|
function sourceCachePath(gitCommonDir) {
|
|
511
577
|
return join(gitCommonDir, "outerlayer", "source.json");
|
|
512
578
|
}
|
|
513
|
-
function findGitRoot(cwd) {
|
|
514
|
-
let dir = cwd;
|
|
515
|
-
for (; ; ) {
|
|
516
|
-
const gitPath = join(dir, ".git");
|
|
517
|
-
let stat;
|
|
518
|
-
try {
|
|
519
|
-
stat = statSync(gitPath);
|
|
520
|
-
} catch {
|
|
521
|
-
stat = void 0;
|
|
522
|
-
}
|
|
523
|
-
if (stat?.isDirectory()) return { repoRoot: dir, gitCommonDir: gitPath };
|
|
524
|
-
if (stat?.isFile()) {
|
|
525
|
-
try {
|
|
526
|
-
const content = readFileSync(gitPath, "utf8").trim();
|
|
527
|
-
const match = /^gitdir:\s*(.+)$/.exec(content);
|
|
528
|
-
if (match) {
|
|
529
|
-
const gitdir = isAbsolute(match[1]) ? match[1] : join(dir, match[1]);
|
|
530
|
-
try {
|
|
531
|
-
const commondir = readFileSync(join(gitdir, "commondir"), "utf8").trim();
|
|
532
|
-
return { repoRoot: dir, gitCommonDir: isAbsolute(commondir) ? commondir : join(gitdir, commondir) };
|
|
533
|
-
} catch {
|
|
534
|
-
return { repoRoot: dir, gitCommonDir: gitdir };
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
} catch {
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
const parent = dirname(dir);
|
|
541
|
-
if (parent === dir) return void 0;
|
|
542
|
-
dir = parent;
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
579
|
function contextPossiblyGoverned(cwd, home = homeFromEnv()) {
|
|
546
580
|
try {
|
|
547
|
-
const found =
|
|
581
|
+
const found = findGitDirs(cwd);
|
|
548
582
|
if (found === void 0) return false;
|
|
549
583
|
const { repoRoot, gitCommonDir } = found;
|
|
550
584
|
if (existsSync(join(repoRoot, PIN_FILENAME))) return true;
|
|
@@ -566,8 +600,6 @@ function contextPossiblyGoverned(cwd, home = homeFromEnv()) {
|
|
|
566
600
|
}
|
|
567
601
|
async function runHookFast(event, home = homeFromEnv(), read = readStdin, env = process.env, spawnImpl = spawnDetached) {
|
|
568
602
|
try {
|
|
569
|
-
const dir = spoolDir(home);
|
|
570
|
-
mkdirSync(dir, { recursive: true });
|
|
571
603
|
const stdin = read();
|
|
572
604
|
let payload = {};
|
|
573
605
|
try {
|
|
@@ -576,6 +608,11 @@ async function runHookFast(event, home = homeFromEnv(), read = readStdin, env =
|
|
|
576
608
|
payload = {};
|
|
577
609
|
}
|
|
578
610
|
const resolvedEvent = String(event ?? payload.hook_event_name ?? "unknown");
|
|
611
|
+
if (resolvedEvent === "PostToolUse" && (env.OUTERLAYER_WORK === void 0 || env.OUTERLAYER_JOB_DIR !== void 0)) {
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
const dir = spoolDir(home);
|
|
615
|
+
mkdirSync(dir, { recursive: true });
|
|
579
616
|
const record = {
|
|
580
617
|
t: (/* @__PURE__ */ new Date()).toISOString(),
|
|
581
618
|
event: resolvedEvent,
|
|
@@ -585,19 +622,23 @@ async function runHookFast(event, home = homeFromEnv(), read = readStdin, env =
|
|
|
585
622
|
};
|
|
586
623
|
appendFileSync(join(dir, "events.jsonl"), JSON.stringify(record) + "\n");
|
|
587
624
|
markHookFired(resolvedEvent, home);
|
|
588
|
-
maybeAutoSync(resolvedEvent, home, spawnImpl);
|
|
589
625
|
const sessionCwd = typeof payload.cwd === "string" ? payload.cwd : process.cwd();
|
|
626
|
+
maybeAutoSync(resolvedEvent, home, spawnImpl, Date.now(), sessionCwd);
|
|
627
|
+
const hookSessionId = typeof payload.session_id === "string" ? payload.session_id : void 0;
|
|
628
|
+
await maybeRenewWorkLease(resolvedEvent, hookSessionId, home, env, spawnImpl, Date.now(), sessionCwd);
|
|
629
|
+
await handleWorkSessionEnd(resolvedEvent, hookSessionId, home, env, spawnImpl, sessionCwd);
|
|
590
630
|
let materializeMessage;
|
|
591
631
|
let deadHooksMessage;
|
|
592
632
|
let workLaunchMessage;
|
|
593
633
|
let hookErrorsMessage;
|
|
634
|
+
let renewalHookMessage;
|
|
594
635
|
if (resolvedEvent === "SessionStart") {
|
|
595
636
|
const hookErrorsBytes = fileSizeBytes(join(dir, "hook-errors.log"));
|
|
596
637
|
const hookErrorsSeen = hookErrorsSeenBytes(home);
|
|
597
638
|
recordHookErrorsSeen(home, hookErrorsBytes);
|
|
598
639
|
if (contextPossiblyGoverned(sessionCwd, home)) {
|
|
599
640
|
try {
|
|
600
|
-
const { materializeContext: materialize } = await import('./context-materialize-
|
|
641
|
+
const { materializeContext: materialize } = await import('./context-materialize-G2FUFC72.js');
|
|
601
642
|
const sessionId = typeof payload.session_id === "string" ? payload.session_id : "unknown";
|
|
602
643
|
const result = await materialize({ cwd: sessionCwd, sessionId, home });
|
|
603
644
|
materializeMessage = result.message;
|
|
@@ -612,20 +653,23 @@ async function runHookFast(event, home = homeFromEnv(), read = readStdin, env =
|
|
|
612
653
|
}
|
|
613
654
|
try {
|
|
614
655
|
const sessionId = typeof payload.session_id === "string" ? payload.session_id : void 0;
|
|
615
|
-
workLaunchMessage = await handleWorkLaunch(sessionId, home, env, spawnImpl);
|
|
656
|
+
workLaunchMessage = await handleWorkLaunch(sessionId, home, env, spawnImpl, sessionCwd);
|
|
616
657
|
} catch (err) {
|
|
617
658
|
logHookError(home, err);
|
|
618
659
|
}
|
|
619
660
|
if (workLaunchMessage === void 0) {
|
|
620
661
|
hookErrorsMessage = hookErrorsContext(home, hookErrorsBytes, hookErrorsSeen) ?? void 0;
|
|
621
662
|
}
|
|
663
|
+
if (env.OUTERLAYER_WORK !== void 0 && env.OUTERLAYER_JOB_DIR === void 0 && workLaunchMessage === void 0) {
|
|
664
|
+
renewalHookMessage = await missingRenewalHookContext(home, sessionCwd);
|
|
665
|
+
}
|
|
622
666
|
}
|
|
623
667
|
writeSessionStartContext(
|
|
624
668
|
resolvedEvent,
|
|
625
669
|
home,
|
|
626
670
|
sessionCwd,
|
|
627
671
|
(out) => process.stdout.write(out),
|
|
628
|
-
[materializeMessage, deadHooksMessage, workLaunchMessage, hookErrorsMessage].filter(
|
|
672
|
+
[materializeMessage, deadHooksMessage, workLaunchMessage, hookErrorsMessage, renewalHookMessage].filter(
|
|
629
673
|
(s) => s !== void 0
|
|
630
674
|
)
|
|
631
675
|
);
|
|
@@ -647,4 +691,4 @@ function logHookError(home, err) {
|
|
|
647
691
|
}
|
|
648
692
|
}
|
|
649
693
|
|
|
650
|
-
export { AUTO_SYNC_DEBOUNCE_MS, SYNC_STALE_AFTER_MS,
|
|
694
|
+
export { AUTO_SYNC_DEBOUNCE_MS, SYNC_STALE_AFTER_MS, WORK_LEASE_RENEW_DEBOUNCE_MS, WORK_LINK_RETRIES, assessSyncHealth, checkInstalledCliStaleness, contextPossiblyGoverned, formatAgo, handleWorkLaunch, handleWorkSessionEnd, hookFiredMarkerPath, installedCliDistDir, maybeAutoSync, maybeRenewWorkLease, readSyncStatus, refreshCommand, runHookFast, sanitizeForContext, spoolDir, workspaceRootOf, writeSyncStatus };
|