@letterstory/cli 0.144.0 → 0.145.2
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 +5 -3
- package/lib/commands/admin.mjs +76 -2
- package/lib/commands/repo.mjs +18 -1
- package/package.json +1 -1
package/lib/cli.mjs
CHANGED
|
@@ -63,8 +63,8 @@ import {
|
|
|
63
63
|
cmdSim,
|
|
64
64
|
} from "./commands.mjs";
|
|
65
65
|
|
|
66
|
-
//
|
|
67
|
-
export const VERSION = "0.
|
|
66
|
+
// Matches cli/package.json; publish-cli.yml rewrites both to the published version.
|
|
67
|
+
export const VERSION = "0.145.2";
|
|
68
68
|
|
|
69
69
|
// Flags that never take a value. Listing them explicitly means `deploy get --json <id>`
|
|
70
70
|
// can't accidentally swallow the id as --json's value. Every `type: "bool"` flag (and its
|
|
@@ -907,7 +907,8 @@ Admin (STAFF key — --staff-key / LETTERSTORY_STAFF_API_KEY; acts before an org
|
|
|
907
907
|
ledger entry. Written by \`npm run lab --
|
|
908
908
|
publish\` in the kernels repo.
|
|
909
909
|
admin models runs [--search T] [--model M] [--starred] | run <id> | star|unstar <id> | scoreboard --model M
|
|
910
|
-
|
|
910
|
+
| requests [--model M] [--outcome O] [--run ID] | request <id> | sampling [--model M --percent P]
|
|
911
|
+
The model hub's saved runs, blind arena and request log (models.letter.dev)
|
|
911
912
|
admin spend summary [--range day|week|month|year | --from D --to D] [--grain day|week|month]
|
|
912
913
|
| reconcile [--from D --to D] | pnl [--range day|week|month|year | --from D --to D]
|
|
913
914
|
| reference add --vendor V [--key K] --metric billed_usd --value N --from D [--to D] --ref "…"
|
|
@@ -1440,6 +1441,7 @@ Repo changes (Claude Code edits your repo in a sandbox; the Letterstory GitHub A
|
|
|
1440
1441
|
repo list [--status running|succeeded|no_changes|failed|cancelled] [--limit n] [--audit <id>]
|
|
1441
1442
|
[--source site_audit_issue|site_quick_check|letterprove_install|visitors_install]
|
|
1442
1443
|
repo publish <run-id> Make PR on a preview in review
|
|
1444
|
+
repo merge <run-id> Merge its open PR (a squash; the repo's branch rules decide)
|
|
1443
1445
|
repo revise <run-id> --instructions <text>|--instructions-file <path|->
|
|
1444
1446
|
Ask for more on a preview; it takes over the preview's link
|
|
1445
1447
|
repo cancel <run-id> Stop it and delete the sandbox; no PR (Discard a preview)
|
package/lib/commands/admin.mjs
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
// admin brand-detection <org-id> (fleet:read)
|
|
20
20
|
// admin brand-detection <org-id> [--term t]... [--exact t]... [--exclude p]... [--note "…"] | --clear [--note "…"] (fleet:instrument)
|
|
21
21
|
// admin recipes list|show|vocab|export|import|save|delete … (recipes:read / recipes:write)
|
|
22
|
-
// admin models runs|run|star|unstar|scoreboard …
|
|
22
|
+
// admin models runs|run|star|unstar|scoreboard|requests|request|sampling … (models:read / models:write)
|
|
23
23
|
// admin replays [--org <id>] [--target <id>] [--status …] [--limit n] (fleet:read)
|
|
24
24
|
// admin replay --org <id> [<response-id> | --target <id>] [--surface …] (fleet:replay)
|
|
25
25
|
// admin create-org --domain acme.com [--name "Acme"] [--test] [--no-key] [--key-name N]
|
|
@@ -4067,7 +4067,7 @@ function spendEntryFromFlags(flags) {
|
|
|
4067
4067
|
|
|
4068
4068
|
const MODELS = "/api/superadmin/models";
|
|
4069
4069
|
const MODELS_USAGE =
|
|
4070
|
-
"Usage: admin models runs [--search TEXT] [--model ballpoint-1] [--starred] [--limit N] | run <id> | star <id> | unstar <id> | scoreboard --model ballpoint-1";
|
|
4070
|
+
"Usage: admin models runs [--search TEXT] [--model ballpoint-1] [--starred] [--limit N] | run <id> | star <id> | unstar <id> | scoreboard --model ballpoint-1 | requests [--model M] [--consumer C] [--org ORG_ID] [--outcome ok|error|waking|rejected] [--operation generate] [--run ID] [--from ISO] [--to ISO] [--limit N] [--cursor NEXT] | request <id> | sampling [--model M --percent P]";
|
|
4071
4071
|
|
|
4072
4072
|
/** The model hub's saved runs and blind arena (models.letter.dev), same functions as its pages. */
|
|
4073
4073
|
async function cmdAdminModels(ctx) {
|
|
@@ -4118,6 +4118,80 @@ async function cmdAdminModels(ctx) {
|
|
|
4118
4118
|
printResult(io, flags, result, () => "");
|
|
4119
4119
|
return 0;
|
|
4120
4120
|
}
|
|
4121
|
+
case "requests": {
|
|
4122
|
+
const params = new URLSearchParams(
|
|
4123
|
+
compact({
|
|
4124
|
+
model: flagStr(flags.model),
|
|
4125
|
+
consumer: flagStr(flags.consumer),
|
|
4126
|
+
org_id: flagStr(flags.org),
|
|
4127
|
+
outcome: flagStr(flags.outcome),
|
|
4128
|
+
operation: flagStr(flags.operation),
|
|
4129
|
+
run: flagStr(flags.run),
|
|
4130
|
+
from: flagStr(flags.from),
|
|
4131
|
+
to: flagStr(flags.to),
|
|
4132
|
+
limit: flagNum(flags.limit)?.toString(),
|
|
4133
|
+
cursor: flagStr(flags.cursor),
|
|
4134
|
+
})
|
|
4135
|
+
);
|
|
4136
|
+
const result = await staffRequest(ctx, "GET", `${MODELS}/requests${params.size ? `?${params}` : ""}`);
|
|
4137
|
+
printResult(io, flags, result, (r) =>
|
|
4138
|
+
(r.requests ?? [])
|
|
4139
|
+
.map(
|
|
4140
|
+
(x) =>
|
|
4141
|
+
`${x.created_at.slice(0, 19)} ${x.model.padEnd(16)} ${x.consumer.padEnd(18)} ${String(x.status).padStart(3)} ${x.outcome.padEnd(8)} ${String(x.latency_ms).padStart(6)} ms ${x.error ? `${x.error.slice(0, 60)} ` : ""}${x.id}`
|
|
4142
|
+
)
|
|
4143
|
+
.concat(r.next ? [`Older: --cursor '${r.next}'`] : [])
|
|
4144
|
+
.join("\n")
|
|
4145
|
+
);
|
|
4146
|
+
return 0;
|
|
4147
|
+
}
|
|
4148
|
+
case "request": {
|
|
4149
|
+
const id = positionals[1];
|
|
4150
|
+
if (!id) throw new CliError("Usage: admin models request <id>");
|
|
4151
|
+
const result = await staffRequest(ctx, "GET", `${MODELS}/requests/${id}`);
|
|
4152
|
+
printResult(io, flags, result, (r) =>
|
|
4153
|
+
[
|
|
4154
|
+
`${r.model} ${r.operation} · ${r.created_at} · ${r.consumer}${r.org_id ? ` · org ${r.org_id}` : ""}${r.run_id ? ` · run ${r.run_id}` : ""}`,
|
|
4155
|
+
`${r.status} ${r.outcome} · ${r.latency_ms} ms${r.error ? ` · ${r.error}` : ""}`,
|
|
4156
|
+
...(r.calls ?? []).map(
|
|
4157
|
+
(c, i) =>
|
|
4158
|
+
` call ${i + 1}: ${c.outcome} ${c.ms} ms ${c.input_tokens ?? 0} → ${c.output_tokens ?? 0} tokens${c.error ? ` · ${c.error}` : ""}`
|
|
4159
|
+
),
|
|
4160
|
+
r.has_bodies
|
|
4161
|
+
? [
|
|
4162
|
+
`request: ${JSON.stringify(r.request)}`,
|
|
4163
|
+
`response: ${JSON.stringify(r.response)}`,
|
|
4164
|
+
...(r.raw ?? []).map((t, i) => `raw ${i + 1}: ${t ?? "—"}`),
|
|
4165
|
+
].join("\n")
|
|
4166
|
+
: "bodies: gone (kept 14 days)",
|
|
4167
|
+
].join("\n")
|
|
4168
|
+
);
|
|
4169
|
+
return 0;
|
|
4170
|
+
}
|
|
4171
|
+
case "sampling": {
|
|
4172
|
+
const model = flagStr(flags.model);
|
|
4173
|
+
const percent = flagNum(flags.percent);
|
|
4174
|
+
if (percent != null) {
|
|
4175
|
+
if (!model) throw new CliError("Usage: admin models sampling --model ballpoint-1 --percent 30");
|
|
4176
|
+
const result = await staffRequest(ctx, "PUT", `${MODELS}/request-sampling`, { model, percent });
|
|
4177
|
+
ok(ctx, `${result.model} keeps ${result.percent}% of its requests`);
|
|
4178
|
+
printResult(io, flags, result, () => "");
|
|
4179
|
+
return 0;
|
|
4180
|
+
}
|
|
4181
|
+
const result = await staffRequest(ctx, "GET", `${MODELS}/request-sampling`);
|
|
4182
|
+
printResult(io, flags, result, (r) =>
|
|
4183
|
+
[
|
|
4184
|
+
`Every model keeps ${r.default_percent}% unless set below; bodies are kept ${r.body_days} days.`,
|
|
4185
|
+
...(r.models ?? [])
|
|
4186
|
+
.filter((m) => !model || m.model === model)
|
|
4187
|
+
.map(
|
|
4188
|
+
(m) =>
|
|
4189
|
+
` ${m.model.padEnd(18)} ${String(m.percent).padStart(6)}% set ${m.updated_at.slice(0, 10)}`
|
|
4190
|
+
),
|
|
4191
|
+
].join("\n")
|
|
4192
|
+
);
|
|
4193
|
+
return 0;
|
|
4194
|
+
}
|
|
4121
4195
|
case "scoreboard": {
|
|
4122
4196
|
const model = flagStr(flags.model);
|
|
4123
4197
|
if (!model) throw new CliError("Usage: admin models scoreboard --model ballpoint-1");
|
package/lib/commands/repo.mjs
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
// repo activity → get_repo_change_activity (what the sandbox is doing; --follow polls)
|
|
5
5
|
// repo list → list_repo_changes repo cancel → cancel_repo_change
|
|
6
6
|
// repo publish → publish_repo_change (Make PR on a preview)
|
|
7
|
+
// repo merge → merge_repo_change (merge its open PR, as a squash)
|
|
7
8
|
// repo revise → revise_repo_change (ask for more on a preview, before the PR)
|
|
8
9
|
// repo say → send_repo_change_message (steer a build while its agent works)
|
|
9
10
|
// repo repos → list_github_app_repos repo branches → list_repo_branches
|
|
@@ -220,6 +221,7 @@ const ARGS = {
|
|
|
220
221
|
},
|
|
221
222
|
cancel_repo_change: { run_id: { positional: 0, required: true, label: "run-id" } },
|
|
222
223
|
publish_repo_change: { run_id: { positional: 0, required: true, label: "run-id" } },
|
|
224
|
+
merge_repo_change: { run_id: { positional: 0, required: true, label: "run-id" } },
|
|
223
225
|
revise_repo_change: {
|
|
224
226
|
run_id: { positional: 0, required: true, label: "run-id" },
|
|
225
227
|
instructions: { value: (flags) => readTextInput(flags, "instructions", "instructions-file") },
|
|
@@ -277,6 +279,7 @@ function describe(run) {
|
|
|
277
279
|
lines.push(` Ask for more: letterstory repo revise ${run.id} --instructions "…"`);
|
|
278
280
|
}
|
|
279
281
|
if (run.prUrl) lines.push(` PR: ${run.prUrl} (${run.prState ?? "state unknown"})`);
|
|
282
|
+
if (run.prState === "open") lines.push(` Merge: letterstory repo merge ${run.id}`);
|
|
280
283
|
if (run.errorCode) lines.push(` Error (${run.errorCode}): ${run.errorDetail ?? ""}`);
|
|
281
284
|
if (run.summary) lines.push(...run.summary.split("\n").map((l) => ` ${l}`));
|
|
282
285
|
return lines.join("\n");
|
|
@@ -316,6 +319,8 @@ export async function cmdRepo(ctx) {
|
|
|
316
319
|
return repoCancel(rest);
|
|
317
320
|
case "publish":
|
|
318
321
|
return repoPublish(rest);
|
|
322
|
+
case "merge":
|
|
323
|
+
return repoMerge(rest);
|
|
319
324
|
case "revise":
|
|
320
325
|
return repoRevise(rest);
|
|
321
326
|
case "say":
|
|
@@ -336,7 +341,7 @@ export async function cmdRepo(ctx) {
|
|
|
336
341
|
return repoSlackTest(rest);
|
|
337
342
|
default:
|
|
338
343
|
throw new CliError(
|
|
339
|
-
`Unknown repo subcommand: ${sub ?? "(none)"}. Try: start, fix-audit, install-letterprove, install-visitors, sites, set-site, reset-site, secrets, set-secret, delete-secret, status, list, publish, revise, say, cancel, repos, branches, prs, settings, suggestions, slack-targets, slack-test`
|
|
344
|
+
`Unknown repo subcommand: ${sub ?? "(none)"}. Try: start, fix-audit, install-letterprove, install-visitors, sites, set-site, reset-site, secrets, set-secret, delete-secret, status, list, publish, merge, revise, say, cancel, repos, branches, prs, settings, suggestions, slack-targets, slack-test`
|
|
340
345
|
);
|
|
341
346
|
}
|
|
342
347
|
}
|
|
@@ -660,6 +665,18 @@ async function repoPublish(ctx) {
|
|
|
660
665
|
return 0;
|
|
661
666
|
}
|
|
662
667
|
|
|
668
|
+
/** `repo merge <run-id>`: merge its open pull request, as a squash; the repo's branch rules decide. */
|
|
669
|
+
async function repoMerge(ctx) {
|
|
670
|
+
const { client, flags, io } = ctx;
|
|
671
|
+
const result = await client.callTool(
|
|
672
|
+
"merge_repo_change",
|
|
673
|
+
argsFromFlags(ARGS.merge_repo_change, flags, ctx.positionals)
|
|
674
|
+
);
|
|
675
|
+
ok(ctx, result.message);
|
|
676
|
+
printResult(io, flags, result.run, describe);
|
|
677
|
+
return 0;
|
|
678
|
+
}
|
|
679
|
+
|
|
663
680
|
/** `repo say <run-id> <message…>` (or --message): steer a build while its agent works. */
|
|
664
681
|
async function repoSay(ctx) {
|
|
665
682
|
const { client, flags, io } = ctx;
|