catherd-cli 1.4.0 → 2.0.0-beta.20261022113309
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 +188 -169
- package/THIRD_PARTY_NOTICES.md +217 -35
- package/catalog/jev.json +27 -1
- package/catalog/models.json +56 -239
- package/package.json +6 -8
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/bin/catherd-mcp +1 -1
- package/plugin/commands/catherd.md +2 -2
- package/plugin/skills/catherd/SKILL.md +97 -356
- package/plugin/skills/catherd-setup/SKILL.md +21 -20
- package/src/adapters/all.ts +0 -4
- package/src/adapters/backend.ts +19 -4
- package/src/adapters/claude-code/index.ts +126 -14
- package/src/adapters/codex/events.ts +10 -0
- package/src/adapters/codex/index.ts +17 -5
- package/src/adapters/cursor/home.ts +1 -1
- package/src/adapters/cursor/index.ts +10 -4
- package/src/adapters/opencode/index.ts +38 -1
- package/src/cli.ts +27 -27
- package/src/domain/agents.ts +3 -1
- package/src/domain/bars.ts +33 -107
- package/src/domain/blocked.ts +72 -0
- package/src/domain/branch.ts +51 -0
- package/src/domain/brief.ts +106 -0
- package/src/domain/catalog.ts +69 -313
- package/src/domain/changes.ts +23 -1
- package/src/domain/cost.ts +19 -29
- package/src/domain/errors.ts +6 -2
- package/src/domain/fail-lines.ts +26 -0
- package/src/domain/failover.ts +37 -75
- package/src/domain/findings.ts +47 -0
- package/src/domain/gate-brief.ts +53 -0
- package/src/domain/hints.ts +13 -2
- package/src/domain/ids.ts +13 -2
- package/src/domain/jev.ts +47 -2
- package/src/domain/judge.ts +124 -0
- package/src/domain/lane.ts +118 -3
- package/src/domain/milestone-memory.ts +53 -0
- package/src/domain/notice.ts +32 -8
- package/src/domain/notify.ts +32 -0
- package/src/domain/plan-doc.ts +198 -0
- package/src/domain/plan-lint.ts +141 -0
- package/src/domain/profile-rules.ts +175 -138
- package/src/domain/profile.ts +140 -63
- package/src/domain/record.ts +68 -3
- package/src/domain/reply.ts +29 -0
- package/src/domain/role-prompts.ts +182 -56
- package/src/domain/role-scope.ts +76 -0
- package/src/domain/role-tools.ts +34 -0
- package/src/domain/roles.ts +2 -0
- package/src/domain/route.ts +90 -0
- package/src/domain/rules.ts +33 -0
- package/src/domain/run-events.ts +298 -0
- package/src/domain/run-machine.ts +668 -0
- package/src/domain/run-state.ts +1310 -0
- package/src/domain/scorecard.ts +197 -0
- package/src/domain/select.ts +229 -72
- package/src/domain/slopalytics.ts +72 -0
- package/src/domain/state.ts +5 -0
- package/src/domain/util.ts +22 -0
- package/src/domain/workspace.ts +130 -0
- package/src/entry/answer-command.ts +39 -0
- package/src/entry/catalog-command.ts +24 -165
- package/src/entry/check-bin.ts +17 -0
- package/src/entry/cli-kit.ts +18 -2
- package/src/entry/daemon-bin.ts +22 -0
- package/src/entry/deps.ts +2 -0
- package/src/entry/doctor-command.ts +20 -2
- package/src/entry/glyphs.ts +15 -2
- package/src/entry/host-arg.ts +28 -0
- package/src/entry/init-command.ts +20 -69
- package/src/entry/knowledge-command.ts +121 -5
- package/src/entry/lock-command.ts +103 -8
- package/src/entry/mcp/handshake.ts +6 -2
- package/src/entry/mcp/role-bin.ts +20 -0
- package/src/entry/mcp/role-server.ts +134 -0
- package/src/entry/mcp/server.ts +85 -21
- package/src/entry/mcp/session-tools.ts +149 -0
- package/src/entry/panel/openapi.ts +189 -0
- package/src/entry/panel/server.ts +410 -0
- package/src/entry/panel-command.ts +68 -0
- package/src/entry/pause-command.ts +67 -0
- package/src/entry/profile-command.ts +33 -18
- package/src/entry/role-cli-command.ts +159 -0
- package/src/entry/runs-command.ts +81 -27
- package/src/entry/scorecard-command.ts +187 -0
- package/src/entry/supervise-bin.ts +1 -1
- package/src/entry/supervise-command.ts +30 -3
- package/src/entry/workspace-command.ts +44 -0
- package/src/infra/boot-lock.ts +25 -0
- package/src/infra/delivery.ts +3 -0
- package/src/infra/dispatch-dir.ts +2 -0
- package/src/infra/env.ts +11 -3
- package/src/infra/git.ts +70 -0
- package/src/infra/heavy-lock.ts +133 -6
- package/src/infra/host-probe.ts +16 -0
- package/src/infra/launch.ts +63 -7
- package/src/infra/lock-activity.ts +88 -0
- package/src/infra/login-env.ts +48 -0
- package/src/infra/memory.ts +45 -0
- package/src/infra/ntfy.ts +40 -0
- package/src/infra/open-url.ts +42 -0
- package/src/infra/paths.ts +37 -2
- package/src/infra/role-mcp.ts +69 -0
- package/src/infra/slopalytics.ts +30 -0
- package/src/infra/store.ts +6 -1
- package/src/infra/supervisor.ts +107 -16
- package/src/services/admission.ts +133 -31
- package/src/services/backends.ts +4 -6
- package/src/services/budget.ts +26 -6
- package/src/services/capture.ts +1 -22
- package/src/services/catalog-service.ts +155 -254
- package/src/services/credentials.ts +15 -26
- package/src/services/daemon.ts +181 -0
- package/src/services/dispatch-service.ts +209 -17
- package/src/services/dispatches.ts +30 -2
- package/src/services/doctor-access.ts +8 -3
- package/src/services/doctor-backends.ts +1 -1
- package/src/services/doctor-checks.ts +47 -0
- package/src/services/doctor-docker.ts +221 -0
- package/src/services/doctor-role-mcp.ts +65 -0
- package/src/services/doctor.ts +72 -15
- package/src/services/end-push.ts +157 -0
- package/src/services/failover-rungs.ts +109 -0
- package/src/services/finalize.ts +24 -2
- package/src/services/gate-env.ts +217 -0
- package/src/services/gate-service.ts +231 -45
- package/src/services/global-install.ts +46 -13
- package/src/services/jev-service.ts +2 -4
- package/src/services/lane-edit.ts +154 -0
- package/src/services/lane-service.ts +291 -55
- package/src/services/milestone-check.ts +433 -0
- package/src/services/milestone-tree.ts +170 -0
- package/src/services/milestones.ts +167 -29
- package/src/services/notifier.ts +124 -4
- package/src/services/notify-service.ts +26 -0
- package/src/services/orchestrator-wait.ts +82 -0
- package/src/services/panel-setup.ts +116 -0
- package/src/services/pause.ts +161 -0
- package/src/services/peek.ts +79 -17
- package/src/services/plan-input.ts +151 -0
- package/src/services/ports.ts +60 -5
- package/src/services/preflight.ts +232 -24
- package/src/services/profile-service.ts +31 -3
- package/src/services/profile-store.ts +18 -6
- package/src/services/protocol.ts +123 -25
- package/src/services/provenance.ts +34 -89
- package/src/services/questions.ts +24 -0
- package/src/services/reconcile.ts +33 -1
- package/src/services/reentry.ts +7 -6
- package/src/services/role-access.ts +27 -0
- package/src/services/routing-service.ts +161 -42
- package/src/services/run-briefs.ts +477 -0
- package/src/services/run-client.ts +217 -0
- package/src/services/run-effects.ts +837 -0
- package/src/services/run-events-store.ts +174 -0
- package/src/services/run-evidence.ts +1 -1
- package/src/services/run-pin.ts +125 -0
- package/src/services/run-read.ts +221 -0
- package/src/services/run-report.ts +176 -0
- package/src/services/run-service.ts +204 -23
- package/src/services/run-store.ts +159 -18
- package/src/services/scorecard-metrics.ts +86 -0
- package/src/services/scorecard.ts +382 -0
- package/src/services/scratch.ts +60 -0
- package/src/services/sessions.ts +3 -1
- package/src/services/state.ts +21 -0
- package/src/services/summary.ts +76 -12
- package/src/services/tree-path.ts +27 -0
- package/src/services/verifier-brief.ts +23 -0
- package/src/services/verifier-step.ts +48 -0
- package/src/services/workspace-admission.ts +259 -0
- package/src/services/workspace-service.ts +259 -0
- package/src/services/workspace-store.ts +95 -0
- package/web/dist/assets/Brand-PKlUX5TE.js +1 -0
- package/web/dist/assets/Brand-PKlUX5TE.js.br +0 -0
- package/web/dist/assets/Brand-PKlUX5TE.js.gz +0 -0
- package/web/dist/assets/Modal-Dxc5so53.js +1 -0
- package/web/dist/assets/Modal-Dxc5so53.js.br +0 -0
- package/web/dist/assets/Modal-Dxc5so53.js.gz +0 -0
- package/web/dist/assets/Pressable-C8-T6Ed_.js +1 -0
- package/web/dist/assets/StatusChip-B5xfI_Mn.js +153 -0
- package/web/dist/assets/StatusChip-B5xfI_Mn.js.br +0 -0
- package/web/dist/assets/StatusChip-B5xfI_Mn.js.gz +0 -0
- package/web/dist/assets/_app-Ea5cpyKe.js +1 -0
- package/web/dist/assets/_app-Ea5cpyKe.js.br +0 -0
- package/web/dist/assets/_app-Ea5cpyKe.js.gz +0 -0
- package/web/dist/assets/_runId-pTG0fXVo.js +1 -0
- package/web/dist/assets/_runId-pTG0fXVo.js.br +0 -0
- package/web/dist/assets/_runId-pTG0fXVo.js.gz +0 -0
- package/web/dist/assets/drawer-Du3cdlok.js +1 -0
- package/web/dist/assets/drawer-Du3cdlok.js.br +0 -0
- package/web/dist/assets/drawer-Du3cdlok.js.gz +0 -0
- package/web/dist/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- package/web/dist/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- package/web/dist/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- package/web/dist/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/web/dist/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
- package/web/dist/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
- package/web/dist/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
- package/web/dist/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
- package/web/dist/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
- package/web/dist/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
- package/web/dist/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- package/web/dist/assets/index-B6GiwDkh.js +11 -0
- package/web/dist/assets/index-B6GiwDkh.js.br +0 -0
- package/web/dist/assets/index-B6GiwDkh.js.gz +0 -0
- package/web/dist/assets/index-C5IbylvM.css +2 -0
- package/web/dist/assets/index-C5IbylvM.css.br +0 -0
- package/web/dist/assets/index-C5IbylvM.css.gz +0 -0
- package/web/dist/assets/label-DFSYBc62.js +1 -0
- package/web/dist/assets/label-DFSYBc62.js.br +0 -0
- package/web/dist/assets/label-DFSYBc62.js.gz +0 -0
- package/web/dist/assets/link-D8EcKZUt.js +1 -0
- package/web/dist/assets/link-D8EcKZUt.js.br +0 -0
- package/web/dist/assets/link-D8EcKZUt.js.gz +0 -0
- package/web/dist/assets/login-DaN5HPPW.js +1 -0
- package/web/dist/assets/login-DaN5HPPW.js.br +0 -0
- package/web/dist/assets/login-DaN5HPPW.js.gz +0 -0
- package/web/dist/assets/query-Cu--ccT3.js +1 -0
- package/web/dist/assets/questions-shoO9OVj.js +1 -0
- package/web/dist/assets/questions-shoO9OVj.js.br +0 -0
- package/web/dist/assets/questions-shoO9OVj.js.gz +0 -0
- package/web/dist/assets/react-query.gen-Bu2yV-qJ.js +12 -0
- package/web/dist/assets/react-query.gen-Bu2yV-qJ.js.br +0 -0
- package/web/dist/assets/react-query.gen-Bu2yV-qJ.js.gz +0 -0
- package/web/dist/assets/rolldown-runtime-XKyaHtl9.js +1 -0
- package/web/dist/assets/run-status-C0DRuV7U.js +1 -0
- package/web/dist/assets/runs-C3rg2zZc.js +1 -0
- package/web/dist/assets/runs-C3rg2zZc.js.br +0 -0
- package/web/dist/assets/runs-C3rg2zZc.js.gz +0 -0
- package/web/dist/assets/setup-5I4Vl9fR.js +1 -0
- package/web/dist/assets/setup-5I4Vl9fR.js.br +0 -0
- package/web/dist/assets/setup-5I4Vl9fR.js.gz +0 -0
- package/web/dist/assets/table-BOxXhqS4.js +1 -0
- package/web/dist/assets/table-BOxXhqS4.js.br +0 -0
- package/web/dist/assets/table-BOxXhqS4.js.gz +0 -0
- package/web/dist/assets/textfield-BzRFkxBF.js +1 -0
- package/web/dist/assets/textfield-BzRFkxBF.js.br +0 -0
- package/web/dist/assets/textfield-BzRFkxBF.js.gz +0 -0
- package/web/dist/assets/time--9kSQbbp.js +1 -0
- package/web/dist/assets/use-css-variable-CpnN9WtA.js +5 -0
- package/web/dist/assets/use-css-variable-CpnN9WtA.js.br +0 -0
- package/web/dist/assets/use-css-variable-CpnN9WtA.js.gz +0 -0
- package/web/dist/assets/useMutation-CVPKlR34.js +1 -0
- package/web/dist/assets/useMutation-CVPKlR34.js.br +0 -0
- package/web/dist/assets/useMutation-CVPKlR34.js.gz +0 -0
- package/web/dist/index.html +15 -0
- package/catalog/ATTRIBUTION.md +0 -36
- package/catalog/scores.json +0 -3202
- package/catalog/sources.json +0 -97
- package/src/adapters/antigravity/events.ts +0 -131
- package/src/adapters/antigravity/home.ts +0 -46
- package/src/adapters/antigravity/index.ts +0 -320
- package/src/adapters/antigravity/models.ts +0 -37
- package/src/adapters/grok/events.ts +0 -112
- package/src/adapters/grok/home.ts +0 -132
- package/src/adapters/grok/index.ts +0 -339
- package/src/adapters/grok/models.ts +0 -48
- package/src/domain/calibration.ts +0 -125
- package/src/domain/sources.ts +0 -144
- package/src/entry/mcp/dispatch-tools.ts +0 -52
- package/src/entry/mcp/lane-tools.ts +0 -84
- package/src/entry/mcp/protocol-tools.ts +0 -57
- package/src/entry/mcp/run-tools.ts +0 -148
- package/src/entry/mcp/setup-tools.ts +0 -108
- package/src/entry/tui/commands.ts +0 -563
- package/src/entry/tui/effects.ts +0 -381
- package/src/entry/tui/fixtures.ts +0 -637
- package/src/entry/tui/profile-edits.ts +0 -146
- package/src/entry/tui/profile-tree.ts +0 -429
- package/src/entry/tui/providers/app.tsx +0 -162
- package/src/entry/tui/providers/boundary.tsx +0 -57
- package/src/entry/tui/providers/data.tsx +0 -160
- package/src/entry/tui/providers/keymap.tsx +0 -166
- package/src/entry/tui/providers/root.tsx +0 -45
- package/src/entry/tui/providers/theme.tsx +0 -17
- package/src/entry/tui/providers/toast.tsx +0 -77
- package/src/entry/tui/run.tsx +0 -114
- package/src/entry/tui/state.ts +0 -373
- package/src/entry/tui/stories.ts +0 -114
- package/src/entry/tui/text.ts +0 -130
- package/src/entry/tui/theme.ts +0 -140
- package/src/entry/tui/views/app.tsx +0 -208
- package/src/entry/tui/views/dialogs.tsx +0 -41
- package/src/entry/tui/views/profile-actions.ts +0 -331
- package/src/entry/tui/views/profiles.tsx +0 -520
- package/src/entry/tui/views/runs.tsx +0 -573
- package/src/entry/tui/views/save-dialog.tsx +0 -340
- package/src/entry/tui/views/status.tsx +0 -209
- package/src/entry/tui/widgets/chrome.tsx +0 -114
- package/src/entry/tui/widgets/dialog-confirm.tsx +0 -58
- package/src/entry/tui/widgets/dialog-prompt.tsx +0 -52
- package/src/entry/tui/widgets/dialog-select.tsx +0 -212
- package/src/entry/tui/widgets/dialog.tsx +0 -68
- package/src/entry/tui/widgets/line.tsx +0 -92
- package/src/entry/tui/widgets/list.tsx +0 -197
- package/src/entry/tui/widgets/spinner.tsx +0 -49
- package/src/entry/tui/widgets/toast.tsx +0 -62
- package/src/infra/sources/arena.ts +0 -87
- package/src/infra/sources/artificial-analysis.ts +0 -112
- package/src/infra/sources/cache.ts +0 -124
- package/src/infra/sources/csv.ts +0 -38
- package/src/infra/sources/epoch.ts +0 -82
- package/src/infra/sources/http.ts +0 -75
- package/src/infra/sources/litellm.ts +0 -58
- package/src/infra/sources/models-dev.ts +0 -59
- package/src/infra/sources/openrouter-endpoints.ts +0 -52
- package/src/infra/sources/openrouter-models.ts +0 -60
- package/src/infra/sources/rows.ts +0 -38
- package/src/infra/sources/vectara.ts +0 -69
- package/src/infra/sources/zip.ts +0 -48
- package/src/services/catalog-refresh.ts +0 -201
- package/src/services/doctor-sources.ts +0 -76
- package/src/services/runs-page.ts +0 -255
- package/src/services/source-derive.ts +0 -374
- package/src/services/source-sync.ts +0 -305
- package/src/services/standins.ts +0 -204
- package/src/services/treat-likes.ts +0 -97
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { CatherdError } from "../domain/errors.ts";
|
|
4
|
+
import type { ProfilePatch } from "../domain/profile.ts";
|
|
5
|
+
import { isApproval } from "../domain/plan-doc.ts";
|
|
6
|
+
import { ROLES } from "../domain/roles.ts";
|
|
7
|
+
import { RUN_UNIT } from "../domain/run-events.ts";
|
|
8
|
+
import { liveDispatches } from "../domain/run-state.ts";
|
|
9
|
+
import { fetchBase, git, gitToplevel } from "../infra/git.ts";
|
|
10
|
+
import { writeTextAtomic } from "../infra/store.ts";
|
|
11
|
+
import { ensureDaemon } from "./daemon.ts";
|
|
12
|
+
import type { Deps } from "./ports.ts";
|
|
13
|
+
import { copyPlanInputs, planInputPaths, resolvePlanInputs } from "./plan-input.ts";
|
|
14
|
+
import { answer, openQuestions } from "./questions.ts";
|
|
15
|
+
import { appendEvent, isDaemonRun, readEvents, replayRun, withEventsLock } from "./run-events-store.ts";
|
|
16
|
+
import { runProfile } from "./run-pin.ts";
|
|
17
|
+
import { findRun, type Run } from "./run-store.ts";
|
|
18
|
+
import { result, startRun } from "./run-service.ts";
|
|
19
|
+
|
|
20
|
+
/** What the session's tools, the panel and the CLI share: they only append events and read the run's state. */
|
|
21
|
+
|
|
22
|
+
const readText = (file: string): string => {
|
|
23
|
+
try {
|
|
24
|
+
return readFileSync(file, "utf8");
|
|
25
|
+
} catch {
|
|
26
|
+
return "";
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/** A 1.5 run has no daemon: this client neither answers nor cancels it. */
|
|
31
|
+
function daemonRun(id: string): Run {
|
|
32
|
+
const run = findRun(id);
|
|
33
|
+
if (!isDaemonRun(run))
|
|
34
|
+
throw new CatherdError("E_RUN_NOT_LIVE", `run ${id} is a 1.5 run: catherd 2.0 does not drive it`, {
|
|
35
|
+
fix: "finish it with catherd 1.x",
|
|
36
|
+
});
|
|
37
|
+
return run;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Fetches `origin/<base>` and refuses a base origin does not have (E_INPUT_INVALID); works from the last fetch offline. */
|
|
41
|
+
async function assertBase(repo: string, base: string): Promise<void> {
|
|
42
|
+
await fetchBase(repo, base);
|
|
43
|
+
const r = await git(repo, ["rev-parse", "--verify", "--quiet", `origin/${base}^{commit}`]);
|
|
44
|
+
if (r.kind !== "ok")
|
|
45
|
+
throw new CatherdError("E_INPUT_INVALID", `origin/${base} does not exist in ${repo}`, {
|
|
46
|
+
fix: "pass a branch origin has, or omit base",
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Starts a 2.0 run: its folder, its pin, `run.started`, and its daemon. The owner's plan files (`plan`, and any
|
|
52
|
+
* `plan:` A-line) are checked before the run exists, copied into it, and approved as input.
|
|
53
|
+
*/
|
|
54
|
+
export async function runStart(
|
|
55
|
+
deps: Deps,
|
|
56
|
+
i: { repo: string; title: string; aLines: string[]; plan?: string[]; base?: string },
|
|
57
|
+
): Promise<{
|
|
58
|
+
run: string;
|
|
59
|
+
dir: string;
|
|
60
|
+
daemon: "revived" | "alive";
|
|
61
|
+
notifyUrl: string;
|
|
62
|
+
plan: string[];
|
|
63
|
+
next: string;
|
|
64
|
+
}> {
|
|
65
|
+
const top = await gitToplevel(i.repo);
|
|
66
|
+
// a repo outside git is startRun's refusal
|
|
67
|
+
const plan = top ? resolvePlanInputs(top, [...(i.plan ?? []), ...planInputPaths(i.aLines)]) : [];
|
|
68
|
+
// a base run's branch must exist on origin before any run folder does
|
|
69
|
+
if (i.base && top) await assertBase(top, i.base);
|
|
70
|
+
const started = await startRun(deps, { repo: i.repo, title: i.title, aLines: i.aLines, base: i.base });
|
|
71
|
+
const run = findRun(started.run);
|
|
72
|
+
copyPlanInputs(run, plan);
|
|
73
|
+
await appendEvent(
|
|
74
|
+
run,
|
|
75
|
+
{
|
|
76
|
+
type: "run.started",
|
|
77
|
+
repo: run.meta.repo,
|
|
78
|
+
title: i.title,
|
|
79
|
+
aLines: i.aLines,
|
|
80
|
+
plan,
|
|
81
|
+
base: i.base ?? null,
|
|
82
|
+
engine: "2.0",
|
|
83
|
+
rolesOff: ROLES.filter((r) => !runProfile(deps, run).roles[r]?.enabled),
|
|
84
|
+
},
|
|
85
|
+
new Date(deps.now()),
|
|
86
|
+
);
|
|
87
|
+
if (plan.length) await appendEvent(run, { type: "plan.approved", by: "input" }, new Date(deps.now()));
|
|
88
|
+
const notifyUrl = deps.profiles.notifyUrl();
|
|
89
|
+
const daemon = ensureDaemon(run) === "alive" ? "alive" : "revived";
|
|
90
|
+
return {
|
|
91
|
+
run: run.id,
|
|
92
|
+
dir: run.dir,
|
|
93
|
+
daemon,
|
|
94
|
+
notifyUrl,
|
|
95
|
+
plan,
|
|
96
|
+
next: plan.length
|
|
97
|
+
? `the run implements ${plan.length} plan file${plan.length === 1 ? "" : "s"} as written: status(run) shows it; parked questions reach ${notifyUrl} and this session`
|
|
98
|
+
: `the architect writes a plan first, and the run waits for your approval (answer approve): status(run) shows it; parked questions reach ${notifyUrl} and this session`,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Answers a parked unit: the owner's words reach the daemon as `question.answered`. */
|
|
103
|
+
export async function answerUnit(
|
|
104
|
+
deps: Deps,
|
|
105
|
+
i: { run: string; unit: string; answer: string },
|
|
106
|
+
): Promise<{ unit: string; parked: string[] }> {
|
|
107
|
+
const run = daemonRun(i.run);
|
|
108
|
+
// the check and the append are one step: two answers to one question never both get through
|
|
109
|
+
await withEventsLock(run, async (add) => {
|
|
110
|
+
const state = replayRun(run);
|
|
111
|
+
const open = Object.keys(state.questions);
|
|
112
|
+
if (!open.includes(i.unit))
|
|
113
|
+
throw new CatherdError("E_INPUT_INVALID", `${i.unit} has no open question`, {
|
|
114
|
+
fix: open.length
|
|
115
|
+
? `the open ones: ${open.join(", ")}`
|
|
116
|
+
: "status(run) shows the run's state: nothing waits for an answer now",
|
|
117
|
+
});
|
|
118
|
+
// the owner answers when they have fixed the plan: the roles read the copies, so copy again
|
|
119
|
+
if (state.plan.length) copyPlanInputs(run, state.plan);
|
|
120
|
+
// a bare approving word approves the plan (the first approval or a re-approval): reduce closes the question
|
|
121
|
+
if (i.unit === RUN_UNIT && state.questions[RUN_UNIT]?.reason === "approval" && isApproval(i.answer)) {
|
|
122
|
+
writeTextAtomic(join(run.dir, "plan-approved.md"), readText(join(run.dir, "plan.md")));
|
|
123
|
+
add({ type: "plan.approved", by: "owner" }, new Date(deps.now()));
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
// a milestone's park is in questions.jsonl too, which land's gate reads
|
|
127
|
+
if (openQuestions(run).some((q) => q.milestone === i.unit))
|
|
128
|
+
await answer(deps, { run: run.id, milestone: i.unit, answer: i.answer });
|
|
129
|
+
add({ type: "question.answered", unit: i.unit, answer: i.answer, by: "owner" }, new Date(deps.now()));
|
|
130
|
+
});
|
|
131
|
+
ensureDaemon(run);
|
|
132
|
+
return { unit: i.unit, parked: Object.keys(replayRun(run).questions) };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Cancels the run; asking twice appends nothing. The daemon stops every live role, then ends the run. */
|
|
136
|
+
export async function cancelRun(
|
|
137
|
+
deps: Deps,
|
|
138
|
+
i: { run: string },
|
|
139
|
+
): Promise<{ cancelling: true; live: string[] }> {
|
|
140
|
+
const run = daemonRun(i.run);
|
|
141
|
+
const s = replayRun(run);
|
|
142
|
+
const live = liveDispatches(s).map((d) => d.name);
|
|
143
|
+
if (!s.cancelling && s.phase !== "ended") {
|
|
144
|
+
await appendEvent(run, { type: "cancel.requested", by: "owner" }, new Date(deps.now()));
|
|
145
|
+
ensureDaemon(run);
|
|
146
|
+
}
|
|
147
|
+
return { cancelling: true, live };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export interface RunResult {
|
|
151
|
+
phase: ReturnType<typeof replayRun>["phase"];
|
|
152
|
+
outcome: ReturnType<typeof replayRun>["outcome"];
|
|
153
|
+
reportPath: string | null;
|
|
154
|
+
report: string | null;
|
|
155
|
+
milestones: {
|
|
156
|
+
id: string;
|
|
157
|
+
status: string;
|
|
158
|
+
commit: string | null;
|
|
159
|
+
digestPath: string | null;
|
|
160
|
+
knownIssues: string[];
|
|
161
|
+
}[];
|
|
162
|
+
/** with a name: the role's record, as 1.5's `result(run, name)` gives it */
|
|
163
|
+
role?: unknown;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** The run's outcome and report; with a role's name, that role's record too. */
|
|
167
|
+
export async function runResult(deps: Deps, i: { run: string; name?: string }): Promise<RunResult> {
|
|
168
|
+
const run = daemonRun(i.run);
|
|
169
|
+
const s = replayRun(run);
|
|
170
|
+
const digests = new Map(
|
|
171
|
+
readEvents(run).flatMap((e) =>
|
|
172
|
+
e.type === "milestone.landed" ? [[e.milestone, e.digestPath] as const] : [],
|
|
173
|
+
),
|
|
174
|
+
);
|
|
175
|
+
const file = join(run.dir, "report.md");
|
|
176
|
+
const has = existsSync(file);
|
|
177
|
+
return {
|
|
178
|
+
phase: s.phase,
|
|
179
|
+
outcome: s.outcome,
|
|
180
|
+
reportPath: has ? file : null,
|
|
181
|
+
report: has ? readFileSync(file, "utf8") : null,
|
|
182
|
+
milestones: s.milestones.map((m) => ({
|
|
183
|
+
id: m.id,
|
|
184
|
+
status: m.status,
|
|
185
|
+
commit: m.commit,
|
|
186
|
+
digestPath: digests.get(m.id) ?? null,
|
|
187
|
+
knownIssues: m.knownIssues,
|
|
188
|
+
})),
|
|
189
|
+
...(i.name === undefined ? {} : { role: await result(deps, { run: run.id, name: i.name }) }),
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** The one `profile` tool: get, validate or set a profile, and the phone's topic url. */
|
|
194
|
+
export function profileTool(
|
|
195
|
+
deps: Deps,
|
|
196
|
+
i: {
|
|
197
|
+
action: "get" | "validate" | "set";
|
|
198
|
+
name?: string;
|
|
199
|
+
repo?: string;
|
|
200
|
+
patch?: ProfilePatch;
|
|
201
|
+
notifyUrl?: string;
|
|
202
|
+
},
|
|
203
|
+
): unknown {
|
|
204
|
+
const repo = i.repo ?? null;
|
|
205
|
+
if (i.notifyUrl !== undefined) {
|
|
206
|
+
deps.profiles.setNotifyUrl(i.notifyUrl);
|
|
207
|
+
if (i.action === "set" && i.patch === undefined) return { notifyUrl: deps.profiles.notifyUrl() };
|
|
208
|
+
}
|
|
209
|
+
switch (i.action) {
|
|
210
|
+
case "get":
|
|
211
|
+
return deps.profiles.get(i.name, repo);
|
|
212
|
+
case "validate":
|
|
213
|
+
return deps.profiles.validate(i.name, repo);
|
|
214
|
+
case "set":
|
|
215
|
+
return deps.profiles.set(i.name, i.patch ?? {}, repo);
|
|
216
|
+
}
|
|
217
|
+
}
|