@letterstory/cli 0.177.8 → 0.177.10

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
@@ -87,7 +87,7 @@ import {
87
87
  import { fetchParkedSections, withoutParked } from "./parked.mjs";
88
88
 
89
89
  // Matches cli/package.json; publish-cli.yml rewrites both to the published version.
90
- export const VERSION = "0.177.8";
90
+ export const VERSION = "0.177.10";
91
91
 
92
92
  /** The exit code for a gate refusal (CARD_REQUIRED, PAYMENT_FAILED, OUT_OF_TOKENS): 1 is a plain error. */
93
93
  export const EXIT_PAYMENT_REQUIRED = 3;
@@ -100,6 +100,11 @@ export const BOOLEAN_FLAGS = new Set([
100
100
  "connect-only",
101
101
  // admin demo ideas: every live recipe, not only the Ideas that fit.
102
102
  "library",
103
+ // admin demo sweep: prepare each recipe's org for a person to start, pull requests, make orgs, skip the refit.
104
+ "prepare",
105
+ "pull-requests",
106
+ "make-orgs",
107
+ "no-refit",
103
108
  // admin targets check: check every target again, checked or not.
104
109
  "recheck",
105
110
  // build list: also move the builds' open changes along while polling.
@@ -1204,6 +1209,14 @@ Admin (STAFF key — --staff-key / LETTERSTORY_STAFF_API_KEY; acts before an org
1204
1209
  admin demo check <org-id> Is it ready to show? Competitors, targets, answers, the
1205
1210
  Plan's rows, the issues open, no PR open, GitHub, the
1206
1211
  phantom's site. Exits 1 when not ready.
1212
+ admin demo ideas <org-id> [--library] [--preview] [--force] | --status [--id <run>]
1213
+ Start every Idea on a demo org for real (pull requests, or
1214
+ previews), or read how the latest run went.
1215
+ admin demo sweep [--prepare] [--pull-requests] [--recipes a,b] [--orgs a,b] [--exclude a,b] [--buy a,b] [--make-orgs] [--no-refit] | --status [--id <sweep>]
1216
+ Every live recipe end to end: each gets a test org whose Ideas
1217
+ offers it, its Start pressed (or, with --prepare, left for you
1218
+ to press in the app), and its outcome judged, with who owns a
1219
+ failure. Exits 1 when the sweep failed.
1207
1220
  admin brand-detection <org-id> [--term <t>]... [--exact <t>]... [--exclude <phrase>]... [--note "…"] | --clear [--note "…"]
1208
1221
  How a client's brand is detected in AI answers, with
1209
1222
  every earlier setting; with flags, set the exact set
@@ -6910,7 +6910,7 @@ const DEMO_ITEM_FLAGS = [
6910
6910
  "setup",
6911
6911
  ];
6912
6912
  const DEMO_USAGE =
6913
- "Usage: admin demo guide | list [--enrollable] | show <org-id> [--moves] | set <org-id> [--enroll] [--unenroll] [--file <state.json>] [--phantom parked|shown] [--main-blog …] [--visitors …] [--probes not_run|shown] [--cms hidden|shown] [--publish-target …] [--search-console …] [--github …] [--domain …] [--setup fresh|finished] [--domain-cap n] [--inject <key>]... [--hide <key>]... [--save-as <name>] [--baseline <owner/repo>[@sha] | --clear-baseline] [--copy-from <org-id> [--copy-scenarios a,b] [--overwrite]] [--auto-reset <scenario>|off] [--every <minutes>] [--release] | domains | new <domain> [--name <n>] [--brief <t>] [--full] [--host vercel|pages] [--no-wait] | teardown <org-id> [--keep-repo] | bootstrap <org-id>... --from <org-id> [--scenarios a,b] [--overwrite] | reset <org-id> <scenario> [--clean-slate] [--close-prs since_applied|all_open|none] [--hold <minutes>] [--restore-repo revert|force] [--rebuild] [--reread-company] [--forget-company] [--forget-site] | check <org-id> | ideas <org-id> [--library] [--preview] [--force] | ideas <org-id> --status [--id <run-id>]";
6913
+ "Usage: admin demo guide | list [--enrollable] | show <org-id> [--moves] | set <org-id> [--enroll] [--unenroll] [--file <state.json>] [--phantom parked|shown] [--main-blog …] [--visitors …] [--probes not_run|shown] [--cms hidden|shown] [--publish-target …] [--search-console …] [--github …] [--domain …] [--setup fresh|finished] [--domain-cap n] [--inject <key>]... [--hide <key>]... [--save-as <name>] [--baseline <owner/repo>[@sha] | --clear-baseline] [--copy-from <org-id> [--copy-scenarios a,b] [--overwrite]] [--auto-reset <scenario>|off] [--every <minutes>] [--release] | domains | new <domain> [--name <n>] [--brief <t>] [--full] [--host vercel|pages] [--no-wait] | teardown <org-id> [--keep-repo] | bootstrap <org-id>... --from <org-id> [--scenarios a,b] [--overwrite] | reset <org-id> <scenario> [--clean-slate] [--close-prs since_applied|all_open|none] [--hold <minutes>] [--restore-repo revert|force] [--rebuild] [--reread-company] [--forget-company] [--forget-site] | check <org-id> | ideas <org-id> [--library] [--preview] [--force] | ideas <org-id> --status [--id <run-id>] | sweep [--prepare] [--pull-requests] [--recipes <id>,…] [--orgs <id>,…] [--exclude <id>,…] [--buy <recipe-id>,…] [--make-orgs] [--no-refit] | sweep --status [--id <sweep-id>]";
6914
6914
 
6915
6915
  const readinessLines = (r) =>
6916
6916
  r
@@ -6936,6 +6936,49 @@ const ideasRunLines = (run) =>
6936
6936
  ].join("\n")
6937
6937
  : "No run yet.";
6938
6938
 
6939
+ const SWEEP_MARK = {
6940
+ ready: "READY",
6941
+ passed: "PASS",
6942
+ failed: "FAIL",
6943
+ waiting: "WAIT",
6944
+ held: "HELD",
6945
+ no_org: "NO ORG",
6946
+ not_derivable: "NO PROFILE",
6947
+ pending: "…",
6948
+ };
6949
+
6950
+ /** A recipe sweep's report (get_recipe_sweep), one block per recipe. */
6951
+ const recipeSweepLines = (sweep, appUrl) =>
6952
+ sweep
6953
+ ? [
6954
+ `${sweep.summary} (${sweep.state}${sweep.note && sweep.state !== "done" ? `: ${sweep.note}` : ""})`,
6955
+ `${sweep.start ? "started by the sweep" : "prepared for you to start"}, ${sweep.delivery === "build" ? "previews" : "pull requests"}, by ${sweep.started_by_name ?? "staff"} at ${sweep.created_at} sweep ${sweep.id}`,
6956
+ ...sweep.items.map((i) =>
6957
+ [
6958
+ `\n${(SWEEP_MARK[i.result] ?? i.result).padEnd(10)} ${i.name}`,
6959
+ i.org ? ` org: ${i.org.name} (${i.org.host})${i.org.made ? ", made for it" : ""}` : null,
6960
+ i.fit[0] ? ` fit: ${i.fit[0].fits}: ${i.fit[0].why}` : null,
6961
+ i.row
6962
+ ? ` row: ${i.row.title}${i.row.line ? ` / ${i.row.line}` : ""}${i.row.button ? ` [${i.row.button}]` : ""}`
6963
+ : null,
6964
+ i.did
6965
+ ? ` did: ${i.did.tools.join(", ") || "no tools"}${i.did.sandbox ? ", a sandbox" : ""}${i.did.cost_usd != null ? `, $${i.did.cost_usd}` : ""}`
6966
+ : null,
6967
+ i.verdict ? ` outcome: ${i.verdict.state}: ${i.verdict.observed}` : null,
6968
+ i.cause ? ` cause: ${i.cause.owner}: ${i.cause.why}` : null,
6969
+ i.profile_problem ? ` profile: ${i.profile_problem}` : null,
6970
+ i.profile?.unsettable?.length
6971
+ ? ` can't be made on a test org: ${i.profile.unsettable.map((u) => u.key).join(", ")}`
6972
+ : null,
6973
+ i.links?.ideas ? ` open: ${appUrl}${i.links.ideas}` : null,
6974
+ i.links?.build ? ` build: ${appUrl}${i.links.build}` : null,
6975
+ ]
6976
+ .filter(Boolean)
6977
+ .join("\n")
6978
+ ),
6979
+ ].join("\n")
6980
+ : "No sweep yet.";
6981
+
6939
6982
  async function cmdAdminDemo(ctx) {
6940
6983
  const { flags, positionals, io } = ctx;
6941
6984
  const sub = positionals[0];
@@ -7194,6 +7237,30 @@ async function cmdAdminDemo(ctx) {
7194
7237
  printResult(io, flags, result, (r) => ideasRunLines(r.run));
7195
7238
  return result.run?.state === "failed" ? 1 : 0;
7196
7239
  }
7240
+ case "sweep": {
7241
+ // Every live recipe end to end on test orgs (run_recipe_sweep), or the latest sweep's report (get_recipe_sweep).
7242
+ const path = `${DEMO}/sweeps`;
7243
+ const id = flagStr(flags.id);
7244
+ const result =
7245
+ flagBool(flags.status) || id
7246
+ ? await staffRequest(ctx, "GET", id ? `${path}?id=${encodeURIComponent(id)}` : path)
7247
+ : await staffPost(
7248
+ ctx,
7249
+ path,
7250
+ compact({
7251
+ start: flagBool(flags.prepare) ? false : undefined,
7252
+ delivery: flagBool(flags["pull-requests"]) ? "build_pr" : undefined,
7253
+ recipes: flags.recipes ? flagList(flags.recipes) : undefined,
7254
+ orgs: flags.orgs ? flagList(flags.orgs) : undefined,
7255
+ exclude: flags.exclude ? flagList(flags.exclude) : undefined,
7256
+ purchases: flags.buy ? flagList(flags.buy) : undefined,
7257
+ make_orgs: flagBool(flags["make-orgs"]) || undefined,
7258
+ refit: flagBool(flags["no-refit"]) ? false : undefined,
7259
+ })
7260
+ );
7261
+ printResult(io, flags, result, (r) => recipeSweepLines(r.sweep, ctx.client.url));
7262
+ return result.sweep?.state === "failed" ? 1 : 0;
7263
+ }
7197
7264
  default:
7198
7265
  throw new CliError(DEMO_USAGE);
7199
7266
  }
@@ -44,6 +44,8 @@ const ARGS = {
44
44
  prompt: { value: (flags, positionals) => flagStr(flags.prompt) ?? (positionals.join(" ").trim() || undefined) },
45
45
  // --ask "<words>": the person's own words, when --prompt was written from them.
46
46
  ask: "ask",
47
+ // --confirms <build id>: the person's yes to the price a build stopped on.
48
+ confirms: "confirms",
47
49
  // --build-it: the words are a request to build, never read as a question.
48
50
  asked_to_build: { flag: "build-it", type: "bool" },
49
51
  domain: "site",
@@ -254,6 +256,7 @@ async function buildStart(ctx) {
254
256
  "prompt",
255
257
  "ask",
256
258
  "build-it",
259
+ "confirms",
257
260
  "site",
258
261
  "subject",
259
262
  "recipe",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letterstory/cli",
3
- "version": "0.177.8",
3
+ "version": "0.177.10",
4
4
  "description": "Spin up and manage Letter phantom blogs from your terminal.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",