@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 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.74.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/
@@ -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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letterstory/cli",
3
- "version": "0.74.0",
3
+ "version": "0.75.0",
4
4
  "description": "Spin up and manage Letterstory phantom blogs from your terminal.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",