@letterstory/cli 0.176.37 → 0.176.39
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 -4
- package/lib/commands/admin.mjs +69 -1
- package/lib/commands/plan.mjs +14 -2
- package/package.json +1 -1
package/lib/cli.mjs
CHANGED
|
@@ -83,7 +83,7 @@ import {
|
|
|
83
83
|
import { fetchParkedSections, withoutParked } from "./parked.mjs";
|
|
84
84
|
|
|
85
85
|
// Matches cli/package.json; publish-cli.yml rewrites both to the published version.
|
|
86
|
-
export const VERSION = "0.176.
|
|
86
|
+
export const VERSION = "0.176.39";
|
|
87
87
|
|
|
88
88
|
/** The exit code for a gate refusal (CARD_REQUIRED, PAYMENT_FAILED, OUT_OF_TOKENS): 1 is a plain error. */
|
|
89
89
|
export const EXIT_PAYMENT_REQUIRED = 3;
|
|
@@ -121,8 +121,10 @@ export const BOOLEAN_FLAGS = new Set([
|
|
|
121
121
|
"fresh",
|
|
122
122
|
// admin letterpress telemetry | prs: every org, test and demo orgs included.
|
|
123
123
|
"all-orgs",
|
|
124
|
-
// admin letterpress prs: since the first PR (all_time).
|
|
124
|
+
// admin letterpress prs | failures: since the first PR or build (all_time).
|
|
125
125
|
"all-time",
|
|
126
|
+
// admin letterpress failures: the builds that took far longer than usual.
|
|
127
|
+
"slow",
|
|
126
128
|
// admin letterpress status | gaps: the customers' reading (no test or demo orgs).
|
|
127
129
|
"customers",
|
|
128
130
|
// build: repeat an open build's ask; post its progress in the org's Slack channel.
|
|
@@ -1669,9 +1671,10 @@ Insights:
|
|
|
1669
1671
|
first), what to build (--lens build), or your own words. Nothing asked:
|
|
1670
1672
|
each list's head. The same answer as the console and Slack.
|
|
1671
1673
|
plan auto [activate|disable] [--site host] [--veto-hours <1-168>|off] [--auto-merge on|off]
|
|
1672
|
-
[--aggression low|medium|high|xhigh] [--
|
|
1674
|
+
[--aggression low|medium|high|xhigh] [--goal geo|seo|agent_conviction|inbound_conversion|any]
|
|
1675
|
+
[--slack-channel <C…>|none]
|
|
1673
1676
|
Auto-Plan on a site: Letterpress picks its strategies and builds
|
|
1674
|
-
them; each pick waits --veto-hours for a veto. Settings shown when none given.
|
|
1677
|
+
them (--goal: one goal's strategies, else any); each pick waits --veto-hours for a veto. Settings shown when none given.
|
|
1675
1678
|
plan pending [--site host] What Auto-Plan picked and is waiting to build
|
|
1676
1679
|
plan veto <proposal-id> Veto one: it is never built
|
|
1677
1680
|
plan later <action-id> [--outcome geo|…] [--site host] Queue a Plan row for later: Auto-Plan starts it once it's ready
|
package/lib/commands/admin.mjs
CHANGED
|
@@ -5241,8 +5241,9 @@ const ENGINE = "/api/superadmin/engine-settings";
|
|
|
5241
5241
|
const LETTERPRESS = "/api/superadmin/letterpress";
|
|
5242
5242
|
const CODING_TELEMETRY = "/api/superadmin/coding-telemetry";
|
|
5243
5243
|
const PULL_REQUESTS = "/api/superadmin/pull-requests";
|
|
5244
|
+
const BUILD_FAILURES = "/api/superadmin/build-failures";
|
|
5244
5245
|
const ENGINE_USAGE =
|
|
5245
|
-
"Usage: admin letterpress status | gaps | telemetry [--days N] [--harness h] [--all-orgs] | prs [--days N | --from YYYY-MM-DD [--to YYYY-MM-DD] | --all-time] [--day YYYY-MM-DD] [--org id] [--state merged|open|closed] [--top N] [--tz zone] [--all-orgs] | blocked [--days N] [--org id] [--all-orgs] | unblock <org-id> <host> <subject> | decisions <org-id> [--site host] [--surface plan|fix|build|site] | undo <org-id> <host> <decision-id> | runs [--status s] [--error-code c] [--first-review-cause agent|context|setup|unknown] [--pr merged|closed|open|none] [--harness h] [--days N] | beyond [--limit N] [--before ISO] | companies | company <org-id> [--site host] | facts <org-id> [--site host] | fits [--recipe id] [--org id] [--site host] [--fits yes|no|unknown|waiting] [--limit N] | replay <run-id> | mark <run-id> <item-id> <right|weak|wrong|lead|gap|recipe|clear> [--note …] | settings | set <weights.<ask>.<reading>|lenses.<lens>.bar.<reading>|lenses.<lens>.bar|lenses.<lens>.research.turns|lenses.<lens>.research.ms|asks.<lens>.ask|asks.<lens>.guidance|dismissals.block_days|preflight.off|explore.ask|explore.research.turns|explore.research.ms|explore.every_days> <value>";
|
|
5246
|
+
"Usage: admin letterpress status | gaps | telemetry [--days N] [--harness h] [--all-orgs] | prs [--days N | --from YYYY-MM-DD [--to YYYY-MM-DD] | --all-time] [--day YYYY-MM-DD] [--org id] [--state merged|open|closed] [--top N] [--tz zone] [--all-orgs] | failures [--days N | --from YYYY-MM-DD [--to YYYY-MM-DD] | --all-time] [--day YYYY-MM-DD] [--org id] [--failure ours|agent|setup|undelivered] [--cause c] [--stopped-at step] [--repo owner/name] [--slow] [--tz zone] [--all-orgs] | blocked [--days N] [--org id] [--all-orgs] | unblock <org-id> <host> <subject> | decisions <org-id> [--site host] [--surface plan|fix|build|site] | undo <org-id> <host> <decision-id> | runs [--status s] [--error-code c] [--first-review-cause agent|context|setup|unknown] [--pr merged|closed|open|none] [--harness h] [--days N] | beyond [--limit N] [--before ISO] | companies | company <org-id> [--site host] | facts <org-id> [--site host] | fits [--recipe id] [--org id] [--site host] [--fits yes|no|unknown|waiting] [--limit N] | replay <run-id> | mark <run-id> <item-id> <right|weak|wrong|lead|gap|recipe|clear> [--note …] | settings | set <weights.<ask>.<reading>|lenses.<lens>.bar.<reading>|lenses.<lens>.bar|lenses.<lens>.research.turns|lenses.<lens>.research.ms|asks.<lens>.ask|asks.<lens>.guidance|dismissals.block_days|preflight.off|explore.ask|explore.research.turns|explore.research.ms|explore.every_days> <value>";
|
|
5246
5247
|
|
|
5247
5248
|
/** One number of the settings from a dotted path and a value: the PUT body save_engine_settings reads. */
|
|
5248
5249
|
function engineSettingsPatch(path, value) {
|
|
@@ -5527,6 +5528,73 @@ async function cmdAdminLetterpress(ctx) {
|
|
|
5527
5528
|
);
|
|
5528
5529
|
return 0;
|
|
5529
5530
|
}
|
|
5531
|
+
case "failures": {
|
|
5532
|
+
// get_build_failures (the days, causes, steps, odd cases, slow builds) or, with a filter,
|
|
5533
|
+
// list_build_failures (the builds behind them).
|
|
5534
|
+
const filters = [
|
|
5535
|
+
["day", "day"],
|
|
5536
|
+
["org", "org_id"],
|
|
5537
|
+
["failure", "failure"],
|
|
5538
|
+
["cause", "cause"],
|
|
5539
|
+
["stopped-at", "stopped_at"],
|
|
5540
|
+
["repo", "repo"],
|
|
5541
|
+
];
|
|
5542
|
+
const slow = flagBool(flags.slow);
|
|
5543
|
+
const listing = slow || filters.some(([flag]) => flags[flag]);
|
|
5544
|
+
const q = new URLSearchParams({
|
|
5545
|
+
...(listing ? { view: "list" } : {}),
|
|
5546
|
+
days: String(flags.days ?? 30),
|
|
5547
|
+
});
|
|
5548
|
+
for (const [flag, key] of [...filters, ["from", "from"], ["to", "to"], ["tz", "tz"], ["limit", "limit"]])
|
|
5549
|
+
if (flags[flag]) q.set(key, String(flags[flag]));
|
|
5550
|
+
if (slow) q.set("slow", "1");
|
|
5551
|
+
if (flagBool(flags["all-orgs"])) q.set("customers", "0");
|
|
5552
|
+
if (flagBool(flags["all-time"])) q.set("all_time", "1");
|
|
5553
|
+
const r = await staffRequest(ctx, "GET", `${BUILD_FAILURES}?${q}`);
|
|
5554
|
+
const pct = (v) => (v == null ? "—" : `${Math.round(100 * v)}%`);
|
|
5555
|
+
const mins = (ms) =>
|
|
5556
|
+
ms == null ? "—" : ms >= 3_600_000 ? `${(ms / 3_600_000).toFixed(1)}h` : `${Math.round(ms / 60_000)}m`;
|
|
5557
|
+
printResult(io, flags, r, (x) =>
|
|
5558
|
+
listing
|
|
5559
|
+
? x.builds.length
|
|
5560
|
+
? x.builds
|
|
5561
|
+
.map(
|
|
5562
|
+
(b) =>
|
|
5563
|
+
`${b.day} ${(b.failure ?? "delivered").padEnd(11)} ${(b.cause_label ?? "").padEnd(28)} ${b.door_label.padEnd(9)} ${(b.org_name ?? b.org_id).padEnd(20)} ${mins(b.work_ms)}${b.typical_ms ? ` (usually ${mins(b.typical_ms)})` : ""} ${b.line ?? ""} ${b.run_id}`
|
|
5564
|
+
)
|
|
5565
|
+
.join("\n")
|
|
5566
|
+
: "No builds match."
|
|
5567
|
+
: [
|
|
5568
|
+
`Builds failed, ${x.since_day} to ${x.until_day} (${x.days} days, ${x.tz})${x.customer_only ? ", customers only" : ""}: ${x.totals.failed} of ${x.totals.builds} (${pct(x.totals.rate)}; ${x.previous.failed} of ${x.previous.builds} the period before); ours ${x.totals.ours}, the agent's ${x.totals.agent}, customer's setup ${x.totals.setup}, didn't deliver ${x.totals.undelivered}`,
|
|
5569
|
+
...x.series
|
|
5570
|
+
.filter((d) => d.builds)
|
|
5571
|
+
.map(
|
|
5572
|
+
(d) =>
|
|
5573
|
+
`${d.day} ${String(d.builds).padStart(3)} builds ${String(d.ours + d.agent + d.setup + d.undelivered).padStart(3)} failed (ours ${d.ours}, agent ${d.agent}, setup ${d.setup}, undelivered ${d.undelivered})`
|
|
5574
|
+
),
|
|
5575
|
+
"why:",
|
|
5576
|
+
...x.causes.map(
|
|
5577
|
+
(c) =>
|
|
5578
|
+
` ${String(c.count).padStart(3)} ${c.label} [${c.cause}, ${c.kind}] ${c.previous} before ${c.lines[0]?.line ?? ""}`
|
|
5579
|
+
),
|
|
5580
|
+
`where they stop: ${
|
|
5581
|
+
x.steps
|
|
5582
|
+
.filter((s) => s.stopped)
|
|
5583
|
+
.map((s) => `${s.label} ${s.stopped}`)
|
|
5584
|
+
.join(", ") || "—"
|
|
5585
|
+
}; delivered ${x.delivered}`,
|
|
5586
|
+
`odd cases: rare ${x.odd.rare.length}, new ${x.odd.new.length}, one customer ${x.odd.one_customer.length}, keeps failing ${x.odd.keeps_failing.length}`,
|
|
5587
|
+
`took longer than usual: ${x.slow.slow_count}`,
|
|
5588
|
+
...x.slow.runs
|
|
5589
|
+
.slice(0, 10)
|
|
5590
|
+
.map(
|
|
5591
|
+
(b) =>
|
|
5592
|
+
` ${mins(b.work_ms)} (usually ${mins(b.typical_ms)}, ${b.ratio?.toFixed(1)}×) ${b.door_label} ${b.org_name ?? b.org_id} ${b.run_id}`
|
|
5593
|
+
),
|
|
5594
|
+
].join("\n")
|
|
5595
|
+
);
|
|
5596
|
+
return 0;
|
|
5597
|
+
}
|
|
5530
5598
|
case "runs": {
|
|
5531
5599
|
// list_coding_runs with fleet.
|
|
5532
5600
|
const q = new URLSearchParams({
|
package/lib/commands/plan.mjs
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
// organization's own domain). The same calls as the Plan's modal and its sidebar's Pending rows.
|
|
3
3
|
// plan auto → get_auto_plan
|
|
4
4
|
// plan auto [activate|disable] [--veto-hours <1-168>|off] [--auto-merge on|off]
|
|
5
|
-
// [--aggression low|medium|high|xhigh] [--
|
|
5
|
+
// [--aggression low|medium|high|xhigh] [--goal geo|seo|agent_conviction|inbound_conversion|any]
|
|
6
|
+
// [--slack-channel <C…>|none] → set_auto_plan
|
|
6
7
|
// plan pending → list_auto_plan_proposals (status pending)
|
|
7
8
|
// plan veto <proposal-id> → veto_auto_plan_proposal
|
|
8
9
|
// plan later <action-id> [--outcome geo|…] → queue_plan_action (Auto-Plan starts it once it's ready)
|
|
@@ -44,6 +45,17 @@ export const ARGS = {
|
|
|
44
45
|
},
|
|
45
46
|
merge: { value: onOff("auto-merge") },
|
|
46
47
|
aggression: { flag: "aggression", enum: ["low", "medium", "high", "xhigh"] },
|
|
48
|
+
// --goal geo picks strategies for that goal only; --goal any (the default) reads every strategy.
|
|
49
|
+
goal: {
|
|
50
|
+
value: (flags) => {
|
|
51
|
+
const v = flagStr(flags.goal);
|
|
52
|
+
if (v === undefined) return undefined;
|
|
53
|
+
if (v === "any") return null;
|
|
54
|
+
if (!["agent_conviction", "geo", "seo", "inbound_conversion"].includes(v))
|
|
55
|
+
throw new CliError("--goal takes agent_conviction, geo, seo, inbound_conversion, or any.");
|
|
56
|
+
return v;
|
|
57
|
+
},
|
|
58
|
+
},
|
|
47
59
|
slack_channel: {
|
|
48
60
|
value: (flags) => {
|
|
49
61
|
const v = flagStr(flags["slack-channel"]);
|
|
@@ -75,7 +87,7 @@ export const ARGS = {
|
|
|
75
87
|
},
|
|
76
88
|
};
|
|
77
89
|
|
|
78
|
-
const SETTING_FLAGS = ["veto-hours", "auto-merge", "aggression", "slack-channel"];
|
|
90
|
+
const SETTING_FLAGS = ["veto-hours", "auto-merge", "aggression", "goal", "slack-channel"];
|
|
79
91
|
|
|
80
92
|
export async function cmdPlan(ctx) {
|
|
81
93
|
const { client, flags, io } = ctx;
|