@outerlayer/cli 0.14.0 → 0.15.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 +17 -0
- package/dist/build-info.json +1 -1
- package/dist/{check-SRGR36KI.js → check-2JSSXCKJ.js} +8 -8
- package/dist/{chunk-FI5UYJMQ.js → chunk-ADJRGVZC.js} +1 -1
- package/dist/{chunk-4OGPT6I2.js → chunk-ASOSIKA4.js} +2 -2
- package/dist/{chunk-SMHOCDCI.js → chunk-F7GBYX2P.js} +3 -3
- package/dist/{chunk-Q2UK3C4P.js → chunk-HXLMZOKU.js} +2 -2
- package/dist/{chunk-SWHQ4LUM.js → chunk-JR6ERP3M.js} +20 -2
- package/dist/{chunk-NHDVD7DU.js → chunk-NXUH742G.js} +10 -10
- package/dist/{chunk-HFKQIZXL.js → chunk-QLTYTPYU.js} +2 -2
- package/dist/{chunk-RBKIH5ST.js → chunk-UUDDPNRJ.js} +1 -1
- package/dist/{chunk-JYIEHQIF.js → chunk-Z3NOIFDR.js} +1 -1
- package/dist/{cli-UW2H3UUW.js → cli-RGGXQTWI.js} +30 -30
- package/dist/{command-5LD7G2QQ.js → command-ETUAEXP2.js} +3 -3
- package/dist/{doctor-H7REBSLQ.js → doctor-CORFPYY6.js} +5 -5
- package/dist/index.js +1 -1
- package/dist/{init-UBXK6MF7.js → init-MIC37V6J.js} +4 -4
- package/dist/{init-NTXA3SN7.js → init-OPB7HHXO.js} +4 -4
- package/dist/{init-cmd-NARDUPIG.js → init-cmd-JR3RKNFB.js} +2 -2
- package/dist/{loop-SW2M5VDD.js → loop-QNSM5GXS.js} +8 -8
- package/dist/{machine-U66FKVDP.js → machine-IT6GG7FF.js} +3 -3
- package/dist/{runner-key-3JSMDRJA.js → runner-key-7SSNMUBQ.js} +4 -4
- package/dist/{status-STZYPU3W.js → status-7ZX57OFJ.js} +3 -3
- package/dist/{work-claim-cmd-LY4U6QHN.js → work-claim-cmd-A6IS6CQ7.js} +3 -3
- package/dist/{work-cmd-RVRQ32EH.js → work-cmd-EQSW5DTD.js} +1 -1
- package/dist/{work-comment-cmd-FFUM4TJU.js → work-comment-cmd-7VLKI7FN.js} +1 -1
- package/dist/{work-open-pr-cmd-HLDWP4T2.js → work-open-pr-cmd-JEVD4HMQ.js} +1 -1
- package/package.json +1 -1
- package/skill-pack/shipped.json +5 -0
- package/skill-pack/template/build/SKILL.md +2 -1
- package/skill-pack/template/build/references/release.md +4 -1
- package/skill-pack/template/build/scripts/criterion-citations.mjs +184 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# @outerlayer/cli
|
|
2
2
|
|
|
3
|
+
## 0.15.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 9f42028: `outerlayer work status` now shows the acceptance criteria recorded for an item, in a Criteria section marked "drafted by the build" when the build wrote them, and as `criteria` in `--json`. The `get_work_item` MCP tool returns the same field.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- e5b5713: A runner with `runner.update.maxWaitHours` set to 0 now claims nothing while a build runs and an update is pending. A clock tick between two reads in the same pass could leave the wait looking one millisecond short and let that pass claim work.
|
|
12
|
+
- 8d3eacb: Update the runner protocol contract to match the gateway's repository governance answer.
|
|
13
|
+
|
|
14
|
+
## 0.14.1
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- f47136a: The starter `/build` skill now links each criterion to the tests that cite it, so the work item's Criteria tab lists them instead of "No tests found". A new script, `criterion-citations.mjs`, runs on the criteria judge's report before it is emitted. It finds tests that name a criterion's id in a title or a comment, whatever the id's form. `outerlayer init --update` adds the script to repositories set up earlier.
|
|
19
|
+
|
|
3
20
|
## 0.14.0
|
|
4
21
|
|
|
5
22
|
### Minor Changes
|
package/dist/build-info.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"id":"2026-10-
|
|
1
|
+
{"id":"2026-10-09T12:01:54.751Z+00e38ff","root":"/home/runner/_work/outerlayer/outerlayer","pkg":"outerlayer"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { probeCredentials } from './chunk-
|
|
4
|
-
import './chunk-
|
|
3
|
+
import { probeCredentials } from './chunk-NXUH742G.js';
|
|
4
|
+
import './chunk-ASOSIKA4.js';
|
|
5
5
|
import './chunk-O765JR4W.js';
|
|
6
6
|
import './chunk-UGP76IZ5.js';
|
|
7
7
|
import './chunk-3JVYMD4G.js';
|
|
@@ -9,24 +9,24 @@ import './chunk-YETTWTAY.js';
|
|
|
9
9
|
import './chunk-RMWLBUCJ.js';
|
|
10
10
|
import './chunk-I2UOAV7X.js';
|
|
11
11
|
import './chunk-HYZRY4KZ.js';
|
|
12
|
-
import './chunk-
|
|
12
|
+
import './chunk-ADJRGVZC.js';
|
|
13
13
|
import './chunk-KFRM23WD.js';
|
|
14
14
|
import './chunk-MSASO5O4.js';
|
|
15
15
|
import './chunk-HHJDN6AU.js';
|
|
16
|
-
import { placeholderQueues, placeholderProblem, permissionModeProblems, permissionModeProblem, needsClaudeCredential, claudeCredentialCheck, claimPermissionProblem } from './chunk-
|
|
16
|
+
import { placeholderQueues, placeholderProblem, permissionModeProblems, permissionModeProblem, needsClaudeCredential, claudeCredentialCheck, claimPermissionProblem } from './chunk-F7GBYX2P.js';
|
|
17
17
|
import './chunk-YYYRBYWG.js';
|
|
18
18
|
import './chunk-TI7B7UEO.js';
|
|
19
19
|
import './chunk-TBT347UY.js';
|
|
20
|
-
import './chunk-
|
|
21
|
-
import './chunk-
|
|
22
|
-
import './chunk-
|
|
20
|
+
import './chunk-HXLMZOKU.js';
|
|
21
|
+
import './chunk-Z3NOIFDR.js';
|
|
22
|
+
import './chunk-UUDDPNRJ.js';
|
|
23
23
|
import './chunk-KMDLFI4P.js';
|
|
24
24
|
import './chunk-QKFB4K7X.js';
|
|
25
25
|
import './chunk-RSNSZ2LJ.js';
|
|
26
26
|
import './chunk-T5VPP6H7.js';
|
|
27
27
|
import './chunk-RLYZLUQ7.js';
|
|
28
28
|
import { parseRunnerEnv } from './chunk-6MZSX4M7.js';
|
|
29
|
-
import './chunk-
|
|
29
|
+
import './chunk-JR6ERP3M.js';
|
|
30
30
|
import './chunk-NPQWKGUA.js';
|
|
31
31
|
import './chunk-IP7BIBXJ.js';
|
|
32
32
|
import { loadRunnerConfig, RunnerConfigError, checkHooks } from './chunk-22RMZBGW.js';
|
|
@@ -4,11 +4,11 @@ import { writeJobFile, readJobFile } from './chunk-RMWLBUCJ.js';
|
|
|
4
4
|
import { DOCKERD_LOG } from './chunk-HYZRY4KZ.js';
|
|
5
5
|
import { senderPidfile } from './chunk-KFRM23WD.js';
|
|
6
6
|
import { installScrubContext } from './chunk-MSASO5O4.js';
|
|
7
|
-
import { AssetError, describeCallFailure, obtainImage, ensureRootfs, gitRemoteFor, startServicesFor, createBuildDisk, machineConfig, refusalCode, resolveAgentUser, AgentUserError, runBuildSteps, removeStagedContext, stageGovernedContext, shellQuote, createRunnerLog, createContainerRuntime, stopContainer, cleanupContainer, outOfMemoryReason, formatDuration, retrySend, formatClock, containerSenderCommand, syncInContainer, copyTranscriptsFromContainer, GH_CONFIG_YML, ghHostsYml, ADDED_USER, isLiteralAddress, signalInContainer, deleteBuildDisk, startCheckContainer, lifecycleSteps, lifecycleFailureReason, writeEvalInput, EVAL_INPUT_NAME, copyTranscripts, senderSignalScript } from './chunk-
|
|
7
|
+
import { AssetError, describeCallFailure, obtainImage, ensureRootfs, gitRemoteFor, startServicesFor, createBuildDisk, machineConfig, refusalCode, resolveAgentUser, AgentUserError, runBuildSteps, removeStagedContext, stageGovernedContext, shellQuote, createRunnerLog, createContainerRuntime, stopContainer, cleanupContainer, outOfMemoryReason, formatDuration, retrySend, formatClock, containerSenderCommand, syncInContainer, copyTranscriptsFromContainer, GH_CONFIG_YML, ghHostsYml, ADDED_USER, isLiteralAddress, signalInContainer, deleteBuildDisk, startCheckContainer, lifecycleSteps, lifecycleFailureReason, writeEvalInput, EVAL_INPUT_NAME, copyTranscripts, senderSignalScript } from './chunk-UUDDPNRJ.js';
|
|
8
8
|
import { childEnvironment } from './chunk-KMDLFI4P.js';
|
|
9
9
|
import { waitForAgent, vmExecArgs } from './chunk-QKFB4K7X.js';
|
|
10
10
|
import { socketPathProblem, vsockLinks, linkVsockSockets } from './chunk-RSNSZ2LJ.js';
|
|
11
|
-
import { WorkCommandError, postJson, isSignatureRefusal, getJson } from './chunk-
|
|
11
|
+
import { WorkCommandError, postJson, isSignatureRefusal, getJson } from './chunk-JR6ERP3M.js';
|
|
12
12
|
import { isBuiltinContainer, isBuiltinProcess, isBuiltinVm, DEFAULT_CHECK_TIME_LIMIT_MINUTES } from './chunk-22RMZBGW.js';
|
|
13
13
|
import { readBranchHead, diffBetween, fetchCommit } from './chunk-R7PF5OOU.js';
|
|
14
14
|
import { isSameProcess, startTimeOf } from './chunk-N5FOE5PS.js';
|
|
@@ -1,8 +1,8 @@
|
|
|
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-
|
|
3
|
+
import { isPlaceholderCommand } from './chunk-HXLMZOKU.js';
|
|
4
|
+
import { describeCallFailure } from './chunk-UUDDPNRJ.js';
|
|
5
|
+
import { WorkCommandError, getJson } from './chunk-JR6ERP3M.js';
|
|
6
6
|
import { builtinKind } from './chunk-22RMZBGW.js';
|
|
7
7
|
import { CLAUDE_CREDENTIAL_VARIABLES, claudeCredential } from './chunk-4Q7OMKAT.js';
|
|
8
8
|
import { sanitizeTerminal } from './chunk-WUYTWNMA.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { detectMachine, supportsContainerBuilds, reachDockerStep, describeMachineDocker, supportsMicroVmBuilds } from './chunk-
|
|
4
|
-
import { MIN_ENGINE_MAJOR } from './chunk-
|
|
3
|
+
import { detectMachine, supportsContainerBuilds, reachDockerStep, describeMachineDocker, supportsMicroVmBuilds } from './chunk-Z3NOIFDR.js';
|
|
4
|
+
import { MIN_ENGINE_MAJOR } from './chunk-UUDDPNRJ.js';
|
|
5
5
|
import { hasStoredCredential, AGENT_COMMANDS, storeClaudeCredential, credentialFromStdin, isAgentName } from './chunk-T5VPP6H7.js';
|
|
6
6
|
import { runnerCredentialProblem } from './chunk-22RMZBGW.js';
|
|
7
7
|
import { runnerDir, RUNNER_DIR_MODE, RUNNER_FILE_MODE } from './chunk-ZM2IMMYN.js';
|
|
@@ -450,6 +450,24 @@ async function runWorkRemove(opts) {
|
|
|
450
450
|
if (!opts.quiet) process.stdout.write(output + "\n");
|
|
451
451
|
return { data, output };
|
|
452
452
|
}
|
|
453
|
+
function statusOutput(data, json) {
|
|
454
|
+
if (json) return JSON.stringify(data);
|
|
455
|
+
const document = JSON.stringify(data, null, 2);
|
|
456
|
+
const criteria = data.criteria;
|
|
457
|
+
if (criteria === void 0) return document;
|
|
458
|
+
if (criteria === null || !Array.isArray(criteria.items)) return `${document}
|
|
459
|
+
|
|
460
|
+
Criteria: none recorded`;
|
|
461
|
+
const mark = criteria.drafted === true ? ` ${C.dim}(drafted by the build)${C.reset}` : "";
|
|
462
|
+
const lines = criteria.items.map((item) => {
|
|
463
|
+
const proof = typeof item.proof === "string" ? ` ${C.dim}[proof: ${sanitizeTerminal(item.proof)}]${C.reset}` : "";
|
|
464
|
+
return ` ${sanitizeTerminal(String(item.id))} ${sanitizeTerminal(String(item.text))}${proof}`;
|
|
465
|
+
});
|
|
466
|
+
return `${document}
|
|
467
|
+
|
|
468
|
+
Criteria${mark}
|
|
469
|
+
${lines.join("\n")}`;
|
|
470
|
+
}
|
|
453
471
|
async function runWorkStatus(opts) {
|
|
454
472
|
const cwd = opts.cwd ?? process.cwd();
|
|
455
473
|
await connectIfOneFits(opts);
|
|
@@ -463,7 +481,7 @@ async function runWorkStatus(opts) {
|
|
|
463
481
|
creds.appId,
|
|
464
482
|
opts.fetchImpl ?? defaultFetch
|
|
465
483
|
);
|
|
466
|
-
const body =
|
|
484
|
+
const body = statusOutput(data2, opts.json === true);
|
|
467
485
|
if (!opts.quiet) process.stdout.write(body + "\n");
|
|
468
486
|
return { data: data2, output: body };
|
|
469
487
|
}
|
|
@@ -476,7 +494,7 @@ async function runWorkStatus(opts) {
|
|
|
476
494
|
throw new WorkCommandError(`no live item for ${repository}#${number}`);
|
|
477
495
|
}
|
|
478
496
|
const { data } = await getJson(creds.url, `/v1/work-items/${item.id}`, creds.apiKey, creds.appId, fetchImpl);
|
|
479
|
-
const output =
|
|
497
|
+
const output = statusOutput(data, opts.json === true);
|
|
480
498
|
if (!opts.quiet) process.stdout.write(output + "\n");
|
|
481
499
|
return { data, output };
|
|
482
500
|
}
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { LEASE_SECONDS, releaseLease, isUnfinished, setStage, jobEnv, runHook, describeHookEnd, provisionMicroVm, deliverCommitHooks, provisionProcess, provisionEnvironment, readProvisionOut, resolveHostBuild, workdirProblem, listenOnBroker, hostEnvironment, hostCommandEnvironment, execArgv, microVmCommandLine, runCommand, execCommandLine, startSender, startHostServices, finishJob, isGroupAlive, outcomeFromExitCode, isTimedOut, readCpuUsec, isIdle, stopOrphanedMicroVms, unfinishedJobs, listAdoptableJobs, retryFailedReleases, recoverLostJobs, hookLimitsFor, needsRenew, renewLease, RENEW_INTERVAL_SECONDS, ALREADY_RELEASED } from './chunk-
|
|
3
|
+
import { LEASE_SECONDS, releaseLease, isUnfinished, setStage, jobEnv, runHook, describeHookEnd, provisionMicroVm, deliverCommitHooks, provisionProcess, provisionEnvironment, readProvisionOut, resolveHostBuild, workdirProblem, listenOnBroker, hostEnvironment, hostCommandEnvironment, execArgv, microVmCommandLine, runCommand, execCommandLine, startSender, startHostServices, finishJob, isGroupAlive, outcomeFromExitCode, isTimedOut, readCpuUsec, isIdle, stopOrphanedMicroVms, unfinishedJobs, listAdoptableJobs, retryFailedReleases, recoverLostJobs, hookLimitsFor, needsRenew, renewLease, RENEW_INTERVAL_SECONDS, ALREADY_RELEASED } from './chunk-ASOSIKA4.js';
|
|
4
4
|
import { writePendingUpdate } from './chunk-O765JR4W.js';
|
|
5
5
|
import { readHousekeepingRecord, writeHousekeepingRecord } from './chunk-UGP76IZ5.js';
|
|
6
6
|
import { decideInstalledSwitch } from './chunk-3JVYMD4G.js';
|
|
7
7
|
import { fetchVersionMeta, npmVersion, npmSupportsProvenance, fetchTarballDigest, fetchProvenance, installVersion, auditSignatures } from './chunk-YETTWTAY.js';
|
|
8
8
|
import { writeJobFile, readJobFile, readJobFileChecked } from './chunk-RMWLBUCJ.js';
|
|
9
9
|
import { compareVersions, satisfiesRange } from './chunk-I2UOAV7X.js';
|
|
10
|
-
import { RUNNER_PROTOCOL } from './chunk-
|
|
11
|
-
import { placeholderQueues, placeholderProblem } from './chunk-
|
|
10
|
+
import { RUNNER_PROTOCOL } from './chunk-ADJRGVZC.js';
|
|
11
|
+
import { placeholderQueues, placeholderProblem } from './chunk-F7GBYX2P.js';
|
|
12
12
|
import { freeBytes, formatBytes } from './chunk-YYYRBYWG.js';
|
|
13
13
|
import { writeMarker, readMarker, currentVersion, removeMarker, entryIn, versionDir, startedThroughCurrent, switchCurrent, cleanVersions, isInLayout, stagingDir } from './chunk-TI7B7UEO.js';
|
|
14
14
|
import { RepoFilter } from './chunk-TBT347UY.js';
|
|
15
|
-
import { describeCallFailure, retrySend, shortClaim, relativeToRunner, formatDuration, provisionContainer, lifecycleSteps, lifecycleFailureReason, prepareBranch, containerCommandLine, BuildTokens, classifyRefusal, refusalCode, measureDisk, createRunnerLog, setUpRunnerCgroup, parseCpuList, ensureTunnel, keepGhConfigCurrent, createContainerRuntime, formatClock } from './chunk-
|
|
15
|
+
import { describeCallFailure, retrySend, shortClaim, relativeToRunner, formatDuration, provisionContainer, lifecycleSteps, lifecycleFailureReason, prepareBranch, containerCommandLine, BuildTokens, classifyRefusal, refusalCode, measureDisk, createRunnerLog, setUpRunnerCgroup, parseCpuList, ensureTunnel, keepGhConfigCurrent, createContainerRuntime, formatClock } from './chunk-UUDDPNRJ.js';
|
|
16
16
|
import { spawnRun } from './chunk-KMDLFI4P.js';
|
|
17
17
|
import { checkFit } from './chunk-RLYZLUQ7.js';
|
|
18
|
-
import { isSignatureRefusal, WorkCommandError, getJson, postJson } from './chunk-
|
|
18
|
+
import { isSignatureRefusal, WorkCommandError, getJson, postJson } from './chunk-JR6ERP3M.js';
|
|
19
19
|
import { isBuiltinContainer, isBuiltinVm, isBuiltinProcess, loadRunnerConfig, checkHooks, builtinKind, DEFAULT_UPDATE_MAX_WAIT_HOURS } from './chunk-22RMZBGW.js';
|
|
20
20
|
import { startTimeOf, claimPidFile, readPidFile, isSameProcess, releasePidFile } from './chunk-N5FOE5PS.js';
|
|
21
21
|
import { builderName, runnerLabelValue, ensureFailureCount, builderSpec, ensureBuilder, BUILD_LABEL, RUNNER_LABEL } from './chunk-4NPZVMJU.js';
|
|
@@ -958,9 +958,9 @@ function considerNamedVersion(state, named) {
|
|
|
958
958
|
logOf(state).line(`this runner cannot update itself to ${named}: ${blocker}`);
|
|
959
959
|
}
|
|
960
960
|
}
|
|
961
|
-
function adoptUpdateWait(state, version) {
|
|
961
|
+
function adoptUpdateWait(state, version, nowMs) {
|
|
962
962
|
const previous = state.updateWait;
|
|
963
|
-
const wait = { version, sinceMs: previous?.sinceMs ??
|
|
963
|
+
const wait = { version, sinceMs: previous?.sinceMs ?? nowMs, holdSinceMs: previous?.holdSinceMs };
|
|
964
964
|
state.updateWait = wait;
|
|
965
965
|
return wait;
|
|
966
966
|
}
|
|
@@ -1012,7 +1012,7 @@ function runningItems(state) {
|
|
|
1012
1012
|
function holdForUpdate(state, version, mayHold = true) {
|
|
1013
1013
|
const log = logOf(state);
|
|
1014
1014
|
const nowMs = nowOf(state).getTime();
|
|
1015
|
-
const wait = adoptUpdateWait(state, version);
|
|
1015
|
+
const wait = adoptUpdateWait(state, version, nowMs);
|
|
1016
1016
|
const failed2 = readLastUpdate(state.config.configPath);
|
|
1017
1017
|
const failedAtMs = failed2?.to === version && failed2.outcome === "retrying" ? new Date(failed2.at ?? Number.NaN).getTime() : Number.NaN;
|
|
1018
1018
|
if (failedAtMs < wait.sinceMs) wait.sinceMs = failedAtMs;
|
|
@@ -1315,7 +1315,7 @@ var CLAIM_REFUSAL_CODES = new Set(
|
|
|
1315
1315
|
"pull_request_from_fork pull_request_on_default_branch pull_request_ambiguous pull_request_repository_not_connected branch_claimed_elsewhere".split(" ")
|
|
1316
1316
|
);
|
|
1317
1317
|
async function tryClaimAndStart(state, item, queue) {
|
|
1318
|
-
const { postJson: postJson2 } = await import('./work-cmd-
|
|
1318
|
+
const { postJson: postJson2 } = await import('./work-cmd-EQSW5DTD.js');
|
|
1319
1319
|
const log = logOf(state);
|
|
1320
1320
|
const hostKey = await loadHostKey(state.config.configPath);
|
|
1321
1321
|
const claim = await retrySend(
|
|
@@ -2072,7 +2072,7 @@ async function start(configPath, deps = { fetchImpl: fetch }) {
|
|
|
2072
2072
|
const outcome = await onStartUnderMarker({
|
|
2073
2073
|
configPath,
|
|
2074
2074
|
own: ownVersionOf(deps),
|
|
2075
|
-
check: update.check ? () => update.check(configPath) : async () => (await import('./check-
|
|
2075
|
+
check: update.check ? () => update.check(configPath) : async () => (await import('./check-2JSSXCKJ.js')).runRunnerCheck(configPath, { fetchImpl: deps.fetchImpl }),
|
|
2076
2076
|
now: () => (deps.now ?? (() => /* @__PURE__ */ new Date()))().getTime(),
|
|
2077
2077
|
log
|
|
2078
2078
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
3
|
import { checkStarterFiles } from './chunk-MTF3PHVZ.js';
|
|
4
|
-
import { resolveCredentials, getJson, WorkCommandError } from './chunk-
|
|
4
|
+
import { resolveCredentials, getJson, WorkCommandError } from './chunk-JR6ERP3M.js';
|
|
5
5
|
import { readStatuslineState, STATE_FRESH_MS } from './chunk-F47JBIAW.js';
|
|
6
6
|
import { isLinkedRepository } from './chunk-YDSMROTB.js';
|
|
7
7
|
import { sanitizeTerminal } from './chunk-WUYTWNMA.js';
|
|
@@ -1095,7 +1095,7 @@ async function gatherDoctorChecks() {
|
|
|
1095
1095
|
const { runnerVmCheck } = await import('./doctor-53F2PYY7.js');
|
|
1096
1096
|
checks.push(runnerVmCheck(configPath, { lima: realLima(), home: homeFromEnv(), instance: runnerVm.instance }));
|
|
1097
1097
|
} else if (hasRunnerBlock(configPath)) {
|
|
1098
|
-
const { runnerHostChecks } = await import('./doctor-
|
|
1098
|
+
const { runnerHostChecks } = await import('./doctor-CORFPYY6.js');
|
|
1099
1099
|
const hooks = (() => {
|
|
1100
1100
|
try {
|
|
1101
1101
|
return loadRunnerConfig(configPath).hooks;
|
|
@@ -3,7 +3,7 @@ import { createRequire } from 'node:module';
|
|
|
3
3
|
import { createDockerHost, childEnvironment, spawnRun } from './chunk-KMDLFI4P.js';
|
|
4
4
|
import { execCapture } from './chunk-QKFB4K7X.js';
|
|
5
5
|
import { GUEST_CID } from './chunk-RSNSZ2LJ.js';
|
|
6
|
-
import { WorkCommandError, postJson } from './chunk-
|
|
6
|
+
import { WorkCommandError, postJson } from './chunk-JR6ERP3M.js';
|
|
7
7
|
import { hostAllowed } from './chunk-UXYLWVTQ.js';
|
|
8
8
|
import { readDefaultBranchHead, cloneRepository, buildRepositoryImage } from './chunk-R7PF5OOU.js';
|
|
9
9
|
import { RecipeError } from './chunk-QOZKPLVJ.js';
|
|
@@ -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-UUDDPNRJ.js';
|
|
4
4
|
import { firecrackerArch, MICROVM_ARCHES, guestAssetsPinned } from './chunk-CX22S5BA.js';
|
|
5
5
|
import { spawnSync } from 'child_process';
|
|
6
6
|
import { existsSync, accessSync, constants, statSync, readFileSync } from 'fs';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { gatherDoctorChecks, doctorExitCode, summarizeChecks, renderDoctorText, recordWrapped, recordUnwrapped, recordRemoval, readManifest, recordInstall, recordStatusline } from './chunk-
|
|
3
|
+
import { gatherDoctorChecks, doctorExitCode, summarizeChecks, renderDoctorText, recordWrapped, recordUnwrapped, recordRemoval, readManifest, recordInstall, recordStatusline } from './chunk-QLTYTPYU.js';
|
|
4
4
|
import './chunk-MTF3PHVZ.js';
|
|
5
|
-
import './chunk-
|
|
5
|
+
import './chunk-JR6ERP3M.js';
|
|
6
6
|
import './chunk-NPQWKGUA.js';
|
|
7
7
|
import './chunk-IP7BIBXJ.js';
|
|
8
8
|
import './chunk-F47JBIAW.js';
|
|
@@ -3872,7 +3872,7 @@ function buildProgram(argv = process.argv) {
|
|
|
3872
3872
|
else process.stdout.write(outcome.text);
|
|
3873
3873
|
return;
|
|
3874
3874
|
}
|
|
3875
|
-
const { runInitCommand } = await import('./init-cmd-
|
|
3875
|
+
const { runInitCommand } = await import('./init-cmd-JR3RKNFB.js');
|
|
3876
3876
|
const code = await runInitCommand(
|
|
3877
3877
|
{ json: opts.json, factory: opts.factory },
|
|
3878
3878
|
{
|
|
@@ -4289,7 +4289,7 @@ function registerWorkCommands(program2) {
|
|
|
4289
4289
|
"\n outerlayer work build --issue 42 # a host builds it\n outerlayer work build --item 7 # retry an item already on Work\n outerlayer work build --issue 42 --local # you build it\n outerlayer work build --issue ENG-123 --tracker linear # a Linear issue\n\nWithout --local, records a build request: a host takes the item when it\nhas a free slot. It never links the calling session and never takes a\nlease, even run from inside one.\n\nWith --local, puts the item on Work with no build request, so no host\ntakes it, and prints the line that starts your session on it. That\nsession links itself and holds the item's lease while it works.\n"
|
|
4290
4290
|
).action(async (opts) => {
|
|
4291
4291
|
warnIfApiKeyFlag(opts);
|
|
4292
|
-
const { runWorkBuild, WorkCommandError } = await import('./work-cmd-
|
|
4292
|
+
const { runWorkBuild, WorkCommandError } = await import('./work-cmd-EQSW5DTD.js');
|
|
4293
4293
|
try {
|
|
4294
4294
|
await runWorkBuild(opts);
|
|
4295
4295
|
} catch (err) {
|
|
@@ -4305,7 +4305,7 @@ function registerWorkCommands(program2) {
|
|
|
4305
4305
|
"\n outerlayer work link-session 42\n\nNames an item that already exists on the calling session \u2014 it never\ncreates one. Create one with `outerlayer work build --local`. Linking\nthe same session to the same item twice is a no-op. Also takes or\nrenews that session's own 20-minute lease, unless --no-lease or\n--ended is given, or another worker already holds a live one.\n"
|
|
4306
4306
|
).action(async (number, opts) => {
|
|
4307
4307
|
warnIfApiKeyFlag(opts);
|
|
4308
|
-
const { runWorkLinkSession, WorkCommandError } = await import('./work-cmd-
|
|
4308
|
+
const { runWorkLinkSession, WorkCommandError } = await import('./work-cmd-EQSW5DTD.js');
|
|
4309
4309
|
try {
|
|
4310
4310
|
await runWorkLinkSession({ ...opts, number, noLease: opts.lease === false, logFailures: opts.logFailures === true });
|
|
4311
4311
|
} catch (err) {
|
|
@@ -4330,8 +4330,8 @@ most one action flag may be given.
|
|
|
4330
4330
|
`
|
|
4331
4331
|
).action(async (opts) => {
|
|
4332
4332
|
warnIfApiKeyFlag(opts);
|
|
4333
|
-
const { runWorkComment, WorkCommentCommandError } = await import('./work-comment-cmd-
|
|
4334
|
-
const { WorkCommandError } = await import('./work-cmd-
|
|
4333
|
+
const { runWorkComment, WorkCommentCommandError } = await import('./work-comment-cmd-7VLKI7FN.js');
|
|
4334
|
+
const { WorkCommandError } = await import('./work-cmd-EQSW5DTD.js');
|
|
4335
4335
|
try {
|
|
4336
4336
|
await runWorkComment(opts);
|
|
4337
4337
|
} catch (err) {
|
|
@@ -4347,8 +4347,8 @@ most one action flag may be given.
|
|
|
4347
4347
|
"\n outerlayer work threads --item 7\n\nPrints the item's general thread, then one thread per acceptance\ncriterion that carries proof or a comment, each with who is waiting\non whom next.\n"
|
|
4348
4348
|
).action(async (opts) => {
|
|
4349
4349
|
warnIfApiKeyFlag(opts);
|
|
4350
|
-
const { runWorkThreads, WorkCommentCommandError } = await import('./work-comment-cmd-
|
|
4351
|
-
const { WorkCommandError } = await import('./work-cmd-
|
|
4350
|
+
const { runWorkThreads, WorkCommentCommandError } = await import('./work-comment-cmd-7VLKI7FN.js');
|
|
4351
|
+
const { WorkCommandError } = await import('./work-cmd-EQSW5DTD.js');
|
|
4352
4352
|
try {
|
|
4353
4353
|
await runWorkThreads(opts);
|
|
4354
4354
|
} catch (err) {
|
|
@@ -4370,7 +4370,7 @@ live addition remains on it.
|
|
|
4370
4370
|
`
|
|
4371
4371
|
).action(async (opts) => {
|
|
4372
4372
|
warnIfApiKeyFlag(opts);
|
|
4373
|
-
const { runWorkRemove, WorkCommandError } = await import('./work-cmd-
|
|
4373
|
+
const { runWorkRemove, WorkCommandError } = await import('./work-cmd-EQSW5DTD.js');
|
|
4374
4374
|
try {
|
|
4375
4375
|
await runWorkRemove(opts);
|
|
4376
4376
|
} catch (err) {
|
|
@@ -4383,7 +4383,7 @@ live addition remains on it.
|
|
|
4383
4383
|
});
|
|
4384
4384
|
workCmd.command("status").description("Show one item's stage, gate ledger, and additions").option("--item <number>", "the item's number inside this factory").option("--issue <number>", "issue number").option("--repo <owner/name>", "repository (default: the current checkout's remote)").option("--json", "machine-readable JSON output").option("--url <url>", "cloud base URL (or OUTERLAYER_URL / config)").addOption(apiKeyOption()).option("--app-id <id>", "factory id the key is bound to (or OUTERLAYER_APP_ID / config)").action(async (opts) => {
|
|
4385
4385
|
warnIfApiKeyFlag(opts);
|
|
4386
|
-
const { runWorkStatus, WorkCommandError } = await import('./work-cmd-
|
|
4386
|
+
const { runWorkStatus, WorkCommandError } = await import('./work-cmd-EQSW5DTD.js');
|
|
4387
4387
|
try {
|
|
4388
4388
|
await runWorkStatus(opts);
|
|
4389
4389
|
} catch (err) {
|
|
@@ -4396,7 +4396,7 @@ live addition remains on it.
|
|
|
4396
4396
|
});
|
|
4397
4397
|
workCmd.command("list").description("List live work items for this factory").option("--stage <spec|build|review>", "filter by stage").option("--section <attention|waiting|flowing>", "filter by Queue section").option("--repo <owner/name>", "repository (default: the current checkout's remote)").option("--unclaimed", "only items with no live lease").option("--claimed", "only items with a live lease, held by a host or a local session").option("--startable", "only items a host can start: a build request, no open PR, no live lease").option("--needs <amend>", "only items with a thread waiting on an agent").option("--json", "machine-readable JSON output").option("--url <url>", "cloud base URL (or OUTERLAYER_URL / config)").addOption(apiKeyOption()).option("--app-id <id>", "factory id the key is bound to (or OUTERLAYER_APP_ID / config)").action(async (opts) => {
|
|
4398
4398
|
warnIfApiKeyFlag(opts);
|
|
4399
|
-
const { runWorkList, WorkCommandError } = await import('./work-cmd-
|
|
4399
|
+
const { runWorkList, WorkCommandError } = await import('./work-cmd-EQSW5DTD.js');
|
|
4400
4400
|
try {
|
|
4401
4401
|
await runWorkList(opts);
|
|
4402
4402
|
} catch (err) {
|
|
@@ -4412,7 +4412,7 @@ live addition remains on it.
|
|
|
4412
4412
|
"\n outerlayer work claim --item 42 --kind implement\n\nRecords a lease held by the API key that sends it. A live lease held\nby a different key is refused; claiming again with the same key extends\nit. --host is a label only.\n"
|
|
4413
4413
|
).action(async (opts) => {
|
|
4414
4414
|
warnIfApiKeyFlag(opts);
|
|
4415
|
-
const { runWorkClaim, WorkCommandError } = await import('./work-claim-cmd-
|
|
4415
|
+
const { runWorkClaim, WorkCommandError } = await import('./work-claim-cmd-A6IS6CQ7.js');
|
|
4416
4416
|
try {
|
|
4417
4417
|
await runWorkClaim(opts);
|
|
4418
4418
|
} catch (err) {
|
|
@@ -4428,7 +4428,7 @@ live addition remains on it.
|
|
|
4428
4428
|
"\n outerlayer work renew --item 42\n\nExtends the live lease this API key holds. Refused with a 410 if the\nlease has expired, or a 403 if a different key holds it.\n"
|
|
4429
4429
|
).action(async (opts) => {
|
|
4430
4430
|
warnIfApiKeyFlag(opts);
|
|
4431
|
-
const { runWorkRenew, WorkCommandError } = await import('./work-claim-cmd-
|
|
4431
|
+
const { runWorkRenew, WorkCommandError } = await import('./work-claim-cmd-A6IS6CQ7.js');
|
|
4432
4432
|
try {
|
|
4433
4433
|
await runWorkRenew(opts);
|
|
4434
4434
|
} catch (err) {
|
|
@@ -4447,7 +4447,7 @@ live addition remains on it.
|
|
|
4447
4447
|
"\n outerlayer work release --item 42 --outcome ok\n outerlayer work release --item 42\n\nMarks the lease this API key holds released, optionally with an\noutcome. A lease held by a different key is refused. Releasing an\nalready-released lease is a no-op that returns the recorded release\ntime and outcome.\n\nRun inside a session with no --host, it releases that session's own\nlease instead, so the item's review opens without ending the\nsession. It never releases another session's lease. --outcome\napplies to a host's lease only. A later tool call in the session\ntakes the lease again.\n"
|
|
4448
4448
|
).action(async (opts) => {
|
|
4449
4449
|
warnIfApiKeyFlag(opts);
|
|
4450
|
-
const { runWorkRelease, WorkCommandError } = await import('./work-claim-cmd-
|
|
4450
|
+
const { runWorkRelease, WorkCommandError } = await import('./work-claim-cmd-A6IS6CQ7.js');
|
|
4451
4451
|
try {
|
|
4452
4452
|
await runWorkRelease(opts);
|
|
4453
4453
|
} catch (err) {
|
|
@@ -4479,8 +4479,8 @@ live addition remains on it.
|
|
|
4479
4479
|
'\n outerlayer work open-pr --title "Add the thing" --body-file pr.md\n outerlayer work open-pr --repository acme/web --title "Add the thing" --body-file pr.md\n\nIn a host build, which holds an item key, the gateway opens the pull\nrequest as the factory\'s GitHub App, from the branch the claim named,\nor edits the one already open on that branch. No gh command runs.\nIn your own session it runs `gh pr create` (or `gh pr edit`) under your\nlogin, then declares the pull request on the item, as `work pr` does.\nRun it again after a change to the title or description.\n'
|
|
4480
4480
|
).action(async (opts) => {
|
|
4481
4481
|
warnIfApiKeyFlag(opts);
|
|
4482
|
-
const { runWorkOpenPr, WorkOpenPrCommandError } = await import('./work-open-pr-cmd-
|
|
4483
|
-
const { WorkCommandError } = await import('./work-cmd-
|
|
4482
|
+
const { runWorkOpenPr, WorkOpenPrCommandError } = await import('./work-open-pr-cmd-JEVD4HMQ.js');
|
|
4483
|
+
const { WorkCommandError } = await import('./work-cmd-EQSW5DTD.js');
|
|
4484
4484
|
try {
|
|
4485
4485
|
await runWorkOpenPr(opts);
|
|
4486
4486
|
} catch (err) {
|
|
@@ -4497,7 +4497,7 @@ function registerRunnerCommands(program2) {
|
|
|
4497
4497
|
const configOption = "the cloud config file to run against (default: ~/.outerlayer/config.json)";
|
|
4498
4498
|
runnerCmd.command("start").description("Run the loop: claim work, run it, report, clean up, repeat").option("--config <path>", configOption).action(async (opts) => {
|
|
4499
4499
|
const { defaultRunnerConfigPath, RunnerConfigError } = await import('./config-N3C6DFQA.js');
|
|
4500
|
-
const { start, RunnerStartError } = await import('./loop-
|
|
4500
|
+
const { start, RunnerStartError } = await import('./loop-QNSM5GXS.js');
|
|
4501
4501
|
const { RunnerAlreadyRunningError } = await import('./pidfile-UZRH774M.js');
|
|
4502
4502
|
const configPath = opts.config ?? defaultRunnerConfigPath();
|
|
4503
4503
|
try {
|
|
@@ -4513,7 +4513,7 @@ function registerRunnerCommands(program2) {
|
|
|
4513
4513
|
});
|
|
4514
4514
|
runnerCmd.command("check").description("Validate the config, the hooks and the key, and print the effective settings").option("--config <path>", configOption).action(async (opts) => {
|
|
4515
4515
|
const { defaultRunnerConfigPath } = await import('./config-N3C6DFQA.js');
|
|
4516
|
-
const { runRunnerCheck } = await import('./check-
|
|
4516
|
+
const { runRunnerCheck } = await import('./check-2JSSXCKJ.js');
|
|
4517
4517
|
const configPath = opts.config ?? defaultRunnerConfigPath();
|
|
4518
4518
|
const result = await runRunnerCheck(configPath);
|
|
4519
4519
|
if (!result.ok) {
|
|
@@ -4542,7 +4542,7 @@ function registerRunnerCommands(program2) {
|
|
|
4542
4542
|
}
|
|
4543
4543
|
try {
|
|
4544
4544
|
if (opts.service === true) {
|
|
4545
|
-
const { installServiceCommand } = await import('./command-
|
|
4545
|
+
const { installServiceCommand } = await import('./command-ETUAEXP2.js');
|
|
4546
4546
|
const result2 = await installServiceCommand({
|
|
4547
4547
|
configPath,
|
|
4548
4548
|
print: opts.print,
|
|
@@ -4598,7 +4598,7 @@ To keep it running, run ${C.yellow}outerlayer runner install --service${C.reset}
|
|
|
4598
4598
|
return;
|
|
4599
4599
|
}
|
|
4600
4600
|
const { defaultRunnerConfigPath } = await import('./config-N3C6DFQA.js');
|
|
4601
|
-
const { uninstallServiceCommand } = await import('./command-
|
|
4601
|
+
const { uninstallServiceCommand } = await import('./command-ETUAEXP2.js');
|
|
4602
4602
|
const result = await uninstallServiceCommand({ configPath: opts.config ?? defaultRunnerConfigPath(), unitName: opts.unitName, home: homeFromEnv() });
|
|
4603
4603
|
if (result.ok) for (const line of result.lines) process.stdout.write(`${C.green}\u2713${C.reset} ${line}
|
|
4604
4604
|
`);
|
|
@@ -4609,10 +4609,10 @@ To keep it running, run ${C.yellow}outerlayer runner install --service${C.reset}
|
|
|
4609
4609
|
"\nWith no apiKey in the config file, and a login saved by `outerlayer login`,\nrunner init creates the runner key itself, with the Runner preset, on the\nfactory --app-id names (or OUTERLAYER_APP_ID, or this repository's\nconnection). Your login needs factory_keys.insert on that factory.\n\nWith --agent claude, the queue commands start Claude Code instead of the\nplaceholder, and Claude's credential is read from stdin: a token from\n`claude setup-token` (sk-ant-oat...) or an API key (sk-ant-api...). It is\nnever printed, and is stored in the runner.env beside the config with mode\n600. Other lines in the file are kept. With nothing piped, a credential\nalready in runner.env is used.\n\n printf '%s' \"$CLAUDE_CODE_OAUTH_TOKEN\" | outerlayer runner init --agent claude\n"
|
|
4610
4610
|
).action(async (opts) => {
|
|
4611
4611
|
const { defaultRunnerConfigPath, RunnerConfigError } = await import('./config-N3C6DFQA.js');
|
|
4612
|
-
const { prepareRunnerInit, runRunnerInit, RunnerInitError, writeRunnerInit } = await import('./init-
|
|
4613
|
-
const { createRunnerKey, runnerKeyLogin } = await import('./runner-key-
|
|
4614
|
-
const { detectMachine } = await import('./machine-
|
|
4615
|
-
const { chooseVm, runVmInit, VmInitError } = await import('./init-
|
|
4612
|
+
const { prepareRunnerInit, runRunnerInit, RunnerInitError, writeRunnerInit } = await import('./init-OPB7HHXO.js');
|
|
4613
|
+
const { createRunnerKey, runnerKeyLogin } = await import('./runner-key-7SSNMUBQ.js');
|
|
4614
|
+
const { detectMachine } = await import('./machine-IT6GG7FF.js');
|
|
4615
|
+
const { chooseVm, runVmInit, VmInitError } = await import('./init-MIC37V6J.js');
|
|
4616
4616
|
const { AGENT_COMMANDS, credentialFromStdin, isAgentName, restartForCredential, RunnerEnvError } = await import('./runner-env-XTC53CHN.js');
|
|
4617
4617
|
const configPath = opts.config ?? defaultRunnerConfigPath();
|
|
4618
4618
|
if (opts.agent !== void 0 && !isAgentName(opts.agent)) {
|
|
@@ -4762,7 +4762,7 @@ ${C.yellow}The runner will not update itself: ${installErr.message}${C.reset}
|
|
|
4762
4762
|
let serviceUp = false;
|
|
4763
4763
|
let restart;
|
|
4764
4764
|
if (installedLayout) {
|
|
4765
|
-
const { installServiceCommand } = await import('./command-
|
|
4765
|
+
const { installServiceCommand } = await import('./command-ETUAEXP2.js');
|
|
4766
4766
|
const service = await installServiceCommand({
|
|
4767
4767
|
configPath,
|
|
4768
4768
|
entryPath: process.argv[1] ?? "",
|
|
@@ -4873,15 +4873,15 @@ Next:
|
|
|
4873
4873
|
});
|
|
4874
4874
|
runnerCmd.command("stop").description("Take no more work; wait for running jobs to finish, then exit").option("--config <path>", "the cloud config file naming the runner to stop").option("--now", "end running jobs immediately, with outcome stopped").action(async (opts) => {
|
|
4875
4875
|
const { defaultRunnerConfigPath } = await import('./config-N3C6DFQA.js');
|
|
4876
|
-
const { requestStop } = await import('./loop-
|
|
4876
|
+
const { requestStop } = await import('./loop-QNSM5GXS.js');
|
|
4877
4877
|
const configPath = opts.config ?? defaultRunnerConfigPath();
|
|
4878
4878
|
await requestStop(configPath, Boolean(opts.now));
|
|
4879
4879
|
});
|
|
4880
4880
|
runnerCmd.command("status").description("List running jobs; with --recent, finished ones too").option("--config <path>", "the cloud config file naming the runner to inspect").option("--recent", "also list finished jobs, newest first, capped at 20").option("--json", "machine-readable JSON output").action(async (opts) => {
|
|
4881
4881
|
const { defaultRunnerConfigPath, loadRunnerConfig } = await import('./config-N3C6DFQA.js');
|
|
4882
4882
|
const { jobsDir, runnerDir } = await import('./paths-OYKMVYJP.js');
|
|
4883
|
-
const { listJobFiles, runnerProcessState } = await import('./loop-
|
|
4884
|
-
const { renderStatus } = await import('./status-
|
|
4883
|
+
const { listJobFiles, runnerProcessState } = await import('./loop-QNSM5GXS.js');
|
|
4884
|
+
const { renderStatus } = await import('./status-7ZX57OFJ.js');
|
|
4885
4885
|
const { readHousekeepingRecord } = await import('./record-C43U4PKS.js');
|
|
4886
4886
|
const path2 = await import('path');
|
|
4887
4887
|
const configPath = opts.config ?? defaultRunnerConfigPath();
|
|
@@ -4937,7 +4937,7 @@ Next:
|
|
|
4937
4937
|
});
|
|
4938
4938
|
runnerCmd.command("logs").argument("<item>", "the work item's number in the factory").description("Print the newest attempt's log for one item").option("--config <path>", "the cloud config file naming the runner to inspect").option("-f, --follow", "keep printing as the log grows").action(async (itemArg, opts) => {
|
|
4939
4939
|
const { defaultRunnerConfigPath } = await import('./config-N3C6DFQA.js');
|
|
4940
|
-
const { listJobFiles } = await import('./loop-
|
|
4940
|
+
const { listJobFiles } = await import('./loop-QNSM5GXS.js');
|
|
4941
4941
|
const { newestAttempt, logPathFor, tail, RunnerLogsError } = await import('./logs-H55NWW4H.js');
|
|
4942
4942
|
const configPath = opts.config ?? defaultRunnerConfigPath();
|
|
4943
4943
|
const item = parseTrackerNumber(itemArg);
|
|
@@ -5,13 +5,13 @@ import { runRunnerInstall, launcherSummary, restartNote } from './chunk-ERI5GBNI
|
|
|
5
5
|
import './chunk-I2UOAV7X.js';
|
|
6
6
|
import './chunk-PTW6W5NR.js';
|
|
7
7
|
import { currentLink, execStartCommand } from './chunk-TI7B7UEO.js';
|
|
8
|
-
import { detectMachine } from './chunk-
|
|
9
|
-
import './chunk-
|
|
8
|
+
import { detectMachine } from './chunk-Z3NOIFDR.js';
|
|
9
|
+
import './chunk-UUDDPNRJ.js';
|
|
10
10
|
import './chunk-KMDLFI4P.js';
|
|
11
11
|
import './chunk-QKFB4K7X.js';
|
|
12
12
|
import './chunk-RSNSZ2LJ.js';
|
|
13
13
|
import { installService, uninstallService, realTaskScheduler } from './chunk-RLYZLUQ7.js';
|
|
14
|
-
import './chunk-
|
|
14
|
+
import './chunk-JR6ERP3M.js';
|
|
15
15
|
import './chunk-NPQWKGUA.js';
|
|
16
16
|
import './chunk-IP7BIBXJ.js';
|
|
17
17
|
import { runnerVmOf, loadRunnerConfig } from './chunk-22RMZBGW.js';
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { needsClaudeCredential, claudeCredentialCheck, missingPermissionOf, missingPermissionFix } from './chunk-
|
|
3
|
+
import { needsClaudeCredential, claudeCredentialCheck, missingPermissionOf, missingPermissionFix } from './chunk-F7GBYX2P.js';
|
|
4
4
|
import { freeBytes, formatBytes } from './chunk-YYYRBYWG.js';
|
|
5
5
|
import { launcherCheck } from './chunk-PTW6W5NR.js';
|
|
6
6
|
import './chunk-TI7B7UEO.js';
|
|
7
7
|
import { RepoFilter } from './chunk-TBT347UY.js';
|
|
8
|
-
import './chunk-
|
|
9
|
-
import { detectMachine, supportsContainerBuilds, describeMachineDocker, reachDockerStep, supportsMicroVmBuilds, microVmArchSupported, microVmAssetsPinned } from './chunk-
|
|
10
|
-
import { MIN_ENGINE_MAJOR } from './chunk-
|
|
8
|
+
import './chunk-HXLMZOKU.js';
|
|
9
|
+
import { detectMachine, supportsContainerBuilds, describeMachineDocker, reachDockerStep, supportsMicroVmBuilds, microVmArchSupported, microVmAssetsPinned } from './chunk-Z3NOIFDR.js';
|
|
10
|
+
import { MIN_ENGINE_MAJOR } from './chunk-UUDDPNRJ.js';
|
|
11
11
|
import { resolveDevcontainerCli, spawnRun } from './chunk-KMDLFI4P.js';
|
|
12
12
|
import './chunk-QKFB4K7X.js';
|
|
13
13
|
import './chunk-RSNSZ2LJ.js';
|
|
14
14
|
import './chunk-T5VPP6H7.js';
|
|
15
15
|
import './chunk-RLYZLUQ7.js';
|
|
16
16
|
import './chunk-6MZSX4M7.js';
|
|
17
|
-
import { getJson, WorkCommandError, signatureRefusalMessage } from './chunk-
|
|
17
|
+
import { getJson, WorkCommandError, signatureRefusalMessage } from './chunk-JR6ERP3M.js';
|
|
18
18
|
import './chunk-NPQWKGUA.js';
|
|
19
19
|
import './chunk-IP7BIBXJ.js';
|
|
20
20
|
import { builtinKind } from './chunk-22RMZBGW.js';
|
package/dist/index.js
CHANGED
|
@@ -22,5 +22,5 @@ if (argv[0] === "statusline") {
|
|
|
22
22
|
process.exit(0);
|
|
23
23
|
}
|
|
24
24
|
(await import('./hook-fast-WYQD6D7T.js')).recordLinkExitFailure(argv);
|
|
25
|
-
var { runCli } = await import('./cli-
|
|
25
|
+
var { runCli } = await import('./cli-RGGXQTWI.js');
|
|
26
26
|
await runCli(process.argv);
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
3
|
import { npmSupportsProvenance } from './chunk-YETTWTAY.js';
|
|
4
|
-
import { RunnerInitError, readConfigObject } from './chunk-
|
|
5
|
-
import './chunk-
|
|
6
|
-
import './chunk-
|
|
4
|
+
import { RunnerInitError, readConfigObject } from './chunk-HXLMZOKU.js';
|
|
5
|
+
import './chunk-Z3NOIFDR.js';
|
|
6
|
+
import './chunk-UUDDPNRJ.js';
|
|
7
7
|
import './chunk-KMDLFI4P.js';
|
|
8
8
|
import './chunk-QKFB4K7X.js';
|
|
9
9
|
import './chunk-RSNSZ2LJ.js';
|
|
10
10
|
import './chunk-T5VPP6H7.js';
|
|
11
11
|
import { checkFit, VM_SERVICE_NAME, RUNNER_RESERVE_BYTES } from './chunk-RLYZLUQ7.js';
|
|
12
12
|
import './chunk-6MZSX4M7.js';
|
|
13
|
-
import './chunk-
|
|
13
|
+
import './chunk-JR6ERP3M.js';
|
|
14
14
|
import './chunk-NPQWKGUA.js';
|
|
15
15
|
import './chunk-IP7BIBXJ.js';
|
|
16
16
|
import { runnerCredentialProblem, resolveBuild, resolveHousekeeping } from './chunk-22RMZBGW.js';
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
export { PLACEHOLDER_MARKER, RunnerInitError, isPlaceholderCommand, prepareRunnerInit, readConfigObject, runRunnerInit, writeRunnerInit } from './chunk-
|
|
4
|
-
import './chunk-
|
|
5
|
-
import './chunk-
|
|
3
|
+
export { PLACEHOLDER_MARKER, RunnerInitError, isPlaceholderCommand, prepareRunnerInit, readConfigObject, runRunnerInit, writeRunnerInit } from './chunk-HXLMZOKU.js';
|
|
4
|
+
import './chunk-Z3NOIFDR.js';
|
|
5
|
+
import './chunk-UUDDPNRJ.js';
|
|
6
6
|
import './chunk-KMDLFI4P.js';
|
|
7
7
|
import './chunk-QKFB4K7X.js';
|
|
8
8
|
import './chunk-RSNSZ2LJ.js';
|
|
9
9
|
import './chunk-T5VPP6H7.js';
|
|
10
10
|
import './chunk-RLYZLUQ7.js';
|
|
11
11
|
import './chunk-6MZSX4M7.js';
|
|
12
|
-
import './chunk-
|
|
12
|
+
import './chunk-JR6ERP3M.js';
|
|
13
13
|
import './chunk-NPQWKGUA.js';
|
|
14
14
|
import './chunk-IP7BIBXJ.js';
|
|
15
15
|
import './chunk-22RMZBGW.js';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { summarizeChecks, summaryText, renderDoctorText } from './chunk-
|
|
3
|
+
import { summarizeChecks, summaryText, renderDoctorText } from './chunk-QLTYTPYU.js';
|
|
4
4
|
import './chunk-MTF3PHVZ.js';
|
|
5
|
-
import './chunk-
|
|
5
|
+
import './chunk-JR6ERP3M.js';
|
|
6
6
|
import './chunk-NPQWKGUA.js';
|
|
7
7
|
import './chunk-IP7BIBXJ.js';
|
|
8
8
|
import './chunk-F47JBIAW.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
export { RunnerStartError, listJobFiles, probeCredentials, requestStop, runnerProcessState, start, tick, watchTick } from './chunk-
|
|
4
|
-
import './chunk-
|
|
3
|
+
export { RunnerStartError, listJobFiles, probeCredentials, requestStop, runnerProcessState, start, tick, watchTick } from './chunk-NXUH742G.js';
|
|
4
|
+
import './chunk-ASOSIKA4.js';
|
|
5
5
|
import './chunk-O765JR4W.js';
|
|
6
6
|
import './chunk-UGP76IZ5.js';
|
|
7
7
|
import './chunk-3JVYMD4G.js';
|
|
@@ -9,24 +9,24 @@ import './chunk-YETTWTAY.js';
|
|
|
9
9
|
import './chunk-RMWLBUCJ.js';
|
|
10
10
|
import './chunk-I2UOAV7X.js';
|
|
11
11
|
import './chunk-HYZRY4KZ.js';
|
|
12
|
-
import './chunk-
|
|
12
|
+
import './chunk-ADJRGVZC.js';
|
|
13
13
|
import './chunk-KFRM23WD.js';
|
|
14
14
|
import './chunk-MSASO5O4.js';
|
|
15
15
|
import './chunk-HHJDN6AU.js';
|
|
16
|
-
import './chunk-
|
|
16
|
+
import './chunk-F7GBYX2P.js';
|
|
17
17
|
import './chunk-YYYRBYWG.js';
|
|
18
18
|
import './chunk-TI7B7UEO.js';
|
|
19
19
|
import './chunk-TBT347UY.js';
|
|
20
|
-
import './chunk-
|
|
21
|
-
import './chunk-
|
|
22
|
-
import './chunk-
|
|
20
|
+
import './chunk-HXLMZOKU.js';
|
|
21
|
+
import './chunk-Z3NOIFDR.js';
|
|
22
|
+
import './chunk-UUDDPNRJ.js';
|
|
23
23
|
import './chunk-KMDLFI4P.js';
|
|
24
24
|
import './chunk-QKFB4K7X.js';
|
|
25
25
|
import './chunk-RSNSZ2LJ.js';
|
|
26
26
|
import './chunk-T5VPP6H7.js';
|
|
27
27
|
import './chunk-RLYZLUQ7.js';
|
|
28
28
|
import './chunk-6MZSX4M7.js';
|
|
29
|
-
import './chunk-
|
|
29
|
+
import './chunk-JR6ERP3M.js';
|
|
30
30
|
import './chunk-NPQWKGUA.js';
|
|
31
31
|
import './chunk-IP7BIBXJ.js';
|
|
32
32
|
import './chunk-22RMZBGW.js';
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
export { describeMachineDocker, detectMachine, microVmArchSupported, microVmAssetsPinned, reachDockerStep, supportsContainerBuilds, supportsMicroVmBuilds } from './chunk-
|
|
4
|
-
import './chunk-
|
|
3
|
+
export { describeMachineDocker, detectMachine, microVmArchSupported, microVmAssetsPinned, reachDockerStep, supportsContainerBuilds, supportsMicroVmBuilds } from './chunk-Z3NOIFDR.js';
|
|
4
|
+
import './chunk-UUDDPNRJ.js';
|
|
5
5
|
import './chunk-KMDLFI4P.js';
|
|
6
6
|
import './chunk-QKFB4K7X.js';
|
|
7
7
|
import './chunk-RSNSZ2LJ.js';
|
|
8
|
-
import './chunk-
|
|
8
|
+
import './chunk-JR6ERP3M.js';
|
|
9
9
|
import './chunk-NPQWKGUA.js';
|
|
10
10
|
import './chunk-IP7BIBXJ.js';
|
|
11
11
|
import './chunk-UXYLWVTQ.js';
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { readConfigObject, RunnerInitError } from './chunk-
|
|
4
|
-
import './chunk-
|
|
5
|
-
import './chunk-
|
|
3
|
+
import { readConfigObject, RunnerInitError } from './chunk-HXLMZOKU.js';
|
|
4
|
+
import './chunk-Z3NOIFDR.js';
|
|
5
|
+
import './chunk-UUDDPNRJ.js';
|
|
6
6
|
import './chunk-KMDLFI4P.js';
|
|
7
7
|
import './chunk-QKFB4K7X.js';
|
|
8
8
|
import './chunk-RSNSZ2LJ.js';
|
|
9
9
|
import './chunk-T5VPP6H7.js';
|
|
10
10
|
import './chunk-RLYZLUQ7.js';
|
|
11
11
|
import './chunk-6MZSX4M7.js';
|
|
12
|
-
import { postJson, WorkCommandError, getJson } from './chunk-
|
|
12
|
+
import { postJson, WorkCommandError, getJson } from './chunk-JR6ERP3M.js';
|
|
13
13
|
import './chunk-NPQWKGUA.js';
|
|
14
14
|
import './chunk-IP7BIBXJ.js';
|
|
15
15
|
import { runnerCredentialProblem } from './chunk-22RMZBGW.js';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { LEASE_SECONDS, ALREADY_RELEASED, RENEW_INTERVAL_SECONDS } from './chunk-
|
|
3
|
+
import { LEASE_SECONDS, ALREADY_RELEASED, RENEW_INTERVAL_SECONDS } from './chunk-ASOSIKA4.js';
|
|
4
4
|
import './chunk-RMWLBUCJ.js';
|
|
5
5
|
import './chunk-HYZRY4KZ.js';
|
|
6
6
|
import './chunk-KFRM23WD.js';
|
|
@@ -8,11 +8,11 @@ import './chunk-MSASO5O4.js';
|
|
|
8
8
|
import './chunk-HHJDN6AU.js';
|
|
9
9
|
import { formatBytes } from './chunk-YYYRBYWG.js';
|
|
10
10
|
import './chunk-TBT347UY.js';
|
|
11
|
-
import { formatDuration, formatClock, relativeToRunner } from './chunk-
|
|
11
|
+
import { formatDuration, formatClock, relativeToRunner } from './chunk-UUDDPNRJ.js';
|
|
12
12
|
import './chunk-KMDLFI4P.js';
|
|
13
13
|
import './chunk-QKFB4K7X.js';
|
|
14
14
|
import './chunk-RSNSZ2LJ.js';
|
|
15
|
-
import './chunk-
|
|
15
|
+
import './chunk-JR6ERP3M.js';
|
|
16
16
|
import './chunk-NPQWKGUA.js';
|
|
17
17
|
import './chunk-IP7BIBXJ.js';
|
|
18
18
|
import './chunk-22RMZBGW.js';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { RUNNER_PROTOCOL } from './chunk-
|
|
4
|
-
import { resolveCredentials, WorkCommandError, parsePositiveInt, postJson, environmentFacts, resolveRepository, findLiveItem } from './chunk-
|
|
5
|
-
export { WorkCommandError } from './chunk-
|
|
3
|
+
import { RUNNER_PROTOCOL } from './chunk-ADJRGVZC.js';
|
|
4
|
+
import { resolveCredentials, WorkCommandError, parsePositiveInt, postJson, environmentFacts, resolveRepository, findLiveItem } from './chunk-JR6ERP3M.js';
|
|
5
|
+
export { WorkCommandError } from './chunk-JR6ERP3M.js';
|
|
6
6
|
import './chunk-NPQWKGUA.js';
|
|
7
7
|
import './chunk-IP7BIBXJ.js';
|
|
8
8
|
import { defaultRunnerConfigPath } from './chunk-22RMZBGW.js';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
export { WorkCommandError, environmentFacts, findLiveItem, getJson, parsePositiveInt, postJson, resolveCredentials, resolveRepository, runWorkBuild, runWorkLinkSession, runWorkList, runWorkRemove, runWorkStatus } from './chunk-
|
|
3
|
+
export { WorkCommandError, environmentFacts, findLiveItem, getJson, parsePositiveInt, postJson, resolveCredentials, resolveRepository, runWorkBuild, runWorkLinkSession, runWorkList, runWorkRemove, runWorkStatus } from './chunk-JR6ERP3M.js';
|
|
4
4
|
import './chunk-NPQWKGUA.js';
|
|
5
5
|
import './chunk-IP7BIBXJ.js';
|
|
6
6
|
import './chunk-YDSMROTB.js';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { resolveCredentials, postJson, getJson } from './chunk-
|
|
3
|
+
import { resolveCredentials, postJson, getJson } from './chunk-JR6ERP3M.js';
|
|
4
4
|
import './chunk-NPQWKGUA.js';
|
|
5
5
|
import { detectActiveSession } from './chunk-IP7BIBXJ.js';
|
|
6
6
|
import './chunk-YDSMROTB.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
3
|
import { itemLabel, runWorkPr, WorkPrCommandError } from './chunk-JJEGKEFV.js';
|
|
4
|
-
import { resolveCredentials } from './chunk-
|
|
4
|
+
import { resolveCredentials } from './chunk-JR6ERP3M.js';
|
|
5
5
|
import './chunk-NPQWKGUA.js';
|
|
6
6
|
import './chunk-IP7BIBXJ.js';
|
|
7
7
|
import './chunk-YDSMROTB.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@outerlayer/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"description": "OuterLayer CLI — commands: init, connect, doctor, login, logout, sync, daemon (alias: watch), emit, work, runner, hooks, mcp, import, context. Capture and sync coding-agent sessions, and record proof of factory work.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Magu Studios, Inc.",
|
package/skill-pack/shipped.json
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"5cf9bd75fb1f984f6e6b470d1f1581dc06168abf062448e6f43711fed8f6632a",
|
|
10
10
|
"6177ea457be0d238659640e6d29807708d60d68533ae3ff93af10fb513139ced",
|
|
11
11
|
"77cab8b446fb1aa865710f7cb8356c5334301a942f58f9243766bf1acf1377ad",
|
|
12
|
+
"93953397c31d47a6f1c06edb8ae2190e1d8a3fca20c616deb33991311cee39a8",
|
|
12
13
|
"a6743050c5c387e962ad4264526d9a3c10edf36823e4b6829f200bf79a4e45e7",
|
|
13
14
|
"b0aa9b6962675f772b67a4390c15b6b0100390983d958c656a826a066399f3d0",
|
|
14
15
|
"f4e1661c6f87d7ff6d23c6182530fa0a9cb87c4dc3f31e48f2be11f32ebb2c00"
|
|
@@ -27,6 +28,7 @@
|
|
|
27
28
|
".outerlayer/skills/build/references/release.md": [
|
|
28
29
|
"0b0af40c82473cf192f697537ed5a84801a14e79bb11790d72226d7f7f49cca0",
|
|
29
30
|
"1113259bdf7d8c28edcbdb9b4c83c2f2bd4c5eaf817cf30bf428478ff91694e9",
|
|
31
|
+
"1f4fe97ca8f11d6681db5734f3103af5ea175a9472ff1d38b10cb463f4956a6a",
|
|
30
32
|
"4687f56bcec766f1926d58a208418f800a1727c073b5e6047b40e3aaac2bd6c9",
|
|
31
33
|
"81a764eb317fcb4672bedd35e7b9aa68d1c0b73f82fc2dfa03e5a78758db8f14",
|
|
32
34
|
"985e02b98105b00c356014e003b2d60e9c458c3dcbeef7378274ccb84af031fc"
|
|
@@ -40,6 +42,9 @@
|
|
|
40
42
|
"6f93e2ae74d3c02a0d4c14a6b2c805f523d234a8c56789f40a7c7c59700ef277",
|
|
41
43
|
"9d4d2e5e0307ea8c38cf885cd67d06a939b7fc2bed65f5080ca9610178949d74"
|
|
42
44
|
],
|
|
45
|
+
".outerlayer/skills/build/scripts/criterion-citations.mjs": [
|
|
46
|
+
"0902118ec216182f9d0ba0f6c7966306ad6891d8e846022123d3f86aaf175fce"
|
|
47
|
+
],
|
|
43
48
|
".outerlayer/skills/build/scripts/record-criteria.mjs": [
|
|
44
49
|
"36f19e730bc220fa21622b3bb0a306999f038b8220a0a07a1efe21ec0f748729",
|
|
45
50
|
"596e9a78e62cbea040a3bf428cd54bf59687e9e89001fad719304f88d03ca2ea",
|
|
@@ -215,8 +215,9 @@ Run them in order.
|
|
|
215
215
|
which links it, and a local session with `gh pr create` and then
|
|
216
216
|
`outerlayer work pr <number>`. When the build recorded criteria, from the issue or drafted, a fresh agent judges them with
|
|
217
217
|
[references/criteria-judge.md](references/criteria-judge.md), and the
|
|
218
|
-
judge's HTML report is emitted:
|
|
218
|
+
judge's HTML report is linked to its tests and emitted:
|
|
219
219
|
|
|
220
|
+
node .outerlayer/skills/build/scripts/criterion-citations.mjs --report <judge-report>.html
|
|
220
221
|
outerlayer emit artifact <judge-report>.html --for acceptance-criteria --caption "Each criterion judged against its test"
|
|
221
222
|
|
|
222
223
|
Its verdict is recorded on the `acceptance-criteria` check, as release.md
|
|
@@ -68,8 +68,11 @@ and a report path under `build-reports/`. Above forty criteria, spawn one
|
|
|
68
68
|
judge per group of about forty in the same wave, each with its own report
|
|
69
69
|
path, and merge their results by concatenation into one report.
|
|
70
70
|
|
|
71
|
-
The judge writes an HTML report with one verdict per criterion.
|
|
71
|
+
The judge writes an HTML report with one verdict per criterion. Link each
|
|
72
|
+
criterion to the tests that cite it, which the Criteria tab lists. With several
|
|
73
|
+
judges, run it on the merged report. Then emit it:
|
|
72
74
|
|
|
75
|
+
node .outerlayer/skills/build/scripts/criterion-citations.mjs --report <judge-report>.html
|
|
73
76
|
outerlayer emit artifact <judge-report>.html --for acceptance-criteria --caption "Each criterion judged against the test that cites it"
|
|
74
77
|
|
|
75
78
|
Then run `outerlayer sync`. List every unsatisfied criterion in the final
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Links each recorded criterion to the tests that cite it, by writing a
|
|
3
|
+
// `criterion-citations` JSON block into the criteria judge's HTML report. The
|
|
4
|
+
// work item's Criteria tab reads that block to list a criterion's tests. Run
|
|
5
|
+
// it on the report just before emitting it. Needs only Node and git.
|
|
6
|
+
//
|
|
7
|
+
// node criterion-citations.mjs --report <path> [--criteria-file <path>]
|
|
8
|
+
//
|
|
9
|
+
// The criteria come from the file `record-criteria.mjs` wrote, by default
|
|
10
|
+
// `<git dir>/build-reports/criteria.json`. A test cites a criterion when the
|
|
11
|
+
// criterion's id appears as a whole token in a test's title or in a comment
|
|
12
|
+
// above it. Only git-tracked test files are scanned, so every link names code
|
|
13
|
+
// that a commit contains. The link names the HEAD commit and is written only
|
|
14
|
+
// when the origin remote is on GitHub; otherwise the test is listed unlinked.
|
|
15
|
+
// A criterion no test cites gets an empty list. An existing block in the
|
|
16
|
+
// report is replaced.
|
|
17
|
+
import { spawnSync } from "node:child_process";
|
|
18
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
19
|
+
import { join } from "node:path";
|
|
20
|
+
import { pathToFileURL } from "node:url";
|
|
21
|
+
|
|
22
|
+
/** A tracked file that holds tests: JS and TS `.test`/`.spec`, Python `test_*.py`/`*_test.py`, Go `*_test.go`, Ruby `*_spec.rb`. */
|
|
23
|
+
export const TEST_FILE = /(?:\.(?:test|spec)\.[cm]?[jt]sx?|(?:^|\/)test_[^/]*\.py|_test\.(?:py|go)|_spec\.rb)$/;
|
|
24
|
+
/** A line that is only a comment: `//`, `#`, `*`, `/*`, `--` or `<!--` first. */
|
|
25
|
+
const COMMENT_LINE = /^\s*(?:\/\/|#|\*|\/\*|--|<!--)/;
|
|
26
|
+
const BLOCK_ID = /<script type="application\/json" id="criterion-citations">[\s\S]*?<\/script>/;
|
|
27
|
+
|
|
28
|
+
/** A criteria file the agent must fix before anything is written. */
|
|
29
|
+
export class CitationInputError extends Error {}
|
|
30
|
+
|
|
31
|
+
function escapeRegExp(text) {
|
|
32
|
+
return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** A pattern that finds `id` as a whole token: not inside a longer id such as `WC-10` for `WC-1`, and still found before a sentence's full stop. */
|
|
36
|
+
export function idPattern(id) {
|
|
37
|
+
return new RegExp(`(?<![A-Za-z0-9_:-])${escapeRegExp(id)}(?![A-Za-z0-9_:-]|\\.[A-Za-z0-9])`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** The ids of a criteria file shaped `{ "criteria": [{ "id" }] }`. */
|
|
41
|
+
export function criterionIds(input) {
|
|
42
|
+
if (typeof input !== "object" || input === null || !Array.isArray(input.criteria)) {
|
|
43
|
+
throw new CitationInputError('the criteria file must be { "criteria": [ ... ] }');
|
|
44
|
+
}
|
|
45
|
+
return input.criteria.map((entry, n) => {
|
|
46
|
+
if (typeof entry !== "object" || entry === null || typeof entry.id !== "string" || entry.id === "") {
|
|
47
|
+
throw new CitationInputError(`criteria[${n}] has no id`);
|
|
48
|
+
}
|
|
49
|
+
return entry.id;
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The 1-based lines of one file's tests that cite `id`. A citation in a
|
|
55
|
+
* comment belongs to the first line after the comment that is not blank or
|
|
56
|
+
* another comment line (the test call); a citation anywhere else, such as a
|
|
57
|
+
* title, belongs to its own line. A comment with nothing after it keeps its
|
|
58
|
+
* own line.
|
|
59
|
+
*/
|
|
60
|
+
export function citingLines(contents, id) {
|
|
61
|
+
const pattern = idPattern(id);
|
|
62
|
+
const lines = contents.split("\n");
|
|
63
|
+
const found = new Set();
|
|
64
|
+
lines.forEach((text, index) => {
|
|
65
|
+
if (!pattern.test(text)) return;
|
|
66
|
+
if (!COMMENT_LINE.test(text)) {
|
|
67
|
+
found.add(index + 1);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
let next = index + 1;
|
|
71
|
+
while (next < lines.length && (lines[next].trim() === "" || COMMENT_LINE.test(lines[next]))) next += 1;
|
|
72
|
+
found.add(next < lines.length ? next + 1 : index + 1);
|
|
73
|
+
});
|
|
74
|
+
return [...found];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** `owner/repo` for a github.com remote in ssh or https form, else `null`. */
|
|
78
|
+
export function parseGithubRemote(remoteUrl) {
|
|
79
|
+
const trimmed = (remoteUrl ?? "").trim();
|
|
80
|
+
const match =
|
|
81
|
+
/^git@github\.com:([^/]+)\/(.+?)(?:\.git)?\/?$/.exec(trimmed) ??
|
|
82
|
+
/^(?:https?|ssh):\/\/(?:[^@/]+@)?(?:www\.)?github\.com(?::\d+)?\/([^/]+)\/(.+?)(?:\.git)?\/?$/.exec(trimmed);
|
|
83
|
+
return match ? `${match[1]}/${match[2]}` : null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** A link to `path` at line `line` of commit `sha`, or `null` when there is no GitHub repository to link to. */
|
|
87
|
+
export function testHref(repository, sha, path, line) {
|
|
88
|
+
if (!repository || !sha) return null;
|
|
89
|
+
const encoded = path.split("/").map(encodeURIComponent).join("/");
|
|
90
|
+
return `https://github.com/${repository}/blob/${sha}/${encoded}#L${line}`;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Each criterion's citing tests, as `{ [id]: [{ path, line, href }] }` in
|
|
95
|
+
* the order the ids are given. `files` is `[{ path, contents }]` for the
|
|
96
|
+
* tracked test files; tests sort by path then line.
|
|
97
|
+
*/
|
|
98
|
+
export function buildCitations({ ids, files, repository = null, sha = null }) {
|
|
99
|
+
const sorted = [...files].sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
|
|
100
|
+
const result = {};
|
|
101
|
+
for (const id of ids) {
|
|
102
|
+
result[id] = sorted.flatMap(({ path, contents }) =>
|
|
103
|
+
citingLines(contents, id).map((line) => ({ path, line, href: testHref(repository, sha, path, line) })),
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** The block as HTML. `<` is escaped so no cited path or title can end the script element early. */
|
|
110
|
+
export function renderBlock(citations) {
|
|
111
|
+
const json = JSON.stringify(citations).replace(/</g, "\\u003c");
|
|
112
|
+
return `<script type="application/json" id="criterion-citations">${json}</script>`;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** `html` with the block set: an existing one replaced, else added before `</body>`, else appended. */
|
|
116
|
+
export function withBlock(html, citations) {
|
|
117
|
+
const block = renderBlock(citations);
|
|
118
|
+
if (BLOCK_ID.test(html)) return html.replace(BLOCK_ID, () => block);
|
|
119
|
+
const at = html.lastIndexOf("</body>");
|
|
120
|
+
return at === -1 ? `${html}\n${block}\n` : `${html.slice(0, at)}${block}\n${html.slice(at)}`;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function git(cwd, args) {
|
|
124
|
+
const run = spawnSync("git", args, { cwd, encoding: "utf8", maxBuffer: 64 * 1024 * 1024 });
|
|
125
|
+
return run.status === 0 ? run.stdout : null;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Reads the repository's tracked test files and its GitHub identity. `null` outside a git checkout. */
|
|
129
|
+
function readRepository(cwd) {
|
|
130
|
+
const top = git(cwd, ["rev-parse", "--show-toplevel"])?.trim();
|
|
131
|
+
const tracked = top ? git(top, ["ls-files", "-z"]) : null;
|
|
132
|
+
if (!top || tracked === null) return null;
|
|
133
|
+
const files = [];
|
|
134
|
+
for (const path of tracked.split("\0").filter((p) => p !== "" && TEST_FILE.test(p))) {
|
|
135
|
+
try {
|
|
136
|
+
files.push({ path, contents: readFileSync(join(top, path), "utf8") });
|
|
137
|
+
} catch {
|
|
138
|
+
// A tracked file deleted from the working tree has no tests to read.
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
const sha = git(top, ["rev-parse", "HEAD"])?.trim() || null;
|
|
142
|
+
const repository = parseGithubRemote(git(top, ["remote", "get-url", "origin"]) ?? "");
|
|
143
|
+
return { files, sha, repository };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function main(argv) {
|
|
147
|
+
const arg = (name) => {
|
|
148
|
+
const at = argv.indexOf(name);
|
|
149
|
+
return at === -1 ? undefined : argv[at + 1];
|
|
150
|
+
};
|
|
151
|
+
const report = arg("--report");
|
|
152
|
+
if (!report) {
|
|
153
|
+
process.stderr.write("usage: criterion-citations.mjs --report <path> [--criteria-file <path>]\n");
|
|
154
|
+
return 2;
|
|
155
|
+
}
|
|
156
|
+
const repo = readRepository(process.cwd());
|
|
157
|
+
if (!repo) {
|
|
158
|
+
process.stderr.write("not inside a git repository\n");
|
|
159
|
+
return 2;
|
|
160
|
+
}
|
|
161
|
+
let criteriaFile = arg("--criteria-file");
|
|
162
|
+
if (!criteriaFile) {
|
|
163
|
+
const gitDir = git(process.cwd(), ["rev-parse", "--absolute-git-dir"])?.trim();
|
|
164
|
+
criteriaFile = join(gitDir ?? "", "build-reports", "criteria.json");
|
|
165
|
+
}
|
|
166
|
+
let ids;
|
|
167
|
+
try {
|
|
168
|
+
ids = criterionIds(JSON.parse(readFileSync(criteriaFile, "utf8")));
|
|
169
|
+
} catch (err) {
|
|
170
|
+
process.stderr.write(`cannot read the criteria from "${criteriaFile}": ${err.message}\n`);
|
|
171
|
+
return 2;
|
|
172
|
+
}
|
|
173
|
+
if (!existsSync(report)) {
|
|
174
|
+
process.stderr.write(`the report "${report}" does not exist\n`);
|
|
175
|
+
return 2;
|
|
176
|
+
}
|
|
177
|
+
const citations = buildCitations({ ids, files: repo.files, repository: repo.repository, sha: repo.sha });
|
|
178
|
+
writeFileSync(report, withBlock(readFileSync(report, "utf8"), citations));
|
|
179
|
+
const cited = ids.filter((id) => citations[id].length > 0).length;
|
|
180
|
+
process.stdout.write(`linked ${cited} of ${ids.length} criteria to the tests that cite them in ${report}\n`);
|
|
181
|
+
return 0;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) process.exitCode = main(process.argv.slice(2));
|