xapi-to 0.1.12 → 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 +9 -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 -39
- package/src/commands/oauth.ts +0 -297
- package/src/commands/register.ts +0 -59
- package/src/commands/topup.ts +0 -33
- package/src/config.ts +0 -69
- package/src/format.ts +0 -61
- package/src/index.ts +0 -205
package/src/commands/register.ts
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* register command: create a new user account
|
|
3
|
-
*
|
|
4
|
-
* POST /auth/register — no auth required
|
|
5
|
-
* Returns apiKey (shown once), claimCode, claimUrl, tweetTemplate
|
|
6
|
-
* Automatically saves apiKey to ~/.xapi/config.json
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { XAPI_API_HOST, saveConfig, scheme } from '../config.ts';
|
|
10
|
-
import { output, err } from '../format.ts';
|
|
11
|
-
|
|
12
|
-
async function registerAccount() {
|
|
13
|
-
const controller = new AbortController();
|
|
14
|
-
const timer = setTimeout(() => controller.abort(), 15_000);
|
|
15
|
-
try {
|
|
16
|
-
const res = await fetch(`${scheme(XAPI_API_HOST)}://${XAPI_API_HOST}/api/auth/register`, {
|
|
17
|
-
method: 'POST',
|
|
18
|
-
headers: { 'Content-Type': 'application/json' },
|
|
19
|
-
signal: controller.signal,
|
|
20
|
-
});
|
|
21
|
-
if (!res.ok) {
|
|
22
|
-
const text = await res.text();
|
|
23
|
-
throw new Error(`HTTP ${res.status}: ${text.slice(0, 300)}`);
|
|
24
|
-
}
|
|
25
|
-
return res.json() as Promise<{
|
|
26
|
-
apiKey: string;
|
|
27
|
-
claimCode: string;
|
|
28
|
-
claimSessionId: string;
|
|
29
|
-
claimUrl: string;
|
|
30
|
-
tweetTemplate: string;
|
|
31
|
-
user: { id: string; accountType: string };
|
|
32
|
-
}>;
|
|
33
|
-
} finally {
|
|
34
|
-
clearTimeout(timer);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export async function register(args: string[], flags: Record<string, string>) {
|
|
39
|
-
try {
|
|
40
|
-
const res = await registerAccount();
|
|
41
|
-
|
|
42
|
-
// auto-save apiKey
|
|
43
|
-
saveConfig({ apiKey: res.apiKey });
|
|
44
|
-
|
|
45
|
-
output({
|
|
46
|
-
apiKey: res.apiKey,
|
|
47
|
-
user: res.user,
|
|
48
|
-
claim: {
|
|
49
|
-
code: res.claimCode,
|
|
50
|
-
sessionId: res.claimSessionId,
|
|
51
|
-
url: res.claimUrl,
|
|
52
|
-
},
|
|
53
|
-
tweetTemplate: res.tweetTemplate,
|
|
54
|
-
note: 'apiKey saved to ~/.xapi/config.json',
|
|
55
|
-
}, flags.format as any);
|
|
56
|
-
} catch (e: any) {
|
|
57
|
-
err('register failed', e.message);
|
|
58
|
-
}
|
|
59
|
-
}
|
package/src/commands/topup.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* topup command
|
|
3
|
-
*
|
|
4
|
-
* Generates a payment URL pointing to the xapi frontend topup page.
|
|
5
|
-
* All params are optional.
|
|
6
|
-
*
|
|
7
|
-
* Usage:
|
|
8
|
-
* xapi topup [--amount <usd>] [--method stripe|x402]
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { getConfig } from '../config.ts';
|
|
12
|
-
import { output } from '../format.ts';
|
|
13
|
-
|
|
14
|
-
const TOPUP_BASE_URL = 'https://www.xapi.to/topup/payment';
|
|
15
|
-
|
|
16
|
-
export async function topup(args: string[], flags: Record<string, string>) {
|
|
17
|
-
const cfg = getConfig();
|
|
18
|
-
|
|
19
|
-
const url = new URL(TOPUP_BASE_URL);
|
|
20
|
-
|
|
21
|
-
if (cfg.apiKey) url.searchParams.set('apikey', cfg.apiKey);
|
|
22
|
-
if (flags.method) url.searchParams.set('method', flags.method);
|
|
23
|
-
|
|
24
|
-
const amountStr = flags.amount || args[0];
|
|
25
|
-
if (amountStr) {
|
|
26
|
-
const amountUsd = parseFloat(amountStr);
|
|
27
|
-
if (!isNaN(amountUsd) && amountUsd > 0) {
|
|
28
|
-
url.searchParams.set('amount', String(amountUsd));
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
output({ url: url.toString() }, flags.format as any);
|
|
33
|
-
}
|
package/src/config.ts
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Config management
|
|
3
|
-
* Only apiKey is user-configurable. Host is built-in.
|
|
4
|
-
* Reads from env var XAPI_KEY or ~/.xapi/config.json
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync, chmodSync } from 'fs';
|
|
8
|
-
import { err } from './format.ts';
|
|
9
|
-
import { homedir } from 'os';
|
|
10
|
-
import { join } from 'path';
|
|
11
|
-
|
|
12
|
-
export const XAPI_ACTION_HOST = process.env.XAPI_ACTION_HOST || 'action.xapi.to'; // action service (capabilities + APIs)
|
|
13
|
-
export const XAPI_API_HOST = process.env.XAPI_API_HOST || 'api.xapi.to'; // auth + agent API
|
|
14
|
-
|
|
15
|
-
/** Returns https:// for remote hosts, http:// for localhost */
|
|
16
|
-
export function scheme(host: string): string {
|
|
17
|
-
return host.startsWith('localhost') || host.startsWith('127.') ? 'http' : 'https';
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export interface XapiConfig {
|
|
21
|
-
actionHost: string;
|
|
22
|
-
apiKey?: string;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const CONFIG_DIR = join(homedir(), '.xapi');
|
|
26
|
-
const CONFIG_FILE = join(CONFIG_DIR, 'config.json');
|
|
27
|
-
|
|
28
|
-
function loadFileConfig(): { apiKey?: string } {
|
|
29
|
-
if (!existsSync(CONFIG_FILE)) return {};
|
|
30
|
-
try {
|
|
31
|
-
return JSON.parse(readFileSync(CONFIG_FILE, 'utf-8'));
|
|
32
|
-
} catch {
|
|
33
|
-
return {};
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function getConfig(): XapiConfig {
|
|
38
|
-
const file = loadFileConfig();
|
|
39
|
-
return {
|
|
40
|
-
actionHost: XAPI_ACTION_HOST,
|
|
41
|
-
apiKey: process.env.XAPI_KEY || process.env.XAPI_API_KEY || file.apiKey,
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function requireApiKey(cfg: XapiConfig): void {
|
|
46
|
-
if (!cfg.apiKey) {
|
|
47
|
-
err('API key not configured', 'Run "npx xapi-to register" to create an account, or "npx xapi-to config set apiKey=<key>" to set an existing key.');
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export function saveConfig(updates: { apiKey?: string }): void {
|
|
52
|
-
const current = loadFileConfig();
|
|
53
|
-
const merged = { ...current, ...updates };
|
|
54
|
-
if (!existsSync(CONFIG_DIR)) mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
|
|
55
|
-
writeFileSync(CONFIG_FILE, JSON.stringify(merged, null, 2), { mode: 0o600 });
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export function showConfig(): void {
|
|
59
|
-
const cfg = getConfig();
|
|
60
|
-
const file = loadFileConfig();
|
|
61
|
-
console.log(JSON.stringify({
|
|
62
|
-
actionHost: cfg.actionHost,
|
|
63
|
-
apiKey: cfg.apiKey ? `${cfg.apiKey.slice(0, 8)}...` : undefined,
|
|
64
|
-
source: {
|
|
65
|
-
apiKey: (process.env.XAPI_KEY || process.env.XAPI_API_KEY) ? 'env' : file.apiKey ? 'file' : 'none',
|
|
66
|
-
},
|
|
67
|
-
configFile: CONFIG_FILE,
|
|
68
|
-
}, null, 2));
|
|
69
|
-
}
|
package/src/format.ts
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Output formatting
|
|
3
|
-
* Supports: json (default, machine-readable), pretty (human-readable), table
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export type OutputFormat = 'json' | 'pretty' | 'table';
|
|
7
|
-
|
|
8
|
-
export function getFormat(): OutputFormat {
|
|
9
|
-
const f = process.env.XAPI_OUTPUT || 'json';
|
|
10
|
-
if (f === 'pretty' || f === 'table') return f;
|
|
11
|
-
return 'json';
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function output(data: unknown, format?: OutputFormat): void {
|
|
15
|
-
const fmt = format || getFormat();
|
|
16
|
-
if (fmt === 'json') {
|
|
17
|
-
console.log(JSON.stringify(data));
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
if (fmt === 'pretty') {
|
|
21
|
-
console.log(JSON.stringify(data, null, 2));
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
// table: try to render arrays of objects as a table
|
|
25
|
-
if (fmt === 'table' && Array.isArray(data)) {
|
|
26
|
-
printTable(data as Record<string, unknown>[]);
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
console.log(JSON.stringify(data, null, 2));
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function printTable(rows: Record<string, unknown>[]): void {
|
|
33
|
-
if (rows.length === 0) {
|
|
34
|
-
console.log('(empty)');
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
const keys = Object.keys(rows[0]);
|
|
38
|
-
const widths = keys.map(k =>
|
|
39
|
-
Math.min(40, Math.max(k.length, ...rows.map(r => String(r[k] ?? '').length)))
|
|
40
|
-
);
|
|
41
|
-
const sep = widths.map(w => '-'.repeat(w)).join(' ');
|
|
42
|
-
const header = keys.map((k, i) => k.padEnd(widths[i])).join(' ');
|
|
43
|
-
console.log(header);
|
|
44
|
-
console.log(sep);
|
|
45
|
-
for (const row of rows) {
|
|
46
|
-
const line = keys.map((k, i) => String(row[k] ?? '').slice(0, widths[i]).padEnd(widths[i])).join(' ');
|
|
47
|
-
console.log(line);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export function err(msg: string, detail?: unknown): never {
|
|
52
|
-
if (process.stderr.isTTY) {
|
|
53
|
-
console.error(`Error: ${msg}`);
|
|
54
|
-
if (detail !== undefined) console.error(` ${detail}`);
|
|
55
|
-
} else {
|
|
56
|
-
const out: Record<string, unknown> = { error: msg };
|
|
57
|
-
if (detail !== undefined) out.detail = detail;
|
|
58
|
-
console.error(JSON.stringify(out));
|
|
59
|
-
}
|
|
60
|
-
process.exit(1);
|
|
61
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
/**
|
|
3
|
-
* xapi CLI - agent-friendly command-line interface for xapi
|
|
4
|
-
*
|
|
5
|
-
* Usage:
|
|
6
|
-
* xapi list [--source capability|api] [--page N] [--page-size N] [--category X]
|
|
7
|
-
* xapi search <query> [--source capability|api] [--category X] [--page N] [--page-size N]
|
|
8
|
-
* xapi categories [--source capability|api]
|
|
9
|
-
* xapi services [--page N] [--page-size N] [--category X]
|
|
10
|
-
* xapi get <id> [--code curl|py|js|ts|go]
|
|
11
|
-
* xapi call <id> --input '{"k":"v"}' [--code curl|py|js|ts|go]
|
|
12
|
-
*
|
|
13
|
-
* xapi config show
|
|
14
|
-
* xapi config set apiKey=<key>
|
|
15
|
-
* xapi health
|
|
16
|
-
*
|
|
17
|
-
* Global flags:
|
|
18
|
-
* --format json|pretty|table output format (default: json)
|
|
19
|
-
* --help show help
|
|
20
|
-
*
|
|
21
|
-
* Env vars:
|
|
22
|
-
* XAPI_KEY API key
|
|
23
|
-
* XAPI_ACTION_HOST Action service host (default: action.xapi.to)
|
|
24
|
-
* XAPI_OUTPUT default output format (json|pretty|table)
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
import * as actionCmds from './commands/action.ts';
|
|
28
|
-
import * as cfgCmds from './commands/config.ts';
|
|
29
|
-
import * as regCmds from './commands/register.ts';
|
|
30
|
-
import * as topupCmds from './commands/topup.ts';
|
|
31
|
-
import * as balanceCmds from './commands/balance.ts';
|
|
32
|
-
import * as oauthCmds from './commands/oauth.ts';
|
|
33
|
-
const { OAUTH_HELP } = oauthCmds;
|
|
34
|
-
|
|
35
|
-
// ── Argument parser ───────────────────────────────────────────────────────────
|
|
36
|
-
|
|
37
|
-
interface ParsedArgs {
|
|
38
|
-
positional: string[];
|
|
39
|
-
flags: Record<string, string>;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function parseArgs(argv: string[]): ParsedArgs {
|
|
43
|
-
const positional: string[] = [];
|
|
44
|
-
const flags: Record<string, string> = {};
|
|
45
|
-
let i = 0;
|
|
46
|
-
while (i < argv.length) {
|
|
47
|
-
const arg = argv[i];
|
|
48
|
-
if (arg.startsWith('--')) {
|
|
49
|
-
const key = arg.slice(2);
|
|
50
|
-
const next = argv[i + 1];
|
|
51
|
-
if (next && !next.startsWith('--')) {
|
|
52
|
-
flags[key] = next;
|
|
53
|
-
i += 2;
|
|
54
|
-
} else {
|
|
55
|
-
flags[key] = 'true';
|
|
56
|
-
i++;
|
|
57
|
-
}
|
|
58
|
-
} else {
|
|
59
|
-
positional.push(arg);
|
|
60
|
-
i++;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return { positional, flags };
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// ── Help ──────────────────────────────────────────────────────────────────────
|
|
67
|
-
|
|
68
|
-
const HELP = `xapi - agent-friendly CLI for xapi
|
|
69
|
-
|
|
70
|
-
USAGE
|
|
71
|
-
xapi <command> [args] [flags]
|
|
72
|
-
|
|
73
|
-
COMMANDS
|
|
74
|
-
list List all actions
|
|
75
|
-
--source capability|api Filter by source type
|
|
76
|
-
--page N --page-size N Pagination
|
|
77
|
-
--category <name> Filter by category
|
|
78
|
-
--service-id <id> Filter by service
|
|
79
|
-
search <query> Search actions by keyword
|
|
80
|
-
--source capability|api Filter by source type
|
|
81
|
-
--category <name> Filter by category
|
|
82
|
-
--page N --page-size N Pagination
|
|
83
|
-
categories List all action categories
|
|
84
|
-
--source capability|api Filter by source type
|
|
85
|
-
services List all services
|
|
86
|
-
--page N --page-size N Pagination
|
|
87
|
-
--category <name> Filter by category
|
|
88
|
-
get <id> [--method GET|POST|...] Get action schema (filter by HTTP method)
|
|
89
|
-
--code <target> Generate code snippet (curl, py, js, ts, go)
|
|
90
|
-
call <id> --input '{"key":"val"}' Execute an action
|
|
91
|
-
--method GET|POST|... Override HTTP method
|
|
92
|
-
--code <target> Generate code snippet instead of executing
|
|
93
|
-
Variants: python.requests, python.httpx, javascript.fetch, javascript.axios
|
|
94
|
-
|
|
95
|
-
oauth bind [--provider twitter] Bind Twitter OAuth to your API key
|
|
96
|
-
oauth status List current OAuth bindings
|
|
97
|
-
oauth unbind <binding-id> Remove an OAuth binding
|
|
98
|
-
oauth providers List available OAuth providers
|
|
99
|
-
|
|
100
|
-
register Create a new user account (apiKey saved automatically)
|
|
101
|
-
balance Show current account balance
|
|
102
|
-
topup [--amount <usd>] [--method stripe|x402] Generate payment URL
|
|
103
|
-
|
|
104
|
-
health Check backend connectivity
|
|
105
|
-
|
|
106
|
-
config show Show current config
|
|
107
|
-
config set apiKey=<key> Save API key to ~/.xapi/config.json
|
|
108
|
-
|
|
109
|
-
GLOBAL FLAGS
|
|
110
|
-
--format json|pretty|table Output format (default: json)
|
|
111
|
-
--help Show help (use with a command for details, e.g. xapi get --help)
|
|
112
|
-
|
|
113
|
-
ENV VARS
|
|
114
|
-
XAPI_KEY API key (header: XAPI-Key)
|
|
115
|
-
XAPI_ACTION_HOST Action service host (default: action.xapi.to)
|
|
116
|
-
XAPI_OUTPUT Default output format
|
|
117
|
-
|
|
118
|
-
EXAMPLES
|
|
119
|
-
xapi register
|
|
120
|
-
xapi list --format table
|
|
121
|
-
xapi list --source capability
|
|
122
|
-
xapi search twitter --source api
|
|
123
|
-
xapi get twitter.tweet_detail
|
|
124
|
-
xapi get twitter.tweet_detail --code curl
|
|
125
|
-
xapi get twitter.tweet_detail --code py --format pretty
|
|
126
|
-
xapi call twitter.tweet_detail --input '{"tweet_id":"1234567890"}'
|
|
127
|
-
xapi call twitter.tweet_detail --input '{"tweet_id":"1234567890"}' --code python
|
|
128
|
-
xapi categories
|
|
129
|
-
xapi services --format table
|
|
130
|
-
xapi config set apiKey=xapi_abc123
|
|
131
|
-
xapi health
|
|
132
|
-
`;
|
|
133
|
-
|
|
134
|
-
// ── Router ────────────────────────────────────────────────────────────────────
|
|
135
|
-
|
|
136
|
-
async function main() {
|
|
137
|
-
const { positional, flags } = parseArgs(process.argv.slice(2));
|
|
138
|
-
|
|
139
|
-
if (positional.length === 0) {
|
|
140
|
-
console.log(HELP);
|
|
141
|
-
process.exit(0);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// inject format from flag into env so format.ts picks it up
|
|
145
|
-
if (flags.format) process.env.XAPI_OUTPUT = flags.format;
|
|
146
|
-
|
|
147
|
-
const [cmd, ...rest] = positional;
|
|
148
|
-
|
|
149
|
-
switch (cmd) {
|
|
150
|
-
// ── Action commands (top-level) ──
|
|
151
|
-
case 'list': return actionCmds.actionList(rest, flags);
|
|
152
|
-
case 'search': return actionCmds.actionSearch(rest, flags);
|
|
153
|
-
case 'categories': return actionCmds.actionCategories(rest, flags);
|
|
154
|
-
case 'services': return actionCmds.actionServices(rest, flags);
|
|
155
|
-
case 'get': return actionCmds.actionGet(rest, flags);
|
|
156
|
-
case 'call': return actionCmds.actionCall(rest, flags);
|
|
157
|
-
|
|
158
|
-
// ── OAuth commands ──
|
|
159
|
-
case 'oauth': {
|
|
160
|
-
if (flags.help || rest.length === 0) {
|
|
161
|
-
console.log(OAUTH_HELP);
|
|
162
|
-
process.exit(0);
|
|
163
|
-
}
|
|
164
|
-
const [subCmd, ...subRest] = rest;
|
|
165
|
-
switch (subCmd) {
|
|
166
|
-
case 'bind': return oauthCmds.oauthBind(subRest, flags);
|
|
167
|
-
case 'status': return oauthCmds.oauthStatus(subRest, flags);
|
|
168
|
-
case 'unbind': return oauthCmds.oauthUnbind(subRest, flags);
|
|
169
|
-
case 'providers': return oauthCmds.oauthProviders(subRest, flags);
|
|
170
|
-
default:
|
|
171
|
-
console.error(JSON.stringify({ error: `unknown oauth command: ${subCmd}`, hint: 'valid commands: bind, status, unbind, providers' }));
|
|
172
|
-
process.exit(1);
|
|
173
|
-
}
|
|
174
|
-
break;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// ── Account commands ──
|
|
178
|
-
case 'register': return regCmds.register(rest, flags);
|
|
179
|
-
case 'balance': return balanceCmds.balance(rest, flags);
|
|
180
|
-
case 'topup': return topupCmds.topup(rest, flags);
|
|
181
|
-
case 'health': return cfgCmds.configHealth(rest, flags);
|
|
182
|
-
|
|
183
|
-
// ── Config commands ──
|
|
184
|
-
case 'config': {
|
|
185
|
-
const [subCmd, ...subRest] = rest;
|
|
186
|
-
switch (subCmd) {
|
|
187
|
-
case 'show': return cfgCmds.configShow(subRest, flags);
|
|
188
|
-
case 'set': return cfgCmds.configSet(subRest, flags);
|
|
189
|
-
default:
|
|
190
|
-
console.error(JSON.stringify({ error: `unknown config command: ${subCmd}` }));
|
|
191
|
-
process.exit(1);
|
|
192
|
-
}
|
|
193
|
-
break;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
default:
|
|
197
|
-
console.error(JSON.stringify({ error: `unknown command: ${cmd}`, hint: 'run xapi --help' }));
|
|
198
|
-
process.exit(1);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
main().catch(e => {
|
|
203
|
-
console.error(JSON.stringify({ error: 'fatal', message: e.message }));
|
|
204
|
-
process.exit(1);
|
|
205
|
-
});
|