@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 CHANGED
@@ -63,8 +63,8 @@ import {
63
63
  cmdSim,
64
64
  } from "./commands.mjs";
65
65
 
66
- // Keep in sync with cli/package.json.
67
- export const VERSION = "0.144.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
- The model hub's saved runs and blind arena (models.letter.dev)
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)
@@ -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 … (models:read / models:write)
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");
@@ -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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letterstory/cli",
3
- "version": "0.144.0",
3
+ "version": "0.145.2",
4
4
  "description": "Spin up and manage Letterstory phantom blogs from your terminal.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",