@weirgate/cli 0.1.0 → 0.3.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/README.md CHANGED
@@ -30,4 +30,17 @@ 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.0**: `weirgate models list` and the MCP tool `weirgate_list_models` (model prices
40
+ and provider policy, with the reason a model isn't allowed); `weirgate audit list`
41
+ pages with `--limit`, `--cursor`, and `--all`.
42
+ - **0.2.0**: CLI and MCP failures use the API's error envelope; `grants create
43
+ --source`; `doctor payments`.
44
+ - **0.1.0**: first release.
45
+
33
46
  MIT licensed. Copyright 2026 Astraen Labs LLC.
@@ -74,10 +74,10 @@ async function exchangeSessionGrantCode(apiUrl, code, fetchImpl) {
74
74
  const apiVersion = response.headers.get("Weirgate-Api-Version") ?? "unknown";
75
75
  const payload = await response.json().catch(() => ({}));
76
76
  if (apiVersion !== API_VERSION) {
77
- throw new WeirgateApiError({ message: `API version mismatch: expected ${API_VERSION}, received ${apiVersion}`, errorType: "api_version_mismatch", status: 502, requestId, apiVersion, details: payload });
77
+ throw new WeirgateApiError({ message: `API version mismatch: expected ${API_VERSION}, received ${apiVersion}`, errorType: "api_version_mismatch", status: 502, requestId, apiVersion, detail: { expected: API_VERSION, received: apiVersion } });
78
78
  }
79
79
  if (!response.ok || typeof payload.value !== "string") {
80
- throw new WeirgateApiError({ message: payload.error?.message ?? `Weirgate API returned HTTP ${response.status}`, errorType: response.headers.get("X-Weirgate-Error-Type") ?? payload.error?.type ?? "remote_error", status: response.status, requestId, apiVersion, details: payload.error?.details ?? payload });
80
+ throw new WeirgateApiError({ message: payload.error?.message ?? `Weirgate API returned HTTP ${response.status}`, errorType: response.headers.get("X-Weirgate-Error-Type") ?? payload.error?.type ?? "remote_error", status: response.status, requestId, apiVersion, ...(payload.error?.detail ? { detail: payload.error.detail } : {}) });
81
81
  }
82
82
  return payload.value;
83
83
  }
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,21 +143,24 @@ 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
- "grants:create": ["weirgate_grant_allowance", { ...common, external_id: requiredFlag(flags, "user"), units: numberRequired(flags, "units"), reason: stringFlag(flags, "reason") }],
155
+ "grants:create": ["weirgate_grant_allowance", { ...common, external_id: requiredFlag(flags, "user"), units: numberRequired(flags, "units"), source: stringFlag(flags, "source") }],
151
156
  "grants:reverse": ["weirgate_reverse_grant", { ...common, grant_id: requiredFlag(flags, "grant") }],
152
157
  "user-tiers:assign": ["weirgate_assign_user_tier", { ...common, external_id: requiredFlag(flags, "user"), tier: requiredFlag(flags, "tier"), top_up_now: booleanFlag(flags, "top-up-now") }],
153
158
  "user-tiers:revert": ["weirgate_revert_user_tier", { ...common, external_id: requiredFlag(flags, "user"), top_up_now: booleanFlag(flags, "top-up-now") }],
154
- "doctor:all": ["weirgate_run_config_doctor", { ...common, checks: ["auth", "provider", "webhook"] }],
159
+ "doctor:all": ["weirgate_run_config_doctor", { ...common, checks: ["auth", "provider", "webhook", "payments"] }],
155
160
  "doctor:auth": ["weirgate_run_config_doctor", { ...common, checks: ["auth"] }],
156
161
  "doctor:provider": ["weirgate_run_config_doctor", { ...common, checks: ["provider"] }],
157
162
  "doctor:webhook": ["weirgate_run_config_doctor", { ...common, checks: ["webhook"] }],
163
+ "doctor:payments": ["weirgate_run_config_doctor", { ...common, checks: ["payments"] }],
158
164
  "sandbox:call": ["weirgate_run_mock_call", { ...common, fixture: stringFlag(flags, "fixture") ?? "success", session_name: stringFlag(flags, "session-name") }],
159
165
  "docs:search": ["weirgate_search_docs", { query: requiredFlag(flags, "query"), section: stringFlag(flags, "section"), limit: numberFlag(flags, "limit") }],
160
166
  "docs:get": ["weirgate_get_doc", { page_id: requiredFlag(flags, "page") }],
@@ -169,6 +175,12 @@ async function commandToTool(client, noun, verb, flags) {
169
175
  return invokeTool(client, "weirgate_get_proposal", { proposal_id: entry[1].proposal_id });
170
176
  return localResult({ dry_run: true, operation: `${noun}:${verb}`, input: redact(entry[1]) });
171
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
+ }
172
184
  if (entry[0] === "__auth_presets")
173
185
  return client.request("/v1/admin/auth-presets");
174
186
  if (entry[0] === "__config_propose")
@@ -297,13 +309,13 @@ function printSuccess(result, flags) {
297
309
  if (output === "yaml")
298
310
  stdout.write(`${YAML.stringify(envelope)}`);
299
311
  else if (output === "table")
300
- 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`);
301
313
  else
