@formo/cli 1.2.0 → 1.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
@@ -91,10 +91,12 @@ Fetch a single wallet profile by address or ENS name.
91
91
  | Option | Description |
92
92
  |---|---|
93
93
  | `--expand` | Comma-separated fields: `apps`, `chains`, `tokens`, `labels` |
94
+ | `--timestamp` | ISO-8601 timestamp; return the closest stored wallet-enrichment snapshot |
94
95
 
95
96
  ```bash
96
97
  formo profiles get 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
97
98
  formo profiles get vitalik.eth --expand labels,chains
99
+ formo profiles get vitalik.eth --timestamp 2025-06-21T10:03:00Z
98
100
  ```
99
101
 
100
102
  ### `profiles search`
@@ -105,6 +107,7 @@ Search wallet profiles with filters, sorting, and pagination. Returns a `Paginat
105
107
  |---|---|
106
108
  | `--address` | Filter by wallet address |
107
109
  | `--search` | Free-text search across address and identity fields |
110
+ | `--timestamp` | ISO-8601 timestamp; requires `--address` and returns the closest stored wallet-enrichment snapshot |
108
111
  | `--page` | Page number (1-indexed, default `1`) |
109
112
  | `--size` | Page size (default `100`, max `1000`) |
110
113
  | `--order-by` | `last_onchain`, `first_onchain`, `net_worth_usd`, `updated_at`, `tx_count`, `first_seen`, `last_seen`, `num_sessions`, `revenue`, `volume`, `points` |
@@ -115,6 +118,7 @@ Search wallet profiles with filters, sorting, and pagination. Returns a `Paginat
115
118
 
116
119
  ```bash
117
120
  formo profiles search --size 10
121
+ formo profiles search --address 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 --timestamp 2025-06-21T10:03:00Z
118
122
  formo profiles search --order-by net_worth_usd --order-dir desc --size 5
119
123
  formo profiles search --page 2 --size 20
120
124
  formo profiles search --filters '[{"field":"users.net_worth_usd","op":"gt","value":10000}]' --size 20
@@ -122,6 +126,8 @@ formo profiles search --filters '[{"field":"users.net_worth_usd","op":"gt","valu
122
126
  formo profiles search --filters '[{"field":"chains.balance","op":"gt","value":1000,"chain_id":"1"}]' --size 20
123
127
  ```
124
128
 
129
+ With `--timestamp`, wallet-enrichment fields come from the stored snapshot closest to that instant. Exact ties select the later snapshot. Expanded chains, apps, and tokens come from the selected profiling batch, while project engagement fields, identity overrides, and labels remain current. `profiles search --timestamp` requires `--address`.
130
+
125
131
  ### Lifecycle tuning (advanced)
126
132
 
127
133
  Both `profiles get` and `profiles search` accept optional flags to override the lifecycle stage thresholds used when computing `lifecycle`:
@@ -241,13 +247,6 @@ Toggle an alert between `active` and `inactive`.
241
247
  formo alerts toggle alert_abc123 --status inactive
