xapi-to 0.1.13 → 0.1.15

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.
@@ -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
- }
@@ -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
- }
@@ -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
- }