xapi-to 0.1.14 → 0.1.16
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 +39 -38
- package/dist/index.js +293 -90
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -21,25 +21,25 @@ The published CLI runs on Node.js 18+. Bun is only required for local source dev
|
|
|
21
21
|
|
|
22
22
|
```bash
|
|
23
23
|
# 1. Register a new account (apiKey saved automatically)
|
|
24
|
-
xapi register
|
|
24
|
+
xapi-to register
|
|
25
25
|
|
|
26
26
|
# 1b. Or register with an inviter's referral code (please replace xapito to your referral code)
|
|
27
|
-
xapi register --referral-code xapito
|
|
27
|
+
xapi-to register --referral-code xapito
|
|
28
28
|
|
|
29
29
|
# 2. Or set an existing key
|
|
30
|
-
xapi config set apiKey=sk-xxx
|
|
30
|
+
xapi-to config set apiKey=sk-xxx
|
|
31
31
|
|
|
32
32
|
# 3. Or via env var
|
|
33
33
|
export XAPI_KEY=sk-xxx
|
|
34
34
|
|
|
35
35
|
# 4. Verify connectivity
|
|
36
|
-
xapi config health
|
|
36
|
+
xapi-to config health
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
## Usage
|
|
40
40
|
|
|
41
41
|
```
|
|
42
|
-
xapi <command> [args] [flags]
|
|
42
|
+
xapi-to <command> [args] [flags]
|
|
43
43
|
```
|
|
44
44
|
|
|
45
45
|
### Action Commands
|
|
@@ -47,23 +47,23 @@ xapi <command> [args] [flags]
|
|
|
47
47
|
Unified interface for capabilities (built-in) and APIs (third-party). Use `--source capability|api` to filter.
|
|
48
48
|
|
|
49
49
|
```bash
|
|
50
|
-
xapi list # list all actions
|
|
51
|
-
xapi list --source capability # only built-in capabilities
|
|
52
|
-
xapi list --source api --category DeFi # filter by source and category
|
|
53
|
-
xapi list --page 2 --page-size 20 # pagination
|
|
54
|
-
xapi list --service-id <id> # filter by service
|
|
50
|
+
xapi-to list # list all actions
|
|
51
|
+
xapi-to list --source capability # only built-in capabilities
|
|
52
|
+
xapi-to list --source api --category DeFi # filter by source and category
|
|
53
|
+
xapi-to list --page 2 --page-size 20 # pagination
|
|
54
|
+
xapi-to list --service-id <id> # filter by service
|
|
55
55
|
|
|
56
|
-
xapi search "twitter" # search by keyword
|
|
57
|
-
xapi search "token price" --source api # search APIs only
|
|
56
|
+
xapi-to search "twitter" # search by keyword
|
|
57
|
+
xapi-to search "token price" --source api # search APIs only
|
|
58
58
|
|
|
59
|
-
xapi categories # list all categories
|
|
60
|
-
xapi categories --source capability # categories for capabilities only
|
|
59
|
+
xapi-to categories # list all categories
|
|
60
|
+
xapi-to categories --source capability # categories for capabilities only
|
|
61
61
|
|
|
62
|
-
xapi services # list all services
|
|
63
|
-
xapi services --category Social --page-size 10 # filter and paginate
|
|
62
|
+
xapi-to services # list all services
|
|
63
|
+
xapi-to services --category Social --page-size 10 # filter and paginate
|
|
64
64
|
|
|
65
|
-
xapi get twitter.tweet_detail # get action schema
|
|
66
|
-
xapi call twitter.tweet_detail --input '{"tweet_id":"1234567890"}' # execute
|
|
65
|
+
xapi-to get twitter.tweet_detail # get action schema
|
|
66
|
+
xapi-to call twitter.tweet_detail --input '{"tweet_id":"1234567890"}' # execute
|
|
67
67
|
```
|
|
68
68
|
|
|
69
69
|
### OAuth
|
|
@@ -71,30 +71,30 @@ xapi call twitter.tweet_detail --input '{"tweet_id":"1234567890"}' # execute
|
|
|
71
71
|
Bind third-party OAuth accounts (e.g. Twitter) to your API key.
|
|
72
72
|
|
|
73
73
|
```bash
|
|
74
|
-
xapi oauth bind --provider twitter # bind Twitter account
|
|
75
|
-
xapi oauth status # list current bindings
|
|
76
|
-
xapi oauth unbind <binding-id> # remove a binding
|
|
77
|
-
xapi oauth providers # list available providers
|
|
74
|
+
xapi-to oauth bind --provider twitter # bind Twitter account
|
|
75
|
+
xapi-to oauth status # list current bindings
|
|
76
|
+
xapi-to oauth unbind <binding-id> # remove a binding
|
|
77
|
+
xapi-to oauth providers # list available providers
|
|
78
78
|
```
|
|
79
79
|
|
|
80
80
|
### Account
|
|
81
81
|
|
|
82
82
|
```bash
|
|
83
|
-
xapi register # create account, saves apiKey automatically
|
|
84
|
-
xapi register --referral-code xapito # register with an inviter's referral code (please replace xapito to your referral code)
|
|
85
|
-
xapi register xapito # positional shorthand for --referral-code
|
|
86
|
-
xapi balance # show USD balance
|
|
87
|
-
xapi topup # generate payment URL
|
|
88
|
-
xapi topup --method stripe --amount 10 # stripe, $10
|
|
89
|
-
xapi topup --method x402 # x402 (USDC on Base)
|
|
83
|
+
xapi-to register # create account, saves apiKey automatically
|
|
84
|
+
xapi-to register --referral-code xapito # register with an inviter's referral code (please replace xapito to your referral code)
|
|
85
|
+
xapi-to register xapito # positional shorthand for --referral-code
|
|
86
|
+
xapi-to balance # show USD balance
|
|
87
|
+
xapi-to topup # generate payment URL
|
|
88
|
+
xapi-to topup --method stripe --amount 10 # stripe, $10
|
|
89
|
+
xapi-to topup --method x402 # x402 (USDC on Base)
|
|
90
90
|
```
|
|
91
91
|
|
|
92
92
|
### Config
|
|
93
93
|
|
|
94
94
|
```bash
|
|
95
|
-
xapi config show # show current config
|
|
96
|
-
xapi config set apiKey=sk-xxx # save API key
|
|
97
|
-
xapi config health # check backend connectivity
|
|
95
|
+
xapi-to config show # show current config
|
|
96
|
+
xapi-to config set apiKey=sk-xxx # save API key
|
|
97
|
+
xapi-to config health # check backend connectivity
|
|
98
98
|
```
|
|
99
99
|
|
|
100
100
|
## Workflow: Always GET before CALL
|
|
@@ -103,13 +103,13 @@ Before calling any action, always read its schema first to understand required p
|
|
|
103
103
|
|
|
104
104
|
```bash
|
|
105
105
|
# 1. Find the action
|
|
106
|
-
xapi search "twitter"
|
|
106
|
+
xapi-to search "twitter"
|
|
107
107
|
|
|
108
108
|
# 2. Read its schema
|
|
109
|
-
xapi get twitter.tweet_detail
|
|
109
|
+
xapi-to get twitter.tweet_detail
|
|
110
110
|
|
|
111
111
|
# 3. Call with correct parameters
|
|
112
|
-
xapi call twitter.tweet_detail --input '{"tweet_id":"1234567890"}'
|
|
112
|
+
xapi-to call twitter.tweet_detail --input '{"tweet_id":"1234567890"}'
|
|
113
113
|
```
|
|
114
114
|
|
|
115
115
|
## Output Formats
|
|
@@ -117,9 +117,9 @@ xapi call twitter.tweet_detail --input '{"tweet_id":"1234567890"}'
|
|
|
117
117
|
All output is JSON by default — designed for agent consumption.
|
|
118
118
|
|
|
119
119
|
```bash
|
|
120
|
-
xapi list --format json # default, machine-readable
|
|
121
|
-
xapi list --format pretty # pretty-printed JSON
|
|
122
|
-
xapi list --format table # human-readable table
|
|
120
|
+
xapi-to list --format json # default, machine-readable
|
|
121
|
+
xapi-to list --format pretty # pretty-printed JSON
|
|
122
|
+
xapi-to list --format table # human-readable table
|
|
123
123
|
```
|
|
124
124
|
|
|
125
125
|
## Environment Variables
|
|
@@ -140,6 +140,7 @@ Config is stored at `~/.xapi/config.json`.
|
|
|
140
140
|
| `twitter.user_by_screen_name` | Get user profile by username |
|
|
141
141
|
| `twitter.user_by_screen_names` | Batch get user profiles by usernames |
|
|
142
142
|
| `twitter.user_tweets` | Get tweets from a user |
|
|
143
|
+
| `twitter.user_tweets_and_replies` | Get tweets and replies from a user |
|
|
143
144
|
| `twitter.user_media` | Get media posts from a user |
|
|
144
145
|
| `twitter.following` | Get user following list |
|
|
145
146
|
| `twitter.followers` | Get user followers |
|
package/dist/index.js
CHANGED
|
@@ -24,12 +24,46 @@ function output(data, format) {
|
|
|
24
24
|
console.log(JSON.stringify(data, null, 2));
|
|
25
25
|
return;
|
|
26
26
|
}
|
|
27
|
-
if (fmt === "table"
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
if (fmt === "table") {
|
|
28
|
+
const rows = tableRows(data);
|
|
29
|
+
if (rows) {
|
|
30
|
+
printTable(rows);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
30
33
|
}
|
|
31
34
|
console.log(JSON.stringify(data, null, 2));
|
|
32
35
|
}
|
|
36
|
+
function tableRows(data) {
|
|
37
|
+
if (Array.isArray(data)) return normalizeRows(data, "value");
|
|
38
|
+
if (!data || typeof data !== "object") return null;
|
|
39
|
+
const obj = data;
|
|
40
|
+
const preferredKeys = ["items", "actions", "results", "services", "categories", "bindings", "providers"];
|
|
41
|
+
for (const key of preferredKeys) {
|
|
42
|
+
const value = obj[key];
|
|
43
|
+
if (Array.isArray(value)) return normalizeRows(value, singularKey(key));
|
|
44
|
+
}
|
|
45
|
+
const firstArray = Object.entries(obj).find(([, value]) => Array.isArray(value));
|
|
46
|
+
return firstArray ? normalizeRows(firstArray[1], singularKey(firstArray[0])) : null;
|
|
47
|
+
}
|
|
48
|
+
function normalizeRows(rows, primitiveKey) {
|
|
49
|
+
return rows.map((row) => {
|
|
50
|
+
if (row && typeof row === "object" && !Array.isArray(row)) {
|
|
51
|
+
return row;
|
|
52
|
+
}
|
|
53
|
+
return { [primitiveKey]: row };
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
function singularKey(key) {
|
|
57
|
+
if (key === "categories") return "category";
|
|
58
|
+
if (key.endsWith("ies")) return `${key.slice(0, -3)}y`;
|
|
59
|
+
if (key.endsWith("s")) return key.slice(0, -1);
|
|
60
|
+
return "value";
|
|
61
|
+
}
|
|
62
|
+
function formatCell(value) {
|
|
63
|
+
if (value === null || value === void 0) return "";
|
|
64
|
+
if (typeof value === "object") return JSON.stringify(value);
|
|
65
|
+
return String(value);
|
|
66
|
+
}
|
|
33
67
|
function printTable(rows) {
|
|
34
68
|
if (rows.length === 0) {
|
|
35
69
|
console.log("(empty)");
|
|
@@ -37,14 +71,14 @@ function printTable(rows) {
|
|
|
37
71
|
}
|
|
38
72
|
const keys = Object.keys(rows[0]);
|
|
39
73
|
const widths = keys.map(
|
|
40
|
-
(k) => Math.min(40, Math.max(k.length, ...rows.map((r) =>
|
|
74
|
+
(k) => Math.min(40, Math.max(k.length, ...rows.map((r) => formatCell(r[k]).length)))
|
|
41
75
|
);
|
|
42
76
|
const sep = widths.map((w) => "-".repeat(w)).join(" ");
|
|
43
77
|
const header = keys.map((k, i) => k.padEnd(widths[i])).join(" ");
|
|
44
78
|
console.log(header);
|
|
45
79
|
console.log(sep);
|
|
46
80
|
for (const row of rows) {
|
|
47
|
-
const line = keys.map((k, i) =>
|
|
81
|
+
const line = keys.map((k, i) => formatCell(row[k]).slice(0, widths[i]).padEnd(widths[i])).join(" ");
|
|
48
82
|
console.log(line);
|
|
49
83
|
}
|
|
50
84
|
}
|
|
@@ -118,10 +152,17 @@ async function request(url, options, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
|
118
152
|
try {
|
|
119
153
|
const res = await fetch(url, { ...options, signal: controller.signal });
|
|
120
154
|
if (!res.ok) {
|
|
121
|
-
const
|
|
122
|
-
throw new Error(`HTTP ${res.status}: ${
|
|
155
|
+
const text2 = await res.text();
|
|
156
|
+
throw new Error(`HTTP ${res.status}: ${text2.slice(0, 300)}`);
|
|
157
|
+
}
|
|
158
|
+
if (res.status === 204) {
|
|
159
|
+
return void 0;
|
|
123
160
|
}
|
|
124
|
-
const
|
|
161
|
+
const text = await res.text();
|
|
162
|
+
if (!text.trim()) {
|
|
163
|
+
return void 0;
|
|
164
|
+
}
|
|
165
|
+
const body = JSON.parse(text);
|
|
125
166
|
if (body && typeof body === "object" && "success" in body && body.success === false) {
|
|
126
167
|
const data = body.data;
|
|
127
168
|
if (data?.statusCode === 401 || data?.error === "Unauthorized") {
|
|
@@ -131,7 +172,7 @@ async function request(url, options, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
|
131
172
|
}
|
|
132
173
|
if (data?.error === "OAuth Required" || data?.statusCode === 403 && data?.message?.includes("OAuth")) {
|
|
133
174
|
throw new Error(
|
|
134
|
-
(data.message || "OAuth authorization required") + '. Run "xapi oauth bind" to connect your account.'
|
|
175
|
+
(data.message || "OAuth authorization required") + '. Run "xapi-to oauth bind" to connect your account.'
|
|
135
176
|
);
|
|
136
177
|
}
|
|
137
178
|
}
|
|
@@ -249,13 +290,15 @@ async function listOAuthProviders(apiHost) {
|
|
|
249
290
|
{ method: "GET", headers: { "Content-Type": "application/json" } }
|
|
250
291
|
);
|
|
251
292
|
}
|
|
252
|
-
async function initiateOAuth(apiKeyId, providerId, jwtToken, apiHost) {
|
|
293
|
+
async function initiateOAuth(apiKeyId, providerId, jwtToken, apiHost, scopes) {
|
|
294
|
+
const body = { apiKeyId, providerId };
|
|
295
|
+
if (scopes) body.scopes = scopes;
|
|
253
296
|
return request(
|
|
254
297
|
`${scheme(apiHost)}://${apiHost}/api/oauth/authorize`,
|
|
255
298
|
{
|
|
256
299
|
method: "POST",
|
|
257
300
|
headers: jwtHeaders(jwtToken),
|
|
258
|
-
body: JSON.stringify(
|
|
301
|
+
body: JSON.stringify(body)
|
|
259
302
|
}
|
|
260
303
|
);
|
|
261
304
|
}
|
|
@@ -266,10 +309,11 @@ async function listOAuthBindings(jwtToken, apiHost) {
|
|
|
266
309
|
);
|
|
267
310
|
}
|
|
268
311
|
async function deleteOAuthBinding(bindingId, jwtToken, apiHost) {
|
|
269
|
-
|
|
312
|
+
const result = await request(
|
|
270
313
|
`${scheme(apiHost)}://${apiHost}/api/oauth/bindings/${bindingId}`,
|
|
271
314
|
{ method: "DELETE", headers: jwtHeaders(jwtToken) }
|
|
272
315
|
);
|
|
316
|
+
return result ?? { success: true };
|
|
273
317
|
}
|
|
274
318
|
|
|
275
319
|
// src/codegen.ts
|
|
@@ -528,10 +572,10 @@ function generateCode(target, params) {
|
|
|
528
572
|
|
|
529
573
|
// src/commands/action.ts
|
|
530
574
|
var VALID_SOURCES = ["capability", "api"];
|
|
531
|
-
var LIST_HELP = `xapi list - List all actions
|
|
575
|
+
var LIST_HELP = `xapi-to list - List all actions
|
|
532
576
|
|
|
533
577
|
USAGE
|
|
534
|
-
xapi list [flags]
|
|
578
|
+
xapi-to list [flags]
|
|
535
579
|
|
|
536
580
|
FLAGS
|
|
537
581
|
--source capability|api Filter by source type
|
|
@@ -542,14 +586,14 @@ FLAGS
|
|
|
542
586
|
--format json|pretty|table Output format
|
|
543
587
|
|
|
544
588
|
EXAMPLES
|
|
545
|
-
xapi list
|
|
546
|
-
xapi list --source api --format table
|
|
547
|
-
xapi list --category social --page 2
|
|
589
|
+
xapi-to list
|
|
590
|
+
xapi-to list --source api --format table
|
|
591
|
+
xapi-to list --category social --page 2
|
|
548
592
|
`;
|
|
549
|
-
var SEARCH_HELP = `xapi search - Search actions by keyword
|
|
593
|
+
var SEARCH_HELP = `xapi-to search - Search actions by keyword
|
|
550
594
|
|
|
551
595
|
USAGE
|
|
552
|
-
xapi search <query> [flags]
|
|
596
|
+
xapi-to search <query> [flags]
|
|
553
597
|
|
|
554
598
|
FLAGS
|
|
555
599
|
--source capability|api Filter by source type
|
|
@@ -559,14 +603,14 @@ FLAGS
|
|
|
559
603
|
--format json|pretty|table Output format
|
|
560
604
|
|
|
561
605
|
EXAMPLES
|
|
562
|
-
xapi search twitter
|
|
563
|
-
xapi search "tweet detail" --source api
|
|
564
|
-
xapi search weather --category utility --format table
|
|
606
|
+
xapi-to search twitter
|
|
607
|
+
xapi-to search "tweet detail" --source api
|
|
608
|
+
xapi-to search weather --category utility --format table
|
|
565
609
|
`;
|
|
566
|
-
var GET_HELP = `xapi get - Get action schema
|
|
610
|
+
var GET_HELP = `xapi-to get - Get action schema
|
|
567
611
|
|
|
568
612
|
USAGE
|
|
569
|
-
xapi get <id> [flags]
|
|
613
|
+
xapi-to get <id> [flags]
|
|
570
614
|
|
|
571
615
|
FLAGS
|
|
572
616
|
--method GET|POST|... Filter by HTTP method
|
|
@@ -591,15 +635,15 @@ CODE TARGETS
|
|
|
591
635
|
go Go (net/http)
|
|
592
636
|
|
|
593
637
|
EXAMPLES
|
|
594
|
-
xapi get twitter.tweet_detail
|
|
595
|
-
xapi get twitter.tweet_detail --method POST
|
|
596
|
-
xapi get twitter.tweet_detail --code curl
|
|
597
|
-
xapi get twitter.tweet_detail --code python.httpx --format pretty
|
|
638
|
+
xapi-to get twitter.tweet_detail
|
|
639
|
+
xapi-to get twitter.tweet_detail --method POST
|
|
640
|
+
xapi-to get twitter.tweet_detail --code curl
|
|
641
|
+
xapi-to get twitter.tweet_detail --code python.httpx --format pretty
|
|
598
642
|
`;
|
|
599
|
-
var CALL_HELP = `xapi call - Execute an action
|
|
643
|
+
var CALL_HELP = `xapi-to call - Execute an action
|
|
600
644
|
|
|
601
645
|
USAGE
|
|
602
|
-
xapi call <id> --input '{"key":"val"}' [flags]
|
|
646
|
+
xapi-to call <id> --input '{"key":"val"}' [flags]
|
|
603
647
|
|
|
604
648
|
FLAGS
|
|
605
649
|
--input <json> Input payload as JSON (required for execution)
|
|
@@ -625,9 +669,9 @@ CODE TARGETS
|
|
|
625
669
|
go Go (net/http)
|
|
626
670
|
|
|
627
671
|
EXAMPLES
|
|
628
|
-
xapi call twitter.tweet_detail --input '{"tweet_id":"1234567890"}'
|
|
629
|
-
xapi call twitter.tweet_detail --input '{"tweet_id":"123"}' --code py
|
|
630
|
-
xapi call twitter.tweet_detail --input '{"tweet_id":"123"}' --code curl --format pretty
|
|
672
|
+
xapi-to call twitter.tweet_detail --input '{"tweet_id":"1234567890"}'
|
|
673
|
+
xapi-to call twitter.tweet_detail --input '{"tweet_id":"123"}' --code py
|
|
674
|
+
xapi-to call twitter.tweet_detail --input '{"tweet_id":"123"}' --code curl --format pretty
|
|
631
675
|
`;
|
|
632
676
|
function showHelpIfRequested(flags, helpText) {
|
|
633
677
|
if (flags.help) {
|
|
@@ -659,6 +703,7 @@ function getSource(flags) {
|
|
|
659
703
|
async function actionList2(args, flags) {
|
|
660
704
|
showHelpIfRequested(flags, LIST_HELP);
|
|
661
705
|
const cfg = getConfig();
|
|
706
|
+
const fmt = flags.format || getFormat();
|
|
662
707
|
try {
|
|
663
708
|
const res = await actionList(cfg, {
|
|
664
709
|
source: getSource(flags),
|
|
@@ -668,7 +713,7 @@ async function actionList2(args, flags) {
|
|
|
668
713
|
service_id: flags["service-id"]
|
|
669
714
|
});
|
|
670
715
|
const actions = res.actions || [];
|
|
671
|
-
if (
|
|
716
|
+
if (fmt === "table") {
|
|
672
717
|
output(actions.map((a) => ({
|
|
673
718
|
id: a.id,
|
|
674
719
|
method: a.method ?? "",
|
|
@@ -688,8 +733,9 @@ async function actionList2(args, flags) {
|
|
|
688
733
|
async function actionSearch2(args, flags) {
|
|
689
734
|
showHelpIfRequested(flags, SEARCH_HELP);
|
|
690
735
|
const query = args[0];
|
|
691
|
-
if (!query) err("usage: xapi search <query>");
|
|
736
|
+
if (!query) err("usage: xapi-to search <query>");
|
|
692
737
|
const cfg = getConfig();
|
|
738
|
+
const fmt = flags.format || getFormat();
|
|
693
739
|
try {
|
|
694
740
|
const res = await actionSearch(query, cfg, {
|
|
695
741
|
source: getSource(flags),
|
|
@@ -698,7 +744,7 @@ async function actionSearch2(args, flags) {
|
|
|
698
744
|
page_size: flags["page-size"] ? parseInt(flags["page-size"]) : void 0
|
|
699
745
|
});
|
|
700
746
|
const results = res.results || [];
|
|
701
|
-
if (
|
|
747
|
+
if (fmt === "table") {
|
|
702
748
|
output(results.map((a) => ({
|
|
703
749
|
id: a.id,
|
|
704
750
|
method: a.method ?? "",
|
|
@@ -717,9 +763,10 @@ async function actionSearch2(args, flags) {
|
|
|
717
763
|
}
|
|
718
764
|
async function actionCategories2(args, flags) {
|
|
719
765
|
const cfg = getConfig();
|
|
766
|
+
const fmt = flags.format || getFormat();
|
|
720
767
|
try {
|
|
721
768
|
const res = await actionCategories(cfg, { source: getSource(flags) });
|
|
722
|
-
if (
|
|
769
|
+
if (fmt === "table") {
|
|
723
770
|
output(res.categories.map((c) => ({ category: c })), "table");
|
|
724
771
|
} else {
|
|
725
772
|
output(res, flags.format);
|
|
@@ -730,6 +777,7 @@ async function actionCategories2(args, flags) {
|
|
|
730
777
|
}
|
|
731
778
|
async function actionServices2(args, flags) {
|
|
732
779
|
const cfg = getConfig();
|
|
780
|
+
const fmt = flags.format || getFormat();
|
|
733
781
|
try {
|
|
734
782
|
const res = await actionServices(cfg, {
|
|
735
783
|
page: flags.page ? parseInt(flags.page) : void 0,
|
|
@@ -737,7 +785,7 @@ async function actionServices2(args, flags) {
|
|
|
737
785
|
category: flags.category
|
|
738
786
|
});
|
|
739
787
|
const services = res.services || [];
|
|
740
|
-
if (
|
|
788
|
+
if (fmt === "table") {
|
|
741
789
|
output(services.map((s) => ({
|
|
742
790
|
id: s.id,
|
|
743
791
|
name: s.name ?? "",
|
|
@@ -756,7 +804,7 @@ async function actionServices2(args, flags) {
|
|
|
756
804
|
async function actionGet2(args, flags) {
|
|
757
805
|
showHelpIfRequested(flags, GET_HELP);
|
|
758
806
|
const id = args[0];
|
|
759
|
-
if (!id) err("usage: xapi get <id> [--method GET|POST|DELETE|...]");
|
|
807
|
+
if (!id) err("usage: xapi-to get <id> [--method GET|POST|DELETE|...]");
|
|
760
808
|
if (flags.code) validateCodeFlag(flags);
|
|
761
809
|
const cfg = getConfig();
|
|
762
810
|
try {
|
|
@@ -788,7 +836,7 @@ async function actionGet2(args, flags) {
|
|
|
788
836
|
async function actionCall2(args, flags) {
|
|
789
837
|
showHelpIfRequested(flags, CALL_HELP);
|
|
790
838
|
const id = args[0];
|
|
791
|
-
if (!id) err(`usage: xapi call <id> --input '{"key":"val"}'`);
|
|
839
|
+
if (!id) err(`usage: xapi-to call <id> --input '{"key":"val"}'`);
|
|
792
840
|
if (flags.code) validateCodeFlag(flags);
|
|
793
841
|
const cfg = getConfig();
|
|
794
842
|
let input = {};
|
|
@@ -826,29 +874,29 @@ __export(config_exports, {
|
|
|
826
874
|
configSet: () => configSet,
|
|
827
875
|
configShow: () => configShow
|
|
828
876
|
});
|
|
829
|
-
var CONFIG_HELP = `xapi config - Manage CLI configuration
|
|
877
|
+
var CONFIG_HELP = `xapi-to config - Manage CLI configuration
|
|
830
878
|
|
|
831
879
|
USAGE
|
|
832
|
-
xapi config <command> [flags]
|
|
880
|
+
xapi-to config <command> [flags]
|
|
833
881
|
|
|
834
882
|
COMMANDS
|
|
835
883
|
show Show current config (host, apiKey path, etc.)
|
|
836
884
|
set apiKey=<key> Save API key to ~/.xapi/config.json
|
|
837
|
-
health Check backend connectivity (alias: xapi health)
|
|
885
|
+
health Check backend connectivity (alias: xapi-to health)
|
|
838
886
|
|
|
839
887
|
FLAGS
|
|
840
888
|
--format json|pretty|table Output format
|
|
841
889
|
|
|
842
890
|
EXAMPLES
|
|
843
|
-
xapi config show
|
|
844
|
-
xapi config set apiKey=xapi_abc123
|
|
845
|
-
xapi config health
|
|
891
|
+
xapi-to config show
|
|
892
|
+
xapi-to config set apiKey=xapi_abc123
|
|
893
|
+
xapi-to config health
|
|
846
894
|
`;
|
|
847
895
|
async function configShow(args, flags) {
|
|
848
896
|
showConfig();
|
|
849
897
|
}
|
|
850
898
|
async function configSet(args, flags) {
|
|
851
|
-
if (args.length === 0) err("usage: xapi config set apiKey=<key>");
|
|
899
|
+
if (args.length === 0) err("usage: xapi-to config set apiKey=<key>");
|
|
852
900
|
const updates = {};
|
|
853
901
|
for (const arg of args) {
|
|
854
902
|
const eq = arg.indexOf("=");
|
|
@@ -895,6 +943,11 @@ async function registerAccount(referralCode) {
|
|
|
895
943
|
}
|
|
896
944
|
async function register(args, flags) {
|
|
897
945
|
try {
|
|
946
|
+
const cfg = getConfig();
|
|
947
|
+
const force = flags.force === "true" || flags.force === "1" || flags.force === "yes";
|
|
948
|
+
if (cfg.apiKey && !force) {
|
|
949
|
+
err("register would overwrite existing apiKey", 'Run "xapi-to register --force" to create a new account and replace the saved key.');
|
|
950
|
+
}
|
|
898
951
|
const rawReferral = flags["referral-code"] ?? flags["referralCode"] ?? args[0];
|
|
899
952
|
const referralCode = typeof rawReferral === "string" && rawReferral !== "true" && rawReferral.length > 0 ? rawReferral : void 0;
|
|
900
953
|
const res = await registerAccount(referralCode);
|
|
@@ -910,7 +963,7 @@ async function register(args, flags) {
|
|
|
910
963
|
},
|
|
911
964
|
tweetTemplate: res.tweetTemplate,
|
|
912
965
|
...referralCode ? { referredBy: referralCode } : {},
|
|
913
|
-
note: "apiKey saved to ~/.xapi/config.json"
|
|
966
|
+
note: force && cfg.apiKey ? "apiKey replaced in ~/.xapi/config.json" : "apiKey saved to ~/.xapi/config.json"
|
|
914
967
|
}, flags.format);
|
|
915
968
|
} catch (e) {
|
|
916
969
|
err("register failed", e.message);
|
|
@@ -967,7 +1020,8 @@ __export(oauth_exports, {
|
|
|
967
1020
|
oauthBind: () => oauthBind,
|
|
968
1021
|
oauthProviders: () => oauthProviders,
|
|
969
1022
|
oauthStatus: () => oauthStatus,
|
|
970
|
-
oauthUnbind: () => oauthUnbind
|
|
1023
|
+
oauthUnbind: () => oauthUnbind,
|
|
1024
|
+
pollForBinding: () => pollForBinding
|
|
971
1025
|
});
|
|
972
1026
|
import { spawnSync } from "child_process";
|
|
973
1027
|
function openBrowser(url) {
|
|
@@ -977,14 +1031,22 @@ function openBrowser(url) {
|
|
|
977
1031
|
} catch {
|
|
978
1032
|
}
|
|
979
1033
|
}
|
|
980
|
-
|
|
1034
|
+
function bindingChangedAfter(binding, startedAtMs, existingBindingIds) {
|
|
1035
|
+
const changedAt = Date.parse(binding.updatedAt || binding.createdAt || "");
|
|
1036
|
+
if (!Number.isFinite(changedAt)) return !existingBindingIds.has(binding.id);
|
|
1037
|
+
return changedAt >= startedAtMs;
|
|
1038
|
+
}
|
|
1039
|
+
async function pollForBinding(apiKeyId, providerId, jwtToken, startedAt, existingBindingIds = /* @__PURE__ */ new Set(), timeoutMs = 5 * 60 * 1e3, intervalMs = 3e3) {
|
|
981
1040
|
const deadline = Date.now() + timeoutMs;
|
|
982
1041
|
const isTTY = process.stdout.isTTY;
|
|
1042
|
+
const startedAtMs = startedAt.getTime() - 5e3;
|
|
983
1043
|
while (Date.now() < deadline) {
|
|
984
1044
|
await new Promise((r) => setTimeout(r, intervalMs));
|
|
985
1045
|
try {
|
|
986
1046
|
const bindings = await listOAuthBindings(jwtToken, XAPI_API_HOST);
|
|
987
|
-
const match = Array.isArray(bindings) ? bindings.find(
|
|
1047
|
+
const match = Array.isArray(bindings) ? bindings.find(
|
|
1048
|
+
(b) => b.apiKeyId === apiKeyId && b.providerId === providerId && bindingChangedAfter(b, startedAtMs, existingBindingIds)
|
|
1049
|
+
) : null;
|
|
988
1050
|
if (match) return match;
|
|
989
1051
|
} catch {
|
|
990
1052
|
}
|
|
@@ -1012,14 +1074,109 @@ async function findCurrentKeyRecord(plaintextKey, jwtToken) {
|
|
|
1012
1074
|
const prefix = plaintextKey.substring(0, 7);
|
|
1013
1075
|
const match = keys.find((k) => k.keyPreview.startsWith(prefix));
|
|
1014
1076
|
if (!match) {
|
|
1015
|
-
|
|
1077
|
+
throw new Error(
|
|
1078
|
+
`Current API key (${prefix}...) was not found in your account keys. Run "xapi-to config set apiKey=<key>" with a valid key before binding OAuth.`
|
|
1079
|
+
);
|
|
1016
1080
|
}
|
|
1017
1081
|
return match;
|
|
1018
1082
|
}
|
|
1019
|
-
|
|
1083
|
+
function resolveScopeDefs(provider) {
|
|
1084
|
+
if (Array.isArray(provider.scopeDefinitions) && provider.scopeDefinitions.length > 0) {
|
|
1085
|
+
return provider.scopeDefinitions;
|
|
1086
|
+
}
|
|
1087
|
+
const raw = (provider.defaultScopes || "").split(/[\s,]+/).filter(Boolean);
|
|
1088
|
+
return raw.map((s) => ({
|
|
1089
|
+
scope: s,
|
|
1090
|
+
label: s,
|
|
1091
|
+
description: "",
|
|
1092
|
+
required: false,
|
|
1093
|
+
category: ""
|
|
1094
|
+
}));
|
|
1095
|
+
}
|
|
1096
|
+
async function selectScopesInteractive(provider) {
|
|
1097
|
+
const defs = resolveScopeDefs(provider);
|
|
1098
|
+
if (defs.length === 0) return "";
|
|
1099
|
+
const required = defs.filter((d) => d.required);
|
|
1100
|
+
const optional = defs.filter((d) => !d.required);
|
|
1101
|
+
const selected = new Set(defs.map((d) => d.scope));
|
|
1102
|
+
if (optional.length === 0) {
|
|
1103
|
+
return required.map((d) => d.scope).join(" ");
|
|
1104
|
+
}
|
|
1105
|
+
const out = process.stderr;
|
|
1106
|
+
let cursor = 0;
|
|
1107
|
+
const hint = " \u2191\u2193 navigate \xB7 space toggle \xB7 a all \xB7 n none \xB7 enter confirm";
|
|
1108
|
+
const buildFrame = () => {
|
|
1109
|
+
const lines = [];
|
|
1110
|
+
for (const d of required) {
|
|
1111
|
+
const desc = d.description ? ` \u2014 ${d.description}` : "";
|
|
1112
|
+
lines.push(` \x1B[2m[*] ${d.label}${desc} (required)\x1B[0m`);
|
|
1113
|
+
}
|
|
1114
|
+
for (let i = 0; i < optional.length; i++) {
|
|
1115
|
+
const d = optional[i];
|
|
1116
|
+
const ptr = cursor === i ? " \x1B[36m\u276F\x1B[0m" : " ";
|
|
1117
|
+
const chk = selected.has(d.scope) ? "\x1B[32m\u2714\x1B[0m" : " ";
|
|
1118
|
+
const desc = d.description ? ` \x1B[2m\u2014 ${d.description}\x1B[0m` : "";
|
|
1119
|
+
lines.push(` ${ptr} [${chk}] ${d.label}${desc}`);
|
|
1120
|
+
}
|
|
1121
|
+
lines.push(`\x1B[2m${hint}\x1B[0m`);
|
|
1122
|
+
return lines.join("\n");
|
|
1123
|
+
};
|
|
1124
|
+
out.write("\n Scopes:\n");
|
|
1125
|
+
out.write("\x1B[s");
|
|
1126
|
+
out.write("\x1B[?25l");
|
|
1127
|
+
out.write(buildFrame());
|
|
1128
|
+
const redraw = () => {
|
|
1129
|
+
out.write("\x1B[u");
|
|
1130
|
+
out.write("\x1B[J");
|
|
1131
|
+
out.write(buildFrame());
|
|
1132
|
+
};
|
|
1133
|
+
return new Promise((resolve) => {
|
|
1134
|
+
const { stdin } = process;
|
|
1135
|
+
const wasRaw = stdin.isRaw;
|
|
1136
|
+
stdin.setRawMode(true);
|
|
1137
|
+
stdin.resume();
|
|
1138
|
+
const finish = (result) => {
|
|
1139
|
+
stdin.removeListener("data", onData);
|
|
1140
|
+
stdin.setRawMode(wasRaw ?? false);
|
|
1141
|
+
stdin.pause();
|
|
1142
|
+
out.write("\x1B[?25h");
|
|
1143
|
+
out.write("\n");
|
|
1144
|
+
resolve(result);
|
|
1145
|
+
};
|
|
1146
|
+
const onData = (buf) => {
|
|
1147
|
+
const key = buf.toString();
|
|
1148
|
+
if (key === "\r" || key === "\n") {
|
|
1149
|
+
finish(Array.from(selected).join(" "));
|
|
1150
|
+
return;
|
|
1151
|
+
}
|
|
1152
|
+
if (key === "") {
|
|
1153
|
+
finish("");
|
|
1154
|
+
process.exit(130);
|
|
1155
|
+
}
|
|
1156
|
+
if (key === "\x1B[A" || key === "k") {
|
|
1157
|
+
cursor = (cursor - 1 + optional.length) % optional.length;
|
|
1158
|
+
} else if (key === "\x1B[B" || key === "j") {
|
|
1159
|
+
cursor = (cursor + 1) % optional.length;
|
|
1160
|
+
} else if (key === " ") {
|
|
1161
|
+
const scope = optional[cursor].scope;
|
|
1162
|
+
if (selected.has(scope)) selected.delete(scope);
|
|
1163
|
+
else selected.add(scope);
|
|
1164
|
+
} else if (key === "a") {
|
|
1165
|
+
for (const d of optional) selected.add(d.scope);
|
|
1166
|
+
} else if (key === "n") {
|
|
1167
|
+
for (const d of optional) selected.delete(d.scope);
|
|
1168
|
+
} else {
|
|
1169
|
+
return;
|
|
1170
|
+
}
|
|
1171
|
+
redraw();
|
|
1172
|
+
};
|
|
1173
|
+
stdin.on("data", onData);
|
|
1174
|
+
});
|
|
1175
|
+
}
|
|
1176
|
+
var OAUTH_HELP = `xapi-to oauth - Manage OAuth bindings
|
|
1020
1177
|
|
|
1021
1178
|
USAGE
|
|
1022
|
-
xapi oauth <command> [flags]
|
|
1179
|
+
xapi-to oauth <command> [flags]
|
|
1023
1180
|
|
|
1024
1181
|
COMMANDS
|
|
1025
1182
|
bind [--provider <name>] Bind an OAuth account to your API key
|
|
@@ -1029,15 +1186,17 @@ COMMANDS
|
|
|
1029
1186
|
|
|
1030
1187
|
FLAGS
|
|
1031
1188
|
--provider <name> OAuth provider (default: twitter)
|
|
1189
|
+
--scopes <scopes> Space-separated scopes (skips interactive selection)
|
|
1032
1190
|
--format json|pretty|table Output format
|
|
1033
1191
|
|
|
1034
1192
|
EXAMPLES
|
|
1035
|
-
xapi oauth bind
|
|
1036
|
-
xapi oauth bind --provider twitter
|
|
1037
|
-
xapi oauth
|
|
1038
|
-
xapi oauth status
|
|
1039
|
-
xapi oauth
|
|
1040
|
-
xapi oauth
|
|
1193
|
+
xapi-to oauth bind
|
|
1194
|
+
xapi-to oauth bind --provider twitter
|
|
1195
|
+
xapi-to oauth bind --scopes "tweet.read users.read"
|
|
1196
|
+
xapi-to oauth status
|
|
1197
|
+
xapi-to oauth status --format pretty
|
|
1198
|
+
xapi-to oauth unbind abc123
|
|
1199
|
+
xapi-to oauth providers
|
|
1041
1200
|
`;
|
|
1042
1201
|
async function oauthBind(args, flags) {
|
|
1043
1202
|
const cfg = getConfig();
|
|
@@ -1063,13 +1222,49 @@ async function oauthBind(args, flags) {
|
|
|
1063
1222
|
`Provider "${providerName}" not found. Available: ${available}`
|
|
1064
1223
|
);
|
|
1065
1224
|
}
|
|
1066
|
-
|
|
1225
|
+
let scopes;
|
|
1226
|
+
let headerPrinted = false;
|
|
1227
|
+
const isTTY = Boolean(
|
|
1228
|
+
process.stdout.isTTY && process.stdin.isTTY && typeof process.stdin.setRawMode === "function"
|
|
1229
|
+
);
|
|
1230
|
+
if (flags.scopes) {
|
|
1231
|
+
scopes = flags.scopes;
|
|
1232
|
+
} else if (isTTY) {
|
|
1233
|
+
const defs = resolveScopeDefs(provider);
|
|
1234
|
+
if (defs.length > 0) {
|
|
1235
|
+
console.error(`
|
|
1236
|
+
Provider : ${provider.name}`);
|
|
1237
|
+
console.error(` API Key : ${keyRecord.keyPreview}`);
|
|
1238
|
+
headerPrinted = true;
|
|
1239
|
+
scopes = await selectScopesInteractive(provider) || void 0;
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
const existingBindingIds = /* @__PURE__ */ new Set();
|
|
1243
|
+
if (isTTY) {
|
|
1244
|
+
try {
|
|
1245
|
+
const existingBindings = await listOAuthBindings(jwtToken, XAPI_API_HOST);
|
|
1246
|
+
if (Array.isArray(existingBindings)) {
|
|
1247
|
+
for (const binding of existingBindings) {
|
|
1248
|
+
if (binding.apiKeyId === keyRecord.id && binding.providerId === provider.id) {
|
|
1249
|
+
existingBindingIds.add(binding.id);
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
} catch {
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
const authorizationStartedAt = /* @__PURE__ */ new Date();
|
|
1257
|
+
const result = await initiateOAuth(keyRecord.id, provider.id, jwtToken, XAPI_API_HOST, scopes);
|
|
1067
1258
|
const { authorizationUrl } = result;
|
|
1068
|
-
const isTTY = process.stdout.isTTY;
|
|
1069
1259
|
if (isTTY) {
|
|
1070
|
-
|
|
1260
|
+
if (!headerPrinted) {
|
|
1261
|
+
console.error(`
|
|
1071
1262
|
Provider : ${provider.name}`);
|
|
1072
|
-
|
|
1263
|
+
console.error(` API Key : ${keyRecord.keyPreview}`);
|
|
1264
|
+
}
|
|
1265
|
+
if (scopes) {
|
|
1266
|
+
console.error(` Scopes : ${scopes}`);
|
|
1267
|
+
}
|
|
1073
1268
|
console.error(`
|
|
1074
1269
|
Authorization URL:
|
|
1075
1270
|
${authorizationUrl}
|
|
@@ -1077,23 +1272,30 @@ async function oauthBind(args, flags) {
|
|
|
1077
1272
|
console.error(" Opening browser...");
|
|
1078
1273
|
openBrowser(authorizationUrl);
|
|
1079
1274
|
console.error(" Waiting for you to complete authorization in the browser...\n");
|
|
1080
|
-
const binding = await pollForBinding(
|
|
1275
|
+
const binding = await pollForBinding(
|
|
1276
|
+
keyRecord.id,
|
|
1277
|
+
provider.id,
|
|
1278
|
+
jwtToken,
|
|
1279
|
+
authorizationStartedAt,
|
|
1280
|
+
existingBindingIds
|
|
1281
|
+
);
|
|
1081
1282
|
if (process.stdout.isTTY) process.stdout.write("\n");
|
|
1082
1283
|
if (binding) {
|
|
1083
1284
|
const account = binding.providerAccountName || "unknown";
|
|
1084
1285
|
console.error(`
|
|
1085
1286
|
Authorization complete! Bound to @${account}
|
|
1086
1287
|
`);
|
|
1087
|
-
output({ status: "success", provider: provider.name, account }, flags.format);
|
|
1288
|
+
output({ status: "success", provider: provider.name, account, scopes }, flags.format);
|
|
1088
1289
|
} else {
|
|
1089
|
-
err("oauth bind timed out", 'Authorization was not completed within 5 minutes. Run "xapi oauth bind" again.');
|
|
1290
|
+
err("oauth bind timed out", 'Authorization was not completed within 5 minutes. Run "xapi-to oauth bind" again.');
|
|
1090
1291
|
}
|
|
1091
1292
|
} else {
|
|
1092
1293
|
output({
|
|
1093
1294
|
status: "pending",
|
|
1094
1295
|
provider: provider.name,
|
|
1095
1296
|
apiKey: keyRecord.keyPreview,
|
|
1096
|
-
authorizationUrl
|
|
1297
|
+
authorizationUrl,
|
|
1298
|
+
scopes
|
|
1097
1299
|
}, flags.format);
|
|
1098
1300
|
}
|
|
1099
1301
|
} catch (e) {
|
|
@@ -1110,7 +1312,7 @@ async function oauthStatus(args, flags) {
|
|
|
1110
1312
|
if (!Array.isArray(bindings) || bindings.length === 0) {
|
|
1111
1313
|
output({
|
|
1112
1314
|
status: "no_bindings",
|
|
1113
|
-
message: 'No OAuth bindings found. Run "xapi oauth bind" to connect an account.'
|
|
1315
|
+
message: 'No OAuth bindings found. Run "xapi-to oauth bind" to connect an account.'
|
|
1114
1316
|
}, flags.format);
|
|
1115
1317
|
return;
|
|
1116
1318
|
}
|
|
@@ -1137,7 +1339,7 @@ async function oauthUnbind(args, flags) {
|
|
|
1137
1339
|
const apiKey = cfg.apiKey;
|
|
1138
1340
|
const bindingId = args[0];
|
|
1139
1341
|
if (!bindingId) {
|
|
1140
|
-
err("usage: xapi oauth unbind <binding-id>", 'Get the binding ID from "xapi oauth status"');
|
|
1342
|
+
err("usage: xapi-to oauth unbind <binding-id>", 'Get the binding ID from "xapi-to oauth status"');
|
|
1141
1343
|
}
|
|
1142
1344
|
try {
|
|
1143
1345
|
const jwtToken = await loginAndGetJwt(apiKey);
|
|
@@ -1182,10 +1384,10 @@ function parseArgs(argv) {
|
|
|
1182
1384
|
}
|
|
1183
1385
|
return { positional, flags };
|
|
1184
1386
|
}
|
|
1185
|
-
var HELP = `xapi - agent-friendly CLI for xapi
|
|
1387
|
+
var HELP = `xapi-to - agent-friendly CLI for xapi
|
|
1186
1388
|
|
|
1187
1389
|
USAGE
|
|
1188
|
-
xapi <command> [args] [flags]
|
|
1390
|
+
xapi-to <command> [args] [flags]
|
|
1189
1391
|
|
|
1190
1392
|
COMMANDS
|
|
1191
1393
|
list List all actions
|
|
@@ -1216,6 +1418,7 @@ COMMANDS
|
|
|
1216
1418
|
|
|
1217
1419
|
register [referral-code] Create a new user account (apiKey saved automatically)
|
|
1218
1420
|
--referral-code <code> Register with an inviter's referral code (also: --referralCode, or as positional arg)
|
|
1421
|
+
--force Replace an existing saved apiKey
|
|
1219
1422
|
balance Show current account balance
|
|
1220
1423
|
topup [--amount <usd>] [--method stripe|x402] Generate payment URL
|
|
1221
1424
|
|
|
@@ -1223,11 +1426,11 @@ COMMANDS
|
|
|
1223
1426
|
|
|
1224
1427
|
config show Show current config
|
|
1225
1428
|
config set apiKey=<key> Save API key to ~/.xapi/config.json
|
|
1226
|
-
config health Check backend connectivity (alias: xapi health)
|
|
1429
|
+
config health Check backend connectivity (alias: xapi-to health)
|
|
1227
1430
|
|
|
1228
1431
|
GLOBAL FLAGS
|
|
1229
1432
|
--format json|pretty|table Output format (default: json)
|
|
1230
|
-
--help Show help (use with a command for details, e.g. xapi get --help)
|
|
1433
|
+
--help Show help (use with a command for details, e.g. xapi-to get --help)
|
|
1231
1434
|
|
|
1232
1435
|
ENV VARS
|
|
1233
1436
|
XAPI_KEY API key (header: XAPI-Key)
|
|
@@ -1235,21 +1438,21 @@ ENV VARS
|
|
|
1235
1438
|
XAPI_OUTPUT Default output format
|
|
1236
1439
|
|
|
1237
1440
|
EXAMPLES
|
|
1238
|
-
xapi register
|
|
1239
|
-
xapi register --referral-code xapito # register with an inviter's referral code
|
|
1240
|
-
xapi register xapito # positional shorthand
|
|
1241
|
-
xapi list --format table
|
|
1242
|
-
xapi list --source capability
|
|
1243
|
-
xapi search twitter --source api
|
|
1244
|
-
xapi get twitter.tweet_detail
|
|
1245
|
-
xapi get twitter.tweet_detail --code curl
|
|
1246
|
-
xapi get twitter.tweet_detail --code py --format pretty
|
|
1247
|
-
xapi call twitter.tweet_detail --input '{"tweet_id":"1234567890"}'
|
|
1248
|
-
xapi call twitter.tweet_detail --input '{"tweet_id":"1234567890"}' --code python
|
|
1249
|
-
xapi categories
|
|
1250
|
-
xapi services --format table
|
|
1251
|
-
xapi config set apiKey=xapi_abc123
|
|
1252
|
-
xapi health
|
|
1441
|
+
xapi-to register
|
|
1442
|
+
xapi-to register --referral-code xapito # register with an inviter's referral code
|
|
1443
|
+
xapi-to register xapito # positional shorthand
|
|
1444
|
+
xapi-to list --format table
|
|
1445
|
+
xapi-to list --source capability
|
|
1446
|
+
xapi-to search twitter --source api
|
|
1447
|
+
xapi-to get twitter.tweet_detail
|
|
1448
|
+
xapi-to get twitter.tweet_detail --code curl
|
|
1449
|
+
xapi-to get twitter.tweet_detail --code py --format pretty
|
|
1450
|
+
xapi-to call twitter.tweet_detail --input '{"tweet_id":"1234567890"}'
|
|
1451
|
+
xapi-to call twitter.tweet_detail --input '{"tweet_id":"1234567890"}' --code python
|
|
1452
|
+
xapi-to categories
|
|
1453
|
+
xapi-to services --format table
|
|
1454
|
+
xapi-to config set apiKey=xapi_abc123
|
|
1455
|
+
xapi-to health
|
|
1253
1456
|
`;
|
|
1254
1457
|
async function main() {
|
|
1255
1458
|
const { positional, flags } = parseArgs(process.argv.slice(2));
|
|
@@ -1325,7 +1528,7 @@ async function main() {
|
|
|
1325
1528
|
break;
|
|
1326
1529
|
}
|
|
1327
1530
|
default:
|
|
1328
|
-
console.error(JSON.stringify({ error: `unknown command: ${cmd}`, hint: "run xapi --help" }));
|
|
1531
|
+
console.error(JSON.stringify({ error: `unknown command: ${cmd}`, hint: "run xapi-to --help" }));
|
|
1329
1532
|
process.exit(1);
|
|
1330
1533
|
}
|
|
1331
1534
|
}
|