@formo/cli 1.0.2 → 1.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Formo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -10,6 +10,23 @@ npm install -g @formo/cli
10
10
  npx @formo/cli
11
11
  ```
12
12
 
13
+ ## Install the Formo Analytics agent skill
14
+
15
+ This repository also ships the [`formo-analytics`](skills/formo-analytics/SKILL.md) skill for Claude Code, Codex, and other [Agent Skills](https://agentskills.io)-compatible tools. It teaches agents to use Formo's MCP server, CLI, and REST API for project-scoped product and onchain analytics.
16
+
17
+ Install it in the current project:
18
+
19
+ ```bash
20
+ npx skills add https://github.com/getformo/cli/tree/main/skills/formo-analytics
21
+ ```
22
+
23
+ Or install it globally for Claude Code and Codex:
24
+
25
+ ```bash
26
+ npx skills add getformo/cli --skill formo-analytics --global \
27
+ --agent claude-code --agent codex
28
+ ```
29
+
13
30
  ## Authentication
14
31
 
15
32
  Save your API key locally:
@@ -24,7 +41,7 @@ Or set the `FORMO_API_KEY` environment variable — it takes precedence over the
24
41
  export FORMO_API_KEY=formo_abc123
25
42
  ```
26
43
 
27
- Get your API key from `Settings → API Keys` in the [Formo dashboard](https://app.formo.so).
44
+ Get your API key from `Settings → API` in the [Formo dashboard](https://app.formo.so).
28
45
 
29
46
  For local development or proxying, override API hosts with:
30
47
 
@@ -87,6 +104,7 @@ Search wallet profiles with filters, sorting, and pagination. Returns a `Paginat
87
104
  | Option | Description |
88
105
  |---|---|
89
106
  | `--address` | Filter by wallet address |
107
+ | `--search` | Free-text search across address and identity fields |
90
108
  | `--page` | Page number (1-indexed, default `1`) |
91
109
  | `--size` | Page size (default `100`, max `1000`) |
92
110
  | `--order-by` | `last_onchain`, `first_onchain`, `net_worth_usd`, `updated_at`, `tx_count`, `first_seen`, `last_seen`, `num_sessions`, `revenue`, `volume`, `points` |
@@ -104,6 +122,20 @@ formo profiles search --conditions '[{"field":"users.net_worth_usd","op":"gt","v
104
122
  formo profiles search --conditions '[{"field":"chains.1.balance","op":"gt","value":1000}]' --size 20
105
123
  ```
106
124
 
125
+ ### Lifecycle tuning (advanced)
126
+
127
+ Both `profiles get` and `profiles search` accept optional flags to override the lifecycle stage thresholds used when computing `lifecycle`:
128
+
129
+ | Option | Description |
130
+ |---|---|
131
+ | `--new-window-days` | Override lifecycle new-user window in days |
132
+ | `--churn-window-days` | Override lifecycle churn window in days |
133
+ | `--power-user-min-active-days` | Override lifecycle power-user minimum active days |
134
+ | `--power-user-window-days` | Override lifecycle power-user window in days |
135
+ | `--resurrected-gap-days` | Override lifecycle resurrected gap in days |
136
+ | `--at-risk-min-days-inactive` | Override lifecycle at-risk minimum inactive days |
137
+ | `--at-risk-prior-active-days-threshold` | Override lifecycle at-risk prior active days threshold |
138
+
107
139
  ### `profiles update <address>`
108
140
 
109
141
  Merge-update identity properties on a wallet profile.
@@ -188,15 +220,16 @@ Get a single alert by ID.
188
220
  | `--trigger-filters` | JSON array of trigger filter objects |
189
221
  | `--recipient` | JSON array of recipient objects |
190
222
  | `--secret` | Webhook secret |
223
+ | `--slack-property-keys` | JSON array of event/user property keys to include in Slack alerts |
191
224
 
192
225
  ```bash
193
226
  formo alerts create --name "High value tx" --trigger-type event \
194
- --trigger-filters '[{"name":"event","operator":"equals","value":"transaction"}]' \
227
+ --trigger-filters '[{"name":"event","operator":"eq","value":"transaction"}]' \
195
228
  --recipient '[{"type":"email","value":["alerts@myapp.com"]}]'
196
229
  ```
197
230
 
198
231
  ### `alerts update <alertId>`
199
- Same options as `create`. Replaces the alert configuration.
232
+ Same options as `create`. Replaces the alert configuration in full — omitted options are reset to their defaults (e.g. leaving out `--trigger-filters` clears the existing trigger filters).
200
233
 
201
234
  ### `alerts delete <alertId>`
202
235
  Delete an alert.
@@ -254,7 +287,7 @@ Delete a board.
254
287
 
255
288
  ## `formo charts`
256
289
 
257
- Chart commands. Charts live inside a board. Requires `charts:read` / `charts:write`.
290
+ Chart commands. Charts live inside a board. Requires `boards:read` / `boards:write`.
258
291
 
259
292
  ### `charts list --board-id <boardId>`
260
293
  List all charts in a board.
@@ -277,8 +310,12 @@ formo charts create --board-id brd_123 --title "Daily Active Users" \
277
310
  formo charts create --board-id brd_123 --body '{"title":"Recent Events","chart_type":"table","query":"SELECT * FROM events LIMIT 10"}'
278
311
  ```
279
312
 
280
- ### `charts update <chartId> --board-id <boardId> --body '<json>'`
281
- Update a chart.
313
+ ### `charts update <chartId> --board-id <boardId> [options]`
314
+ Update a chart. Accepts the same options as `create`: a raw `--body '<json>'` and/or typed flags (`--title`, `--chart-type`, `--query`, `--description`, `--x-axis`, `--y-axis`, `--group-by`, `--steps`, `--settings`). Typed flags override matching `--body` keys.
315
+
316
+ ```bash
317
+ formo charts update chart_abc123 --board-id brd_123 --title "Renamed chart"
318
+ ```
282
319
 
283
320
  ### `charts query <chartId> --board-id <boardId> --date-from <YYYY-MM-DD> --date-to <YYYY-MM-DD>`
284
321
  Execute a saved chart that uses `{{date_from}}` / `{{date_to}}` variables.
@@ -384,7 +421,7 @@ Pre-built analytics pipes — the same data that powers the Formo dashboard —
384
421
  |---|---|
385
422
  | `--date-from` | Inclusive start date `YYYY-MM-DD` (default: 7 days before `--date-to`) |
386
423
  | `--date-to` | Inclusive end date `YYYY-MM-DD` (default: today) |
387
- | `--filters` | JSON array of `[{field,op,value}]`. Use `in`/`notIn` with a pipe-delimited value (e.g. `"chrome\|firefox"`) |
424
+ | `--filters` | JSON array of `[{field,op,value}]`. Use `in`/`nin` with a pipe-delimited value (e.g. `"chrome\|firefox"`) |
388
425
  | `--params` | JSON object of pipe-specific params merged into the query (e.g. `{"limit":10,"group_by":"device"}`) |
389
426
 
390
427
  ```bash
@@ -392,7 +429,7 @@ formo analytics kpis
392
429
  formo analytics kpis --date-from 2026-04-01 --date-to 2026-04-30 --params '{"group_by":"device"}'
393
430
  formo analytics funnel --date-from 2026-04-01 --date-to 2026-04-30 --params '{"steps":[{"type":"event","event":"page","name":"page::0","filters":[]},{"type":"track","event":"connect","name":"connect::1","filters":[]}],"window_seconds":86400}'
394
431
  formo analytics top_wallets --date-from 2026-04-01 --date-to 2026-04-30 --params '{"limit":10}'
395
- formo analytics retention --filters '[{"field":"location","op":"equals","value":"US"}]'
432
+ formo analytics retention --filters '[{"field":"location","op":"eq","value":"US"}]'
396
433
  ```
397
434
 
398
435
  > Requires `query:read` scope. Run `formo analytics <pipe> --help` for the pipe-specific params accepted via `--params`.
@@ -403,7 +440,7 @@ formo analytics retention --filters '[{"field":"location","op":"equals","value":
403
440
 
404
441
  ### `import wallets`
405
442
 
406
- Bulk-import wallet addresses into the project via the events API.
443
+ Bulk-import wallet addresses into the project via the main Formo API, authenticated with your workspace API key.
407
444
 
408
445
  | Option | Description |
409
446
  |---|---|
@@ -415,17 +452,26 @@ formo import wallets --addresses '["0xabc...","0xdef..."]'
415
452
  formo import wallets --rows '[{"address":"0xabc...","properties":{"display_name":"Alice"}}]'
416
453
  ```
417
454
 
455
+ > Requires `profiles:write` scope. Only available on Scale and Enterprise plans.
456
+
418
457
  ---
419
458
 
420
459
  ## `formo events`
421
460
 
422
461
  ### `events ingest`
423
462
 
424
- Send raw analytics events to `events.formo.so`. This command uses a project SDK write key, not the workspace API key.
463
+ Send raw analytics events to `events.formo.so`. This command uses a project SDK write key, not the workspace API key — pass it via `--write-key` or the `FORMO_WRITE_KEY` environment variable.
464
+
465
+ | Option | Description |
466
+ |---|---|
467
+ | `--event` | Single event as a JSON object; wrapped in an array before sending |
468
+ | `--events` | JSON array of event objects to send as a batch |
469
+ | `--write-key` | Project SDK write key (defaults to `FORMO_WRITE_KEY`) |
425
470
 
426
471
  ```bash
427
472
  export FORMO_WRITE_KEY=formo_write_key_xxx
428
473
  formo events ingest --event '{"type":"track","channel":"cli","version":"1","anonymous_id":"anon_123","event":"CLI Test","context":{},"properties":{},"original_timestamp":"2026-04-27T23:05:38.000Z","sent_at":"2026-04-27T23:05:42.000Z","message_id":"cli-test-1"}'
474
+ formo events ingest --events '[{"type":"track","event":"First"},{"type":"track","event":"Second"}]'
429
475
  ```
430
476
 
431
477
  ---
@@ -448,7 +494,7 @@ formo events ingest --event '{"type":"track","channel":"cli","version":"1","anon
448
494
  | Field | Type | Description |
449
495
  |---|---|---|
450
496
  | `field` | `string` | Typed path (see prefixes below) |
451
- | `op` | `string` | `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `in`, `nin` |
497
+ | `op` | `string` | `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `in`, `nin`, `contains` (social fields only), `notEmpty` / `isEmpty` (value-less existence checks). Long-form spellings (`equals`, `greater`, `includes`, …) are retired — the API rejects them with a `400` naming the token |
452
498
  | `value` | `any` | Value to compare against |
453
499
  | `scope` | `string` | _(token filters only)_ `any` or `protocol` |
454
500
  | `appId` | `string` | _(token filters with `scope: protocol`)_ e.g. `aave-v3` |
@@ -1,36 +1,28 @@
1
1
  import { Cli } from 'incur';
2
+ import { type PaginationOptions } from '../lib/pagination';
3
+ export type { PaginationOptions };
2
4
  export declare const alerts: Cli.Cli<{}, undefined, undefined>;
3
- export interface PaginationOptions {
4
- page?: number;
5
- size?: number;
6
- }
7
- export declare function listAlertsRun(options?: PaginationOptions): Promise<import("axios").AxiosResponse<any, any, {}>>;
8
- export declare function getAlertRun(alertId: string): Promise<import("axios").AxiosResponse<any, any, {}>>;
9
- export interface CreateAlertOptions {
10
- name: string;
11
- triggerType: 'event' | 'user' | string;
12
- triggerFilters?: string;
13
- recipient?: string;
14
- secret?: string;
15
- slackPropertyKeys?: string;
16
- }
17
- export declare function buildAlertBody(options: CreateAlertOptions | UpdateAlertOptions): Record<string, unknown>;
18
- export declare function createAlertRun(options: CreateAlertOptions): Promise<import("axios").AxiosResponse<any, any, {}>>;
19
- export interface UpdateAlertOptions {
5
+ export declare function listAlertsRun(options?: PaginationOptions): Promise<unknown>;
6
+ export declare function getAlertRun(alertId: string): Promise<unknown>;
7
+ export interface AlertBodyOptions {
20
8
  name: string;
21
- triggerType: 'event' | 'user' | string;
9
+ triggerType: string;
22
10
  triggerFilters?: string;
23
11
  recipient?: string;
24
12
  secret?: string;
25
13
  slackPropertyKeys?: string;
26
14
  }
27
- export declare function updateAlertRun(alertId: string, options: UpdateAlertOptions): Promise<import("axios").AxiosResponse<any, any, {}>>;
28
- export declare function deleteAlertRun(alertId: string): Promise<import("axios").AxiosResponse<any, any, {}>>;
29
- export declare function toggleAlertRun(alertId: string, status: string): Promise<import("axios").AxiosResponse<any, any, {}>>;
15
+ export type CreateAlertOptions = AlertBodyOptions;
16
+ export type UpdateAlertOptions = AlertBodyOptions;
17
+ export declare function buildAlertBody(options: AlertBodyOptions): Record<string, unknown>;
18
+ export declare function createAlertRun(options: CreateAlertOptions): Promise<unknown>;
19
+ export declare function updateAlertRun(alertId: string, options: AlertBodyOptions): Promise<unknown>;
20
+ export declare function deleteAlertRun(alertId: string): Promise<unknown>;
21
+ export declare function toggleAlertRun(alertId: string, status: string): Promise<unknown>;
30
22
  export interface TestAlertOptions {
31
23
  sampleEvent?: string;
32
24
  sampleUser?: string;
33
25
  recipientOverrides?: string;
34
26
  }
35
27
  export declare function buildTestAlertBody(options: TestAlertOptions): Record<string, unknown> | undefined;
36
- export declare function testAlertRun(alertId: string, options?: TestAlertOptions): Promise<import("axios").AxiosResponse<any, any, {}>>;
28
+ export declare function testAlertRun(alertId: string, options?: TestAlertOptions): Promise<unknown>;
@@ -13,28 +13,19 @@ exports.testAlertRun = testAlertRun;
13
13
  const incur_1 = require("incur");
14
14
  const client_1 = require("../lib/client");
15
15
  const json_1 = require("../lib/json");
16
+ const pagination_1 = require("../lib/pagination");
16
17
  exports.alerts = incur_1.Cli.create('alerts', {
17
18
  description: 'Project alert commands — create, list, update, and delete alerts',
18
19
  });
19
- function buildPaginationParams(options = {}) {
20
- const params = {};
21
- if (options.page !== undefined)
22
- params.page = options.page;
23
- if (options.size !== undefined)
24
- params.size = options.size;
25
- return params;
26
- }
20
+ // ── List alerts ──
27
21
  function listAlertsRun(options = {}) {
28
22
  (0, client_1.requireApiKey)();
29
23
  const client = (0, client_1.createClient)();
30
- return client.get('/v0/alerts/', { params: buildPaginationParams(options) });
24
+ return client.get('/v0/alerts/', { params: (0, pagination_1.buildPaginationParams)(options) });
31
25
  }
32
26
  exports.alerts.command('list', {
33
27
  description: 'List all alerts for the project',
34
- options: incur_1.z.object({
35
- page: incur_1.z.coerce.number().optional().describe('Page number (1-indexed, default 1)'),
36
- size: incur_1.z.coerce.number().optional().describe('Page size (default 100, max 200)'),
37
- }),
28
+ options: incur_1.z.object(pagination_1.paginationOptionsSchema),
38
29
  examples: [{ description: 'List all project alerts' }],
39
30
  hint: 'Requires alerts:read scope on your API key.',
40
31
  run({ options }) {
@@ -60,6 +51,24 @@ exports.alerts.command('get', {
60
51
  return getAlertRun(args.alertId);
61
52
  },
62
53
  });
54
+ // Shared option fragment for `create` and `update` (same PUT/POST body).
55
+ const alertBodyOptionsSchema = {
56
+ name: incur_1.z.string().describe('Alert name'),
57
+ triggerType: incur_1.z.enum(['event', 'user']).describe('Trigger type'),
58
+ triggerFilters: incur_1.z
59
+ .string()
60
+ .optional()
61
+ .describe('JSON array of trigger filter objects'),
62
+ recipient: incur_1.z
63
+ .string()
64
+ .optional()
65
+ .describe('JSON array of recipient objects'),
66
+ secret: incur_1.z.string().optional().describe('Webhook secret for the alert'),
67
+ slackPropertyKeys: incur_1.z
68
+ .string()
69
+ .optional()
70
+ .describe('JSON array of event/user property keys to include in Slack alerts'),
71
+ };
63
72
  function buildAlertBody(options) {
64
73
  const body = {
65
74
  name: options.name,
@@ -87,23 +96,7 @@ function createAlertRun(options) {
87
96
  }
88
97
  exports.alerts.command('create', {
89
98
  description: 'Create a new project alert',
90
- options: incur_1.z.object({
91
- name: incur_1.z.string().describe('Alert name'),
92
- triggerType: incur_1.z.enum(['event', 'user']).describe('Trigger type'),
93
- triggerFilters: incur_1.z
94
- .string()
95
- .optional()
96
- .describe('JSON array of trigger filter objects'),
97
- recipient: incur_1.z
98
- .string()
99
- .optional()
100
- .describe('JSON array of recipient objects'),
101
- secret: incur_1.z.string().optional().describe('Webhook secret for the alert'),
102
- slackPropertyKeys: incur_1.z
103
- .string()
104
- .optional()
105
- .describe('JSON array of event/user property keys to include in Slack alerts'),
106
- }),
99
+ options: incur_1.z.object(alertBodyOptionsSchema),
107
100
  examples: [
108
101
  {
109
102
  options: { name: 'High value tx', triggerType: 'event' },
@@ -115,33 +108,18 @@ exports.alerts.command('create', {
115
108
  return createAlertRun(options);
116
109
  },
117
110
  });
111
+ // ── Update an alert ──
118
112
  function updateAlertRun(alertId, options) {
119
113
  (0, client_1.requireApiKey)();
120
114
  const client = (0, client_1.createClient)();
121
115
  return client.put(`/v0/alerts/${encodeURIComponent(alertId)}`, buildAlertBody(options));
122
116
  }
123
117
  exports.alerts.command('update', {
124
- description: 'Update an existing alert',
118
+ description: 'Update an existing alert (full replace — omitted options reset to defaults)',
125
119
  args: incur_1.z.object({
126
120
  alertId: incur_1.z.string().describe('Alert ID to update'),
127
121
  }),
128
- options: incur_1.z.object({
129
- name: incur_1.z.string().describe('Alert name'),
130
- triggerType: incur_1.z.enum(['event', 'user']).describe('Trigger type'),
131
- triggerFilters: incur_1.z
132
- .string()
133
- .optional()
134
- .describe('JSON array of trigger filter objects'),
135
- recipient: incur_1.z
136
- .string()
137
- .optional()
138
- .describe('JSON array of recipient objects'),
139
- secret: incur_1.z.string().optional().describe('Webhook secret for the alert'),
140
- slackPropertyKeys: incur_1.z
141
- .string()
142
- .optional()
143
- .describe('JSON array of event/user property keys to include in Slack alerts'),
144
- }),
122
+ options: incur_1.z.object(alertBodyOptionsSchema),
145
123
  examples: [
146
124
  {
147
125
  args: { alertId: 'alert_abc123' },
@@ -22,4 +22,4 @@ export interface AnalyticsOptions {
22
22
  * Exported for unit testing.
23
23
  */
24
24
  export declare function buildAnalyticsParams(options: AnalyticsOptions): Record<string, string | number | boolean>;
25
- export declare function runAnalytics(pipe: string, options: AnalyticsOptions): Promise<import("axios").AxiosResponse<any, any, {}>>;
25
+ export declare function runAnalytics(pipe: string, options: AnalyticsOptions): Promise<unknown>;
@@ -5,6 +5,7 @@ exports.buildAnalyticsParams = buildAnalyticsParams;
5
5
  exports.runAnalytics = runAnalytics;
6
6
  const incur_1 = require("incur");
7
7
  const client_1 = require("../lib/client");
8
+ const json_1 = require("../lib/json");
8
9
  exports.analytics = incur_1.Cli.create('analytics', {
9
10
  description: 'Pre-built analytics query commands — KPIs, funnels, retention, revenue, and top-N breakdowns',
10
11
  });
@@ -15,7 +16,7 @@ exports.analytics = incur_1.Cli.create('analytics', {
15
16
  const PIPES = [
16
17
  { name: 'kpis', description: 'Traffic KPIs: visitors, pageviews, bounce rate, session duration' },
17
18
  { name: 'event_timeseries', description: 'Event counts over time' },
18
- { name: 'funnel', description: 'Conversion funnel across ordered steps. --params: steps (JSON array of {type,event,name,filters?}), window_seconds, funnel_type, breakdown' },
19
+ { name: 'funnel', description: 'Conversion funnel across ordered steps. --params: steps (JSON array of {type,event,name,filters?}), window_seconds, funnel_type, group_by, limit, attribution' },
19
20
  { name: 'flow', description: 'User path/flow analysis. --params: start_step / end_step (JSON {type,event,...}), global_filters, window_seconds, max_steps' },
20
21
  { name: 'frequency', description: 'Engagement frequency distribution' },
21
22
  { name: 'lifecycle', description: 'User lifecycle stages (new, returning, power, resurrected, churned)' },
@@ -62,16 +63,7 @@ function buildAnalyticsParams(options) {
62
63
  const out = {};
63
64
  // --params first, so the validated flags below override it.
64
65
  if (options.params) {
65
- let parsed;
66
- try {
67
- parsed = JSON.parse(options.params);
68
- }
69
- catch {
70
- throw new Error('--params must be a valid JSON object');
71
- }
72
- if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
73
- throw new Error('--params must be a valid JSON object');
74
- }
66
+ const parsed = (0, json_1.parseJsonObject)(options.params, '--params');
75
67
  for (const [key, value] of Object.entries(parsed)) {
76
68
  if (RESERVED_PARAM_KEYS.has(key)) {
77
69
  throw new Error(`--params may not set "${key}" — use the --date-from/--date-to/--filters flags instead`);
@@ -123,7 +115,7 @@ const sharedOptions = incur_1.z.object({
123
115
  .string()
124
116
  .optional()
125
117
  .describe('JSON array of filter conditions: [{"field","op","value"}]. ' +
126
- 'Use op "in"/"notIn" with a pipe-delimited value (e.g. "chrome|firefox").'),
118
+ 'Use op "in"/"nin" with a pipe-delimited value (e.g. "chrome|firefox").'),
127
119
  params: incur_1.z
128
120
  .string()
129
121
  .optional()
@@ -1,11 +1,9 @@
1
1
  import { Cli } from 'incur';
2
+ import { type PaginationOptions } from '../lib/pagination';
3
+ export type { PaginationOptions };
2
4
  export declare const boards: Cli.Cli<{}, undefined, undefined>;
3
- export interface PaginationOptions {
4
- page?: number;
5
- size?: number;
6
- }
7
- export declare function listBoardsRun(options?: PaginationOptions): Promise<import("axios").AxiosResponse<any, any, {}>>;
8
- export declare function getBoardRun(boardId: string): Promise<import("axios").AxiosResponse<any, any, {}>>;
5
+ export declare function listBoardsRun(options?: PaginationOptions): Promise<unknown>;
6
+ export declare function getBoardRun(boardId: string): Promise<unknown>;
9
7
  export interface CreateBoardOptions {
10
8
  title?: string;
11
9
  name?: string;
@@ -13,12 +11,12 @@ export interface CreateBoardOptions {
13
11
  isPublic?: boolean;
14
12
  }
15
13
  export declare function buildBoardBody(options: CreateBoardOptions | UpdateBoardOptions): Record<string, unknown>;
16
- export declare function createBoardRun(options: CreateBoardOptions): Promise<import("axios").AxiosResponse<any, any, {}>>;
14
+ export declare function createBoardRun(options: CreateBoardOptions): Promise<unknown>;
17
15
  export interface UpdateBoardOptions {
18
16
  title?: string;
19
17
  name?: string;
20
18
  description?: string;
21
19
  isPublic?: boolean;
22
20
  }
23
- export declare function updateBoardRun(boardId: string, options: UpdateBoardOptions): Promise<import("axios").AxiosResponse<any, any, {}>>;
24
- export declare function deleteBoardRun(boardId: string): Promise<import("axios").AxiosResponse<any, any, {}>>;
21
+ export declare function updateBoardRun(boardId: string, options: UpdateBoardOptions): Promise<unknown>;
22
+ export declare function deleteBoardRun(boardId: string): Promise<unknown>;
@@ -9,29 +9,19 @@ exports.updateBoardRun = updateBoardRun;
9
9
  exports.deleteBoardRun = deleteBoardRun;
10
10
  const incur_1 = require("incur");
11
11
  const client_1 = require("../lib/client");
12
+ const pagination_1 = require("../lib/pagination");
12
13
  exports.boards = incur_1.Cli.create('boards', {
13
14
  description: 'Dashboard board commands — create, list, update, and delete boards',
14
15
  });
15
- function buildPaginationParams(options = {}) {
16
- const params = {};
17
- if (options.page !== undefined)
18
- params.page = options.page;
19
- if (options.size !== undefined)
20
- params.size = options.size;
21
- return params;
22
- }
23
16
  // ── List boards ──
24
17
  function listBoardsRun(options = {}) {
25
18
  (0, client_1.requireApiKey)();
26
19
  const client = (0, client_1.createClient)();
27
- return client.get('/v0/boards/', { params: buildPaginationParams(options) });
20
+ return client.get('/v0/boards/', { params: (0, pagination_1.buildPaginationParams)(options) });
28
21
  }
29
22
  exports.boards.command('list', {
30
23
  description: 'List all boards for the project',
31
- options: incur_1.z.object({
32
- page: incur_1.z.coerce.number().optional().describe('Page number (1-indexed, default 1)'),
33
- size: incur_1.z.coerce.number().optional().describe('Page size (default 100, max 200)'),
34
- }),
24
+ options: incur_1.z.object(pagination_1.paginationOptionsSchema),
35
25
  examples: [{ description: 'List all dashboard boards' }],
36
26
  hint: 'Requires boards:read scope on your API key.',
37
27
  run({ options }) {
@@ -62,7 +52,7 @@ function buildBoardBody(options) {
62
52
  const body = {};
63
53
  if (title !== undefined) {
64
54
  if (!title)
65
- throw new Error('--title must not be empty');
55
+ throw new Error('--title (or its deprecated alias --name) must not be empty');
66
56
  body.title = title;
67
57
  }
68
58
  if (options.description !== undefined) {
@@ -1,9 +1,7 @@
1
1
  import { Cli } from 'incur';
2
+ import { type PaginationOptions } from '../lib/pagination';
3
+ export type { PaginationOptions };
2
4
  export declare const charts: Cli.Cli<{}, undefined, undefined>;
3
- export interface PaginationOptions {
4
- page?: number;
5
- size?: number;
6
- }
7
5
  export interface ChartBodyOptions {
8
6
  body?: string;
9
7
  query?: string;
@@ -17,18 +15,18 @@ export interface ChartBodyOptions {
17
15
  settings?: string;
18
16
  }
19
17
  export declare function buildChartBody(options: ChartBodyOptions): Record<string, unknown>;
20
- export declare function listChartsRun(boardId: string, options?: PaginationOptions): Promise<import("axios").AxiosResponse<any, any, {}>>;
21
- export declare function listChartSummariesRun(boardId: string, options?: PaginationOptions): Promise<import("axios").AxiosResponse<any, any, {}>>;
22
- export declare function getChartRun(boardId: string, chartId: string): Promise<import("axios").AxiosResponse<any, any, {}>>;
18
+ export declare function listChartsRun(boardId: string, options?: PaginationOptions): Promise<unknown>;
19
+ export declare function listChartSummariesRun(boardId: string, options?: PaginationOptions): Promise<unknown>;
20
+ export declare function getChartRun(boardId: string, chartId: string): Promise<unknown>;
23
21
  export interface QueryChartOptions {
24
22
  dateFrom: string;
25
23
  dateTo: string;
26
24
  }
27
- export declare function queryChartRun(boardId: string, chartId: string, options: QueryChartOptions): Promise<import("axios").AxiosResponse<any, any, {}>>;
28
- export declare function createChartRun(boardId: string, input: string | ChartBodyOptions): Promise<import("axios").AxiosResponse<any, any, {}>>;
29
- export declare function updateChartRun(boardId: string, chartId: string, input: string | ChartBodyOptions): Promise<import("axios").AxiosResponse<any, any, {}>>;
30
- export declare function moveChartRun(boardId: string, chartId: string, targetBoardId: string): Promise<import("axios").AxiosResponse<any, any, {}>>;
25
+ export declare function queryChartRun(boardId: string, chartId: string, options: QueryChartOptions): Promise<unknown>;
26
+ export declare function createChartRun(boardId: string, input: string | ChartBodyOptions): Promise<unknown>;
27
+ export declare function updateChartRun(boardId: string, chartId: string, input: string | ChartBodyOptions): Promise<unknown>;
28
+ export declare function moveChartRun(boardId: string, chartId: string, targetBoardId: string): Promise<unknown>;
31
29
  export declare function normalizeDuplicateChartResponse(result: unknown): unknown;
32
30
  export declare function duplicateChartRun(boardId: string, chartId: string): Promise<unknown>;
33
- export declare function reorderChartsRun(boardId: string, chartIds: string): Promise<import("axios").AxiosResponse<any, any, {}>>;
34
- export declare function deleteChartRun(boardId: string, chartId: string): Promise<import("axios").AxiosResponse<any, any, {}>>;
31
+ export declare function reorderChartsRun(boardId: string, chartIds: string): Promise<unknown>;
32
+ export declare function deleteChartRun(boardId: string, chartId: string): Promise<unknown>;
@@ -17,6 +17,7 @@ const incur_1 = require("incur");
17
17
  const client_1 = require("../lib/client");
18
18
  const json_1 = require("../lib/json");
19
19
  const sql_1 = require("../lib/sql");
20
+ const pagination_1 = require("../lib/pagination");
20
21
  exports.charts = incur_1.Cli.create('charts', {
21
22
  description: 'Chart commands — create, list, query, move, duplicate, reorder, update, and delete charts within boards',
22
23
  });
@@ -32,14 +33,6 @@ const chartTypeSchema = incur_1.z.enum([
32
33
  'user_paths',
33
34
  'retention',
34
35
  ]);
35
- function buildPaginationParams(options = {}) {
36
- const params = {};
37
- if (options.page !== undefined)
38
- params.page = options.page;
39
- if (options.size !== undefined)
40
- params.size = options.size;
41
- return params;
42
- }
43
36
  function hasTypedChartFields(options) {
44
37
  return [
45
38
  options.query,
@@ -120,15 +113,14 @@ function listChartsRun(boardId, options = {}) {
120
113
  (0, client_1.requireApiKey)();
121
114
  const client = (0, client_1.createClient)();
122
115
  return client.get(`/v0/boards/${encodeURIComponent(boardId)}/charts/`, {
123
- params: buildPaginationParams(options),
116
+ params: (0, pagination_1.buildPaginationParams)(options),
124
117
  });
125
118
  }
126
119
  exports.charts.command('list', {
127
120
  description: 'List all charts for a board, including executed results',
128
121
  options: incur_1.z.object({
129
122
  boardId: incur_1.z.string().describe('Board ID to list charts from'),
130
- page: incur_1.z.coerce.number().optional().describe('Page number (1-indexed, default 1)'),
131
- size: incur_1.z.coerce.number().optional().describe('Page size (default 100, max 200)'),
123
+ ...pagination_1.paginationOptionsSchema,
132
124
  }),
133
125
  examples: [
134
126
  {
@@ -146,15 +138,14 @@ function listChartSummariesRun(boardId, options = {}) {
146
138
  (0, client_1.requireApiKey)();
147
139
  const client = (0, client_1.createClient)();
148
140
  return client.get(`/v0/boards/${encodeURIComponent(boardId)}/charts/meta`, {
149
- params: buildPaginationParams(options),
141
+ params: (0, pagination_1.buildPaginationParams)(options),
150
142
  });
151
143
  }
152
144
  exports.charts.command('meta', {
153
145
  description: 'List lightweight chart metadata for a board without query results',
154
146
  options: incur_1.z.object({
155
147
  boardId: incur_1.z.string().describe('Board ID to list chart metadata from'),
156
- page: incur_1.z.coerce.number().optional().describe('Page number (1-indexed, default 1)'),
157
- size: incur_1.z.coerce.number().optional().describe('Page size (default 100, max 200)'),
148
+ ...pagination_1.paginationOptionsSchema,
158
149
  }),
159
150
  examples: [
160
151
  {
@@ -265,6 +256,11 @@ function updateChartRun(boardId, chartId, input) {
265
256
  (0, client_1.requireApiKey)();
266
257
  const client = (0, client_1.createClient)();
267
258
  const updates = coerceChartBody(input);
259
+ // The charts endpoint only supports full-body PUT, so emulate a partial
260
+ // update by fetching the current chart and merging flags over it. Two
261
+ // consequences: a concurrent edit between the GET and PUT is overwritten,
262
+ // and fields can't be cleared via typed flags (nulls are normalized to
263
+ // undefined below) — use --body with explicit nulls to clear.
268
264
  return client
269
265
  .get(`/v0/boards/${encodeURIComponent(boardId)}/charts/${encodeURIComponent(chartId)}`)
270
266
  .then((current) => {
@@ -1,12 +1,10 @@
1
1
  import { Cli } from 'incur';
2
+ import { type PaginationOptions } from '../lib/pagination';
3
+ export type { PaginationOptions };
2
4
  export declare const contracts: Cli.Cli<{}, undefined, undefined>;
3
- export interface PaginationOptions {
4
- page?: number;
5
- size?: number;
6
- }
7
- export declare function listContractsRun(options?: PaginationOptions): Promise<import("axios").AxiosResponse<any, any, {}>>;
8
- export declare function getContractRun(chain: string, address: string): Promise<import("axios").AxiosResponse<any, any, {}>>;
9
- export declare function getContractRecommendationsRun(): Promise<import("axios").AxiosResponse<any, any, {}>>;
5
+ export declare function listContractsRun(options?: PaginationOptions): Promise<unknown>;
6
+ export declare function getContractRun(chain: string, address: string): Promise<unknown>;
7
+ export declare function getContractRecommendationsRun(): Promise<unknown>;
10
8
  export interface CreateContractOptions {
11
9
  address: string;
12
10
  chain: number;
@@ -17,7 +15,7 @@ export interface CreateContractOptions {
17
15
  includeInPipeline?: boolean;
18
16
  }
19
17
  export declare function buildCreateContractBody(options: CreateContractOptions): Record<string, unknown>;
20
- export declare function createContractRun(options: CreateContractOptions): Promise<import("axios").AxiosResponse<any, any, {}>>;
18
+ export declare function createContractRun(options: CreateContractOptions): Promise<unknown>;
21
19
  export interface UpdateContractOptions {
22
20
  name: string;
23
21
  abi: string;
@@ -26,9 +24,9 @@ export interface UpdateContractOptions {
26
24
  includeInPipeline?: boolean;
27
25
  }
28
26
  export declare function buildUpdateContractBody(chain: string | number, address: string, options: UpdateContractOptions): Record<string, unknown>;
29
- export declare function updateContractRun(chain: string, address: string, options: UpdateContractOptions): Promise<import("axios").AxiosResponse<any, any, {}>>;
30
- export declare function updateContractPipelineRun(chain: string, address: string, includeInPipeline: boolean): Promise<import("axios").AxiosResponse<any, any, {}>>;
27
+ export declare function updateContractRun(chain: string, address: string, options: UpdateContractOptions): Promise<unknown>;
28
+ export declare function updateContractPipelineRun(chain: string, address: string, includeInPipeline: boolean): Promise<unknown>;
31
29
  export declare function buildUpdateContractPipelineBody(includeInPipeline: boolean): {
32
30
  include_in_pipeline: boolean;
33
31
  };
34
- export declare function deleteContractRun(chain: string, address: string): Promise<import("axios").AxiosResponse<any, any, {}>>;
32
+ export declare function deleteContractRun(chain: string, address: string): Promise<unknown>;