@formo/cli 0.1.0 → 0.2.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 +285 -41
- package/dist/commands/alerts.d.ts +1 -0
- package/dist/commands/alerts.js +10 -29
- package/dist/commands/contracts.d.ts +12 -0
- package/dist/commands/contracts.js +18 -10
- package/dist/commands/import.d.ts +4 -0
- package/dist/commands/import.js +9 -5
- package/dist/commands/profiles.d.ts +22 -2
- package/dist/commands/profiles.js +181 -11
- package/dist/commands/query.d.ts +2 -0
- package/dist/commands/query.js +25 -0
- package/dist/commands/segments.d.ts +4 -0
- package/dist/commands/segments.js +9 -5
- package/dist/index.d.ts +1 -1
- package/dist/index.js +112 -132
- package/dist/lib/client.d.ts +26 -0
- package/dist/lib/client.js +30 -7
- package/package.json +10 -1
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.profiles = void 0;
|
|
3
|
+
exports.profilesLabels = exports.profiles = void 0;
|
|
4
4
|
exports.getProfileRun = getProfileRun;
|
|
5
5
|
exports.searchProfilesRun = searchProfilesRun;
|
|
6
|
+
exports.buildUpdateProfileBody = buildUpdateProfileBody;
|
|
7
|
+
exports.updateProfileRun = updateProfileRun;
|
|
8
|
+
exports.buildCreateLabelBody = buildCreateLabelBody;
|
|
9
|
+
exports.createProfileLabelRun = createProfileLabelRun;
|
|
10
|
+
exports.buildDeleteLabelBody = buildDeleteLabelBody;
|
|
11
|
+
exports.deleteProfileLabelRun = deleteProfileLabelRun;
|
|
6
12
|
const incur_1 = require("incur");
|
|
7
13
|
const client_1 = require("../lib/client");
|
|
8
14
|
exports.profiles = incur_1.Cli.create('profiles', {
|
|
@@ -45,10 +51,10 @@ function searchProfilesRun(options) {
|
|
|
45
51
|
const params = {};
|
|
46
52
|
if (options.address)
|
|
47
53
|
params.address = options.address;
|
|
48
|
-
if (options.
|
|
49
|
-
params.
|
|
50
|
-
if (options.
|
|
51
|
-
params.
|
|
54
|
+
if (options.page !== undefined)
|
|
55
|
+
params.page = options.page;
|
|
56
|
+
if (options.size !== undefined)
|
|
57
|
+
params.size = options.size;
|
|
52
58
|
if (options.orderBy)
|
|
53
59
|
params.order_by = options.orderBy;
|
|
54
60
|
if (options.orderDir)
|
|
@@ -73,8 +79,8 @@ exports.profiles.command('search', {
|
|
|
73
79
|
description: 'Search wallet profiles with optional filters',
|
|
74
80
|
options: incur_1.z.object({
|
|
75
81
|
address: incur_1.z.string().optional().describe('Filter by wallet address'),
|
|
76
|
-
|
|
77
|
-
|
|
82
|
+
page: incur_1.z.coerce.number().optional().describe('Page number (1-indexed, default 1)'),
|
|
83
|
+
size: incur_1.z.coerce.number().optional().describe('Page size (default 100, max 1000)'),
|
|
78
84
|
orderBy: incur_1.z
|
|
79
85
|
.enum([
|
|
80
86
|
'last_onchain',
|
|
@@ -103,15 +109,19 @@ exports.profiles.command('search', {
|
|
|
103
109
|
.describe('Logic operator for combining conditions: "and" (default) or "or"'),
|
|
104
110
|
}),
|
|
105
111
|
examples: [
|
|
106
|
-
{ options: {
|
|
112
|
+
{ options: { size: 10 }, description: 'List first 10 profiles' },
|
|
107
113
|
{
|
|
108
|
-
options: { orderBy: 'net_worth_usd', orderDir: 'desc',
|
|
114
|
+
options: { orderBy: 'net_worth_usd', orderDir: 'desc', size: 5 },
|
|
109
115
|
description: 'Top 5 profiles by net worth',
|
|
110
116
|
},
|
|
117
|
+
{
|
|
118
|
+
options: { page: 2, size: 20 },
|
|
119
|
+
description: 'Get the second page of 20 profiles',
|
|
120
|
+
},
|
|
111
121
|
{
|
|
112
122
|
options: {
|
|
113
123
|
conditions: '[{"field":"net_worth_usd","op":"gt","value":10000}]',
|
|
114
|
-
|
|
124
|
+
size: 20,
|
|
115
125
|
},
|
|
116
126
|
description: 'Search profiles with net worth > 10000',
|
|
117
127
|
},
|
|
@@ -119,7 +129,7 @@ exports.profiles.command('search', {
|
|
|
119
129
|
options: {
|
|
120
130
|
conditions: '[{"field":"net_worth_usd","op":"gt","value":10000},{"field":"tx_count","op":"gt","value":50}]',
|
|
121
131
|
logic: 'or',
|
|
122
|
-
|
|
132
|
+
size: 20,
|
|
123
133
|
},
|
|
124
134
|
description: 'Search profiles matching either condition',
|
|
125
135
|
},
|
|
@@ -128,3 +138,163 @@ exports.profiles.command('search', {
|
|
|
128
138
|
return searchProfilesRun(options);
|
|
129
139
|
},
|
|
130
140
|
});
|
|
141
|
+
function buildUpdateProfileBody(options) {
|
|
142
|
+
let body;
|
|
143
|
+
try {
|
|
144
|
+
body = JSON.parse(options.properties);
|
|
145
|
+
if (!body || typeof body !== 'object' || Array.isArray(body))
|
|
146
|
+
throw new Error('not an object');
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
throw new Error('--properties must be a JSON object of property keys');
|
|
150
|
+
}
|
|
151
|
+
if (Object.keys(body).length === 0) {
|
|
152
|
+
throw new Error('--properties must contain at least one key');
|
|
153
|
+
}
|
|
154
|
+
return body;
|
|
155
|
+
}
|
|
156
|
+
function updateProfileRun(address, options) {
|
|
157
|
+
(0, client_1.requireApiKey)();
|
|
158
|
+
const client = (0, client_1.createClient)();
|
|
159
|
+
return client.put(`/v0/profiles/${encodeURIComponent(address)}/properties`, buildUpdateProfileBody(options));
|
|
160
|
+
}
|
|
161
|
+
exports.profiles.command('update', {
|
|
162
|
+
description: 'Merge-update identity properties on a wallet profile',
|
|
163
|
+
args: incur_1.z.object({
|
|
164
|
+
address: incur_1.z.string().describe('Wallet address (0x... or ENS name)'),
|
|
165
|
+
}),
|
|
166
|
+
options: incur_1.z.object({
|
|
167
|
+
properties: incur_1.z
|
|
168
|
+
.string()
|
|
169
|
+
.describe('JSON object of properties to merge. Allowed keys: user_id, display_name, email, farcaster, discord, twitter, telegram, instagram, website, github, linkedin, facebook, tiktok, youtube, reddit, avatar, description, location, ens, lens, basenames, linea'),
|
|
170
|
+
}),
|
|
171
|
+
examples: [
|
|
172
|
+
{
|
|
173
|
+
args: { address: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045' },
|
|
174
|
+
options: {
|
|
175
|
+
properties: '{"display_name":"Vitalik","twitter":"VitalikButerin"}',
|
|
176
|
+
},
|
|
177
|
+
description: 'Set display name and Twitter handle',
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
args: { address: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045' },
|
|
181
|
+
options: { properties: '{"email":"alice@example.com"}' },
|
|
182
|
+
description: 'Set just the email',
|
|
183
|
+
},
|
|
184
|
+
],
|
|
185
|
+
hint: 'Requires profiles:write scope on your API key. Only the listed keys are accepted; unknown keys are rejected.',
|
|
186
|
+
run({ args, options }) {
|
|
187
|
+
return updateProfileRun(args.address, options);
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
// ── Labels sub-resource ──
|
|
191
|
+
exports.profilesLabels = incur_1.Cli.create('labels', {
|
|
192
|
+
description: 'Manage labels on a wallet profile',
|
|
193
|
+
});
|
|
194
|
+
function buildCreateLabelBody(options) {
|
|
195
|
+
if (options.labels) {
|
|
196
|
+
try {
|
|
197
|
+
const parsed = JSON.parse(options.labels);
|
|
198
|
+
if (!Array.isArray(parsed) || parsed.length === 0)
|
|
199
|
+
throw new Error('not a non-empty array');
|
|
200
|
+
return parsed;
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
throw new Error('--labels must be a non-empty JSON array of UserLabelInput objects');
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (options.tagId) {
|
|
207
|
+
const single = { tag_id: options.tagId };
|
|
208
|
+
if (options.value)
|
|
209
|
+
single.value = options.value;
|
|
210
|
+
if (options.chainId)
|
|
211
|
+
single.chain_id = options.chainId;
|
|
212
|
+
return single;
|
|
213
|
+
}
|
|
214
|
+
throw new Error('Provide --tagId (single label) or --labels (batch JSON array)');
|
|
215
|
+
}
|
|
216
|
+
function createProfileLabelRun(address, options) {
|
|
217
|
+
(0, client_1.requireApiKey)();
|
|
218
|
+
const client = (0, client_1.createClient)();
|
|
219
|
+
return client.post(`/v0/profiles/${encodeURIComponent(address)}/labels`, buildCreateLabelBody(options));
|
|
220
|
+
}
|
|
221
|
+
exports.profilesLabels.command('create', {
|
|
222
|
+
description: 'Upsert one or more labels on a wallet profile',
|
|
223
|
+
args: incur_1.z.object({
|
|
224
|
+
address: incur_1.z.string().describe('Wallet address (0x... or ENS name)'),
|
|
225
|
+
}),
|
|
226
|
+
options: incur_1.z.object({
|
|
227
|
+
tagId: incur_1.z
|
|
228
|
+
.string()
|
|
229
|
+
.optional()
|
|
230
|
+
.describe('Label identifier (e.g. "vip", "airdrop_eligible")'),
|
|
231
|
+
value: incur_1.z.string().optional().describe('Optional label value (e.g. tier name, country code)'),
|
|
232
|
+
chainId: incur_1.z.string().optional().describe('Optional chain identifier the label applies to'),
|
|
233
|
+
labels: incur_1.z
|
|
234
|
+
.string()
|
|
235
|
+
.optional()
|
|
236
|
+
.describe('JSON array of UserLabelInput objects for batch upsert'),
|
|
237
|
+
}),
|
|
238
|
+
examples: [
|
|
239
|
+
{
|
|
240
|
+
args: { address: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045' },
|
|
241
|
+
options: { tagId: 'vip' },
|
|
242
|
+
description: 'Tag a wallet as VIP',
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
args: { address: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045' },
|
|
246
|
+
options: { tagId: 'tier', value: 'gold', chainId: '1' },
|
|
247
|
+
description: 'Apply a tiered label scoped to a chain',
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
args: { address: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045' },
|
|
251
|
+
options: { labels: '[{"tag_id":"vip"},{"tag_id":"airdrop_eligible","chain_id":"1"}]' },
|
|
252
|
+
description: 'Apply multiple labels in one call',
|
|
253
|
+
},
|
|
254
|
+
],
|
|
255
|
+
hint: 'Requires profiles:write scope on your API key.',
|
|
256
|
+
run({ args, options }) {
|
|
257
|
+
return createProfileLabelRun(args.address, options);
|
|
258
|
+
},
|
|
259
|
+
});
|
|
260
|
+
function buildDeleteLabelBody(options) {
|
|
261
|
+
if (!options.tagId) {
|
|
262
|
+
throw new Error('--tagId is required');
|
|
263
|
+
}
|
|
264
|
+
const body = { tag_id: options.tagId };
|
|
265
|
+
if (options.chainId)
|
|
266
|
+
body.chain_id = options.chainId;
|
|
267
|
+
return body;
|
|
268
|
+
}
|
|
269
|
+
function deleteProfileLabelRun(address, options) {
|
|
270
|
+
(0, client_1.requireApiKey)();
|
|
271
|
+
const client = (0, client_1.createClient)();
|
|
272
|
+
return client.delete(`/v0/profiles/${encodeURIComponent(address)}/labels`, { data: buildDeleteLabelBody(options) });
|
|
273
|
+
}
|
|
274
|
+
exports.profilesLabels.command('delete', {
|
|
275
|
+
description: 'Delete a label from a wallet profile',
|
|
276
|
+
args: incur_1.z.object({
|
|
277
|
+
address: incur_1.z.string().describe('Wallet address (0x... or ENS name)'),
|
|
278
|
+
}),
|
|
279
|
+
options: incur_1.z.object({
|
|
280
|
+
tagId: incur_1.z.string().describe('Label identifier to delete'),
|
|
281
|
+
chainId: incur_1.z.string().optional().describe('Optional chain identifier to scope the deletion'),
|
|
282
|
+
}),
|
|
283
|
+
examples: [
|
|
284
|
+
{
|
|
285
|
+
args: { address: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045' },
|
|
286
|
+
options: { tagId: 'vip' },
|
|
287
|
+
description: 'Remove the vip label',
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
args: { address: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045' },
|
|
291
|
+
options: { tagId: 'tier', chainId: '1' },
|
|
292
|
+
description: 'Remove a chain-scoped label',
|
|
293
|
+
},
|
|
294
|
+
],
|
|
295
|
+
hint: 'Requires profiles:write scope on your API key.',
|
|
296
|
+
run({ args, options }) {
|
|
297
|
+
return deleteProfileLabelRun(args.address, options);
|
|
298
|
+
},
|
|
299
|
+
});
|
|
300
|
+
exports.profiles.command(exports.profilesLabels);
|
package/dist/commands/query.d.ts
CHANGED
package/dist/commands/query.js
CHANGED
|
@@ -1,9 +1,34 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.query = void 0;
|
|
3
4
|
exports.queryRunRun = queryRunRun;
|
|
5
|
+
const incur_1 = require("incur");
|
|
4
6
|
const client_1 = require("../lib/client");
|
|
7
|
+
exports.query = incur_1.Cli.create('query', {
|
|
8
|
+
description: 'SQL analytics query commands',
|
|
9
|
+
});
|
|
5
10
|
function queryRunRun(sql) {
|
|
6
11
|
(0, client_1.requireApiKey)();
|
|
7
12
|
const client = (0, client_1.createClient)();
|
|
8
13
|
return client.post('/v0/query/', { query: sql });
|
|
9
14
|
}
|
|
15
|
+
exports.query.command('run', {
|
|
16
|
+
description: 'Run a SQL query against your Formo analytics data',
|
|
17
|
+
args: incur_1.z.object({
|
|
18
|
+
sql: incur_1.z.string().describe('SQL query string to execute'),
|
|
19
|
+
}),
|
|
20
|
+
examples: [
|
|
21
|
+
{
|
|
22
|
+
args: { sql: 'SELECT count(*) FROM events' },
|
|
23
|
+
description: 'Count all events',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
args: { sql: 'SELECT address, net_worth_usd FROM wallet_profiles ORDER BY net_worth_usd DESC LIMIT 10' },
|
|
27
|
+
description: 'Top 10 wallets by net worth',
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
hint: 'Requires query:read scope on your API key.',
|
|
31
|
+
run({ args }) {
|
|
32
|
+
return queryRunRun(args.sql);
|
|
33
|
+
},
|
|
34
|
+
});
|
|
@@ -5,5 +5,9 @@ export interface CreateSegmentOptions {
|
|
|
5
5
|
title: string;
|
|
6
6
|
filterSets: string;
|
|
7
7
|
}
|
|
8
|
+
export declare function buildCreateSegmentBody(options: CreateSegmentOptions): {
|
|
9
|
+
title: string;
|
|
10
|
+
filterSets: unknown;
|
|
11
|
+
};
|
|
8
12
|
export declare function createSegmentRun(options: CreateSegmentOptions): Promise<import("axios").AxiosResponse<any, any, {}>>;
|
|
9
13
|
export declare function deleteSegmentRun(segmentId: string): Promise<import("axios").AxiosResponse<any, any, {}>>;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.segments = void 0;
|
|
4
4
|
exports.listSegmentsRun = listSegmentsRun;
|
|
5
|
+
exports.buildCreateSegmentBody = buildCreateSegmentBody;
|
|
5
6
|
exports.createSegmentRun = createSegmentRun;
|
|
6
7
|
exports.deleteSegmentRun = deleteSegmentRun;
|
|
7
8
|
const incur_1 = require("incur");
|
|
@@ -23,9 +24,7 @@ exports.segments.command('list', {
|
|
|
23
24
|
return listSegmentsRun();
|
|
24
25
|
},
|
|
25
26
|
});
|
|
26
|
-
function
|
|
27
|
-
(0, client_1.requireApiKey)();
|
|
28
|
-
const client = (0, client_1.createClient)();
|
|
27
|
+
function buildCreateSegmentBody(options) {
|
|
29
28
|
let parsedFilterSets;
|
|
30
29
|
try {
|
|
31
30
|
parsedFilterSets = JSON.parse(options.filterSets);
|
|
@@ -33,10 +32,15 @@ function createSegmentRun(options) {
|
|
|
33
32
|
catch {
|
|
34
33
|
throw new Error('--filterSets must be a valid JSON array');
|
|
35
34
|
}
|
|
36
|
-
return
|
|
35
|
+
return {
|
|
37
36
|
title: options.title,
|
|
38
37
|
filterSets: parsedFilterSets,
|
|
39
|
-
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function createSegmentRun(options) {
|
|
41
|
+
(0, client_1.requireApiKey)();
|
|
42
|
+
const client = (0, client_1.createClient)();
|
|
43
|
+
return client.post('/v0/segments/', buildCreateSegmentBody(options));
|
|
40
44
|
}
|
|
41
45
|
exports.segments.command('create', {
|
|
42
46
|
description: 'Create a new user segment',
|
package/dist/index.d.ts
CHANGED