@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 +7 -1
- package/lib/commands/admin.mjs +50 -1
- package/package.json +1 -1
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.
|
|
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
|
package/lib/commands/admin.mjs
CHANGED
|
@@ -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
|