catherd-cli 1.5.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 +164 -208
- package/THIRD_PARTY_NOTICES.md +217 -35
- package/catalog/jev.json +27 -1
- package/catalog/models.json +56 -247
- 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 -421
- package/plugin/skills/catherd-setup/SKILL.md +20 -19
- package/src/adapters/all.ts +0 -4
- package/src/adapters/backend.ts +1 -2
- package/src/adapters/cursor/home.ts +1 -1
- package/src/adapters/cursor/index.ts +4 -4
- package/src/cli.ts +22 -27
- 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 +21 -6
- package/src/domain/catalog.ts +69 -324
- package/src/domain/cost.ts +15 -30
- package/src/domain/errors.ts +2 -2
- package/src/domain/fail-lines.ts +26 -0
- package/src/domain/failover.ts +37 -87
- package/src/domain/findings.ts +47 -0
- package/src/domain/gate-brief.ts +1 -0
- 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 +22 -3
- package/src/domain/milestone-memory.ts +53 -0
- package/src/domain/notice.ts +25 -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 +101 -160
- package/src/domain/profile.ts +116 -63
- package/src/domain/record.ts +53 -2
- package/src/domain/role-prompts.ts +131 -45
- package/src/domain/role-scope.ts +4 -34
- package/src/domain/role-tools.ts +2 -2
- package/src/domain/roles.ts +2 -0
- package/src/domain/route.ts +17 -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 +151 -146
- package/src/domain/slopalytics.ts +72 -0
- package/src/entry/answer-command.ts +39 -0
- package/src/entry/catalog-command.ts +25 -174
- package/src/entry/check-bin.ts +17 -0
- package/src/entry/cli-kit.ts +1 -1
- package/src/entry/daemon-bin.ts +22 -0
- package/src/entry/glyphs.ts +15 -2
- package/src/entry/init-command.ts +18 -68
- package/src/entry/knowledge-command.ts +45 -4
- package/src/entry/lock-command.ts +6 -7
- package/src/entry/mcp/role-server.ts +18 -0
- package/src/entry/mcp/server.ts +13 -40
- 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/profile-command.ts +10 -14
- package/src/entry/role-cli-command.ts +47 -3
- package/src/entry/runs-command.ts +3 -19
- package/src/entry/scorecard-command.ts +187 -0
- package/src/entry/supervise-bin.ts +1 -1
- package/src/infra/env.ts +0 -2
- package/src/infra/git.ts +64 -0
- package/src/infra/heavy-lock.ts +45 -7
- package/src/infra/launch.ts +63 -7
- 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 +2 -2
- package/src/infra/slopalytics.ts +30 -0
- package/src/infra/store.ts +6 -1
- package/src/services/admission.ts +24 -9
- package/src/services/backends.ts +4 -6
- package/src/services/capture.ts +1 -22
- package/src/services/catalog-service.ts +155 -262
- package/src/services/credentials.ts +4 -20
- package/src/services/daemon.ts +181 -0
- package/src/services/dispatch-service.ts +5 -1
- package/src/services/doctor-backends.ts +1 -1
- package/src/services/doctor-checks.ts +47 -0
- package/src/services/doctor.ts +4 -9
- package/src/services/failover-rungs.ts +109 -0
- package/src/services/finalize.ts +12 -0
- package/src/services/gate-env.ts +1 -2
- package/src/services/gate-service.ts +21 -6
- package/src/services/jev-service.ts +2 -4
- package/src/services/lane-service.ts +34 -3
- package/src/services/milestone-check.ts +433 -0
- package/src/services/milestone-tree.ts +170 -0
- package/src/services/milestones.ts +35 -8
- package/src/services/notifier.ts +69 -2
- package/src/services/notify-service.ts +26 -0
- package/src/services/panel-setup.ts +116 -0
- package/src/services/plan-input.ts +151 -0
- package/src/services/ports.ts +30 -4
- package/src/services/preflight.ts +82 -8
- package/src/services/profile-service.ts +30 -3
- package/src/services/profile-store.ts +8 -3
- package/src/services/protocol.ts +4 -0
- package/src/services/provenance.ts +31 -90
- package/src/services/questions.ts +9 -0
- package/src/services/reconcile.ts +11 -0
- package/src/services/routing-service.ts +26 -15
- 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-read.ts +221 -0
- package/src/services/run-report.ts +176 -0
- package/src/services/run-service.ts +55 -6
- package/src/services/run-store.ts +54 -7
- package/src/services/scorecard-metrics.ts +86 -0
- package/src/services/scorecard.ts +382 -0
- package/src/services/summary.ts +27 -3
- package/src/services/tree-path.ts +27 -0
- package/src/services/workspace-admission.ts +5 -13
- package/src/services/workspace-service.ts +5 -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 -44
- 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 -335
- package/src/adapters/antigravity/models.ts +0 -37
- package/src/adapters/grok/events.ts +0 -112
- package/src/adapters/grok/home.ts +0 -187
- package/src/adapters/grok/index.ts +0 -358
- package/src/adapters/grok/models.ts +0 -48
- package/src/domain/calibration.ts +0 -132
- package/src/domain/sources.ts +0 -144
- package/src/entry/mcp/dispatch-tools.ts +0 -52
- package/src/entry/mcp/lane-tools.ts +0 -131
- package/src/entry/mcp/protocol-tools.ts +0 -63
- package/src/entry/mcp/push-tools.ts +0 -16
- package/src/entry/mcp/run-tools.ts +0 -164
- package/src/entry/mcp/setup-tools.ts +0 -109
- package/src/entry/mcp/workspace-tools.ts +0 -142
- package/src/entry/tui/commands.ts +0 -563
- package/src/entry/tui/effects.ts +0 -385
- 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 -400
- 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 -619
- package/src/entry/tui/views/save-dialog.tsx +0 -344
- package/src/entry/tui/views/status.tsx +0 -213
- 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 -95
- package/src/infra/sources/artificial-analysis.ts +0 -119
- package/src/infra/sources/cache.ts +0 -126
- 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 -290
- package/src/services/source-derive.ts +0 -397
- package/src/services/source-sync.ts +0 -312
- package/src/services/standins.ts +0 -238
- package/src/services/treat-likes.ts +0 -113
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
import {
|
|
2
|
+
copyFileSync,
|
|
3
|
+
existsSync,
|
|
4
|
+
mkdirSync,
|
|
5
|
+
readdirSync,
|
|
6
|
+
readFileSync,
|
|
7
|
+
realpathSync,
|
|
8
|
+
rmSync,
|
|
9
|
+
writeFileSync,
|
|
10
|
+
} from "node:fs";
|
|
11
|
+
import { dirname, join, resolve } from "node:path";
|
|
12
|
+
import { z } from "zod";
|
|
13
|
+
import { CatherdError } from "../domain/errors.ts";
|
|
14
|
+
import { latestOutcomes } from "../domain/route.ts";
|
|
15
|
+
import {
|
|
16
|
+
assertLabel,
|
|
17
|
+
SCORECARD_TASKS,
|
|
18
|
+
assertScorecardRows,
|
|
19
|
+
type ScorecardFile,
|
|
20
|
+
ScorecardFileSchema,
|
|
21
|
+
type ScorecardTask,
|
|
22
|
+
ScorecardTaskSchema,
|
|
23
|
+
type ScorecardTaskId,
|
|
24
|
+
type TaskScore,
|
|
25
|
+
} from "../domain/scorecard.ts";
|
|
26
|
+
import { assetPath } from "../infra/assets.ts";
|
|
27
|
+
import { git, gitHead } from "../infra/git.ts";
|
|
28
|
+
import { dataDir } from "../infra/paths.ts";
|
|
29
|
+
import { ensurePrivateDir, readVersioned, writeJsonAtomic } from "../infra/store.ts";
|
|
30
|
+
import { landedMilestones, reviewRounds } from "./milestones.ts";
|
|
31
|
+
import { checkEnvFor, runCheck } from "./preflight.ts";
|
|
32
|
+
import { runMilestones } from "./protocol.ts";
|
|
33
|
+
import { openQuestions } from "./questions.ts";
|
|
34
|
+
import {
|
|
35
|
+
findRun,
|
|
36
|
+
listRuns,
|
|
37
|
+
readAgentRuns,
|
|
38
|
+
readOutcomes,
|
|
39
|
+
readRecords,
|
|
40
|
+
type Run,
|
|
41
|
+
supersededBy,
|
|
42
|
+
} from "./run-store.ts";
|
|
43
|
+
import { failoverRecovered, interventionsOf, pausedMs, runEnd } from "./scorecard-metrics.ts";
|
|
44
|
+
import { quotaByBackend } from "./summary.ts";
|
|
45
|
+
|
|
46
|
+
// Plan 28: prepare a scorecard task's reproducible starting point, score the run that ended there, store the row.
|
|
47
|
+
|
|
48
|
+
/** The checkout's `scorecard/` folder, or null for an installed package (it ships no fixtures). */
|
|
49
|
+
export const scorecardRoot = (): string | null => {
|
|
50
|
+
const root = assetPath("scorecard");
|
|
51
|
+
return existsSync(root) ? root : null;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const defaultPreparedRoot = (): string => join(dataDir(), "scorecard");
|
|
55
|
+
|
|
56
|
+
const minutes = (ms: number): number => Math.round(ms / 6000) / 10;
|
|
57
|
+
|
|
58
|
+
export function loadTask(id: string, root: string): ScorecardTask {
|
|
59
|
+
if (!(SCORECARD_TASKS as readonly string[]).includes(id))
|
|
60
|
+
throw new CatherdError("E_INPUT_INVALID", `no scorecard task ${id}`, {
|
|
61
|
+
fix: `catherd scorecard prepare ${SCORECARD_TASKS.join("|")}`,
|
|
62
|
+
});
|
|
63
|
+
const file = join(root, "tasks", id, "task.json");
|
|
64
|
+
const task = readVersioned(file, ScorecardTaskSchema, 1);
|
|
65
|
+
if (task.id !== id)
|
|
66
|
+
throw new CatherdError("E_CONFIG_INVALID", `${file} names task ${task.id}, not ${id}`, {
|
|
67
|
+
fix: `set "id" to ${id} in ${file}`,
|
|
68
|
+
});
|
|
69
|
+
return task;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const PreparedSchema = z.looseObject({
|
|
73
|
+
schema: z.literal(1),
|
|
74
|
+
task: z.enum(SCORECARD_TASKS),
|
|
75
|
+
dir: z.string(),
|
|
76
|
+
repo: z.string(),
|
|
77
|
+
plan: z.string(),
|
|
78
|
+
startCommit: z.string(),
|
|
79
|
+
limits: z.string().nullable(),
|
|
80
|
+
shim: z.string().nullable(),
|
|
81
|
+
preparedAt: z.string(),
|
|
82
|
+
prompt: z.string(),
|
|
83
|
+
});
|
|
84
|
+
export type Prepared = z.infer<typeof PreparedSchema> & { task: ScorecardTaskId };
|
|
85
|
+
|
|
86
|
+
/** A URL (`scheme://`) or an scp-like `user@host:path`: anything else is a local path. */
|
|
87
|
+
const REMOTE = /^(?:[a-z][a-z0-9+.-]*:\/\/|[^/\s]+@[^/\s:]+:)/i;
|
|
88
|
+
|
|
89
|
+
const GIT_LONG = 30 * 60_000;
|
|
90
|
+
|
|
91
|
+
/** A git command that must succeed: its failure is `fail`'s error. */
|
|
92
|
+
async function must(
|
|
93
|
+
repo: string,
|
|
94
|
+
args: string[],
|
|
95
|
+
fail: () => CatherdError,
|
|
96
|
+
timeoutMs?: number,
|
|
97
|
+
): Promise<string> {
|
|
98
|
+
const r = await git(repo, args, timeoutMs);
|
|
99
|
+
if (r.kind !== "ok") throw fail();
|
|
100
|
+
return r.out;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export async function prepareTask(i: {
|
|
104
|
+
task: ScorecardTask;
|
|
105
|
+
root: string;
|
|
106
|
+
into: string;
|
|
107
|
+
source?: string;
|
|
108
|
+
now: Date;
|
|
109
|
+
setup: boolean;
|
|
110
|
+
}): Promise<Prepared> {
|
|
111
|
+
const { task } = i;
|
|
112
|
+
const given = i.source ?? task.source;
|
|
113
|
+
if (!given)
|
|
114
|
+
throw new CatherdError("E_INPUT_INVALID", `${task.id} names no source`, {
|
|
115
|
+
fix: `catherd scorecard prepare ${task.id} --source <path or URL>`,
|
|
116
|
+
});
|
|
117
|
+
// the clone runs inside the new folder, so a local path must be absolute before it gets there
|
|
118
|
+
const src = REMOTE.test(given) ? given : resolve(given);
|
|
119
|
+
ensurePrivateDir(dirname(i.into));
|
|
120
|
+
try {
|
|
121
|
+
mkdirSync(i.into);
|
|
122
|
+
} catch (e) {
|
|
123
|
+
if ((e as NodeJS.ErrnoException).code === "EEXIST")
|
|
124
|
+
throw new CatherdError("E_INPUT_INVALID", `${i.into} already exists`, {
|
|
125
|
+
fix: "prepare again: the folder name carries the time",
|
|
126
|
+
});
|
|
127
|
+
throw e;
|
|
128
|
+
}
|
|
129
|
+
// the run's own repo toplevel is the real path; findRun and listRuns key runs by it
|
|
130
|
+
const into = realpathSync(i.into);
|
|
131
|
+
const repo = join(into, "repo");
|
|
132
|
+
const io = (what: string) => () =>
|
|
133
|
+
new CatherdError("E_IO_UNEXPECTED", `${what} failed in ${into}`, {
|
|
134
|
+
fix: `check that git works in ${into}`,
|
|
135
|
+
});
|
|
136
|
+
await must(
|
|
137
|
+
into,
|
|
138
|
+
["clone", "--quiet", "--no-checkout", src, "repo"],
|
|
139
|
+
() =>
|
|
140
|
+
new CatherdError("E_INPUT_INVALID", `cannot clone ${src}`, {
|
|
141
|
+
fix: `catherd scorecard prepare ${task.id} --source <path or URL>`,
|
|
142
|
+
}),
|
|
143
|
+
GIT_LONG,
|
|
144
|
+
);
|
|
145
|
+
await must(
|
|
146
|
+
repo,
|
|
147
|
+
["checkout", "--quiet", "-b", `scorecard/${task.id}`, task.commit],
|
|
148
|
+
() =>
|
|
149
|
+
new CatherdError("E_INPUT_INVALID", `${src} has no commit ${task.commit}`, {
|
|
150
|
+
fix: `catherd scorecard prepare ${task.id} --source <a clone that has it>`,
|
|
151
|
+
}),
|
|
152
|
+
);
|
|
153
|
+
await must(repo, ["remote", "remove", "origin"], io("git remote remove"));
|
|
154
|
+
|
|
155
|
+
if (task.inject) {
|
|
156
|
+
for (const e of task.inject.edits) {
|
|
157
|
+
const file = join(repo, e.path);
|
|
158
|
+
const text = existsSync(file) ? readFileSync(file, "utf8") : "";
|
|
159
|
+
const parts = text.split(e.find);
|
|
160
|
+
if (parts.length !== 2)
|
|
161
|
+
throw new CatherdError(
|
|
162
|
+
"E_CONFIG_INVALID",
|
|
163
|
+
`${e.path}: the inject text is there ${parts.length - 1} times, not once`,
|
|
164
|
+
{
|
|
165
|
+
fix: `update scorecard/tasks/${task.id}/task.json`,
|
|
166
|
+
},
|
|
167
|
+
);
|
|
168
|
+
writeFileSync(file, parts.join(e.replace));
|
|
169
|
+
}
|
|
170
|
+
await must(repo, ["add", "-A"], io("git add"));
|
|
171
|
+
await must(
|
|
172
|
+
repo,
|
|
173
|
+
[
|
|
174
|
+
"-c",
|
|
175
|
+
"user.name=catherd-scorecard",
|
|
176
|
+
"-c",
|
|
177
|
+
"user.email=scorecard@catherd.invalid",
|
|
178
|
+
"commit",
|
|
179
|
+
"--quiet",
|
|
180
|
+
"-m",
|
|
181
|
+
task.inject.message,
|
|
182
|
+
],
|
|
183
|
+
io("git commit"),
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
let plan: string;
|
|
188
|
+
if ("file" in task.plan) {
|
|
189
|
+
plan = join(into, "plan.md");
|
|
190
|
+
copyFileSync(join(i.root, "tasks", task.id, task.plan.file), plan);
|
|
191
|
+
} else {
|
|
192
|
+
plan = join(repo, task.plan.repoPath);
|
|
193
|
+
if (!existsSync(plan))
|
|
194
|
+
throw new CatherdError("E_CONFIG_INVALID", `${task.plan.repoPath} is not in ${src} at ${task.commit}`, {
|
|
195
|
+
fix: `update scorecard/tasks/${task.id}/task.json`,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
let limits: string | null = null;
|
|
200
|
+
if (task.limits > 0) {
|
|
201
|
+
limits = join(into, "limits");
|
|
202
|
+
mkdirSync(join(limits, "taken"), { recursive: true });
|
|
203
|
+
mkdirSync(join(limits, "tokens"));
|
|
204
|
+
for (let n = 1; n <= task.limits; n++)
|
|
205
|
+
writeFileSync(join(limits, "tokens", String(n)), "", { flag: "w" });
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (i.setup) {
|
|
209
|
+
const env = await checkEnvFor(repo);
|
|
210
|
+
for (const cmd of task.setup) {
|
|
211
|
+
const r = await runCheck(repo, cmd, GIT_LONG, env);
|
|
212
|
+
if (r.code !== 0 || r.timedOut)
|
|
213
|
+
throw new CatherdError(
|
|
214
|
+
"E_IO_UNEXPECTED",
|
|
215
|
+
`setup \`${cmd}\` failed in ${repo}: ${r.tail.at(-1) ?? "no output"}`,
|
|
216
|
+
{
|
|
217
|
+
fix: `fix it and run it in ${repo}, or prepare again`,
|
|
218
|
+
},
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const startCommit = (await must(repo, ["rev-parse", "HEAD"], io("git rev-parse"))).trim();
|
|
224
|
+
const prepared: Prepared = {
|
|
225
|
+
schema: 1,
|
|
226
|
+
task: task.id,
|
|
227
|
+
dir: into,
|
|
228
|
+
repo,
|
|
229
|
+
plan,
|
|
230
|
+
startCommit,
|
|
231
|
+
limits,
|
|
232
|
+
shim: limits ? join(i.root, "shim") : null,
|
|
233
|
+
preparedAt: i.now.toISOString(),
|
|
234
|
+
prompt: task.prompt.replaceAll("{plan}", () => plan).replaceAll("{acceptance}", () => task.acceptance),
|
|
235
|
+
};
|
|
236
|
+
writeJsonAtomic(join(into, "prepared.json"), prepared);
|
|
237
|
+
return prepared;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export function readPrepared(dir: string): Prepared {
|
|
241
|
+
const file = join(dir, "prepared.json");
|
|
242
|
+
if (!existsSync(file))
|
|
243
|
+
throw new CatherdError("E_INPUT_INVALID", `${dir} is not a prepared scorecard task`, {
|
|
244
|
+
fix: `catherd scorecard prepare ${SCORECARD_TASKS.join("|")}`,
|
|
245
|
+
});
|
|
246
|
+
return readVersioned(file, PreparedSchema, 1) as Prepared;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/** The one run to score: the named one, else the only open run created in the prepared repo since it was prepared. */
|
|
250
|
+
function pickRun(p: Prepared, id: string | undefined): Run {
|
|
251
|
+
if (id) {
|
|
252
|
+
const run = findRun(id);
|
|
253
|
+
if (run.meta.repo !== p.repo)
|
|
254
|
+
throw new CatherdError("E_INPUT_INVALID", `run ${id} belongs to ${run.meta.repo}, not ${p.repo}`, {
|
|
255
|
+
fix: "name a run of the prepared repo, or leave --run out",
|
|
256
|
+
});
|
|
257
|
+
return run;
|
|
258
|
+
}
|
|
259
|
+
const since = Date.parse(p.preparedAt);
|
|
260
|
+
const runs = listRuns([p.repo]).runs.filter(
|
|
261
|
+
(r) => Date.parse(r.meta.createdAt) >= since && !supersededBy(r),
|
|
262
|
+
);
|
|
263
|
+
if (runs.length === 0)
|
|
264
|
+
throw new CatherdError("E_INPUT_INVALID", `no run in ${p.repo} yet`, {
|
|
265
|
+
fix: "run the task's start line, paste its prompt, and record once it has landed or been given up",
|
|
266
|
+
});
|
|
267
|
+
if (runs.length > 1)
|
|
268
|
+
throw new CatherdError("E_INPUT_INVALID", `${p.repo} has runs ${runs.map((r) => r.id).join(", ")}`, {
|
|
269
|
+
fix: `catherd scorecard record ${p.dir} --label <l> --run <id>`,
|
|
270
|
+
});
|
|
271
|
+
return runs[0] as Run;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export async function scoreTask(i: {
|
|
275
|
+
dir: string;
|
|
276
|
+
root: string;
|
|
277
|
+
run?: string;
|
|
278
|
+
chat?: number;
|
|
279
|
+
now: number;
|
|
280
|
+
}): Promise<TaskScore> {
|
|
281
|
+
const p = readPrepared(i.dir);
|
|
282
|
+
const task = loadTask(p.task, i.root);
|
|
283
|
+
const run = pickRun(p, i.run);
|
|
284
|
+
const milestones = runMilestones(run);
|
|
285
|
+
const landed = landedMilestones(run);
|
|
286
|
+
const finished =
|
|
287
|
+
milestones.length > 0 &&
|
|
288
|
+
milestones.every((m) => landed.includes(m)) &&
|
|
289
|
+
openQuestions(run).length === 0 &&
|
|
290
|
+
!supersededBy(run);
|
|
291
|
+
const end = runEnd(run);
|
|
292
|
+
const start = Date.parse(run.meta.createdAt);
|
|
293
|
+
|
|
294
|
+
const copies = task.acceptFiles.map((f) => ({
|
|
295
|
+
from: join(i.root, "tasks", task.id, f.from),
|
|
296
|
+
to: join(p.repo, f.to),
|
|
297
|
+
}));
|
|
298
|
+
for (const c of copies)
|
|
299
|
+
if (existsSync(c.to))
|
|
300
|
+
throw new CatherdError(
|
|
301
|
+
"E_INPUT_INVALID",
|
|
302
|
+
`${c.to.slice(p.repo.length + 1)} already exists in the run's tree`,
|
|
303
|
+
{
|
|
304
|
+
fix: "the run wrote a file the hidden acceptance test needs; rename the file in the run, or score it by hand",
|
|
305
|
+
},
|
|
306
|
+
);
|
|
307
|
+
const done: string[] = [];
|
|
308
|
+
let check: Awaited<ReturnType<typeof runCheck>>;
|
|
309
|
+
const head = await gitHead(p.repo);
|
|
310
|
+
try {
|
|
311
|
+
for (const c of copies) {
|
|
312
|
+
mkdirSync(dirname(c.to), { recursive: true });
|
|
313
|
+
copyFileSync(c.from, c.to);
|
|
314
|
+
done.push(c.to);
|
|
315
|
+
}
|
|
316
|
+
check = await runCheck(
|
|
317
|
+
p.repo,
|
|
318
|
+
task.acceptance,
|
|
319
|
+
task.acceptanceTimeoutMin * 60_000,
|
|
320
|
+
await checkEnvFor(p.repo),
|
|
321
|
+
);
|
|
322
|
+
} finally {
|
|
323
|
+
for (const f of done) rmSync(f, { force: true });
|
|
324
|
+
}
|
|
325
|
+
const acceptance = {
|
|
326
|
+
passed: check.code === 0 && !check.timedOut,
|
|
327
|
+
code: check.code,
|
|
328
|
+
timedOut: check.timedOut,
|
|
329
|
+
tail: check.tail,
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
const outcomes = latestOutcomes(readOutcomes(run));
|
|
333
|
+
const takenDir = p.limits ? join(p.limits, "taken") : null;
|
|
334
|
+
return {
|
|
335
|
+
task: task.id,
|
|
336
|
+
run: run.id,
|
|
337
|
+
catherdVersion: run.meta.catherdVersion,
|
|
338
|
+
startCommit: p.startCommit,
|
|
339
|
+
head,
|
|
340
|
+
milestones,
|
|
341
|
+
landed,
|
|
342
|
+
finished,
|
|
343
|
+
interventions: interventionsOf(run, end, i.chat ?? 0),
|
|
344
|
+
reviewRounds: milestones.reduce((n, m) => n + reviewRounds(run, m), 0),
|
|
345
|
+
lanes: {
|
|
346
|
+
total: outcomes.length,
|
|
347
|
+
landed: outcomes.filter((o) => o.landed).length,
|
|
348
|
+
climbs: outcomes.reduce((n, o) => n + o.climbs.length, 0),
|
|
349
|
+
},
|
|
350
|
+
wallMinutes: minutes(end - start),
|
|
351
|
+
pausedMinutes: minutes(pausedMs(run, end)),
|
|
352
|
+
quota: {
|
|
353
|
+
backends: quotaByBackend(readRecords(run).records),
|
|
354
|
+
claudeAgentTokens: readAgentRuns(run).reduce((n, a) => n + a.totalTokens, 0),
|
|
355
|
+
},
|
|
356
|
+
acceptance,
|
|
357
|
+
firstTry: acceptance.passed && finished && new Set(landed).size === landed.length,
|
|
358
|
+
injected:
|
|
359
|
+
task.limits > 0
|
|
360
|
+
? {
|
|
361
|
+
limits: takenDir && existsSync(takenDir) ? readdirSync(takenDir).length : 0,
|
|
362
|
+
standInOk: failoverRecovered(run),
|
|
363
|
+
}
|
|
364
|
+
: null,
|
|
365
|
+
recordedAt: new Date(i.now).toISOString(),
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
export const resultsFile = (dir: string, label: string): string => join(dir, `${assertLabel(label)}.json`);
|
|
370
|
+
|
|
371
|
+
export function readScorecard(file: string): ScorecardFile | null {
|
|
372
|
+
return existsSync(file) ? assertScorecardRows(readVersioned(file, ScorecardFileSchema, 1), file) : null;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/** Sets the task's row in the label's file (a re-record replaces it) and returns the file's contents. */
|
|
376
|
+
export function saveTaskScore(file: string, label: string, s: TaskScore): ScorecardFile {
|
|
377
|
+
const f = readScorecard(file) ?? { schema: 1 as const, label, tasks: {} };
|
|
378
|
+
f.tasks[s.task] = s;
|
|
379
|
+
ensurePrivateDir(dirname(file));
|
|
380
|
+
writeJsonAtomic(file, f);
|
|
381
|
+
return f;
|
|
382
|
+
}
|
package/src/services/summary.ts
CHANGED
|
@@ -3,11 +3,13 @@ import type { QueueCapability } from "../infra/codex-queue.ts";
|
|
|
3
3
|
import { inspectionHost, inspectDeliveries, type DeliveryInspection } from "./run-debug.ts";
|
|
4
4
|
import { resolve } from "node:path";
|
|
5
5
|
import { type BudgetStatus, budgetStatus } from "../domain/budget.ts";
|
|
6
|
-
import { isCatherdError } from "../domain/errors.ts";
|
|
7
|
-
import type { Tokens } from "../domain/record.ts";
|
|
6
|
+
import { errorMessage, isCatherdError } from "../domain/errors.ts";
|
|
7
|
+
import type { RunRecord, Tokens } from "../domain/record.ts";
|
|
8
|
+
import type { QuotaRow } from "../domain/scorecard.ts";
|
|
8
9
|
import { median } from "../domain/util.ts";
|
|
9
10
|
import { nonBlankLines, readJsonl } from "../infra/store.ts";
|
|
10
11
|
import { spendOf } from "./budget.ts";
|
|
12
|
+
import { ensureDaemon } from "./daemon.ts";
|
|
11
13
|
import { verifierStepView, type VerifierStepView } from "./verifier-step.ts";
|
|
12
14
|
import { type Pause, pausesOver } from "./pause.ts";
|
|
13
15
|
import { pinChanges } from "./run-pin.ts";
|
|
@@ -64,6 +66,13 @@ export interface RunSummary {
|
|
|
64
66
|
/** One run at a glance. Spec §3.4: reads are pure; nothing here finalizes a dispatch or writes a file. */
|
|
65
67
|
export function summarizeRun(deps: Deps, run: Run): RunSummary {
|
|
66
68
|
const now = deps.now();
|
|
69
|
+
const warnings: string[] = [];
|
|
70
|
+
// an unfinished 2.0 run whose daemon died gets it back from any reader: status, watch, the panel
|
|
71
|
+
try {
|
|
72
|
+
ensureDaemon(run);
|
|
73
|
+
} catch (e) {
|
|
74
|
+
warnings.push(`daemon: ${errorMessage(e)}`);
|
|
75
|
+
}
|
|
67
76
|
const { records, corrupt } = readRecords(run);
|
|
68
77
|
const live = liveDispatches(run, now);
|
|
69
78
|
const recorded = new Set(records.map((r) => r.dispatchId));
|
|
@@ -71,7 +80,7 @@ export function summarizeRun(deps: Deps, run: Run): RunSummary {
|
|
|
71
80
|
const jev = readJsonl<{ source?: string }>(runPaths(run.dir).jev).rows;
|
|
72
81
|
const sum = (f: (t: Tokens) => number) => records.reduce((n, r) => n + f(r.tokens), 0);
|
|
73
82
|
let budget: BudgetStatus | null = null;
|
|
74
|
-
|
|
83
|
+
if (corrupt) warnings.push(`runs.jsonl: skipped ${corrupt} unreadable row(s)`);
|
|
75
84
|
// spec 1.5 "Pinned per run": a change since the pin, which dispatch does not follow
|
|
76
85
|
const pinned = pinChanges(deps, run);
|
|
77
86
|
for (const c of pinned) warnings.push(`pinned: ${c}; dispatch keeps the pinned value (run_pin re-pins)`);
|
|
@@ -250,6 +259,21 @@ function harnessCosts(runs: Run[]): HarnessCost[] {
|
|
|
250
259
|
});
|
|
251
260
|
}
|
|
252
261
|
|
|
262
|
+
/** Plan 28: tokens and usage limits per backend over `records`, sorted by backend. */
|
|
263
|
+
export function quotaByBackend(records: RunRecord[]): QuotaRow[] {
|
|
264
|
+
const by = new Map<string, QuotaRow>();
|
|
265
|
+
for (const r of records) {
|
|
266
|
+
const q = by.get(r.backend) ?? { backend: r.backend, runs: 0, input: 0, cached: 0, output: 0, limits: 0 };
|
|
267
|
+
q.runs++;
|
|
268
|
+
q.input += r.tokens.input;
|
|
269
|
+
q.cached += r.tokens.cached;
|
|
270
|
+
q.output += r.tokens.output;
|
|
271
|
+
if (r.status === "limit") q.limits++;
|
|
272
|
+
by.set(r.backend, q);
|
|
273
|
+
}
|
|
274
|
+
return [...by.values()].sort((a, b) => a.backend.localeCompare(b.backend));
|
|
275
|
+
}
|
|
276
|
+
|
|
253
277
|
/** Per role and rung over past runs: runs, ok, refusals, climbs from it, seconds and tokens. Reads only. */
|
|
254
278
|
export function runsSummary(f: { run?: string; repo?: string; role?: string; sinceDays?: number }): {
|
|
255
279
|
rungs: RungStats[];
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { CatherdError } from "../domain/errors.ts";
|
|
4
|
+
import type { Run } from "./run-store.ts";
|
|
5
|
+
|
|
6
|
+
/** `<run dir>/trees/<m>`: where milestone `m` of a base run works. */
|
|
7
|
+
export const treeDir = (run: Run, m: string): string => join(run.dir, "trees", m);
|
|
8
|
+
|
|
9
|
+
/** The marker beside the tree, never inside it, that Setup: finished. */
|
|
10
|
+
export const readyFile = (run: Run, m: string): string => join(run.dir, "trees", `${m}.ready`);
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Where milestone `m` works: its worktree in a base run once it exists, else the repo (a run without base, the
|
|
14
|
+
* run itself, a milestone not claimed yet). A claimed milestone whose folder is gone is an error, never the repo:
|
|
15
|
+
* its commits, checks and merge would land in the owner's own checkout. Kept apart from milestone-tree.ts so the services that only ask
|
|
16
|
+
* where a milestone works do not import the claiming code, which imports them.
|
|
17
|
+
*/
|
|
18
|
+
export function treeOf(run: Run, m: string | null): string {
|
|
19
|
+
if (!run.meta.base || m === null) return run.meta.repo;
|
|
20
|
+
const tree = treeDir(run, m);
|
|
21
|
+
if (existsSync(tree)) return tree;
|
|
22
|
+
if (existsSync(readyFile(run, m)))
|
|
23
|
+
throw new CatherdError("E_TREE", `the worktree of ${m} is gone: ${tree}`, {
|
|
24
|
+
fix: "restore the tree (git worktree add it back on the milestone's branch) or cancel the run",
|
|
25
|
+
});
|
|
26
|
+
return run.meta.repo;
|
|
27
|
+
}
|
|
@@ -3,11 +3,11 @@ import { budgetStatus, formatBudget, type BudgetStatus, type Spend } from "../do
|
|
|
3
3
|
import { CatherdError, errorMessage, isCatherdError } from "../domain/errors.ts";
|
|
4
4
|
import type { Workspace, WorkspaceStep } from "../domain/workspace.ts";
|
|
5
5
|
import { withFileLock } from "../infra/filelock.ts";
|
|
6
|
-
import {
|
|
6
|
+
import { gitToplevel, isAncestor } from "../infra/git.ts";
|
|
7
7
|
import { writeTextAtomic } from "../infra/store.ts";
|
|
8
8
|
import { spendOf } from "./budget.ts";
|
|
9
9
|
import { pendingDispatches } from "./dispatches.ts";
|
|
10
|
-
import {
|
|
10
|
+
import { landedCommitOf, landedMilestones } from "./milestones.ts";
|
|
11
11
|
import { readRecords, type Run, runPaths } from "./run-store.ts";
|
|
12
12
|
import {
|
|
13
13
|
findWorkspace,
|
|
@@ -123,14 +123,6 @@ export async function dependencyBlockers(
|
|
|
123
123
|
return out;
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
/** The commit the ledger holds for `milestone`, its latest landing; null when it never landed. */
|
|
127
|
-
function landedCommitOf(run: Run, milestone: string): string | null {
|
|
128
|
-
const commits = landedCommits(run);
|
|
129
|
-
const milestones = landedMilestones(run);
|
|
130
|
-
const at = milestones.lastIndexOf(milestone);
|
|
131
|
-
return at < 0 ? null : (commits[at] ?? null);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
126
|
/**
|
|
135
127
|
* A `release: "merge"` step releases its dependents once its landed commit is an ancestor of its base ref
|
|
136
128
|
* (`git merge-base --is-ancestor`), in the repository as it stands: catherd never fetches or polls.
|
|
@@ -139,9 +131,9 @@ async function unmerged(step: WorkspaceStep, child: Run): Promise<string | null>
|
|
|
139
131
|
if (step.release !== "merge" || !step.base) return null;
|
|
140
132
|
const commit = landedCommitOf(child, step.milestone);
|
|
141
133
|
if (!commit) return `${step.id} has no landed commit for ${step.milestone} in its ledger`;
|
|
142
|
-
const
|
|
143
|
-
if (
|
|
144
|
-
if (
|
|
134
|
+
const merged = await isAncestor(child.meta.repo, commit, step.base);
|
|
135
|
+
if (merged === true) return null;
|
|
136
|
+
if (merged === false)
|
|
145
137
|
return `${step.id}'s ${step.milestone} (${commit.slice(0, 7)}) is not merged into ${step.base} yet: merge it, git fetch, then ask again`;
|
|
146
138
|
return `${step.id}: git cannot tell whether ${commit.slice(0, 7)} is in ${step.base} (does the ref exist in ${child.meta.repo}?)`;
|
|
147
139
|
}
|
|
@@ -6,7 +6,10 @@ import { CatherdError } from "../domain/errors.ts";
|
|
|
6
6
|
import { WORKSPACE_LIMIT, WorkspaceIdSchema, WorkspaceSchema, type Workspace } from "../domain/workspace.ts";
|
|
7
7
|
import { withFileLock } from "../infra/filelock.ts";
|
|
8
8
|
import { gitToplevel } from "../infra/git.ts";
|
|
9
|
+
import { indexPath } from "../infra/paths.ts";
|
|
10
|
+
import { readIndexFile } from "../infra/slopalytics.ts";
|
|
9
11
|
import { readVersioned, writeTextAtomic } from "../infra/store.ts";
|
|
12
|
+
import { writeRunIndex } from "./catalog-service.ts";
|
|
10
13
|
import { listDispatches } from "./dispatches.ts";
|
|
11
14
|
import { landedCommits, landedMilestones } from "./milestones.ts";
|
|
12
15
|
import { assertNotPaused, pausesOver } from "./pause.ts";
|
|
@@ -193,6 +196,8 @@ async function childFor(
|
|
|
193
196
|
workspace: { id: workspace.id, step: step.id },
|
|
194
197
|
});
|
|
195
198
|
writePin(deps, run);
|
|
199
|
+
// pinned like startRun does, so a pair the machine index drops later never breaks the child midway
|
|
200
|
+
writeRunIndex(run.dir, readIndexFile(indexPath()));
|
|
196
201
|
}
|
|
197
202
|
const contract = existsSync(contractFile) ? join(run.dir, "workspace-contract.md") : null;
|
|
198
203
|
if (contract && !existsSync(contract)) writeTextAtomic(contract, readFileSync(contractFile, "utf8"));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{r as e}from"./rolldown-runtime-XKyaHtl9.js";import{$ as t,Sn as n,xn as r}from"./react-query.gen-Bu2yV-qJ.js";import{u as i}from"./index-B6GiwDkh.js";var a=e(n(),1),o=`heroui-theme`,s=`(prefers-color-scheme: dark)`;function c(e){if(typeof window>`u`)return()=>{};let t=window.matchMedia(s);return t.addEventListener(`change`,e),()=>t.removeEventListener(`change`,e)}function l(){return window.matchMedia?.(s).matches?`dark`:`light`}function u(){}function d(e,t){t!==e&&(t&&document.documentElement.classList.remove(t),document.documentElement.classList.add(e),document.documentElement.setAttribute(`data-theme`,e))}function f(e=`system`){let[t,n]=(0,a.useState)(()=>typeof window>`u`?e:localStorage.getItem(o)??e),r=(0,a.useSyncExternalStore)(c,l,u),s=t===`system`?r:t,f=(0,a.useRef)(void 0);return i(()=>{s&&(d(s,f.current),f.current=s)},[s]),{resolvedTheme:s,setTheme:(0,a.useCallback)(e=>{typeof window<`u`&&(localStorage.setItem(o,e),n(e))},[]),theme:t}}var p=r();function m({className:e}){return(0,p.jsxs)(`svg`,{viewBox:`0 0 20 20`,"aria-hidden":`true`,className:t(`size-5 shrink-0`,e),children:[(0,p.jsx)(`rect`,{x:`2`,y:`12`,width:`16`,height:`4`,className:`fill-foreground`}),(0,p.jsx)(`rect`,{x:`3`,y:`6`,width:`3`,height:`3`,className:`fill-accent`}),(0,p.jsx)(`rect`,{x:`8.5`,y:`6`,width:`3`,height:`3`,className:`fill-ink-faint`}),(0,p.jsx)(`rect`,{x:`14`,y:`6`,width:`3`,height:`3`,className:`fill-ink-faint`})]})}function h({className:e}){return(0,p.jsxs)(`span`,{className:t(`inline-flex items-center gap-2`,e),children:[(0,p.jsx)(m,{}),(0,p.jsx)(`span`,{className:`text-[15px] font-semibold tracking-[-0.02em]`,children:`catherd`})]})}export{f as n,h as t};
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{r as e}from"./rolldown-runtime-XKyaHtl9.js";import{Dt as t,G as n,K as r,Lt as i,Ot as a,Sn as o,Vt as s,W as c,X as l,_t as u,ct as d,dt as f,ft as p,ut as m}from"./react-query.gen-Bu2yV-qJ.js";import{$ as h,Q as g,X as _,Z as v,bt as y,ft as b,m as x,nt as S,ot as C,p as w,pt as T,st as E,tt as D,vt as O}from"./use-css-variable-CpnN9WtA.js";import{D as k}from"./label-DFSYBc62.js";var A=e(o(),1),j=typeof document<`u`&&window.visualViewport;function M(){let e=t(),n=(0,A.useRef)(null),[r,a]=(0,A.useState)(()=>e?{width:0,height:0}:N());return(0,A.useEffect)(()=>{let e=e=>{a(t=>e.width===t.width&&e.height===t.height?t:e)},t=()=>{j&&j.scale>1||e(N())},r=t=>{j&&j.scale>1||k(u(t))&&(n.current=O(t=>{t||e({width:document.documentElement.clientWidth,height:document.documentElement.clientHeight})}))};return e(N()),s()&&i()&&window.addEventListener(`blur`,r,!0),j?j.addEventListener(`resize`,t):window.addEventListener(`resize`,t),()=>{n.current?.(),s()&&i()&&window.removeEventListener(`blur`,r,!0),j?j.removeEventListener(`resize`,t):window.removeEventListener(`resize`,t)}},[]),r}function N(){return{width:j?Math.min(j.width*j.scale,document.documentElement.clientWidth):document.documentElement.clientWidth,height:j?j.height*j.scale:document.documentElement.clientHeight}}function P(e,t,n){let{overlayProps:r,underlayProps:i}=S({...e,isOpen:t.isOpen,onClose:t.close},n);D({isDisabled:!t.isOpen,UNSTABLE_overrideFocus:e.UNSTABLE_overrideFocus}),v();let{isEntering:a}=e;return(0,A.useEffect)(()=>{if(t.isOpen&&n.current)return a?C(n.current):E([n.current],{shouldUseInert:!0})},[t.isOpen,a,n]),{modalProps:f(r),underlayProps:i}}var F=(0,A.createContext)(null),I=(0,A.createContext)(null),L=(0,A.forwardRef)(function(e,t){if((0,A.useContext)(I))return A.createElement(H,{...e,modalRef:t},e.children);let{isDismissable:n,isKeyboardDismissDisabled:r,isOpen:i,defaultOpen:a,onOpenChange:o,children:s,isEntering:c,isExiting:l,UNSTABLE_portalContainer:u,shouldCloseOnInteractOutside:d,...f}=e;return A.createElement(z,{isDismissable:n,isKeyboardDismissDisabled:r,isOpen:i,defaultOpen:a,onOpenChange:o,isEntering:c,isExiting:l,UNSTABLE_portalContainer:u,shouldCloseOnInteractOutside:d},A.createElement(H,{...f,modalRef:t},s))});function R(e,r){[e,r]=n(e,r,F);let i=(0,A.useContext)(x),a=_(e),o=e.isOpen!=null||e.defaultOpen!=null||!i?a:i,s=d(r),c=(0,A.useRef)(null),l=b(s,o.isOpen,e.onExit),u=(0,A.useRef)(null),f=b(c,o.isOpen,e=>u.current?.(e)),p=l||f||e.isExiting||!1,m=t();return!o.isOpen&&!p||m?null:A.createElement(B,{...e,state:o,isExiting:p,overlayRef:s,modalRef:c,onExitRef:u})}var z=(0,A.forwardRef)(R);function B({UNSTABLE_portalContainer:e,UNSTABLE_deferUntilEntered:t,...n}){let i=n.modalRef,{state:o}=n,[s,u]=(0,A.useState)(!1),d=T(n.overlayRef,s,n.onEnter)||n.isEntering||!1,p=!!t&&(!s||d),{modalProps:h,underlayProps:_}=P({...n,isEntering:p},o,i),v=(0,A.useContext)(w),b=t?V(v,{isEntering:p}):v,S=r({...n,defaultClassName:`react-aria-ModalOverlay`,values:{isEntering:d,isExiting:n.isExiting,state:o}}),C=M(),E,D;if(typeof document<`u`){let e=y(document.body)?document.body:document.scrollingElement||document.documentElement,t=e.getBoundingClientRect().width%1,n=e.getBoundingClientRect().height%1;E=e.scrollWidth-t,D=e.scrollHeight-n}let k={...S.style,"--visual-viewport-width":C.width+`px`,"--visual-viewport-height":C.height+`px`,"--page-width":E===void 0?void 0:E+`px`,"--page-height":D===void 0?void 0:D+`px`};return a(()=>O(()=>u(!0)),[]),A.createElement(g,{isExiting:n.isExiting,portalContainer:e},A.createElement(l.div,{...f(m(n,{global:!0}),_),...S,style:k,ref:n.overlayRef,"data-entering":d||void 0,"data-exiting":n.isExiting||void 0},A.createElement(c,{values:[[I,{modalProps:h,modalRef:i,isExiting:n.isExiting,onExitRef:n.onExitRef,isOpen:s,isDismissable:n.isDismissable}],[x,o],[w,b]]},S.children)))}function V(e,t){if(e&&`slots`in e&&e.slots){let n={};for(let r in e.slots)n[r]={...e.slots[r],...t};return{...e,slots:n}}return{...e,...t}}function H(e){let{modalProps:t,modalRef:n,isExiting:i,onExitRef:o,isDismissable:s,isOpen:c}=(0,A.useContext)(I),u=(0,A.useContext)(x),g=(0,A.useMemo)(()=>p(e.modalRef,n),[e.modalRef,n]),_=d(g),v=T(_,c,e.onEnter),y=r({...e,defaultClassName:`react-aria-Modal`,values:{isEntering:v,isExiting:i,state:u}});return a(()=>{o&&(o.current=e.onExit)}),A.createElement(l.div,{...f(m(e,{global:!0}),t),...y,ref:_,"data-entering":v||void 0,"data-exiting":i||void 0},s&&A.createElement(h,{onDismiss:u.close}),y.children)}export{z as n,L as t};
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{r as e}from"./rolldown-runtime-XKyaHtl9.js";import{C as t,O as n,Sn as r,ct as i,dt as a,ft as o}from"./react-query.gen-Bu2yV-qJ.js";var s=e(r(),1),c=s.forwardRef(({children:e,...r},c)=>{c=i(c);let{pressProps:l}=t({...r,ref:c}),{focusableProps:u}=n(r,c),d=s.Children.only(e);(0,s.useEffect)(()=>{},[c,r.isDisabled]);let f=d.props.ref;return s.cloneElement(d,{...a(l,u,d.props),ref:o(f,c)})});export{c as t};
|