@letterstory/cli 0.177.22 → 0.177.23

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
@@ -88,7 +88,7 @@ import {
88
88
  import { fetchParkedSections, withoutParked } from "./parked.mjs";
89
89
 
90
90
  // Matches cli/package.json; publish-cli.yml rewrites both to the published version.
91
- export const VERSION = "0.177.22";
91
+ export const VERSION = "0.177.23";
92
92
 
93
93
  /** The exit code for a gate refusal (CARD_REQUIRED, PAYMENT_FAILED, OUT_OF_TOKENS): 1 is a plain error. */
94
94
  export const EXIT_PAYMENT_REQUIRED = 3;
@@ -106,6 +106,8 @@ export const BOOLEAN_FLAGS = new Set([
106
106
  "pull-requests",
107
107
  "make-orgs",
108
108
  "no-refit",
109
+ // admin demo sweep --new-org: one fresh test org for exactly the picks.
110
+ "new-org",
109
111
  // build --check: read every gate a start meets and start nothing.
110
112
  "check",
111
113
  // admin invariants: press every startable row as a check too.
@@ -1226,11 +1228,12 @@ Admin (STAFF key — --staff-key / LETTERSTORY_STAFF_API_KEY; acts before an org
1226
1228
  admin demo ideas <org-id> [--library] [--preview] [--force] | --status [--id <run>]
1227
1229
  Start every Idea on a demo org for real (pull requests, or
1228
1230
  previews), or read how the latest run went.
1229
- admin demo sweep [--prepare] [--pull-requests] [--recipes a,b] [--variation <n|name>] [--orgs a,b] [--exclude a,b] [--buy a,b] [--make-orgs] [--no-refit] | --status [--id <sweep>] | --reset <recipe> [--id <sweep>]
1231
+ admin demo sweep [--prepare] [--pull-requests] [--recipes a,b] [--variation <n|name>] [--pick <recipe>[:<variation>]]... [--new-org] [--orgs a,b] [--exclude a,b] [--buy a,b] [--make-orgs] [--no-refit] | --status [--id <sweep>] | --reset <recipe> [--id <sweep>]
1230
1232
  Every live recipe end to end: each gets a test org whose Ideas
1231
1233
  offers it, its Start pressed (or, with --prepare, left for you
1232
1234
  to press in the app), and its outcome judged, with who owns a
1233
- failure. Exits 1 when the sweep failed.
1235
+ failure. --new-org --pick a --pick b:2 makes one fresh test org
1236
+ for exactly those recipes. Exits 1 when the sweep failed.
1234
1237
  admin brand-detection <org-id> [--term <t>]... [--exact <t>]... [--exclude <phrase>]... [--note "…"] | --clear [--note "…"]
1235
1238
  How a client's brand is detected in AI answers, with
1236
1239
  every earlier setting; with flags, set the exact set
@@ -7007,7 +7007,7 @@ const DEMO_ITEM_FLAGS = [
7007
7007
  "setup",
7008
7008
  ];
7009
7009
  const DEMO_USAGE =
7010
- "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>,…] [--variation <n|name>] [--orgs <id>,…] [--exclude <id>,…] [--buy <recipe-id>,…] [--make-orgs] [--no-refit] | sweep --status [--id <sweep-id>] | sweep --reset <recipe-id> [--id <sweep-id>]";
7010
+ "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>,…] [--variation <n|name>] [--pick <recipe>[:<variation>]]... [--new-org] [--orgs <id>,…] [--exclude <id>,…] [--buy <recipe-id>,…] [--make-orgs] [--no-refit] | sweep --status [--id <sweep-id>] | sweep --reset <recipe-id> [--id <sweep-id>]";
7011
7011
 
7012
7012
  const readinessLines = (r) =>
7013
7013
  r
@@ -7044,12 +7044,32 @@ const SWEEP_MARK = {
7044
7044
  pending: "…",
7045
7045
  };
7046
7046
 
7047
+ /** A variation flag: a number is its place (1 is the recipe itself); anything else its name. */
7048
+ const variationFlag = (v) => (v ? (/^\d+$/.test(v) ? Number(v) : v) : undefined);
7049
+
7050
+ /** `--pick <recipe>[:<variation>]`: a recipe in its variation (the first colon splits; a name may hold more). */
7051
+ const pickFlag = (v) => {
7052
+ const at = v.indexOf(":");
7053
+ return at < 0
7054
+ ? { recipe: v.trim() }
7055
+ : compact({ recipe: v.slice(0, at).trim(), variation: variationFlag(v.slice(at + 1).trim()) });
7056
+ };
7057
+
7058
+ /** A new test org's stages (get_recipe_sweep `new_org`). */
7059
+ const NEW_ORG_STAGE = { site: "site written", connected: "connected", setup: "setup done", fit: "fit decided" };
7060
+
7047
7061
  /** A recipe sweep's report (get_recipe_sweep), one block per recipe. */
7048
7062
  const recipeSweepLines = (sweep, appUrl) =>
7049
7063
  sweep
7050
7064
  ? [
7051
7065
  `${sweep.summary} (${sweep.state}${sweep.note && sweep.state !== "done" ? `: ${sweep.note}` : ""})`,
7052
7066
  `${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}`,
7067
+ ...(sweep.new_org
7068
+ ? [
7069
+ `new test org: ${sweep.new_org.name ?? sweep.new_org.domain ?? "being made"} ${sweep.new_org.status === "done" ? "ready" : `${sweep.new_org.status}: ${NEW_ORG_STAGE[sweep.new_org.current] ?? sweep.new_org.current}`}${sweep.new_org.note ? ` (${sweep.new_org.note})` : ""}`,
7070
+ sweep.new_org.ideas ? ` open org: ${appUrl}${sweep.new_org.ideas}` : null,
7071
+ ].filter(Boolean)
7072
+ : []),
7053
7073
  ...sweep.items.map((i) =>
7054
7074
  [
7055
7075
  `\n${(SWEEP_MARK[i.result] ?? i.result).padEnd(10)} ${i.name}${i.variation ? ` (variation ${i.variation}: ${i.variation_name ?? ""})` : ""}`,
@@ -7320,18 +7340,6 @@ async function cmdAdminDemo(ctx) {
7320
7340
  const orgId = requirePositional(positionals, 1, "org-id");
7321
7341
  const path = `${DEMO}/${encodeURIComponent(orgId)}/ideas`;
7322
7342
  const id = flagStr(flags.id);
7323
- // The retest loop: one recipe brought back on its test org (reset_recipe_sweep_item).
7324
- if (flagStr(flags.reset)) {
7325
- const reset = await staffPost(ctx, `${path}/reset`, compact({ id, recipe_id: flagStr(flags.reset) }));
7326
- printResult(io, flags, reset, (r) =>
7327
- [
7328
- ...r.reset.undone.map((u) => `undone: ${u}`),
7329
- ...r.reset.kept.map((k) => `kept: ${k}`),
7330
- recipeSweepLines(r.sweep, ctx.client.url),
7331
- ].join("\n")
7332
- );
7333
- return 0;
7334
- }
7335
7343
  const result =
7336
7344
  flagBool(flags.status) || id
7337
7345
  ? await staffRequest(ctx, "GET", id ? `${path}?id=${encodeURIComponent(id)}` : path)
@@ -7351,6 +7359,18 @@ async function cmdAdminDemo(ctx) {
7351
7359
  // Every live recipe end to end on test orgs (run_recipe_sweep), or the latest sweep's report (get_recipe_sweep).
7352
7360
  const path = `${DEMO}/sweeps`;
7353
7361
  const id = flagStr(flags.id);
7362
+ // The retest loop: one recipe brought back on its test org (reset_recipe_sweep_item).
7363
+ if (flagStr(flags.reset)) {
7364
+ const reset = await staffPost(ctx, `${path}/reset`, compact({ id, recipe_id: flagStr(flags.reset) }));
7365
+ printResult(io, flags, reset, (r) =>
7366
+ [
7367
+ ...r.reset.undone.map((u) => `undone: ${u}`),
7368
+ ...r.reset.kept.map((k) => `kept: ${k}`),
7369
+ recipeSweepLines(r.sweep, ctx.client.url),
7370
+ ].join("\n")
7371
+ );
7372
+ return 0;
7373
+ }
7354
7374
  const result =
7355
7375
  flagBool(flags.status) || id
7356
7376
  ? await staffRequest(ctx, "GET", id ? `${path}?id=${encodeURIComponent(id)}` : path)
@@ -7360,16 +7380,14 @@ async function cmdAdminDemo(ctx) {
7360
7380
  compact({
7361
7381
  start: flagBool(flags.prepare) ? false : undefined,
7362
7382
  delivery: flagBool(flags["pull-requests"]) ? "build_pr" : undefined,
7363
- recipes: flags.recipes ? flagList(flags.recipes) : undefined,
7364
- // A number is the variation's place (1 is the recipe itself); anything else its name.
7365
- variation: flagStr(flags.variation)
7366
- ? /^\d+$/.test(flagStr(flags.variation))
7367
- ? Number(flagStr(flags.variation))
7368
- : flagStr(flags.variation)
7369
- : undefined,
7370
- orgs: flags.orgs ? flagList(flags.orgs) : undefined,
7371
- exclude: flags.exclude ? flagList(flags.exclude) : undefined,
7372
- purchases: flags.buy ? flagList(flags.buy) : undefined,
7383
+ recipes: flags.recipes ? flagCsv(flags.recipes) : undefined,
7384
+ variation: variationFlag(flagStr(flags.variation)),
7385
+ // --pick <recipe>[:<variation>], repeatable: each recipe in its variation.
7386
+ picks: flags.pick ? flagList(flags.pick).map(pickFlag) : undefined,
7387
+ new_org: flagBool(flags["new-org"]) || undefined,
7388
+ orgs: flags.orgs ? flagCsv(flags.orgs) : undefined,
7389
+ exclude: flags.exclude ? flagCsv(flags.exclude) : undefined,
7390
+ purchases: flags.buy ? flagCsv(flags.buy) : undefined,
7373
7391
  make_orgs: flagBool(flags["make-orgs"]) || undefined,
7374
7392
  refit: flagBool(flags["no-refit"]) ? false : undefined,
7375
7393
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letterstory/cli",
3
- "version": "0.177.22",
3
+ "version": "0.177.23",
4
4
  "description": "Spin up and manage Letter phantom blogs from your terminal.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",