@letterstory/cli 0.176.51 → 0.176.52

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
@@ -85,7 +85,7 @@ import {
85
85
  import { fetchParkedSections, withoutParked } from "./parked.mjs";
86
86
 
87
87
  // Matches cli/package.json; publish-cli.yml rewrites both to the published version.
88
- export const VERSION = "0.176.51";
88
+ export const VERSION = "0.176.52";
89
89
 
90
90
  /** The exit code for a gate refusal (CARD_REQUIRED, PAYMENT_FAILED, OUT_OF_TOKENS): 1 is a plain error. */
91
91
  export const EXIT_PAYMENT_REQUIRED = 3;
@@ -177,6 +177,8 @@ export const BOOLEAN_FLAGS = new Set([
177
177
  "no-modifiers",
178
178
  // site map: do not start an audit for a host that has none.
179
179
  "no-audit",
180
+ // admin deployments: answer from our rows alone, without reading the host.
181
+ "no-host",
180
182
  // site metrics: skip the previous window (no deltas, no revamp indicator).
181
183
  "no-compare",
182
184
  // site refresh / observe: the loop's switches.
@@ -1005,6 +1007,10 @@ Admin (STAFF key — --staff-key / LETTERSTORY_STAFF_API_KEY; acts before an org
1005
1007
  read-only SQL across every org; --sql prints
1006
1008
  the queries behind the answer. Needs the
1007
1009
  fleet:query capability on the staff key.
1010
+ admin deployments [--health down|failing|…] [--org <id>] [--q <text>] [--archived] [--test] [--no-host] [--json]
1011
+ Every site Letter built and hosts, across every org,
1012
+ worst first: health, the problem, and what the host
1013
+ has that no app names. Needs fleet:read.
1008
1014
  admin recipes list | show <id|name> | vocab | stats Recipes: Letter's one library of direction for
1009
1015
  Letterpress on Plan, Build and Fix (/superadmin/recipes),
1010
1016
  each a name, a description, the recipe (what Letterpress
@@ -23,6 +23,7 @@
23
23
  // admin letterpress beyond [--limit N] [--before ISO] (what members asked that went past the library)
24
24
  // admin letterpress settings|flip|set … (recipes:read / recipes:write)
25
25
  // admin demo guide|list|show|set|reset|check … (demo:read / demo:write)
26
+ // admin deployments [--health down|failing|blocked|setting_up|live|paused|archived] [--org <id>] [--q <text>] [--archived] [--test] [--no-host] (fleet:read)
26
27
  // admin models runs|run|star|unstar|scoreboard|requests|request|sampling … (models:read / models:write)
27
28
  // admin replays [--org <id>] [--target <id>] [--status …] [--limit n] (fleet:read)
28
29
  // admin replay --org <id> [<response-id> | --target <id>] [--surface …] (fleet:replay)
@@ -6960,11 +6961,59 @@ export async function cmdAdmin(ctx) {
6960
6961
  if (sub === "recipes") return cmdAdminRecipes({ ...ctx, positionals: ctx.positionals.slice(1) });
6961
6962
  if (sub === "letterpress") return cmdAdminLetterpress({ ...ctx, positionals: ctx.positionals.slice(1) });
6962
6963
  if (sub === "demo") return cmdAdminDemo({ ...ctx, positionals: ctx.positionals.slice(1) });
6964
+ if (sub === "deployments" || sub === "deploys")
6965
+ return cmdAdminDeployments({ ...ctx, positionals: ctx.positionals.slice(1) });
6963
6966
  throw new CliError(
6964
- 'Usage: admin recipes … | admin letterpress … | admin demo … | admin republish … | admin create-org --domain <bare-domain> [--onboard] … | admin ask "<question>" | admin patterns … | admin experiments … | admin fleet-experiments … | admin crm … | admin spend … | admin evals … | admin brand-detection … | admin tuning … | admin tuning-context | admin replays … (see --help)'
6967
+ 'Usage: admin recipes … | admin letterpress … | admin demo … | admin deployments … | admin republish … | admin create-org --domain <bare-domain> [--onboard] … | admin ask "<question>" | admin patterns … | admin experiments … | admin fleet-experiments … | admin crm … | admin spend … | admin evals … | admin brand-detection … | admin tuning … | admin tuning-context | admin replays … (see --help)'
6965
6968
  );
6966
6969
  }
6967
6970
 
6971
+ const MANAGED_HEALTH = ["down", "failing", "blocked", "setting_up", "live", "paused", "archived"];
6972
+
6973
+ // `admin deployments` — every site Letter built and hosts, across every organization
6974
+ // (docs/architecture/deployments.md, "Every managed site"): worst first, each with its org, domain,
6975
+ // health and the problem in one sentence, then what the host has that no app of ours names. The
6976
+ // same read as Super Admin → Deployments and the staff-only `list_managed_deployments` tool.
6977
+ async function cmdAdminDeployments(ctx) {
6978
+ const { flags, io } = ctx;
6979
+ const health = flagStr(flags.health);
6980
+ if (health !== undefined && !MANAGED_HEALTH.includes(health))
6981
+ throw new CliError(`--health is one of ${MANAGED_HEALTH.join(", ")}.`);
6982
+ const params = new URLSearchParams();
6983
+ if (health) params.set("health", health);
6984
+ if (flagStr(flags.org)) params.set("org_id", flagStr(flags.org));
6985
+ if (flagStr(flags.q)) params.set("q", flagStr(flags.q));
6986
+ if (flagBool(flags.archived)) params.set("include_archived", "1");
6987
+ if (flagBool(flags.test)) params.set("include_test", "1");
6988
+ if (flagBool(flags["no-host"])) params.set("check_host", "0");
6989
+ const qs = params.toString();
6990
+ const result = await staffRequest(ctx, "GET", `/api/superadmin/deployments${qs ? `?${qs}` : ""}`);
6991
+ printResult(io, flags, result, (r) => {
6992
+ const c = r.counts;
6993
+ const lines = [
6994
+ `${c.total} managed site${c.total === 1 ? "" : "s"} across ${c.orgs} org${c.orgs === 1 ? "" : "s"}: ` +
6995
+ MANAGED_HEALTH.filter((h) => c[h] > 0)
6996
+ .map((h) => `${c[h]} ${h.replace("_", " ")}`)
6997
+ .join(", "),
6998
+ ];
6999
+ if (!r.apps.length) lines.push(" (none match)");
7000
+ for (const a of r.apps) {
7001
+ lines.push(
7002
+ `${a.id} ${a.health.padEnd(10)} ${a.name.slice(0, 26).padEnd(26)} ${(a.org.name ?? a.org.id).slice(0, 22).padEnd(22)} ${a.url ?? a.domain ?? "no domain yet"}`
7003
+ );
7004
+ if (a.problem) lines.push(` ${a.problem}`);
7005
+ else if (a.health === "setting_up" && a.stage_note) lines.push(` ${a.stage}: ${a.stage_note}`);
7006
+ }
7007
+ if (r.host.read) {
7008
+ lines.push("", `Host: ${r.host.apps} app${r.host.apps === 1 ? "" : "s"} on our account.`);
7009
+ for (const u of r.host.untracked)
7010
+ lines.push(` untracked ${u.name} (${u.status}) ${u.url ?? ""}`.trimEnd());
7011
+ } else lines.push("", `Host not read: ${r.host.note}`);
7012
+ return lines.join("\n");
7013
+ });
7014
+ return 0;
7015
+ }
7016
+
6968
7017
  // `admin brand-detection <org-id>` — how the client's brand is detected in AI answers;
6969
7018
  // with --term / --exact / --exclude it SETS the exact set (replacing the inferred one),
6970
7019
  // --clear goes back to inferred (recorded as a decision, with --note as the why). Every
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letterstory/cli",
3
- "version": "0.176.51",
3
+ "version": "0.176.52",
4
4
  "description": "Spin up and manage Letter phantom blogs from your terminal.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",