302
314
  stdout.write(`${JSON.stringify(envelope)}\n`);
303
315
  }
304
316
  function printError(error, flags) {
305
317
  const typed = error instanceof WeirgateApiError ? error : new WeirgateApiError({ message: error instanceof Error ? error.message : String(error), errorType: "internal", status: 500, requestId: "local", apiVersion: API_VERSION });
306
- const envelope = { ok: false, error: { type: typed.errorType, message: typed.message, details: typed.details }, request_id: typed.requestId, api_version: typed.apiVersion };
318
+ const envelope = { ok: false, error: { type: typed.errorType, message: typed.message, request_id: typed.requestId, ...(typed.detail ? { detail: typed.detail } : {}) }, api_version: typed.apiVersion };
307
319
  if (flags.json || !process.stderr.isTTY)
308
320
  process.stderr.write(`${JSON.stringify(envelope)}\n`);
309
321
  else
@@ -342,10 +354,10 @@ function redact(value) { if (Array.isArray(value))
342
354
  return value.map(redact); if (!value || typeof value !== "object")
343
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)])); }
344
356
  function help() {
345
- 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`;
346
358
  }
347
359
  function completion(shell) {
348
- 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";
349
361
  if (shell === "zsh")
350
362
  return `#compdef weirgate\n_arguments '1:command:(${commands})'\n`;
351
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) => ({
@@ -60,7 +60,7 @@ export function createMcpServer(auth) {
60
60
  }
61
61
  catch (error) {
62
62
  if (error instanceof WeirgateApiError)
63
- return errorResult(error.errorType, error.message, error.requestId, error.details, error.apiVersion);
63
+ return errorResult(error.errorType, error.message, error.requestId, error.detail, error.apiVersion);
64
64
  return errorResult("invalid_request", error instanceof Error ? error.message : String(error), "local");
65
65
  }
66
66
  });
@@ -75,8 +75,9 @@ function removeSecretValues(value) {
75
75
  ? []
76
76
  : [[key, removeSecretValues(item)]]));
77
77
  }