242
248
  ```
243
249
 
244
- ### `alerts test <alertId>`
245
- Send a test alert delivery with optional sample payloads.
246
-
247
- ```bash
248
- formo alerts test alert_abc123 --sample-event '{"event":"transaction","revenue":250}'
249
- ```
250
-
251
250
  ---
252
251
 
253
252
  ## `formo boards`
@@ -290,7 +289,7 @@ Delete a board.
290
289
  Chart commands. Charts live inside a board. Requires `boards:read` / `boards:write`.
291
290
 
292
291
  ### `charts list --board-id <boardId>`
293
- List all charts in a board.
292
+ List charts in a board. Returns lightweight summaries by default; pass `--results` to execute each chart’s query and include full results.
294
293
 
295
294
  ### `charts get <chartId> --board-id <boardId>`
296
295
  Get a single chart by ID.
@@ -342,8 +341,6 @@ List all tracked contracts. Returns `{ data: Contract[], deploy: { last_deployed
342
341
  ### `contracts get <chain> <address>`
343
342
  Get a single tracked contract.
344
343
 
345
- ### `contracts recommendations`
346
- List contracts the project already interacts with but has not added yet.
347
344
 
348
345
  ### `contracts create`
349
346
 
@@ -373,8 +370,6 @@ formo contracts create --address 0x1f9840a85d5af5bf1d1762f925bdaddc4201f984 --ch
373
370
  | `--start-block` | Optional start block |
374
371
  | `--include-in-pipeline` | Include or exclude this contract from the Goldsky events pipeline |
375
372
 
376
- ### `contracts pipeline <chain> <address> --include-in-pipeline <true|false>`
377
- Toggle pipeline inclusion without re-sending the full ABI/events payload.
378
373
 
379
374
  ### `contracts delete <chain> <address>`
380
375
  Remove a tracked contract.
@@ -1,7 +1,7 @@
1
1
  import { Cli } from 'incur';
2
2
  import { type PaginationOptions } from '../lib/pagination';
3
3
  export type { PaginationOptions };
4
- export declare const alerts: Cli.Cli<{}, undefined, undefined>;
4
+ export declare const alerts: Cli.Cli<{}, undefined, undefined, undefined>;
5
5
  export declare function listAlertsRun(options?: PaginationOptions): Promise<unknown>;
6
6
  export declare function getAlertRun(alertId: string): Promise<unknown>;
7
7
  export interface AlertBodyOptions {
@@ -19,10 +19,3 @@ export declare function createAlertRun(options: CreateAlertOptions): Promise<unk
19
19
  export declare function updateAlertRun(alertId: string, options: AlertBodyOptions): Promise<unknown>;
20
20
  export declare function deleteAlertRun(alertId: string): Promise<unknown>;
21
21
  export declare function toggleAlertRun(alertId: string, status: string): Promise<unknown>;
22
- export interface TestAlertOptions {
23
- sampleEvent?: string;
24
- sampleUser?: string;
25
- recipientOverrides?: string;
26
- }
27
- export declare function buildTestAlertBody(options: TestAlertOptions): Record<string, unknown> | undefined;
28
- export declare function testAlertRun(alertId: string, options?: TestAlertOptions): Promise<unknown>;
@@ -8,8 +8,6 @@ exports.createAlertRun = createAlertRun;
8
8
  exports.updateAlertRun = updateAlertRun;
9
9
  exports.deleteAlertRun = deleteAlertRun;
10
10
  exports.toggleAlertRun = toggleAlertRun;
11
- exports.buildTestAlertBody = buildTestAlertBody;
12
- exports.testAlertRun = testAlertRun;
13
11
  const incur_1 = require("incur");
14
12
  const client_1 = require("../lib/client");
15
13
  const filters_1 = require("../lib/filters");
@@ -201,54 +199,3 @@ exports.alerts.command('toggle', {
201
199
  return toggleAlertRun(args.alertId, options.status);
202
200
  },
203
201
  });
204
- function buildTestAlertBody(options) {
205
- const body = {};
206
- if (options.sampleEvent !== undefined) {
207
- body.sampleEvent = (0, json_1.parseJsonObject)(options.sampleEvent, '--sample-event');
208
- }
209
- if (options.sampleUser !== undefined) {
210
- body.sampleUser = (0, json_1.parseJsonObject)(options.sampleUser, '--sample-user');
211
- }
212
- if (options.recipientOverrides !== undefined) {
213
- body.recipientOverrides = (0, json_1.parseJsonArray)(options.recipientOverrides, '--recipient-overrides');
214
- }
215
- return Object.keys(body).length > 0 ? body : undefined;
216
- }
217
- function testAlertRun(alertId, options = {}) {
218
- (0, client_1.requireApiKey)();
219
- const client = (0, client_1.createClient)();
220
- return client.post(`/v0/alerts/${encodeURIComponent(alertId)}/test`, buildTestAlertBody(options));
221
- }
222
- exports.alerts.command('test', {
223
- description: 'Send a test delivery for an alert',
224
- args: incur_1.z.object({
225
- alertId: incur_1.z.string().describe('Alert ID to test'),
226
- }),
227
- options: incur_1.z.object({
228
- sampleEvent: incur_1.z
229
- .string()
230
- .optional()
231
- .describe('Optional JSON object to use as the sample event'),
232
- sampleUser: incur_1.z
233
- .string()
234
- .optional()
235
- .describe('Optional JSON object to use as the sample user/profile'),
236
- recipientOverrides: incur_1.z
237
- .string()
238
- .optional()
239
- .describe('Optional JSON array of recipient objects to test instead of saved recipients'),
240
- }),
241
- examples: [
242
- {
243
- args: { alertId: 'alert_abc123' },
244
- options: {
245
- sampleEvent: '{"event":"transaction","revenue":250}',
246
- },
247
- description: 'Send a test alert with a sample event',
248
- },
249
- ],
250
- hint: 'Requires alerts:write scope on your API key.',
251
- run({ args, options }) {
252
- return testAlertRun(args.alertId, options);
253
- },
254
- });
@@ -1,5 +1,5 @@
1
1
  import { Cli } from 'incur';
2
- export declare const analytics: Cli.Cli<{}, undefined, undefined>;
2
+ export declare const analytics: Cli.Cli<{}, undefined, undefined, undefined>;
3
3
  export interface AnalyticsOptions {
4
4
  dateFrom?: string;
5
5
  dateTo?: string;
@@ -22,10 +22,10 @@ const PIPES = [
22
22
  { name: 'frequency', description: 'Engagement frequency distribution' },
23
23
  { name: 'lifecycle', description: 'User lifecycle stages (new, returning, power, resurrected, churned)' },
24
24
  { name: 'retention', description: 'Retention cohort analysis (params: id_type, event_type, event_name, min_users)' },
25
- { name: 'revenue_overview', description: 'Revenue overview with optional breakdown (params: group_by, rank_by)' },
26
- { name: 'revenue_by_metric', description: 'Revenue ranked by a metric column (params: metric_column, limit, offset)' },
25
+ { name: 'revenue_overview', description: 'Revenue overview with optional breakdown (params: group_by — incl. channel_type and paid_source for ad network, rank_by)' },
26
+ { name: 'revenue_by_metric', description: 'Revenue ranked by a metric column (params: metric_column — incl. channel and paid_source for ad network, limit, offset)' },
27
27
  { name: 'revenue_timeseries', description: 'Revenue over time (params: address)' },
28
- { name: 'volume_by_metric', description: 'Trading volume ranked by a metric column (params: metric_column, limit, offset)' },
28
+ { name: 'volume_by_metric', description: 'Trading volume ranked by a metric column (params: metric_column — incl. channel and paid_source for ad network, limit, offset)' },
29
29
  { name: 'top_chains', description: 'Top chains by activity (params: limit, offset)' },
30
30
  { name: 'top_events', description: 'Top events by count (params: limit, offset, type)' },
31
31
  { name: 'top_locations', description: 'Top locations (params: limit, offset)' },
@@ -1,7 +1,7 @@
1
1
  import { Cli } from 'incur';
2
2
  import { type PaginationOptions } from '../lib/pagination';
3
3
  export type { PaginationOptions };
4
- export declare const boards: Cli.Cli<{}, undefined, undefined>;
4
+ export declare const boards: Cli.Cli<{}, undefined, undefined, undefined>;
5
5
  export declare function listBoardsRun(options?: PaginationOptions): Promise<unknown>;
6
6
  export declare function getBoardRun(boardId: string): Promise<unknown>;
7
7
  export interface CreateBoardOptions {
@@ -1,7 +1,7 @@
1
1
  import { Cli } from 'incur';
2
2
  import { type PaginationOptions } from '../lib/pagination';
3
3
  export type { PaginationOptions };
4
- export declare const charts: Cli.Cli<{}, undefined, undefined>;
4
+ export declare const charts: Cli.Cli<{}, undefined, undefined, undefined>;
5
5
  export interface ChartBodyOptions {
6
6
  body?: string;
7
7
  query?: string;
@@ -15,7 +15,7 @@ export interface ChartBodyOptions {
15
15
  settings?: string;
16
16
  }
17
17
  export declare function buildChartBody(options: ChartBodyOptions): Record<string, unknown>;
18
- export declare function listChartsRun(boardId: string, options?: PaginationOptions): Promise<unknown>;
18
+ export declare function listChartsRun(boardId: string, options?: PaginationOptions, results?: boolean): Promise<unknown>;
19
19
  export declare function listChartSummariesRun(boardId: string, options?: PaginationOptions): Promise<unknown>;
20
20
  export declare function getChartRun(boardId: string, chartId: string): Promise<unknown>;
21
21
  export interface QueryChartOptions {
@@ -109,35 +109,48 @@ const chartBodyOptions = incur_1.z.object({
109
109
  .describe('JSON object of type-specific chart settings (for example, user_paths anchors/maxSteps/nodesPerStep or retention entryFilter/retentionFilter)'),
110
110
  });
111
111
  // ── List charts for a board ──
112
- function listChartsRun(boardId, options = {}) {
112
+ function listChartsRun(boardId, options = {}, results = false) {
113
113
  (0, client_1.requireApiKey)();
114
114
  const client = (0, client_1.createClient)();
115
115
  return client.get(`/v0/boards/${encodeURIComponent(boardId)}/charts/`, {
116
- params: (0, pagination_1.buildPaginationParams)(options),
116
+ params: {
117
+ ...(0, pagination_1.buildPaginationParams)(options),
118
+ // The public API returns lightweight summaries unless asked to execute.
119
+ ...(results ? { include: 'results' } : {}),
120
+ },
117
121
  });
118
122
  }
119
123
  exports.charts.command('list', {
120
- description: 'List all charts for a board, including executed results',
124
+ description: 'List charts for a board (summaries by default; --results executes each chart query)',
121
125
  options: incur_1.z.object({
122
126
  boardId: incur_1.z.string().describe('Board ID to list charts from'),
127
+ results: incur_1.z
128
+ .boolean()
129
+ .optional()
130
+ .describe('Execute each chart query and include results (slower)'),
123
131
  ...pagination_1.paginationOptionsSchema,
124
132
  }),
125
133
  examples: [
126
134
  {
127
135
  options: { boardId: 'board_abc123' },
128
- description: 'List all charts in a board',
136
+ description: 'List chart summaries for a board',
137
+ },
138
+ {
139
+ options: { boardId: 'board_abc123', results: true },
140
+ description: 'List charts with executed query results',
129
141
  },
130
142
  ],
131
143
  hint: 'Requires boards:read scope on your API key.',
132
144
  run({ options }) {
133
- return listChartsRun(options.boardId, options);
145
+ return listChartsRun(options.boardId, options, options.results ?? false);
134
146
  },
135
147
  });
136
148
  // ── List chart metadata for a board ──
137
149
  function listChartSummariesRun(boardId, options = {}) {
138
150
  (0, client_1.requireApiKey)();
139
151
  const client = (0, client_1.createClient)();
140
- return client.get(`/v0/boards/${encodeURIComponent(boardId)}/charts/meta`, {
152
+ // Summaries are the list endpoint's default; /charts/meta is internal-only.
153
+ return client.get(`/v0/boards/${encodeURIComponent(boardId)}/charts/`, {
141
154
  params: (0, pagination_1.buildPaginationParams)(options),
142
155
  });
143
156
  }
@@ -1,10 +1,9 @@
1
1
  import { Cli } from 'incur';
2
2
  import { type PaginationOptions } from '../lib/pagination';
3
3
  export type { PaginationOptions };
4
- export declare const contracts: Cli.Cli<{}, undefined, undefined>;
4
+ export declare const contracts: Cli.Cli<{}, undefined, undefined, undefined>;
5
5
  export declare function listContractsRun(options?: PaginationOptions): Promise<unknown>;
6
6
  export declare function getContractRun(chain: string, address: string): Promise<unknown>;
7
- export declare function getContractRecommendationsRun(): Promise<unknown>;
8
7
  export interface CreateContractOptions {
9
8
  address: string;
10
9
  chain: number;
@@ -25,8 +24,4 @@ export interface UpdateContractOptions {
25
24
  }
26
25
  export declare function buildUpdateContractBody(chain: string | number, address: string, options: UpdateContractOptions): Record<string, unknown>;
27
26
  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>;
29
- export declare function buildUpdateContractPipelineBody(includeInPipeline: boolean): {
30
- include_in_pipeline: boolean;
31
- };
32
27
  export declare function deleteContractRun(chain: string, address: string): Promise<unknown>;
@@ -3,20 +3,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.contracts = void 0;
4
4
  exports.listContractsRun = listContractsRun;
5
5
  exports.getContractRun = getContractRun;
6
- exports.getContractRecommendationsRun = getContractRecommendationsRun;
7
6
  exports.buildCreateContractBody = buildCreateContractBody;
8
7
  exports.createContractRun = createContractRun;
9
8
  exports.buildUpdateContractBody = buildUpdateContractBody;
10
9
  exports.updateContractRun = updateContractRun;
11
- exports.updateContractPipelineRun = updateContractPipelineRun;
12
- exports.buildUpdateContractPipelineBody = buildUpdateContractPipelineBody;
13
10
  exports.deleteContractRun = deleteContractRun;
14
11
  const incur_1 = require("incur");
15
12
  const client_1 = require("../lib/client");
16
13
  const json_1 = require("../lib/json");
17
14
  const pagination_1 = require("../lib/pagination");
18
15
  exports.contracts = incur_1.Cli.create('contracts', {
19
- description: 'Smart contract commands — register, list, recommend, update, toggle pipeline inclusion, and remove tracked contracts',
16
+ description: 'Smart contract commands — register, list, update, and remove tracked contracts',
20
17
  });
21
18
  function parseChain(chain) {
22
19
  const value = typeof chain === 'number' ? chain : Number(chain);
@@ -48,25 +45,6 @@ function getContractRun(chain, address) {
48
45
  const client = (0, client_1.createClient)();
49
46
  return client.get(`/v0/contracts/${parseChain(chain)}/${encodeURIComponent(address)}`);
50
47
  }
51
- // ── Recommended contracts ──
52
- function getContractRecommendationsRun() {
53
- (0, client_1.requireApiKey)();
54
- const client = (0, client_1.createClient)();
55
- return client.get('/v0/contracts/recommendations');
56
- }
57
- exports.contracts.command('recommendations', {
58
- description: 'List contracts the project already interacts with but has not added yet',
59
- options: incur_1.z.object({}),
60
- examples: [
61
- {
62
- description: 'Show recommended contracts to add for decoding/monitoring',
63
- },
64
- ],
65
- hint: 'Requires contracts:read scope on your API key.',
66
- run() {
67
- return getContractRecommendationsRun();
68
- },
69
- });
70
48
  exports.contracts.command('get', {
71
49
  description: 'Get a tracked contract by chain and address',
72
50
  args: incur_1.z.object({
@@ -197,41 +175,6 @@ exports.contracts.command('update', {
197
175
  return updateContractRun(args.chain, args.address, options);
198
176
  },
199
177
  });
200
- // ── Toggle contract pipeline inclusion ──
201
- function updateContractPipelineRun(chain, address, includeInPipeline) {
202
- (0, client_1.requireApiKey)();
203
- const client = (0, client_1.createClient)();
204
- return client.patch(`/v0/contracts/${parseChain(chain)}/${encodeURIComponent(address)}/pipeline`, buildUpdateContractPipelineBody(includeInPipeline));
205
- }
206
- function buildUpdateContractPipelineBody(includeInPipeline) {
207
- return { include_in_pipeline: includeInPipeline };
208
- }
209
- exports.contracts.command('pipeline', {
210
- description: 'Toggle whether a tracked contract is included in the project events pipeline',
211
- args: incur_1.z.object({
212
- chain: incur_1.z.string().describe('Chain ID'),
213
- address: incur_1.z.string().describe('Contract address (0x...)'),
214
- }),
215
- options: incur_1.z.object({
216
- includeInPipeline: incur_1.z
217
- .boolean()
218
- .describe('true to include the contract in the pipeline, false to exclude it'),
219
- }),
220
- examples: [
221
- {
222
- args: {
223
- chain: '1',
224
- address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
225
- },
226
- options: { includeInPipeline: false },
227
- description: 'Keep ABI decoding but exclude this contract from pipeline deploys',
228
- },
229
- ],
230
- hint: 'Requires contracts:write scope on your API key.',
231
- run({ args, options }) {
232
- return updateContractPipelineRun(args.chain, args.address, options.includeInPipeline);
233
- },
234
- });
235
178
  // ── Delete a contract ──
236
179
  function deleteContractRun(chain, address) {
237
180
  (0, client_1.requireApiKey)();
@@ -1,5 +1,5 @@
1
1
  import { Cli } from 'incur';
2
- export declare const events: Cli.Cli<{}, undefined, undefined>;
2
+ export declare const events: Cli.Cli<{}, undefined, undefined, undefined>;
3
3
  export interface IngestEventsOptions {
4
4
  events?: string;
5
5
  event?: string;
@@ -1,5 +1,5 @@
1
1
  import { Cli } from 'incur';
2
- export declare const importCmd: Cli.Cli<{}, undefined, undefined>;
2
+ export declare const importCmd: Cli.Cli<{}, undefined, undefined, undefined>;
3
3
  export interface ImportWalletsOptions {
4
4
  addresses?: string;
5
5
  rows?: string;
@@ -1,5 +1,5 @@
1
1
  import { Cli } from 'incur';
2
- export declare const profiles: Cli.Cli<{}, undefined, undefined>;
2
+ export declare const profiles: Cli.Cli<{}, undefined, undefined, undefined>;
3
3
  export interface LifecycleThresholdOptions {
4
4
  newWindowDays?: number;
5
5
  churnWindowDays?: number;
@@ -11,11 +11,13 @@ export interface LifecycleThresholdOptions {
11
11
  }
12
12
  export interface GetProfileOptions extends LifecycleThresholdOptions {
13
13
  expand?: string;
14
+ timestamp?: string;
14
15
  }
15
16
  export declare function getProfileRun(address: string, optionsOrExpand?: GetProfileOptions | string): Promise<unknown>;
16
17
  export interface SearchProfilesOptions extends LifecycleThresholdOptions {
17
18
  address?: string;
18
19
  search?: string;
20
+ timestamp?: string;
19
21
  page?: number;
20
22
  size?: number;
21
23
  orderBy?: string;
@@ -42,8 +44,8 @@ export interface BatchUpdateProfilesOptions {
42
44
  }
43
45
  export declare function buildBatchUpdateProfilesBody(options: BatchUpdateProfilesOptions): Record<string, unknown>[];
44
46
  export declare function batchUpdateProfilesRun(options: BatchUpdateProfilesOptions): Promise<unknown>;
45
- export declare const profilesProperties: Cli.Cli<{}, undefined, undefined>;
46
- export declare const profilesLabels: Cli.Cli<{}, undefined, undefined>;
47
+ export declare const profilesProperties: Cli.Cli<{}, undefined, undefined, undefined>;
48
+ export declare const profilesLabels: Cli.Cli<{}, undefined, undefined, undefined>;
47
49
  export interface CreateProfileLabelOptions {
48
50
  tagId?: string;
49
51
  value?: string;
@@ -75,6 +75,10 @@ const lifecycleThresholdOptions = {
75
75
  .optional()
76
76
  .describe('Override lifecycle at-risk prior active days threshold'),
77
77
  };
78
+ const profileTimestampOption = incur_1.z
79
+ .string()
80
+ .datetime({ offset: true })
81
+ .optional();
78
82
  function getProfileRun(address, optionsOrExpand = {}) {
79
83
  (0, client_1.requireApiKey)();
80
84
  const client = (0, client_1.createClient)();
@@ -84,6 +88,8 @@ function getProfileRun(address, optionsOrExpand = {}) {
84
88
  const params = {};
85
89
  if (options.expand)
86
90
  params.expand = options.expand;
91
+ if (options.timestamp)
92
+ params.timestamp = options.timestamp;
87
93
  addLifecycleThresholdParams(params, options);
88
94
  return client.get(`/v0/profiles/${encodeURIComponent(address)}`, { params });
89
95
  }
@@ -97,6 +103,7 @@ exports.profiles.command('get', {
97
103
  .string()
98
104
  .optional()
99
105
  .describe('Comma-separated list of fields to expand: apps,chains,tokens,labels'),
106
+ timestamp: profileTimestampOption.describe('Return the wallet-enrichment snapshot closest to this ISO-8601 timestamp'),
100
107
  ...lifecycleThresholdOptions,
101
108
  }),
102
109
  examples: [
@@ -106,6 +113,11 @@ exports.profiles.command('get', {
106
113
  options: { expand: 'labels,chains' },
107
114
  description: 'Get profile with expanded labels and chains',
108
115
  },
116
+ {
117
+ args: { address: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045' },
118
+ options: { timestamp: '2025-06-21T10:03:00Z' },
119
+ description: 'Get the closest stored wallet-enrichment snapshot',
120
+ },
109
121
  ],
110
122
  hint: 'Requires profiles:read scope on your API key.',
111
123
  run({ args, options }) {
@@ -275,6 +287,9 @@ function parseSearchFilters(raw) {
275
287
  return parsed;
276
288
  }
277
289
  function searchProfilesRun(options) {
290
+ if (options.timestamp && !options.address) {
291
+ throw new Error('--timestamp requires --address');
292
+ }
278
293
  (0, client_1.requireApiKey)();
279
294
  const client = (0, client_1.createClient)();
280
295
  const params = {};
@@ -282,6 +297,8 @@ function searchProfilesRun(options) {
282
297
  params.address = options.address;
283
298
  if (options.search)
284
299
  params.search = options.search;
300
+ if (options.timestamp)
301
+ params.timestamp = options.timestamp;
285
302
  if (options.page !== undefined)
286
303
  params.page = options.page;
287
304
  if (options.size !== undefined)
@@ -313,6 +330,7 @@ exports.profiles.command('search', {
313
330
  options: incur_1.z.object({
314
331
  address: incur_1.z.string().optional().describe('Filter by wallet address'),
315
332
  search: incur_1.z.string().optional().describe('Free-text search across address and identity fields'),
333
+ timestamp: profileTimestampOption.describe('Return the closest wallet-enrichment snapshot; requires --address'),
316
334
  page: incur_1.z.coerce
317
335
  .number()
318
336
  .int()
@@ -373,6 +391,13 @@ exports.profiles.command('search', {
373
391
  }),
374
392
  examples: [
375
393
  { options: { size: 10 }, description: 'List first 10 profiles' },
394
+ {
395
+ options: {
396
+ address: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
397
+ timestamp: '2025-06-21T10:03:00Z',
398
+ },
399
+ description: 'Search the closest stored wallet-enrichment snapshot',
400
+ },
376
401
  {
377
402
  options: { orderBy: 'net_worth_usd', orderDir: 'desc', size: 5 },
378
403
  description: 'Top 5 profiles by net worth',
@@ -1,3 +1,3 @@
1
1
  import { Cli } from 'incur';
2
- export declare const query: Cli.Cli<{}, undefined, undefined>;
2
+ export declare const query: Cli.Cli<{}, undefined, undefined, undefined>;
3
3
  export declare function queryRunRun(sql: string): Promise<unknown>;
@@ -1,7 +1,7 @@
1
1
  import { Cli } from 'incur';
2
2
  import { type PaginationOptions } from '../lib/pagination';
3
3
  export type { PaginationOptions };
4
- export declare const segments: Cli.Cli<{}, undefined, undefined>;
4
+ export declare const segments: Cli.Cli<{}, undefined, undefined, undefined>;
5
5
  export declare function listSegmentsRun(options?: PaginationOptions): Promise<unknown>;
6
6
  export interface CreateSegmentOptions {
7
7
  title: string;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
2
  import { Cli } from "incur";
3
- declare const cli: Cli.Cli<{}, undefined, undefined>;
3
+ declare const cli: Cli.Cli<{}, undefined, undefined, undefined>;
4
4
  export default cli;
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@formo/cli",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "packageManager": "pnpm@11.1.2",
5
5
  "engines": {
6
6
  "node": ">=22.12"
7
7
  },
8
- "description": "Formo API CLI — query profiles and analytics data",
8
+ "description": "Formo API CLI \u2014 query profiles and analytics data",
9
9
  "license": "MIT",
10
10
  "repository": {
11
11
  "type": "git",
@@ -23,6 +23,10 @@
23
23
  "dist",
24
24
  "README.md"
25
25
  ],
26
+ "publishConfig": {
27
+ "access": "public",
28
+ "provenance": true
29
+ },
26
30
  "scripts": {
27
31
  "build": "tsc",
28
32
  "prepublishOnly": "pnpm build",
@@ -36,7 +40,7 @@
36
40
  },
37
41
  "dependencies": {
38
42
  "axios": "^1.18.0",
39
- "incur": "0.3.25"
43
+ "incur": "0.4.26"
40
44
  },
41
45
  "devDependencies": {
42
46
  "@eslint/js": "^10.0.1",