@weirgate/cli 0.2.0 → 0.3.1

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/README.md CHANGED
@@ -30,4 +30,20 @@ Provider-key values are entered only through `weirgate provider-keys put|rotate`
30
30
  local browser page by default, or hidden terminal input with `--stdin`) and never appear
31
31
  in output. Exit codes are listed in the guide.
32
32
 
33
+ `weirgate models list` shows model prices and provider policy (`--upstream`,
34
+ `--modality`), and `weirgate audit list --all` reads the whole audit log (up to 10,000
35
+ events; `--limit` and `--cursor` page by hand).
36
+
37
+ ## Changes
38
+
39
+ - **0.3.1**: the CLI and `weirgate-mcp` refuse the platform super-admin key on every
40
+ command (exit code 4, `platform_key_rejected`) and ask for a tenant-scoped key, instead
41
+ of acting on the first tenant the platform key can see.
42
+ - **0.3.0**: `weirgate models list` and the MCP tool `weirgate_list_models` (model prices
43
+ and provider policy, with the reason a model isn't allowed); `weirgate audit list`
44
+ pages with `--limit`, `--cursor`, and `--all`.
45
+ - **0.2.0**: CLI and MCP failures use the API's error envelope; `grants create
46
+ --source`; `doctor payments`.
47
+ - **0.1.0**: first release.
48
+
33
49
  MIT licensed. Copyright 2026 Astraen Labs LLC.
package/dist/cli/index.js CHANGED
@@ -10,7 +10,10 @@ import { AgentApiClient, API_VERSION, DEFAULT_API_URL, pathPart, WeirgateApiErro
10
10
  import { invokeTool } from "../shared/tools.js";
11
11
  import { browserSessionGrant } from "./browser-session-grant.js";
12
12
  import { exitCodeFor } from "./exit-codes.js";
13
+ import { AUDIT_ALL_CAP, listAllAuditEvents, modelsTable } from "./listing.js";
13
14
  let deferringRequiredFlags = false;
15
+ /** A command-specific `--output table` view; other commands print YAML. */
16
+ let tableView;
14
17
  const parsed = parse(process.argv.slice(2));
15
18
  try {
16
19
  const result = await run(parsed);
@@ -140,11 +143,13 @@ async function commandToTool(client, noun, verb, flags) {
140
143
  "proposals:get": ["weirgate_get_proposal", { proposal_id: requiredFlag(flags, "proposal") }],
141
144
  "proposals:cancel": ["weirgate_cancel_proposal", { proposal_id: requiredFlag(flags, "proposal") }],
142
145
  "proposals:apply": ["weirgate_apply_proposal", { proposal_id: requiredFlag(flags, "proposal"), expected_base_revision: numberFlag(flags, "expected-base-revision"), idempotency_key: requiredFlag(flags, "idempotency-key") }],
143
- "usage:query": ["weirgate_query_usage", { ...common, since: requiredFlag(flags, "since"), until: requiredFlag(flags, "until"), group_by: stringFlag(flags, "group-by"), limit: numberFlag(flags, "limit") }],
146
+ "usage:query": ["weirgate_query_usage", { ...common, since: requiredFlag(flags, "since"), until: requiredFlag(flags, "until"), group_by: stringFlag(flags, "group-by"), bucket: stringFlag(flags, "bucket"), limit: numberFlag(flags, "limit") }],
147
+ "usage:failures": ["weirgate_list_failures", { ...common, since: stringFlag(flags, "since"), until: stringFlag(flags, "until"), cursor: stringFlag(flags, "cursor"), limit: numberFlag(flags, "limit") }],
144
148
  "pricing-insights:get": ["weirgate_get_pricing_insights", { ...common, since: requiredFlag(flags, "since"), until: requiredFlag(flags, "until") }],
145
149
  "users:timeline": ["weirgate_get_user_timeline", { ...common, external_id: requiredFlag(flags, "user") }],
146
150
  "events:list": ["weirgate_list_events", common],
147
- "audit:list": ["weirgate_list_audit_events", { ...common, since: stringFlag(flags, "since"), until: stringFlag(flags, "until"), action: stringFlag(flags, "action") }],
151
+ "audit:list": ["weirgate_list_audit_events", { ...common, since: stringFlag(flags, "since"), until: stringFlag(flags, "until"), action: stringFlag(flags, "action"), limit: numberFlag(flags, "limit"), cursor: stringFlag(flags, "cursor") }],
152
+ "models:list": ["weirgate_list_models", { upstream: stringFlag(flags, "upstream"), modality: stringFlag(flags, "modality") }],
148
153
  "provider-keys:list": ["weirgate_list_provider_key_slots", common],
149
154
  "provider-keys:test": ["weirgate_test_provider_key", { ...common, provider: requiredFlag(flags, "provider") }],
150
155
  "grants:create": ["weirgate_grant_allowance", { ...common, external_id: requiredFlag(flags, "user"), units: numberRequired(flags, "units"), source: stringFlag(flags, "source") }],
@@ -170,6 +175,12 @@ async function commandToTool(client, noun, verb, flags) {
170
175
  return invokeTool(client, "weirgate_get_proposal", { proposal_id: entry[1].proposal_id });
171
176
  return localResult({ dry_run: true, operation: `${noun}:${verb}`, input: redact(entry[1]) });
172
177
  }
178
+ if (noun === "models")
179
+ tableView = modelsTable;
180
+ if (noun === "audit" && flags.all) {
181
+ const input = entry[1];
182
+ return listAllAuditEvents(client, { app_id: input.app_id, action: input.action, since: input.since, until: input.until }, { cursor: input.cursor, pageSize: input.limit });
183
+ }
173
184
  if (entry[0] === "__auth_presets")
174
185
  return client.request("/v1/admin/auth-presets");
175
186
  if (entry[0] === "__config_propose")
@@ -298,7 +309,7 @@ function printSuccess(result, flags) {
298
309
  if (output === "yaml")
299
310
  stdout.write(`${YAML.stringify(envelope)}`);
300
311
  else if (output === "table")
301
- stdout.write(`${human(result.data)}${flags["request-id"] ? `\nrequest_id: ${result.request_id}` : ""}\n`);
312
+ stdout.write(`${(tableView ?? human)(result.data)}${flags["request-id"] ? `\nrequest_id: ${result.request_id}` : ""}\n`);
302
313
  else
303
314
  stdout.write(`${JSON.stringify(envelope)}\n`);
304
315
  }
@@ -343,10 +354,10 @@ function redact(value) { if (Array.isArray(value))
343
354
  return value.map(redact); if (!value || typeof value !== "object")
344
355
  return value; return Object.fromEntries(Object.entries(value).map(([key, item]) => [/^(key|secret|token|password|credential|authorization)$/i.test(key) ? key : key, /^(key|secret|token|password|credential|authorization)$/i.test(key) ? "[redacted]" : redact(item)])); }
345
356
  function help() {
346
- return `weirgate <noun> <verb> [flags]\n\nAuth: auth login|status|logout, mcp install, init\nApps: apps list|get|propose, config export|validate|propose|apply, auth-presets list\nConfig: features/tiers/rules/overrides/proposals, doctor, sandbox\nOps: usage, pricing-insights, users, user-tiers, events, audit, provider-keys, grants, docs\nOutput: --output json|table|yaml; --json; mutations use --idempotency-key\nAuth login: browser consent by default; --stdin accepts a pre-minted key\nSecrets: provider-keys put|rotate defaults to --browser; --stdin rejects pipes without --allow-pipe\n`;
357
+ return `weirgate <noun> <verb> [flags]\n\nAuth: auth login|status|logout, mcp install, init\nApps: apps list|get|propose, config export|validate|propose|apply, auth-presets list\nConfig: features/tiers/rules/overrides/proposals, doctor, sandbox\nOps: usage, pricing-insights, users, user-tiers, events, audit, provider-keys, grants, docs\nModels: models list [--upstream openrouter|openai|anthropic|google|xai] [--modality chat|embedding|image|audio]\nAudit: audit list [--limit n] [--cursor c] [--all] (--all follows next_cursor, up to ${AUDIT_ALL_CAP} events)\nOutput: --output json|table|yaml; --json; mutations use --idempotency-key\nAuth login: browser consent by default; --stdin accepts a pre-minted key\nSecrets: provider-keys put|rotate defaults to --browser; --stdin rejects pipes without --allow-pipe\n`;
347
358
  }
348
359
  function completion(shell) {
349
- const commands = "auth mcp init apps config auth-presets features tiers rules overrides proposals usage pricing-insights users user-tiers events audit provider-keys grants doctor sandbox docs completion";
360
+ const commands = "auth mcp init apps config auth-presets models features tiers rules overrides proposals usage pricing-insights users user-tiers events audit provider-keys grants doctor sandbox docs completion";
350
361
  if (shell === "zsh")
351
362
  return `#compdef weirgate\n_arguments '1:command:(${commands})'\n`;
352
363
  if (shell === "fish")
@@ -0,0 +1,66 @@
1
+ import { record } from "../shared/api-client.js";
2
+ /** `weirgate audit list --all` stops after this many events; resume with `--cursor <next_cursor>`. */
3
+ export const AUDIT_ALL_CAP = 10_000;
4
+ const AUDIT_MAX_PAGE = 500;
5
+ /**
6
+ * Follows `pagination.next_cursor` from `cursor` (or the newest event) until the log ends or
7
+ * `cap` events are read. The first page asks for `include=total`, so the result says how many
8
+ * events match the filters. A capped result keeps `truncated: true` and the `next_cursor` to resume from.
9
+ */
10
+ export async function listAllAuditEvents(client, filters, options = {}) {
11
+ const cap = options.cap ?? AUDIT_ALL_CAP;
12
+ const pageSize = Math.min(options.pageSize ?? AUDIT_MAX_PAGE, AUDIT_MAX_PAGE);
13
+ const events = [];
14
+ let cursor = options.cursor;
15
+ let total;
16
+ let pages = 0;
17
+ let last;
18
+ do {
19
+ const limit = Math.min(pageSize, cap - events.length);
20
+ last = await client.request("/v1/admin/audit", {
21
+ query: { ...filters, limit, cursor, ...(pages === 0 ? { include: "total" } : {}) },
22
+ });
23
+ pages += 1;
24
+ const page = record(last.data);
25
+ if (pages === 1)
26
+ total = page.pagination?.total;
27
+ events.push(...(page.data ?? []));
28
+ cursor = page.pagination?.truncated ? page.pagination.next_cursor ?? undefined : undefined;
29
+ } while (cursor && events.length < cap);
30
+ return {
31
+ ...last,
32
+ data: {
33
+ data: events,
34
+ pagination: {
35
+ limit: pageSize,
36
+ returned: events.length,
37
+ pages,
38
+ cap,
39
+ truncated: cursor !== undefined,
40
+ next_cursor: cursor ?? null,
41
+ ...(total === undefined ? {} : { total }),
42
+ },
43
+ },
44
+ };
45
+ }
46
+ const price = (value) => value === null ? "-" : `$${value}`;
47
+ const policyCell = (entry) => entry.allowed ? (entry.state === "allowed" ? "yes" : `yes (${entry.state})`) : "no";
48
+ /** The `--output table` view of `weirgate models list`: one line per row, prices in USD per 1M tokens. */
49
+ export function modelsTable(data) {
50
+ const catalog = record(data);
51
+ const rows = catalog.data ?? [];
52
+ const header = ["UPSTREAM", "MODEL", "IN/1M", "OUT/1M", "CONTEXT", "DEV KEY", "USER KEY"];
53
+ const lines = rows.map((row) => [
54
+ row.upstream,
55
+ row.model,
56
+ price(row.input_usd_per_million),
57
+ price(row.output_usd_per_million),
58
+ row.context_length === null ? "-" : String(row.context_length),
59
+ policyCell(row.policy.developer),
60
+ policyCell(row.policy.user),
61
+ ]);
62
+ const widths = header.map((title, column) => Math.max(title.length, ...lines.map((line) => line[column].length)));
63
+ const format = (line) => line.map((cell, column) => column === line.length - 1 ? cell : cell.padEnd(widths[column])).join(" ");
64
+ const footer = `${rows.length} models · prices as of ${catalog.as_of ?? "unknown"}${catalog.stale ? " (stale)" : ""} · policy revision ${catalog.policy_revision}`;
65
+ return [format(header), ...lines.map(format), "", footer].join("\n");
66
+ }
@@ -20,7 +20,7 @@ const rank = { read: 0, propose: 1, apply: 2 };
20
20
  export function createMcpServer(auth) {
21
21
  const server = new Server({ name: "weirgate", version: API_VERSION }, {
22
22
  capabilities: { tools: { listChanged: false } },
23
- instructions: "Operate Weirgate only through the frozen 2026-07-18 public API. Propose before apply. Provider keys are entered outside the model-facing MCP surface.",
23
+ instructions: "Operate Weirgate only through the 2026-07-18 public API. Propose before apply. Provider keys are entered outside the model-facing MCP surface.",
24
24
  });
25
25
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
26
26
  tools: allowedTools({ scope: auth.identity.scope, tool_groups: auth.identity.tool_groups }).map((tool) => ({
@@ -16,6 +16,7 @@ export const TOOL_DEFINITIONS = [
16
16
  { name: "weirgate_get_app", title: "Get app", description: "Get one visible app configuration.", scope: "read", group: "setup", inputSchema: objectSchema(app, appRequired) },
17
17
  { name: "weirgate_list_features", title: "List features", description: "List configured features for an app.", scope: "read", group: "setup", inputSchema: objectSchema(app, appRequired) },
18
18
  { name: "weirgate_get_feature", title: "Get feature", description: "Get a configured feature.", scope: "read", group: "setup", inputSchema: objectSchema({ ...app, feature_id: str() }, ["app_id", "feature_id"]) },
19
+ { name: "weirgate_list_models", title: "List models", description: "List models a feature can use, with list prices per million tokens (USD), context length, and whether the provider policy allows each for the developer's key and the user's own key (with a reason when not). Use `upstream` and `model` from a row as the feature's values.", scope: "read", group: "setup", inputSchema: objectSchema({ upstream: { enum: ["openrouter", "openai", "anthropic", "google", "xai"], description: "Provider slot the feature uses" }, modality: { enum: ["chat", "embedding", "image", "audio"] } }) },
19
20
  { name: "weirgate_search_docs", title: "Search docs", description: "Search the authenticated canonical Markdown corpus.", scope: "read", group: "docs", inputSchema: objectSchema({ query: str(), section: str(), limit: integer() }, ["query"]) },
20
21
  { name: "weirgate_get_doc", title: "Get doc", description: "Read one canonical Markdown document.", scope: "read", group: "docs", inputSchema: objectSchema({ page_id: str() }, ["page_id"]) },
21
22
  { name: "weirgate_get_onboarding_state", title: "Get onboarding state", description: "Read durable onboarding readiness evidence.", scope: "read", group: "setup", inputSchema: objectSchema(app, appRequired) },
@@ -24,7 +25,8 @@ export const TOOL_DEFINITIONS = [
24
25
  { name: "weirgate_test_provider_key", title: "Test provider key", description: "Run a non-billable metadata probe for an existing provider slot.", scope: "read", group: "provider_keys", inputSchema: objectSchema({ ...app, provider: str() }, ["app_id", "provider"]) },
25
26
  { name: "weirgate_get_integration_snippet", title: "Get integration snippet", description: "Generate an HTTP/curl integration snippet.", scope: "read", group: "setup", inputSchema: objectSchema({ ...app, mode: { enum: ["http", "curl"] }, feature_id: str() }, appRequired) },
26
27
  { name: "weirgate_run_mock_call", title: "Run mock call", description: "Run a deterministic sandbox fixture; never invokes live inference.", scope: "apply", group: "sandbox", inputSchema: objectSchema({ ...app, fixture: str(), session_name: str(), idempotency_key: str() }, ["app_id", "fixture"]) },
27
- { name: "weirgate_query_usage", title: "Query usage", description: "Query windowed usage rollups.", scope: "read", group: "usage", inputSchema: objectSchema({ ...app, since: str(), until: str(), group_by: { enum: ["feature", "user"] }, limit: integer() }, ["app_id", "since", "until"]) },
28
+ { name: "weirgate_query_usage", title: "Query usage", description: "Query windowed usage totals, groups, latency, refusals, and an optional day/hour series.", scope: "read", group: "usage", inputSchema: objectSchema({ ...app, since: str(), until: str(), group_by: { enum: ["feature", "user", "model", "funding_rail", "refusal_code"] }, bucket: { enum: ["day", "hour"] }, limit: integer() }, ["app_id", "since", "until"]) },
29
+ { name: "weirgate_list_failures", title: "List failures", description: "List recent refused and failed metered requests, newest first, with request ids for request traces.", scope: "read", group: "usage", inputSchema: objectSchema({ ...app, since: str(), until: str(), cursor: str(), limit: integer() }, appRequired) },
28
30
  { name: "weirgate_get_pricing_insights", title: "Get pricing insights", description: "Read closed-window pricing evidence.", scope: "read", group: "usage", inputSchema: objectSchema({ ...app, since: str(), until: str() }, ["app_id", "since", "until"]) },
29
31
  { name: "weirgate_get_user_timeline", title: "Get user timeline", description: "Read one user's tenant-scoped operational timeline.", scope: "read", group: "usage", inputSchema: objectSchema({ ...app, external_id: str() }, ["app_id", "external_id"]) },
30
32
  { name: "weirgate_list_events", title: "List events", description: "List webhook delivery events for an app.", scope: "read", group: "webhooks", inputSchema: objectSchema({ ...app, status: str(), cursor: str(), limit: integer() }, appRequired) },
@@ -106,6 +108,7 @@ export async function invokeTool(client, name, rawInput) {
106
108
  const r = await client.request("/v1/admin/config");
107
109
  return { ...r, data: record(findApp(r.data, String(input.app_id)).features)[String(input.feature_id)] };
108
110
  }
111
+ case "weirgate_list_models": return client.request("/v1/admin/provider-policy/models", { query: q(input, ["upstream", "modality"]) });
109
112
  case "weirgate_search_docs": return client.request("/v1/admin/docs/search", { query: q(input, ["query", "section", "limit"]) });
110
113
  case "weirgate_get_doc": return client.request(`/v1/admin/docs/${pathPart(input.page_id)}`);
111
114
  case "weirgate_get_onboarding_state": return client.request(`/v1/admin/apps/${a()}/readiness`);
@@ -117,7 +120,8 @@ export async function invokeTool(client, name, rawInput) {
117
120
  const session = await client.request(`/v1/admin/apps/${a()}/sandbox/sessions`, { method: "POST", body: { name: input.session_name ?? "agent mock call" }, idempotencyKey: input.idempotency_key });
118
121
  return client.request(`/v1/admin/apps/${a()}/sandbox/sessions/${pathPart(session.data.id)}/runs`, { method: "POST", body: { fixture: input.fixture } });
119
122
  }
120
- case "weirgate_query_usage": return client.request(`/v1/admin/apps/${a()}/usage`, { query: q(input, ["since", "until", "group_by", "limit"]) });
123
+ case "weirgate_query_usage": return client.request(`/v1/admin/apps/${a()}/usage`, { query: q(input, ["since", "until", "group_by", "bucket", "limit"]) });
124
+ case "weirgate_list_failures": return client.request(`/v1/admin/apps/${a()}/failures`, { query: q(input, ["since", "until", "cursor", "limit"]) });
121
125
  case "weirgate_get_pricing_insights": return client.request(`/v1/admin/apps/${a()}/pricing-insights`, { query: q(input, ["since", "until"]) });
122
126
  case "weirgate_get_user_timeline": return client.request(`/v1/admin/apps/${a()}/users/${pathPart(input.external_id)}`);
123
127
  case "weirgate_list_events": return client.request(`/v1/admin/apps/${a()}/webhooks/deliveries`, { query: q(input, ["status", "cursor", "limit"]) });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weirgate/cli",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "Weirgate CLI and local stdio MCP server: scoped, proposal-first management of Weirgate apps.",
5
5
  "license": "MIT",
6
6
  "author": "Astraen Labs LLC",