@naxodev/apnea 0.1.0 → 0.2.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/README.md +8 -10
- package/dist/cli.js +552 -530
- package/docs/adr/0005-harness-profiles.md +1 -1
- package/docs/adr/0010-package-split.md +1 -1
- package/docs/protocol/config.md +7 -22
- package/docs/protocol/manual-gate.md +1 -1
- package/docs/protocol/overview.md +1 -1
- package/extension/domain/herdr.ts +0 -86
- package/extension/domain/paths.ts +1 -2
- package/extension/domain/setup.ts +0 -20
- package/extension/domain/types.ts +0 -9
- package/extension/domain/verify-commands.ts +200 -108
- package/extension/errors.ts +1 -1
- package/extension/schema/config.ts +31 -17
- package/extension/schema/state.ts +16 -3
- package/extension/services/herdr.ts +5 -161
- package/extension/services/vcs.ts +393 -21
- package/extension/workflows/commit.ts +8 -5
- package/extension/workflows/dispatch.ts +60 -177
- package/extension/workflows/setup.ts +2 -109
- package/extension/workflows/start.ts +0 -1
- package/extension/workflows/wait.ts +1 -57
- package/package.json +1 -2
- package/schemas/config.schema.json +6 -6
- package/schemas/state.schema.json +5 -1
- package/herdr-plugin/herdr-plugin.toml +0 -15
- package/herdr-plugin/scripts/run-task.sh +0 -8
package/dist/cli.js
CHANGED
|
@@ -35989,13 +35989,11 @@ var ProfileSchema = exports_Schema.Struct({
|
|
|
35989
35989
|
var RoleBindingSchema = exports_Schema.Struct({
|
|
35990
35990
|
profile: exports_Schema.String.check(exports_Schema.isMinLength(1))
|
|
35991
35991
|
});
|
|
35992
|
-
var PaneStyleSchema = exports_Schema.Literals(["regular", "floating"]);
|
|
35993
35992
|
var GlobalConfigSchema = exports_Schema.Struct({
|
|
35994
35993
|
profiles: exports_Schema.optional(exports_Schema.Record(exports_Schema.String, ProfileSchema)),
|
|
35995
35994
|
roles: exports_Schema.optional(exports_Schema.Record(exports_Schema.String, RoleBindingSchema)),
|
|
35996
35995
|
review_round_cap: exports_Schema.optional(exports_Schema.Number),
|
|
35997
|
-
timeouts_ms: exports_Schema.optional(exports_Schema.Record(exports_Schema.String, exports_Schema.Number))
|
|
35998
|
-
pane_style: exports_Schema.optional(PaneStyleSchema)
|
|
35996
|
+
timeouts_ms: exports_Schema.optional(exports_Schema.Record(exports_Schema.String, exports_Schema.Number))
|
|
35999
35997
|
});
|
|
36000
35998
|
var PROJECT_KNOWN = new Set([
|
|
36001
35999
|
"roles",
|
|
@@ -36015,8 +36013,7 @@ var ProjectConfigSchema = exports_Schema.Struct({
|
|
|
36015
36013
|
roles: exports_Schema.optional(exports_Schema.Record(exports_Schema.String, RoleBindingSchema)),
|
|
36016
36014
|
review_round_cap: exports_Schema.optional(exports_Schema.Number),
|
|
36017
36015
|
timeouts_ms: exports_Schema.optional(exports_Schema.Record(exports_Schema.String, exports_Schema.Number)),
|
|
36018
|
-
isolation: exports_Schema.optional(exports_Schema.Literal("shared_cwd"))
|
|
36019
|
-
pane_style: exports_Schema.optional(PaneStyleSchema)
|
|
36016
|
+
isolation: exports_Schema.optional(exports_Schema.Literal("shared_cwd"))
|
|
36020
36017
|
});
|
|
36021
36018
|
function configFail(message, path2) {
|
|
36022
36019
|
return exports_Result.fail(path2 !== undefined ? new ConfigError({ message, path: path2 }) : new ConfigError({ message }));
|
|
@@ -36032,6 +36029,9 @@ function decodeGlobalConfig(raw) {
|
|
|
36032
36029
|
if (exports_Result.isFailure(objR))
|
|
36033
36030
|
return configFail(objR.failure.message);
|
|
36034
36031
|
const obj = objR.success;
|
|
36032
|
+
if ("pane_style" in obj && obj.pane_style !== "regular" && obj.pane_style !== "floating") {
|
|
36033
|
+
return configFail(`invalid legacy pane_style=${JSON.stringify(obj.pane_style)}; expected "regular" or "floating"`);
|
|
36034
|
+
}
|
|
36035
36035
|
if ("isolation" in obj && obj.isolation !== undefined && obj.isolation !== "shared_cwd") {
|
|
36036
36036
|
return configFail(`unimplemented config value isolation=${JSON.stringify(obj.isolation)} (v1 only supports shared_cwd or omit)`);
|
|
36037
36037
|
}
|
|
@@ -36047,7 +36047,8 @@ function decodeGlobalConfig(raw) {
|
|
|
36047
36047
|
}
|
|
36048
36048
|
}
|
|
36049
36049
|
}
|
|
36050
|
-
const
|
|
36050
|
+
const { pane_style: _legacy, ...globalConfig } = obj;
|
|
36051
|
+
const decoded = exports_Schema.decodeUnknownResult(GlobalConfigSchema)(globalConfig);
|
|
36051
36052
|
if (exports_Result.isFailure(decoded)) {
|
|
36052
36053
|
return configFail(decoded.failure.message);
|
|
36053
36054
|
}
|
|
@@ -36075,13 +36076,11 @@ function decodeGlobalConfig(raw) {
|
|
|
36075
36076
|
timeouts[k] = v;
|
|
36076
36077
|
}
|
|
36077
36078
|
}
|
|
36078
|
-
const pane_style = d.pane_style === "regular" || d.pane_style === "floating" ? d.pane_style : "regular";
|
|
36079
36079
|
return exports_Result.succeed({
|
|
36080
36080
|
profiles,
|
|
36081
36081
|
roles,
|
|
36082
36082
|
review_round_cap: typeof d.review_round_cap === "number" && d.review_round_cap >= 1 ? d.review_round_cap : 3,
|
|
36083
|
-
timeouts_ms: timeouts
|
|
36084
|
-
pane_style
|
|
36083
|
+
timeouts_ms: timeouts
|
|
36085
36084
|
});
|
|
36086
36085
|
}
|
|
36087
36086
|
function decodeProjectConfig(raw) {
|
|
@@ -36092,6 +36091,9 @@ function decodeProjectConfig(raw) {
|
|
|
36092
36091
|
if (exports_Result.isFailure(objR))
|
|
36093
36092
|
return configFail(objR.failure.message);
|
|
36094
36093
|
const obj = objR.success;
|
|
36094
|
+
if ("pane_style" in obj && obj.pane_style !== "regular" && obj.pane_style !== "floating") {
|
|
36095
|
+
return configFail(`invalid legacy pane_style=${JSON.stringify(obj.pane_style)}; expected "regular" or "floating"`);
|
|
36096
|
+
}
|
|
36095
36097
|
for (const key of Object.keys(obj)) {
|
|
36096
36098
|
if (PROJECT_FORBIDDEN.has(key)) {
|
|
36097
36099
|
return configFail(`project config must not set ${key} (binaries/profiles only allowed in global config)`);
|
|
@@ -36116,7 +36118,8 @@ function decodeProjectConfig(raw) {
|
|
|
36116
36118
|
}
|
|
36117
36119
|
}
|
|
36118
36120
|
}
|
|
36119
|
-
const
|
|
36121
|
+
const { pane_style: _legacy, ...projectConfig } = obj;
|
|
36122
|
+
const decoded = exports_Schema.decodeUnknownResult(ProjectConfigSchema)(projectConfig, {
|
|
36120
36123
|
onExcessProperty: "error"
|
|
36121
36124
|
});
|
|
36122
36125
|
if (exports_Result.isFailure(decoded)) {
|
|
@@ -36142,8 +36145,7 @@ function applyProjectConfig(cfg, overlay) {
|
|
|
36142
36145
|
profiles: cfg.profiles,
|
|
36143
36146
|
roles,
|
|
36144
36147
|
review_round_cap: overlay.review_round_cap !== undefined && overlay.review_round_cap >= 1 ? overlay.review_round_cap : cfg.review_round_cap,
|
|
36145
|
-
timeouts_ms: timeouts
|
|
36146
|
-
pane_style: overlay.pane_style !== undefined ? overlay.pane_style : cfg.pane_style
|
|
36148
|
+
timeouts_ms: timeouts
|
|
36147
36149
|
};
|
|
36148
36150
|
}
|
|
36149
36151
|
function resolveRoleCmdResult(cfg, role, mode = ROLE_MODE[role]) {
|
|
@@ -36353,37 +36355,9 @@ var ConfigLive = exports_Layer.effect(Config, exports_Effect.gen(function* () {
|
|
|
36353
36355
|
// extension/services/herdr.ts
|
|
36354
36356
|
import { spawnSync } from "child_process";
|
|
36355
36357
|
import * as fs3 from "fs";
|
|
36356
|
-
import * as os2 from "os";
|
|
36357
36358
|
import * as path3 from "path";
|
|
36358
36359
|
|
|
36359
36360
|
// extension/domain/herdr.ts
|
|
36360
|
-
function parseHerdrVersion(raw) {
|
|
36361
|
-
const m = raw.match(/(\d+)\.(\d+)\.(\d+)/);
|
|
36362
|
-
if (!m)
|
|
36363
|
-
return null;
|
|
36364
|
-
return [Number(m[1]), Number(m[2]), Number(m[3])];
|
|
36365
|
-
}
|
|
36366
|
-
function versionGte(a, b) {
|
|
36367
|
-
const [a0, a1, a2] = a;
|
|
36368
|
-
const [b0, b1, b2] = b;
|
|
36369
|
-
if (a0 !== b0)
|
|
36370
|
-
return a0 > b0;
|
|
36371
|
-
if (a1 !== b1)
|
|
36372
|
-
return a1 > b1;
|
|
36373
|
-
return a2 >= b2;
|
|
36374
|
-
}
|
|
36375
|
-
function supportsFloating(version2) {
|
|
36376
|
-
return version2 != null && versionGte(version2, [0, 7, 4]);
|
|
36377
|
-
}
|
|
36378
|
-
function effectivePaneStyle(configured, role) {
|
|
36379
|
-
if (configured === "regular") {
|
|
36380
|
-
return { style: "regular", effective: "regular" };
|
|
36381
|
-
}
|
|
36382
|
-
if (role === "planner" || role === "reviewer") {
|
|
36383
|
-
return { style: "floating", effective: "floating" };
|
|
36384
|
-
}
|
|
36385
|
-
return { style: "regular", effective: "regular (interactive role)" };
|
|
36386
|
-
}
|
|
36387
36361
|
function shellJoin(parts) {
|
|
36388
36362
|
return parts.map((p) => {
|
|
36389
36363
|
if (p === "&&" || p === "|" || p === "exec" || p === "env")
|
|
@@ -36401,31 +36375,6 @@ function looksLikeShellOnly(names2) {
|
|
|
36401
36375
|
return /^(zsh|bash|sh|fish)$/i.test(t) || t === "-zsh" || t === "-bash" || t === "-sh";
|
|
36402
36376
|
});
|
|
36403
36377
|
}
|
|
36404
|
-
function parseFloatingExit(text) {
|
|
36405
|
-
const t = text.trim();
|
|
36406
|
-
const n = Number.parseInt(t, 10);
|
|
36407
|
-
return Number.isFinite(n) ? n : null;
|
|
36408
|
-
}
|
|
36409
|
-
function floatingTaskScriptBody(opts) {
|
|
36410
|
-
return [
|
|
36411
|
-
"#!/bin/bash",
|
|
36412
|
-
"set -uo pipefail",
|
|
36413
|
-
`EXIT_FILE=${shellJoin([opts.exitFileAbs])}`,
|
|
36414
|
-
"write_exit() {",
|
|
36415
|
-
" local st=$?",
|
|
36416
|
-
` printf '%s
|
|
36417
|
-
' "$st" > "$EXIT_FILE" 2>/dev/null || true`,
|
|
36418
|
-
"}",
|
|
36419
|
-
"trap write_exit EXIT",
|
|
36420
|
-
"trap 'exit 129' HUP",
|
|
36421
|
-
"trap 'exit 130' INT",
|
|
36422
|
-
"trap 'exit 143' TERM",
|
|
36423
|
-
shellJoin(["cd", opts.root]),
|
|
36424
|
-
shellJoin([...opts.resolvedCmd, "--", opts.prompt]),
|
|
36425
|
-
""
|
|
36426
|
-
].join(`
|
|
36427
|
-
`);
|
|
36428
|
-
}
|
|
36429
36378
|
|
|
36430
36379
|
// extension/services/herdr.ts
|
|
36431
36380
|
class Herdr extends exports_Context.Service()("apnea/Herdr") {
|
|
@@ -36489,27 +36438,6 @@ function resolveExecutable(bin, envPath = process.env.PATH) {
|
|
|
36489
36438
|
}
|
|
36490
36439
|
return null;
|
|
36491
36440
|
}
|
|
36492
|
-
function floatingPanePath(base2 = process.env.PATH ?? "", home = os2.homedir()) {
|
|
36493
|
-
const extras = [
|
|
36494
|
-
path3.join(home, ".local", "bin"),
|
|
36495
|
-
path3.join(home, ".bun", "bin"),
|
|
36496
|
-
"/opt/homebrew/bin",
|
|
36497
|
-
"/usr/local/bin"
|
|
36498
|
-
];
|
|
36499
|
-
const parts = base2.split(path3.delimiter).filter(Boolean);
|
|
36500
|
-
const seen = new Set(parts);
|
|
36501
|
-
for (const extra of extras) {
|
|
36502
|
-
if (seen.has(extra))
|
|
36503
|
-
continue;
|
|
36504
|
-
try {
|
|
36505
|
-
if (fs3.statSync(extra).isDirectory()) {
|
|
36506
|
-
parts.push(extra);
|
|
36507
|
-
seen.add(extra);
|
|
36508
|
-
}
|
|
36509
|
-
} catch {}
|
|
36510
|
-
}
|
|
36511
|
-
return parts.join(path3.delimiter);
|
|
36512
|
-
}
|
|
36513
36441
|
function herdrEnabledSync() {
|
|
36514
36442
|
return process.env.HERDR_ENV === "1";
|
|
36515
36443
|
}
|
|
@@ -36628,21 +36556,6 @@ function paneSendKeysSync(paneId, keys4) {
|
|
|
36628
36556
|
throw new HerdrError({ message: `herdr pane send-keys failed: ${r.raw}` });
|
|
36629
36557
|
}
|
|
36630
36558
|
}
|
|
36631
|
-
function herdrVersionSync() {
|
|
36632
|
-
return parseHerdrVersion(herdrCli(["--version"]).raw);
|
|
36633
|
-
}
|
|
36634
|
-
function hasApneaPluginSync() {
|
|
36635
|
-
const r = herdrCli(["plugin", "list", "--plugin", "apnea", "--json"]);
|
|
36636
|
-
const json2 = r.json;
|
|
36637
|
-
if (json2) {
|
|
36638
|
-
const res = resultOf(json2);
|
|
36639
|
-
const plugins = res?.plugins ?? [];
|
|
36640
|
-
if (plugins.some((p) => p.plugin_id === "apnea" || p.id === "apnea")) {
|
|
36641
|
-
return true;
|
|
36642
|
-
}
|
|
36643
|
-
}
|
|
36644
|
-
return /"(?:plugin_id|id)"\s*:\s*"apnea"/.test(r.raw);
|
|
36645
|
-
}
|
|
36646
36559
|
function paneForegroundNamesSync(paneId) {
|
|
36647
36560
|
try {
|
|
36648
36561
|
const r = spawnSync("herdr", ["pane", "process-info", "--pane", paneId], {
|
|
@@ -36900,8 +36813,6 @@ var makeHerdrLive = (hostAdapter) => exports_Layer.effect(Herdr, exports_Effect.
|
|
|
36900
36813
|
try: herdrAvailabilitySync,
|
|
36901
36814
|
catch: toHerdrError
|
|
36902
36815
|
}),
|
|
36903
|
-
version: exports_Effect.sync(herdrVersionSync),
|
|
36904
|
-
hasApneaPlugin: exports_Effect.sync(hasApneaPluginSync),
|
|
36905
36816
|
paneGet: (paneId) => exports_Effect.sync(() => paneGetSync(paneId)),
|
|
36906
36817
|
paneRun,
|
|
36907
36818
|
paneReadRecent: (paneId) => exports_Effect.try({
|
|
@@ -36909,73 +36820,7 @@ var makeHerdrLive = (hostAdapter) => exports_Layer.effect(Herdr, exports_Effect.
|
|
|
36909
36820
|
catch: toHerdrError
|
|
36910
36821
|
}),
|
|
36911
36822
|
paneForegroundNames: (paneId) => exports_Effect.sync(() => paneForegroundNamesSync(paneId)),
|
|
36912
|
-
runInteractivePrompt: (...args2) => runInteractivePromptImpl(hostAdapter, ...args2)
|
|
36913
|
-
writeFloatingTaskScript: (scriptAbs, root, cmd, prompt, exitFileAbs) => exports_Effect.try({
|
|
36914
|
-
try: () => {
|
|
36915
|
-
if (cmd.length === 0) {
|
|
36916
|
-
throw new HerdrError({
|
|
36917
|
-
message: "floating oneshot cmd is empty; set cmd_oneshot on the role profile"
|
|
36918
|
-
});
|
|
36919
|
-
}
|
|
36920
|
-
const bin = cmd[0];
|
|
36921
|
-
if (bin === undefined || bin === "") {
|
|
36922
|
-
throw new HerdrError({
|
|
36923
|
-
message: "floating oneshot binary is empty; set cmd_oneshot on the role profile"
|
|
36924
|
-
});
|
|
36925
|
-
}
|
|
36926
|
-
const resolved = resolveExecutable(bin);
|
|
36927
|
-
if (!resolved) {
|
|
36928
|
-
throw new HerdrError({
|
|
36929
|
-
message: `floating oneshot binary "${bin}" not found on PATH; use an absolute cmd_oneshot or set pane_style=regular`
|
|
36930
|
-
});
|
|
36931
|
-
}
|
|
36932
|
-
const resolvedCmd = [resolved, ...cmd.slice(1)];
|
|
36933
|
-
const body = floatingTaskScriptBody({
|
|
36934
|
-
root,
|
|
36935
|
-
resolvedCmd,
|
|
36936
|
-
prompt,
|
|
36937
|
-
exitFileAbs
|
|
36938
|
-
});
|
|
36939
|
-
fs3.writeFileSync(scriptAbs, body, "utf8");
|
|
36940
|
-
fs3.chmodSync(scriptAbs, 493);
|
|
36941
|
-
},
|
|
36942
|
-
catch: toHerdrError
|
|
36943
|
-
}),
|
|
36944
|
-
openFloatingPane: (taskScriptAbs, _root) => exports_Effect.try({
|
|
36945
|
-
try: () => {
|
|
36946
|
-
const r = herdrCli([
|
|
36947
|
-
"plugin",
|
|
36948
|
-
"pane",
|
|
36949
|
-
"open",
|
|
36950
|
-
"--plugin",
|
|
36951
|
-
"apnea",
|
|
36952
|
-
"--entrypoint",
|
|
36953
|
-
"worker",
|
|
36954
|
-
"--placement",
|
|
36955
|
-
"popup",
|
|
36956
|
-
"--env",
|
|
36957
|
-
`APNEA_TASK_SCRIPT=${taskScriptAbs}`,
|
|
36958
|
-
"--env",
|
|
36959
|
-
`PATH=${floatingPanePath()}`
|
|
36960
|
-
]);
|
|
36961
|
-
if (!r.ok) {
|
|
36962
|
-
const raw = r.raw.trim();
|
|
36963
|
-
if (/popup already open/i.test(raw)) {
|
|
36964
|
-
throw new HerdrError({
|
|
36965
|
-
message: "floating popup already open \u2014 herdr allows only one; dismiss it or workflow_wait for the in-flight oneshot before dispatching again"
|
|
36966
|
-
});
|
|
36967
|
-
}
|
|
36968
|
-
throw new HerdrError({
|
|
36969
|
-
message: `herdr plugin pane open failed: ${raw || r.raw}`
|
|
36970
|
-
});
|
|
36971
|
-
}
|
|
36972
|
-
},
|
|
36973
|
-
catch: toHerdrError
|
|
36974
|
-
}),
|
|
36975
|
-
linkPlugin: (dir) => exports_Effect.sync(() => {
|
|
36976
|
-
const r = herdrCli(["plugin", "link", dir]);
|
|
36977
|
-
return { ok: r.ok, raw: r.raw };
|
|
36978
|
-
})
|
|
36823
|
+
runInteractivePrompt: (...args2) => runInteractivePromptImpl(hostAdapter, ...args2)
|
|
36979
36824
|
})));
|
|
36980
36825
|
var HerdrLive = makeHerdrLive(neutralHostAdapter);
|
|
36981
36826
|
|
|
@@ -37020,7 +36865,6 @@ var RunStateSchema = exports_Schema.Struct({
|
|
|
37020
36865
|
pending_role: exports_Schema.NullOr(RoleSchema),
|
|
37021
36866
|
pending_pane_id: exports_Schema.optionalKey(exports_Schema.NullOr(exports_Schema.String)),
|
|
37022
36867
|
pending_pane_label: exports_Schema.optionalKey(exports_Schema.NullOr(exports_Schema.String)),
|
|
37023
|
-
pending_floating_exit: exports_Schema.optionalKey(exports_Schema.NullOr(exports_Schema.String)),
|
|
37024
36868
|
pending_started_at: exports_Schema.optionalKey(exports_Schema.NullOr(exports_Schema.Number)),
|
|
37025
36869
|
pending_deadline_ms: exports_Schema.optionalKey(exports_Schema.NullOr(exports_Schema.Number)),
|
|
37026
36870
|
pending_nudged_at: exports_Schema.optionalKey(exports_Schema.NullOr(exports_Schema.Number)),
|
|
@@ -37034,6 +36878,12 @@ var RunStateSchema = exports_Schema.Struct({
|
|
|
37034
36878
|
phase_package_rework: exports_Schema.optionalKey(exports_Schema.Boolean)
|
|
37035
36879
|
});
|
|
37036
36880
|
function decodeRunState(json2, path4 = "state.json") {
|
|
36881
|
+
if (json2 !== null && typeof json2 === "object" && !Array.isArray(json2) && "pending_floating_exit" in json2 && json2.pending_floating_exit !== null) {
|
|
36882
|
+
return exports_Result.fail(new StateCorrupt({
|
|
36883
|
+
path: path4,
|
|
36884
|
+
message: 'this run has an active legacy floating dispatch, but floating dispatch was removed; dismiss or terminate the old popup first, then run `apnea abandon` and `apnea start "<goal>"`'
|
|
36885
|
+
}));
|
|
36886
|
+
}
|
|
37037
36887
|
const decoded = exports_Schema.decodeUnknownResult(RunStateSchema)(json2);
|
|
37038
36888
|
if (exports_Result.isFailure(decoded)) {
|
|
37039
36889
|
return exports_Result.fail(new StateCorrupt({
|
|
@@ -37057,7 +36907,6 @@ function decodeRunState(json2, path4 = "state.json") {
|
|
|
37057
36907
|
pending_role: d.pending_role,
|
|
37058
36908
|
pending_pane_id: d.pending_pane_id ?? null,
|
|
37059
36909
|
pending_pane_label: d.pending_pane_label ?? null,
|
|
37060
|
-
pending_floating_exit: d.pending_floating_exit ?? null,
|
|
37061
36910
|
pending_started_at: d.pending_started_at ?? null,
|
|
37062
36911
|
pending_deadline_ms: d.pending_deadline_ms ?? null,
|
|
37063
36912
|
pending_nudged_at: d.pending_nudged_at ?? null,
|
|
@@ -37140,8 +36989,161 @@ var RunStoreLive = exports_Layer.effect(RunStore, exports_Effect.gen(function* (
|
|
|
37140
36989
|
}));
|
|
37141
36990
|
|
|
37142
36991
|
// extension/services/vcs.ts
|
|
37143
|
-
import { spawnSync as spawnSync2 } from "child_process";
|
|
36992
|
+
import { spawn, spawnSync as spawnSync2 } from "child_process";
|
|
36993
|
+
import { mkdtempSync, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
36994
|
+
import { tmpdir } from "os";
|
|
37144
36995
|
import * as path5 from "path";
|
|
36996
|
+
|
|
36997
|
+
// extension/domain/verify-commands.ts
|
|
36998
|
+
function normalizeVerifySource(source) {
|
|
36999
|
+
return source.replace(/\r\n?/g, `
|
|
37000
|
+
`);
|
|
37001
|
+
}
|
|
37002
|
+
function atxHeading(line) {
|
|
37003
|
+
const match8 = /^ {0,3}(#{1,6})(?:[\t ]+(.*?)|[\t ]*)$/.exec(line);
|
|
37004
|
+
if (!match8)
|
|
37005
|
+
return null;
|
|
37006
|
+
return {
|
|
37007
|
+
rank: match8[1].length,
|
|
37008
|
+
text: (match8[2] ?? "").replace(/[\t ]+#+[\t ]*$/, "").trim()
|
|
37009
|
+
};
|
|
37010
|
+
}
|
|
37011
|
+
function boldHeading(line) {
|
|
37012
|
+
return /^ {0,3}\*\*(.+?)\*\*[\t ]*$/.exec(line)?.[1]?.trim() ?? null;
|
|
37013
|
+
}
|
|
37014
|
+
function scanMarkdown(text) {
|
|
37015
|
+
const fences = [];
|
|
37016
|
+
const headings = [];
|
|
37017
|
+
let open;
|
|
37018
|
+
let offset = 0;
|
|
37019
|
+
while (offset < text.length) {
|
|
37020
|
+
const newline = text.indexOf(`
|
|
37021
|
+
`, offset);
|
|
37022
|
+
const lineEnd = newline === -1 ? text.length : newline;
|
|
37023
|
+
const next = newline === -1 ? text.length : newline + 1;
|
|
37024
|
+
const line = text.slice(offset, lineEnd);
|
|
37025
|
+
if (open) {
|
|
37026
|
+
const closing = /^ {0,3}(`{3,}|~{3,})[\t ]*$/.exec(line)?.[1];
|
|
37027
|
+
if (closing?.[0] === open.marker && closing.length >= open.length) {
|
|
37028
|
+
fences.push({
|
|
37029
|
+
start: open.start,
|
|
37030
|
+
bodyStart: open.bodyStart,
|
|
37031
|
+
bodyEnd: offset,
|
|
37032
|
+
info: open.info,
|
|
37033
|
+
indent: open.indent
|
|
37034
|
+
});
|
|
37035
|
+
open = undefined;
|
|
37036
|
+
}
|
|
37037
|
+
} else {
|
|
37038
|
+
const opening = /^( {0,3})(`{3,}|~{3,})(.*)$/.exec(line);
|
|
37039
|
+
const marker = opening?.[2];
|
|
37040
|
+
const info = opening?.[3] ?? "";
|
|
37041
|
+
if (marker && !(marker[0] === "`" && info.includes("`"))) {
|
|
37042
|
+
open = {
|
|
37043
|
+
marker: marker[0],
|
|
37044
|
+
length: marker.length,
|
|
37045
|
+
start: offset,
|
|
37046
|
+
bodyStart: next,
|
|
37047
|
+
info: info.trim(),
|
|
37048
|
+
indent: opening[1].length
|
|
37049
|
+
};
|
|
37050
|
+
} else {
|
|
37051
|
+
const atx = atxHeading(line);
|
|
37052
|
+
const bold = boldHeading(line);
|
|
37053
|
+
if (atx) {
|
|
37054
|
+
headings.push({
|
|
37055
|
+
start: offset,
|
|
37056
|
+
end: next,
|
|
37057
|
+
rank: atx.rank,
|
|
37058
|
+
text: atx.text
|
|
37059
|
+
});
|
|
37060
|
+
} else if (bold !== null) {
|
|
37061
|
+
headings.push({ start: offset, end: next, rank: null, text: bold });
|
|
37062
|
+
}
|
|
37063
|
+
}
|
|
37064
|
+
}
|
|
37065
|
+
offset = next;
|
|
37066
|
+
}
|
|
37067
|
+
return { fences, headings, unclosedFence: open !== undefined };
|
|
37068
|
+
}
|
|
37069
|
+
function toVerifyBlock(text, fence) {
|
|
37070
|
+
const language = fence.info.toLowerCase();
|
|
37071
|
+
if (language !== "bash" && language !== "sh" && language !== "shell") {
|
|
37072
|
+
return null;
|
|
37073
|
+
}
|
|
37074
|
+
const body = text.slice(fence.bodyStart, fence.bodyEnd);
|
|
37075
|
+
const source = fence.indent === 0 ? body : body.replace(new RegExp(`^ {0,${fence.indent}}`, "gm"), "");
|
|
37076
|
+
const hasExecutableLine = source.split(`
|
|
37077
|
+
`).some((line) => line.trim() !== "" && !line.trimStart().startsWith("#"));
|
|
37078
|
+
if (!hasExecutableLine)
|
|
37079
|
+
return null;
|
|
37080
|
+
return {
|
|
37081
|
+
interpreter: language === "sh" ? "sh" : "bash",
|
|
37082
|
+
source
|
|
37083
|
+
};
|
|
37084
|
+
}
|
|
37085
|
+
function isShellFence(fence) {
|
|
37086
|
+
return /^(?:bash|sh|shell)$/i.test(fence.info);
|
|
37087
|
+
}
|
|
37088
|
+
function endsInContinuation(line) {
|
|
37089
|
+
const run2 = /(\\+)$/.exec(line);
|
|
37090
|
+
return run2 !== null && run2[1].length % 2 === 1;
|
|
37091
|
+
}
|
|
37092
|
+
function extractLegacyBlocks(text) {
|
|
37093
|
+
const blocks = [];
|
|
37094
|
+
const rawLines = text.split(`
|
|
37095
|
+
`);
|
|
37096
|
+
for (let i = 0;i < rawLines.length; i++) {
|
|
37097
|
+
const match8 = rawLines[i].match(/^\s*(?:\$\s+)?((?:test |node |npm |bun |bunx |chmod |head ).+)$/);
|
|
37098
|
+
if (!match8)
|
|
37099
|
+
continue;
|
|
37100
|
+
let source = match8[1];
|
|
37101
|
+
while (endsInContinuation(source) && i + 1 < rawLines.length) {
|
|
37102
|
+
source = source.slice(0, -1) + rawLines[++i];
|
|
37103
|
+
}
|
|
37104
|
+
if (endsInContinuation(source))
|
|
37105
|
+
source = source.slice(0, -1);
|
|
37106
|
+
blocks.push({ interpreter: "bash", source: source.trim() });
|
|
37107
|
+
}
|
|
37108
|
+
return blocks;
|
|
37109
|
+
}
|
|
37110
|
+
function extractVerifyBlocks(phasePackageText) {
|
|
37111
|
+
const text = normalizeVerifySource(phasePackageText);
|
|
37112
|
+
const { fences, headings, unclosedFence } = scanMarkdown(text);
|
|
37113
|
+
if (unclosedFence)
|
|
37114
|
+
return [];
|
|
37115
|
+
const verifyHeading = headings.find((heading) => heading.text.toLowerCase() === "verify commands");
|
|
37116
|
+
if (verifyHeading) {
|
|
37117
|
+
const nextHeading = headings.find((heading) => heading.start >= verifyHeading.end && (heading.rank === null || verifyHeading.rank === null || heading.rank !== null && heading.rank <= verifyHeading.rank));
|
|
37118
|
+
const sectionEnd = nextHeading?.start ?? text.length;
|
|
37119
|
+
const sectionFences = fences.filter((fence) => fence.start >= verifyHeading.end && fence.start < sectionEnd);
|
|
37120
|
+
const blocks = sectionFences.filter(isShellFence).map((fence) => toVerifyBlock(text, fence)).filter((block) => block !== null);
|
|
37121
|
+
return sectionFences.length > 0 ? blocks : extractLegacyBlocks(text.slice(verifyHeading.end, sectionEnd));
|
|
37122
|
+
}
|
|
37123
|
+
const shellFences = fences.filter(isShellFence);
|
|
37124
|
+
if (shellFences.length > 0) {
|
|
37125
|
+
const block = toVerifyBlock(text, shellFences[shellFences.length - 1]);
|
|
37126
|
+
return block ? [block] : [];
|
|
37127
|
+
}
|
|
37128
|
+
return fences.length === 0 ? extractLegacyBlocks(text) : [];
|
|
37129
|
+
}
|
|
37130
|
+
function formatVerifyBlock(block) {
|
|
37131
|
+
const source = normalizeVerifySource(block.source);
|
|
37132
|
+
const body = source.endsWith(`
|
|
37133
|
+
`) ? source.slice(0, -1) : source;
|
|
37134
|
+
const displayedSource = body.split(`
|
|
37135
|
+
`).map((line) => `| ${line}`).join(`
|
|
37136
|
+
`);
|
|
37137
|
+
return `${block.interpreter} -e [verification block]
|
|
37138
|
+
${displayedSource}`;
|
|
37139
|
+
}
|
|
37140
|
+
function formatVerifyCommand(block) {
|
|
37141
|
+
const source = normalizeVerifySource(block.source);
|
|
37142
|
+
const quotedSource = `'${source.replaceAll("'", `'"'"'`)}'`;
|
|
37143
|
+
return `${block.interpreter} -e -c ${quotedSource}`;
|
|
37144
|
+
}
|
|
37145
|
+
|
|
37146
|
+
// extension/services/vcs.ts
|
|
37145
37147
|
class Vcs extends exports_Context.Service()("apnea/Vcs") {
|
|
37146
37148
|
}
|
|
37147
37149
|
function run2(cmd, args2, cwd2) {
|
|
@@ -37157,6 +37159,248 @@ function run2(cmd, args2, cwd2) {
|
|
|
37157
37159
|
code: r.status ?? 1
|
|
37158
37160
|
};
|
|
37159
37161
|
}
|
|
37162
|
+
function verificationError(error2, temporaryDirectory) {
|
|
37163
|
+
const message = error2 instanceof Error ? `${error2.name}: ${error2.message}` : String(error2);
|
|
37164
|
+
return temporaryDirectory ? message.replaceAll(temporaryDirectory, "[temporary verification directory]") : message;
|
|
37165
|
+
}
|
|
37166
|
+
var VERIFY_LOG_LIMIT = 10 * 1024 * 1024;
|
|
37167
|
+
var VERIFY_KILL_CLOSE_GRACE_MS = 2500;
|
|
37168
|
+
var VERIFY_RESULT_RESERVE = 2048;
|
|
37169
|
+
var VERIFY_WRAPPER_SOURCE = `exec 2>&1
|
|
37170
|
+
exec "$1" -e "$2"
|
|
37171
|
+
`;
|
|
37172
|
+
var VERIFY_DISPLAY_LIMIT_NOTICE = `verification log limit of ${VERIFY_LOG_LIMIT} bytes would be exceeded by the verification block display; block was not executed`;
|
|
37173
|
+
var VERIFY_LOG_LIMIT_NOTICE = `verification log limit of ${VERIFY_LOG_LIMIT} bytes reached; output was truncated and verification stopped`;
|
|
37174
|
+
var VERIFY_LIMIT_NOTICE_RESERVE = 1 + Math.max(Buffer.byteLength(VERIFY_DISPLAY_LIMIT_NOTICE), Buffer.byteLength(VERIFY_LOG_LIMIT_NOTICE));
|
|
37175
|
+
function utf8BytesAfterAppend(usedBytes, limitBytes, text) {
|
|
37176
|
+
const nextBytes = usedBytes + Buffer.byteLength(text);
|
|
37177
|
+
return nextBytes <= limitBytes ? nextBytes : null;
|
|
37178
|
+
}
|
|
37179
|
+
|
|
37180
|
+
class VerificationLog {
|
|
37181
|
+
limit;
|
|
37182
|
+
#chunks = [];
|
|
37183
|
+
#contentLimit;
|
|
37184
|
+
#bytes = 0;
|
|
37185
|
+
#limited = false;
|
|
37186
|
+
constructor(limit) {
|
|
37187
|
+
this.limit = limit;
|
|
37188
|
+
this.#contentLimit = Math.max(0, limit - VERIFY_LIMIT_NOTICE_RESERVE);
|
|
37189
|
+
}
|
|
37190
|
+
get remaining() {
|
|
37191
|
+
return this.#contentLimit - this.#bytes;
|
|
37192
|
+
}
|
|
37193
|
+
canAppendBytes(bytes) {
|
|
37194
|
+
return bytes <= this.remaining;
|
|
37195
|
+
}
|
|
37196
|
+
append(text) {
|
|
37197
|
+
const nextBytes = utf8BytesAfterAppend(this.#bytes, this.#contentLimit, text);
|
|
37198
|
+
if (nextBytes === null)
|
|
37199
|
+
return false;
|
|
37200
|
+
this.#chunks.push(text);
|
|
37201
|
+
this.#bytes = nextBytes;
|
|
37202
|
+
return true;
|
|
37203
|
+
}
|
|
37204
|
+
addLimitNotice(notice) {
|
|
37205
|
+
if (this.#limited)
|
|
37206
|
+
return;
|
|
37207
|
+
this.#limited = true;
|
|
37208
|
+
const previous = this.#chunks.at(-1);
|
|
37209
|
+
if (this.#bytes > 0 && !previous?.endsWith(`
|
|
37210
|
+
`)) {
|
|
37211
|
+
this.#chunks.push(`
|
|
37212
|
+
`);
|
|
37213
|
+
this.#bytes += 1;
|
|
37214
|
+
}
|
|
37215
|
+
this.#chunks.push(notice);
|
|
37216
|
+
this.#bytes += Buffer.byteLength(notice);
|
|
37217
|
+
}
|
|
37218
|
+
toString() {
|
|
37219
|
+
return this.#chunks.join("").trimEnd();
|
|
37220
|
+
}
|
|
37221
|
+
}
|
|
37222
|
+
function verifyBlockDisplayByteLength(block) {
|
|
37223
|
+
const source = block.source;
|
|
37224
|
+
const bodyEnd = source.endsWith(`
|
|
37225
|
+
`) ? source.length - 1 : source.length;
|
|
37226
|
+
let lineCount = 1;
|
|
37227
|
+
for (let index2 = 0;index2 < bodyEnd; index2++) {
|
|
37228
|
+
if (source.charCodeAt(index2) === 10)
|
|
37229
|
+
lineCount += 1;
|
|
37230
|
+
}
|
|
37231
|
+
const bodyBytes = Buffer.byteLength(source) - (bodyEnd < source.length ? 1 : 0);
|
|
37232
|
+
return Buffer.byteLength(`${block.interpreter} -e [verification block]
|
|
37233
|
+
`) + bodyBytes + lineCount * 2;
|
|
37234
|
+
}
|
|
37235
|
+
async function taskkillTree(pid) {
|
|
37236
|
+
return new Promise((resolve6) => {
|
|
37237
|
+
let settled = false;
|
|
37238
|
+
let killer;
|
|
37239
|
+
const finish = (ok2) => {
|
|
37240
|
+
if (settled)
|
|
37241
|
+
return;
|
|
37242
|
+
settled = true;
|
|
37243
|
+
clearTimeout(timer);
|
|
37244
|
+
resolve6(ok2);
|
|
37245
|
+
};
|
|
37246
|
+
try {
|
|
37247
|
+
killer = spawn("taskkill", ["/PID", String(pid), "/T", "/F"], {
|
|
37248
|
+
stdio: "ignore",
|
|
37249
|
+
windowsHide: true
|
|
37250
|
+
});
|
|
37251
|
+
} catch {
|
|
37252
|
+
resolve6(false);
|
|
37253
|
+
return;
|
|
37254
|
+
}
|
|
37255
|
+
const timer = setTimeout(() => {
|
|
37256
|
+
try {
|
|
37257
|
+
killer.kill("SIGKILL");
|
|
37258
|
+
} catch {}
|
|
37259
|
+
finish(false);
|
|
37260
|
+
}, 2000);
|
|
37261
|
+
killer.once("error", () => finish(false));
|
|
37262
|
+
killer.once("close", (code) => finish(code === 0));
|
|
37263
|
+
});
|
|
37264
|
+
}
|
|
37265
|
+
function snapshotProcessDescendants(rootPid) {
|
|
37266
|
+
const snapshot = spawnSync2("ps", ["-axo", "pid=,ppid="], {
|
|
37267
|
+
encoding: "utf8",
|
|
37268
|
+
maxBuffer: 10 * 1024 * 1024
|
|
37269
|
+
});
|
|
37270
|
+
if (snapshot.status !== 0 || snapshot.error)
|
|
37271
|
+
return [];
|
|
37272
|
+
const children = new Map;
|
|
37273
|
+
for (const line of (snapshot.stdout ?? "").split(`
|
|
37274
|
+
`)) {
|
|
37275
|
+
const match8 = /^\s*(\d+)\s+(\d+)\s*$/.exec(line);
|
|
37276
|
+
if (!match8)
|
|
37277
|
+
continue;
|
|
37278
|
+
const pid = Number(match8[1]);
|
|
37279
|
+
const parentPid = Number(match8[2]);
|
|
37280
|
+
const siblings = children.get(parentPid);
|
|
37281
|
+
if (siblings)
|
|
37282
|
+
siblings.push(pid);
|
|
37283
|
+
else
|
|
37284
|
+
children.set(parentPid, [pid]);
|
|
37285
|
+
}
|
|
37286
|
+
const descendants = [];
|
|
37287
|
+
const pending = [...children.get(rootPid) ?? []];
|
|
37288
|
+
for (let index2 = 0;index2 < pending.length; index2++) {
|
|
37289
|
+
const pid = pending[index2];
|
|
37290
|
+
descendants.push(pid);
|
|
37291
|
+
pending.push(...children.get(pid) ?? []);
|
|
37292
|
+
}
|
|
37293
|
+
return descendants;
|
|
37294
|
+
}
|
|
37295
|
+
async function killProcessTree(child) {
|
|
37296
|
+
const pid = child.pid;
|
|
37297
|
+
if (process.platform === "win32" && pid !== undefined) {
|
|
37298
|
+
if (await taskkillTree(pid))
|
|
37299
|
+
return;
|
|
37300
|
+
} else if (pid !== undefined) {
|
|
37301
|
+
const descendants = snapshotProcessDescendants(pid);
|
|
37302
|
+
try {
|
|
37303
|
+
process.kill(-pid, "SIGKILL");
|
|
37304
|
+
} catch {}
|
|
37305
|
+
for (const descendantPid of descendants) {
|
|
37306
|
+
try {
|
|
37307
|
+
process.kill(descendantPid, "SIGKILL");
|
|
37308
|
+
} catch {}
|
|
37309
|
+
}
|
|
37310
|
+
}
|
|
37311
|
+
try {
|
|
37312
|
+
child.kill("SIGKILL");
|
|
37313
|
+
} catch {}
|
|
37314
|
+
}
|
|
37315
|
+
function runVerificationProcess(wrapper, interpreter, script4, cwd2, timeoutMs, outputLimit) {
|
|
37316
|
+
return new Promise((resolve6) => {
|
|
37317
|
+
let child;
|
|
37318
|
+
try {
|
|
37319
|
+
child = spawn("sh", [wrapper, interpreter, script4], {
|
|
37320
|
+
cwd: cwd2,
|
|
37321
|
+
detached: process.platform !== "win32",
|
|
37322
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
37323
|
+
windowsHide: true
|
|
37324
|
+
});
|
|
37325
|
+
} catch (error2) {
|
|
37326
|
+
resolve6({
|
|
37327
|
+
code: 1,
|
|
37328
|
+
output: "",
|
|
37329
|
+
error: verificationError(error2)
|
|
37330
|
+
});
|
|
37331
|
+
return;
|
|
37332
|
+
}
|
|
37333
|
+
const output = [];
|
|
37334
|
+
let outputSize = 0;
|
|
37335
|
+
let outputExceeded = false;
|
|
37336
|
+
let processError;
|
|
37337
|
+
let termination;
|
|
37338
|
+
let timeout3;
|
|
37339
|
+
let postKillCompletion;
|
|
37340
|
+
let settled = false;
|
|
37341
|
+
const terminate = (message) => {
|
|
37342
|
+
if (settled)
|
|
37343
|
+
return;
|
|
37344
|
+
processError ??= message;
|
|
37345
|
+
if (termination)
|
|
37346
|
+
return;
|
|
37347
|
+
termination = killProcessTree(child);
|
|
37348
|
+
postKillCompletion = setTimeout(() => {
|
|
37349
|
+
child.stdout?.destroy();
|
|
37350
|
+
finish(1);
|
|
37351
|
+
}, VERIFY_KILL_CLOSE_GRACE_MS);
|
|
37352
|
+
};
|
|
37353
|
+
const capture = (chunk) => {
|
|
37354
|
+
if (outputExceeded)
|
|
37355
|
+
return;
|
|
37356
|
+
const available = outputLimit - outputSize;
|
|
37357
|
+
if (chunk.length > available) {
|
|
37358
|
+
if (available > 0)
|
|
37359
|
+
output.push(chunk.subarray(0, available));
|
|
37360
|
+
outputSize = outputLimit;
|
|
37361
|
+
outputExceeded = true;
|
|
37362
|
+
terminate(`verification output exceeded ${outputLimit} bytes`);
|
|
37363
|
+
return;
|
|
37364
|
+
}
|
|
37365
|
+
output.push(chunk);
|
|
37366
|
+
outputSize += chunk.length;
|
|
37367
|
+
};
|
|
37368
|
+
const onStdout = (chunk) => capture(chunk);
|
|
37369
|
+
const onError3 = (error2) => {
|
|
37370
|
+
terminate(`verification process error: ${verificationError(error2)}`);
|
|
37371
|
+
};
|
|
37372
|
+
const finish = (code) => {
|
|
37373
|
+
if (settled)
|
|
37374
|
+
return;
|
|
37375
|
+
settled = true;
|
|
37376
|
+
if (timeout3)
|
|
37377
|
+
clearTimeout(timeout3);
|
|
37378
|
+
if (postKillCompletion)
|
|
37379
|
+
clearTimeout(postKillCompletion);
|
|
37380
|
+
child.stdout?.off("data", onStdout);
|
|
37381
|
+
child.off("error", onError3);
|
|
37382
|
+
child.off("close", onClose);
|
|
37383
|
+
resolve6({
|
|
37384
|
+
code,
|
|
37385
|
+
output: Buffer.concat(output).toString("utf8"),
|
|
37386
|
+
...processError === undefined ? {} : { error: processError }
|
|
37387
|
+
});
|
|
37388
|
+
};
|
|
37389
|
+
const onClose = (code) => {
|
|
37390
|
+
if (termination) {
|
|
37391
|
+
termination.then(() => finish(code ?? 1), () => finish(code ?? 1));
|
|
37392
|
+
} else {
|
|
37393
|
+
finish(code ?? 1);
|
|
37394
|
+
}
|
|
37395
|
+
};
|
|
37396
|
+
child.stdout?.on("data", onStdout);
|
|
37397
|
+
child.once("error", onError3);
|
|
37398
|
+
child.once("close", onClose);
|
|
37399
|
+
timeout3 = setTimeout(() => {
|
|
37400
|
+
terminate(`verification timed out after ${timeoutMs}ms`);
|
|
37401
|
+
}, timeoutMs);
|
|
37402
|
+
});
|
|
37403
|
+
}
|
|
37160
37404
|
function filterAppPaths(summary) {
|
|
37161
37405
|
return summary.split(/\r?\n/).filter((line) => {
|
|
37162
37406
|
const t = line.trim();
|
|
@@ -37262,33 +37506,93 @@ var VcsLive = exports_Layer.effect(Vcs, exports_Effect.gen(function* () {
|
|
|
37262
37506
|
run2("jj", ["bookmark", "create", name, "-r", "@-"], root);
|
|
37263
37507
|
}
|
|
37264
37508
|
});
|
|
37265
|
-
const runVerify = (root,
|
|
37266
|
-
const
|
|
37267
|
-
|
|
37268
|
-
|
|
37269
|
-
|
|
37270
|
-
|
|
37509
|
+
const runVerify = (root, blocks, timeoutMs) => exports_Effect.promise(async () => {
|
|
37510
|
+
const log2 = new VerificationLog(VERIFY_LOG_LIMIT);
|
|
37511
|
+
let temporaryDirectory;
|
|
37512
|
+
let ok2 = true;
|
|
37513
|
+
let operation = "create temporary verification directory";
|
|
37514
|
+
try {
|
|
37515
|
+
temporaryDirectory = mkdtempSync(path5.join(tmpdir(), "apnea-verify-"));
|
|
37516
|
+
const wrapper = path5.join(temporaryDirectory, "run-block.sh");
|
|
37517
|
+
operation = "write verification wrapper";
|
|
37518
|
+
writeFileSync2(wrapper, VERIFY_WRAPPER_SOURCE, {
|
|
37271
37519
|
encoding: "utf8",
|
|
37272
|
-
|
|
37273
|
-
maxBuffer: 10 * 1024 * 1024
|
|
37520
|
+
mode: 384
|
|
37274
37521
|
});
|
|
37275
|
-
const
|
|
37276
|
-
|
|
37277
|
-
|
|
37278
|
-
|
|
37279
|
-
|
|
37280
|
-
|
|
37281
|
-
|
|
37282
|
-
|
|
37522
|
+
for (const [index2, block] of blocks.entries()) {
|
|
37523
|
+
const source = normalizeVerifySource(block.source);
|
|
37524
|
+
const normalizedBlock = { ...block, source };
|
|
37525
|
+
const script4 = path5.join(temporaryDirectory, `block-${index2 + 1}.${block.interpreter}`);
|
|
37526
|
+
const displayBytes = 2 + verifyBlockDisplayByteLength(normalizedBlock) + 1;
|
|
37527
|
+
if (!log2.canAppendBytes(displayBytes)) {
|
|
37528
|
+
log2.addLimitNotice(VERIFY_DISPLAY_LIMIT_NOTICE);
|
|
37529
|
+
ok2 = false;
|
|
37530
|
+
break;
|
|
37531
|
+
}
|
|
37532
|
+
log2.append(`$ ${formatVerifyBlock(normalizedBlock)}
|
|
37533
|
+
`);
|
|
37534
|
+
operation = `write ${block.interpreter} verification block`;
|
|
37535
|
+
writeFileSync2(script4, source, {
|
|
37536
|
+
encoding: "utf8",
|
|
37537
|
+
mode: 384
|
|
37538
|
+
});
|
|
37539
|
+
operation = `run ${block.interpreter} verification block`;
|
|
37540
|
+
const result3 = await runVerificationProcess(wrapper, block.interpreter, script4, root, timeoutMs, Math.max(0, log2.remaining - VERIFY_RESULT_RESERVE));
|
|
37541
|
+
const output = verificationError(result3.output.trimEnd(), temporaryDirectory);
|
|
37542
|
+
if (output && !log2.append(`${output}
|
|
37543
|
+
`)) {
|
|
37544
|
+
log2.addLimitNotice(VERIFY_LOG_LIMIT_NOTICE);
|
|
37545
|
+
ok2 = false;
|
|
37546
|
+
break;
|
|
37547
|
+
}
|
|
37548
|
+
if (!log2.append(`exit=${result3.code}
|
|
37549
|
+
`)) {
|
|
37550
|
+
log2.addLimitNotice(VERIFY_LOG_LIMIT_NOTICE);
|
|
37551
|
+
ok2 = false;
|
|
37552
|
+
break;
|
|
37553
|
+
}
|
|
37554
|
+
if (result3.error) {
|
|
37555
|
+
const error2 = verificationError(result3.error, temporaryDirectory);
|
|
37556
|
+
if (!log2.append(`${error2}
|
|
37557
|
+
`)) {
|
|
37558
|
+
log2.addLimitNotice(VERIFY_LOG_LIMIT_NOTICE);
|
|
37559
|
+
}
|
|
37560
|
+
ok2 = false;
|
|
37561
|
+
break;
|
|
37562
|
+
}
|
|
37563
|
+
if (result3.code !== 0) {
|
|
37564
|
+
ok2 = false;
|
|
37565
|
+
break;
|
|
37566
|
+
}
|
|
37567
|
+
if (index2 < blocks.length - 1 && !log2.append(`
|
|
37568
|
+
`)) {
|
|
37569
|
+
log2.addLimitNotice(VERIFY_LOG_LIMIT_NOTICE);
|
|
37570
|
+
ok2 = false;
|
|
37571
|
+
break;
|
|
37572
|
+
}
|
|
37283
37573
|
}
|
|
37284
|
-
|
|
37285
|
-
|
|
37286
|
-
|
|
37574
|
+
} catch (error2) {
|
|
37575
|
+
const message = `${operation} failed: ${verificationError(error2, temporaryDirectory)}
|
|
37576
|
+
`;
|
|
37577
|
+
if (!log2.append(message)) {
|
|
37578
|
+
log2.addLimitNotice(VERIFY_LOG_LIMIT_NOTICE);
|
|
37579
|
+
}
|
|
37580
|
+
ok2 = false;
|
|
37581
|
+
} finally {
|
|
37582
|
+
if (temporaryDirectory) {
|
|
37583
|
+
try {
|
|
37584
|
+
rmSync2(temporaryDirectory, { recursive: true, force: true });
|
|
37585
|
+
} catch (error2) {
|
|
37586
|
+
const message = `clean up temporary verification directory failed: ${verificationError(error2, temporaryDirectory)}
|
|
37587
|
+
`;
|
|
37588
|
+
if (!log2.append(message)) {
|
|
37589
|
+
log2.addLimitNotice(VERIFY_LOG_LIMIT_NOTICE);
|
|
37590
|
+
}
|
|
37591
|
+
ok2 = false;
|
|
37592
|
+
}
|
|
37287
37593
|
}
|
|
37288
|
-
lines.push("");
|
|
37289
37594
|
}
|
|
37290
|
-
return { ok:
|
|
37291
|
-
`) };
|
|
37595
|
+
return { ok: ok2, log: log2.toString() };
|
|
37292
37596
|
});
|
|
37293
37597
|
return Vcs.of({
|
|
37294
37598
|
detect,
|
|
@@ -37455,85 +37759,6 @@ function toolAllowed(step, tool) {
|
|
|
37455
37759
|
return exports_Result.succeed(undefined);
|
|
37456
37760
|
}
|
|
37457
37761
|
|
|
37458
|
-
// extension/domain/verify-commands.ts
|
|
37459
|
-
function endsInContinuation(line) {
|
|
37460
|
-
const run3 = /(\\+)$/.exec(line);
|
|
37461
|
-
return run3 !== null && run3[1].length % 2 === 1;
|
|
37462
|
-
}
|
|
37463
|
-
function logicalCommands(lines) {
|
|
37464
|
-
const out = [];
|
|
37465
|
-
let acc = "";
|
|
37466
|
-
let pending = false;
|
|
37467
|
-
for (const line of lines) {
|
|
37468
|
-
const isCommentish = line.trim().startsWith("#");
|
|
37469
|
-
if (!pending && isCommentish)
|
|
37470
|
-
continue;
|
|
37471
|
-
if (pending && isCommentish && (/\s$/.test(acc) || /^\s/.test(line))) {
|
|
37472
|
-
out.push(acc);
|
|
37473
|
-
acc = "";
|
|
37474
|
-
pending = false;
|
|
37475
|
-
continue;
|
|
37476
|
-
}
|
|
37477
|
-
const continues = endsInContinuation(line);
|
|
37478
|
-
const text = continues ? line.slice(0, -1) : line;
|
|
37479
|
-
acc = pending ? acc + text : text;
|
|
37480
|
-
pending = continues;
|
|
37481
|
-
if (!pending) {
|
|
37482
|
-
out.push(acc);
|
|
37483
|
-
acc = "";
|
|
37484
|
-
}
|
|
37485
|
-
}
|
|
37486
|
-
if (pending)
|
|
37487
|
-
out.push(acc);
|
|
37488
|
-
return out;
|
|
37489
|
-
}
|
|
37490
|
-
function toCommands(lines) {
|
|
37491
|
-
const cmds = [];
|
|
37492
|
-
for (const joined of logicalCommands(lines)) {
|
|
37493
|
-
const t = joined.trim();
|
|
37494
|
-
if (t)
|
|
37495
|
-
cmds.push(t);
|
|
37496
|
-
}
|
|
37497
|
-
return cmds;
|
|
37498
|
-
}
|
|
37499
|
-
function commandsFromFenceBody(body) {
|
|
37500
|
-
return toCommands(body.split(/\r?\n/));
|
|
37501
|
-
}
|
|
37502
|
-
function extractVerifyCommands(phasePackageText) {
|
|
37503
|
-
const section = phasePackageText.match(/(?:^|\n)(?:#{1,6}\s*|\*\*)Verify commands(?:\*\*)?\s*\r?\n([\s\S]*?)(?=\n#{1,6}\s|\n\*\*[A-Z]|$)/i);
|
|
37504
|
-
if (section) {
|
|
37505
|
-
const fence = section[1].match(/```(?:sh|bash|shell)\r?\n([\s\S]*?)```/i);
|
|
37506
|
-
if (fence) {
|
|
37507
|
-
const cmds2 = commandsFromFenceBody(fence[1]);
|
|
37508
|
-
if (cmds2.length)
|
|
37509
|
-
return cmds2;
|
|
37510
|
-
}
|
|
37511
|
-
}
|
|
37512
|
-
const all5 = [
|
|
37513
|
-
...phasePackageText.matchAll(/```(?:sh|bash|shell)\r?\n([\s\S]*?)```/gi)
|
|
37514
|
-
];
|
|
37515
|
-
for (let i = all5.length - 1;i >= 0; i--) {
|
|
37516
|
-
const cmds2 = commandsFromFenceBody(all5[i][1]);
|
|
37517
|
-
if (cmds2.length)
|
|
37518
|
-
return cmds2;
|
|
37519
|
-
}
|
|
37520
|
-
const cmds = [];
|
|
37521
|
-
const rawLines = phasePackageText.split(/\r?\n/);
|
|
37522
|
-
for (let i = 0;i < rawLines.length; i++) {
|
|
37523
|
-
const m = rawLines[i].match(/^\s*(?:\$\s+)?((?:test |node |npm |bun |bunx |chmod |head ).+)$/);
|
|
37524
|
-
if (!m)
|
|
37525
|
-
continue;
|
|
37526
|
-
let cmd = m[1];
|
|
37527
|
-
while (endsInContinuation(cmd) && i + 1 < rawLines.length) {
|
|
37528
|
-
cmd = cmd.slice(0, -1) + rawLines[++i];
|
|
37529
|
-
}
|
|
37530
|
-
if (endsInContinuation(cmd))
|
|
37531
|
-
cmd = cmd.slice(0, -1);
|
|
37532
|
-
cmds.push(cmd.trim());
|
|
37533
|
-
}
|
|
37534
|
-
return cmds;
|
|
37535
|
-
}
|
|
37536
|
-
|
|
37537
37762
|
// extension/workflows/commit.ts
|
|
37538
37763
|
var commitWorkflow = (params, root) => exports_Effect.gen(function* () {
|
|
37539
37764
|
const store = yield* RunStore;
|
|
@@ -37581,8 +37806,8 @@ var commitWorkflow = (params, root) => exports_Effect.gen(function* () {
|
|
|
37581
37806
|
});
|
|
37582
37807
|
}
|
|
37583
37808
|
const pkgText = yield* fs4.readFile(pkgAbs);
|
|
37584
|
-
const
|
|
37585
|
-
if (!
|
|
37809
|
+
const blocks = extractVerifyBlocks(pkgText);
|
|
37810
|
+
if (!blocks.length) {
|
|
37586
37811
|
return yield* new ArtifactInvalid({
|
|
37587
37812
|
artifact: pkgRel,
|
|
37588
37813
|
message: "no verify commands found in phase package (need ```sh block)"
|
|
@@ -37590,14 +37815,14 @@ var commitWorkflow = (params, root) => exports_Effect.gen(function* () {
|
|
|
37590
37815
|
}
|
|
37591
37816
|
const cfg = yield* config.load(root);
|
|
37592
37817
|
const verifyTimeout = cfg.timeouts_ms.verify ?? 900000;
|
|
37593
|
-
const verify = yield* vcs.runVerify(root,
|
|
37818
|
+
const verify = yield* vcs.runVerify(root, blocks, verifyTimeout);
|
|
37594
37819
|
const vlog = path6.join(path6.dirname(reviewAbs), "verify.log");
|
|
37595
37820
|
yield* fs4.mkdir(path6.dirname(vlog), { recursive: true });
|
|
37596
37821
|
yield* fs4.writeFile(vlog, `${verify.log}
|
|
37597
37822
|
`);
|
|
37598
37823
|
if (!verify.ok) {
|
|
37599
37824
|
return yield* new VerifyFailed({
|
|
37600
|
-
commands:
|
|
37825
|
+
commands: blocks.map(formatVerifyCommand),
|
|
37601
37826
|
outputs: [verify.log.slice(-2000)],
|
|
37602
37827
|
verify_log: rel(vlog, root)
|
|
37603
37828
|
});
|
|
@@ -37860,50 +38085,11 @@ On rework, read latest code-review and fix.`;
|
|
|
37860
38085
|
details: { role, tried: briefCandidates }
|
|
37861
38086
|
});
|
|
37862
38087
|
}
|
|
37863
|
-
const
|
|
37864
|
-
|
|
37865
|
-
|
|
37866
|
-
|
|
37867
|
-
|
|
37868
|
-
const version2 = yield* herdr.version;
|
|
37869
|
-
if (!supportsFloating(version2)) {
|
|
37870
|
-
return yield* new HerdrError({
|
|
37871
|
-
message: "floating panes need herdr >= 0.7.4 \u2014 run `herdr update`, or set pane_style=regular"
|
|
37872
|
-
});
|
|
37873
|
-
}
|
|
37874
|
-
if (!(yield* herdr.hasApneaPlugin)) {
|
|
37875
|
-
return yield* new HerdrError({
|
|
37876
|
-
message: `apnea herdr plugin not linked. Run /apnea setup, or: herdr plugin link ${livePackageRoot}/herdr-plugin`
|
|
37877
|
-
});
|
|
37878
|
-
}
|
|
37879
|
-
if (state.pending_floating_exit) {
|
|
37880
|
-
const prevExitAbs = abs2(state.pending_floating_exit, root);
|
|
37881
|
-
if (!(yield* fs4.exists(prevExitAbs))) {
|
|
37882
|
-
return yield* new GateRefused({
|
|
37883
|
-
gate: "floating_in_flight",
|
|
37884
|
-
message: "floating oneshot already in flight (popup still open). Call workflow_wait, or dismiss the popup and re-dispatch after it exits",
|
|
37885
|
-
details: {
|
|
37886
|
-
pending_artifact: state.pending_artifact,
|
|
37887
|
-
pending_floating_exit: state.pending_floating_exit
|
|
37888
|
-
}
|
|
37889
|
-
});
|
|
37890
|
-
}
|
|
37891
|
-
}
|
|
37892
|
-
const cmdResult = yield* exports_Effect.result(config.resolveRoleCmd(cfg, role, "oneshot"));
|
|
37893
|
-
if (exports_Result.isFailure(cmdResult)) {
|
|
37894
|
-
return yield* new HerdrError({
|
|
37895
|
-
message: `floating dispatch requires cmd_oneshot on the role profile: ${cmdResult.failure.message}`
|
|
37896
|
-
});
|
|
37897
|
-
}
|
|
37898
|
-
roleCmd = cmdResult.success;
|
|
37899
|
-
} else {
|
|
37900
|
-
roleCmd = yield* config.resolveRoleCmd(cfg, role, "interactive");
|
|
37901
|
-
profileFingerprint = JSON.stringify([
|
|
37902
|
-
cfg.roles[role]?.profile ?? null,
|
|
37903
|
-
roleCmd
|
|
37904
|
-
]);
|
|
37905
|
-
}
|
|
37906
|
-
}
|
|
38088
|
+
const roleCmd = yield* config.resolveRoleCmd(cfg, role, "interactive");
|
|
38089
|
+
const profileFingerprint = JSON.stringify([
|
|
38090
|
+
cfg.roles[role]?.profile ?? null,
|
|
38091
|
+
roleCmd
|
|
38092
|
+
]);
|
|
37907
38093
|
if (role === "reviewer") {
|
|
37908
38094
|
state.reviewer_tree_fingerprint = yield* vcsSvc.treeFingerprint(root, state.vcs);
|
|
37909
38095
|
}
|
|
@@ -37943,9 +38129,7 @@ On rework, read latest code-review and fix.`;
|
|
|
37943
38129
|
].join(`
|
|
37944
38130
|
`);
|
|
37945
38131
|
let launch2 = {
|
|
37946
|
-
mode: ROLE_MODE[role]
|
|
37947
|
-
pane_style: cfg.pane_style,
|
|
37948
|
-
pane_style_effective: paneStyle.effective
|
|
38132
|
+
mode: ROLE_MODE[role]
|
|
37949
38133
|
};
|
|
37950
38134
|
const rollbackLaunch = (restoreState = true) => exports_Effect.gen(function* () {
|
|
37951
38135
|
const errors4 = [];
|
|
@@ -37956,8 +38140,6 @@ On rework, read latest code-review and fix.`;
|
|
|
37956
38140
|
}
|
|
37957
38141
|
});
|
|
37958
38142
|
yield* attempt("remove task", fs4.remove(taskFile));
|
|
37959
|
-
yield* attempt("remove floating script", fs4.remove(taskFile.replace(/\.md$/, ".sh")));
|
|
37960
|
-
yield* attempt("remove floating exit marker", fs4.remove(taskFile.replace(/\.md$/, ".exit")));
|
|
37961
38143
|
yield* attempt("remove replacement artifact", fs4.remove(artifactAbs));
|
|
37962
38144
|
if (backupAbs != null) {
|
|
37963
38145
|
yield* attempt("restore prior artifact", fs4.rename(backupAbs, artifactAbs));
|
|
@@ -37980,7 +38162,6 @@ On rework, read latest code-review and fix.`;
|
|
|
37980
38162
|
markPending(preparedAt);
|
|
37981
38163
|
state.pending_pane_id = null;
|
|
37982
38164
|
state.pending_pane_label = null;
|
|
37983
|
-
state.pending_floating_exit = null;
|
|
37984
38165
|
const preparedState = yield* exports_Effect.exit(store.save(state, root));
|
|
37985
38166
|
if (exports_Exit.isFailure(preparedState)) {
|
|
37986
38167
|
const persistenceError = new HerdrError({
|
|
@@ -38002,104 +38183,58 @@ On rework, read latest code-review and fix.`;
|
|
|
38002
38183
|
next: "workflow_wait"
|
|
38003
38184
|
}, ["workflow_wait"]);
|
|
38004
38185
|
}
|
|
38005
|
-
|
|
38006
|
-
|
|
38007
|
-
|
|
38008
|
-
|
|
38009
|
-
|
|
38010
|
-
|
|
38011
|
-
|
|
38012
|
-
const
|
|
38013
|
-
|
|
38014
|
-
|
|
38015
|
-
|
|
38016
|
-
|
|
38017
|
-
|
|
38018
|
-
|
|
38019
|
-
|
|
38020
|
-
|
|
38021
|
-
yield* store.save(state, root);
|
|
38022
|
-
const opened = yield* exports_Effect.result(herdr.openFloatingPane(scriptAbs, root));
|
|
38023
|
-
if (exports_Result.isFailure(opened)) {
|
|
38186
|
+
const cmd = roleCmd;
|
|
38187
|
+
const remembered = state.role_panes[role] ?? null;
|
|
38188
|
+
const prefer = remembered?.profile_fingerprint === profileFingerprint ? remembered : null;
|
|
38189
|
+
const launched = yield* exports_Effect.result(herdr.runInteractivePrompt(role, cmd, prompt, prefer));
|
|
38190
|
+
if (exports_Result.isFailure(launched)) {
|
|
38191
|
+
if (launched.failure.details?.delivery === "unknown") {
|
|
38192
|
+
const paneId = String(launched.failure.details.pane_id);
|
|
38193
|
+
const paneLabel = String(launched.failure.details.pane_label);
|
|
38194
|
+
state.pending_pane_id = paneId;
|
|
38195
|
+
state.pending_pane_label = paneLabel;
|
|
38196
|
+
state.role_panes[role] = {
|
|
38197
|
+
pane_id: paneId,
|
|
38198
|
+
label: paneLabel,
|
|
38199
|
+
profile_fingerprint: profileFingerprint
|
|
38200
|
+
};
|
|
38201
|
+
yield* store.save(state, root);
|
|
38024
38202
|
return yield* new HerdrError({
|
|
38025
|
-
message:
|
|
38026
|
-
...
|
|
38203
|
+
message: launched.failure.message,
|
|
38204
|
+
...launched.failure.command !== undefined ? { command: launched.failure.command } : {},
|
|
38027
38205
|
details: {
|
|
38028
|
-
...
|
|
38029
|
-
delivery: "unknown",
|
|
38206
|
+
...launched.failure.details ?? {},
|
|
38030
38207
|
task_attempted: taskRef.task,
|
|
38031
38208
|
artifact: artifactRel,
|
|
38032
38209
|
pending_preserved: true
|
|
38033
38210
|
}
|
|
38034
38211
|
});
|
|
38035
38212
|
}
|
|
38036
|
-
|
|
38037
|
-
|
|
38038
|
-
|
|
38039
|
-
|
|
38040
|
-
|
|
38041
|
-
exit: rel(exitAbs, root),
|
|
38042
|
-
cmd,
|
|
38043
|
-
prompt
|
|
38044
|
-
};
|
|
38045
|
-
} else {
|
|
38046
|
-
const cmd = roleCmd;
|
|
38047
|
-
const remembered = state.role_panes[role] ?? null;
|
|
38048
|
-
const prefer = remembered?.profile_fingerprint === profileFingerprint ? remembered : null;
|
|
38049
|
-
const launched = yield* exports_Effect.result(herdr.runInteractivePrompt(role, cmd, prompt, prefer));
|
|
38050
|
-
if (exports_Result.isFailure(launched)) {
|
|
38051
|
-
if (launched.failure.details?.delivery === "unknown") {
|
|
38052
|
-
const paneId = String(launched.failure.details.pane_id);
|
|
38053
|
-
const paneLabel = String(launched.failure.details.pane_label);
|
|
38054
|
-
state.pending_pane_id = paneId;
|
|
38055
|
-
state.pending_pane_label = paneLabel;
|
|
38056
|
-
state.pending_floating_exit = null;
|
|
38057
|
-
state.role_panes[role] = {
|
|
38058
|
-
pane_id: paneId,
|
|
38059
|
-
label: paneLabel,
|
|
38060
|
-
profile_fingerprint: profileFingerprint
|
|
38061
|
-
};
|
|
38062
|
-
yield* store.save(state, root);
|
|
38063
|
-
return yield* new HerdrError({
|
|
38064
|
-
message: launched.failure.message,
|
|
38065
|
-
...launched.failure.command !== undefined ? { command: launched.failure.command } : {},
|
|
38066
|
-
details: {
|
|
38067
|
-
...launched.failure.details ?? {},
|
|
38068
|
-
task_attempted: taskRef.task,
|
|
38069
|
-
artifact: artifactRel,
|
|
38070
|
-
pending_preserved: true
|
|
38071
|
-
}
|
|
38072
|
-
});
|
|
38073
|
-
}
|
|
38074
|
-
const rollbackErrors = yield* rollbackLaunch();
|
|
38075
|
-
return yield* herdrAfterRollback(launched.failure, {
|
|
38076
|
-
task_attempted: taskRef.task,
|
|
38077
|
-
artifact: artifactRel
|
|
38078
|
-
}, rollbackErrors);
|
|
38079
|
-
}
|
|
38080
|
-
const r = launched.success;
|
|
38081
|
-
launch2 = {
|
|
38082
|
-
mode: "interactive",
|
|
38083
|
-
pane_id: r.pane_id,
|
|
38084
|
-
label: r.label,
|
|
38085
|
-
reused: r.reused,
|
|
38086
|
-
cmd,
|
|
38087
|
-
prompt,
|
|
38088
|
-
pane_style: cfg.pane_style,
|
|
38089
|
-
pane_style_effective: paneStyle.effective,
|
|
38090
|
-
prompt_accepted: r.prompt_accepted,
|
|
38091
|
-
prompt_attempts: r.prompt_attempts,
|
|
38092
|
-
last_status: r.last_status ?? null
|
|
38093
|
-
};
|
|
38094
|
-
state.pending_pane_id = r.pane_id;
|
|
38095
|
-
state.pending_pane_label = r.label;
|
|
38096
|
-
state.pending_floating_exit = null;
|
|
38097
|
-
state.role_panes[role] = {
|
|
38098
|
-
pane_id: r.pane_id,
|
|
38099
|
-
label: r.label,
|
|
38100
|
-
profile_fingerprint: profileFingerprint
|
|
38101
|
-
};
|
|
38213
|
+
const rollbackErrors = yield* rollbackLaunch();
|
|
38214
|
+
return yield* herdrAfterRollback(launched.failure, {
|
|
38215
|
+
task_attempted: taskRef.task,
|
|
38216
|
+
artifact: artifactRel
|
|
38217
|
+
}, rollbackErrors);
|
|
38102
38218
|
}
|
|
38219
|
+
const r = launched.success;
|
|
38220
|
+
launch2 = {
|
|
38221
|
+
mode: "interactive",
|
|
38222
|
+
pane_id: r.pane_id,
|
|
38223
|
+
label: r.label,
|
|
38224
|
+
reused: r.reused,
|
|
38225
|
+
cmd,
|
|
38226
|
+
prompt,
|
|
38227
|
+
prompt_accepted: r.prompt_accepted,
|
|
38228
|
+
prompt_attempts: r.prompt_attempts,
|
|
38229
|
+
last_status: r.last_status ?? null
|
|
38230
|
+
};
|
|
38231
|
+
state.pending_pane_id = r.pane_id;
|
|
38232
|
+
state.pending_pane_label = r.label;
|
|
38233
|
+
state.role_panes[role] = {
|
|
38234
|
+
pane_id: r.pane_id,
|
|
38235
|
+
label: r.label,
|
|
38236
|
+
profile_fingerprint: profileFingerprint
|
|
38237
|
+
};
|
|
38103
38238
|
const launchedAt = yield* exports_Clock.currentTimeMillis;
|
|
38104
38239
|
markPending(launchedAt);
|
|
38105
38240
|
yield* store.save(state, root);
|
|
@@ -38131,12 +38266,6 @@ function deepMergeProfiles(existing, incoming) {
|
|
|
38131
38266
|
}
|
|
38132
38267
|
return out;
|
|
38133
38268
|
}
|
|
38134
|
-
function preservePaneStyle(prev) {
|
|
38135
|
-
const v = prev.pane_style;
|
|
38136
|
-
if (v === "regular" || v === "floating")
|
|
38137
|
-
return v;
|
|
38138
|
-
return;
|
|
38139
|
-
}
|
|
38140
38269
|
function buildProfiles(has4) {
|
|
38141
38270
|
const profiles = {};
|
|
38142
38271
|
if (has4.pi) {
|
|
@@ -38195,16 +38324,12 @@ function buildGlobalConfig(opts) {
|
|
|
38195
38324
|
if (!force && prev.profiles && typeof prev.profiles === "object") {
|
|
38196
38325
|
nextProfiles = deepMergeProfiles(prev.profiles, profiles);
|
|
38197
38326
|
}
|
|
38198
|
-
const preservedPaneStyle = preservePaneStyle(prev);
|
|
38199
38327
|
const globalConfig = {
|
|
38200
38328
|
profiles: nextProfiles,
|
|
38201
38329
|
roles: force || !prev.roles ? roles : prev.roles,
|
|
38202
38330
|
review_round_cap: typeof prev.review_round_cap === "number" ? prev.review_round_cap : 3,
|
|
38203
38331
|
timeouts_ms: prev.timeouts_ms && typeof prev.timeouts_ms === "object" ? prev.timeouts_ms : { ...DEFAULT_TIMEOUTS }
|
|
38204
38332
|
};
|
|
38205
|
-
if (preservedPaneStyle !== undefined) {
|
|
38206
|
-
globalConfig.pane_style = preservedPaneStyle;
|
|
38207
|
-
}
|
|
38208
38333
|
return globalConfig;
|
|
38209
38334
|
}
|
|
38210
38335
|
function detectionNotes(has4) {
|
|
@@ -38253,53 +38378,8 @@ function mergeAgentsMd(existing) {
|
|
|
38253
38378
|
`;
|
|
38254
38379
|
return `${existing}${sep2}${AGENTS_SECTION}`;
|
|
38255
38380
|
}
|
|
38256
|
-
var provisionHerdrPlugin = (opts) => exports_Effect.gen(function* () {
|
|
38257
|
-
const fs4 = yield* FileSystem;
|
|
38258
|
-
const herdr = yield* Herdr;
|
|
38259
|
-
const notes = [];
|
|
38260
|
-
const srcExists = yield* fs4.exists(opts.srcDir);
|
|
38261
|
-
if (!srcExists) {
|
|
38262
|
-
notes.push("herdr-plugin missing from package \u2014 reinstall @naxodev/apnea");
|
|
38263
|
-
return { copied: null, linked: false, already_linked: false, notes };
|
|
38264
|
-
}
|
|
38265
|
-
yield* fs4.copyDir(opts.srcDir, opts.destDir);
|
|
38266
|
-
const runTask = path8.join(opts.destDir, "scripts", "run-task.sh");
|
|
38267
|
-
if (yield* fs4.exists(runTask)) {
|
|
38268
|
-
yield* fs4.chmod(runTask, 493);
|
|
38269
|
-
}
|
|
38270
|
-
if (!supportsFloating(opts.version)) {
|
|
38271
|
-
const ver = opts.version == null ? "unknown" : `${opts.version[0]}.${opts.version[1]}.${opts.version[2]}`;
|
|
38272
|
-
notes.push(`herdr ${ver} < 0.7.4 \u2014 floating panes unavailable; run \`herdr update\`, then re-run /apnea setup`);
|
|
38273
|
-
return {
|
|
38274
|
-
copied: opts.destDir,
|
|
38275
|
-
linked: false,
|
|
38276
|
-
already_linked: false,
|
|
38277
|
-
notes
|
|
38278
|
-
};
|
|
38279
|
-
}
|
|
38280
|
-
if (yield* herdr.hasApneaPlugin) {
|
|
38281
|
-
return {
|
|
38282
|
-
copied: opts.destDir,
|
|
38283
|
-
linked: false,
|
|
38284
|
-
already_linked: true,
|
|
38285
|
-
notes
|
|
38286
|
-
};
|
|
38287
|
-
}
|
|
38288
|
-
const linkResult = yield* herdr.linkPlugin(opts.destDir);
|
|
38289
|
-
if (!linkResult.ok) {
|
|
38290
|
-
notes.push(`herdr plugin link failed: ${linkResult.raw.trim() || "(no output)"}`);
|
|
38291
|
-
return {
|
|
38292
|
-
copied: opts.destDir,
|
|
38293
|
-
linked: false,
|
|
38294
|
-
already_linked: false,
|
|
38295
|
-
notes
|
|
38296
|
-
};
|
|
38297
|
-
}
|
|
38298
|
-
return { copied: opts.destDir, linked: true, already_linked: false, notes };
|
|
38299
|
-
});
|
|
38300
38381
|
var setupWorkflow = (params, root, deps) => exports_Effect.gen(function* () {
|
|
38301
38382
|
const fs4 = yield* FileSystem;
|
|
38302
|
-
const herdr = yield* Herdr;
|
|
38303
38383
|
const readJsonSafe = (filePath) => exports_Effect.gen(function* () {
|
|
38304
38384
|
const present = yield* fs4.exists(filePath);
|
|
38305
38385
|
if (!present)
|
|
@@ -38355,18 +38435,6 @@ var setupWorkflow = (params, root, deps) => exports_Effect.gen(function* () {
|
|
|
38355
38435
|
const e = materialized.failure;
|
|
38356
38436
|
missing.push(`role agent dir failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
38357
38437
|
}
|
|
38358
|
-
let herdrPlugin = null;
|
|
38359
|
-
let herdrVer = null;
|
|
38360
|
-
if (has4.herdr) {
|
|
38361
|
-
const version2 = yield* herdr.version;
|
|
38362
|
-
herdrVer = version2 == null ? null : `${version2[0]}.${version2[1]}.${version2[2]}`;
|
|
38363
|
-
herdrPlugin = yield* provisionHerdrPlugin({
|
|
38364
|
-
srcDir: path8.join(packageRoot(), "herdr-plugin"),
|
|
38365
|
-
destDir: path8.join(path8.dirname(globalConfigPath()), "herdr-plugin"),
|
|
38366
|
-
version: version2
|
|
38367
|
-
});
|
|
38368
|
-
missing.push(...herdrPlugin.notes);
|
|
38369
|
-
}
|
|
38370
38438
|
let agentsMdPath = null;
|
|
38371
38439
|
if (params.agents_md) {
|
|
38372
38440
|
const target2 = path8.join(root, "AGENTS.md");
|
|
@@ -38391,14 +38459,6 @@ var setupWorkflow = (params, root, deps) => exports_Effect.gen(function* () {
|
|
|
38391
38459
|
if (params.agents_md) {
|
|
38392
38460
|
data.agents_md = agentsMdPath;
|
|
38393
38461
|
}
|
|
38394
|
-
if (has4.herdr) {
|
|
38395
|
-
data.herdr_version = herdrVer;
|
|
38396
|
-
data.herdr_plugin = herdrPlugin ? {
|
|
38397
|
-
copied: herdrPlugin.copied,
|
|
38398
|
-
linked: herdrPlugin.linked,
|
|
38399
|
-
already_linked: herdrPlugin.already_linked
|
|
38400
|
-
} : null;
|
|
38401
|
-
}
|
|
38402
38462
|
return ok(`wrote global config ${gPath}`, data);
|
|
38403
38463
|
});
|
|
38404
38464
|
|
|
@@ -38487,7 +38547,6 @@ var startWorkflow = (params, root) => exports_Effect.gen(function* () {
|
|
|
38487
38547
|
pending_role: null,
|
|
38488
38548
|
pending_pane_id: null,
|
|
38489
38549
|
pending_pane_label: null,
|
|
38490
|
-
pending_floating_exit: null,
|
|
38491
38550
|
pending_started_at: null,
|
|
38492
38551
|
pending_deadline_ms: null,
|
|
38493
38552
|
pending_nudged_at: null,
|
|
@@ -38682,13 +38741,6 @@ var waitWorkflow = (params, root, hooks = {}) => exports_Effect.gen(function* ()
|
|
|
38682
38741
|
const text = yield* fs4.readFile(artifactAbs);
|
|
38683
38742
|
return parseFrontMatter(text);
|
|
38684
38743
|
});
|
|
38685
|
-
const readFloatingExitCode = (exitAbs) => exports_Effect.gen(function* () {
|
|
38686
|
-
const present = yield* fs4.exists(exitAbs);
|
|
38687
|
-
if (!present)
|
|
38688
|
-
return null;
|
|
38689
|
-
const text = yield* fs4.readFile(exitAbs);
|
|
38690
|
-
return parseFloatingExit(text);
|
|
38691
|
-
});
|
|
38692
38744
|
const advanceOnComplete = (fm, msg = "artifact ready") => exports_Effect.gen(function* () {
|
|
38693
38745
|
if (fm.rework !== undefined && (kind !== "code_review" || fm.verdict !== "CHANGES_REQUIRED")) {
|
|
38694
38746
|
return yield* new ArtifactInvalid({
|
|
@@ -38742,7 +38794,6 @@ var waitWorkflow = (params, root, hooks = {}) => exports_Effect.gen(function* ()
|
|
|
38742
38794
|
state.pending_role = null;
|
|
38743
38795
|
state.pending_pane_id = null;
|
|
38744
38796
|
state.pending_pane_label = null;
|
|
38745
|
-
state.pending_floating_exit = null;
|
|
38746
38797
|
state.pending_started_at = null;
|
|
38747
38798
|
state.pending_deadline_ms = null;
|
|
38748
38799
|
resetRecoveryLadder(state);
|
|
@@ -38758,11 +38809,9 @@ var waitWorkflow = (params, root, hooks = {}) => exports_Effect.gen(function* ()
|
|
|
38758
38809
|
step: next
|
|
38759
38810
|
}, nextAfter(next));
|
|
38760
38811
|
});
|
|
38761
|
-
const floatingFlushMs = 2000;
|
|
38762
38812
|
let lastStatus = "waiting";
|
|
38763
38813
|
let shellOnlyPolls = 0;
|
|
38764
38814
|
let idleSince = null;
|
|
38765
|
-
let floatingExitSeenAt = null;
|
|
38766
38815
|
let nudged = state.pending_nudged_at != null;
|
|
38767
38816
|
let extendedOnce = state.pending_extended;
|
|
38768
38817
|
let finalNudgeGrace = state.pending_final_grace;
|
|
@@ -38805,33 +38854,6 @@ var waitWorkflow = (params, root, hooks = {}) => exports_Effect.gen(function* ()
|
|
|
38805
38854
|
if (isCompleteArtifact(fm, { requireVerdict })) {
|
|
38806
38855
|
return yield* advanceOnComplete(fm, nudged ? "artifact ready after nudge" : "artifact ready");
|
|
38807
38856
|
}
|
|
38808
|
-
if (state.pending_floating_exit) {
|
|
38809
|
-
const exitAbs = abs2(state.pending_floating_exit, root);
|
|
38810
|
-
const code = yield* readFloatingExitCode(exitAbs);
|
|
38811
|
-
if (code != null) {
|
|
38812
|
-
lastStatus = `floating_exit_${code}`;
|
|
38813
|
-
floatingExitSeenAt ??= now2;
|
|
38814
|
-
if (now2 - floatingExitSeenAt >= floatingFlushMs) {
|
|
38815
|
-
const again = yield* readArtifact();
|
|
38816
|
-
if (isCompleteArtifact(again, { requireVerdict })) {
|
|
38817
|
-
return yield* advanceOnComplete(again, "artifact ready (floating oneshot exited)");
|
|
38818
|
-
}
|
|
38819
|
-
state.pending_floating_exit = null;
|
|
38820
|
-
state.last_error = `floating oneshot exited ${code} without ${pendingArtifact}`;
|
|
38821
|
-
yield* store.save(state, root);
|
|
38822
|
-
return yield* new HerdrError({
|
|
38823
|
-
message: `floating ${state.pending_role} exited (code ${code}) without writing ${pendingArtifact}`,
|
|
38824
|
-
details: {
|
|
38825
|
-
exit_code: code,
|
|
38826
|
-
last_agent_status: lastStatus,
|
|
38827
|
-
hint: code === 129 ? "popup received Hangup (dismiss/focus steal) \u2014 re-dispatch same round; keep focus on the popup" : "inspect oneshot output; re-dispatch same round or set pane_style=regular"
|
|
38828
|
-
}
|
|
38829
|
-
});
|
|
38830
|
-
}
|
|
38831
|
-
} else {
|
|
38832
|
-
lastStatus = "floating_running";
|
|
38833
|
-
}
|
|
38834
|
-
}
|
|
38835
38857
|
if ((yield* herdr.enabled) && state.pending_pane_id) {
|
|
38836
38858
|
const info = yield* herdr.paneGet(state.pending_pane_id);
|
|
38837
38859
|
if (!info.ok) {
|