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,72 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { CatherdError } from "./errors.ts";
|
|
3
|
+
|
|
4
|
+
/** One model at one effort, as the Slopalytics proxy indexes it: its intelligence and the facts that come with it. */
|
|
5
|
+
export interface SlopRecord {
|
|
6
|
+
family: string;
|
|
7
|
+
effort: string;
|
|
8
|
+
intelligence: number;
|
|
9
|
+
inputPrice: number | null;
|
|
10
|
+
outputPrice: number | null;
|
|
11
|
+
speed: number | null;
|
|
12
|
+
context: number | null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const RecordSchema = z.object({
|
|
16
|
+
family: z.string(),
|
|
17
|
+
effort: z.string(),
|
|
18
|
+
intelligence: z.number(),
|
|
19
|
+
inputPrice: z.number().nullable(),
|
|
20
|
+
outputPrice: z.number().nullable(),
|
|
21
|
+
speed: z.number().nullable(),
|
|
22
|
+
context: z.number().nullable(),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
/** `<data>/slopalytics.json`: the last good answer, when it was fetched and checked, and the last failure. */
|
|
26
|
+
export const IndexFileSchema = z.looseObject({
|
|
27
|
+
schema: z.literal(1),
|
|
28
|
+
checkedAt: z.iso.datetime(),
|
|
29
|
+
fetchedAt: z.string().nullable(),
|
|
30
|
+
error: z.object({ at: z.string(), message: z.string() }).nullable(),
|
|
31
|
+
models: z.array(RecordSchema),
|
|
32
|
+
});
|
|
33
|
+
export type IndexFile = z.infer<typeof IndexFileSchema>;
|
|
34
|
+
|
|
35
|
+
/** A finite number or a finite numeric string (the proxy sends some prices as strings), else null. */
|
|
36
|
+
const num = (v: unknown): number | null => {
|
|
37
|
+
const n = typeof v === "string" && v.trim() !== "" ? Number(v) : v;
|
|
38
|
+
return typeof n === "number" && Number.isFinite(n) ? n : null;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/** The proxy's answer as the records catherd ranks on: only a record with a positive intelligence is a pair. */
|
|
42
|
+
export function parseProxy(body: unknown): { fetchedAt: string | null; models: SlopRecord[] } {
|
|
43
|
+
const b = body as { fetchedAt?: unknown; models?: unknown } | null;
|
|
44
|
+
if (typeof b !== "object" || b === null || !Array.isArray(b.models))
|
|
45
|
+
throw new CatherdError("E_INPUT_INVALID", "Slopalytics answered without a models list");
|
|
46
|
+
const models: SlopRecord[] = [];
|
|
47
|
+
for (const raw of b.models) {
|
|
48
|
+
const r = (raw ?? {}) as Record<string, unknown>;
|
|
49
|
+
const intelligence = num(r.intelligence);
|
|
50
|
+
if (typeof r.family !== "string" || !r.family) continue;
|
|
51
|
+
if (typeof r.effort !== "string" || !r.effort) continue;
|
|
52
|
+
if (intelligence === null || intelligence <= 0) continue;
|
|
53
|
+
const context = num(r.context);
|
|
54
|
+
models.push({
|
|
55
|
+
family: r.family,
|
|
56
|
+
effort: r.effort,
|
|
57
|
+
intelligence,
|
|
58
|
+
inputPrice: num(r.inputPrice),
|
|
59
|
+
outputPrice: num(r.outputPrice),
|
|
60
|
+
speed: num(r.speed),
|
|
61
|
+
context: context !== null && Number.isInteger(context) && context > 0 ? context : null,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return { fetchedAt: typeof b.fetchedAt === "string" ? b.fetchedAt : null, models };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** A model's Slopalytics family key: its provider dropped, lower case, dots as dashes (`gpt-6.1-sol` → `gpt-6-1-sol`). */
|
|
68
|
+
export const slopKey = (model: string): string =>
|
|
69
|
+
model
|
|
70
|
+
.slice(model.lastIndexOf("/") + 1)
|
|
71
|
+
.toLowerCase()
|
|
72
|
+
.replaceAll(".", "-");
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
|
+
import { CatherdError } from "../domain/errors.ts";
|
|
3
|
+
import { answerUnit } from "../services/run-client.ts";
|
|
4
|
+
import { defaultDeps } from "./deps.ts";
|
|
5
|
+
import { JSON_ARG, printJson, refuseInRole } from "./cli-kit.ts";
|
|
6
|
+
|
|
7
|
+
/** What the phone message names: the run, the unit and the answer, as one command. */
|
|
8
|
+
export const answerCommand = defineCommand({
|
|
9
|
+
meta: {
|
|
10
|
+
name: "answer",
|
|
11
|
+
description: "Answer a parked unit of a run: it runs again with your answer",
|
|
12
|
+
},
|
|
13
|
+
args: {
|
|
14
|
+
run: { type: "positional", required: true, description: "the run id" },
|
|
15
|
+
unit: {
|
|
16
|
+
type: "positional",
|
|
17
|
+
required: true,
|
|
18
|
+
description: "the parked unit: the run, a milestone or a lane",
|
|
19
|
+
},
|
|
20
|
+
answer: {
|
|
21
|
+
type: "positional",
|
|
22
|
+
required: false,
|
|
23
|
+
description: "your answer (several words need no quotes)",
|
|
24
|
+
},
|
|
25
|
+
...JSON_ARG,
|
|
26
|
+
},
|
|
27
|
+
async run({ args }) {
|
|
28
|
+
refuseInRole(process.env, "catherd answer");
|
|
29
|
+
// citty names the first words; the rest of the line is the answer's too
|
|
30
|
+
const answer = (args._ as string[]).slice(2).join(" ").trim();
|
|
31
|
+
if (!answer)
|
|
32
|
+
throw new CatherdError("E_INPUT_INVALID", "no answer given", {
|
|
33
|
+
fix: 'catherd answer <run> <unit> "<your answer>"',
|
|
34
|
+
});
|
|
35
|
+
const r = await answerUnit(defaultDeps(), { run: args.run, unit: args.unit, answer });
|
|
36
|
+
if (args.json) return printJson(r);
|
|
37
|
+
console.log(`answered ${r.unit}${r.parked.length ? `; still waiting: ${r.parked.join(", ")}` : ""}`);
|
|
38
|
+
},
|
|
39
|
+
});
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { HOST_ARG, terminalHost } from "./host-arg.ts";
|
|
2
1
|
import { defineCommand } from "citty";
|
|
3
2
|
import { CatherdError, isCatherdError } from "../domain/errors.ts";
|
|
4
3
|
import { ROLES, type Role } from "../domain/roles.ts";
|
|
@@ -6,68 +5,35 @@ import { gitToplevel } from "../infra/git.ts";
|
|
|
6
5
|
import {
|
|
7
6
|
type CatalogModel,
|
|
8
7
|
catalogQuery,
|
|
8
|
+
type IndexStatus,
|
|
9
9
|
type Refreshed,
|
|
10
10
|
refreshDiscovery,
|
|
11
|
-
|
|
11
|
+
refreshIndex,
|
|
12
12
|
} from "../services/catalog-service.ts";
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import { clearTreatLike, type LeftOnStandIn, resetTreatLikes, suggestFor } from "../services/treat-likes.ts";
|
|
16
|
-
import { exitCodeOf, JSON_ARG, mark, printError, refuseInRole } from "./cli-kit.ts";
|
|
13
|
+
import { indexCheck } from "../services/doctor-checks.ts";
|
|
14
|
+
import { exitCodeOf, JSON_ARG, mark, printError } from "./cli-kit.ts";
|
|
17
15
|
|
|
18
|
-
/**
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
export function syncLines(r: SyncReport, plain = false): string[] {
|
|
23
|
-
if (r.busy) return ["- sources: another sync is running; its results apply when it ends"];
|
|
24
|
-
const out: string[] = [];
|
|
25
|
-
for (const s of r.sources) {
|
|
26
|
-
if (s.state === "fetched") out.push(`${mark("ok", plain)} ${s.source}: fetched`);
|
|
27
|
-
else if (s.state === "fresh") out.push(`- ${s.source}: fresh (fetched ${s.fetchedAt})`);
|
|
28
|
-
else if (s.state === "skipped") out.push(`- ${s.source}: ${s.detail}`);
|
|
29
|
-
else
|
|
30
|
-
out.push(
|
|
31
|
-
`${mark("warn", plain)} ${s.source}: ${s.error}; ${s.fetchedAt ? `keeps the answer fetched ${s.fetchedAt}` : "no earlier answer"}`,
|
|
32
|
-
);
|
|
33
|
-
}
|
|
34
|
-
if (r.newlyScored.length) out.push(`newly scored: ${r.newlyScored.join(", ")}`);
|
|
35
|
-
for (const n of r.noLongerNeeded)
|
|
36
|
-
out.push(`stand-in no longer needed: ${n.rung} has values of its own for what ${n.like} lent it`);
|
|
37
|
-
for (const w of r.warnings) out.push(`${mark("warn", plain)} ${w}`);
|
|
38
|
-
return out;
|
|
16
|
+
/** `sync`'s one line: the index's pair count and age, or why the last fetch failed and what was kept. */
|
|
17
|
+
export function syncLine(s: IndexStatus, plain = false, now = Date.now()): string {
|
|
18
|
+
const c = indexCheck(now, s);
|
|
19
|
+
return `${mark(c.state === "ok" ? "ok" : "warn", plain)} ${c.label}: ${c.detail}`;
|
|
39
20
|
}
|
|
40
21
|
|
|
41
22
|
const sync = defineCommand({
|
|
42
23
|
meta: {
|
|
43
24
|
name: "sync",
|
|
44
25
|
description:
|
|
45
|
-
"Fetch the
|
|
26
|
+
"Fetch the Slopalytics index now (it is refreshed at most hourly unless --force) and show how many pairs it holds",
|
|
46
27
|
},
|
|
47
28
|
args: {
|
|
48
|
-
force: { type: "boolean", description: "fetch
|
|
49
|
-
unmatched: {
|
|
50
|
-
type: "boolean",
|
|
51
|
-
description: "also list each source's ids that match no model in the catalog",
|
|
52
|
-
},
|
|
29
|
+
force: { type: "boolean", description: "fetch even when the index was checked within the hour" },
|
|
53
30
|
...JSON_ARG,
|
|
54
31
|
},
|
|
55
32
|
async run({ args }) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if (args.unmatched && !args.json) {
|
|
61
|
-
const unmatched = readDerived()?.unmatched ?? {};
|
|
62
|
-
if (Object.keys(unmatched).length === 0) console.log("unmatched: none");
|
|
63
|
-
for (const [source, ids] of Object.entries(unmatched))
|
|
64
|
-
console.log(`unmatched in ${source}: ${ids.join(", ")}`);
|
|
65
|
-
}
|
|
66
|
-
const got = r.sources.some((s) => s.state === "fetched" || s.state === "fresh");
|
|
67
|
-
process.exitCode = r.failed.length && !got ? 1 : 0;
|
|
68
|
-
} catch (e) {
|
|
69
|
-
fail(e);
|
|
70
|
-
}
|
|
33
|
+
const s = await refreshIndex({ force: args.force === true });
|
|
34
|
+
if (args.json) console.log(JSON.stringify(s, null, 2));
|
|
35
|
+
else console.log(syncLine(s));
|
|
36
|
+
process.exitCode = s.pairs === 0 ? 1 : 0;
|
|
71
37
|
},
|
|
72
38
|
});
|
|
73
39
|
|
|
@@ -80,29 +46,19 @@ export function formatRefreshed(r: Refreshed, plain = false): string {
|
|
|
80
46
|
].join("\n");
|
|
81
47
|
}
|
|
82
48
|
|
|
83
|
-
/**
|
|
84
|
-
* One model: its rungs scored and roles, then (spec 1.2 §4.1, §5.3, §8) its price and speed facts, and each
|
|
85
|
-
* rung's values with their confidence and source, and the runs catherd has made on it.
|
|
86
|
-
*/
|
|
49
|
+
/** One model: its indexed pairs and roles, then each pair's intelligence, price, speed and the runs catherd has made on it. */
|
|
87
50
|
export function formatModel(m: CatalogModel): string {
|
|
88
|
-
const scored = m.rungs.filter((r) => r.enabled).length;
|
|
89
51
|
const listed = m.listed === false ? " not offered by this account" : "";
|
|
90
52
|
const lines = [
|
|
91
|
-
`${m.backend}:${m.model} ${
|
|
92
|
-
];
|
|
93
|
-
const facts = [
|
|
94
|
-
...(m.price ? [`$${m.price.input}/$${m.price.output} per M tokens in/out`] : []),
|
|
95
|
-
...Object.entries(m.speed).map(([k, v]) => `${k} ${Number(v.toPrecision(4))}`),
|
|
53
|
+
`${m.backend}:${m.model} ${m.rungs.length} ${m.rungs.length === 1 ? "pair" : "pairs"} roles ${m.roles.join(",") || "none"}${listed}`,
|
|
96
54
|
];
|
|
97
|
-
if (facts.length) lines.push(` ${facts.join(" · ")}`);
|
|
98
55
|
for (const r of m.rungs) {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
([
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
lines.push(` #${r.rung.slice(r.rung.lastIndexOf("#") + 1)} ${values.join(" · ") || "unscored"}`);
|
|
56
|
+
const parts = [
|
|
57
|
+
`intelligence ${r.intelligence.toFixed(1)}`,
|
|
58
|
+
...(r.price ? [`$${r.price.input}/$${r.price.output} per M`] : []),
|
|
59
|
+
...(r.speed !== null ? [`${Number(r.speed.toPrecision(3))} tok/s`] : []),
|
|
60
|
+
];
|
|
61
|
+
lines.push(` #${r.rung.slice(r.rung.lastIndexOf("#") + 1)} ${parts.join(" · ")}`);
|
|
106
62
|
if (r.evidence) lines.push(` runs: ${r.evidence}`);
|
|
107
63
|
}
|
|
108
64
|
return lines.join("\n");
|
|
@@ -129,12 +85,11 @@ const refresh = defineCommand({
|
|
|
129
85
|
});
|
|
130
86
|
|
|
131
87
|
const list = defineCommand({
|
|
132
|
-
meta: { name: "list", description: "The models catherd can place, with their
|
|
88
|
+
meta: { name: "list", description: "The models catherd can place, with their indexed pairs" },
|
|
133
89
|
args: {
|
|
134
90
|
backend: { type: "string", description: "only this backend or billing key" },
|
|
135
91
|
role: { type: "string", description: `only models for this role (${ROLES.join(", ")})` },
|
|
136
92
|
text: { type: "string", description: "only ids containing this" },
|
|
137
|
-
scored: { type: "boolean", description: "only models with a scored rung" },
|
|
138
93
|
...JSON_ARG,
|
|
139
94
|
},
|
|
140
95
|
async run({ args }) {
|
|
@@ -149,7 +104,6 @@ const list = defineCommand({
|
|
|
149
104
|
backend: args.backend,
|
|
150
105
|
role: args.role as Role | undefined,
|
|
151
106
|
text: args.text,
|
|
152
|
-
scoredOnly: args.scored === true,
|
|
153
107
|
limit: 10_000,
|
|
154
108
|
repo: await hereRepo(),
|
|
155
109
|
});
|
|
@@ -161,111 +115,8 @@ const list = defineCommand({
|
|
|
161
115
|
},
|
|
162
116
|
});
|
|
163
117
|
|
|
164
|
-
/** Spec
|
|
165
|
-
export function leftLines(left: LeftOnStandIn[], plain = false): string[] {
|
|
166
|
-
return left.map((l) => {
|
|
167
|
-
const what = l.unscored
|
|
168
|
-
? ["unscored: routing skips it"]
|
|
169
|
-
: [
|
|
170
|
-
...(l.dims.length ? [`on an inferred stand-in for ${l.dims.join(", ")}`] : []),
|
|
171
|
-
...(l.missing?.length
|
|
172
|
-
? [`with no ${l.missing.join(", ")} value: it clears no bar that needs one`]
|
|
173
|
-
: []),
|
|
174
|
-
];
|
|
175
|
-
return `${mark("warn", plain)} ${l.profile}: ${l.rung} is left ${what.join(", and ")}`;
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/** Spec 1.2 §6.4 `--suggest`: the three nearest stand-ins, each with its distance and the features it rests on. */
|
|
180
|
-
export function suggestLines(r: ReturnType<typeof suggestFor>, rung: string): string[] {
|
|
181
|
-
const head = r.lacking.length
|
|
182
|
-
? `${r.canonical} has no ${r.lacking.join(", ")} value of its own; the nearest stand-ins:`
|
|
183
|
-
: `${r.canonical} has every value the bars use; the nearest scored rungs:`;
|
|
184
|
-
if (r.suggestions.length === 0)
|
|
185
|
-
return [
|
|
186
|
-
`${r.canonical}: no scored rung shares 3 features with it; map it by hand: catherd catalog treat-like ${rung} <rung>`,
|
|
187
|
-
];
|
|
188
|
-
return [
|
|
189
|
-
head,
|
|
190
|
-
...r.suggestions.map(
|
|
191
|
-
(s, i) =>
|
|
192
|
-
` ${i + 1}. ${s.like} distance ${s.distance.toFixed(2)} lends ${s.lends.join(", ")} on ${s.features.join(", ")}`,
|
|
193
|
-
),
|
|
194
|
-
`map one: catherd catalog treat-like ${rung} <rung>`,
|
|
195
|
-
];
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
const treatLike = defineCommand({
|
|
199
|
-
meta: {
|
|
200
|
-
name: "treat-like",
|
|
201
|
-
description:
|
|
202
|
-
"Score an unscored rung as a scored one; --suggest ranks stand-ins, --clear and --reset remove your mappings",
|
|
203
|
-
},
|
|
204
|
-
args: {
|
|
205
|
-
...HOST_ARG,
|
|
206
|
-
rung: {
|
|
207
|
-
type: "positional",
|
|
208
|
-
required: false,
|
|
209
|
-
description: "the unscored rung, backend:model#effort or model#effort",
|
|
210
|
-
},
|
|
211
|
-
like: { type: "positional", required: false, description: "the scored rung whose scores it borrows" },
|
|
212
|
-
suggest: { type: "string", description: "list the 3 nearest stand-ins for this rung" },
|
|
213
|
-
clear: { type: "string", description: "remove your treat-like for this rung" },
|
|
214
|
-
reset: { type: "boolean", description: "remove every treat-like of yours" },
|
|
215
|
-
...JSON_ARG,
|
|
216
|
-
},
|
|
217
|
-
async run({ args }) {
|
|
218
|
-
try {
|
|
219
|
-
const modes = [
|
|
220
|
-
args.suggest !== undefined,
|
|
221
|
-
args.clear !== undefined,
|
|
222
|
-
args.reset === true,
|
|
223
|
-
args.rung !== undefined,
|
|
224
|
-
];
|
|
225
|
-
if (modes.filter(Boolean).length !== 1 || (args.rung !== undefined && args.like === undefined))
|
|
226
|
-
throw new CatherdError(
|
|
227
|
-
"E_INPUT_INVALID",
|
|
228
|
-
"treat-like takes <rung> <like>, or one of --suggest, --clear, --reset",
|
|
229
|
-
{
|
|
230
|
-
fix: "catherd catalog treat-like <rung> <like> | --suggest <rung> | --clear <rung> | --reset",
|
|
231
|
-
},
|
|
232
|
-
);
|
|
233
|
-
if (args.suggest !== undefined) {
|
|
234
|
-
const r = suggestFor(args.suggest);
|
|
235
|
-
if (args.json) console.log(JSON.stringify(r, null, 2));
|
|
236
|
-
else for (const l of suggestLines(r, args.suggest)) console.log(l);
|
|
237
|
-
return;
|
|
238
|
-
}
|
|
239
|
-
refuseInRole(process.env, "catherd catalog treat-like");
|
|
240
|
-
if (args.clear !== undefined) {
|
|
241
|
-
const r = await clearTreatLike(args.clear, terminalHost(args.host).host);
|
|
242
|
-
if (args.json) return console.log(JSON.stringify(r, null, 2));
|
|
243
|
-
for (const l of leftLines(r.left)) console.log(l);
|
|
244
|
-
console.log(`${mark("ok")} ${r.rung} is no longer treated like ${r.like}`);
|
|
245
|
-
return;
|
|
246
|
-
}
|
|
247
|
-
if (args.reset) {
|
|
248
|
-
const r = await resetTreatLikes(terminalHost(args.host).host);
|
|
249
|
-
if (args.json) return console.log(JSON.stringify(r, null, 2));
|
|
250
|
-
for (const l of leftLines(r.left)) console.log(l);
|
|
251
|
-
console.log(
|
|
252
|
-
r.removed.length
|
|
253
|
-
? `${mark("ok")} removed ${r.removed.length} treat-like${r.removed.length === 1 ? "" : "s"}: ${r.removed.map(([a, b]) => `${a} → ${b}`).join(", ")}`
|
|
254
|
-
: "no treat-like of yours to remove",
|
|
255
|
-
);
|
|
256
|
-
return;
|
|
257
|
-
}
|
|
258
|
-
const r = await saveTreatLike(args.rung as string, args.like as string);
|
|
259
|
-
if (args.json) return console.log(JSON.stringify(r, null, 2));
|
|
260
|
-
console.log(`${mark("ok")} ${r.rung} is treated like ${r.like}`);
|
|
261
|
-
} catch (e) {
|
|
262
|
-
fail(e);
|
|
263
|
-
}
|
|
264
|
-
},
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
/** Spec §8 and 1.2 §9: `catherd catalog refresh|list|sync|treat-like <rung> <like> | --suggest|--clear|--reset`. */
|
|
118
|
+
/** Spec §8: `catherd catalog refresh|list|sync`. */
|
|
268
119
|
export const catalogCommand = defineCommand({
|
|
269
120
|
meta: { name: "catalog", description: "The model catalog" },
|
|
270
|
-
subCommands: { refresh, list, sync
|
|
121
|
+
subCommands: { refresh, list, sync },
|
|
271
122
|
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// The detached check worker's entry: it imports only what running one milestone check needs, never src/cli.ts.
|
|
3
|
+
import { errorMessage } from "../domain/errors.ts";
|
|
4
|
+
import { checkWorker } from "../services/milestone-check.ts";
|
|
5
|
+
|
|
6
|
+
const flight = process.argv[2];
|
|
7
|
+
if (!flight) {
|
|
8
|
+
console.error("usage: check-bin.ts <flight file>");
|
|
9
|
+
process.exit(2);
|
|
10
|
+
}
|
|
11
|
+
try {
|
|
12
|
+
await checkWorker(flight);
|
|
13
|
+
} catch (e) {
|
|
14
|
+
console.error(`catherd check: ${errorMessage(e)}`);
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
process.exit(0);
|
package/src/entry/cli-kit.ts
CHANGED
|
@@ -46,7 +46,7 @@ export const printJson = (v: unknown): void => console.log(JSON.stringify(v, nul
|
|
|
46
46
|
export const JSON_ARG = { json: { type: "boolean", description: "print JSON" } } as const;
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
|
-
* The glyph for a state, as in `✓ ready`:
|
|
49
|
+
* The glyph for a state, as in `✓ ready`: catherd's own (spec §9.3), ASCII with `--plain` only;
|
|
50
50
|
* NO_COLOR drops colour, never glyphs. The caller adds the word.
|
|
51
51
|
*/
|
|
52
52
|
export const mark = (state: State, plain = false): string => glyph(state, plain);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// The detached run daemon's entry: like the supervisor's it imports only what driving a run needs, never
|
|
3
|
+
// src/cli.ts.
|
|
4
|
+
import { errorMessage } from "../domain/errors.ts";
|
|
5
|
+
import { runDaemon } from "../services/daemon.ts";
|
|
6
|
+
import { defaultDeps } from "./deps.ts";
|
|
7
|
+
|
|
8
|
+
const runId = process.argv[2];
|
|
9
|
+
if (!runId) {
|
|
10
|
+
console.error("usage: daemon-bin.ts <run id>");
|
|
11
|
+
process.exit(2);
|
|
12
|
+
}
|
|
13
|
+
try {
|
|
14
|
+
// nobody's role and no Claude Code session: the daemon answers to the run's events
|
|
15
|
+
const result = await runDaemon({ ...defaultDeps(), session: null, role: null }, runId);
|
|
16
|
+
if (result === "lost-lock") console.error(`catherd daemon: another daemon holds run ${runId}`);
|
|
17
|
+
} catch (e) {
|
|
18
|
+
console.error(`catherd daemon: ${errorMessage(e)}`);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
// the lock is released and the run's events are written: a hook's call left hanging must not keep it alive
|
|
22
|
+
process.exit(0);
|
package/src/entry/glyphs.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Spec §9.3's glyphs, each with its ASCII twin for `--plain`:
|
|
3
|
-
*
|
|
2
|
+
* Spec §9.3's glyphs, each with its ASCII twin for `--plain`: every CLI command draws state with these, so
|
|
3
|
+
* `mcp`, `lock` and `_supervise` can load them (spec §3.1).
|
|
4
4
|
*/
|
|
5
5
|
const GLYPHS = {
|
|
6
6
|
ok: ["✓", "+"],
|
|
@@ -32,3 +32,16 @@ export const glyph = (g: Glyph, plain: boolean): string => GLYPHS[g][plain ? 1 :
|
|
|
32
32
|
|
|
33
33
|
/** The words state always travels with (spec §9.3: green/amber/red only for state, always with a word). */
|
|
34
34
|
export type State = "ok" | "warn" | "fail" | "skip" | "info";
|
|
35
|
+
|
|
36
|
+
export type Mood = "good" | "working" | "waiting" | "failed";
|
|
37
|
+
export const FACES: Record<Mood, string> = {
|
|
38
|
+
good: "=^.^=",
|
|
39
|
+
working: "=o.o=",
|
|
40
|
+
waiting: "=-.-=",
|
|
41
|
+
failed: "=x.x=",
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/** The conductor, for the init welcome only (spec §9.3). ASCII already. */
|
|
45
|
+
export function mascot(mood: Mood): [string, string, string] {
|
|
46
|
+
return [" /\\_/\\ .", `(${FACES[mood]})/`, ' (")(")'].map((l) => l.padEnd(9)) as [string, string, string];
|
|
47
|
+
}
|
|
@@ -6,20 +6,17 @@ import { errorMessage, isCatherdError } from "../domain/errors.ts";
|
|
|
6
6
|
import { configDir } from "../infra/paths.ts";
|
|
7
7
|
import { VERSION } from "../infra/version.ts";
|
|
8
8
|
import { doctor } from "../services/doctor.ts";
|
|
9
|
-
import {
|
|
10
|
-
import { aaKey, saveAaKey } from "../services/credentials.ts";
|
|
9
|
+
import { type IndexStatus, refreshIndex } from "../services/catalog-service.ts";
|
|
11
10
|
import { jevKey, saveJevKey, testJevKey } from "../services/jev-service.ts";
|
|
12
|
-
import { type SyncReport, syncSources } from "../services/source-sync.ts";
|
|
13
11
|
import { reinstallCommand } from "../services/doctor-checks.ts";
|
|
14
12
|
import { ensureGlobal, type GlobalInstallDeps, realGlobalInstall } from "../services/global-install.ts";
|
|
15
13
|
import { hasProfileFile, type InitResult, initSetup, moveLegacy } from "../services/setup.ts";
|
|
16
14
|
import { activeName } from "../services/profile-store.ts";
|
|
17
|
-
import { formatRefreshed
|
|
15
|
+
import { formatRefreshed } from "./catalog-command.ts";
|
|
18
16
|
import { mark as markOf, refuseInRole } from "./cli-kit.ts";
|
|
19
17
|
import { formatReport } from "./doctor-command.ts";
|
|
20
18
|
import { mcpHandshake } from "./mcp/handshake.ts";
|
|
21
|
-
import type
|
|
22
|
-
import { mascot } from "./tui/theme.ts";
|
|
19
|
+
import { mascot, type State } from "./glyphs.ts";
|
|
23
20
|
import { type Prompter, prompter } from "./prompt.ts";
|
|
24
21
|
|
|
25
22
|
/** What `init` prints last (spec §9.1): the plugin install commands, to paste into a terminal. */
|
|
@@ -30,7 +27,7 @@ export const PLUGIN_STEPS = [
|
|
|
30
27
|
"Then start a new Claude Code session, so it loads the plugin and the catherd agents.",
|
|
31
28
|
];
|
|
32
29
|
|
|
33
|
-
/** Spec §9.3: the full mascot greets `init`
|
|
30
|
+
/** Spec §9.3: the full mascot greets `init` nowhere else. */
|
|
34
31
|
export function welcomeLines(version: string): string[] {
|
|
35
32
|
const [ears, face, paws] = mascot("good");
|
|
36
33
|
return [ears, `${face} catherd ${version}`, `${paws} herds your coding agents`, ""];
|
|
@@ -72,68 +69,22 @@ export async function jevStep(
|
|
|
72
69
|
}
|
|
73
70
|
|
|
74
71
|
/**
|
|
75
|
-
*
|
|
76
|
-
* key, else a prompt (Enter skips). A typed key is tested with one request: a 200 saves it, a 401 does not,
|
|
77
|
-
* and a key that cannot be checked (no network) is not saved either. It never stops `init`.
|
|
78
|
-
*/
|
|
79
|
-
export async function aaStep(
|
|
80
|
-
ask: Prompter | null,
|
|
81
|
-
d: {
|
|
82
|
-
testAaKey?: (key: string) => Promise<{ result: "ok" | "refused" | "unchecked"; error?: string }>;
|
|
83
|
-
saveAaKey?: (key: string) => void;
|
|
84
|
-
} = {},
|
|
85
|
-
plain = false,
|
|
86
|
-
): Promise<void> {
|
|
87
|
-
const mark = (state: State) => markOf(state, plain);
|
|
88
|
-
if (process.env.ARTIFICIAL_ANALYSIS_API_KEY?.trim()) {
|
|
89
|
-
ask?.skip?.();
|
|
90
|
-
return console.log(`${mark("ok")} Artificial Analysis: using ARTIFICIAL_ANALYSIS_API_KEY`);
|
|
91
|
-
}
|
|
92
|
-
if (aaKey()) {
|
|
93
|
-
ask?.skip?.();
|
|
94
|
-
return console.log(`${mark("ok")} Artificial Analysis: using the saved key`);
|
|
95
|
-
}
|
|
96
|
-
const key = ask ? await ask.secret("Artificial Analysis API key (optional; Enter skips): ") : "";
|
|
97
|
-
if (!key)
|
|
98
|
-
return console.log(
|
|
99
|
-
"- Artificial Analysis: no key; scores come from the keyless sources (add one later with catherd init)",
|
|
100
|
-
);
|
|
101
|
-
const t = await (d.testAaKey ?? testAaKey)(key).catch((e: unknown) => ({
|
|
102
|
-
result: "unchecked" as const,
|
|
103
|
-
error: errorMessage(e),
|
|
104
|
-
}));
|
|
105
|
-
if (t.result === "refused")
|
|
106
|
-
return console.log(`${mark("warn")} Artificial Analysis: the key was refused (401), so it was not saved`);
|
|
107
|
-
if (t.result === "unchecked")
|
|
108
|
-
return console.log(
|
|
109
|
-
`${mark("warn")} Artificial Analysis: the key could not be checked (${t.error ?? "no answer"}), so it was not saved; run catherd init again to retry`,
|
|
110
|
-
);
|
|
111
|
-
try {
|
|
112
|
-
(d.saveAaKey ?? saveAaKey)(key);
|
|
113
|
-
console.log(`${mark("ok")} Artificial Analysis: the key answers; saved with mode 600`);
|
|
114
|
-
} catch (e) {
|
|
115
|
-
console.log(
|
|
116
|
-
`${mark("warn")} Artificial Analysis: could not save the key: ${errorMessage(e).split("\n").join(" ")}`,
|
|
117
|
-
);
|
|
118
|
-
if (isCatherdError(e) && e.fix) console.log(` fix: ${e.fix}`);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Spec 1.2 §9: `init` syncs the public sources in the foreground, after the keys. It never stops `init`;
|
|
72
|
+
* `init` refreshes the Slopalytics index in the foreground, after the Jev key. It never stops `init`;
|
|
124
73
|
* CATHERD_NO_SYNC=1 skips it.
|
|
125
74
|
*/
|
|
126
|
-
export async function syncStep(
|
|
75
|
+
export async function syncStep(
|
|
76
|
+
o: { plain?: boolean; sync?: () => Promise<IndexStatus> } = {},
|
|
77
|
+
): Promise<void> {
|
|
127
78
|
const plain = o.plain === true;
|
|
128
79
|
if (process.env.CATHERD_NO_SYNC === "1")
|
|
129
|
-
return console.log("-
|
|
130
|
-
console.log("
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
80
|
+
return console.log("- catalog: not fetched (CATHERD_NO_SYNC=1); catherd catalog sync fetches it");
|
|
81
|
+
console.log("fetching the Slopalytics index…");
|
|
82
|
+
const s = await (o.sync ?? (() => refreshIndex({ auto: true })))().catch(
|
|
83
|
+
(e: unknown) => ({ error: errorMessage(e), pairs: 0 }) as Pick<IndexStatus, "error" | "pairs">,
|
|
84
|
+
);
|
|
85
|
+
if (s.error === null) return console.log(`${markOf("ok", plain)} Slopalytics: ${s.pairs} pairs indexed`);
|
|
86
|
+
console.log(`${markOf("warn", plain)} Slopalytics: ${s.error.split("\n").join(" ")}`);
|
|
87
|
+
console.log(" fix: catherd catalog sync");
|
|
137
88
|
}
|
|
138
89
|
|
|
139
90
|
/**
|
|
@@ -190,12 +141,12 @@ export function profileLines(r: InitResult, plain = false): string[] {
|
|
|
190
141
|
return out;
|
|
191
142
|
}
|
|
192
143
|
|
|
193
|
-
/** Spec §8 `catherd init [--no-input]`: first-run setup without the
|
|
144
|
+
/** Spec §8 `catherd init [--no-input]`: first-run setup without the panel. */
|
|
194
145
|
export const initCommand = defineCommand({
|
|
195
146
|
meta: {
|
|
196
147
|
name: "init",
|
|
197
148
|
description:
|
|
198
|
-
"First run: the global catherd command, the Jev key,
|
|
149
|
+
"First run: the global catherd command, the Jev key, a fetch of the Slopalytics model index, the default profile, its agents, and a readiness report. Piped, it reads the answers from stdin one per line, a line per question even when this machine skips it (the Jev key, the profile, whether to replace it), and waits for stdin to close; --no-input asks nothing",
|
|
199
150
|
},
|
|
200
151
|
args: {
|
|
201
152
|
...HOST_ARG,
|
|
@@ -224,7 +175,6 @@ export const initCommand = defineCommand({
|
|
|
224
175
|
console.log(`catherd ${VERSION}: setting up in ${configDir()}`);
|
|
225
176
|
await globalStep(VERSION, { skip: (args as { global?: boolean }).global === false, plain });
|
|
226
177
|
await jevStep(ask, {}, plain);
|
|
227
|
-
await aaStep(ask, {}, plain);
|
|
228
178
|
await syncStep({ plain });
|
|
229
179
|
const repo = await gitToplevel(process.cwd());
|
|
230
180
|
// 0.x files go first, so a legacy profile about to be moved aside is never asked about
|