@letterstory/cli 0.74.0 → 0.75.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 +2 -1
- package/lib/commands/admin.mjs +34 -2
- 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.75.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.
|
|
@@ -585,6 +585,7 @@ Admin (STAFF key — --staff-key / LETTERSTORY_STAFF_API_KEY; acts before an org
|
|
|
585
585
|
| entries [--from D] [--to D] [--vendor V] [--area A] [--source invoice|csv_import|api|manual]
|
|
586
586
|
| add --vendor V --amount N --from D [--to D] --area A --category C [--basis billed|estimated] …
|
|
587
587
|
| import <file.csv> [--vendor Concentrate] [--area A] [--keep-ledger-key] [--dry-run]
|
|
588
|
+
| usage <events.json> per-call costs as kernels/lettertrace push them
|
|
588
589
|
| remove <entry-id> | services | service <slug> [--owner …] [--monthly N|none] [--renewal D]
|
|
589
590
|
Where Letter's money goes (spending.letter.dev):
|
|
590
591
|
totals, run rate, area/vendor/provider/model/
|
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|remove|services|service … (spend:read / spend:write)
|
|
16
|
+
// admin spend summary|entries|add|import|usage|remove|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]
|
|
@@ -3171,6 +3171,38 @@ async function cmdAdminSpend(ctx) {
|
|
|
3171
3171
|
printResult(io, flags, result, () => "");
|
|
3172
3172
|
return 0;
|
|
3173
3173
|
}
|
|
3174
|
+
case "usage": {
|
|
3175
|
+
// Per-call model costs, as kernels / lettertrace push them (POST /api/superadmin/spend/usage):
|
|
3176
|
+
// a JSON file holding { events: [...] } or a bare array. Idempotent on each event_id.
|
|
3177
|
+
const path = positionals[1];
|
|
3178
|
+
if (!path) throw new CliError("Usage: admin spend usage <events.json> ({ events: [...] } or an array)");
|
|
3179
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
3180
|
+
const events = Array.isArray(parsed) ? parsed : parsed?.events;
|
|
3181
|
+
if (!Array.isArray(events) || events.length === 0) throw new CliError(`${path} holds no events.`);
|
|
3182
|
+
let inserted = 0;
|
|
3183
|
+
let duplicates = 0;
|
|
3184
|
+
let cost = 0;
|
|
3185
|
+
const rejected = [];
|
|
3186
|
+
for (let i = 0; i < events.length; i += 1000) {
|
|
3187
|
+
const r = await staffPost(ctx, `${SPEND}/usage`, { events: events.slice(i, i + 1000) });
|
|
3188
|
+
inserted += r.inserted;
|
|
3189
|
+
duplicates += r.duplicates;
|
|
3190
|
+
cost += r.cost_usd;
|
|
3191
|
+
rejected.push(...(r.rejected ?? []));
|
|
3192
|
+
}
|
|
3193
|
+
ok(
|
|
3194
|
+
ctx,
|
|
3195
|
+
`${events.length} events: ${inserted} new (${usd(cost)}), ${duplicates} already on file` +
|
|
3196
|
+
(rejected.length ? `, ${rejected.length} refused` : "")
|
|
3197
|
+
);
|
|
3198
|
+
printResult(
|
|
3199
|
+
io,
|
|
3200
|
+
flags,
|
|
3201
|
+
{ received: events.length, inserted, duplicates, cost_usd: cost, rejected },
|
|
3202
|
+
() => ""
|
|
3203
|
+
);
|
|
3204
|
+
return 0;
|
|
3205
|
+
}
|
|
3174
3206
|
case "remove": {
|
|
3175
3207
|
const id = positionals[1];
|
|
3176
3208
|
if (!id) throw new CliError("Usage: admin spend remove <entry-id>");
|
|
@@ -3208,7 +3240,7 @@ async function cmdAdminSpend(ctx) {
|
|
|
3208
3240
|
}
|
|
3209
3241
|
default:
|
|
3210
3242
|
throw new CliError(
|
|
3211
|
-
"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> … | remove <entry-id> | services | service <slug> --owner … --monthly …"
|
|
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 …"
|
|
3212
3244
|
);
|
|
3213
3245
|
}
|
|
3214
3246
|
}
|