@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
package/dist/index.js
CHANGED
|
@@ -12,213 +12,214 @@ const query_1 = require("./commands/query");
|
|
|
12
12
|
const segments_1 = require("./commands/segments");
|
|
13
13
|
const config_1 = require("./lib/config");
|
|
14
14
|
const ui_1 = require("./lib/ui");
|
|
15
|
-
const DASHBOARD_URL =
|
|
16
|
-
const DOCS_URL =
|
|
17
|
-
const API_BASE_URL =
|
|
15
|
+
const DASHBOARD_URL = "https://app.formo.so";
|
|
16
|
+
const DOCS_URL = "https://docs.formo.so";
|
|
17
|
+
const API_BASE_URL = "https://api.formo.so";
|
|
18
18
|
function loginGuide() {
|
|
19
19
|
return [
|
|
20
|
-
|
|
21
|
-
ui_1.color.boldGreen(
|
|
22
|
-
|
|
23
|
-
` ${ui_1.color.white(
|
|
24
|
-
` ${ui_1.color.white(
|
|
25
|
-
` ${ui_1.color.white(
|
|
26
|
-
` ${ui_1.color.white(
|
|
27
|
-
|
|
28
|
-
` ${ui_1.color.green(
|
|
29
|
-
|
|
20
|
+
"",
|
|
21
|
+
ui_1.color.boldGreen("How to get your API key:"),
|
|
22
|
+
"",
|
|
23
|
+
` ${ui_1.color.white("1.")} Go to ${ui_1.color.cyan(DASHBOARD_URL)}`,
|
|
24
|
+
` ${ui_1.color.white("2.")} Navigate to ${ui_1.color.bold("Settings")} → ${ui_1.color.bold("API Keys")}`,
|
|
25
|
+
` ${ui_1.color.white("3.")} Click ${ui_1.color.bold('"Create API Key"')} and copy the key`,
|
|
26
|
+
` ${ui_1.color.white("4.")} Run:`,
|
|
27
|
+
"",
|
|
28
|
+
` ${ui_1.color.green("formo login <formo_your_api_key_here>")}`,
|
|
29
|
+
"",
|
|
30
30
|
ui_1.color.dim(` Or set the environment variable:`),
|
|
31
|
-
|
|
32
|
-
` ${ui_1.color.green(
|
|
33
|
-
|
|
31
|
+
"",
|
|
32
|
+
` ${ui_1.color.green("export FORMO_API_KEY=formo_your_api_key_here")}`,
|
|
33
|
+
"",
|
|
34
34
|
ui_1.color.dim(` Docs: ${DOCS_URL}`),
|
|
35
|
-
|
|
36
|
-
].join(
|
|
35
|
+
"",
|
|
36
|
+
].join("\n");
|
|
37
37
|
}
|
|
38
38
|
async function validateAndFetchWorkspace(apiKey) {
|
|
39
39
|
try {
|
|
40
40
|
const res = await fetch(`${API_BASE_URL}/api/validate-api-key`, {
|
|
41
|
-
method:
|
|
42
|
-
headers: {
|
|
41
|
+
method: "POST",
|
|
42
|
+
headers: { "Content-Type": "application/json" },
|
|
43
43
|
body: JSON.stringify({ apiKey }),
|
|
44
44
|
});
|
|
45
45
|
if (!res.ok)
|
|
46
46
|
return null;
|
|
47
47
|
const body = (await res.json());
|
|
48
|
-
if (!body.
|
|
48
|
+
if (!body.validated)
|
|
49
49
|
return null;
|
|
50
50
|
return {
|
|
51
|
-
workspace: body.
|
|
52
|
-
projectId: body.
|
|
51
|
+
workspace: body.details,
|
|
52
|
+
projectId: body.scopes?.project_id ?? "",
|
|
53
53
|
};
|
|
54
54
|
}
|
|
55
55
|
catch {
|
|
56
56
|
return null;
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
|
-
const cli = incur_1.Cli.create(
|
|
60
|
-
version:
|
|
61
|
-
description:
|
|
59
|
+
const cli = incur_1.Cli.create("formo", {
|
|
60
|
+
version: "0.2.0",
|
|
61
|
+
description: "Formo API CLI — Web3 analytics from the terminal",
|
|
62
62
|
sync: {
|
|
63
63
|
suggestions: [
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
'import wallet addresses',
|
|
64
|
+
"get the profile for wallet 0xabc",
|
|
65
|
+
"search profiles with net worth > 10000",
|
|
66
|
+
"run a SQL query on my analytics data",
|
|
67
|
+
"search profiles ordered by last_onchain desc",
|
|
68
|
+
"list all project alerts",
|
|
69
|
+
"create an alert for high-value transactions",
|
|
70
|
+
"list charts in a board",
|
|
71
|
+
"list all tracked contracts",
|
|
72
|
+
"register a new smart contract",
|
|
73
|
+
"list user segments",
|
|
74
|
+
"import wallet addresses",
|
|
76
75
|
],
|
|
77
76
|
},
|
|
78
77
|
});
|
|
79
78
|
// ── login ──
|
|
80
|
-
cli.command(
|
|
81
|
-
description:
|
|
79
|
+
cli.command("login", {
|
|
80
|
+
description: "Authenticate with your Formo API key",
|
|
82
81
|
args: incur_1.z.object({
|
|
83
|
-
apiKey: incur_1.z.string().optional().describe(
|
|
82
|
+
apiKey: incur_1.z.string().optional().describe("Your formo_ API key"),
|
|
84
83
|
}),
|
|
85
84
|
options: incur_1.z.object({}),
|
|
86
|
-
examples: [
|
|
87
|
-
{ args: { apiKey: 'formo_abc123' }, description: 'Save API key' },
|
|
88
|
-
],
|
|
85
|
+
examples: [{ args: { apiKey: "formo_abc123" }, description: "Save API key" }],
|
|
89
86
|
async run({ args }) {
|
|
90
87
|
// No key provided → show guide
|
|
91
88
|
if (!args.apiKey) {
|
|
92
89
|
if (process.stdout.isTTY) {
|
|
93
90
|
process.stderr.write(loginGuide());
|
|
94
91
|
}
|
|
95
|
-
return { ok: false, message:
|
|
92
|
+
return { ok: false, message: "No API key provided. See guide above." };
|
|
96
93
|
}
|
|
97
94
|
const isTTY = process.stdout.isTTY;
|
|
98
95
|
// Validate the key against the API
|
|
99
96
|
if (isTTY) {
|
|
100
|
-
process.stderr.write(
|
|
97
|
+
process.stderr.write("\n" + ui_1.color.dim("Validating API key…") + "\n");
|
|
101
98
|
}
|
|
102
99
|
const workspaceInfo = await validateAndFetchWorkspace(args.apiKey);
|
|
103
100
|
// Save key + workspace info (save even if validation fails — user might be offline)
|
|
104
101
|
// Only include workspace/projectId when present so offline logins don't wipe existing values
|
|
105
102
|
(0, config_1.saveConfig)({
|
|
106
103
|
apiKey: args.apiKey,
|
|
107
|
-
...(workspaceInfo?.workspace !== undefined && {
|
|
108
|
-
|
|
104
|
+
...(workspaceInfo?.workspace !== undefined && {
|
|
105
|
+
workspace: workspaceInfo.workspace,
|
|
106
|
+
}),
|
|
107
|
+
...(workspaceInfo?.projectId !== undefined && {
|
|
108
|
+
projectId: workspaceInfo.projectId,
|
|
109
|
+
}),
|
|
109
110
|
});
|
|
110
111
|
// Show feedback in human mode
|
|
111
112
|
if (isTTY) {
|
|
112
113
|
const masked = args.apiKey.length > 12
|
|
113
|
-
? args.apiKey.slice(0, 8) +
|
|
114
|
-
:
|
|
114
|
+
? args.apiKey.slice(0, 8) + "…" + args.apiKey.slice(-4)
|
|
115
|
+
: "***";
|
|
115
116
|
if (workspaceInfo) {
|
|
116
|
-
process.stderr.write((0, ui_1.success)(
|
|
117
|
-
|
|
117
|
+
process.stderr.write((0, ui_1.success)("API key validated and saved!") +
|
|
118
|
+
"\n" +
|
|
118
119
|
(0, ui_1.info)(`Key: ${ui_1.color.dim(masked)}`) +
|
|
119
|
-
|
|
120
|
+
"\n" +
|
|
120
121
|
(0, ui_1.info)(`Workspace: ${ui_1.color.bold(workspaceInfo.workspace)}`) +
|
|
121
|
-
|
|
122
|
+
"\n" +
|
|
122
123
|
(workspaceInfo.projectId
|
|
123
|
-
? (0, ui_1.info)(`Project: ${ui_1.color.dim(workspaceInfo.projectId)}`) +
|
|
124
|
-
:
|
|
125
|
-
(0, ui_1.info)(`Config: ${ui_1.color.dim(
|
|
126
|
-
|
|
127
|
-
ui_1.color.dim(
|
|
128
|
-
|
|
129
|
-
ui_1.color.dim(
|
|
130
|
-
ui_1.color.green(
|
|
131
|
-
ui_1.color.dim(
|
|
132
|
-
|
|
124
|
+
? (0, ui_1.info)(`Project: ${ui_1.color.dim(workspaceInfo.projectId)}`) + "\n"
|
|
125
|
+
: "") +
|
|
126
|
+
(0, ui_1.info)(`Config: ${ui_1.color.dim("~/.config/formo/config.json")}`) +
|
|
127
|
+
"\n\n" +
|
|
128
|
+
ui_1.color.dim("You can now use all formo commands.") +
|
|
129
|
+
"\n" +
|
|
130
|
+
ui_1.color.dim("Run ") +
|
|
131
|
+
ui_1.color.green("formo --help") +
|
|
132
|
+
ui_1.color.dim(" to see available commands.") +
|
|
133
|
+
"\n\n");
|
|
133
134
|
}
|
|
134
135
|
else {
|
|
135
|
-
process.stderr.write((0, ui_1.warn)(
|
|
136
|
-
|
|
136
|
+
process.stderr.write((0, ui_1.warn)("API key saved but could not be validated.") +
|
|
137
|
+
"\n" +
|
|
137
138
|
(0, ui_1.info)(`Key: ${ui_1.color.dim(masked)}`) +
|
|
138
|
-
|
|
139
|
-
(0, ui_1.info)(`Config: ${ui_1.color.dim(
|
|
140
|
-
|
|
141
|
-
ui_1.color.dim(
|
|
142
|
-
|
|
139
|
+
"\n" +
|
|
140
|
+
(0, ui_1.info)(`Config: ${ui_1.color.dim("~/.config/formo/config.json")}`) +
|
|
141
|
+
"\n" +
|
|
142
|
+
ui_1.color.dim("The API might be unreachable. Your key is saved and will be used for requests.") +
|
|
143
|
+
"\n\n");
|
|
143
144
|
}
|
|
144
145
|
}
|
|
145
146
|
return {
|
|
146
147
|
ok: true,
|
|
147
148
|
message: workspaceInfo
|
|
148
|
-
?
|
|
149
|
-
:
|
|
149
|
+
? "API key validated and saved"
|
|
150
|
+
: "API key saved (not validated)",
|
|
150
151
|
workspace: workspaceInfo?.workspace ?? null,
|
|
151
152
|
projectId: workspaceInfo?.projectId ?? null,
|
|
152
153
|
};
|
|
153
154
|
},
|
|
154
155
|
});
|
|
155
156
|
// ── logout ──
|
|
156
|
-
cli.command(
|
|
157
|
-
description:
|
|
157
|
+
cli.command("logout", {
|
|
158
|
+
description: "Remove saved API key and clear authentication",
|
|
158
159
|
run() {
|
|
159
160
|
const hadKey = !!(0, config_1.readConfig)().apiKey;
|
|
160
161
|
(0, config_1.clearConfig)();
|
|
161
162
|
if (process.stdout.isTTY) {
|
|
162
|
-
process.stderr.write(
|
|
163
|
+
process.stderr.write("\n");
|
|
163
164
|
if (hadKey) {
|
|
164
|
-
process.stderr.write((0, ui_1.success)(
|
|
165
|
-
|
|
166
|
-
(0, ui_1.info)(
|
|
167
|
-
|
|
165
|
+
process.stderr.write((0, ui_1.success)("Logged out successfully.") +
|
|
166
|
+
"\n" +
|
|
167
|
+
(0, ui_1.info)("API key removed from ~/.config/formo/config.json") +
|
|
168
|
+
"\n");
|
|
168
169
|
}
|
|
169
170
|
else {
|
|
170
|
-
process.stderr.write((0, ui_1.info)(
|
|
171
|
+
process.stderr.write((0, ui_1.info)("No saved API key found — already logged out.") + "\n");
|
|
171
172
|
}
|
|
172
173
|
if (process.env.FORMO_API_KEY) {
|
|
173
|
-
process.stderr.write(
|
|
174
|
-
(0, ui_1.warn)(`${ui_1.color.yellow(
|
|
175
|
-
|
|
176
|
-
(0, ui_1.info)(`Run ${ui_1.color.green(
|
|
177
|
-
|
|
174
|
+
process.stderr.write("\n" +
|
|
175
|
+
(0, ui_1.warn)(`${ui_1.color.yellow("FORMO_API_KEY")} env var is still set.`) +
|
|
176
|
+
"\n" +
|
|
177
|
+
(0, ui_1.info)(`Run ${ui_1.color.green("unset FORMO_API_KEY")} to fully log out.`) +
|
|
178
|
+
"\n");
|
|
178
179
|
}
|
|
179
|
-
process.stderr.write(
|
|
180
|
+
process.stderr.write("\n");
|
|
180
181
|
}
|
|
181
182
|
return {
|
|
182
183
|
ok: true,
|
|
183
|
-
message: hadKey ?
|
|
184
|
+
message: hadKey ? "API key removed" : "Already logged out",
|
|
184
185
|
envVarSet: !!process.env.FORMO_API_KEY,
|
|
185
186
|
};
|
|
186
187
|
},
|
|
187
188
|
});
|
|
188
189
|
// ── status ──
|
|
189
|
-
cli.command(
|
|
190
|
-
description:
|
|
190
|
+
cli.command("status", {
|
|
191
|
+
description: "Show current authentication and CLI status",
|
|
191
192
|
run() {
|
|
192
193
|
const apiKey = (0, config_1.getApiKey)();
|
|
193
194
|
const config = (0, config_1.readConfig)();
|
|
194
195
|
const source = process.env.FORMO_API_KEY
|
|
195
|
-
?
|
|
196
|
-
:
|
|
196
|
+
? "FORMO_API_KEY env var"
|
|
197
|
+
: "config file";
|
|
197
198
|
if (process.stdout.isTTY) {
|
|
198
|
-
process.stderr.write(
|
|
199
|
+
process.stderr.write("\n");
|
|
199
200
|
if (apiKey) {
|
|
200
201
|
const masked = apiKey.length > 12
|
|
201
|
-
? apiKey.slice(0, 8) +
|
|
202
|
-
:
|
|
203
|
-
process.stderr.write((0, ui_1.success)(
|
|
204
|
-
|
|
202
|
+
? apiKey.slice(0, 8) + "…" + apiKey.slice(-4)
|
|
203
|
+
: "***";
|
|
204
|
+
process.stderr.write((0, ui_1.success)("Authenticated") +
|
|
205
|
+
"\n" +
|
|
205
206
|
(0, ui_1.info)(`Key: ${ui_1.color.dim(masked)}`) +
|
|
206
|
-
|
|
207
|
+
"\n" +
|
|
207
208
|
(0, ui_1.info)(`Source: ${ui_1.color.dim(source)}`) +
|
|
208
|
-
|
|
209
|
+
"\n" +
|
|
209
210
|
(config.workspace
|
|
210
|
-
? (0, ui_1.info)(`Workspace: ${ui_1.color.bold(config.workspace)}`) +
|
|
211
|
-
:
|
|
211
|
+
? (0, ui_1.info)(`Workspace: ${ui_1.color.bold(config.workspace)}`) + "\n"
|
|
212
|
+
: "") +
|
|
212
213
|
(config.projectId
|
|
213
|
-
? (0, ui_1.info)(`Project ID: ${ui_1.color.dim(config.projectId)}`) +
|
|
214
|
-
:
|
|
215
|
-
|
|
214
|
+
? (0, ui_1.info)(`Project ID: ${ui_1.color.dim(config.projectId)}`) + "\n"
|
|
215
|
+
: "") +
|
|
216
|
+
"\n");
|
|
216
217
|
}
|
|
217
218
|
else {
|
|
218
|
-
process.stderr.write((0, ui_1.error)(
|
|
219
|
-
|
|
220
|
-
(0, ui_1.info)(`Run ${ui_1.color.green(
|
|
221
|
-
|
|
219
|
+
process.stderr.write((0, ui_1.error)("Not authenticated") +
|
|
220
|
+
"\n\n" +
|
|
221
|
+
(0, ui_1.info)(`Run ${ui_1.color.green("formo login")} to get started.`) +
|
|
222
|
+
"\n\n");
|
|
222
223
|
}
|
|
223
224
|
}
|
|
224
225
|
return {
|
|
@@ -226,34 +227,13 @@ cli.command('status', {
|
|
|
226
227
|
source: apiKey ? source : null,
|
|
227
228
|
workspace: config.workspace ?? null,
|
|
228
229
|
projectId: config.projectId ?? null,
|
|
229
|
-
configFile: config.apiKey ?
|
|
230
|
+
configFile: config.apiKey ? "~/.config/formo/config.json" : null,
|
|
230
231
|
};
|
|
231
232
|
},
|
|
232
233
|
});
|
|
233
|
-
// ── query ──
|
|
234
|
-
cli.command('query', {
|
|
235
|
-
description: 'Run a SQL query against your Formo analytics data',
|
|
236
|
-
args: incur_1.z.object({
|
|
237
|
-
sql: incur_1.z.string().describe('SQL query string to execute'),
|
|
238
|
-
}),
|
|
239
|
-
options: incur_1.z.object({}),
|
|
240
|
-
examples: [
|
|
241
|
-
{
|
|
242
|
-
args: { sql: 'SELECT count(*) FROM events' },
|
|
243
|
-
description: 'Count all events',
|
|
244
|
-
},
|
|
245
|
-
{
|
|
246
|
-
args: { sql: 'SELECT address, net_worth_usd FROM wallet_profiles ORDER BY net_worth_usd DESC LIMIT 10' },
|
|
247
|
-
description: 'Top 10 wallets by net worth',
|
|
248
|
-
},
|
|
249
|
-
],
|
|
250
|
-
hint: 'Requires query:read scope on your API key.',
|
|
251
|
-
run({ args }) {
|
|
252
|
-
return (0, query_1.queryRunRun)(args.sql);
|
|
253
|
-
},
|
|
254
|
-
});
|
|
255
234
|
// ── command groups ──
|
|
256
235
|
cli.command(profiles_1.profiles);
|
|
236
|
+
cli.command(query_1.query);
|
|
257
237
|
cli.command(alerts_1.alerts);
|
|
258
238
|
cli.command(boards_1.boards);
|
|
259
239
|
cli.command(charts_1.charts);
|
|
@@ -262,7 +242,7 @@ cli.command(segments_1.segments);
|
|
|
262
242
|
cli.command(import_1.importCmd);
|
|
263
243
|
// Show banner when run with no args (root help)
|
|
264
244
|
const args = process.argv.slice(2);
|
|
265
|
-
const isRootHelp = args.length === 0 || (args.length === 1 && args[0] ===
|
|
245
|
+
const isRootHelp = args.length === 0 || (args.length === 1 && args[0] === "--help");
|
|
266
246
|
if (isRootHelp && process.stdout.isTTY) {
|
|
267
247
|
process.stderr.write((0, ui_1.banner)());
|
|
268
248
|
}
|
package/dist/lib/client.d.ts
CHANGED
|
@@ -1,3 +1,29 @@
|
|
|
1
|
+
import { AxiosError } from 'axios';
|
|
2
|
+
export interface ApiErrorBody {
|
|
3
|
+
error?: {
|
|
4
|
+
code?: string;
|
|
5
|
+
message?: string;
|
|
6
|
+
doc_url?: string;
|
|
7
|
+
param?: string;
|
|
8
|
+
details?: Record<string, unknown>;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export interface DecoratedApiError extends Error {
|
|
12
|
+
status?: number;
|
|
13
|
+
code?: string;
|
|
14
|
+
docUrl?: string;
|
|
15
|
+
param?: string;
|
|
16
|
+
details?: Record<string, unknown>;
|
|
17
|
+
transportCode?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Translate an AxiosError into a thrown Error with the API's structured
|
|
21
|
+
* `{ error: { code, message, doc_url, param, details } }` envelope decoded
|
|
22
|
+
* onto the Error instance and into a multi-line, human-readable `.message`.
|
|
23
|
+
*
|
|
24
|
+
* Exported for unit testing — used by the response interceptor below.
|
|
25
|
+
*/
|
|
26
|
+
export declare function parseApiError(error: AxiosError): DecoratedApiError;
|
|
1
27
|
declare function createClient(): import("axios").AxiosInstance;
|
|
2
28
|
export declare function requireApiKey(): void;
|
|
3
29
|
export { createClient };
|
package/dist/lib/client.js
CHANGED
|
@@ -3,11 +3,40 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.parseApiError = parseApiError;
|
|
6
7
|
exports.requireApiKey = requireApiKey;
|
|
7
8
|
exports.createClient = createClient;
|
|
8
9
|
const axios_1 = __importDefault(require("axios"));
|
|
9
10
|
const config_1 = require("./config");
|
|
10
11
|
const BASE_URL = 'https://api.formo.so';
|
|
12
|
+
/**
|
|
13
|
+
* Translate an AxiosError into a thrown Error with the API's structured
|
|
14
|
+
* `{ error: { code, message, doc_url, param, details } }` envelope decoded
|
|
15
|
+
* onto the Error instance and into a multi-line, human-readable `.message`.
|
|
16
|
+
*
|
|
17
|
+
* Exported for unit testing — used by the response interceptor below.
|
|
18
|
+
*/
|
|
19
|
+
function parseApiError(error) {
|
|
20
|
+
const status = error.response?.status;
|
|
21
|
+
const body = error.response?.data;
|
|
22
|
+
const apiError = body?.error;
|
|
23
|
+
const baseMessage = apiError?.message ?? error.message;
|
|
24
|
+
const parts = [];
|
|
25
|
+
parts.push(apiError?.code ? `[${apiError.code}] ${baseMessage}` : baseMessage);
|
|
26
|
+
if (apiError?.param)
|
|
27
|
+
parts.push(`Param: ${apiError.param}`);
|
|
28
|
+
if (apiError?.doc_url)
|
|
29
|
+
parts.push(`Docs: ${apiError.doc_url}`);
|
|
30
|
+
const message = parts.join('\n ');
|
|
31
|
+
return Object.assign(new Error(message), {
|
|
32
|
+
status,
|
|
33
|
+
code: apiError?.code,
|
|
34
|
+
docUrl: apiError?.doc_url,
|
|
35
|
+
param: apiError?.param,
|
|
36
|
+
details: apiError?.details,
|
|
37
|
+
transportCode: error.code,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
11
40
|
function createClient() {
|
|
12
41
|
const apiKey = (0, config_1.getApiKey)();
|
|
13
42
|
const baseURL = BASE_URL;
|
|
@@ -20,13 +49,7 @@ function createClient() {
|
|
|
20
49
|
},
|
|
21
50
|
});
|
|
22
51
|
instance.interceptors.response.use((res) => res.data, (error) => {
|
|
23
|
-
|
|
24
|
-
const data = error.response?.data;
|
|
25
|
-
const errorField = data?.error;
|
|
26
|
-
const message = data?.message ??
|
|
27
|
-
(typeof errorField === 'object' ? errorField?.message : errorField) ??
|
|
28
|
-
error.message;
|
|
29
|
-
throw Object.assign(new Error(message), { status, code: error.code });
|
|
52
|
+
throw parseApiError(error);
|
|
30
53
|
});
|
|
31
54
|
return instance;
|
|
32
55
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@formo/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"packageManager": "pnpm@10.28.2",
|
|
4
5
|
"description": "Formo API CLI — query profiles and analytics data",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/getformo/cli.git"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/getformo/cli#readme",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/getformo/cli/issues"
|
|
13
|
+
},
|
|
5
14
|
"bin": {
|
|
6
15
|
"formo": "dist/index.js"
|
|
7
16
|
},
|