@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,61 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { openSync, fstatSync, readSync, closeSync } from 'fs';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
|
|
6
|
+
createRequire(import.meta.url);
|
|
7
|
+
var SESSION_DETECT_TAIL_BYTES = 64 * 1024;
|
|
8
|
+
var SESSION_DETECT_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
9
|
+
var readTail = (path, maxBytes) => {
|
|
10
|
+
try {
|
|
11
|
+
const fd = openSync(path, "r");
|
|
12
|
+
try {
|
|
13
|
+
const size = fstatSync(fd).size;
|
|
14
|
+
const length = Math.min(size, maxBytes);
|
|
15
|
+
const data = Buffer.alloc(length);
|
|
16
|
+
readSync(fd, data, 0, length, size - length);
|
|
17
|
+
return { data, truncated: size > length };
|
|
18
|
+
} finally {
|
|
19
|
+
closeSync(fd);
|
|
20
|
+
}
|
|
21
|
+
} catch {
|
|
22
|
+
return void 0;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
function detectActiveSession(opts) {
|
|
26
|
+
if (!opts.env.CLAUDECODE) return void 0;
|
|
27
|
+
const read = opts.readTailImpl ?? readTail;
|
|
28
|
+
const tail = read(join(opts.home, ".outerlayer", "spool", "events.jsonl"), SESSION_DETECT_TAIL_BYTES);
|
|
29
|
+
if (!tail || tail.data.length === 0) return void 0;
|
|
30
|
+
let lines = tail.data.toString("utf8").split("\n");
|
|
31
|
+
if (tail.truncated) lines = lines.slice(1);
|
|
32
|
+
const records = [];
|
|
33
|
+
for (const line of lines) {
|
|
34
|
+
const text = line.trim();
|
|
35
|
+
if (!text) continue;
|
|
36
|
+
try {
|
|
37
|
+
records.push(JSON.parse(text));
|
|
38
|
+
} catch {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const ended = /* @__PURE__ */ new Set();
|
|
43
|
+
for (const record of records) {
|
|
44
|
+
if (record.event === "SessionEnd" && typeof record.sessionId === "string" && record.sessionId !== "") {
|
|
45
|
+
ended.add(record.sessionId);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
let newest;
|
|
49
|
+
for (const record of records) {
|
|
50
|
+
if (typeof record.sessionId !== "string" || record.sessionId === "") continue;
|
|
51
|
+
if (record.event === "SessionEnd" || ended.has(record.sessionId)) continue;
|
|
52
|
+
if (typeof record.cwd !== "string" || !opts.cwds.includes(record.cwd)) continue;
|
|
53
|
+
if (typeof record.t !== "string") continue;
|
|
54
|
+
const tMs = Date.parse(record.t);
|
|
55
|
+
if (!Number.isFinite(tMs) || opts.nowMs - tMs > SESSION_DETECT_MAX_AGE_MS) continue;
|
|
56
|
+
newest = record.sessionId;
|
|
57
|
+
}
|
|
58
|
+
return newest;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export { detectActiveSession };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { statSync, readFileSync, existsSync, mkdirSync, writeFileSync, chmodSync } from 'fs';
|
|
4
|
+
import { join, isAbsolute, dirname } from 'path';
|
|
5
|
+
|
|
6
|
+
createRequire(import.meta.url);
|
|
7
|
+
function findGitDirs(cwd) {
|
|
8
|
+
let dir = cwd;
|
|
9
|
+
for (; ; ) {
|
|
10
|
+
const gitPath = join(dir, ".git");
|
|
11
|
+
let stat;
|
|
12
|
+
try {
|
|
13
|
+
stat = statSync(gitPath);
|
|
14
|
+
} catch {
|
|
15
|
+
stat = void 0;
|
|
16
|
+
}
|
|
17
|
+
if (stat?.isDirectory()) return { repoRoot: dir, gitCommonDir: gitPath };
|
|
18
|
+
if (stat?.isFile()) {
|
|
19
|
+
try {
|
|
20
|
+
const content = readFileSync(gitPath, "utf8").trim();
|
|
21
|
+
const match = /^gitdir:\s*(.+)$/.exec(content);
|
|
22
|
+
if (match) {
|
|
23
|
+
const gitdir = isAbsolute(match[1]) ? match[1] : join(dir, match[1]);
|
|
24
|
+
try {
|
|
25
|
+
const commondir = readFileSync(join(gitdir, "commondir"), "utf8").trim();
|
|
26
|
+
return { repoRoot: dir, gitCommonDir: isAbsolute(commondir) ? commondir : join(gitdir, commondir) };
|
|
27
|
+
} catch {
|
|
28
|
+
return { repoRoot: dir, gitCommonDir: gitdir };
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
} catch {
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const parent = dirname(dir);
|
|
35
|
+
if (parent === dir) return void 0;
|
|
36
|
+
dir = parent;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function connectionPath(gitCommonDir) {
|
|
40
|
+
return join(gitCommonDir, "outerlayer", "connection.json");
|
|
41
|
+
}
|
|
42
|
+
function readRepoConnection(cwd) {
|
|
43
|
+
try {
|
|
44
|
+
const dirs = findGitDirs(cwd);
|
|
45
|
+
if (dirs === void 0) return void 0;
|
|
46
|
+
const parsed = JSON.parse(readFileSync(connectionPath(dirs.gitCommonDir), "utf8"));
|
|
47
|
+
if (typeof parsed?.appId !== "string" || parsed.appId === "") return void 0;
|
|
48
|
+
return parsed;
|
|
49
|
+
} catch {
|
|
50
|
+
return void 0;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function writeRepoConnection(gitCommonDir, connection) {
|
|
54
|
+
const path = connectionPath(gitCommonDir);
|
|
55
|
+
if (!existsSync(dirname(path))) mkdirSync(dirname(path), { recursive: true });
|
|
56
|
+
writeFileSync(path, JSON.stringify(connection, null, 2) + "\n", { mode: 384 });
|
|
57
|
+
try {
|
|
58
|
+
chmodSync(path, 384);
|
|
59
|
+
} catch {
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export { findGitDirs, readRepoConnection, writeRepoConnection };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { mkdirSync, writeFileSync, renameSync, unlinkSync, readFileSync } from 'fs';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
|
|
6
|
+
createRequire(import.meta.url);
|
|
7
|
+
var SAFE_SESSION_ID = /^[A-Za-z0-9_-]+$/;
|
|
8
|
+
function reportedCostDir(home) {
|
|
9
|
+
return join(home, ".outerlayer", "reported-cost");
|
|
10
|
+
}
|
|
11
|
+
function reportedCostPath(home, sessionId) {
|
|
12
|
+
return join(reportedCostDir(home), `${sessionId}.json`);
|
|
13
|
+
}
|
|
14
|
+
function recordReportedCost(home, sessionId, costUsd) {
|
|
15
|
+
if (typeof sessionId !== "string" || !SAFE_SESSION_ID.test(sessionId)) return;
|
|
16
|
+
if (!Number.isFinite(costUsd) || costUsd < 0) return;
|
|
17
|
+
if (readReportedCost(home, sessionId) === costUsd) return;
|
|
18
|
+
const target = reportedCostPath(home, sessionId);
|
|
19
|
+
const tmp = `${target}.${process.pid}.tmp`;
|
|
20
|
+
try {
|
|
21
|
+
mkdirSync(reportedCostDir(home), { recursive: true });
|
|
22
|
+
writeFileSync(tmp, JSON.stringify({ costUsd, recordedAt: (/* @__PURE__ */ new Date()).toISOString() }));
|
|
23
|
+
renameSync(tmp, target);
|
|
24
|
+
} catch {
|
|
25
|
+
try {
|
|
26
|
+
unlinkSync(tmp);
|
|
27
|
+
} catch {
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function readReportedCost(home, sessionId) {
|
|
32
|
+
if (!SAFE_SESSION_ID.test(sessionId)) return null;
|
|
33
|
+
try {
|
|
34
|
+
const parsed = JSON.parse(readFileSync(reportedCostPath(home, sessionId), "utf8"));
|
|
35
|
+
return Number.isFinite(parsed.costUsd) && parsed.costUsd >= 0 ? parsed.costUsd : null;
|
|
36
|
+
} catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function applyReportedCost(session, home) {
|
|
41
|
+
if (session.agent.type !== "claude-code" || session.subagent?.parentSessionId) return;
|
|
42
|
+
const costUsd = readReportedCost(home, session.id);
|
|
43
|
+
if (costUsd === null) return;
|
|
44
|
+
session.totals.agentReportedCostUsd = costUsd;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export { applyReportedCost, recordReportedCost };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { readCloudConfig } from './chunk-EABW6AJQ.js';
|
|
4
|
+
import { readRepoConnection, findGitDirs } from './chunk-BJ3KTMDH.js';
|
|
5
|
+
import { homeFromEnv } from './chunk-VNVZDWO3.js';
|
|
6
|
+
|
|
7
|
+
createRequire(import.meta.url);
|
|
8
|
+
|
|
9
|
+
// src/credentials.ts
|
|
10
|
+
var MISSING_HELP = "Run `outerlayer login` to sign in, or set OUTERLAYER_URL / OUTERLAYER_API_KEY / OUTERLAYER_APP_ID. Mint a key in the dashboard: Settings \u2192 API keys.";
|
|
11
|
+
var NO_FACTORY_MESSAGE = "you are logged in, but no factory is chosen for this repository. Run `outerlayer connect` to choose one, or pass --app-id / set OUTERLAYER_APP_ID.";
|
|
12
|
+
var NO_FACTORY_OUTSIDE_REPOSITORY_MESSAGE = "you are logged in, but this directory is not in a git repository, so no factory is chosen. Pass --app-id / set OUTERLAYER_APP_ID, or run this from inside a connected repository.";
|
|
13
|
+
var LOGIN_TOKEN_PREFIX = "olu_";
|
|
14
|
+
function unauthorizedMessage(credential) {
|
|
15
|
+
return credential.startsWith(LOGIN_TOKEN_PREFIX) ? "not authorized \u2014 the login is expired or was revoked. Run `outerlayer login` to sign in again." : "not authorized \u2014 the API key is unknown, expired, or bound to a different factory. Mint a fresh key: dashboard \u2192 Settings \u2192 API keys.";
|
|
16
|
+
}
|
|
17
|
+
function resolveGatewayCredentials(sources = {}) {
|
|
18
|
+
const env = sources.env ?? process.env;
|
|
19
|
+
const config = readCloudConfig(sources.home ?? homeFromEnv(env));
|
|
20
|
+
const url = sources.url ?? env.OUTERLAYER_URL ?? config.url;
|
|
21
|
+
const key = sources.apiKey ?? (env.OUTERLAYER_API_KEY || void 0) ?? config.apiKey;
|
|
22
|
+
const kind = key ? "key" : "login";
|
|
23
|
+
const credential = key ?? config.accountToken;
|
|
24
|
+
const cwd = sources.cwd ?? process.cwd();
|
|
25
|
+
const appId = sources.appId ?? (env.OUTERLAYER_APP_ID || void 0) ?? (kind === "key" ? config.appId : readRepoConnection(cwd)?.appId);
|
|
26
|
+
if (credential && url && !appId && kind === "login") {
|
|
27
|
+
const message = findGitDirs(cwd) === void 0 ? NO_FACTORY_OUTSIDE_REPOSITORY_MESSAGE : NO_FACTORY_MESSAGE;
|
|
28
|
+
return { ok: false, message, url, credential, appId, kind };
|
|
29
|
+
}
|
|
30
|
+
if (!url || !credential || !appId) {
|
|
31
|
+
const missing = [!url && "--url", !credential && "--api-key", !appId && "--app-id"].filter(Boolean).join(", ");
|
|
32
|
+
return { ok: false, message: `missing ${missing}. ${MISSING_HELP}`, url, credential, appId, kind };
|
|
33
|
+
}
|
|
34
|
+
return { ok: true, url, credential, appId, kind };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export { resolveGatewayCredentials, unauthorizedMessage };
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { EXEC_PORT } from './chunk-B2G7JHHB.js';
|
|
4
|
+
import { RELAY_PORT } from './chunk-AUFG23AA.js';
|
|
5
|
+
import { spawn, spawnSync } from 'child_process';
|
|
6
|
+
import { writeFileSync, chmodSync, mkdirSync } from 'fs';
|
|
7
|
+
|
|
8
|
+
createRequire(import.meta.url);
|
|
9
|
+
var HELPER_PATH = "/opt/outerlayer/bin/outerlayer-guest";
|
|
10
|
+
var KERNEL_FLAG = "outerlayer.relay";
|
|
11
|
+
var DOCKER_FLAG = "outerlayer.docker";
|
|
12
|
+
var DOCKERD_CONFIG = "/run/outerlayer-dockerd.json";
|
|
13
|
+
var DOCKERD_LOG = "/run/dockerd.log";
|
|
14
|
+
var DOCKER_DATA_ROOT = "/build/docker";
|
|
15
|
+
var BUILD_DISK = "/dev/vdb";
|
|
16
|
+
var HOSTNAME = "outerlayer-build";
|
|
17
|
+
var HOSTS = "127.0.0.1 localhost\n::1 localhost ip6-localhost ip6-loopback\n";
|
|
18
|
+
var RESOLV_CONF = "# This microVM has no network device. Names are resolved by the runner's proxy.\n";
|
|
19
|
+
var BUILD_DIRECTORIES = ["/build/tmp", "/build/home", "/build/work"];
|
|
20
|
+
var WORLD_WRITABLE = 1023;
|
|
21
|
+
function relayPortsFrom(cmdline) {
|
|
22
|
+
const value = new RegExp(`(?:^|\\s)${KERNEL_FLAG.replace(".", "\\.")}=(\\S*)`).exec(cmdline)?.[1];
|
|
23
|
+
if (value === void 0 || value === "") return [];
|
|
24
|
+
return value.split(",").map((text) => {
|
|
25
|
+
const port = Number(text);
|
|
26
|
+
if (!/^\d+$/.test(text) || port < 1 || port > 65535) throw new Error(`${KERNEL_FLAG} on the kernel command line names ${JSON.stringify(text)}, which is not a port`);
|
|
27
|
+
return port;
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
function dockerRequestedBy(cmdline) {
|
|
31
|
+
return new RegExp(`(?:^|\\s)${DOCKER_FLAG.replace(".", "\\.")}=1(?:\\s|$)`).test(cmdline);
|
|
32
|
+
}
|
|
33
|
+
function startDocker(host) {
|
|
34
|
+
const proxy = `http://127.0.0.1:${RELAY_PORT}`;
|
|
35
|
+
host.mount({ source: "cgroup2", target: "/sys/fs/cgroup", type: "cgroup2" });
|
|
36
|
+
host.mkdir(DOCKER_DATA_ROOT, 457);
|
|
37
|
+
host.writeFile(
|
|
38
|
+
DOCKERD_CONFIG,
|
|
39
|
+
`${JSON.stringify({ "data-root": DOCKER_DATA_ROOT, proxies: { "http-proxy": proxy, "https-proxy": proxy, "no-proxy": "localhost,127.0.0.1" } }, null, 2)}
|
|
40
|
+
`
|
|
41
|
+
);
|
|
42
|
+
host.spawnDetached(["/bin/sh", "-c", `exec dockerd --config-file ${DOCKERD_CONFIG} >${DOCKERD_LOG} 2>&1`]);
|
|
43
|
+
}
|
|
44
|
+
function runGuestInit(host, cmdline) {
|
|
45
|
+
const ports = relayPortsFrom(cmdline);
|
|
46
|
+
host.mount({ source: BUILD_DISK, target: "/build", type: "ext4" });
|
|
47
|
+
host.chmod("/build", WORLD_WRITABLE);
|
|
48
|
+
for (const path of BUILD_DIRECTORIES) {
|
|
49
|
+
host.mkdir(path, WORLD_WRITABLE);
|
|
50
|
+
host.chmod(path, WORLD_WRITABLE);
|
|
51
|
+
}
|
|
52
|
+
host.mount({ source: "/build/tmp", target: "/tmp", bind: true });
|
|
53
|
+
const files = [
|
|
54
|
+
["hosts", HOSTS],
|
|
55
|
+
["hostname", `${HOSTNAME}
|
|
56
|
+
`],
|
|
57
|
+
["resolv.conf", RESOLV_CONF]
|
|
58
|
+
];
|
|
59
|
+
for (const [name, text] of files) {
|
|
60
|
+
host.writeFile(`/run/${name}`, text);
|
|
61
|
+
host.mount({ source: `/run/${name}`, target: `/etc/${name}`, bind: true });
|
|
62
|
+
}
|
|
63
|
+
host.writeFile("/proc/sys/kernel/hostname", HOSTNAME);
|
|
64
|
+
host.mount({ source: "tmpfs", target: "/outerlayer/job", type: "tmpfs", options: "mode=0755" });
|
|
65
|
+
host.spawnDetached([HELPER_PATH, "relay", ...ports.flatMap((port) => ["--forward", String(port)])]);
|
|
66
|
+
if (dockerRequestedBy(cmdline)) startDocker(host);
|
|
67
|
+
host.spawnDetached([HELPER_PATH, "exec-agent", "--port", String(EXEC_PORT)]);
|
|
68
|
+
}
|
|
69
|
+
function realGuestHost() {
|
|
70
|
+
return {
|
|
71
|
+
mount(args) {
|
|
72
|
+
const argv = ["mount", "--target", args.target];
|
|
73
|
+
if (args.source !== void 0) argv.push("--source", args.source);
|
|
74
|
+
if (args.type !== void 0) argv.push("--type", args.type);
|
|
75
|
+
if (args.options !== void 0) argv.push("--options", args.options);
|
|
76
|
+
if (args.bind === true) argv.push("--bind");
|
|
77
|
+
const result = spawnSync(HELPER_PATH, argv, { encoding: "utf8" });
|
|
78
|
+
if (result.status !== 0) throw new Error(`mounting ${args.target} failed: ${result.stderr.trim()}`);
|
|
79
|
+
},
|
|
80
|
+
mkdir: (path, mode) => void mkdirSync(path, { recursive: true, mode }),
|
|
81
|
+
chmod: (path, mode) => chmodSync(path, mode),
|
|
82
|
+
writeFile: (path, text) => writeFileSync(path, text),
|
|
83
|
+
spawnDetached(argv) {
|
|
84
|
+
spawn(argv[0], argv.slice(1), { detached: true, stdio: ["ignore", "inherit", "inherit"] }).unref();
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export { DOCKERD_LOG, HELPER_PATH, realGuestHost, relayPortsFrom, runGuestInit };
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { detectMachine, supportsContainerBuilds, reachDockerStep, describeMachineDocker, supportsMicroVmBuilds } from './chunk-L2DBRKRA.js';
|
|
4
|
+
import { MIN_ENGINE_MAJOR } from './chunk-7A6RVXPZ.js';
|
|
5
|
+
import { RUNNER_DIR_MODE, runnerDir, RUNNER_FILE_MODE } from './chunk-ZM2IMMYN.js';
|
|
6
|
+
import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'fs';
|
|
7
|
+
import { dirname } from 'path';
|
|
8
|
+
|
|
9
|
+
createRequire(import.meta.url);
|
|
10
|
+
var RunnerInitError = class extends Error {
|
|
11
|
+
};
|
|
12
|
+
var UNMARKED_PLACEHOLDER_COMMAND = "echo 'edit runner.commands in this config file: it must start your agent on item $OUTERLAYER_WORK' >&2; exit 1";
|
|
13
|
+
var PLACEHOLDER_MARKER = "outerlayer-placeholder";
|
|
14
|
+
var PLACEHOLDER_COMMAND = `${UNMARKED_PLACEHOLDER_COMMAND} # ${PLACEHOLDER_MARKER}`;
|
|
15
|
+
function isolationNote(machine) {
|
|
16
|
+
const has = describeMachineDocker(machine);
|
|
17
|
+
const reach = reachDockerStep(machine);
|
|
18
|
+
const needs = reach !== void 0 ? `Isolated builds need Docker Engine ${MIN_ENGINE_MAJOR} or later: ${reach}, then set both hooks to builtin:container.` : machine.platform === "windows" ? `Isolated builds need Docker Engine ${MIN_ENGINE_MAJOR} or later inside WSL2: install it there, run the runner from a WSL2 shell, and set both hooks to builtin:container.` : `Isolated builds need Docker Engine ${MIN_ENGINE_MAJOR} or later on Linux or WSL2. Once it is installed, set both hooks to builtin:container.`;
|
|
19
|
+
return `This machine has ${has}, so builds run as processes under this user (isolation: shared-user), through the tunnel. ${needs}`;
|
|
20
|
+
}
|
|
21
|
+
function processRefusal(machine) {
|
|
22
|
+
const reach = reachDockerStep(machine);
|
|
23
|
+
const needs = machine.platform === "darwin" ? "Isolated builds on a Mac run in a Linux VM: run this again with --vm." : reach !== void 0 ? `Isolated builds need Docker Engine ${MIN_ENGINE_MAJOR} or later: ${reach}, then run this again.` : machine.platform === "windows" ? `Isolated builds need Docker Engine ${MIN_ENGINE_MAJOR} or later inside WSL2: install it there, then run this again from a WSL2 shell.` : `Isolated builds need Docker Engine ${MIN_ENGINE_MAJOR} or later on Linux or WSL2: install it, then run this again.`;
|
|
24
|
+
return `This machine has ${describeMachineDocker(machine)}, so it cannot isolate a build. ${needs} To run builds as processes under this user instead, where a build can read this user's files and the runner's Claude credential, run this again with --allow-process-builds.`;
|
|
25
|
+
}
|
|
26
|
+
function isPlaceholderCommand(command) {
|
|
27
|
+
return command === UNMARKED_PLACEHOLDER_COMMAND || command.includes(PLACEHOLDER_MARKER);
|
|
28
|
+
}
|
|
29
|
+
function readConfigObject(configPath) {
|
|
30
|
+
if (!existsSync(configPath)) {
|
|
31
|
+
throw new RunnerInitError(
|
|
32
|
+
`${configPath}: no config file. Run \`outerlayer login\` first, then this command.`
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
return JSON.parse(readFileSync(configPath, "utf8"));
|
|
37
|
+
} catch (err) {
|
|
38
|
+
throw new RunnerInitError(`${configPath}: not valid JSON (${err instanceof Error ? err.message : String(err)})`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function parseStdinConfig(configPath, text) {
|
|
42
|
+
if (existsSync(configPath)) {
|
|
43
|
+
throw new RunnerInitError(
|
|
44
|
+
`${configPath}: a config file is already here, so the runner key in it is not replaced. Remove it, or delete the VM and start again.`
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
let config;
|
|
48
|
+
try {
|
|
49
|
+
config = JSON.parse(text);
|
|
50
|
+
} catch (err) {
|
|
51
|
+
throw new RunnerInitError(`the config on stdin is not valid JSON (${err instanceof Error ? err.message : String(err)})`);
|
|
52
|
+
}
|
|
53
|
+
if (typeof config.url !== "string" || typeof config.apiKey !== "string" || typeof config.appId !== "string") {
|
|
54
|
+
throw new RunnerInitError("the config on stdin needs url, apiKey and appId.");
|
|
55
|
+
}
|
|
56
|
+
const carried = config.runner;
|
|
57
|
+
delete config.runner;
|
|
58
|
+
return { config, carried };
|
|
59
|
+
}
|
|
60
|
+
function runRunnerInit(configPath, opts = {}) {
|
|
61
|
+
let config;
|
|
62
|
+
let carried;
|
|
63
|
+
if (opts.configFromStdin === void 0) {
|
|
64
|
+
config = readConfigObject(configPath);
|
|
65
|
+
if (config.runner !== void 0) {
|
|
66
|
+
throw new RunnerInitError(`${configPath}: a runner block is already here. Edit it, or remove it and run this again.`);
|
|
67
|
+
}
|
|
68
|
+
} else {
|
|
69
|
+
({ config, carried } = parseStdinConfig(configPath, opts.configFromStdin));
|
|
70
|
+
}
|
|
71
|
+
const machine = opts.machine ?? detectMachine();
|
|
72
|
+
if (opts.requireContainer === true && !supportsContainerBuilds(machine)) {
|
|
73
|
+
const fix = reachDockerStep(machine) ?? `install Docker Engine ${MIN_ENGINE_MAJOR} or later in the VM`;
|
|
74
|
+
throw new RunnerInitError(`the VM has ${describeMachineDocker(machine)}, but the runner in it needs container builds: ${fix}, then run this again.`);
|
|
75
|
+
}
|
|
76
|
+
const hooks = opts.requireContainer !== true && supportsMicroVmBuilds(machine) ? "builtin:vm" : supportsContainerBuilds(machine) ? "builtin:container" : "builtin:process";
|
|
77
|
+
if (hooks === "builtin:process" && opts.allowProcess !== true) throw new RunnerInitError(processRefusal(machine));
|
|
78
|
+
if (opts.configFromStdin !== void 0) mkdirSync(dirname(configPath), { recursive: true, mode: RUNNER_DIR_MODE });
|
|
79
|
+
mkdirSync(runnerDir(configPath), { recursive: true, mode: RUNNER_DIR_MODE });
|
|
80
|
+
config.runner = {
|
|
81
|
+
queues: ["implement", "amend"],
|
|
82
|
+
concurrency: 1,
|
|
83
|
+
pollSeconds: 60,
|
|
84
|
+
timeLimitMinutes: 480,
|
|
85
|
+
idleLimitMinutes: 30,
|
|
86
|
+
// A placeholder that fails loudly. A command line that quietly did
|
|
87
|
+
// nothing would claim an item, run, exit zero, and record `ok` —
|
|
88
|
+
// every attempt reading as a success that produced nothing.
|
|
89
|
+
commands: {
|
|
90
|
+
implement: PLACEHOLDER_COMMAND,
|
|
91
|
+
amend: PLACEHOLDER_COMMAND
|
|
92
|
+
},
|
|
93
|
+
...carried,
|
|
94
|
+
hooks: { provision: hooks, cleanup: hooks }
|
|
95
|
+
};
|
|
96
|
+
mkdirSync(dirname(configPath), { recursive: true });
|
|
97
|
+
writeFileSync(configPath, `${JSON.stringify(config, null, 2)}
|
|
98
|
+
`, { mode: RUNNER_FILE_MODE });
|
|
99
|
+
return { configPath, hooks, ...hooks === "builtin:process" ? { note: isolationNote(machine) } : {} };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export { PLACEHOLDER_MARKER, RunnerInitError, isPlaceholderCommand, readConfigObject, runRunnerInit };
|
|
@@ -5,7 +5,7 @@ import { mkdirSync, appendFileSync, readFileSync } from 'fs';
|
|
|
5
5
|
import { join } from 'path';
|
|
6
6
|
|
|
7
7
|
createRequire(import.meta.url);
|
|
8
|
-
var HOW_TO_CREATE_AN_ITEM = "outerlayer work
|
|
8
|
+
var HOW_TO_CREATE_AN_ITEM = "outerlayer work build --local";
|
|
9
9
|
function parseWorkItemEnv(raw) {
|
|
10
10
|
if (raw === void 0) return { reason: "OUTERLAYER_WORK is not set" };
|
|
11
11
|
const value = raw.trim();
|
|
@@ -58,6 +58,21 @@ function launchedItemForSession(sessionId, home, readImpl = readLaunchSpool) {
|
|
|
58
58
|
}
|
|
59
59
|
return found;
|
|
60
60
|
}
|
|
61
|
+
function launchedItemsBySession(home, readImpl = readLaunchSpool) {
|
|
62
|
+
const items = /* @__PURE__ */ new Map();
|
|
63
|
+
const read = readImpl(launchesPath(home));
|
|
64
|
+
if (read.kind !== "read") return items;
|
|
65
|
+
for (const line of read.text.split("\n")) {
|
|
66
|
+
try {
|
|
67
|
+
const record = JSON.parse(line);
|
|
68
|
+
if (typeof record.sessionId === "string" && typeof record.item === "number") {
|
|
69
|
+
items.set(record.sessionId, record.item);
|
|
70
|
+
}
|
|
71
|
+
} catch {
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return items;
|
|
75
|
+
}
|
|
61
76
|
function readLaunchSpoolState(home, readImpl = readLaunchSpool) {
|
|
62
77
|
const ids = /* @__PURE__ */ new Set();
|
|
63
78
|
const read = readImpl(launchesPath(home));
|
|
@@ -78,4 +93,4 @@ function readLaunchSpoolState(home, readImpl = readLaunchSpool) {
|
|
|
78
93
|
return { ids, ...malformedLines > 0 ? { malformedLines } : {} };
|
|
79
94
|
}
|
|
80
95
|
|
|
81
|
-
export { appendLaunchRecord, launchStatusIsDecisive, launchedItemForSession, launchesPath, parseWorkItemEnv, readLaunchSpoolState };
|
|
96
|
+
export { appendLaunchRecord, launchStatusIsDecisive, launchedItemForSession, launchedItemsBySession, launchesPath, parseWorkItemEnv, readLaunchSpoolState };
|
|
@@ -1,18 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { detectActiveSession } from './chunk-
|
|
4
|
-
import { resolveRepoIdentity } from './chunk-
|
|
5
|
-
import './chunk-YRYMNBGQ.js';
|
|
6
|
-
import './chunk-65WXAANR.js';
|
|
3
|
+
import { detectActiveSession } from './chunk-BFESLKPP.js';
|
|
4
|
+
import { resolveRepoIdentity } from './chunk-G7FSV7JX.js';
|
|
7
5
|
import { parseTrackerNumber, trackerNumberError } from './chunk-WZBHBH3J.js';
|
|
8
|
-
import './chunk-
|
|
9
|
-
import './chunk-
|
|
10
|
-
import './chunk-I3ETLSNE.js';
|
|
11
|
-
import './chunk-YOOSBOKS.js';
|
|
12
|
-
import './chunk-R5KBGVII.js';
|
|
13
|
-
import { readCloudConfig } from './chunk-TFUIDMOB.js';
|
|
6
|
+
import { defaultFetch } from './chunk-BCLJSQCV.js';
|
|
7
|
+
import { unauthorizedMessage, resolveGatewayCredentials } from './chunk-BOLTI6LR.js';
|
|
14
8
|
import { homeFromEnv } from './chunk-VNVZDWO3.js';
|
|
15
|
-
import './chunk-Z5GEFMRW.js';
|
|
16
9
|
|
|
17
10
|
createRequire(import.meta.url);
|
|
18
11
|
|
|
@@ -23,19 +16,9 @@ var RESET = "\x1B[0m";
|
|
|
23
16
|
var WorkPrCommandError = class extends Error {
|
|
24
17
|
};
|
|
25
18
|
function resolveCredentials(opts) {
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const url = opts.url ?? env.OUTERLAYER_URL ?? fileConfig.url;
|
|
30
|
-
const apiKey = opts.apiKey ?? env.OUTERLAYER_API_KEY ?? fileConfig.apiKey;
|
|
31
|
-
const appId = opts.appId ?? env.OUTERLAYER_APP_ID ?? fileConfig.appId;
|
|
32
|
-
if (!url || !apiKey || !appId) {
|
|
33
|
-
const missing = [!url && "--url", !apiKey && "--api-key", !appId && "--app-id"].filter(Boolean).join(", ");
|
|
34
|
-
throw new WorkPrCommandError(
|
|
35
|
-
`missing ${missing}. Run \`outerlayer login --url <your gateway>\` and paste the key at the prompt, or set OUTERLAYER_URL / OUTERLAYER_API_KEY / OUTERLAYER_APP_ID. Mint a key in the dashboard: Settings \u2192 API keys.`
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
return { url, apiKey, appId };
|
|
19
|
+
const resolved = resolveGatewayCredentials(opts);
|
|
20
|
+
if (!resolved.ok) throw new WorkPrCommandError(resolved.message);
|
|
21
|
+
return { url: resolved.url, apiKey: resolved.credential, appId: resolved.appId };
|
|
39
22
|
}
|
|
40
23
|
function resolveRepository(repoFlag, cwd) {
|
|
41
24
|
if (repoFlag) return repoFlag;
|
|
@@ -69,7 +52,7 @@ async function runWorkPr(opts) {
|
|
|
69
52
|
"no active session detected. `work pr` declares a pull request from inside the session that opened it."
|
|
70
53
|
);
|
|
71
54
|
}
|
|
72
|
-
const fetchImpl = opts.fetchImpl ??
|
|
55
|
+
const fetchImpl = opts.fetchImpl ?? defaultFetch;
|
|
73
56
|
const endpoint = new URL("/v1/work-items/pull-requests", creds.url).toString();
|
|
74
57
|
let response;
|
|
75
58
|
try {
|
|
@@ -87,7 +70,7 @@ async function runWorkPr(opts) {
|
|
|
87
70
|
}
|
|
88
71
|
if (response.status === 401) {
|
|
89
72
|
throw new WorkPrCommandError(
|
|
90
|
-
|
|
73
|
+
unauthorizedMessage(creds.apiKey)
|
|
91
74
|
);
|
|
92
75
|
}
|
|
93
76
|
let json;
|
|
@@ -30,5 +30,16 @@ function writeCloudConfig(config, home = homeFromEnv()) {
|
|
|
30
30
|
} catch {
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
|
+
function removeCloudConfigKeys(keys, home = homeFromEnv()) {
|
|
34
|
+
const path = cloudConfigPath(home);
|
|
35
|
+
if (!existsSync(path)) return;
|
|
36
|
+
const stored = { ...readCloudConfig(home) };
|
|
37
|
+
for (const key of keys) delete stored[key];
|
|
38
|
+
writeFileSync(path, JSON.stringify(stored, null, 2) + "\n", { mode: 384 });
|
|
39
|
+
try {
|
|
40
|
+
chmodSync(path, 384);
|
|
41
|
+
} catch {
|
|
42
|
+
}
|
|
43
|
+
}
|
|
33
44
|
|
|
34
|
-
export { cloudConfigPath, readCloudConfig, writeCloudConfig };
|
|
45
|
+
export { cloudConfigPath, readCloudConfig, removeCloudConfigKeys, writeCloudConfig };
|
|
@@ -7975,4 +7975,4 @@ function normalizeRepoName(repo) {
|
|
|
7975
7975
|
var CONTEXT_PIN_FILENAME = ".outerlayer-pin.json";
|
|
7976
7976
|
var CONTEXT_PIN_SCHEMA_VERSION = 1;
|
|
7977
7977
|
|
|
7978
|
-
export { CONTEXT_PIN_FILENAME, CONTEXT_PIN_SCHEMA_VERSION, CONTEXT_SOURCE_CONFIG_PATH, classifyTree, emitTree, generatedHeaderSource, normalizePath, normalizeRepoName, parseGoverns, parseOuterlayerConfig };
|
|
7978
|
+
export { CONTEXT_PIN_FILENAME, CONTEXT_PIN_SCHEMA_VERSION, CONTEXT_SOURCE_CONFIG_PATH, classifyTree, emitTree, generatedHeaderSource, normalizePath, normalizeRepoName, parseGoverns, parseOuterlayerConfig, splitRawFile };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import {
|
|
3
|
+
import { recordReportedCost } from './chunk-BKO6JEZI.js';
|
|
4
|
+
import { decodeWrappedCommand } from './chunk-5QRP3MVS.js';
|
|
4
5
|
import { homeFromEnv } from './chunk-VNVZDWO3.js';
|
|
5
6
|
import { readFileSync } from 'fs';
|
|
6
7
|
import { spawn } from 'child_process';
|
|
@@ -133,6 +134,7 @@ async function runStatuslineFast(args, opts = {}) {
|
|
|
133
134
|
const now = (opts.now ?? Date.now)();
|
|
134
135
|
const stdinText = (opts.read ?? readStdin)();
|
|
135
136
|
const stdin = parseStatuslineStdin(stdinText);
|
|
137
|
+
recordReportedCost(home, stdin.session_id, stdin.cost?.total_cost_usd);
|
|
136
138
|
const state = readStatuslineState(home);
|
|
137
139
|
const ourLine = renderStatusline(stdin, state, now);
|
|
138
140
|
const { wrapId } = parseStatuslineArgs(args);
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
|
|
4
|
+
createRequire(import.meta.url);
|
|
5
|
+
|
|
6
|
+
// src/runner/container/disk.ts
|
|
7
|
+
var UNITS = { "": 1, k: 1024, m: 1024 ** 2, g: 1024 ** 3 };
|
|
8
|
+
function parseSize(text) {
|
|
9
|
+
const match = /^(\d+(?:\.\d+)?)([kKmMgG]?)$/.exec(text);
|
|
10
|
+
if (match === null) return void 0;
|
|
11
|
+
const bytes = Number(match[1]) * UNITS[match[2].toLowerCase()];
|
|
12
|
+
return bytes > 0 ? bytes : void 0;
|
|
13
|
+
}
|
|
14
|
+
function parseDuKilobytes(output) {
|
|
15
|
+
const match = /^(\d+)\s/.exec(output);
|
|
16
|
+
return match === null ? void 0 : Number(match[1]);
|
|
17
|
+
}
|
|
18
|
+
function exceedsDisk(usedKilobytes, limit) {
|
|
19
|
+
const bytes = parseSize(limit);
|
|
20
|
+
return usedKilobytes * 1024 > (bytes ?? Number.POSITIVE_INFINITY);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// src/runner/allow-hosts.ts
|
|
24
|
+
var MAX_ALLOW_HOSTS = 500;
|
|
25
|
+
var LABEL = /^(?!-)[a-z0-9-]{1,63}(?<!-)$/;
|
|
26
|
+
var IPV4 = /^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/;
|
|
27
|
+
function isAddress(text) {
|
|
28
|
+
if (IPV4.test(text)) return true;
|
|
29
|
+
try {
|
|
30
|
+
new URL(`http://[${text}]/`);
|
|
31
|
+
return true;
|
|
32
|
+
} catch {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function isHostName(text) {
|
|
37
|
+
const labels = text.split(".");
|
|
38
|
+
return text.length <= 253 && labels.every((label) => LABEL.test(label)) && !/^\d+$/.test(labels.at(-1));
|
|
39
|
+
}
|
|
40
|
+
function allowHostProblem(entry) {
|
|
41
|
+
if (typeof entry !== "string") return "is not a string";
|
|
42
|
+
const text = entry.toLowerCase();
|
|
43
|
+
if (isAddress(text)) return void 0;
|
|
44
|
+
if (text.startsWith("*.")) {
|
|
45
|
+
const domain = text.slice(2);
|
|
46
|
+
if (!isHostName(domain)) return "is not a host name after *.";
|
|
47
|
+
if (!domain.includes(".")) return "is too broad: a pattern needs a domain with at least two labels after *.";
|
|
48
|
+
return void 0;
|
|
49
|
+
}
|
|
50
|
+
return isHostName(text) ? void 0 : "is not a host name, a *. pattern or an IP address";
|
|
51
|
+
}
|
|
52
|
+
function hostAllowed(host, allowHosts) {
|
|
53
|
+
const name = host.toLowerCase().replace(/\.$/, "");
|
|
54
|
+
return allowHosts.some((entry) => {
|
|
55
|
+
const pattern = entry.toLowerCase();
|
|
56
|
+
return pattern.startsWith("*.") ? name.endsWith(pattern.slice(1)) : name === pattern;
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export { MAX_ALLOW_HOSTS, allowHostProblem, exceedsDisk, hostAllowed, parseDuKilobytes, parseSize };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { decodeWrappedCommand } from './chunk-
|
|
3
|
+
import { decodeWrappedCommand } from './chunk-5QRP3MVS.js';
|
|
4
4
|
import { homeFromEnv } from './chunk-VNVZDWO3.js';
|
|
5
5
|
import { readFileSync, mkdirSync, appendFileSync, writeFileSync } from 'fs';
|
|
6
6
|
import { spawn } from 'child_process';
|