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 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 XAPI_API_KEY=sk-xxx
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 xToken balance
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
- | `XAPI_API_KEY` | API key (overrides config file) |
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xapi-to",
3
- "version": "0.1.10",
3
+ "version": "0.1.12",
4
4
  "description": "Agent-friendly CLI for xapi - discover and call capabilities and APIs",
5
5
  "type": "module",
6
6
  "bin": {
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>): string {
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 XAPI_API_KEY env var or replace with your key',
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: \${XAPI_API_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 XAPI_API_KEY env var or replace with your key',
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["XAPI_API_KEY"],`,
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 XAPI_API_KEY env var or replace with your key',
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.XAPI_API_KEY,`,
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 XAPI_API_KEY env var or replace with your key',
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.XAPI_API_KEY,`,
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 XAPI_API_KEY env var or replace with your key',
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.XAPI_API_KEY!,`,
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 XAPI_API_KEY env var or replace with your key',
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("XAPI_API_KEY"))',
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 {',
@@ -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 input = buildDefaultInput(action.input ?? {});
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
- if (flags.method) {
308
- input = { ...input, method: flags.method.toUpperCase() };
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, input, cfg);
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);
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * balance command
3
- * Fetches xTokenBalance from GET /auth/me
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<{ xTokenBalance: string; accountType: string; tier: string }>(
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.xTokenBalance,
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 XAPI_API_KEY or ~/.xapi/config.json
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 config health
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
- * XAPI_API_KEY API key
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
- XAPI_API_KEY API key (header: XAPI-Key)
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 config health
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);