@aiaiai-pt/martha-cli 0.62.0 → 0.63.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/CHANGELOG.md CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  All notable changes to `@aiaiai-pt/martha-cli`. Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). This project adheres to semver — `0.x` releases may include breaking changes between minor versions.
4
4
 
5
+ ## [0.63.0] — 2026-10-09
6
+
7
+ ### Added — spend ceilings narrowed by API Client (#1635)
8
+
9
+ A ceiling covers the tenant, narrowed to an AI Agent and/or an API Client. It
10
+ applies to a call when every dimension it names matches, so one AI Agent can be
11
+ capped within one API Client.
12
+
13
+ - `martha budget set --client <id>` (combine with `--agent <uuid>` for an agent within a client).
14
+ - `martha budget list` names each ceiling's scope: `tenant`, `agent <name>`,
15
+ `client <name>`, or `agent <name> via client <name>`.
16
+ - `martha spend --group-by client`: spend per API Client.
17
+
5
18
  ## [0.62.0] — 2026-10-08
6
19
 
7
20
  ### Added — `--org` picks the organization for multi-organization tokens (#1611)
package/dist/index.js CHANGED
@@ -11244,7 +11244,7 @@ init_config();
11244
11244
  init_errors();
11245
11245
 
11246
11246
  // src/version.ts
11247
- var CLI_VERSION = "0.62.0";
11247
+ var CLI_VERSION = "0.63.0";
11248
11248
 
11249
11249
  // src/commands/sessions.ts
11250
11250
  function relativeTime(iso) {
@@ -21744,7 +21744,7 @@ ${models.length} models`));
21744
21744
 
21745
21745
  // src/commands/spend.ts
21746
21746
  init_errors();
21747
- var GROUPINGS = ["day", "agent", "model", "surface", "billing_owner"];
21747
+ var GROUPINGS = ["day", "agent", "model", "surface", "billing_owner", "client"];
21748
21748
  function usd(value) {
21749
21749
  return value == null ? "-" : `$${Number(value).toFixed(4)}`;
21750
21750
  }
@@ -21803,6 +21803,13 @@ ${data.tenant_id}: ${t.calls} calls, ${usd(t.cost_usd)}` + (t.unpriced_calls ? `
21803
21803
  init_errors();
21804
21804
  var PERIODS = ["day", "month"];
21805
21805
  var ACTIONS = ["warn", "require_approval", "block"];
21806
+ function budgetScope(b) {
21807
+ const agent = b.agent_id ? `agent ${b.agent_name ?? b.agent_id}` : null;
21808
+ const client = b.client_id ? `client ${b.client_name ?? b.client_id}` : null;
21809
+ if (agent && client)
21810
+ return `${agent} via ${client}`;
21811
+ return agent ?? client ?? "tenant";
21812
+ }
21806
21813
  function oneOf(value, allowed, flag) {
21807
21814
  if (!allowed.includes(value)) {
21808
21815
  throw new CLIError(`${flag} must be one of: ${allowed.join(", ")}`, 4 /* Validation */);
@@ -21832,22 +21839,25 @@ function registerBudgetCommands(program) {
21832
21839
  return;
21833
21840
  }
21834
21841
  for (const b of data.budgets) {
21835
- const scope = b.agent_id ? `agent ${b.agent_id}` : "tenant";
21836
- console.log(`${b.id} ${scope.padEnd(42)} ${b.period.padEnd(6)} $${b.limit_usd} ${b.action}`);
21842
+ console.log(`${b.id} ${budgetScope(b).padEnd(42)} ${b.period.padEnd(6)} $${b.limit_usd} ${b.action}`);
21837
21843
  }
21838
21844
  });
21839
- budget.command("set").description("Create or replace a ceiling (one per tenant-or-agent + period)").requiredOption("--period <p>", `One of: ${PERIODS.join(", ")}`).requiredOption("--limit <usd>", "Ceiling in USD (cache reads included)").requiredOption("--action <a>", `One of: ${ACTIONS.join(", ")}`).option("--agent <uuid>", "Scope to one AI Agent (default: whole tenant)").option("--note <text>", "Why this ceiling exists").option("--tenant <id>", "Tenant (defaults to your own)").action(async (opts) => {
21845
+ budget.command("set").description("Create or replace a ceiling (one per scope + period)").requiredOption("--period <p>", `One of: ${PERIODS.join(", ")}`).requiredOption("--limit <usd>", "Ceiling in USD (cache reads included)").requiredOption("--action <a>", `One of: ${ACTIONS.join(", ")}`).option("--agent <uuid>", "Narrow to one AI Agent (default: whole tenant)").option("--client <id>", "Narrow to one API Client (with --agent: that agent within it)").option("--note <text>", "Why this ceiling exists").option("--tenant <id>", "Tenant (defaults to your own)").action(async (opts) => {
21840
21846
  oneOf(opts.period, PERIODS, "--period");
21841
21847
  oneOf(opts.action, ACTIONS, "--action");
21842
21848
  if (!/^\d+(\.\d{1,4})?$/.test(opts.limit)) {
21843
21849
  throw new CLIError("--limit must be a non-negative USD amount", 4 /* Validation */);
21844
21850
  }
21851
+ if (opts.client !== undefined && !/^[1-9]\d*$/.test(opts.client)) {
21852
+ throw new CLIError("--client must be an API Client id (a positive integer)", 4 /* Validation */);
21853
+ }
21845
21854
  const ctx = context();
21846
21855
  const saved = await ctx.api.put(await path(ctx, opts.tenant), {
21847
21856
  period: opts.period,
21848
21857
  limit_usd: opts.limit,
21849
21858
  action: opts.action,
21850
21859
  agent_id: opts.agent ?? null,
21860
+ client_id: opts.client !== undefined ? Number(opts.client) : null,
21851
21861
  note: opts.note ?? null
21852
21862
  });
21853
21863
  if (program.opts().json)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiaiai-pt/martha-cli",
3
- "version": "0.62.0",
3
+ "version": "0.63.0",
4
4
  "description": "Terminal-first client for the Martha AI platform",
5
5
  "homepage": "https://docs.martha.nomadriver.co",
6
6
  "repository": {