@letterstory/cli 0.176.96 → 0.176.98
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/lib/cli.mjs +26 -2
- package/lib/commands/admin.mjs +26 -1
- package/lib/commands/models.mjs +219 -0
- package/lib/commands.mjs +1 -0
- package/lib/parked.mjs +1 -1
- package/package.json +1 -1
package/lib/cli.mjs
CHANGED
|
@@ -82,11 +82,12 @@ import {
|
|
|
82
82
|
cmdReplicate,
|
|
83
83
|
cmdDeploy,
|
|
84
84
|
cmdRefs,
|
|
85
|
+
cmdModels,
|
|
85
86
|
} from "./commands.mjs";
|
|
86
87
|
import { fetchParkedSections, withoutParked } from "./parked.mjs";
|
|
87
88
|
|
|
88
89
|
// Matches cli/package.json; publish-cli.yml rewrites both to the published version.
|
|
89
|
-
export const VERSION = "0.176.
|
|
90
|
+
export const VERSION = "0.176.98";
|
|
90
91
|
|
|
91
92
|
/** The exit code for a gate refusal (CARD_REQUIRED, PAYMENT_FAILED, OUT_OF_TOKENS): 1 is a plain error. */
|
|
92
93
|
export const EXIT_PAYMENT_REQUIRED = 3;
|
|
@@ -130,7 +131,9 @@ export const BOOLEAN_FLAGS = new Set([
|
|
|
130
131
|
"start",
|
|
131
132
|
// site research-builds: run now even when the stored pass is under an hour old.
|
|
132
133
|
"fresh",
|
|
133
|
-
// admin letterpress
|
|
134
|
+
// admin letterpress tokens: today so far against yesterday and a week ago.
|
|
135
|
+
"so-far",
|
|
136
|
+
// admin letterpress telemetry | prs | tokens: every org, test and demo orgs included.
|
|
134
137
|
"all-orgs",
|
|
135
138
|
// admin letterpress prs | failures: since the first PR or build (all_time).
|
|
136
139
|
"all-time",
|
|
@@ -415,6 +418,10 @@ export const BOOLEAN_FLAGS = new Set([
|
|
|
415
418
|
"dry",
|
|
416
419
|
// admin billing emails --bounced
|
|
417
420
|
"bounced",
|
|
421
|
+
// models add: train once the links are added. models confirm: keep the found links not named, or don't train yet.
|
|
422
|
+
"train",
|
|
423
|
+
"keep-rest",
|
|
424
|
+
"no-train",
|
|
418
425
|
]);
|
|
419
426
|
|
|
420
427
|
// Tiny argv parser: `--flag value`, `--flag=value`, boolean `--flag`, and positionals.
|
|
@@ -2017,6 +2024,22 @@ Refs (Visual Refs: other companies' brands to design something new after, apart
|
|
|
2017
2024
|
Every brand's direction for it, as one file
|
|
2018
2025
|
refs brief-show <brief-id> The brief, once ready
|
|
2019
2026
|
|
|
2027
|
+
Models (your own writing models, each trained on writing you give it so it writes in your voice):
|
|
2028
|
+
models [list] [--archived] Your models: where each stands, its links, its version
|
|
2029
|
+
models create "<name>" [--budget <usd>] [--description <d>]
|
|
2030
|
+
Budget: the most one training run may cost (default $100)
|
|
2031
|
+
models show <id> Its links, its training and what it cost
|
|
2032
|
+
models add <id> <url>… [--train] Links to pieces of your writing; --train starts training
|
|
2033
|
+
models find <id> "<the writing to find>" [--max <n>] Letter finds the links and proposes them
|
|
2034
|
+
models confirm <id> [<link-id>…] [--keep-rest] [--no-train]
|
|
2035
|
+
Confirm what was found (all, or the ids) and train on it
|
|
2036
|
+
models remove <id> <link-id>… Take links out of the next version
|
|
2037
|
+
models train <id> [--retry] Train the next version, or try a stopped one again
|
|
2038
|
+
models stop <id> Stop what is training; what it spent is charged
|
|
2039
|
+
models write <id> "<what to write>" [--words <n>] Write with its trained version
|
|
2040
|
+
models update <id> [--name <n>] [--budget <usd>] [--description <d>]
|
|
2041
|
+
models archive <id> | models restore <id>
|
|
2042
|
+
|
|
2020
2043
|
Bench (Benches: open-source benchmarks on their own domains, kept running; @letterstory.com only):
|
|
2021
2044
|
bench [list] Your benches: stage, what each waits on, domain
|
|
2022
2045
|
bench create (--prompt "<what it is and how it works>"|--prompt-file <path>) [--name <n>] [--domain <d>]
|
|
@@ -2157,6 +2180,7 @@ const CLIENT_COMMANDS = {
|
|
|
2157
2180
|
replicate: cmdReplicate,
|
|
2158
2181
|
deploy: cmdDeploy,
|
|
2159
2182
|
refs: cmdRefs,
|
|
2183
|
+
models: cmdModels,
|
|
2160
2184
|
// Platform-scoped (staff key) — the only commands that act BEFORE an org exists.
|
|
2161
2185
|
admin: cmdAdmin,
|
|
2162
2186
|
};
|
package/lib/commands/admin.mjs
CHANGED
|
@@ -5261,9 +5261,10 @@ const ENGINE = "/api/superadmin/engine-settings";
|
|
|
5261
5261
|
const LETTERPRESS = "/api/superadmin/letterpress";
|
|
5262
5262
|
const CODING_TELEMETRY = "/api/superadmin/coding-telemetry";
|
|
5263
5263
|
const PULL_REQUESTS = "/api/superadmin/pull-requests";
|
|
5264
|
+
const PLATFORM_USAGE = "/api/superadmin/usage/platform";
|
|
5264
5265
|
const BUILD_FAILURES = "/api/superadmin/build-failures";
|
|
5265
5266
|
const ENGINE_USAGE =
|
|
5266
|
-
"Usage: admin letterpress status | gaps | telemetry [--days N] [--harness h] [--all-orgs] | prs [--days N | --from YYYY-MM-DD [--to YYYY-MM-DD] | --all-time] [--day YYYY-MM-DD] [--org id] [--state merged|open|closed] [--top N] [--tz zone] [--all-orgs] | failures [--days N | --from YYYY-MM-DD [--to YYYY-MM-DD] | --all-time] [--day YYYY-MM-DD] [--org id] [--failure ours|agent|setup|undelivered] [--cause c] [--stopped-at step] [--repo owner/name] [--slow] [--tz zone] [--all-orgs] | blocked [--days N] [--org id] [--all-orgs] | unblock <org-id> <host> <subject> | decisions <org-id> [--site host] [--surface plan|fix|build|site] | undo <org-id> <host> <decision-id> | runs [--status s] [--error-code c] [--first-review-cause agent|context|setup|unknown] [--pr merged|closed|open|none] [--harness h] [--days N] | beyond [--limit N] [--before ISO] | companies | company <org-id> [--site host] | facts <org-id> [--site host] | fits [--recipe id] [--org id] [--site host] [--fits yes|no|unknown|waiting] [--limit N] | replay <run-id> | mark <run-id> <item-id> <right|weak|wrong|lead|gap|recipe|clear> [--note …] | settings | set <weights.<ask>.<reading>|lenses.<lens>.bar.<reading>|lenses.<lens>.bar|lenses.<lens>.research.turns|lenses.<lens>.research.ms|asks.<lens>.ask|asks.<lens>.guidance|dismissals.block_days|preflight.off|explore.ask|explore.research.turns|explore.research.ms|explore.every_days> <value>";
|
|
5267
|
+
"Usage: admin letterpress status | gaps | telemetry [--days N] [--harness h] [--all-orgs] | tokens [--days N] [--so-far] [--all-orgs] | prs [--days N | --from YYYY-MM-DD [--to YYYY-MM-DD] | --all-time] [--day YYYY-MM-DD] [--org id] [--state merged|open|closed] [--top N] [--tz zone] [--all-orgs] | failures [--days N | --from YYYY-MM-DD [--to YYYY-MM-DD] | --all-time] [--day YYYY-MM-DD] [--org id] [--failure ours|agent|setup|undelivered] [--cause c] [--stopped-at step] [--repo owner/name] [--slow] [--tz zone] [--all-orgs] | blocked [--days N] [--org id] [--all-orgs] | unblock <org-id> <host> <subject> | decisions <org-id> [--site host] [--surface plan|fix|build|site] | undo <org-id> <host> <decision-id> | runs [--status s] [--error-code c] [--first-review-cause agent|context|setup|unknown] [--pr merged|closed|open|none] [--harness h] [--days N] | beyond [--limit N] [--before ISO] | companies | company <org-id> [--site host] | facts <org-id> [--site host] | fits [--recipe id] [--org id] [--site host] [--fits yes|no|unknown|waiting] [--limit N] | replay <run-id> | mark <run-id> <item-id> <right|weak|wrong|lead|gap|recipe|clear> [--note …] | settings | set <weights.<ask>.<reading>|lenses.<lens>.bar.<reading>|lenses.<lens>.bar|lenses.<lens>.research.turns|lenses.<lens>.research.ms|asks.<lens>.ask|asks.<lens>.guidance|dismissals.block_days|preflight.off|explore.ask|explore.research.turns|explore.research.ms|explore.every_days> <value>";
|
|
5267
5268
|
|
|
5268
5269
|
/** One number of the settings from a dotted path and a value: the PUT body save_engine_settings reads. */
|
|
5269
5270
|
function engineSettingsPatch(path, value) {
|
|
@@ -5506,6 +5507,30 @@ async function cmdAdminLetterpress(ctx) {
|
|
|
5506
5507
|
);
|
|
5507
5508
|
return 0;
|
|
5508
5509
|
}
|
|
5510
|
+
case "tokens": {
|
|
5511
|
+
// get_platform_usage: every customer's tokens day by day, Letterpress's on its own (Letterpress → Today).
|
|
5512
|
+
const q = new URLSearchParams({ days: String(flags.days ?? 30) });
|
|
5513
|
+
if (flagBool(flags["all-orgs"])) q.set("customers", "0");
|
|
5514
|
+
if (flagBool(flags["so-far"])) q.set("so_far", "1");
|
|
5515
|
+
const r = await staffRequest(ctx, "GET", `${PLATFORM_USAGE}?${q}`);
|
|
5516
|
+
const n = (v) => Math.round(v).toLocaleString("en-US");
|
|
5517
|
+
printResult(io, flags, r, (x) =>
|
|
5518
|
+
[
|
|
5519
|
+
`Tokens, ${x.since_day} to ${x.until_day} (${x.tz})${x.customer_only ? ", customers only" : ""}: ${n(x.totals.tokens)}, Letterpress ${n(x.totals.letterpress)}`,
|
|
5520
|
+
`by product: ${x.categories.map((c) => `${c.label} ${n(x.totals.by_category[c.key] ?? 0)}`).join(", ") || "—"}`,
|
|
5521
|
+
...x.series.map(
|
|
5522
|
+
(d) =>
|
|
5523
|
+
`${d.day} ${n(d.tokens).padStart(10)} tokens ${n(d.letterpress).padStart(8)} Letterpress`
|
|
5524
|
+
),
|
|
5525
|
+
...(x.so_far
|
|
5526
|
+
? [
|
|
5527
|
+
`today so far ${n(x.so_far.today)}; by this time yesterday ${n(x.so_far.yesterday)}, a week ago ${n(x.so_far.week_ago)}`,
|
|
5528
|
+
]
|
|
5529
|
+
: []),
|
|
5530
|
+
].join("\n")
|
|
5531
|
+
);
|
|
5532
|
+
return 0;
|
|
5533
|
+
}
|
|
5509
5534
|
case "prs": {
|
|
5510
5535
|
// get_pull_requests (the days) or, with --day / --org / --state, list_pull_requests (the PRs behind them).
|
|
5511
5536
|
const listing = flags.day || flags.org || flags.state;
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
// `models` — your own writing models from a terminal (docs/architecture/writing-models.md): each post-trained
|
|
2
|
+
// on writing you give it, so it writes in your voice. Thin wrappers over the writing model tools
|
|
3
|
+
// (create_writing_model, list_writing_models, get_writing_model, update_writing_model, add_writing_model_links,
|
|
4
|
+
// find_writing_model_links, confirm_writing_model_links, remove_writing_model_links, train_writing_model,
|
|
5
|
+
// write_with_writing_model), the same lib functions the app calls.
|
|
6
|
+
//
|
|
7
|
+
// letterstory models [list] [--archived]
|
|
8
|
+
// letterstory models create "<name>" [--budget <usd>] [--description <d>]
|
|
9
|
+
// letterstory models show <id>
|
|
10
|
+
// letterstory models add <id> <url>… [--train]
|
|
11
|
+
// letterstory models find <id> "<the writing to find>" [--max <n>]
|
|
12
|
+
// letterstory models confirm <id> [<link-id>…] [--keep-rest] [--no-train] no link ids: all of them
|
|
13
|
+
// letterstory models remove <id> <link-id>…
|
|
14
|
+
// letterstory models train <id> [--retry]
|
|
15
|
+
// letterstory models stop <id> stop what is training
|
|
16
|
+
// letterstory models write <id> "<what to write>" [--words <n>]
|
|
17
|
+
// letterstory models update <id> [--name <n>] [--budget <usd>] [--description <d>]
|
|
18
|
+
// letterstory models archive <id> | models restore <id>
|
|
19
|
+
|
|
20
|
+
import { CliError } from "../client.mjs";
|
|
21
|
+
import { argsFromFlags, flagStr, printResult, refuseUnknownFlags } from "./shared.mjs";
|
|
22
|
+
|
|
23
|
+
const modelId = { positional: 0, label: "model-id", required: true };
|
|
24
|
+
const rest = (from) => (flags, positionals) => {
|
|
25
|
+
const list = positionals.slice(from).filter(Boolean);
|
|
26
|
+
return list.length ? list : undefined;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const ARGS = {
|
|
30
|
+
create_writing_model: {
|
|
31
|
+
name: { value: (flags, positionals) => flagStr(flags.name) ?? (positionals.join(" ").trim() || undefined) },
|
|
32
|
+
description: "description",
|
|
33
|
+
budget_usd: { flag: "budget", type: "num" },
|
|
34
|
+
},
|
|
35
|
+
list_writing_models: { archived: { flag: "archived", type: "bool" } },
|
|
36
|
+
get_writing_model: { model_id: modelId },
|
|
37
|
+
update_writing_model: {
|
|
38
|
+
model_id: modelId,
|
|
39
|
+
name: "name",
|
|
40
|
+
description: "description",
|
|
41
|
+
budget_usd: { flag: "budget", type: "num" },
|
|
42
|
+
archived: { internal: "models archive / models restore" },
|
|
43
|
+
},
|
|
44
|
+
add_writing_model_links: {
|
|
45
|
+
model_id: modelId,
|
|
46
|
+
urls: { value: rest(1) },
|
|
47
|
+
train: { flag: "train", type: "bool" },
|
|
48
|
+
},
|
|
49
|
+
find_writing_model_links: {
|
|
50
|
+
model_id: modelId,
|
|
51
|
+
prompt: { value: (flags, positionals) => positionals.slice(1).join(" ").trim() || undefined },
|
|
52
|
+
max_links: { flag: "max", type: "num" },
|
|
53
|
+
},
|
|
54
|
+
confirm_writing_model_links: {
|
|
55
|
+
model_id: modelId,
|
|
56
|
+
confirm: { value: (flags, positionals) => rest(1)(flags, positionals) ?? "all" },
|
|
57
|
+
decline_rest: { flag: "keep-rest", type: "bool", invert: true },
|
|
58
|
+
train: { flag: "no-train", type: "bool", invert: true },
|
|
59
|
+
},
|
|
60
|
+
remove_writing_model_links: { model_id: modelId, source_ids: { value: rest(1) } },
|
|
61
|
+
train_writing_model: { model_id: modelId, retry: { flag: "retry", type: "bool" } },
|
|
62
|
+
cancel_writing_model_run: { model_id: modelId },
|
|
63
|
+
write_with_writing_model: {
|
|
64
|
+
model_id: modelId,
|
|
65
|
+
ask: { value: (flags, positionals) => positionals.slice(1).join(" ").trim() || undefined },
|
|
66
|
+
max_words: { flag: "words", type: "num" },
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
function call(ctx, tool, extra = {}) {
|
|
71
|
+
return ctx.client.callTool(tool, { ...argsFromFlags(ARGS[tool], ctx.flags, ctx.positionals), ...extra });
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const STATE = {
|
|
75
|
+
empty: "no writing yet",
|
|
76
|
+
confirming: "links to confirm",
|
|
77
|
+
untrained: "not trained",
|
|
78
|
+
training: "training",
|
|
79
|
+
stopped: "needs you",
|
|
80
|
+
ready: "ready",
|
|
81
|
+
archived: "archived",
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export async function cmdModels(ctx) {
|
|
85
|
+
const sub = ctx.positionals[0];
|
|
86
|
+
const next = { ...ctx, positionals: ctx.positionals.slice(1) };
|
|
87
|
+
switch (sub) {
|
|
88
|
+
case undefined:
|
|
89
|
+
case "list":
|
|
90
|
+
case "ls": {
|
|
91
|
+
refuseUnknownFlags(next, ARGS.list_writing_models, []);
|
|
92
|
+
const result = await call(next, "list_writing_models");
|
|
93
|
+
printResult(next.io, next.flags, result, (r) => {
|
|
94
|
+
if (!r.models?.length) return 'No models yet. Make one: models create "Our voice"';
|
|
95
|
+
return r.models
|
|
96
|
+
.map(
|
|
97
|
+
(m) =>
|
|
98
|
+
`${m.id} ${m.name.slice(0, 28).padEnd(28)} ${(STATE[m.state] ?? m.state).padEnd(17)} ${m.links.confirmed} links${m.version ? ` v${m.version.n}` : ""}`
|
|
99
|
+
)
|
|
100
|
+
.join("\n");
|
|
101
|
+
});
|
|
102
|
+
return 0;
|
|
103
|
+
}
|
|
104
|
+
case "create": {
|
|
105
|
+
refuseUnknownFlags(next, ARGS.create_writing_model, ["name"]);
|
|
106
|
+
const args = argsFromFlags(ARGS.create_writing_model, next.flags, next.positionals);
|
|
107
|
+
if (!args.name) throw new CliError('Name it: models create "Our voice" [--budget 100]');
|
|
108
|
+
return modelShow(next, await next.client.callTool("create_writing_model", args));
|
|
109
|
+
}
|
|
110
|
+
case "show":
|
|
111
|
+
return modelShow(next, await call(next, "get_writing_model"));
|
|
112
|
+
case "add": {
|
|
113
|
+
refuseUnknownFlags(next, ARGS.add_writing_model_links, []);
|
|
114
|
+
const args = argsFromFlags(ARGS.add_writing_model_links, next.flags, next.positionals);
|
|
115
|
+
if (!args.urls) throw new CliError("Give links: models add <id> <url> [<url>…] [--train]");
|
|
116
|
+
const result = await next.client.callTool("add_writing_model_links", args);
|
|
117
|
+
return trainingShow(next, result, `Added ${result.added} links.`);
|
|
118
|
+
}
|
|
119
|
+
case "find": {
|
|
120
|
+
refuseUnknownFlags(next, ARGS.find_writing_model_links, []);
|
|
121
|
+
const args = argsFromFlags(ARGS.find_writing_model_links, next.flags, next.positionals);
|
|
122
|
+
if (!args.prompt) throw new CliError('Say what to find: models find <id> "every post on our blog"');
|
|
123
|
+
const result = await next.client.callTool("find_writing_model_links", args);
|
|
124
|
+
printResult(next.io, next.flags, result, (r) =>
|
|
125
|
+
[
|
|
126
|
+
`Found ${r.proposed.length} links (of ${r.considered} pages), waiting to be confirmed:`,
|
|
127
|
+
...r.proposed.map((s) => ` ${s.id} ${s.title ? `${s.title.slice(0, 50)} ` : ""}${s.url}`),
|
|
128
|
+
"",
|
|
129
|
+
`Confirm them all and train: models confirm ${args.model_id}`,
|
|
130
|
+
`Or only some: models confirm ${args.model_id} <link-id> [<link-id>…]`,
|
|
131
|
+
].join("\n")
|
|
132
|
+
);
|
|
133
|
+
return 0;
|
|
134
|
+
}
|
|
135
|
+
case "confirm": {
|
|
136
|
+
refuseUnknownFlags(next, ARGS.confirm_writing_model_links, []);
|
|
137
|
+
const result = await call(next, "confirm_writing_model_links");
|
|
138
|
+
return trainingShow(
|
|
139
|
+
next,
|
|
140
|
+
result,
|
|
141
|
+
`Confirmed ${result.confirmed} links${result.declined ? `, declined ${result.declined}` : ""}.`
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
case "remove": {
|
|
145
|
+
const args = argsFromFlags(ARGS.remove_writing_model_links, next.flags, next.positionals);
|
|
146
|
+
if (!args.source_ids) throw new CliError("Give the link ids: models remove <id> <link-id> [<link-id>…]");
|
|
147
|
+
const result = await next.client.callTool("remove_writing_model_links", args);
|
|
148
|
+
printResult(next.io, next.flags, result, (r) => `Removed ${r.removed} links.`);
|
|
149
|
+
return 0;
|
|
150
|
+
}
|
|
151
|
+
case "train": {
|
|
152
|
+
refuseUnknownFlags(next, ARGS.train_writing_model, []);
|
|
153
|
+
const result = await call(next, "train_writing_model");
|
|
154
|
+
printResult(next.io, next.flags, result, (r) => runLine(r.run));
|
|
155
|
+
return 0;
|
|
156
|
+
}
|
|
157
|
+
case "stop": {
|
|
158
|
+
refuseUnknownFlags(next, ARGS.cancel_writing_model_run, []);
|
|
159
|
+
const result = await call(next, "cancel_writing_model_run");
|
|
160
|
+
printResult(next.io, next.flags, result, (r) => runLine(r.run));
|
|
161
|
+
return 0;
|
|
162
|
+
}
|
|
163
|
+
case "write": {
|
|
164
|
+
refuseUnknownFlags(next, ARGS.write_with_writing_model, []);
|
|
165
|
+
const args = argsFromFlags(ARGS.write_with_writing_model, next.flags, next.positionals);
|
|
166
|
+
if (!args.ask) throw new CliError('Say what to write: models write <id> "Write a 300-word post on…"');
|
|
167
|
+
const result = await next.client.callTool("write_with_writing_model", args);
|
|
168
|
+
printResult(next.io, next.flags, result, (r) => r.text);
|
|
169
|
+
return 0;
|
|
170
|
+
}
|
|
171
|
+
case "update":
|
|
172
|
+
refuseUnknownFlags(next, ARGS.update_writing_model, []);
|
|
173
|
+
return modelShow(next, await call(next, "update_writing_model"));
|
|
174
|
+
case "archive":
|
|
175
|
+
case "restore":
|
|
176
|
+
return modelShow(next, await call(next, "update_writing_model", { archived: sub === "archive" }));
|
|
177
|
+
default:
|
|
178
|
+
throw new CliError(
|
|
179
|
+
"Usage: models [list|create|show|add|find|confirm|remove|train|stop|write|update|archive|restore]"
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function runLine(run) {
|
|
185
|
+
if (!run) return "Not trained yet.";
|
|
186
|
+
const where = run.blockedOn ? `stopped: ${run.blockedOn}` : (run.stageNote ?? run.stage);
|
|
187
|
+
return `Version ${run.n}: ${run.status}, ${where} ($${run.spentUsd} of up to $${run.budgetUsd})`;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function trainingShow(ctx, result, line) {
|
|
191
|
+
printResult(ctx.io, ctx.flags, result, (r) => {
|
|
192
|
+
const t = r.training;
|
|
193
|
+
const after = !t
|
|
194
|
+
? ""
|
|
195
|
+
: t.started
|
|
196
|
+
? `\n${runLine(t.run)}\nFollow it: models show ${r.model.id}`
|
|
197
|
+
: `\nNot training: ${t.reason}`;
|
|
198
|
+
return `${line}${after}`;
|
|
199
|
+
});
|
|
200
|
+
return 0;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function modelShow(ctx, result) {
|
|
204
|
+
printResult(ctx.io, ctx.flags, result, (r) => {
|
|
205
|
+
const m = r.model;
|
|
206
|
+
const lines = [
|
|
207
|
+
`${m.name} ${m.id}`,
|
|
208
|
+
` ${STATE[m.state] ?? m.state}, built on ${m.base}, up to $${m.budgetUsd} a training run`,
|
|
209
|
+
` ${m.links.confirmed} links (${m.links.read} read, ${m.links.words} words)${m.links.proposed ? `, ${m.links.proposed} to confirm` : ""}`,
|
|
210
|
+
` ${runLine(m.latestRun)}`,
|
|
211
|
+
];
|
|
212
|
+
const proposed = m.sources.filter((s) => s.status === "proposed");
|
|
213
|
+
if (proposed.length) lines.push("", `Confirm the links found and train: models confirm ${m.id}`);
|
|
214
|
+
else if (!m.links.confirmed)
|
|
215
|
+
lines.push("", `Give it writing: models add ${m.id} <url>… or models find ${m.id} "<where>"`);
|
|
216
|
+
return lines.join("\n");
|
|
217
|
+
});
|
|
218
|
+
return 0;
|
|
219
|
+
}
|
package/lib/commands.mjs
CHANGED
package/lib/parked.mjs
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// parked. Which are parked is decided on the server (Super Admin → Parked), so `help` asks it
|
|
4
4
|
// (GET /api/parked, `cli_sections`). This list is only what it falls back to when the server can't
|
|
5
5
|
// be reached: the state when this version shipped.
|
|
6
|
-
export const PARKED_HELP_SECTIONS = ["Seers", "Shredder", "Ledes"];
|
|
6
|
+
export const PARKED_HELP_SECTIONS = ["Seers", "Shredder", "Ledes", "Models"];
|
|
7
7
|
|
|
8
8
|
/** The parked help sections the server names now, or null when it can't say in time. */
|
|
9
9
|
export async function fetchParkedSections(url, { fetchImpl = globalThis.fetch, timeoutMs = 1500 } = {}) {
|