@letterstory/cli 0.75.0 → 0.76.0
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 +5 -1
- package/lib/commands/admin.mjs +88 -7
- package/package.json +1 -1
package/lib/cli.mjs
CHANGED
|
@@ -56,7 +56,7 @@ import {
|
|
|
56
56
|
} from "./commands.mjs";
|
|
57
57
|
|
|
58
58
|
// Keep in sync with cli/package.json.
|
|
59
|
-
export const VERSION = "0.
|
|
59
|
+
export const VERSION = "0.76.0";
|
|
60
60
|
|
|
61
61
|
// Flags that never take a value. Listing them explicitly means `deploy get --json <id>`
|
|
62
62
|
// can't accidentally swallow the id as --json's value.
|
|
@@ -582,8 +582,12 @@ Admin (STAFF key — --staff-key / LETTERSTORY_STAFF_API_KEY; acts before an org
|
|
|
582
582
|
ledger entry. Written by \`npm run lab --
|
|
583
583
|
publish\` in the kernels repo.
|
|
584
584
|
admin spend summary [--range day|week|month|year | --from D --to D] [--grain day|week|month]
|
|
585
|
+
| reconcile [--from D --to D] | pnl [--range day|week|month|year | --from D --to D]
|
|
586
|
+
| reference add --vendor V [--key K] --metric billed_usd --value N --from D [--to D] --ref "…"
|
|
587
|
+
| stripe-sync --from D --to D
|
|
585
588
|
| entries [--from D] [--to D] [--vendor V] [--area A] [--source invoice|csv_import|api|manual]
|
|
586
589
|
| add --vendor V --amount N --from D [--to D] --area A --category C [--basis billed|estimated] …
|
|
590
|
+
| import <file.csv> [--vendor Concentrate] [--area A] [--ledger-key K] [--dry-run]
|
|
587
591
|
| import <file.csv> [--vendor Concentrate] [--area A] [--keep-ledger-key] [--dry-run]
|
|
588
592
|
| usage <events.json> per-call costs as kernels/lettertrace push them
|
|
589
593
|
| remove <entry-id> | services | service <slug> [--owner …] [--monthly N|none] [--renewal D]
|
package/lib/commands/admin.mjs
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
// The old names — evals, eval-add, eval-set, proposals, propose-eval, capture-eval, reject-eval — still work.)
|
|
14
14
|
// admin evals scoreboard|cases|runs|run|triage|guard|retire … (fleet:read / tuning:experiment)
|
|
15
15
|
// admin crm import|capture|signals|record|signal|accounts|account|score|slack-channels|link-slack|white-glove|set-white-glove|churn|record-churn|undo-churn|onboard|onboarding|retry-onboarding|onboard-plan|payment-link … (crm:read / crm:write)
|
|
16
|
-
// admin spend summary|entries|add|import|usage|remove|services|service …
|
|
16
|
+
// admin spend summary|reconcile|pnl|entries|add|import|usage|remove|reference|stripe-sync|services|service … (spend:read / spend:write)
|
|
17
17
|
// admin replays [--org <id>] [--target <id>] [--status …] [--limit n] (fleet:read)
|
|
18
18
|
// admin replay --org <id> [<response-id> | --target <id>] [--surface …] (fleet:replay)
|
|
19
19
|
// admin create-org --domain acme.com [--name "Acme"] [--test] [--no-key] [--key-name N]
|
|
@@ -3019,7 +3019,7 @@ const SPEND_CATEGORIES = [
|
|
|
3019
3019
|
"devtools",
|
|
3020
3020
|
"other",
|
|
3021
3021
|
];
|
|
3022
|
-
const SPEND_BASES = ["billed", "estimated"];
|
|
3022
|
+
const SPEND_BASES = ["billed", "reported", "estimated"];
|
|
3023
3023
|
const SPEND_SOURCES = ["invoice", "csv_import", "api", "manual"];
|
|
3024
3024
|
|
|
3025
3025
|
function usd(n) {
|
|
@@ -3088,6 +3088,8 @@ function spendEntryFromFlags(flags) {
|
|
|
3088
3088
|
source_ref: flagStr(flags.ref),
|
|
3089
3089
|
note: flagStr(flags.note),
|
|
3090
3090
|
external_id: flagStr(flags["external-id"]),
|
|
3091
|
+
byok: flags.byok === undefined ? undefined : flagBool(flags.byok),
|
|
3092
|
+
ledger_scope: flags["ledger-scope"] === undefined ? undefined : flagBool(flags["ledger-scope"]),
|
|
3091
3093
|
});
|
|
3092
3094
|
}
|
|
3093
3095
|
|
|
@@ -3140,7 +3142,7 @@ async function cmdAdminSpend(ctx) {
|
|
|
3140
3142
|
const path = positionals[1];
|
|
3141
3143
|
if (!path)
|
|
3142
3144
|
throw new CliError(
|
|
3143
|
-
'Usage: admin spend import <file.csv> [--vendor Concentrate] [--area kernels] [--category ai_model] [--basis billed|estimated] [--ref "…"] [--
|
|
3145
|
+
'Usage: admin spend import <file.csv> [--vendor Concentrate] [--area kernels] [--category ai_model] [--basis billed|estimated] [--ref "…"] [--ledger-key \"Main Concentrate Key\"] [--dry-run]'
|
|
3144
3146
|
);
|
|
3145
3147
|
const body = compact({
|
|
3146
3148
|
csv: readFileSync(path, "utf8"),
|
|
@@ -3149,7 +3151,7 @@ async function cmdAdminSpend(ctx) {
|
|
|
3149
3151
|
category: oneOf(flags, "category", SPEND_CATEGORIES),
|
|
3150
3152
|
basis: oneOf(flags, "basis", SPEND_BASES),
|
|
3151
3153
|
source_ref: flagStr(flags.ref) ?? `CSV import: ${path.split("/").pop()}`,
|
|
3152
|
-
|
|
3154
|
+
ledger_key: flagStr(flags["ledger-key"]),
|
|
3153
3155
|
dry_run: flagBool(flags["dry-run"]) || undefined,
|
|
3154
3156
|
});
|
|
3155
3157
|
const result = await staffPost(ctx, `${SPEND}/import`, body);
|
|
@@ -3157,8 +3159,8 @@ async function cmdAdminSpend(ctx) {
|
|
|
3157
3159
|
ctx,
|
|
3158
3160
|
`${result.used} of ${result.rows} rows → ${result.entries.length} entries, ${usd(result.total)}` +
|
|
3159
3161
|
(result.days ? ` (${result.days.from} → ${result.days.to})` : "") +
|
|
3160
|
-
(result.
|
|
3161
|
-
? `; ${result.
|
|
3162
|
+
(result.ledgerScopeRows
|
|
3163
|
+
? `; ${result.ledgerScopeRows} rows on the ledger's own key kept as ledger-scope (reconciled, not added)`
|
|
3162
3164
|
: "") +
|
|
3163
3165
|
(flagBool(flags["dry-run"]) ? " (dry run, nothing written)" : "")
|
|
3164
3166
|
);
|
|
@@ -3211,6 +3213,85 @@ async function cmdAdminSpend(ctx) {
|
|
|
3211
3213
|
printResult(io, flags, result);
|
|
3212
3214
|
return 0;
|
|
3213
3215
|
}
|
|
3216
|
+
case "reconcile": {
|
|
3217
|
+
const params = new URLSearchParams(compact({ from: flagStr(flags.from), to: flagStr(flags.to) }));
|
|
3218
|
+
const result = await staffRequest(ctx, "GET", `${SPEND}/reconcile${params.size ? `?${params}` : ""}`);
|
|
3219
|
+
printResult(io, flags, result, (r) =>
|
|
3220
|
+
[
|
|
3221
|
+
`${r.window.from} → ${r.window.to}: ${r.rows.filter((x) => x.flag).length} of ${r.rows.length} checks flagged`,
|
|
3222
|
+
...r.rows.map(
|
|
3223
|
+
(x) =>
|
|
3224
|
+
`${x.flag ? "⚑" : " "} ${x.label.slice(0, 44).padEnd(44)} ours ${usd(x.ours.value).padStart(10)} theirs ${usd(x.theirs.value).padStart(10)} ${x.diffPct === null ? "" : `${x.diffPct > 0 ? "+" : ""}${x.diffPct}%`}`
|
|
3225
|
+
),
|
|
3226
|
+
...r.notes.map((n) => ` · ${n}`),
|
|
3227
|
+
].join("\n")
|
|
3228
|
+
);
|
|
3229
|
+
return 0;
|
|
3230
|
+
}
|
|
3231
|
+
case "pnl": {
|
|
3232
|
+
const params = new URLSearchParams(
|
|
3233
|
+
compact({
|
|
3234
|
+
range: oneOf(flags, "range", SPEND_RANGES),
|
|
3235
|
+
from: flagStr(flags.from),
|
|
3236
|
+
to: flagStr(flags.to),
|
|
3237
|
+
})
|
|
3238
|
+
);
|
|
3239
|
+
const result = await staffRequest(ctx, "GET", `${SPEND}/pnl${params.size ? `?${params}` : ""}`);
|
|
3240
|
+
printResult(io, flags, result, (p) =>
|
|
3241
|
+
[
|
|
3242
|
+
`${p.range.from} → ${p.range.to}: revenue ${usd(p.totals.revenue)} (${p.revenueSource}) · cost ${usd(p.totals.cost.total)} · margin ${usd(p.totals.margin)}${p.totals.marginPct === null ? "" : ` (${p.totals.marginPct}%)`}`,
|
|
3243
|
+
` MRR ${usd(p.totals.mrr)} · invoiced ${usd(p.totals.invoiced)} · owed ${usd(p.totals.owed)} · Stripe fees ${usd(p.totals.stripeFees)}`,
|
|
3244
|
+
"",
|
|
3245
|
+
...p.lines.map(
|
|
3246
|
+
(l) =>
|
|
3247
|
+
` ${l.label.slice(0, 46).padEnd(46)} rev ${usd(l.revenue).padStart(9)} cost ${usd(l.cost).padStart(9)} margin ${usd(l.margin).padStart(10)}`
|
|
3248
|
+
),
|
|
3249
|
+
].join("\n")
|
|
3250
|
+
);
|
|
3251
|
+
return 0;
|
|
3252
|
+
}
|
|
3253
|
+
case "reference": {
|
|
3254
|
+
const vendor = flagStr(flags.vendor);
|
|
3255
|
+
const metric = flagStr(flags.metric);
|
|
3256
|
+
const value = flagNum(flags.value);
|
|
3257
|
+
const from = flagStr(flags.from);
|
|
3258
|
+
const to = flagStr(flags.to) ?? from;
|
|
3259
|
+
const ref = flagStr(flags.ref);
|
|
3260
|
+
if (positionals[1] !== "add" || !vendor || !metric || value === undefined || !from || !ref) {
|
|
3261
|
+
throw new CliError(
|
|
3262
|
+
'Usage: admin spend reference add --vendor Concentrate [--key "Writing Kernels v2"] --metric billed_usd --value 17324.24 --from 2026-09-10 --to 2026-10-09 --ref "Concentrate dashboard 2026-10-09"'
|
|
3263
|
+
);
|
|
3264
|
+
}
|
|
3265
|
+
const result = await staffPost(
|
|
3266
|
+
ctx,
|
|
3267
|
+
`${SPEND}/references`,
|
|
3268
|
+
compact({
|
|
3269
|
+
vendor,
|
|
3270
|
+
key_label: flagStr(flags.key),
|
|
3271
|
+
metric,
|
|
3272
|
+
value,
|
|
3273
|
+
period_start: from,
|
|
3274
|
+
period_end: to,
|
|
3275
|
+
source_ref: ref,
|
|
3276
|
+
note: flagStr(flags.note),
|
|
3277
|
+
})
|
|
3278
|
+
);
|
|
3279
|
+
ok(ctx, `Recorded ${metric} ${value} for ${vendor}${flags.key ? ` (${flagStr(flags.key)})` : ""}.`);
|
|
3280
|
+
printResult(io, flags, result);
|
|
3281
|
+
return 0;
|
|
3282
|
+
}
|
|
3283
|
+
case "stripe-sync": {
|
|
3284
|
+
const from = flagStr(flags.from);
|
|
3285
|
+
const to = flagStr(flags.to);
|
|
3286
|
+
if (!from || !to) throw new CliError("Usage: admin spend stripe-sync --from 2026-01-01 --to 2026-10-09");
|
|
3287
|
+
const result = await staffPost(ctx, `${SPEND}/stripe-sync`, { from, to });
|
|
3288
|
+
ok(
|
|
3289
|
+
ctx,
|
|
3290
|
+
`Stripe ${result.status}: ${result.transactions ?? 0} balance transactions, fees filed for ${result.feeDays ?? 0} days.`
|
|
3291
|
+
);
|
|
3292
|
+
printResult(io, flags, result);
|
|
3293
|
+
return 0;
|
|
3294
|
+
}
|
|
3214
3295
|
case "services": {
|
|
3215
3296
|
const result = await staffRequest(ctx, "GET", `${SPEND}/services`);
|
|
3216
3297
|
printResult(io, flags, result, formatSpendServices);
|
|
@@ -3240,7 +3321,7 @@ async function cmdAdminSpend(ctx) {
|
|
|
3240
3321
|
}
|
|
3241
3322
|
default:
|
|
3242
3323
|
throw new CliError(
|
|
3243
|
-
"Usage: admin spend summary [--range day|week|month|year | --from D --to D] [--grain day|week|month] | entries [--from D] [--to D] [--vendor V] [--area A] [--source S] | add --vendor V --amount N --from D … | import <file.csv> … | usage <events.json> | remove <entry-id> | services | service <slug> --owner … --monthly …"
|
|
3324
|
+
"Usage: admin spend summary [--range day|week|month|year | --from D --to D] [--grain day|week|month] | reconcile [--from D --to D] | pnl [--range …] | reference add … | stripe-sync --from D --to D | entries [--from D] [--to D] [--vendor V] [--area A] [--source S] | add --vendor V --amount N --from D … | import <file.csv> … | usage <events.json> | remove <entry-id> | services | service <slug> --owner … --monthly …"
|
|
3244
3325
|
);
|
|
3245
3326
|
}
|
|
3246
3327
|
}
|