@outerlayer/cli 0.5.1 → 0.6.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 +11 -0
- package/README.md +9 -0
- package/dist/{agent-setup-IFH3FB5X.js → agent-setup-USZ2SAXC.js} +2 -2
- package/dist/build-info.json +1 -1
- package/dist/{check-4Q33B5ZE.js → check-CCVPBN7A.js} +24 -14
- package/dist/{chunk-WOBNRVQT.js → chunk-3DEVYTNQ.js} +1 -1
- package/dist/{chunk-F4NU4H3V.js → chunk-43J3M6KB.js} +95 -11
- package/dist/{chunk-W7UTFZDH.js → chunk-4NLKY4WR.js} +2 -2
- package/dist/{lima-XN65D7GN.js → chunk-77MM5RYD.js} +0 -1
- package/dist/{chunk-D2KWEAN6.js → chunk-7DBI6SNT.js} +1 -1
- package/dist/{chunk-ZXBITXTS.js → chunk-AIW6GLET.js} +1 -1
- package/dist/{chunk-EX6M5JPR.js → chunk-ANK2B36Y.js} +131 -15
- package/dist/{chunk-RMAZ2NWA.js → chunk-BNMCXPG7.js} +14 -2
- package/dist/{chunk-UFFXNXLQ.js → chunk-FA2BOX4O.js} +31 -29
- package/dist/chunk-FBTE2A2X.js +38 -0
- package/dist/{chunk-DVDEBNQJ.js → chunk-GQD22KKN.js} +17 -6
- package/dist/{chunk-BCJNHMZT.js → chunk-HHJDN6AU.js} +18 -3
- package/dist/{chunk-JLDZTG3A.js → chunk-IASWYRVG.js} +1 -1
- package/dist/{install-cmd-7WXOIFDO.js → chunk-JWOQNNN2.js} +0 -1
- package/dist/{chunk-H4CK2YK4.js → chunk-MIGEDH5P.js} +16 -8
- package/dist/{chunk-CHLVJNTE.js → chunk-MXB5VB4Y.js} +17 -5
- package/dist/chunk-NQXFAGHU.js +211 -0
- package/dist/chunk-OUUINEYB.js +27 -0
- package/dist/{chunk-OR6AEEDO.js → chunk-PMAMOSTP.js} +3 -3
- package/dist/{chunk-RTXKYLCA.js → chunk-RBOMWNOG.js} +1 -1
- package/dist/{chunk-T52PKE2Y.js → chunk-RZSF5B46.js} +149 -7
- package/dist/chunk-T6OIJQRT.js +31 -0
- package/dist/{chunk-EGABNHZG.js → chunk-UMFWVGJO.js} +148 -18
- package/dist/{chunk-UWH5DSUP.js → chunk-VMJ3MMBX.js} +203 -80
- package/dist/chunk-ZILGTTY6.js +75 -0
- package/dist/{cli-LHSUYYMI.js → cli-7WCNPBYC.js} +134 -61
- package/dist/command-ARFK3OYF.js +162 -0
- package/dist/{connect-cmd-OJBFHZWP.js → connect-cmd-PCESZLTR.js} +4 -4
- package/dist/{context-adopt-UMA4O5NY.js → context-adopt-G5JAELE7.js} +2 -2
- package/dist/{context-materialize-G2FUFC72.js → context-materialize-3HLPZAF6.js} +2 -2
- package/dist/{dist-WDV7U7UJ.js → dist-RDLP4GGJ.js} +1 -1
- package/dist/{doctor-MOA3RIEP.js → doctor-VOMYOHQU.js} +32 -12
- package/dist/{emit-artifact-cmd-FDWYKURA.js → emit-artifact-cmd-TBF46VXZ.js} +6 -7
- package/dist/{emit-criteria-cmd-OCXHLK34.js → emit-criteria-cmd-3427XKFW.js} +3 -3
- package/dist/{emit-finding-cmd-POW7PKIA.js → emit-finding-cmd-GSI75K36.js} +3 -3
- package/dist/{emit-result-cmd-ID35SH53.js → emit-result-cmd-3PRQKB55.js} +6 -7
- package/dist/{hook-fast-EYENPK6F.js → hook-fast-NJBBZQ5Q.js} +1 -1
- package/dist/index.js +3 -2
- package/dist/{init-2IPOIXJV.js → init-OHYFIKZD.js} +26 -55
- package/dist/{init-FYDJAKTM.js → init-WVR4BZQ4.js} +8 -8
- package/dist/{init-cmd-KLIDRVXQ.js → init-cmd-HMFNIFPI.js} +7 -7
- package/dist/inspect-XAUSB7L3.js +63 -0
- package/dist/install-DSUZE2TJ.js +13 -0
- package/dist/install-cmd-453C7IZT.js +8 -0
- package/dist/lima-7NPCNGWT.js +6 -0
- package/dist/{loop-NEBUGD2Q.js → loop-UAJB34KA.js} +24 -11
- package/dist/{machine-ECU3QCZD.js → machine-2JOWGRSZ.js} +7 -7
- package/dist/session-sender-ED26V45B.js +28 -0
- package/dist/{status-CLATHCTQ.js → status-6WNXX2O7.js} +22 -8
- package/dist/{sync-cmd-SWQ5KLPL.js → sync-cmd-YONVGUYS.js} +6 -7
- package/dist/{watch-3YW6Z3DQ.js → watch-HHKAF26L.js} +6 -7
- package/dist/{work-claim-cmd-3OPWA77E.js → work-claim-cmd-LXBZTMJA.js} +5 -5
- package/dist/{work-cmd-NUR4CAG6.js → work-cmd-OVNF44FY.js} +3 -3
- package/dist/{work-comment-cmd-6TTTYGHM.js → work-comment-cmd-6DGQNTAH.js} +3 -3
- package/dist/{work-launch-YI4CJDAP.js → work-launch-R5HTAP6X.js} +1 -1
- package/dist/{work-open-pr-cmd-KZ7QQFLD.js → work-open-pr-cmd-FCZHFHZS.js} +4 -4
- package/dist/{work-pr-cmd-QNUBE6IQ.js → work-pr-cmd-QJJVHZDC.js} +2 -2
- package/package.json +2 -1
- package/dist/chunk-ZOC7JSYC.js +0 -83
- package/dist/{chunk-XDDW4FRS.js → chunk-OIRNH6UO.js} +1 -1
|
@@ -3,7 +3,7 @@ import { createRequire } from 'node:module';
|
|
|
3
3
|
import { deadHooksState, huskyPrepareScript, preparePackageManagerCommand } from './chunk-XCCLFVXM.js';
|
|
4
4
|
import { readRepoConnection, findGitDirs } from './chunk-BJ3KTMDH.js';
|
|
5
5
|
import { homeFromEnv } from './chunk-VNVZDWO3.js';
|
|
6
|
-
import { readFileSync, mkdirSync, writeFileSync, renameSync, existsSync, statSync, appendFileSync,
|
|
6
|
+
import { readFileSync, mkdirSync, writeFileSync, renameSync, existsSync, statSync, appendFileSync, opendirSync, unlinkSync } from 'fs';
|
|
7
7
|
import { spawn, execFileSync } from 'child_process';
|
|
8
8
|
import { createHash } from 'crypto';
|
|
9
9
|
import { join, dirname, resolve } from 'path';
|
|
@@ -347,18 +347,11 @@ var readStdin = () => {
|
|
|
347
347
|
}
|
|
348
348
|
};
|
|
349
349
|
var AUTO_SYNC_DEBOUNCE_MS = 5 * 60 * 1e3;
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
try {
|
|
353
|
-
return openSync(path, "a");
|
|
354
|
-
} catch {
|
|
355
|
-
return "ignore";
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
var spawnDetached = (command, args, stderrPath, cwd) => {
|
|
350
|
+
var LOG_FAILURES_FLAG = "--log-failures";
|
|
351
|
+
var spawnDetached = (command, args, cwd) => {
|
|
359
352
|
const child = spawn(command, args, {
|
|
360
353
|
detached: true,
|
|
361
|
-
stdio: ["ignore", "ignore",
|
|
354
|
+
stdio: ["ignore", "ignore", "ignore"],
|
|
362
355
|
...cwd !== void 0 ? { cwd } : {}
|
|
363
356
|
});
|
|
364
357
|
child.on("error", () => {
|
|
@@ -382,7 +375,7 @@ function maybeAutoSync(event, home = homeFromEnv(), spawnImpl = spawnDetached, n
|
|
|
382
375
|
if (!config.appId) return;
|
|
383
376
|
} else if (!config.accountToken || readRepoConnection(cwd) === void 0) return;
|
|
384
377
|
writeFileSync(marker, String(now));
|
|
385
|
-
spawnImpl(process.execPath, [process.argv[1], "sync", "--quiet"],
|
|
378
|
+
spawnImpl(process.execPath, [process.argv[1], "sync", "--quiet"], cwd);
|
|
386
379
|
} catch {
|
|
387
380
|
}
|
|
388
381
|
}
|
|
@@ -395,13 +388,13 @@ function hookErrorsContext(home, writtenBytes, seenBytes) {
|
|
|
395
388
|
}
|
|
396
389
|
async function handleWorkLaunch(sessionId, home, env, spawnImpl = spawnDetached, cwd = process.cwd()) {
|
|
397
390
|
if (!sessionId || env.OUTERLAYER_WORK === void 0) return void 0;
|
|
398
|
-
const { parseWorkItemEnv, appendLaunchRecord } = await import('./work-launch-
|
|
391
|
+
const { parseWorkItemEnv, appendLaunchRecord } = await import('./work-launch-R5HTAP6X.js');
|
|
399
392
|
const parsed = parseWorkItemEnv(env.OUTERLAYER_WORK);
|
|
400
393
|
if ("reason" in parsed) {
|
|
401
394
|
logHookError(home, new Error(`OUTERLAYER_WORK ignored: ${parsed.reason}`));
|
|
402
395
|
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")}.`;
|
|
403
396
|
}
|
|
404
|
-
appendLaunchRecord({ sessionId, item: parsed.number }, home);
|
|
397
|
+
appendLaunchRecord({ sessionId, item: parsed.number, ...env.OUTERLAYER_JOB_DIR === void 0 ? {} : { job: env.OUTERLAYER_JOB_DIR } }, home);
|
|
405
398
|
const args = [
|
|
406
399
|
process.argv[1],
|
|
407
400
|
"work",
|
|
@@ -417,17 +410,16 @@ async function handleWorkLaunch(sessionId, home, env, spawnImpl = spawnDetached,
|
|
|
417
410
|
// hook has already returned) and cannot double-link, because the
|
|
418
411
|
// link is keyed by the session id passed above.
|
|
419
412
|
"--retry",
|
|
420
|
-
String(WORK_LINK_RETRIES)
|
|
413
|
+
String(WORK_LINK_RETRIES),
|
|
414
|
+
LOG_FAILURES_FLAG
|
|
421
415
|
];
|
|
422
416
|
if (env.OUTERLAYER_JOB_DIR !== void 0) args.push("--no-lease");
|
|
423
417
|
spawnImpl(
|
|
424
418
|
process.execPath,
|
|
425
419
|
args,
|
|
426
|
-
// The link
|
|
427
|
-
//
|
|
428
|
-
//
|
|
429
|
-
// no trace of why.
|
|
430
|
-
join(spoolDir(home), "hook-errors.log"),
|
|
420
|
+
// The link appends its own failure line to hook-errors.log (see
|
|
421
|
+
// `--log-failures`), so a Node warning on a link that worked never
|
|
422
|
+
// reaches the log.
|
|
431
423
|
cwd
|
|
432
424
|
);
|
|
433
425
|
return void 0;
|
|
@@ -456,30 +448,28 @@ async function maybeRenewWorkLease(event, sessionId, home = homeFromEnv(), env =
|
|
|
456
448
|
if (now - statSync(marker).mtimeMs < WORK_LEASE_RENEW_DEBOUNCE_MS) return;
|
|
457
449
|
} catch {
|
|
458
450
|
}
|
|
459
|
-
const { parseWorkItemEnv } = await import('./work-launch-
|
|
451
|
+
const { parseWorkItemEnv } = await import('./work-launch-R5HTAP6X.js');
|
|
460
452
|
const parsed = parseWorkItemEnv(env.OUTERLAYER_WORK);
|
|
461
453
|
if ("reason" in parsed) return;
|
|
462
454
|
writeFileSync(marker, String(now));
|
|
463
455
|
spawnImpl(
|
|
464
456
|
process.execPath,
|
|
465
|
-
[process.argv[1], "work", "link-session", String(parsed.number), "--session-id", sessionId],
|
|
466
|
-
join(spoolDir(home), "hook-errors.log"),
|
|
457
|
+
[process.argv[1], "work", "link-session", String(parsed.number), "--session-id", sessionId, LOG_FAILURES_FLAG],
|
|
467
458
|
cwd
|
|
468
459
|
);
|
|
469
460
|
} catch {
|
|
470
461
|
}
|
|
471
462
|
}
|
|
472
|
-
async function handleWorkSessionEnd(event, sessionId,
|
|
463
|
+
async function handleWorkSessionEnd(event, sessionId, _home = homeFromEnv(), env = process.env, spawnImpl = spawnDetached, cwd = process.cwd()) {
|
|
473
464
|
try {
|
|
474
465
|
if (event !== "SessionEnd") return;
|
|
475
466
|
if (!sessionId || env.OUTERLAYER_WORK === void 0) return;
|
|
476
|
-
const { parseWorkItemEnv } = await import('./work-launch-
|
|
467
|
+
const { parseWorkItemEnv } = await import('./work-launch-R5HTAP6X.js');
|
|
477
468
|
const parsed = parseWorkItemEnv(env.OUTERLAYER_WORK);
|
|
478
469
|
if ("reason" in parsed) return;
|
|
479
470
|
spawnImpl(
|
|
480
471
|
process.execPath,
|
|
481
|
-
[process.argv[1], "work", "link-session", String(parsed.number), "--session-id", sessionId, "--ended"],
|
|
482
|
-
join(spoolDir(home), "hook-errors.log"),
|
|
472
|
+
[process.argv[1], "work", "link-session", String(parsed.number), "--session-id", sessionId, "--ended", LOG_FAILURES_FLAG],
|
|
483
473
|
cwd
|
|
484
474
|
);
|
|
485
475
|
} catch {
|
|
@@ -638,7 +628,7 @@ async function runHookFast(event, home = homeFromEnv(), read = readStdin, env =
|
|
|
638
628
|
recordHookErrorsSeen(home, hookErrorsBytes);
|
|
639
629
|
if (contextPossiblyGoverned(sessionCwd, home)) {
|
|
640
630
|
try {
|
|
641
|
-
const { materializeContext: materialize } = await import('./context-materialize-
|
|
631
|
+
const { materializeContext: materialize } = await import('./context-materialize-3HLPZAF6.js');
|
|
642
632
|
const sessionId = typeof payload.session_id === "string" ? payload.session_id : "unknown";
|
|
643
633
|
const result = await materialize({ cwd: sessionCwd, sessionId, home });
|
|
644
634
|
materializeMessage = result.message;
|
|
@@ -677,7 +667,9 @@ async function runHookFast(event, home = homeFromEnv(), read = readStdin, env =
|
|
|
677
667
|
logHookError(home, err);
|
|
678
668
|
}
|
|
679
669
|
}
|
|
670
|
+
var hookErrorsLogged = 0;
|
|
680
671
|
function logHookError(home, err) {
|
|
672
|
+
hookErrorsLogged++;
|
|
681
673
|
try {
|
|
682
674
|
const dir = spoolDir(home);
|
|
683
675
|
mkdirSync(dir, { recursive: true });
|
|
@@ -690,5 +682,15 @@ function logHookError(home, err) {
|
|
|
690
682
|
} catch {
|
|
691
683
|
}
|
|
692
684
|
}
|
|
685
|
+
function recordLinkExitFailure(argv, home = homeFromEnv(), target = process) {
|
|
686
|
+
const at = argv.indexOf("link-session");
|
|
687
|
+
if (at < 0 || !argv.includes(LOG_FAILURES_FLAG)) return;
|
|
688
|
+
const number = argv[at + 1] ?? "?";
|
|
689
|
+
target.once("exit", (code) => {
|
|
690
|
+
if (code !== 0 && hookErrorsLogged === 0) {
|
|
691
|
+
logHookError(home, new Error(`work link-session #${number} exited with code ${code} before it reported a failure`));
|
|
692
|
+
}
|
|
693
|
+
});
|
|
694
|
+
}
|
|
693
695
|
|
|
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 };
|
|
696
|
+
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, logHookError, maybeAutoSync, maybeRenewWorkLease, readSyncStatus, recordLinkExitFailure, refreshCommand, runHookFast, sanitizeForContext, spoolDir, workspaceRootOf, writeSyncStatus };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
|
|
4
|
+
createRequire(import.meta.url);
|
|
5
|
+
|
|
6
|
+
// src/runner/vm/service.ts
|
|
7
|
+
var VM_SERVICE_NAME = "outerlayer-runner";
|
|
8
|
+
var UNIT_MARKER = "# Written by outerlayer runner install --service; edit the runner config, not this file.";
|
|
9
|
+
var STOP_MARGIN_MINUTES = 10;
|
|
10
|
+
function escapeUnitPath(path) {
|
|
11
|
+
return path.replace(/%/g, "%%").replace(/ /g, "\\x20");
|
|
12
|
+
}
|
|
13
|
+
function runnerUnit(unit) {
|
|
14
|
+
return `${UNIT_MARKER}
|
|
15
|
+
[Unit]
|
|
16
|
+
Description=OuterLayer runner
|
|
17
|
+
After=network-online.target${unit.docker ? " docker.service" : ""}
|
|
18
|
+
Wants=network-online.target
|
|
19
|
+
${unit.docker ? "Requires=docker.service\n" : ""}
|
|
20
|
+
[Service]
|
|
21
|
+
User=${unit.user}
|
|
22
|
+
ExecStart=${unit.execStart}
|
|
23
|
+
EnvironmentFile=-${escapeUnitPath(`${unit.configDir}/runner.env`)}
|
|
24
|
+
Restart=on-failure
|
|
25
|
+
RestartForceExitStatus=75
|
|
26
|
+
RestartSec=30
|
|
27
|
+
KillSignal=SIGTERM
|
|
28
|
+
KillMode=mixed
|
|
29
|
+
TimeoutStopSec=${unit.timeLimitMinutes + STOP_MARGIN_MINUTES}min
|
|
30
|
+
OOMPolicy=continue
|
|
31
|
+
Delegate=yes
|
|
32
|
+
|
|
33
|
+
[Install]
|
|
34
|
+
WantedBy=multi-user.target
|
|
35
|
+
`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export { UNIT_MARKER, VM_SERVICE_NAME, runnerUnit };
|
|
@@ -27,7 +27,12 @@ function launchesPath(home) {
|
|
|
27
27
|
}
|
|
28
28
|
function appendLaunchRecord(input, home) {
|
|
29
29
|
mkdirSync(join(home, ".outerlayer", "spool"), { recursive: true });
|
|
30
|
-
const record = {
|
|
30
|
+
const record = {
|
|
31
|
+
sessionId: input.sessionId,
|
|
32
|
+
item: input.item,
|
|
33
|
+
...input.job === void 0 ? {} : { job: input.job },
|
|
34
|
+
t: input.t ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
35
|
+
};
|
|
31
36
|
appendFileSync(launchesPath(home), `${JSON.stringify(record)}
|
|
32
37
|
`);
|
|
33
38
|
}
|
|
@@ -75,22 +80,28 @@ function launchedItemsBySession(home, readImpl = readLaunchSpool) {
|
|
|
75
80
|
}
|
|
76
81
|
function readLaunchSpoolState(home, readImpl = readLaunchSpool) {
|
|
77
82
|
const ids = /* @__PURE__ */ new Set();
|
|
83
|
+
const records = /* @__PURE__ */ new Map();
|
|
78
84
|
const read = readImpl(launchesPath(home));
|
|
79
|
-
if (read.kind === "unreadable") return { ids, unreadable: read.reason };
|
|
80
|
-
if (read.kind === "absent") return { ids };
|
|
85
|
+
if (read.kind === "unreadable") return { ids, records, unreadable: read.reason };
|
|
86
|
+
if (read.kind === "absent") return { ids, records };
|
|
81
87
|
let malformedLines = 0;
|
|
82
88
|
for (const line of read.text.split("\n")) {
|
|
83
89
|
const text = line.trim();
|
|
84
90
|
if (!text) continue;
|
|
85
91
|
try {
|
|
86
92
|
const record = JSON.parse(text);
|
|
87
|
-
if (typeof record.sessionId === "string" && record.sessionId !== "")
|
|
88
|
-
|
|
93
|
+
if (typeof record.sessionId === "string" && record.sessionId !== "") {
|
|
94
|
+
ids.add(record.sessionId);
|
|
95
|
+
records.set(record.sessionId, {
|
|
96
|
+
...typeof record.item === "number" ? { item: record.item } : {},
|
|
97
|
+
...typeof record.job === "string" ? { job: record.job } : {}
|
|
98
|
+
});
|
|
99
|
+
} else malformedLines += 1;
|
|
89
100
|
} catch {
|
|
90
101
|
malformedLines += 1;
|
|
91
102
|
}
|
|
92
103
|
}
|
|
93
|
-
return { ids, ...malformedLines > 0 ? { malformedLines } : {} };
|
|
104
|
+
return { ids, records, ...malformedLines > 0 ? { malformedLines } : {} };
|
|
94
105
|
}
|
|
95
106
|
|
|
96
107
|
export { appendLaunchRecord, launchStatusIsDecisive, launchedItemForSession, launchedItemsBySession, launchesPath, parseWorkItemEnv, readLaunchSpoolState };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { readLaunchSpoolState, launchesPath } from './chunk-
|
|
3
|
+
import { readLaunchSpoolState, launchesPath } from './chunk-GQD22KKN.js';
|
|
4
4
|
|
|
5
5
|
createRequire(import.meta.url);
|
|
6
6
|
|
|
@@ -17,18 +17,33 @@ function launchRefusalForSession(sessionId, home) {
|
|
|
17
17
|
return `this session was not launched with OUTERLAYER_WORK naming a work item, so nothing it produces may leave the machine. Start the session with OUTERLAYER_WORK set (for example OUTERLAYER_WORK=42), or run this from CI.`;
|
|
18
18
|
}
|
|
19
19
|
var LaunchFilter = class {
|
|
20
|
-
|
|
20
|
+
/**
|
|
21
|
+
* `records`, when given, scopes a live sender:
|
|
22
|
+
* - a build sender (`build`) admits only a session launched for its item in its own job; a record with no job
|
|
23
|
+
* is one an older hook wrote, which is admitted on the item alone;
|
|
24
|
+
* - any other live sender skips a session launched inside a runner job, which only that job's sender sends.
|
|
25
|
+
* `sync` passes no records and decides by the launch record's existence alone.
|
|
26
|
+
*/
|
|
27
|
+
constructor(launchedSessionIds, records, build) {
|
|
21
28
|
this.launchedSessionIds = launchedSessionIds;
|
|
29
|
+
this.records = records;
|
|
30
|
+
this.build = build;
|
|
22
31
|
/** Sessions dropped for carrying no launch record — for the sync summary. */
|
|
23
32
|
this.skippedTotal = 0;
|
|
24
33
|
}
|
|
25
34
|
/** True when the session should sync. Records skips for reporting. */
|
|
26
35
|
allows(session) {
|
|
27
36
|
const key = session.subagent?.parentSessionId ?? session.id;
|
|
28
|
-
const allowed = typeof key === "string" && this.launchedSessionIds.has(key);
|
|
37
|
+
const allowed = typeof key === "string" && this.launchedSessionIds.has(key) && this.inScope(this.records?.get(key));
|
|
29
38
|
if (!allowed) this.skippedTotal += 1;
|
|
30
39
|
return allowed;
|
|
31
40
|
}
|
|
41
|
+
inScope(facts) {
|
|
42
|
+
if (this.build !== void 0) {
|
|
43
|
+
return facts !== void 0 && facts.item === this.build.item && (facts.job === void 0 || facts.job === this.build.jobDir);
|
|
44
|
+
}
|
|
45
|
+
return facts?.job === void 0;
|
|
46
|
+
}
|
|
32
47
|
};
|
|
33
48
|
|
|
34
49
|
export { LaunchFilter, launchRefusalForSession };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
3
|
import { detectActiveSession } from './chunk-BFESLKPP.js';
|
|
4
|
-
import { resolveRepoIdentity } from './chunk-
|
|
4
|
+
import { resolveRepoIdentity } from './chunk-ANK2B36Y.js';
|
|
5
5
|
import { parseTrackerNumber, trackerNumberError } from './chunk-WZBHBH3J.js';
|
|
6
6
|
import { defaultFetch } from './chunk-BCLJSQCV.js';
|
|
7
7
|
import { unauthorizedMessage, resolveGatewayCredentials } from './chunk-BOLTI6LR.js';
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
3
|
import { isInLayout, packageRootIn, versionDir, switchCurrent, execStartLine } from './chunk-YYYXRJUW.js';
|
|
4
4
|
import { RUNNER_DIR_MODE } from './chunk-ZM2IMMYN.js';
|
|
5
|
-
import './chunk-Z5GEFMRW.js';
|
|
6
5
|
import { realpathSync, readFileSync, mkdirSync, rmSync, cpSync, existsSync } from 'fs';
|
|
7
6
|
import { dirname, sep, join } from 'path';
|
|
8
7
|
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { isLinkedRepository } from './chunk-
|
|
3
|
+
import { isLinkedRepository } from './chunk-7DBI6SNT.js';
|
|
4
4
|
import { readStatuslineState, STATE_FRESH_MS } from './chunk-F47JBIAW.js';
|
|
5
|
-
import { resolveCredentials, getJson, WorkCommandError } from './chunk-
|
|
6
|
-
import { sanitizeTerminal } from './chunk-
|
|
7
|
-
import { resolveRepoIdentity, compareVersions, SUPPORTED_VERSIONS } from './chunk-
|
|
5
|
+
import { resolveCredentials, getJson, WorkCommandError } from './chunk-BNMCXPG7.js';
|
|
6
|
+
import { sanitizeTerminal } from './chunk-3DEVYTNQ.js';
|
|
7
|
+
import { resolveRepoIdentity, compareVersions, SUPPORTED_VERSIONS } from './chunk-ANK2B36Y.js';
|
|
8
8
|
import { C } from './chunk-YRYMNBGQ.js';
|
|
9
9
|
import { readSettings, settingsPath, isOurStatuslineSlot, statuslineWrappedOriginal, REGISTERED_EVENTS, isOurHookCommand, ourCommandBins, cliBinResolvable, ourDirectScriptBins, wrappedOriginalCommand } from './chunk-5QRP3MVS.js';
|
|
10
10
|
import { readPin, readContextManifest, readDegraded, readSourceCache } from './chunk-WIOZAJ2W.js';
|
|
11
11
|
import { defaultFetch } from './chunk-BCLJSQCV.js';
|
|
12
12
|
import { resolveGatewayCredentials } from './chunk-BOLTI6LR.js';
|
|
13
13
|
import { readCloudConfig } from './chunk-EABW6AJQ.js';
|
|
14
|
-
import { sanitizeForContext, spoolDir, assessSyncHealth, readSyncStatus, SYNC_STALE_AFTER_MS, formatAgo, checkInstalledCliStaleness, workspaceRootOf, hookFiredMarkerPath, refreshCommand } from './chunk-
|
|
14
|
+
import { sanitizeForContext, spoolDir, assessSyncHealth, readSyncStatus, SYNC_STALE_AFTER_MS, formatAgo, checkInstalledCliStaleness, workspaceRootOf, hookFiredMarkerPath, refreshCommand } from './chunk-FA2BOX4O.js';
|
|
15
15
|
import { deadHooksState, preparePackageManagerCommand, contextExcludesPresent } from './chunk-XCCLFVXM.js';
|
|
16
16
|
import { findGitDirs } from './chunk-BJ3KTMDH.js';
|
|
17
17
|
import { homeFromEnv } from './chunk-VNVZDWO3.js';
|
|
@@ -1012,11 +1012,11 @@ async function gatherDoctorChecks() {
|
|
|
1012
1012
|
if (credential[0]?.status === "pass") checks.push(...await githubAppChecks());
|
|
1013
1013
|
const runnerVm = runnerVmOf(configPath);
|
|
1014
1014
|
if (runnerVm !== void 0) {
|
|
1015
|
-
const { realLima } = await import('./lima-
|
|
1015
|
+
const { realLima } = await import('./lima-7NPCNGWT.js');
|
|
1016
1016
|
const { runnerVmCheck } = await import('./doctor-53F2PYY7.js');
|
|
1017
1017
|
checks.push(runnerVmCheck(configPath, { lima: realLima(), home: homeFromEnv(), instance: runnerVm.instance }));
|
|
1018
1018
|
} else if (hasRunnerBlock(configPath)) {
|
|
1019
|
-
const { runnerHostChecks } = await import('./doctor-
|
|
1019
|
+
const { runnerHostChecks } = await import('./doctor-VOMYOHQU.js');
|
|
1020
1020
|
const hooks = (() => {
|
|
1021
1021
|
try {
|
|
1022
1022
|
return loadRunnerConfig(configPath).hooks;
|
|
@@ -1047,7 +1047,15 @@ async function gatherDoctorChecks() {
|
|
|
1047
1047
|
return void 0;
|
|
1048
1048
|
}
|
|
1049
1049
|
})();
|
|
1050
|
-
|
|
1050
|
+
const service = await (async () => {
|
|
1051
|
+
const { existsSync: existsSync2 } = await import('fs');
|
|
1052
|
+
if (process.platform !== "linux" || !existsSync2("/run/systemd/system")) return void 0;
|
|
1053
|
+
const { inspectService, realInspectProbes } = await import('./inspect-XAUSB7L3.js');
|
|
1054
|
+
const { DEFAULT_UNIT_NAME } = await import('./install-DSUZE2TJ.js');
|
|
1055
|
+
const { dirname: dirname2 } = await import('path');
|
|
1056
|
+
return inspectService(DEFAULT_UNIT_NAME, dirname2(configPath), realInspectProbes());
|
|
1057
|
+
})();
|
|
1058
|
+
checks.push(...await runnerHostChecks({ configPath, hooks, gateway, repos, runner, service }));
|
|
1051
1059
|
}
|
|
1052
1060
|
return checks;
|
|
1053
1061
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { isPlaceholderCommand } from './chunk-
|
|
4
|
-
import { describeCallFailure } from './chunk-
|
|
5
|
-
import { WorkCommandError, getJson } from './chunk-
|
|
6
|
-
import { sanitizeTerminal } from './chunk-
|
|
3
|
+
import { isPlaceholderCommand } from './chunk-4NLKY4WR.js';
|
|
4
|
+
import { describeCallFailure } from './chunk-RZSF5B46.js';
|
|
5
|
+
import { WorkCommandError, getJson } from './chunk-BNMCXPG7.js';
|
|
6
|
+
import { sanitizeTerminal } from './chunk-3DEVYTNQ.js';
|
|
7
7
|
import { builtinKind } from './chunk-3DD6AOWC.js';
|
|
8
|
-
import {
|
|
8
|
+
import { CLAUDE_CREDENTIAL_VARIABLES, claudeCredential } from './chunk-AUFG23AA.js';
|
|
9
9
|
import { signerFor } from './chunk-M5POMKKW.js';
|
|
10
10
|
|
|
11
11
|
createRequire(import.meta.url);
|
|
@@ -27,6 +27,18 @@ function needsClaudeCredential(hooks) {
|
|
|
27
27
|
var CREDENTIAL_FIX = "Run `claude setup-token`, then start the runner with CLAUDE_CODE_OAUTH_TOKEN set to the token it prints (or ANTHROPIC_API_KEY set to an API key), for example with EnvironmentFile= in the runner's systemd unit";
|
|
28
28
|
function claudeCredentialCheck(env, options = {}) {
|
|
29
29
|
const name = "Claude credential";
|
|
30
|
+
if (options.service !== void 0) {
|
|
31
|
+
const { unit, variables } = options.service;
|
|
32
|
+
if (variables.length === 0) {
|
|
33
|
+
return {
|
|
34
|
+
name,
|
|
35
|
+
status: "fail",
|
|
36
|
+
detail: `the service ${unit} has no Claude credential: neither ${CLAUDE_CREDENTIAL_VARIABLES.slice(0, 2).join(" nor ")} is set in its runner.env, so every build fails at provision as agent_credential_missing`,
|
|
37
|
+
fix: `${CREDENTIAL_FIX}, then restart the service: sudo systemctl restart ${unit}.`
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
return { name, status: "pass", detail: `${variables[0]} is set in the runner.env of the service ${unit}` };
|
|
41
|
+
}
|
|
30
42
|
const held = claudeCredential(env);
|
|
31
43
|
if (held === void 0) {
|
|
32
44
|
return {
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { runnerUnit, UNIT_MARKER } from './chunk-FBTE2A2X.js';
|
|
4
|
+
import { checkFit } from './chunk-OUUINEYB.js';
|
|
5
|
+
import { builtinKind } from './chunk-3DD6AOWC.js';
|
|
6
|
+
import { runnerDir } from './chunk-ZM2IMMYN.js';
|
|
7
|
+
import { dirname } from 'path';
|
|
8
|
+
import { spawnSync } from 'child_process';
|
|
9
|
+
|
|
10
|
+
createRequire(import.meta.url);
|
|
11
|
+
function withSystemd(conf) {
|
|
12
|
+
const lines = (conf ?? "").split("\n");
|
|
13
|
+
if (lines.at(-1) === "") lines.pop();
|
|
14
|
+
const boot = lines.findIndex((line) => line.trim() === "[boot]");
|
|
15
|
+
if (boot === -1) {
|
|
16
|
+
return { text: `${lines.length === 0 ? "" : `${lines.join("\n")}
|
|
17
|
+
|
|
18
|
+
`}[boot]
|
|
19
|
+
systemd=true
|
|
20
|
+
`, changed: true };
|
|
21
|
+
}
|
|
22
|
+
let end = lines.findIndex((line, at) => at > boot && /^\s*\[/.test(line));
|
|
23
|
+
if (end === -1) end = lines.length;
|
|
24
|
+
const key = lines.findIndex((line, at) => at > boot && at < end && /^\s*systemd\s*=/.test(line));
|
|
25
|
+
if (key !== -1) {
|
|
26
|
+
if (/^\s*systemd\s*=\s*true\s*$/i.test(lines[key])) return { text: `${lines.join("\n")}
|
|
27
|
+
`, changed: false };
|
|
28
|
+
lines[key] = "systemd=true";
|
|
29
|
+
} else {
|
|
30
|
+
lines.splice(boot + 1, 0, "systemd=true");
|
|
31
|
+
}
|
|
32
|
+
return { text: `${lines.join("\n")}
|
|
33
|
+
`, changed: true };
|
|
34
|
+
}
|
|
35
|
+
function taskNameFor(unitName) {
|
|
36
|
+
return unitName;
|
|
37
|
+
}
|
|
38
|
+
function taskCommand(distro) {
|
|
39
|
+
return `wsl.exe -d ${distro} --exec sleep infinity`;
|
|
40
|
+
}
|
|
41
|
+
function realTaskScheduler(run = defaultRun) {
|
|
42
|
+
const message = (result) => (result.stderr.trim() || result.stdout.trim()).slice(0, 300);
|
|
43
|
+
return {
|
|
44
|
+
available: () => run(["/?"]).status === 0,
|
|
45
|
+
create: (name, command) => {
|
|
46
|
+
const result = run(["/Create", "/F", "/TN", name, "/SC", "ONLOGON", "/TR", command]);
|
|
47
|
+
return result.status === 0 ? { ok: true } : { ok: false, message: message(result) };
|
|
48
|
+
},
|
|
49
|
+
exists: (name) => run(["/Query", "/TN", name]).status === 0,
|
|
50
|
+
remove: (name) => {
|
|
51
|
+
const result = run(["/Delete", "/F", "/TN", name]);
|
|
52
|
+
return result.status === 0 ? { ok: true } : { ok: false, message: message(result) };
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function defaultRun(args) {
|
|
57
|
+
const result = spawnSync("schtasks.exe", args, { encoding: "utf8", timeout: 3e4 });
|
|
58
|
+
return { status: result.error === void 0 ? result.status : null, stdout: result.stdout ?? "", stderr: result.stderr ?? "" };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// src/runner/service/install.ts
|
|
62
|
+
var DEFAULT_UNIT_NAME = "outerlayer-runner";
|
|
63
|
+
function unitPathFor(unitName = DEFAULT_UNIT_NAME) {
|
|
64
|
+
return `/etc/systemd/system/${unitName}.service`;
|
|
65
|
+
}
|
|
66
|
+
var outcome = (ok, state, lines, unit) => ({ ok, state, lines, ...unit === void 0 ? {} : { unit } });
|
|
67
|
+
function asRoot(deps, lines, say, argv) {
|
|
68
|
+
say(`sudo systemctl ${argv.join(" ")}`);
|
|
69
|
+
const result = deps.systemd.systemctl(argv, { sudo: true });
|
|
70
|
+
if (result.status !== 0) lines.push(`sudo systemctl ${argv.join(" ")} failed with exit status ${result.status}`);
|
|
71
|
+
return result.status === 0;
|
|
72
|
+
}
|
|
73
|
+
function attempt(lines, step) {
|
|
74
|
+
try {
|
|
75
|
+
step();
|
|
76
|
+
return true;
|
|
77
|
+
} catch (err) {
|
|
78
|
+
lines.push(err instanceof Error ? err.message : String(err));
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
var OWN_HOOKS = "the hooks are the host's own executables, so the runner does not write a service for them: run `outerlayer runner install --service --print` to print the unit, and edit it for your hooks";
|
|
83
|
+
function installService(request, deps) {
|
|
84
|
+
const unitName = request.unitName ?? DEFAULT_UNIT_NAME;
|
|
85
|
+
const unitPath = unitPathFor(unitName);
|
|
86
|
+
const lines = [];
|
|
87
|
+
const say = deps.say ?? ((line) => void lines.push(line));
|
|
88
|
+
const kind = builtinKind(request.hooks.provision);
|
|
89
|
+
const own = kind === "own" || builtinKind(request.hooks.cleanup) === "own";
|
|
90
|
+
const unit = runnerUnit({
|
|
91
|
+
user: request.user,
|
|
92
|
+
configDir: dirname(request.configPath),
|
|
93
|
+
execStart: request.execStart,
|
|
94
|
+
timeLimitMinutes: request.timeLimitMinutes,
|
|
95
|
+
docker: kind === "container" || kind === "vm" || builtinKind(request.hooks.cleanup) === "container" || builtinKind(request.hooks.cleanup) === "vm"
|
|
96
|
+
});
|
|
97
|
+
if (request.print === true) return outcome(true, "printed", [], unit);
|
|
98
|
+
if (own) return outcome(false, "refused", [OWN_HOOKS]);
|
|
99
|
+
if (deps.platform === "darwin" || deps.platform === "windows") {
|
|
100
|
+
return outcome(false, "refused", [
|
|
101
|
+
deps.platform === "darwin" ? "the runner on a Mac lives in a Linux VM, and its service is set up there: run `outerlayer runner init --vm`" : "the runner on Windows lives in WSL2: run this from a WSL2 shell"
|
|
102
|
+
]);
|
|
103
|
+
}
|
|
104
|
+
if (!deps.systemd.running()) {
|
|
105
|
+
if (deps.platform === "wsl2" && deps.wslConf !== void 0) {
|
|
106
|
+
const merged = withSystemd(deps.wslConf.read());
|
|
107
|
+
if (merged.changed) {
|
|
108
|
+
say("sudo tee /etc/wsl.conf");
|
|
109
|
+
const wslConf = deps.wslConf;
|
|
110
|
+
if (!attempt(lines, () => wslConf.write(merged.text))) return outcome(false, "refused", lines);
|
|
111
|
+
}
|
|
112
|
+
lines.push(
|
|
113
|
+
"systemd is not running in this WSL2 distribution. `systemd=true` is " + (merged.changed ? "now" : "already") + " in /etc/wsl.conf; run `wsl --shutdown` once from Windows, then run this again. No service or scheduled task is set up until then"
|
|
114
|
+
);
|
|
115
|
+
return outcome(true, "needs-wsl-shutdown", lines);
|
|
116
|
+
}
|
|
117
|
+
return outcome(false, "refused", ["systemd was not found (/run/systemd/system is missing), so no service is set up. `--print` prints the unit for another service manager"]);
|
|
118
|
+
}
|
|
119
|
+
if (kind === "container" || kind === "vm") {
|
|
120
|
+
const fit = checkFit({ concurrency: request.concurrency, buildMemory: request.buildMemory, usableBytes: deps.usableMemoryBytes });
|
|
121
|
+
if (!fit.fits) return outcome(false, "refused", [`${fit.message}. Nothing is set up: lower runner.concurrency or runner.build.memory, then run this again`]);
|
|
122
|
+
}
|
|
123
|
+
const existing = deps.systemd.read(unitPath);
|
|
124
|
+
const write = () => {
|
|
125
|
+
say(`sudo tee ${unitPath}`);
|
|
126
|
+
return attempt(lines, () => deps.systemd.write(unitPath, unit));
|
|
127
|
+
};
|
|
128
|
+
const running = () => deps.systemd.systemctl(["is-enabled", unitName]).status === 0 && deps.systemd.systemctl(["is-active", unitName]).status === 0;
|
|
129
|
+
const ensureTask = () => {
|
|
130
|
+
if (deps.platform === "wsl2") lines.push(...ensureLoginTask(deps, unitName));
|
|
131
|
+
};
|
|
132
|
+
if (existing === unit) {
|
|
133
|
+
if (!running() && !asRoot(deps, lines, say, ["enable", "--now", unitName])) return outcome(false, "refused", lines);
|
|
134
|
+
lines.push(`the service ${unitName} is current: the unit already matches the config, so nothing was changed`);
|
|
135
|
+
ensureTask();
|
|
136
|
+
return outcome(true, "current", lines);
|
|
137
|
+
}
|
|
138
|
+
if (existing === void 0) {
|
|
139
|
+
if (!write()) return outcome(false, "refused", lines);
|
|
140
|
+
if (!asRoot(deps, lines, say, ["daemon-reload"]) || !asRoot(deps, lines, say, ["enable", "--now", unitName])) {
|
|
141
|
+
say(`sudo rm ${unitPath}`);
|
|
142
|
+
if (attempt(lines, () => deps.systemd.remove(unitPath))) deps.systemd.systemctl(["daemon-reload"], { sudo: true });
|
|
143
|
+
return outcome(false, "refused", lines);
|
|
144
|
+
}
|
|
145
|
+
lines.push(`installed and started the service ${unitName}: the runner starts at boot and after a crash`);
|
|
146
|
+
ensureTask();
|
|
147
|
+
return outcome(true, "installed", lines);
|
|
148
|
+
}
|
|
149
|
+
if (!existing.startsWith(UNIT_MARKER) && request.replace !== true) {
|
|
150
|
+
lines.push(`${unitPath} differs from what this command writes, and it was not written by it, so it was left alone. Pass --replace to replace it, or \`--print\` to compare`);
|
|
151
|
+
return outcome(false, "differs", lines);
|
|
152
|
+
}
|
|
153
|
+
if (!write()) return outcome(false, "refused", lines);
|
|
154
|
+
if (!asRoot(deps, lines, say, ["daemon-reload"])) return outcome(false, "refused", lines);
|
|
155
|
+
if ((!existing.startsWith(UNIT_MARKER) || !running()) && !asRoot(deps, lines, say, ["enable", "--now", unitName])) return outcome(false, "refused", lines);
|
|
156
|
+
ensureTask();
|
|
157
|
+
if (deps.buildsRunning(runnerDir(request.configPath))) {
|
|
158
|
+
lines.push(`rewrote ${unitPath}; a build is running, so the service was not restarted and the new unit applies at the next restart`);
|
|
159
|
+
return outcome(true, "pending-restart", lines);
|
|
160
|
+
}
|
|
161
|
+
if (!asRoot(deps, lines, say, ["restart", unitName])) return outcome(false, "refused", lines);
|
|
162
|
+
lines.push(`rewrote ${unitPath} and restarted the service ${unitName}`);
|
|
163
|
+
return outcome(true, "updated", lines);
|
|
164
|
+
}
|
|
165
|
+
function ensureLoginTask(deps, unitName) {
|
|
166
|
+
const tasks = deps.tasks;
|
|
167
|
+
const name = taskNameFor(unitName);
|
|
168
|
+
const command = taskCommand(deps.distro ?? "Ubuntu");
|
|
169
|
+
const manual = `from Windows, run: schtasks /Create /F /TN ${name} /SC ONLOGON /TR "${command}"`;
|
|
170
|
+
if (tasks === void 0 || !tasks.available()) {
|
|
171
|
+
return [`Windows interop is off, so the scheduled task that starts WSL2 at login was not made: ${manual}`];
|
|
172
|
+
}
|
|
173
|
+
if (tasks.exists(name)) return [`the scheduled task ${name} already starts WSL2 at login`];
|
|
174
|
+
const created = tasks.create(name, command);
|
|
175
|
+
return created.ok ? [`made the scheduled task ${name}: it starts WSL2 at login and keeps it running, so the service runs from login`] : [`the scheduled task ${name} was not made (${created.message ?? "schtasks.exe failed"}): ${manual}`];
|
|
176
|
+
}
|
|
177
|
+
function uninstallService(request, deps) {
|
|
178
|
+
const unitName = request.unitName ?? DEFAULT_UNIT_NAME;
|
|
179
|
+
const unitPath = unitPathFor(unitName);
|
|
180
|
+
const lines = [];
|
|
181
|
+
const say = deps.say ?? ((line) => void lines.push(line));
|
|
182
|
+
const taskName = taskNameFor(unitName);
|
|
183
|
+
const removeTask = () => {
|
|
184
|
+
if (deps.platform !== "wsl2" || deps.tasks === void 0 || !deps.tasks.available() || !deps.tasks.exists(taskName)) return;
|
|
185
|
+
const removed = deps.tasks.remove(taskName);
|
|
186
|
+
lines.push(removed.ok ? `removed the scheduled task ${taskName}` : `the scheduled task ${taskName} was not removed (${removed.message ?? "schtasks.exe failed"}): from Windows, run: schtasks /Delete /F /TN ${taskName}`);
|
|
187
|
+
};
|
|
188
|
+
const existing = deps.systemd.running() ? deps.systemd.read(unitPath) : void 0;
|
|
189
|
+
if (existing === void 0) {
|
|
190
|
+
removeTask();
|
|
191
|
+
lines.push(`no service ${unitName} is installed, so there is nothing to remove`);
|
|
192
|
+
return outcome(true, "absent", lines);
|
|
193
|
+
}
|
|
194
|
+
if (!existing.startsWith(UNIT_MARKER)) {
|
|
195
|
+
lines.push(`${unitPath} was not written by \`runner install --service\`, so it was left alone`);
|
|
196
|
+
return outcome(false, "differs", lines);
|
|
197
|
+
}
|
|
198
|
+
if (!asRoot(deps, lines, say, ["disable", "--now", unitName])) return outcome(false, "refused", lines);
|
|
199
|
+
say(`sudo rm ${unitPath}`);
|
|
200
|
+
if (!attempt(lines, () => deps.systemd.remove(unitPath))) return outcome(false, "refused", lines);
|
|
201
|
+
if (deps.systemd.read(unitPath) !== void 0) {
|
|
202
|
+
lines.push(`${unitPath} is still there after the remove, so the service ${unitName} was not removed`);
|
|
203
|
+
return outcome(false, "refused", lines);
|
|
204
|
+
}
|
|
205
|
+
if (!asRoot(deps, lines, say, ["daemon-reload"])) return outcome(false, "refused", lines);
|
|
206
|
+
removeTask();
|
|
207
|
+
lines.push(`removed the service ${unitName}`);
|
|
208
|
+
return outcome(true, "removed", lines);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export { DEFAULT_UNIT_NAME, installService, realTaskScheduler, uninstallService, unitPathFor };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { parseSize } from './chunk-F6GFZXZ3.js';
|
|
4
|
+
|
|
5
|
+
createRequire(import.meta.url);
|
|
6
|
+
|
|
7
|
+
// src/runner/service/fit.ts
|
|
8
|
+
var GIB = 1024 ** 3;
|
|
9
|
+
var RUNNER_RESERVE_BYTES = 2 * GIB;
|
|
10
|
+
function gib(bytes) {
|
|
11
|
+
return `${Number((bytes / GIB).toFixed(1))} GiB`;
|
|
12
|
+
}
|
|
13
|
+
function checkFit(input) {
|
|
14
|
+
const perBuild = parseSize(input.buildMemory);
|
|
15
|
+
if (perBuild === void 0) return { fits: true };
|
|
16
|
+
const neededBytes = input.concurrency * perBuild;
|
|
17
|
+
const availableBytes = Math.max(0, input.usableBytes - RUNNER_RESERVE_BYTES);
|
|
18
|
+
if (neededBytes <= availableBytes) return { fits: true };
|
|
19
|
+
return {
|
|
20
|
+
fits: false,
|
|
21
|
+
neededBytes,
|
|
22
|
+
availableBytes,
|
|
23
|
+
message: `runner.concurrency ${input.concurrency} times runner.build.memory ${input.buildMemory} needs ${gib(neededBytes)}, and ${gib(availableBytes)} is available (${gib(input.usableBytes)} of memory less ${gib(RUNNER_RESERVE_BYTES)} for the runner and the system)`
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export { RUNNER_RESERVE_BYTES, checkFit };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { dashboardBase, isRepositoryLinked, workPageUrl, notLinkedText } from './chunk-
|
|
4
|
-
import { sanitizeTerminal } from './chunk-
|
|
5
|
-
import { remoteLookupFailed } from './chunk-
|
|
3
|
+
import { dashboardBase, isRepositoryLinked, workPageUrl, notLinkedText } from './chunk-7DBI6SNT.js';
|
|
4
|
+
import { sanitizeTerminal } from './chunk-3DEVYTNQ.js';
|
|
5
|
+
import { remoteLookupFailed } from './chunk-ANK2B36Y.js';
|
|
6
6
|
import { resolveGatewayCredentials } from './chunk-BOLTI6LR.js';
|
|
7
7
|
import { findGitDirs, readRepoConnection, writeRepoConnection } from './chunk-BJ3KTMDH.js';
|
|
8
8
|
import { createInterface } from 'readline/promises';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { MIN_ENGINE_MAJOR } from './chunk-
|
|
3
|
+
import { MIN_ENGINE_MAJOR } from './chunk-RZSF5B46.js';
|
|
4
4
|
import { firecrackerArch, MICROVM_ARCHES, guestAssetsPinned } from './chunk-Z62HNKEJ.js';
|
|
5
5
|
import { spawnSync } from 'child_process';
|
|
6
6
|
import { existsSync, accessSync, constants, statSync, readFileSync } from 'fs';
|