xapi-to 0.1.10 → 0.1.12
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 +3 -3
- package/package.json +1 -1
- package/src/client.ts +2 -1
- package/src/codegen.ts +21 -20
- package/src/commands/action.ts +11 -7
- package/src/commands/balance.ts +3 -3
- package/src/config.ts +3 -3
- package/src/index.ts +8 -6
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ xapi register
|
|
|
25
25
|
xapi config set apiKey=sk-xxx
|
|
26
26
|
|
|
27
27
|
# 3. Or via env var
|
|
28
|
-
export
|
|
28
|
+
export XAPI_KEY=sk-xxx
|
|
29
29
|
|
|
30
30
|
# 4. Verify connectivity
|
|
31
31
|
xapi config health
|
|
@@ -76,7 +76,7 @@ xapi oauth providers # list available providers
|
|
|
76
76
|
|
|
77
77
|
```bash
|
|
78
78
|
xapi register # create account, saves apiKey automatically
|
|
79
|
-
xapi balance # show
|
|
79
|
+
xapi balance # show USD balance
|
|
80
80
|
xapi topup # generate payment URL
|
|
81
81
|
xapi topup --method stripe --amount 10 # stripe, $10
|
|
82
82
|
xapi topup --method x402 # x402 (USDC on Base)
|
|
@@ -119,7 +119,7 @@ xapi list --format table # human-readable table
|
|
|
119
119
|
|
|
120
120
|
| Variable | Description |
|
|
121
121
|
|---|---|
|
|
122
|
-
| `
|
|
122
|
+
| `XAPI_KEY` | API key (overrides config file) |
|
|
123
123
|
| `XAPI_ACTION_HOST` | Action service host (default: `action.xapi.to`) |
|
|
124
124
|
| `XAPI_OUTPUT` | Default output format (`json`\|`pretty`\|`table`) |
|
|
125
125
|
|
package/package.json
CHANGED
package/src/client.ts
CHANGED
|
@@ -124,13 +124,14 @@ export async function actionCall(
|
|
|
124
124
|
actionId: string,
|
|
125
125
|
input: Record<string, unknown>,
|
|
126
126
|
opts: ClientOptions,
|
|
127
|
+
httpMethod?: string,
|
|
127
128
|
) {
|
|
128
129
|
return request<unknown>(
|
|
129
130
|
`${baseUrl(opts)}/v1/actions/execute`,
|
|
130
131
|
{
|
|
131
132
|
method: 'POST',
|
|
132
133
|
headers: headers(opts.apiKey),
|
|
133
|
-
body: JSON.stringify({ action_id: actionId, input }),
|
|
134
|
+
body: JSON.stringify({ action_id: actionId, ...(httpMethod ? { method: httpMethod } : {}), input }),
|
|
134
135
|
},
|
|
135
136
|
EXECUTE_TIMEOUT_MS,
|
|
136
137
|
);
|
package/src/codegen.ts
CHANGED
|
@@ -13,6 +13,7 @@ export interface CodegenParams {
|
|
|
13
13
|
actionId: string;
|
|
14
14
|
input: Record<string, unknown>;
|
|
15
15
|
actionHost: string;
|
|
16
|
+
method?: string;
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
interface ResolvedTarget {
|
|
@@ -109,8 +110,8 @@ function baseUrl(actionHost: string): string {
|
|
|
109
110
|
return `${scheme(actionHost)}://${actionHost}/v1/actions/execute`;
|
|
110
111
|
}
|
|
111
112
|
|
|
112
|
-
function jsonBody(actionId: string, input: Record<string, unknown
|
|
113
|
-
return JSON.stringify({ action_id: actionId, input }, null, 2);
|
|
113
|
+
function jsonBody(actionId: string, input: Record<string, unknown>, method?: string): string {
|
|
114
|
+
return JSON.stringify({ action_id: actionId, ...(method ? { method } : {}), input }, null, 2);
|
|
114
115
|
}
|
|
115
116
|
|
|
116
117
|
/** Re-indent a multi-line string so continuation lines are aligned */
|
|
@@ -127,22 +128,22 @@ function shellEscape(s: string): string {
|
|
|
127
128
|
|
|
128
129
|
function genCurl(params: CodegenParams): string {
|
|
129
130
|
const url = baseUrl(params.actionHost);
|
|
130
|
-
const body = jsonBody(params.actionId, params.input);
|
|
131
|
+
const body = jsonBody(params.actionId, params.input, params.method);
|
|
131
132
|
return [
|
|
132
|
-
'# Set
|
|
133
|
+
'# Set XAPI_KEY env var or replace with your key',
|
|
133
134
|
`curl -X POST '${shellEscape(url)}' \\`,
|
|
134
135
|
` -H 'Content-Type: application/json' \\`,
|
|
135
|
-
` -H "XAPI-Key: \${
|
|
136
|
+
` -H "XAPI-Key: \${XAPI_KEY}" \\`,
|
|
136
137
|
` -d '${shellEscape(body)}'`,
|
|
137
138
|
].join('\n');
|
|
138
139
|
}
|
|
139
140
|
|
|
140
141
|
function genPython(lib: 'requests' | 'httpx', params: CodegenParams): string {
|
|
141
142
|
const url = baseUrl(params.actionHost);
|
|
142
|
-
const payload = { action_id: params.actionId, input: params.input };
|
|
143
|
+
const payload = { action_id: params.actionId, ...(params.method ? { method: params.method } : {}), input: params.input };
|
|
143
144
|
return [
|
|
144
145
|
`# pip install ${lib}`,
|
|
145
|
-
'# Set
|
|
146
|
+
'# Set XAPI_KEY env var or replace with your key',
|
|
146
147
|
'import os',
|
|
147
148
|
`import ${lib}`,
|
|
148
149
|
'',
|
|
@@ -150,7 +151,7 @@ function genPython(lib: 'requests' | 'httpx', params: CodegenParams): string {
|
|
|
150
151
|
` "${url}",`,
|
|
151
152
|
` headers={`,
|
|
152
153
|
` "Content-Type": "application/json",`,
|
|
153
|
-
` "XAPI-Key": os.environ["
|
|
154
|
+
` "XAPI-Key": os.environ["XAPI_KEY"],`,
|
|
154
155
|
` },`,
|
|
155
156
|
` json=${indent(pythonDict(payload), 4)},`,
|
|
156
157
|
`)`,
|
|
@@ -160,14 +161,14 @@ function genPython(lib: 'requests' | 'httpx', params: CodegenParams): string {
|
|
|
160
161
|
|
|
161
162
|
function genJavaScriptFetch(params: CodegenParams): string {
|
|
162
163
|
const url = baseUrl(params.actionHost);
|
|
163
|
-
const body = jsonBody(params.actionId, params.input);
|
|
164
|
+
const body = jsonBody(params.actionId, params.input, params.method);
|
|
164
165
|
return [
|
|
165
|
-
'// Set
|
|
166
|
+
'// Set XAPI_KEY env var or replace with your key',
|
|
166
167
|
`const resp = await fetch("${url}", {`,
|
|
167
168
|
` method: "POST",`,
|
|
168
169
|
` headers: {`,
|
|
169
170
|
` "Content-Type": "application/json",`,
|
|
170
|
-
` "XAPI-Key": process.env.
|
|
171
|
+
` "XAPI-Key": process.env.XAPI_KEY,`,
|
|
171
172
|
` },`,
|
|
172
173
|
` body: JSON.stringify(${indent(body, 2)}),`,
|
|
173
174
|
`});`,
|
|
@@ -177,10 +178,10 @@ function genJavaScriptFetch(params: CodegenParams): string {
|
|
|
177
178
|
|
|
178
179
|
function genJavaScriptAxios(params: CodegenParams): string {
|
|
179
180
|
const url = baseUrl(params.actionHost);
|
|
180
|
-
const body = jsonBody(params.actionId, params.input);
|
|
181
|
+
const body = jsonBody(params.actionId, params.input, params.method);
|
|
181
182
|
return [
|
|
182
183
|
'// npm install axios',
|
|
183
|
-
'// Set
|
|
184
|
+
'// Set XAPI_KEY env var or replace with your key',
|
|
184
185
|
'import axios from "axios";',
|
|
185
186
|
'',
|
|
186
187
|
`const resp = await axios.post(`,
|
|
@@ -189,7 +190,7 @@ function genJavaScriptAxios(params: CodegenParams): string {
|
|
|
189
190
|
` {`,
|
|
190
191
|
` headers: {`,
|
|
191
192
|
` "Content-Type": "application/json",`,
|
|
192
|
-
` "XAPI-Key": process.env.
|
|
193
|
+
` "XAPI-Key": process.env.XAPI_KEY,`,
|
|
193
194
|
` },`,
|
|
194
195
|
` },`,
|
|
195
196
|
`);`,
|
|
@@ -199,14 +200,14 @@ function genJavaScriptAxios(params: CodegenParams): string {
|
|
|
199
200
|
|
|
200
201
|
function genTypescriptFetch(params: CodegenParams): string {
|
|
201
202
|
const url = baseUrl(params.actionHost);
|
|
202
|
-
const body = jsonBody(params.actionId, params.input);
|
|
203
|
+
const body = jsonBody(params.actionId, params.input, params.method);
|
|
203
204
|
return [
|
|
204
|
-
'// Set
|
|
205
|
+
'// Set XAPI_KEY env var or replace with your key',
|
|
205
206
|
`const resp: Response = await fetch("${url}", {`,
|
|
206
207
|
` method: "POST",`,
|
|
207
208
|
` headers: {`,
|
|
208
209
|
` "Content-Type": "application/json",`,
|
|
209
|
-
` "XAPI-Key": process.env.
|
|
210
|
+
` "XAPI-Key": process.env.XAPI_KEY!,`,
|
|
210
211
|
` },`,
|
|
211
212
|
` body: JSON.stringify(${indent(body, 2)}),`,
|
|
212
213
|
`});`,
|
|
@@ -217,10 +218,10 @@ function genTypescriptFetch(params: CodegenParams): string {
|
|
|
217
218
|
|
|
218
219
|
function genGo(params: CodegenParams): string {
|
|
219
220
|
const url = baseUrl(params.actionHost);
|
|
220
|
-
const body = jsonBody(params.actionId, params.input);
|
|
221
|
+
const body = jsonBody(params.actionId, params.input, params.method);
|
|
221
222
|
const escaped = body.replace(/`/g, '` + "`" + `');
|
|
222
223
|
return [
|
|
223
|
-
'// Set
|
|
224
|
+
'// Set XAPI_KEY env var or replace with your key',
|
|
224
225
|
'package main',
|
|
225
226
|
'',
|
|
226
227
|
'import (',
|
|
@@ -238,7 +239,7 @@ function genGo(params: CodegenParams): string {
|
|
|
238
239
|
'\t\tpanic(err)',
|
|
239
240
|
'\t}',
|
|
240
241
|
'\treq.Header.Set("Content-Type", "application/json")',
|
|
241
|
-
'\treq.Header.Set("XAPI-Key", os.Getenv("
|
|
242
|
+
'\treq.Header.Set("XAPI-Key", os.Getenv("XAPI_KEY"))',
|
|
242
243
|
'',
|
|
243
244
|
'\tresp, err := http.DefaultClient.Do(req)',
|
|
244
245
|
'\tif err != nil {',
|
package/src/commands/action.ts
CHANGED
|
@@ -278,8 +278,8 @@ export async function actionGet(args: string[], flags: Record<string, string>) {
|
|
|
278
278
|
);
|
|
279
279
|
}
|
|
280
280
|
const action = filtered[0] as any;
|
|
281
|
-
const
|
|
282
|
-
const result = generateCode(flags.code, { actionId: id, input, actionHost: cfg.actionHost });
|
|
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
283
|
outputCode(result, flags);
|
|
284
284
|
return;
|
|
285
285
|
}
|
|
@@ -303,20 +303,24 @@ export async function actionCall(args: string[], flags: Record<string, string>)
|
|
|
303
303
|
} catch {
|
|
304
304
|
err('--input must be valid JSON');
|
|
305
305
|
}
|
|
306
|
+
if (typeof input !== 'object' || input === null || Array.isArray(input)) {
|
|
307
|
+
err('--input must be a JSON object');
|
|
308
|
+
}
|
|
306
309
|
}
|
|
307
|
-
|
|
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);
|
|
310
314
|
|
|
311
315
|
if (flags.code) {
|
|
312
|
-
const result = generateCode(flags.code, { actionId: id, input, actionHost: cfg.actionHost });
|
|
316
|
+
const result = generateCode(flags.code, { actionId: id, input: cleanInput, actionHost: cfg.actionHost, method });
|
|
313
317
|
outputCode(result, flags);
|
|
314
318
|
return;
|
|
315
319
|
}
|
|
316
320
|
|
|
317
321
|
requireApiKey(cfg);
|
|
318
322
|
try {
|
|
319
|
-
const res = await client.actionCall(id,
|
|
323
|
+
const res = await client.actionCall(id, cleanInput, cfg, method);
|
|
320
324
|
output(res, flags.format as any);
|
|
321
325
|
} catch (e: any) {
|
|
322
326
|
err('call failed', e.message);
|
package/src/commands/balance.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* balance command
|
|
3
|
-
* Fetches
|
|
3
|
+
* Fetches balance from GET /auth/me
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { getConfig, requireApiKey, XAPI_API_HOST, scheme } from '../config.ts';
|
|
@@ -20,12 +20,12 @@ export async function balance(args: string[], flags: Record<string, string>) {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
try {
|
|
23
|
-
const me = await request<{
|
|
23
|
+
const me = await request<{ balance: string; accountType: string; tier: string }>(
|
|
24
24
|
`${scheme(XAPI_API_HOST)}://${XAPI_API_HOST}/api/auth/me`,
|
|
25
25
|
{ method: 'GET', headers: { Authorization: `Bearer ${token!}` } },
|
|
26
26
|
);
|
|
27
27
|
output({
|
|
28
|
-
balance: me.
|
|
28
|
+
balance: me.balance,
|
|
29
29
|
accountType: me.accountType,
|
|
30
30
|
tier: me.tier,
|
|
31
31
|
}, flags.format as any);
|
package/src/config.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Config management
|
|
3
3
|
* Only apiKey is user-configurable. Host is built-in.
|
|
4
|
-
* Reads from env var
|
|
4
|
+
* Reads from env var XAPI_KEY or ~/.xapi/config.json
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { existsSync, readFileSync, writeFileSync, mkdirSync, chmodSync } from 'fs';
|
|
@@ -38,7 +38,7 @@ export function getConfig(): XapiConfig {
|
|
|
38
38
|
const file = loadFileConfig();
|
|
39
39
|
return {
|
|
40
40
|
actionHost: XAPI_ACTION_HOST,
|
|
41
|
-
apiKey: process.env.XAPI_API_KEY || file.apiKey,
|
|
41
|
+
apiKey: process.env.XAPI_KEY || process.env.XAPI_API_KEY || file.apiKey,
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
44
|
|
|
@@ -62,7 +62,7 @@ export function showConfig(): void {
|
|
|
62
62
|
actionHost: cfg.actionHost,
|
|
63
63
|
apiKey: cfg.apiKey ? `${cfg.apiKey.slice(0, 8)}...` : undefined,
|
|
64
64
|
source: {
|
|
65
|
-
apiKey: process.env.XAPI_API_KEY ? 'env' : file.apiKey ? 'file' : 'none',
|
|
65
|
+
apiKey: (process.env.XAPI_KEY || process.env.XAPI_API_KEY) ? 'env' : file.apiKey ? 'file' : 'none',
|
|
66
66
|
},
|
|
67
67
|
configFile: CONFIG_FILE,
|
|
68
68
|
}, null, 2));
|
package/src/index.ts
CHANGED
|
@@ -12,14 +12,14 @@
|
|
|
12
12
|
*
|
|
13
13
|
* xapi config show
|
|
14
14
|
* xapi config set apiKey=<key>
|
|
15
|
-
* xapi
|
|
15
|
+
* xapi health
|
|
16
16
|
*
|
|
17
17
|
* Global flags:
|
|
18
18
|
* --format json|pretty|table output format (default: json)
|
|
19
19
|
* --help show help
|
|
20
20
|
*
|
|
21
21
|
* Env vars:
|
|
22
|
-
*
|
|
22
|
+
* XAPI_KEY API key
|
|
23
23
|
* XAPI_ACTION_HOST Action service host (default: action.xapi.to)
|
|
24
24
|
* XAPI_OUTPUT default output format (json|pretty|table)
|
|
25
25
|
*/
|
|
@@ -88,6 +88,7 @@ COMMANDS
|
|
|
88
88
|
get <id> [--method GET|POST|...] Get action schema (filter by HTTP method)
|
|
89
89
|
--code <target> Generate code snippet (curl, py, js, ts, go)
|
|
90
90
|
call <id> --input '{"key":"val"}' Execute an action
|
|
91
|
+
--method GET|POST|... Override HTTP method
|
|
91
92
|
--code <target> Generate code snippet instead of executing
|
|
92
93
|
Variants: python.requests, python.httpx, javascript.fetch, javascript.axios
|
|
93
94
|
|
|
@@ -100,16 +101,17 @@ COMMANDS
|
|
|
100
101
|
balance Show current account balance
|
|
101
102
|
topup [--amount <usd>] [--method stripe|x402] Generate payment URL
|
|
102
103
|
|
|
104
|
+
health Check backend connectivity
|
|
105
|
+
|
|
103
106
|
config show Show current config
|
|
104
107
|
config set apiKey=<key> Save API key to ~/.xapi/config.json
|
|
105
|
-
config health Check backend connectivity
|
|
106
108
|
|
|
107
109
|
GLOBAL FLAGS
|
|
108
110
|
--format json|pretty|table Output format (default: json)
|
|
109
111
|
--help Show help (use with a command for details, e.g. xapi get --help)
|
|
110
112
|
|
|
111
113
|
ENV VARS
|
|
112
|
-
|
|
114
|
+
XAPI_KEY API key (header: XAPI-Key)
|
|
113
115
|
XAPI_ACTION_HOST Action service host (default: action.xapi.to)
|
|
114
116
|
XAPI_OUTPUT Default output format
|
|
115
117
|
|
|
@@ -126,7 +128,7 @@ EXAMPLES
|
|
|
126
128
|
xapi categories
|
|
127
129
|
xapi services --format table
|
|
128
130
|
xapi config set apiKey=xapi_abc123
|
|
129
|
-
xapi
|
|
131
|
+
xapi health
|
|
130
132
|
`;
|
|
131
133
|
|
|
132
134
|
// ── Router ────────────────────────────────────────────────────────────────────
|
|
@@ -176,6 +178,7 @@ async function main() {
|
|
|
176
178
|
case 'register': return regCmds.register(rest, flags);
|
|
177
179
|
case 'balance': return balanceCmds.balance(rest, flags);
|
|
178
180
|
case 'topup': return topupCmds.topup(rest, flags);
|
|
181
|
+
case 'health': return cfgCmds.configHealth(rest, flags);
|
|
179
182
|
|
|
180
183
|
// ── Config commands ──
|
|
181
184
|
case 'config': {
|
|
@@ -183,7 +186,6 @@ async function main() {
|
|
|
183
186
|
switch (subCmd) {
|
|
184
187
|
case 'show': return cfgCmds.configShow(subRest, flags);
|
|
185
188
|
case 'set': return cfgCmds.configSet(subRest, flags);
|
|
186
|
-
case 'health': return cfgCmds.configHealth(subRest, flags);
|
|
187
189
|
default:
|
|
188
190
|
console.error(JSON.stringify({ error: `unknown config command: ${subCmd}` }));
|
|
189
191
|
process.exit(1);
|