xapi-to 0.1.13 → 0.1.14
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 +4 -2
- package/dist/index.js +1335 -0
- package/package.json +14 -9
- package/src/client.ts +0 -265
- package/src/codegen.ts +0 -307
- package/src/commands/action.ts +0 -328
- package/src/commands/balance.ts +0 -35
- package/src/commands/config.ts +0 -58
- package/src/commands/oauth.ts +0 -297
- package/src/commands/register.ts +0 -77
- package/src/commands/topup.ts +0 -33
- package/src/config.ts +0 -69
- package/src/format.ts +0 -61
- package/src/index.ts +0 -216
package/src/commands/action.ts
DELETED
|
@@ -1,328 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Top-level action commands: list, search, categories, services, get, call
|
|
3
|
-
* Unified interface for all actions (capabilities + APIs).
|
|
4
|
-
* Use --source capability|api to filter by source type.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { getConfig, requireApiKey } from '../config.ts';
|
|
8
|
-
import * as client from '../client.ts';
|
|
9
|
-
import { output, err, getFormat } from '../format.ts';
|
|
10
|
-
import { generateCode, buildDefaultInput, resolveTarget } from '../codegen.ts';
|
|
11
|
-
|
|
12
|
-
const VALID_SOURCES = ['capability', 'api'];
|
|
13
|
-
|
|
14
|
-
// ── Subcommand help texts ────────────────────────────────────────────────────
|
|
15
|
-
|
|
16
|
-
const LIST_HELP = `xapi list - List all actions
|
|
17
|
-
|
|
18
|
-
USAGE
|
|
19
|
-
xapi list [flags]
|
|
20
|
-
|
|
21
|
-
FLAGS
|
|
22
|
-
--source capability|api Filter by source type
|
|
23
|
-
--category <name> Filter by category
|
|
24
|
-
--service-id <id> Filter by service
|
|
25
|
-
--page N Page number (default: 1)
|
|
26
|
-
--page-size N Results per page
|
|
27
|
-
--format json|pretty|table Output format
|
|
28
|
-
|
|
29
|
-
EXAMPLES
|
|
30
|
-
xapi list
|
|
31
|
-
xapi list --source api --format table
|
|
32
|
-
xapi list --category social --page 2
|
|
33
|
-
`;
|
|
34
|
-
|
|
35
|
-
const SEARCH_HELP = `xapi search - Search actions by keyword
|
|
36
|
-
|
|
37
|
-
USAGE
|
|
38
|
-
xapi search <query> [flags]
|
|
39
|
-
|
|
40
|
-
FLAGS
|
|
41
|
-
--source capability|api Filter by source type
|
|
42
|
-
--category <name> Filter by category
|
|
43
|
-
--page N Page number (default: 1)
|
|
44
|
-
--page-size N Results per page
|
|
45
|
-
--format json|pretty|table Output format
|
|
46
|
-
|
|
47
|
-
EXAMPLES
|
|
48
|
-
xapi search twitter
|
|
49
|
-
xapi search "tweet detail" --source api
|
|
50
|
-
xapi search weather --category utility --format table
|
|
51
|
-
`;
|
|
52
|
-
|
|
53
|
-
const GET_HELP = `xapi get - Get action schema
|
|
54
|
-
|
|
55
|
-
USAGE
|
|
56
|
-
xapi get <id> [flags]
|
|
57
|
-
|
|
58
|
-
FLAGS
|
|
59
|
-
--method GET|POST|... Filter by HTTP method
|
|
60
|
-
--code <target> Generate code snippet instead of showing schema
|
|
61
|
-
--format json|pretty|table Output format
|
|
62
|
-
|
|
63
|
-
CODE TARGETS
|
|
64
|
-
curl cURL command
|
|
65
|
-
py, python Python (requests)
|
|
66
|
-
python.requests Python with requests
|
|
67
|
-
py.requests alias for python.requests
|
|
68
|
-
python.httpx Python with httpx
|
|
69
|
-
py.httpx alias for python.httpx
|
|
70
|
-
js, javascript JavaScript (fetch)
|
|
71
|
-
javascript.fetch JavaScript with fetch
|
|
72
|
-
js.fetch alias for javascript.fetch
|
|
73
|
-
javascript.axios JavaScript with axios
|
|
74
|
-
js.axios alias for javascript.axios
|
|
75
|
-
ts, typescript TypeScript (fetch)
|
|
76
|
-
typescript.fetch TypeScript with fetch
|
|
77
|
-
ts.fetch alias for typescript.fetch
|
|
78
|
-
go Go (net/http)
|
|
79
|
-
|
|
80
|
-
EXAMPLES
|
|
81
|
-
xapi get twitter.tweet_detail
|
|
82
|
-
xapi get twitter.tweet_detail --method POST
|
|
83
|
-
xapi get twitter.tweet_detail --code curl
|
|
84
|
-
xapi get twitter.tweet_detail --code python.httpx --format pretty
|
|
85
|
-
`;
|
|
86
|
-
|
|
87
|
-
const CALL_HELP = `xapi call - Execute an action
|
|
88
|
-
|
|
89
|
-
USAGE
|
|
90
|
-
xapi call <id> --input '{"key":"val"}' [flags]
|
|
91
|
-
|
|
92
|
-
FLAGS
|
|
93
|
-
--input <json> Input payload as JSON (required for execution)
|
|
94
|
-
--method GET|POST|... Override HTTP method
|
|
95
|
-
--code <target> Generate code snippet instead of executing
|
|
96
|
-
--format json|pretty|table Output format
|
|
97
|
-
|
|
98
|
-
CODE TARGETS
|
|
99
|
-
curl cURL command
|
|
100
|
-
py, python Python (requests)
|
|
101
|
-
python.requests Python with requests
|
|
102
|
-
py.requests alias for python.requests
|
|
103
|
-
python.httpx Python with httpx
|
|
104
|
-
py.httpx alias for python.httpx
|
|
105
|
-
js, javascript JavaScript (fetch)
|
|
106
|
-
javascript.fetch JavaScript with fetch
|
|
107
|
-
js.fetch alias for javascript.fetch
|
|
108
|
-
javascript.axios JavaScript with axios
|
|
109
|
-
js.axios alias for javascript.axios
|
|
110
|
-
ts, typescript TypeScript (fetch)
|
|
111
|
-
typescript.fetch TypeScript with fetch
|
|
112
|
-
ts.fetch alias for typescript.fetch
|
|
113
|
-
go Go (net/http)
|
|
114
|
-
|
|
115
|
-
EXAMPLES
|
|
116
|
-
xapi call twitter.tweet_detail --input '{"tweet_id":"1234567890"}'
|
|
117
|
-
xapi call twitter.tweet_detail --input '{"tweet_id":"123"}' --code py
|
|
118
|
-
xapi call twitter.tweet_detail --input '{"tweet_id":"123"}' --code curl --format pretty
|
|
119
|
-
`;
|
|
120
|
-
|
|
121
|
-
/** Print subcommand help and exit if --help flag is set */
|
|
122
|
-
function showHelpIfRequested(flags: Record<string, string>, helpText: string): void {
|
|
123
|
-
if (flags.help) {
|
|
124
|
-
console.log(helpText);
|
|
125
|
-
process.exit(0);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/** Validate --code flag: check for bare flag and unknown target (fail fast before I/O) */
|
|
130
|
-
function validateCodeFlag(flags: Record<string, string>): void {
|
|
131
|
-
if (flags.code === 'true') {
|
|
132
|
-
err('--code requires a target language, e.g. --code curl, --code py, --code js');
|
|
133
|
-
}
|
|
134
|
-
resolveTarget(flags.code);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/** Output code snippet respecting --format */
|
|
138
|
-
function outputCode(result: { lang: string; lib: string; code: string }, flags: Record<string, string>) {
|
|
139
|
-
const fmt = flags.format || getFormat();
|
|
140
|
-
if (fmt === 'json') {
|
|
141
|
-
output({ language: result.lang, library: result.lib, code: result.code }, 'json');
|
|
142
|
-
} else {
|
|
143
|
-
console.log(result.code);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/** Validate and return source filter from --source flag */
|
|
148
|
-
function getSource(flags: Record<string, string>): string | undefined {
|
|
149
|
-
if (!flags.source) return undefined;
|
|
150
|
-
if (!VALID_SOURCES.includes(flags.source)) {
|
|
151
|
-
err(`invalid --source value: "${flags.source}". Must be "capability" or "api".`);
|
|
152
|
-
}
|
|
153
|
-
return flags.source;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
export async function actionList(args: string[], flags: Record<string, string>) {
|
|
157
|
-
showHelpIfRequested(flags, LIST_HELP);
|
|
158
|
-
const cfg = getConfig();
|
|
159
|
-
try {
|
|
160
|
-
const res = await client.actionList(cfg, {
|
|
161
|
-
source: getSource(flags),
|
|
162
|
-
page: flags.page ? parseInt(flags.page) : undefined,
|
|
163
|
-
page_size: flags['page-size'] ? parseInt(flags['page-size']) : undefined,
|
|
164
|
-
category: flags.category,
|
|
165
|
-
service_id: flags['service-id'],
|
|
166
|
-
});
|
|
167
|
-
const actions = (res.actions || []) as any[];
|
|
168
|
-
if (flags.format === 'table') {
|
|
169
|
-
output(actions.map((a: any) => ({
|
|
170
|
-
id: a.id,
|
|
171
|
-
method: a.method ?? '',
|
|
172
|
-
displayName: a.displayName ?? '',
|
|
173
|
-
source: a.source ?? '',
|
|
174
|
-
category: a.meta?.category ?? '',
|
|
175
|
-
status: a.status ?? '',
|
|
176
|
-
cost: a.meta?.cost ?? '',
|
|
177
|
-
})), 'table');
|
|
178
|
-
} else {
|
|
179
|
-
output(res, flags.format as any);
|
|
180
|
-
}
|
|
181
|
-
} catch (e: any) {
|
|
182
|
-
err('list failed', e.message);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
export async function actionSearch(args: string[], flags: Record<string, string>) {
|
|
187
|
-
showHelpIfRequested(flags, SEARCH_HELP);
|
|
188
|
-
const query = args[0];
|
|
189
|
-
if (!query) err('usage: xapi search <query>');
|
|
190
|
-
const cfg = getConfig();
|
|
191
|
-
try {
|
|
192
|
-
const res = await client.actionSearch(query, cfg, {
|
|
193
|
-
source: getSource(flags),
|
|
194
|
-
category: flags.category,
|
|
195
|
-
page: flags.page ? parseInt(flags.page) : undefined,
|
|
196
|
-
page_size: flags['page-size'] ? parseInt(flags['page-size']) : undefined,
|
|
197
|
-
});
|
|
198
|
-
const results = (res.results || []) as any[];
|
|
199
|
-
if (flags.format === 'table') {
|
|
200
|
-
output(results.map((a: any) => ({
|
|
201
|
-
id: a.id,
|
|
202
|
-
method: a.method ?? '',
|
|
203
|
-
displayName: a.displayName ?? '',
|
|
204
|
-
source: a.source ?? '',
|
|
205
|
-
category: a.meta?.category ?? '',
|
|
206
|
-
status: a.status ?? '',
|
|
207
|
-
cost: a.meta?.cost ?? '',
|
|
208
|
-
})), 'table');
|
|
209
|
-
} else {
|
|
210
|
-
output(res, flags.format as any);
|
|
211
|
-
}
|
|
212
|
-
} catch (e: any) {
|
|
213
|
-
err('search failed', e.message);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
export async function actionCategories(args: string[], flags: Record<string, string>) {
|
|
218
|
-
const cfg = getConfig();
|
|
219
|
-
try {
|
|
220
|
-
const res = await client.actionCategories(cfg, { source: getSource(flags) });
|
|
221
|
-
if (flags.format === 'table') {
|
|
222
|
-
output(res.categories.map(c => ({ category: c })), 'table');
|
|
223
|
-
} else {
|
|
224
|
-
output(res, flags.format as any);
|
|
225
|
-
}
|
|
226
|
-
} catch (e: any) {
|
|
227
|
-
err('categories failed', e.message);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
export async function actionServices(args: string[], flags: Record<string, string>) {
|
|
232
|
-
const cfg = getConfig();
|
|
233
|
-
try {
|
|
234
|
-
const res = await client.actionServices(cfg, {
|
|
235
|
-
page: flags.page ? parseInt(flags.page) : undefined,
|
|
236
|
-
page_size: flags['page-size'] ? parseInt(flags['page-size']) : undefined,
|
|
237
|
-
category: flags.category,
|
|
238
|
-
});
|
|
239
|
-
const services = (res.services || []) as any[];
|
|
240
|
-
if (flags.format === 'table') {
|
|
241
|
-
output(services.map((s: any) => ({
|
|
242
|
-
id: s.id,
|
|
243
|
-
name: s.name ?? '',
|
|
244
|
-
category: s.category ?? '',
|
|
245
|
-
source: s.source ?? '',
|
|
246
|
-
endpoints: s.endpointCount ?? '',
|
|
247
|
-
status: s.status ?? '',
|
|
248
|
-
})), 'table');
|
|
249
|
-
} else {
|
|
250
|
-
output(res, flags.format as any);
|
|
251
|
-
}
|
|
252
|
-
} catch (e: any) {
|
|
253
|
-
err('services failed', e.message);
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
export async function actionGet(args: string[], flags: Record<string, string>) {
|
|
258
|
-
showHelpIfRequested(flags, GET_HELP);
|
|
259
|
-
const id = args[0];
|
|
260
|
-
if (!id) err('usage: xapi get <id> [--method GET|POST|DELETE|...]');
|
|
261
|
-
if (flags.code) validateCodeFlag(flags);
|
|
262
|
-
const cfg = getConfig();
|
|
263
|
-
try {
|
|
264
|
-
const res = await client.actionGet(id, cfg);
|
|
265
|
-
const actions = Array.isArray(res) ? res : [res];
|
|
266
|
-
const methodFilter = flags.method?.toUpperCase();
|
|
267
|
-
const filtered = methodFilter
|
|
268
|
-
? actions.filter((a: any) => a.method?.toUpperCase() === methodFilter)
|
|
269
|
-
: actions;
|
|
270
|
-
if (filtered.length === 0) {
|
|
271
|
-
err(`no endpoint found for method "${methodFilter}" in action "${id}"`);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
if (flags.code) {
|
|
275
|
-
if (filtered.length > 1) {
|
|
276
|
-
process.stderr.write(
|
|
277
|
-
`Warning: action "${id}" has ${filtered.length} endpoints; using method "${(filtered[0] as any).method}". Use --method to select a specific one.\n`,
|
|
278
|
-
);
|
|
279
|
-
}
|
|
280
|
-
const action = filtered[0] as any;
|
|
281
|
-
const { method: _schemaMethod, ...cleanCodeInput } = buildDefaultInput(action.input ?? {});
|
|
282
|
-
const result = generateCode(flags.code, { actionId: id, input: cleanCodeInput, actionHost: cfg.actionHost, method: action.method });
|
|
283
|
-
outputCode(result, flags);
|
|
284
|
-
return;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
output(filtered.length === 1 ? filtered[0] : filtered, flags.format as any);
|
|
288
|
-
} catch (e: any) {
|
|
289
|
-
err('get failed', e.message);
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
export async function actionCall(args: string[], flags: Record<string, string>) {
|
|
294
|
-
showHelpIfRequested(flags, CALL_HELP);
|
|
295
|
-
const id = args[0];
|
|
296
|
-
if (!id) err('usage: xapi call <id> --input \'{"key":"val"}\'');
|
|
297
|
-
if (flags.code) validateCodeFlag(flags);
|
|
298
|
-
const cfg = getConfig();
|
|
299
|
-
let input: Record<string, unknown> = {};
|
|
300
|
-
if (flags.input) {
|
|
301
|
-
try {
|
|
302
|
-
input = JSON.parse(flags.input);
|
|
303
|
-
} catch {
|
|
304
|
-
err('--input must be valid JSON');
|
|
305
|
-
}
|
|
306
|
-
if (typeof input !== 'object' || input === null || Array.isArray(input)) {
|
|
307
|
-
err('--input must be a JSON object');
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
// method 作为独立参数传递,兼容 input 内的 method
|
|
311
|
-
const { method: inputMethod, ...cleanInput } = input;
|
|
312
|
-
const method = flags.method?.toUpperCase()
|
|
313
|
-
|| (typeof inputMethod === 'string' ? inputMethod.toUpperCase() : undefined);
|
|
314
|
-
|
|
315
|
-
if (flags.code) {
|
|
316
|
-
const result = generateCode(flags.code, { actionId: id, input: cleanInput, actionHost: cfg.actionHost, method });
|
|
317
|
-
outputCode(result, flags);
|
|
318
|
-
return;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
requireApiKey(cfg);
|
|
322
|
-
try {
|
|
323
|
-
const res = await client.actionCall(id, cleanInput, cfg, method);
|
|
324
|
-
output(res, flags.format as any);
|
|
325
|
-
} catch (e: any) {
|
|
326
|
-
err('call failed', e.message);
|
|
327
|
-
}
|
|
328
|
-
}
|
package/src/commands/balance.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* balance command
|
|
3
|
-
* Fetches balance from GET /auth/me
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { getConfig, requireApiKey, XAPI_API_HOST, scheme } from '../config.ts';
|
|
7
|
-
import { loginWithApiKey, request } from '../client.ts';
|
|
8
|
-
import { output, err } from '../format.ts';
|
|
9
|
-
|
|
10
|
-
export async function balance(args: string[], flags: Record<string, string>) {
|
|
11
|
-
const cfg = getConfig();
|
|
12
|
-
requireApiKey(cfg);
|
|
13
|
-
|
|
14
|
-
let token: string;
|
|
15
|
-
try {
|
|
16
|
-
const res = await loginWithApiKey(cfg.apiKey!, XAPI_API_HOST);
|
|
17
|
-
token = res.accessToken;
|
|
18
|
-
} catch (e: any) {
|
|
19
|
-
err('login failed', e.message);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
try {
|
|
23
|
-
const me = await request<{ balance: string; accountType: string; tier: string }>(
|
|
24
|
-
`${scheme(XAPI_API_HOST)}://${XAPI_API_HOST}/api/auth/me`,
|
|
25
|
-
{ method: 'GET', headers: { Authorization: `Bearer ${token!}` } },
|
|
26
|
-
);
|
|
27
|
-
output({
|
|
28
|
-
balance: me.balance,
|
|
29
|
-
accountType: me.accountType,
|
|
30
|
-
tier: me.tier,
|
|
31
|
-
}, flags.format as any);
|
|
32
|
-
} catch (e: any) {
|
|
33
|
-
err('balance fetch failed', e.message);
|
|
34
|
-
}
|
|
35
|
-
}
|
package/src/commands/config.ts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* config commands: show, set, health
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { getConfig, saveConfig, showConfig } from '../config.ts';
|
|
6
|
-
import { healthCheck } from '../client.ts';
|
|
7
|
-
import { output, err } from '../format.ts';
|
|
8
|
-
|
|
9
|
-
export const CONFIG_HELP = `xapi config - Manage CLI configuration
|
|
10
|
-
|
|
11
|
-
USAGE
|
|
12
|
-
xapi config <command> [flags]
|
|
13
|
-
|
|
14
|
-
COMMANDS
|
|
15
|
-
show Show current config (host, apiKey path, etc.)
|
|
16
|
-
set apiKey=<key> Save API key to ~/.xapi/config.json
|
|
17
|
-
health Check backend connectivity (alias: xapi health)
|
|
18
|
-
|
|
19
|
-
FLAGS
|
|
20
|
-
--format json|pretty|table Output format
|
|
21
|
-
|
|
22
|
-
EXAMPLES
|
|
23
|
-
xapi config show
|
|
24
|
-
xapi config set apiKey=xapi_abc123
|
|
25
|
-
xapi config health
|
|
26
|
-
`;
|
|
27
|
-
|
|
28
|
-
export async function configShow(args: string[], flags: Record<string, string>) {
|
|
29
|
-
showConfig();
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export async function configSet(args: string[], flags: Record<string, string>) {
|
|
33
|
-
// xapi config set apiKey=xapi_xxx
|
|
34
|
-
if (args.length === 0) err('usage: xapi config set apiKey=<key>');
|
|
35
|
-
const updates: { apiKey?: string } = {};
|
|
36
|
-
for (const arg of args) {
|
|
37
|
-
const eq = arg.indexOf('=');
|
|
38
|
-
if (eq < 1) err(`invalid key=value: ${arg}`);
|
|
39
|
-
const key = arg.slice(0, eq);
|
|
40
|
-
if (key === 'host') err('host is built-in and cannot be configured');
|
|
41
|
-
if (key !== 'apiKey') err(`unknown config key: ${key} (only apiKey is configurable)`);
|
|
42
|
-
updates.apiKey = arg.slice(eq + 1);
|
|
43
|
-
}
|
|
44
|
-
saveConfig(updates);
|
|
45
|
-
console.log(JSON.stringify({ ok: true, updated: Object.keys(updates) }));
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export async function configHealth(args: string[], flags: Record<string, string>) {
|
|
49
|
-
const cfg = getConfig();
|
|
50
|
-
const start = Date.now();
|
|
51
|
-
try {
|
|
52
|
-
await healthCheck(cfg);
|
|
53
|
-
output({ status: 'ok', host: cfg.actionHost, latency_ms: Date.now() - start }, flags.format as any);
|
|
54
|
-
} catch (e: any) {
|
|
55
|
-
output({ status: 'error', host: cfg.actionHost, error: e.message }, flags.format as any);
|
|
56
|
-
process.exit(1);
|
|
57
|
-
}
|
|
58
|
-
}
|