78
- function errorResult(type, message, requestId, details, apiVersion = API_VERSION) {
79
- const body = { error: { type, message, ...(details === undefined ? {} : { details }) }, request_id: requestId, api_version: apiVersion };
78
+ /** Mirrors the HTTP error envelope (`error.type`, `message`, `request_id`, `detail`), plus `api_version`. */
79
+ function errorResult(type, message, requestId, detail, apiVersion = API_VERSION) {
80
+ const body = { error: { type, message, request_id: requestId, ...(detail === undefined ? {} : { detail }) }, api_version: apiVersion };
80
81
  return {
81
82
  isError: true,
82
83
  content: [{ type: "text", text: JSON.stringify(body) }],
@@ -5,7 +5,8 @@ export class WeirgateApiError extends Error {
5
5
  status;
6
6
  requestId;
7
7
  apiVersion;
8
- details;
8
+ /** The API error's `detail` (for example `detail.reason`), as the HTTP envelope carries it. */
9
+ detail;
9
10
  constructor(input) {
10
11
  super(input.message);
11
12
  this.name = "WeirgateApiError";
@@ -13,7 +14,7 @@ export class WeirgateApiError extends Error {
13
14
  this.status = input.status;
14
15
  this.requestId = input.requestId;
15
16
  this.apiVersion = input.apiVersion;
16
- this.details = input.details;
17
+ this.detail = input.detail;
17
18
  }
18
19
  }
19
20
  export class AgentApiClient {
@@ -73,7 +74,7 @@ export class AgentApiClient {
73
74
  status: 502,
74
75
  requestId,
75
76
  apiVersion,
76
- details: payload,
77
+ detail: { expected: API_VERSION, received: apiVersion },
77
78
  });
78
79
  }
79
80
  if (!response.ok) {
@@ -84,7 +85,7 @@ export class AgentApiClient {
84
85
  status: response.status,
85
86
  requestId,
86
87
  apiVersion,
87
- details: body?.error?.details ?? payload,
88
+ ...(body?.error?.detail ? { detail: body.error.detail } : {}),
88
89
  });
89
90
  }
90
91
  return { data: payload, request_id: requestId, api_version: API_VERSION };
@@ -16,15 +16,17 @@ 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) },
22
- { name: "weirgate_run_config_doctor", title: "Run config doctor", description: "Run non-billable configuration checks.", scope: "read", group: "setup", inputSchema: objectSchema({ ...app, checks: { type: "array", items: { enum: ["auth", "provider", "webhook"] } } }, appRequired) },
23
+ { name: "weirgate_run_config_doctor", title: "Run config doctor", description: "Run non-billable configuration checks.", scope: "read", group: "setup", inputSchema: objectSchema({ ...app, checks: { type: "array", items: { enum: ["auth", "provider", "webhook", "payments"] } } }, appRequired) },
23
24
  { name: "weirgate_list_provider_key_slots", title: "List provider key slots", description: "List write-only provider slot metadata. No key values are returned.", scope: "read", group: "provider_keys", inputSchema: objectSchema(app) },
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) },
@@ -44,7 +46,7 @@ export const TOOL_DEFINITIONS = [
44
46
  { name: "weirgate_preview_rules", title: "Preview rules", description: "Preview rules against recent traffic and bind a content hash.", scope: "propose", group: "rules", inputSchema: objectSchema({ ...app, ruleset: document(), since: str(), until: str() }, ["app_id", "ruleset", "since", "until"]) },
45
47
  { name: "weirgate_propose_rules", title: "Propose rules", description: "Create a preview-hash-bound rules proposal.", scope: "propose", group: "rules", inputSchema: objectSchema({ ...app, ruleset: document(), preview_id: str() }, ["app_id", "ruleset", "preview_id"]) },
46
48
  { name: "weirgate_propose_user_override", title: "Propose user override", description: "Preview and propose a per-user velocity override.", scope: "propose", group: "rules", inputSchema: objectSchema({ ...app, external_id: str(), override: document(), since: str(), until: str() }, ["app_id", "external_id", "override"]) },
47
- { name: "weirgate_grant_allowance", title: "Grant allowance", description: "Grant units idempotently to a user.", scope: "apply", group: "credits", inputSchema: objectSchema({ ...app, external_id: str(), units: integer(), reason: str(), idempotency_key: str() }, ["app_id", "external_id", "units", "idempotency_key"]) },
49
+ { name: "weirgate_grant_allowance", title: "Grant allowance", description: "Grant units idempotently to a user.", scope: "apply", group: "credits", inputSchema: objectSchema({ ...app, external_id: str(), units: { type: "number", exclusiveMinimum: 0 }, source: str(), idempotency_key: str() }, ["app_id", "external_id", "units", "idempotency_key"]) },
48
50
  { name: "weirgate_reverse_grant", title: "Reverse grant", description: "Reverse an allowance grant idempotently.", scope: "apply", group: "credits", inputSchema: objectSchema({ ...app, grant_id: str(), idempotency_key: str() }, ["app_id", "grant_id", "idempotency_key"]) },
49
51
  { name: "weirgate_assign_user_tier", title: "Assign user tier", description: "Schedule a configured user tier for the next monthly grant, with an optional current-period delta top-up.", scope: "apply", group: "plans", inputSchema: objectSchema({ ...app, external_id: str(), tier: str(), top_up_now: { type: "boolean" }, idempotency_key: str() }, ["app_id", "external_id", "tier", "idempotency_key"]) },
50
52
  { name: "weirgate_revert_user_tier", title: "Revert user tier", description: "Schedule a user to return to the app default tier, with an optional current-period delta top-up.", scope: "apply", group: "plans", inputSchema: objectSchema({ ...app, external_id: str(), top_up_now: { type: "boolean" }, idempotency_key: str() }, ["app_id", "external_id", "idempotency_key"]) },
@@ -106,10 +108,11 @@ 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`);
112
- case "weirgate_run_config_doctor": return client.request(`/v1/admin/apps/${a()}/doctor`, { method: "POST", body: { checks: input.checks ?? ["auth", "provider", "webhook"] } });
115
+ case "weirgate_run_config_doctor": return client.request(`/v1/admin/apps/${a()}/doctor`, { method: "POST", body: { checks: input.checks ?? ["auth", "provider", "webhook", "payments"] } });
113
116
  case "weirgate_list_provider_key_slots": return client.request("/v1/admin/provider-keys", { query: q(input, ["app_id"]) });
114
117
  case "weirgate_test_provider_key": return client.request(`/v1/admin/provider-keys/${pathPart(input.provider)}/health`, { method: "POST", query: { app_id: String(input.app_id) } });
115
118
  case "weirgate_get_integration_snippet": return client.request(`/v1/admin/apps/${a()}/integration-snippet`, { query: q(input, ["mode", "feature_id"]) });
@@ -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"]) });
@@ -154,7 +158,7 @@ export async function invokeTool(client, name, rawInput) {
154
158
  const preview = await client.request(`/v1/admin/apps/${a()}/rules:preview`, { method: "POST", body: { ruleset, since: input.since, until: input.until } });
155
159
  return client.request(`/v1/admin/apps/${a()}/rules/proposals`, { method: "POST", body: { ruleset, preview_id: preview.data.preview_id } });
156
160
  }
157
- case "weirgate_grant_allowance": return client.request(`/v1/admin/apps/${a()}/users/${pathPart(input.external_id)}/grants`, { method: "POST", idempotencyKey: String(input.idempotency_key), body: { units: input.units, reason: input.reason ?? "agent grant" } });
161
+ case "weirgate_grant_allowance": return client.request(`/v1/admin/apps/${a()}/users/${pathPart(input.external_id)}/grants`, { method: "POST", idempotencyKey: String(input.idempotency_key), body: { units: input.units, ...(input.source === undefined ? {} : { source: input.source }) } });
158
162
  case "weirgate_reverse_grant": return client.request(`/v1/admin/apps/${a()}/grants/${pathPart(input.grant_id)}/reverse`, { method: "POST", idempotencyKey: String(input.idempotency_key) });
159
163
  case "weirgate_assign_user_tier": return client.request(`/v1/admin/apps/${a()}/users/${pathPart(input.external_id)}/tier`, { method: "PUT", idempotencyKey: String(input.idempotency_key), body: { tier: input.tier, top_up_now: input.top_up_now ?? false } });
160
164
  case "weirgate_revert_user_tier": return client.request(`/v1/admin/apps/${a()}/users/${pathPart(input.external_id)}/tier`, { method: "DELETE", idempotencyKey: String(input.idempotency_key), body: { top_up_now: input.top_up_now ?? false } });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weirgate/cli",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
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",