@lidianai/cli 0.1.0 → 0.1.1
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/AGENTS.md +26 -0
- package/README.md +12 -9
- package/bun.lock +43 -0
- package/dist/index.js +137 -43
- package/package.json +1 -1
- package/src/commands/{act.ts → consume.ts} +11 -11
- package/src/commands/discover.ts +59 -0
- package/src/index.ts +141 -29
- package/src/lib/output.ts +19 -10
- package/src/commands/query.ts +0 -45
package/AGENTS.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# AGENTS.md - Lidian CLI
|
|
2
|
+
|
|
3
|
+
Guidelines for agents working in the `cli/` package.
|
|
4
|
+
|
|
5
|
+
## Core Standards
|
|
6
|
+
|
|
7
|
+
- Keep implementations simple, clean, and easy to read.
|
|
8
|
+
- Prefer direct logic over clever abstractions and indirection.
|
|
9
|
+
- Avoid obscure decisions and surprising behavior.
|
|
10
|
+
|
|
11
|
+
## Required Simplification Pass
|
|
12
|
+
|
|
13
|
+
After any feature or code change, always review your own diff and simplify before finishing:
|
|
14
|
+
|
|
15
|
+
- remove dead code and temporary scaffolding
|
|
16
|
+
- collapse duplicated logic
|
|
17
|
+
- simplify branching/flow where possible
|
|
18
|
+
- improve naming for clarity
|
|
19
|
+
|
|
20
|
+
If complexity is unavoidable, add a short comment explaining why it is necessary.
|
|
21
|
+
|
|
22
|
+
## Tooling Rules
|
|
23
|
+
|
|
24
|
+
- Use `bun`/`bunx` only.
|
|
25
|
+
- Run `bun run lint` and `bun run typecheck` before completion.
|
|
26
|
+
- Use explicit types and avoid `any`.
|
package/README.md
CHANGED
|
@@ -5,36 +5,39 @@ Bun CLI for Lidian core REST endpoints.
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
|
|
9
|
-
# or
|
|
10
|
-
bunx @lidian/cli --help
|
|
8
|
+
bunx @lidianai/cli --help
|
|
11
9
|
```
|
|
12
10
|
|
|
13
11
|
## Commands
|
|
14
12
|
|
|
15
13
|
```bash
|
|
16
|
-
lidian
|
|
17
|
-
lidian
|
|
18
|
-
lidian account [--api-key <key>] [--json]
|
|
14
|
+
lidian discover --q "<term>" [--page 1] [--pageSize 1..3] [--category <name>] [--auth-type none|api_key|bearer|basic|oauth2|custom] [--min-price <cents>] [--max-price <cents>] [--api-key <key>] [--env production|staging] [--api-base <url>] [--json]
|
|
15
|
+
lidian consume --endpoint-id <uuid> --params '<json>' [--payment-rail prepaid_credits|x402] [--network base|ethereum] [--api-key <key>] [--env production|staging] [--api-base <url>] [--json]
|
|
16
|
+
lidian account [--api-key <key>] [--env production|staging] [--api-base <url>] [--json]
|
|
19
17
|
lidian login [--key ld_...] [--json]
|
|
20
18
|
lidian --help
|
|
21
19
|
```
|
|
22
20
|
|
|
23
|
-
`
|
|
21
|
+
`discover` returns API matches (`items[]`) from `/v1/discover` with metadata and confidence fields (`matchScore`, `matchPercent`).
|
|
24
22
|
|
|
25
23
|
## Auth
|
|
26
24
|
|
|
27
25
|
- Store key locally: `lidian login --key ld_...` (writes `~/.lidian/config.json`)
|
|
28
26
|
- Or run `lidian login` and paste your key after browser auth flow.
|
|
29
27
|
- Resolution order: `--api-key` -> `LIDIAN_API_KEY` -> `~/.lidian/config.json`.
|
|
30
|
-
-
|
|
28
|
+
- API base resolution order:
|
|
29
|
+
- `--api-base`
|
|
30
|
+
- `LIDIAN_API_BASE`
|
|
31
|
+
- `--env` (`production` or `staging`)
|
|
32
|
+
- `LIDIAN_ENV` (`production` or `staging`)
|
|
33
|
+
- default `https://api.lidian.ai`
|
|
31
34
|
|
|
32
35
|
## x402
|
|
33
36
|
|
|
34
37
|
When `--payment-rail x402` is used, CLI performs:
|
|
35
38
|
1. `POST /v1/payments/requirements`
|
|
36
39
|
2. `POST /v1/payments/verify`
|
|
37
|
-
3. `POST /v1/
|
|
40
|
+
3. `POST /v1/consume`
|
|
38
41
|
|
|
39
42
|
## Dev
|
|
40
43
|
|
package/bun.lock
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"lockfileVersion": 1,
|
|
3
|
+
"configVersion": 1,
|
|
4
|
+
"workspaces": {
|
|
5
|
+
"": {
|
|
6
|
+
"name": "@lidian/cli",
|
|
7
|
+
"devDependencies": {
|
|
8
|
+
"@biomejs/biome": "^2.4.4",
|
|
9
|
+
"@types/bun": "^1.3.9",
|
|
10
|
+
"typescript": "^5.9.3",
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
"packages": {
|
|
15
|
+
"@biomejs/biome": ["@biomejs/biome@2.4.4", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.4.4", "@biomejs/cli-darwin-x64": "2.4.4", "@biomejs/cli-linux-arm64": "2.4.4", "@biomejs/cli-linux-arm64-musl": "2.4.4", "@biomejs/cli-linux-x64": "2.4.4", "@biomejs/cli-linux-x64-musl": "2.4.4", "@biomejs/cli-win32-arm64": "2.4.4", "@biomejs/cli-win32-x64": "2.4.4" }, "bin": { "biome": "bin/biome" } }, "sha512-tigwWS5KfJf0cABVd52NVaXyAVv4qpUXOWJ1rxFL8xF1RVoeS2q/LK+FHgYoKMclJCuRoCWAPy1IXaN9/mS61Q=="],
|
|
16
|
+
|
|
17
|
+
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.4.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-jZ+Xc6qvD6tTH5jM6eKX44dcbyNqJHssfl2nnwT6vma6B1sj7ZLTGIk6N5QwVBs5xGN52r3trk5fgd3sQ9We9A=="],
|
|
18
|
+
|
|
19
|
+
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.4.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-Dh1a/+W+SUCXhEdL7TiX3ArPTFCQKJTI1mGncZNWfO+6suk+gYA4lNyJcBB+pwvF49uw0pEbUS49BgYOY4hzUg=="],
|
|
20
|
+
|
|
21
|
+
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.4.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-V/NFfbWhsUU6w+m5WYbBenlEAz8eYnSqRMDMAW3K+3v0tYVkNyZn8VU0XPxk/lOqNXLSCCrV7FmV/u3SjCBShg=="],
|
|
22
|
+
|
|
23
|
+
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.4.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-+sPAXq3bxmFwhVFJnSwkSF5Rw2ZAJMH3MF6C9IveAEOdSpgajPhoQhbbAK12SehN9j2QrHpk4J/cHsa/HqWaYQ=="],
|
|
24
|
+
|
|
25
|
+
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.4.4", "", { "os": "linux", "cpu": "x64" }, "sha512-R4+ZCDtG9kHArasyBO+UBD6jr/FcFCTH8QkNTOCu0pRJzCWyWC4EtZa2AmUZB5h3e0jD7bRV2KvrENcf8rndBg=="],
|
|
26
|
+
|
|
27
|
+
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.4.4", "", { "os": "linux", "cpu": "x64" }, "sha512-gGvFTGpOIQDb5CQ2VC0n9Z2UEqlP46c4aNgHmAMytYieTGEcfqhfCFnhs6xjt0S3igE6q5GLuIXtdQt3Izok+g=="],
|
|
28
|
+
|
|
29
|
+
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.4.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-trzCqM7x+Gn832zZHgr28JoYagQNX4CZkUZhMUac2YxvvyDRLJDrb5m9IA7CaZLlX6lTQmADVfLEKP1et1Ma4Q=="],
|
|
30
|
+
|
|
31
|
+
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.4.4", "", { "os": "win32", "cpu": "x64" }, "sha512-gnOHKVPFAAPrpoPt2t+Q6FZ7RPry/FDV3GcpU53P3PtLNnQjBmKyN2Vh/JtqXet+H4pme8CC76rScwdjDcT1/A=="],
|
|
32
|
+
|
|
33
|
+
"@types/bun": ["@types/bun@1.3.9", "", { "dependencies": { "bun-types": "1.3.9" } }, "sha512-KQ571yULOdWJiMH+RIWIOZ7B2RXQGpL1YQrBtLIV3FqDcCu6FsbFUBwhdKUlCKUpS3PJDsHlJ1QKlpxoVR+xtw=="],
|
|
34
|
+
|
|
35
|
+
"@types/node": ["@types/node@25.3.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-4K3bqJpXpqfg2XKGK9bpDTc6xO/xoUP/RBWS7AtRMug6zZFaRekiLzjVtAoZMquxoAbzBvy5nxQ7veS5eYzf8A=="],
|
|
36
|
+
|
|
37
|
+
"bun-types": ["bun-types@1.3.9", "", { "dependencies": { "@types/node": "*" } }, "sha512-+UBWWOakIP4Tswh0Bt0QD0alpTY8cb5hvgiYeWCMet9YukHbzuruIEeXC2D7nMJPB12kbh8C7XJykSexEqGKJg=="],
|
|
38
|
+
|
|
39
|
+
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
|
40
|
+
|
|
41
|
+
"undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
|
|
42
|
+
}
|
|
43
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -24,15 +24,15 @@ var verifyPaymentAddress = async (http, apiKey, payTo) => {
|
|
|
24
24
|
return http.post("/v1/payments/verify", { payTo }, apiKey);
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
-
// src/commands/
|
|
28
|
-
var
|
|
27
|
+
// src/commands/consume.ts
|
|
28
|
+
var runConsumeCommand = async (http, apiKey, input) => {
|
|
29
29
|
if (!isUuid(input.endpointId)) {
|
|
30
30
|
throw new CliError("endpointId must be a valid UUID.");
|
|
31
31
|
}
|
|
32
32
|
if (input.paymentRail === "x402") {
|
|
33
33
|
const requirements = await requestPaymentRequirements(http, apiKey, input.endpointId, input.network);
|
|
34
34
|
const verification = await verifyPaymentAddress(http, apiKey, requirements.payTo);
|
|
35
|
-
const execution2 = await http.post("/v1/
|
|
35
|
+
const execution2 = await http.post("/v1/consume", input, apiKey);
|
|
36
36
|
return {
|
|
37
37
|
execution: execution2,
|
|
38
38
|
payment: {
|
|
@@ -41,13 +41,35 @@ var runActCommand = async (http, apiKey, input) => {
|
|
|
41
41
|
}
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
|
-
const execution = await http.post("/v1/
|
|
44
|
+
const execution = await http.post("/v1/consume", input, apiKey);
|
|
45
45
|
return { execution };
|
|
46
46
|
};
|
|
47
47
|
var isUuid = (value) => {
|
|
48
48
|
return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
+
// src/commands/discover.ts
|
|
52
|
+
var runDiscoverCommand = async (http, apiKey, input) => {
|
|
53
|
+
if (input.pageSize < 1 || input.pageSize > 3) {
|
|
54
|
+
throw new CliError("pageSize must be between 1 and 3.");
|
|
55
|
+
}
|
|
56
|
+
const params = new URLSearchParams;
|
|
57
|
+
params.set("q", input.q);
|
|
58
|
+
params.set("page", String(input.page));
|
|
59
|
+
params.set("pageSize", String(input.pageSize));
|
|
60
|
+
if (input.category)
|
|
61
|
+
params.set("category", input.category);
|
|
62
|
+
if (input.authType)
|
|
63
|
+
params.set("authType", input.authType);
|
|
64
|
+
if (typeof input.minPrice === "number") {
|
|
65
|
+
params.set("minPrice", String(input.minPrice));
|
|
66
|
+
}
|
|
67
|
+
if (typeof input.maxPrice === "number") {
|
|
68
|
+
params.set("maxPrice", String(input.maxPrice));
|
|
69
|
+
}
|
|
70
|
+
return http.get(`/v1/discover?${params.toString()}`, apiKey);
|
|
71
|
+
};
|
|
72
|
+
|
|
51
73
|
// src/commands/login.ts
|
|
52
74
|
import { stdin, stdout } from "process";
|
|
53
75
|
import { createInterface } from "readline/promises";
|
|
@@ -93,7 +115,7 @@ var printResult = (result, asJson) => {
|
|
|
93
115
|
}
|
|
94
116
|
print(formatForHuman(result));
|
|
95
117
|
};
|
|
96
|
-
var
|
|
118
|
+
var printDiscoverResult = (result, asJson) => {
|
|
97
119
|
if (asJson) {
|
|
98
120
|
printResult(result, true);
|
|
99
121
|
return;
|
|
@@ -105,10 +127,10 @@ var printQueryResult = (result, asJson) => {
|
|
|
105
127
|
print(`Found ${result.items.length} of ${result.total} APIs (page ${result.page}).`);
|
|
106
128
|
for (const item of result.items) {
|
|
107
129
|
const confidence = item.matchPercent ? ` confidence=${item.matchPercent.toFixed(1)}%` : "";
|
|
108
|
-
print(`- ${item.name} (${item.id}) auth=${item.authType} cost=${item.defaultCostPerUse}c${confidence}`);
|
|
130
|
+
print(`- ${item.name} (${item.id}) auth=${item.authType} cost=${item.defaultCostPerUse}c (${formatUsd(item.defaultCostPerUse)})${confidence}`);
|
|
109
131
|
}
|
|
110
132
|
};
|
|
111
|
-
var
|
|
133
|
+
var printConsumeResult = (result, asJson) => {
|
|
112
134
|
if (asJson) {
|
|
113
135
|
printResult(result.execution, true);
|
|
114
136
|
return;
|
|
@@ -124,10 +146,10 @@ var printAccountResult = (result, asJson) => {
|
|
|
124
146
|
return;
|
|
125
147
|
}
|
|
126
148
|
print(`Account: ${result.user.id}`);
|
|
127
|
-
print(`Balance: ${result.balance.balance}
|
|
149
|
+
print(`Balance: ${result.balance.balance} cents (${formatUsd(result.balance.balance)})`);
|
|
128
150
|
};
|
|
129
151
|
var printExecutionResult = (result) => {
|
|
130
|
-
print(`Execution succeeded. Spent=${result.credits.spent} balance=${result.credits.balance}`);
|
|
152
|
+
print(`Execution succeeded. Spent=${result.credits.spent} cents (${formatUsd(result.credits.spent)}) balance=${result.credits.balance} cents (${formatUsd(result.credits.balance)})`);
|
|
131
153
|
print(JSON.stringify(result.data, null, 2));
|
|
132
154
|
};
|
|
133
155
|
var formatForHuman = (value) => {
|
|
@@ -136,6 +158,9 @@ var formatForHuman = (value) => {
|
|
|
136
158
|
}
|
|
137
159
|
return JSON.stringify(value, null, 2);
|
|
138
160
|
};
|
|
161
|
+
var formatUsd = (cents) => {
|
|
162
|
+
return `$${(cents / 100).toFixed(2)}`;
|
|
163
|
+
};
|
|
139
164
|
var fail = (error) => {
|
|
140
165
|
if (error instanceof CliError) {
|
|
141
166
|
printError(`Error: ${error.message}`);
|
|
@@ -198,19 +223,6 @@ var openUrl = (url) => {
|
|
|
198
223
|
} catch {}
|
|
199
224
|
};
|
|
200
225
|
|
|
201
|
-
// src/commands/query.ts
|
|
202
|
-
var runQueryCommand = async (http, apiKey, input) => {
|
|
203
|
-
if (input.pageSize < 1 || input.pageSize > 3) {
|
|
204
|
-
throw new CliError("pageSize must be between 1 and 3.");
|
|
205
|
-
}
|
|
206
|
-
const params = new URLSearchParams({
|
|
207
|
-
q: input.q,
|
|
208
|
-
page: String(input.page),
|
|
209
|
-
pageSize: String(input.pageSize)
|
|
210
|
-
});
|
|
211
|
-
return http.get(`/v1/query?${params.toString()}`, apiKey);
|
|
212
|
-
};
|
|
213
|
-
|
|
214
226
|
// src/lib/auth.ts
|
|
215
227
|
var resolveApiKey = async (argsApiKey) => {
|
|
216
228
|
const config = await readConfig();
|
|
@@ -264,10 +276,23 @@ var handleResponse = async (response) => {
|
|
|
264
276
|
|
|
265
277
|
// src/index.ts
|
|
266
278
|
var DEFAULT_API_BASE = "https://api.lidian.ai";
|
|
267
|
-
var
|
|
279
|
+
var API_BASE_BY_ENV = {
|
|
280
|
+
production: "https://api.lidian.ai",
|
|
281
|
+
staging: "https://staging-api.lidian.ai"
|
|
282
|
+
};
|
|
283
|
+
var GLOBAL_OPTIONS = new Set(["api-key", "api-base", "env", "json", "help"]);
|
|
284
|
+
var BOOLEAN_OPTIONS = new Set(["json", "help"]);
|
|
268
285
|
var COMMAND_OPTIONS = {
|
|
269
|
-
|
|
270
|
-
|
|
286
|
+
discover: new Set([
|
|
287
|
+
"q",
|
|
288
|
+
"page",
|
|
289
|
+
"pageSize",
|
|
290
|
+
"category",
|
|
291
|
+
"auth-type",
|
|
292
|
+
"min-price",
|
|
293
|
+
"max-price"
|
|
294
|
+
]),
|
|
295
|
+
consume: new Set(["endpoint-id", "params", "payment-rail", "network"]),
|
|
271
296
|
account: new Set([]),
|
|
272
297
|
login: new Set(["key"])
|
|
273
298
|
};
|
|
@@ -277,7 +302,7 @@ var main = async () => {
|
|
|
277
302
|
return;
|
|
278
303
|
}
|
|
279
304
|
const parsed = parseArgs(process.argv.slice(2));
|
|
280
|
-
const apiBase =
|
|
305
|
+
const apiBase = resolveApiBase(asString(parsed.options["api-base"]), asEnvironment(asString(parsed.options.env) ?? process.env.LIDIAN_ENV));
|
|
281
306
|
const asJson = Boolean(parsed.options.json);
|
|
282
307
|
const http = createHttpClient(apiBase);
|
|
283
308
|
switch (parsed.command) {
|
|
@@ -291,39 +316,46 @@ var main = async () => {
|
|
|
291
316
|
}
|
|
292
317
|
return;
|
|
293
318
|
}
|
|
294
|
-
case "
|
|
319
|
+
case "discover": {
|
|
295
320
|
const apiKey = await resolveApiKey(asString(parsed.options["api-key"]));
|
|
296
321
|
const qValue = asString(parsed.options.q);
|
|
297
322
|
if (!qValue) {
|
|
298
|
-
throw new CliError("Missing --q for
|
|
323
|
+
throw new CliError("Missing --q for discover command.");
|
|
299
324
|
}
|
|
300
325
|
const page = toInt(asString(parsed.options.page), 1);
|
|
301
326
|
const pageSize = toInt(asString(parsed.options.pageSize), 1);
|
|
302
|
-
const
|
|
327
|
+
const authType = asAuthType(asString(parsed.options["auth-type"]));
|
|
328
|
+
const minPrice = toNumber(asString(parsed.options["min-price"]), "min-price");
|
|
329
|
+
const maxPrice = toNumber(asString(parsed.options["max-price"]), "max-price");
|
|
330
|
+
const result = await runDiscoverCommand(http, apiKey, {
|
|
303
331
|
q: qValue,
|
|
304
332
|
page,
|
|
305
|
-
pageSize
|
|
333
|
+
pageSize,
|
|
334
|
+
category: asString(parsed.options.category),
|
|
335
|
+
...authType ? { authType } : {},
|
|
336
|
+
...typeof minPrice === "number" ? { minPrice } : {},
|
|
337
|
+
...typeof maxPrice === "number" ? { maxPrice } : {}
|
|
306
338
|
});
|
|
307
|
-
|
|
339
|
+
printDiscoverResult(result, asJson);
|
|
308
340
|
return;
|
|
309
341
|
}
|
|
310
|
-
case "
|
|
342
|
+
case "consume": {
|
|
311
343
|
const apiKey = await resolveApiKey(asString(parsed.options["api-key"]));
|
|
312
344
|
const endpointIdValue = asString(parsed.options["endpoint-id"]);
|
|
313
345
|
if (!endpointIdValue) {
|
|
314
|
-
throw new CliError("Missing --endpoint-id for
|
|
346
|
+
throw new CliError("Missing --endpoint-id for consume command.");
|
|
315
347
|
}
|
|
316
348
|
const paramsRaw = asString(parsed.options.params) ?? "{}";
|
|
317
349
|
const params = parseJsonObject(paramsRaw, "--params");
|
|
318
350
|
const paymentRail = asPaymentRail(asString(parsed.options["payment-rail"]) ?? "prepaid_credits");
|
|
319
|
-
const network = asString(parsed.options.network);
|
|
320
|
-
const result = await
|
|
351
|
+
const network = asNetwork(asString(parsed.options.network));
|
|
352
|
+
const result = await runConsumeCommand(http, apiKey, {
|
|
321
353
|
endpointId: endpointIdValue,
|
|
322
354
|
params,
|
|
323
355
|
paymentRail,
|
|
324
356
|
...network ? { network } : {}
|
|
325
357
|
});
|
|
326
|
-
|
|
358
|
+
printConsumeResult(result, asJson);
|
|
327
359
|
return;
|
|
328
360
|
}
|
|
329
361
|
case "account": {
|
|
@@ -338,9 +370,9 @@ var main = async () => {
|
|
|
338
370
|
};
|
|
339
371
|
var parseArgs = (argv) => {
|
|
340
372
|
const command = argv[0];
|
|
341
|
-
if (command !== "
|
|
373
|
+
if (command !== "discover" && command !== "consume" && command !== "account" && command !== "login") {
|
|
342
374
|
printUsage();
|
|
343
|
-
throw new CliError("Invalid command. Use one of: login,
|
|
375
|
+
throw new CliError("Invalid command. Use one of: login, discover, consume, account.", 1);
|
|
344
376
|
}
|
|
345
377
|
const options = {};
|
|
346
378
|
let index = 1;
|
|
@@ -356,6 +388,9 @@ var parseArgs = (argv) => {
|
|
|
356
388
|
}
|
|
357
389
|
const next = argv[index + 1];
|
|
358
390
|
if (!next || next.startsWith("--")) {
|
|
391
|
+
if (!BOOLEAN_OPTIONS.has(key)) {
|
|
392
|
+
throw new CliError(`Missing value for --${key}`);
|
|
393
|
+
}
|
|
359
394
|
options[key] = true;
|
|
360
395
|
index += 1;
|
|
361
396
|
continue;
|
|
@@ -371,10 +406,16 @@ var parseArgs = (argv) => {
|
|
|
371
406
|
var printUsage = () => {
|
|
372
407
|
print("Usage:");
|
|
373
408
|
print(" lidian login [--key ld_...] [--json]");
|
|
374
|
-
print(' lidian
|
|
375
|
-
print("
|
|
409
|
+
print(' lidian discover --q "<term>" [--page 1] [--pageSize 1..3] [--category <name>] [--auth-type none|api_key|bearer|basic|oauth2|custom]');
|
|
410
|
+
print(" [--min-price <cents>] [--max-price <cents>] [--api-key <key>] [--env production|staging] [--api-base <url>] [--json]");
|
|
411
|
+
print(" lidian consume --endpoint-id <uuid> --params '<json>' [--payment-rail prepaid_credits|x402] [--api-key <key>] [--env production|staging] [--api-base <url>] [--json]");
|
|
376
412
|
print(" [--network base|ethereum]");
|
|
377
|
-
print(" lidian account [--api-key <key>] [--json]");
|
|
413
|
+
print(" lidian account [--api-key <key>] [--env production|staging] [--api-base <url>] [--json]");
|
|
414
|
+
print("");
|
|
415
|
+
print("Env resolution:");
|
|
416
|
+
print(" --api-base > LIDIAN_API_BASE > --env > LIDIAN_ENV > production");
|
|
417
|
+
print(` production=${API_BASE_BY_ENV.production}`);
|
|
418
|
+
print(` staging=${API_BASE_BY_ENV.staging}`);
|
|
378
419
|
};
|
|
379
420
|
var asString = (value) => {
|
|
380
421
|
if (typeof value === "string")
|
|
@@ -385,13 +426,27 @@ var toInt = (value, fallback) => {
|
|
|
385
426
|
if (!value)
|
|
386
427
|
return fallback;
|
|
387
428
|
const parsed = Number.parseInt(value, 10);
|
|
388
|
-
if (Number.isNaN(parsed)) {
|
|
429
|
+
if (Number.isNaN(parsed) || parsed < 1) {
|
|
389
430
|
throw new CliError(`Invalid integer value: ${value}`);
|
|
390
431
|
}
|
|
391
432
|
return parsed;
|
|
392
433
|
};
|
|
434
|
+
var toNumber = (value, flagName) => {
|
|
435
|
+
if (!value)
|
|
436
|
+
return;
|
|
437
|
+
const parsed = Number(value);
|
|
438
|
+
if (Number.isNaN(parsed)) {
|
|
439
|
+
throw new CliError(`Invalid --${flagName} value: ${value}`);
|
|
440
|
+
}
|
|
441
|
+
return parsed;
|
|
442
|
+
};
|
|
393
443
|
var parseJsonObject = (value, flagName) => {
|
|
394
|
-
|
|
444
|
+
let parsed;
|
|
445
|
+
try {
|
|
446
|
+
parsed = JSON.parse(value);
|
|
447
|
+
} catch {
|
|
448
|
+
throw new CliError(`${flagName} must be valid JSON.`);
|
|
449
|
+
}
|
|
395
450
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
396
451
|
throw new CliError(`${flagName} must be a JSON object.`);
|
|
397
452
|
}
|
|
@@ -402,4 +457,43 @@ var asPaymentRail = (value) => {
|
|
|
402
457
|
return value;
|
|
403
458
|
throw new CliError("Invalid --payment-rail. Use prepaid_credits or x402.");
|
|
404
459
|
};
|
|
460
|
+
var asAuthType = (value) => {
|
|
461
|
+
if (!value)
|
|
462
|
+
return;
|
|
463
|
+
const valid = new Set([
|
|
464
|
+
"none",
|
|
465
|
+
"api_key",
|
|
466
|
+
"bearer",
|
|
467
|
+
"basic",
|
|
468
|
+
"oauth2",
|
|
469
|
+
"custom"
|
|
470
|
+
]);
|
|
471
|
+
if (valid.has(value)) {
|
|
472
|
+
return value;
|
|
473
|
+
}
|
|
474
|
+
throw new CliError("Invalid --auth-type. Use none, api_key, bearer, basic, oauth2, or custom.");
|
|
475
|
+
};
|
|
476
|
+
var asNetwork = (value) => {
|
|
477
|
+
if (!value)
|
|
478
|
+
return;
|
|
479
|
+
if (value === "base" || value === "ethereum")
|
|
480
|
+
return value;
|
|
481
|
+
throw new CliError("Invalid --network. Use base or ethereum.");
|
|
482
|
+
};
|
|
483
|
+
var asEnvironment = (value) => {
|
|
484
|
+
if (!value)
|
|
485
|
+
return;
|
|
486
|
+
if (value === "production" || value === "staging")
|
|
487
|
+
return value;
|
|
488
|
+
throw new CliError("Invalid --env. Use production or staging.");
|
|
489
|
+
};
|
|
490
|
+
var resolveApiBase = (cliApiBase, environment) => {
|
|
491
|
+
if (cliApiBase)
|
|
492
|
+
return cliApiBase;
|
|
493
|
+
if (process.env.LIDIAN_API_BASE)
|
|
494
|
+
return process.env.LIDIAN_API_BASE;
|
|
495
|
+
if (environment)
|
|
496
|
+
return API_BASE_BY_ENV[environment];
|
|
497
|
+
return DEFAULT_API_BASE;
|
|
498
|
+
};
|
|
405
499
|
main().catch(fail);
|
package/package.json
CHANGED
|
@@ -8,14 +8,14 @@ import {
|
|
|
8
8
|
|
|
9
9
|
export type PaymentRail = "prepaid_credits" | "x402";
|
|
10
10
|
|
|
11
|
-
export interface
|
|
11
|
+
export interface ConsumeCommandInput {
|
|
12
12
|
endpointId: string;
|
|
13
13
|
params: Record<string, unknown>;
|
|
14
14
|
paymentRail: PaymentRail;
|
|
15
15
|
network?: string;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
export interface
|
|
18
|
+
export interface ConsumeApiResponse {
|
|
19
19
|
data: unknown;
|
|
20
20
|
credits: {
|
|
21
21
|
spent: number;
|
|
@@ -23,19 +23,19 @@ export interface ActApiResponse {
|
|
|
23
23
|
};
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
export interface
|
|
27
|
-
execution:
|
|
26
|
+
export interface ConsumeCommandResult {
|
|
27
|
+
execution: ConsumeApiResponse;
|
|
28
28
|
payment?: {
|
|
29
29
|
requirements: PaymentRequirementsResponse;
|
|
30
30
|
verified: boolean;
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
export const
|
|
34
|
+
export const runConsumeCommand = async (
|
|
35
35
|
http: HttpClient,
|
|
36
36
|
apiKey: string,
|
|
37
|
-
input:
|
|
38
|
-
): Promise<
|
|
37
|
+
input: ConsumeCommandInput,
|
|
38
|
+
): Promise<ConsumeCommandResult> => {
|
|
39
39
|
if (!isUuid(input.endpointId)) {
|
|
40
40
|
throw new CliError("endpointId must be a valid UUID.");
|
|
41
41
|
}
|
|
@@ -53,8 +53,8 @@ export const runActCommand = async (
|
|
|
53
53
|
requirements.payTo,
|
|
54
54
|
);
|
|
55
55
|
|
|
56
|
-
const execution = await http.post<
|
|
57
|
-
"/v1/
|
|
56
|
+
const execution = await http.post<ConsumeApiResponse, ConsumeCommandInput>(
|
|
57
|
+
"/v1/consume",
|
|
58
58
|
input,
|
|
59
59
|
apiKey,
|
|
60
60
|
);
|
|
@@ -68,8 +68,8 @@ export const runActCommand = async (
|
|
|
68
68
|
};
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
const execution = await http.post<
|
|
72
|
-
"/v1/
|
|
71
|
+
const execution = await http.post<ConsumeApiResponse, ConsumeCommandInput>(
|
|
72
|
+
"/v1/consume",
|
|
73
73
|
input,
|
|
74
74
|
apiKey,
|
|
75
75
|
);
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { CliError } from "@/lib/errors";
|
|
2
|
+
import type { HttpClient } from "@/lib/http";
|
|
3
|
+
|
|
4
|
+
export interface DiscoverCommandInput {
|
|
5
|
+
q: string;
|
|
6
|
+
page: number;
|
|
7
|
+
pageSize: number;
|
|
8
|
+
category?: string;
|
|
9
|
+
authType?: "none" | "api_key" | "bearer" | "basic" | "oauth2" | "custom";
|
|
10
|
+
minPrice?: number;
|
|
11
|
+
maxPrice?: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface DiscoverApiResponse {
|
|
15
|
+
items: Array<{
|
|
16
|
+
id: string;
|
|
17
|
+
merchantId: string | null;
|
|
18
|
+
name: string;
|
|
19
|
+
description: string | null;
|
|
20
|
+
endpointBase: string;
|
|
21
|
+
authType: "none" | "api_key" | "bearer" | "basic" | "oauth2" | "custom";
|
|
22
|
+
defaultCostPerUse: number;
|
|
23
|
+
isActive: boolean;
|
|
24
|
+
openapiSpecUrl: string | null;
|
|
25
|
+
createdAt: string;
|
|
26
|
+
updatedAt: string;
|
|
27
|
+
matchScore?: number;
|
|
28
|
+
matchPercent?: number;
|
|
29
|
+
}>;
|
|
30
|
+
total: number;
|
|
31
|
+
page: number;
|
|
32
|
+
pageSize: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const runDiscoverCommand = async (
|
|
36
|
+
http: HttpClient,
|
|
37
|
+
apiKey: string,
|
|
38
|
+
input: DiscoverCommandInput,
|
|
39
|
+
): Promise<DiscoverApiResponse> => {
|
|
40
|
+
if (input.pageSize < 1 || input.pageSize > 3) {
|
|
41
|
+
throw new CliError("pageSize must be between 1 and 3.");
|
|
42
|
+
}
|
|
43
|
+
const params = new URLSearchParams();
|
|
44
|
+
params.set("q", input.q);
|
|
45
|
+
params.set("page", String(input.page));
|
|
46
|
+
params.set("pageSize", String(input.pageSize));
|
|
47
|
+
if (input.category) params.set("category", input.category);
|
|
48
|
+
if (input.authType) params.set("authType", input.authType);
|
|
49
|
+
if (typeof input.minPrice === "number") {
|
|
50
|
+
params.set("minPrice", String(input.minPrice));
|
|
51
|
+
}
|
|
52
|
+
if (typeof input.maxPrice === "number") {
|
|
53
|
+
params.set("maxPrice", String(input.maxPrice));
|
|
54
|
+
}
|
|
55
|
+
return http.get<DiscoverApiResponse>(
|
|
56
|
+
`/v1/discover?${params.toString()}`,
|
|
57
|
+
apiKey,
|
|
58
|
+
);
|
|
59
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
3
|
import { runAccountCommand } from "@/commands/account";
|
|
4
|
-
import { type PaymentRail,
|
|
4
|
+
import { type PaymentRail, runConsumeCommand } from "@/commands/consume";
|
|
5
|
+
import { runDiscoverCommand } from "@/commands/discover";
|
|
5
6
|
import { runLoginCommand } from "@/commands/login";
|
|
6
|
-
import { runQueryCommand } from "@/commands/query";
|
|
7
7
|
import { resolveApiKey } from "@/lib/auth";
|
|
8
8
|
import { CliError } from "@/lib/errors";
|
|
9
9
|
import { createHttpClient } from "@/lib/http";
|
|
@@ -11,20 +11,35 @@ import {
|
|
|
11
11
|
fail,
|
|
12
12
|
print,
|
|
13
13
|
printAccountResult,
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
printConsumeResult,
|
|
15
|
+
printDiscoverResult,
|
|
16
16
|
} from "@/lib/output";
|
|
17
17
|
|
|
18
18
|
interface ParsedArgs {
|
|
19
|
-
command: "
|
|
19
|
+
command: "discover" | "consume" | "account" | "login";
|
|
20
20
|
options: Record<string, string | boolean>;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
const DEFAULT_API_BASE = "https://api.lidian.ai";
|
|
24
|
-
const
|
|
24
|
+
const API_BASE_BY_ENV = {
|
|
25
|
+
production: "https://api.lidian.ai",
|
|
26
|
+
staging: "https://staging-api.lidian.ai",
|
|
27
|
+
} as const;
|
|
28
|
+
type Environment = keyof typeof API_BASE_BY_ENV;
|
|
29
|
+
|
|
30
|
+
const GLOBAL_OPTIONS = new Set(["api-key", "api-base", "env", "json", "help"]);
|
|
31
|
+
const BOOLEAN_OPTIONS = new Set(["json", "help"]);
|
|
25
32
|
const COMMAND_OPTIONS: Record<ParsedArgs["command"], Set<string>> = {
|
|
26
|
-
|
|
27
|
-
|
|
33
|
+
discover: new Set([
|
|
34
|
+
"q",
|
|
35
|
+
"page",
|
|
36
|
+
"pageSize",
|
|
37
|
+
"category",
|
|
38
|
+
"auth-type",
|
|
39
|
+
"min-price",
|
|
40
|
+
"max-price",
|
|
41
|
+
]),
|
|
42
|
+
consume: new Set(["endpoint-id", "params", "payment-rail", "network"]),
|
|
28
43
|
account: new Set([]),
|
|
29
44
|
login: new Set(["key"]),
|
|
30
45
|
};
|
|
@@ -35,10 +50,9 @@ const main = async (): Promise<void> => {
|
|
|
35
50
|
return;
|
|
36
51
|
}
|
|
37
52
|
const parsed = parseArgs(process.argv.slice(2));
|
|
38
|
-
const apiBase =
|
|
39
|
-
parsed.options["api-base"]
|
|
40
|
-
|
|
41
|
-
DEFAULT_API_BASE,
|
|
53
|
+
const apiBase = resolveApiBase(
|
|
54
|
+
asString(parsed.options["api-base"]),
|
|
55
|
+
asEnvironment(asString(parsed.options.env) ?? process.env.LIDIAN_ENV),
|
|
42
56
|
);
|
|
43
57
|
const asJson = Boolean(parsed.options.json);
|
|
44
58
|
const http = createHttpClient(apiBase);
|
|
@@ -54,41 +68,54 @@ const main = async (): Promise<void> => {
|
|
|
54
68
|
}
|
|
55
69
|
return;
|
|
56
70
|
}
|
|
57
|
-
case "
|
|
71
|
+
case "discover": {
|
|
58
72
|
const apiKey = await resolveApiKey(asString(parsed.options["api-key"]));
|
|
59
73
|
const qValue = asString(parsed.options.q);
|
|
60
74
|
if (!qValue) {
|
|
61
|
-
throw new CliError("Missing --q for
|
|
75
|
+
throw new CliError("Missing --q for discover command.");
|
|
62
76
|
}
|
|
63
77
|
const page = toInt(asString(parsed.options.page), 1);
|
|
64
78
|
const pageSize = toInt(asString(parsed.options.pageSize), 1);
|
|
65
|
-
const
|
|
79
|
+
const authType = asAuthType(asString(parsed.options["auth-type"]));
|
|
80
|
+
const minPrice = toNumber(
|
|
81
|
+
asString(parsed.options["min-price"]),
|
|
82
|
+
"min-price",
|
|
83
|
+
);
|
|
84
|
+
const maxPrice = toNumber(
|
|
85
|
+
asString(parsed.options["max-price"]),
|
|
86
|
+
"max-price",
|
|
87
|
+
);
|
|
88
|
+
const result = await runDiscoverCommand(http, apiKey, {
|
|
66
89
|
q: qValue,
|
|
67
90
|
page,
|
|
68
91
|
pageSize,
|
|
92
|
+
category: asString(parsed.options.category),
|
|
93
|
+
...(authType ? { authType } : {}),
|
|
94
|
+
...(typeof minPrice === "number" ? { minPrice } : {}),
|
|
95
|
+
...(typeof maxPrice === "number" ? { maxPrice } : {}),
|
|
69
96
|
});
|
|
70
|
-
|
|
97
|
+
printDiscoverResult(result, asJson);
|
|
71
98
|
return;
|
|
72
99
|
}
|
|
73
|
-
case "
|
|
100
|
+
case "consume": {
|
|
74
101
|
const apiKey = await resolveApiKey(asString(parsed.options["api-key"]));
|
|
75
102
|
const endpointIdValue = asString(parsed.options["endpoint-id"]);
|
|
76
103
|
if (!endpointIdValue) {
|
|
77
|
-
throw new CliError("Missing --endpoint-id for
|
|
104
|
+
throw new CliError("Missing --endpoint-id for consume command.");
|
|
78
105
|
}
|
|
79
106
|
const paramsRaw = asString(parsed.options.params) ?? "{}";
|
|
80
107
|
const params = parseJsonObject(paramsRaw, "--params");
|
|
81
108
|
const paymentRail = asPaymentRail(
|
|
82
109
|
asString(parsed.options["payment-rail"]) ?? "prepaid_credits",
|
|
83
110
|
);
|
|
84
|
-
const network = asString(parsed.options.network);
|
|
85
|
-
const result = await
|
|
111
|
+
const network = asNetwork(asString(parsed.options.network));
|
|
112
|
+
const result = await runConsumeCommand(http, apiKey, {
|
|
86
113
|
endpointId: endpointIdValue,
|
|
87
114
|
params,
|
|
88
115
|
paymentRail,
|
|
89
116
|
...(network ? { network } : {}),
|
|
90
117
|
});
|
|
91
|
-
|
|
118
|
+
printConsumeResult(result, asJson);
|
|
92
119
|
return;
|
|
93
120
|
}
|
|
94
121
|
case "account": {
|
|
@@ -105,14 +132,14 @@ const main = async (): Promise<void> => {
|
|
|
105
132
|
const parseArgs = (argv: string[]): ParsedArgs => {
|
|
106
133
|
const command = argv[0];
|
|
107
134
|
if (
|
|
108
|
-
command !== "
|
|
109
|
-
command !== "
|
|
135
|
+
command !== "discover" &&
|
|
136
|
+
command !== "consume" &&
|
|
110
137
|
command !== "account" &&
|
|
111
138
|
command !== "login"
|
|
112
139
|
) {
|
|
113
140
|
printUsage();
|
|
114
141
|
throw new CliError(
|
|
115
|
-
"Invalid command. Use one of: login,
|
|
142
|
+
"Invalid command. Use one of: login, discover, consume, account.",
|
|
116
143
|
1,
|
|
117
144
|
);
|
|
118
145
|
}
|
|
@@ -132,6 +159,9 @@ const parseArgs = (argv: string[]): ParsedArgs => {
|
|
|
132
159
|
}
|
|
133
160
|
const next = argv[index + 1];
|
|
134
161
|
if (!next || next.startsWith("--")) {
|
|
162
|
+
if (!BOOLEAN_OPTIONS.has(key)) {
|
|
163
|
+
throw new CliError(`Missing value for --${key}`);
|
|
164
|
+
}
|
|
135
165
|
options[key] = true;
|
|
136
166
|
index += 1;
|
|
137
167
|
continue;
|
|
@@ -151,13 +181,23 @@ const printUsage = (): void => {
|
|
|
151
181
|
print("Usage:");
|
|
152
182
|
print(" lidian login [--key ld_...] [--json]");
|
|
153
183
|
print(
|
|
154
|
-
' lidian
|
|
184
|
+
' lidian discover --q "<term>" [--page 1] [--pageSize 1..3] [--category <name>] [--auth-type none|api_key|bearer|basic|oauth2|custom]',
|
|
155
185
|
);
|
|
156
186
|
print(
|
|
157
|
-
"
|
|
187
|
+
" [--min-price <cents>] [--max-price <cents>] [--api-key <key>] [--env production|staging] [--api-base <url>] [--json]",
|
|
188
|
+
);
|
|
189
|
+
print(
|
|
190
|
+
" lidian consume --endpoint-id <uuid> --params '<json>' [--payment-rail prepaid_credits|x402] [--api-key <key>] [--env production|staging] [--api-base <url>] [--json]",
|
|
158
191
|
);
|
|
159
192
|
print(" [--network base|ethereum]");
|
|
160
|
-
print(
|
|
193
|
+
print(
|
|
194
|
+
" lidian account [--api-key <key>] [--env production|staging] [--api-base <url>] [--json]",
|
|
195
|
+
);
|
|
196
|
+
print("");
|
|
197
|
+
print("Env resolution:");
|
|
198
|
+
print(" --api-base > LIDIAN_API_BASE > --env > LIDIAN_ENV > production");
|
|
199
|
+
print(` production=${API_BASE_BY_ENV.production}`);
|
|
200
|
+
print(` staging=${API_BASE_BY_ENV.staging}`);
|
|
161
201
|
};
|
|
162
202
|
|
|
163
203
|
const asString = (value: string | boolean | undefined): string | undefined => {
|
|
@@ -168,17 +208,34 @@ const asString = (value: string | boolean | undefined): string | undefined => {
|
|
|
168
208
|
const toInt = (value: string | undefined, fallback: number): number => {
|
|
169
209
|
if (!value) return fallback;
|
|
170
210
|
const parsed = Number.parseInt(value, 10);
|
|
171
|
-
if (Number.isNaN(parsed)) {
|
|
211
|
+
if (Number.isNaN(parsed) || parsed < 1) {
|
|
172
212
|
throw new CliError(`Invalid integer value: ${value}`);
|
|
173
213
|
}
|
|
174
214
|
return parsed;
|
|
175
215
|
};
|
|
176
216
|
|
|
217
|
+
const toNumber = (
|
|
218
|
+
value: string | undefined,
|
|
219
|
+
flagName: string,
|
|
220
|
+
): number | undefined => {
|
|
221
|
+
if (!value) return undefined;
|
|
222
|
+
const parsed = Number(value);
|
|
223
|
+
if (Number.isNaN(parsed)) {
|
|
224
|
+
throw new CliError(`Invalid --${flagName} value: ${value}`);
|
|
225
|
+
}
|
|
226
|
+
return parsed;
|
|
227
|
+
};
|
|
228
|
+
|
|
177
229
|
const parseJsonObject = (
|
|
178
230
|
value: string,
|
|
179
231
|
flagName: string,
|
|
180
232
|
): Record<string, unknown> => {
|
|
181
|
-
|
|
233
|
+
let parsed: unknown;
|
|
234
|
+
try {
|
|
235
|
+
parsed = JSON.parse(value) as unknown;
|
|
236
|
+
} catch {
|
|
237
|
+
throw new CliError(`${flagName} must be valid JSON.`);
|
|
238
|
+
}
|
|
182
239
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
183
240
|
throw new CliError(`${flagName} must be a JSON object.`);
|
|
184
241
|
}
|
|
@@ -190,4 +247,59 @@ const asPaymentRail = (value: string): PaymentRail => {
|
|
|
190
247
|
throw new CliError("Invalid --payment-rail. Use prepaid_credits or x402.");
|
|
191
248
|
};
|
|
192
249
|
|
|
250
|
+
const asAuthType = (
|
|
251
|
+
value: string | undefined,
|
|
252
|
+
):
|
|
253
|
+
| "none"
|
|
254
|
+
| "api_key"
|
|
255
|
+
| "bearer"
|
|
256
|
+
| "basic"
|
|
257
|
+
| "oauth2"
|
|
258
|
+
| "custom"
|
|
259
|
+
| undefined => {
|
|
260
|
+
if (!value) return undefined;
|
|
261
|
+
const valid = new Set([
|
|
262
|
+
"none",
|
|
263
|
+
"api_key",
|
|
264
|
+
"bearer",
|
|
265
|
+
"basic",
|
|
266
|
+
"oauth2",
|
|
267
|
+
"custom",
|
|
268
|
+
]);
|
|
269
|
+
if (valid.has(value)) {
|
|
270
|
+
return value as
|
|
271
|
+
| "none"
|
|
272
|
+
| "api_key"
|
|
273
|
+
| "bearer"
|
|
274
|
+
| "basic"
|
|
275
|
+
| "oauth2"
|
|
276
|
+
| "custom";
|
|
277
|
+
}
|
|
278
|
+
throw new CliError(
|
|
279
|
+
"Invalid --auth-type. Use none, api_key, bearer, basic, oauth2, or custom.",
|
|
280
|
+
);
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
const asNetwork = (value: string | undefined): string | undefined => {
|
|
284
|
+
if (!value) return undefined;
|
|
285
|
+
if (value === "base" || value === "ethereum") return value;
|
|
286
|
+
throw new CliError("Invalid --network. Use base or ethereum.");
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
const asEnvironment = (value: string | undefined): Environment | undefined => {
|
|
290
|
+
if (!value) return undefined;
|
|
291
|
+
if (value === "production" || value === "staging") return value;
|
|
292
|
+
throw new CliError("Invalid --env. Use production or staging.");
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
const resolveApiBase = (
|
|
296
|
+
cliApiBase: string | undefined,
|
|
297
|
+
environment: Environment | undefined,
|
|
298
|
+
): string => {
|
|
299
|
+
if (cliApiBase) return cliApiBase;
|
|
300
|
+
if (process.env.LIDIAN_API_BASE) return process.env.LIDIAN_API_BASE;
|
|
301
|
+
if (environment) return API_BASE_BY_ENV[environment];
|
|
302
|
+
return DEFAULT_API_BASE;
|
|
303
|
+
};
|
|
304
|
+
|
|
193
305
|
main().catch(fail);
|
package/src/lib/output.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { AccountApiResponse } from "@/commands/account";
|
|
2
|
-
import type {
|
|
3
|
-
|
|
2
|
+
import type {
|
|
3
|
+
ConsumeApiResponse,
|
|
4
|
+
ConsumeCommandResult,
|
|
5
|
+
} from "@/commands/consume";
|
|
6
|
+
import type { DiscoverApiResponse } from "@/commands/discover";
|
|
4
7
|
import { CliError } from "@/lib/errors";
|
|
5
8
|
|
|
6
9
|
export const print = (message: string): void => {
|
|
@@ -19,8 +22,8 @@ export const printResult = (result: unknown, asJson: boolean): void => {
|
|
|
19
22
|
print(formatForHuman(result));
|
|
20
23
|
};
|
|
21
24
|
|
|
22
|
-
export const
|
|
23
|
-
result:
|
|
25
|
+
export const printDiscoverResult = (
|
|
26
|
+
result: DiscoverApiResponse,
|
|
24
27
|
asJson: boolean,
|
|
25
28
|
): void => {
|
|
26
29
|
if (asJson) {
|
|
@@ -39,13 +42,13 @@ export const printQueryResult = (
|
|
|
39
42
|
? ` confidence=${item.matchPercent.toFixed(1)}%`
|
|
40
43
|
: "";
|
|
41
44
|
print(
|
|
42
|
-
`- ${item.name} (${item.id}) auth=${item.authType} cost=${item.defaultCostPerUse}c${confidence}`,
|
|
45
|
+
`- ${item.name} (${item.id}) auth=${item.authType} cost=${item.defaultCostPerUse}c (${formatUsd(item.defaultCostPerUse)})${confidence}`,
|
|
43
46
|
);
|
|
44
47
|
}
|
|
45
48
|
};
|
|
46
49
|
|
|
47
|
-
export const
|
|
48
|
-
result:
|
|
50
|
+
export const printConsumeResult = (
|
|
51
|
+
result: ConsumeCommandResult,
|
|
49
52
|
asJson: boolean,
|
|
50
53
|
): void => {
|
|
51
54
|
if (asJson) {
|
|
@@ -69,12 +72,14 @@ export const printAccountResult = (
|
|
|
69
72
|
return;
|
|
70
73
|
}
|
|
71
74
|
print(`Account: ${result.user.id}`);
|
|
72
|
-
print(
|
|
75
|
+
print(
|
|
76
|
+
`Balance: ${result.balance.balance} cents (${formatUsd(result.balance.balance)})`,
|
|
77
|
+
);
|
|
73
78
|
};
|
|
74
79
|
|
|
75
|
-
const printExecutionResult = (result:
|
|
80
|
+
const printExecutionResult = (result: ConsumeApiResponse): void => {
|
|
76
81
|
print(
|
|
77
|
-
`Execution succeeded. Spent=${result.credits.spent} balance=${result.credits.balance}`,
|
|
82
|
+
`Execution succeeded. Spent=${result.credits.spent} cents (${formatUsd(result.credits.spent)}) balance=${result.credits.balance} cents (${formatUsd(result.credits.balance)})`,
|
|
78
83
|
);
|
|
79
84
|
print(JSON.stringify(result.data, null, 2));
|
|
80
85
|
};
|
|
@@ -86,6 +91,10 @@ const formatForHuman = (value: unknown): string => {
|
|
|
86
91
|
return JSON.stringify(value, null, 2);
|
|
87
92
|
};
|
|
88
93
|
|
|
94
|
+
const formatUsd = (cents: number): string => {
|
|
95
|
+
return `$${(cents / 100).toFixed(2)}`;
|
|
96
|
+
};
|
|
97
|
+
|
|
89
98
|
export const fail = (error: unknown): never => {
|
|
90
99
|
if (error instanceof CliError) {
|
|
91
100
|
printError(`Error: ${error.message}`);
|
package/src/commands/query.ts
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { CliError } from "@/lib/errors";
|
|
2
|
-
import type { HttpClient } from "@/lib/http";
|
|
3
|
-
|
|
4
|
-
export interface QueryCommandInput {
|
|
5
|
-
q: string;
|
|
6
|
-
page: number;
|
|
7
|
-
pageSize: number;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export interface QueryApiResponse {
|
|
11
|
-
items: Array<{
|
|
12
|
-
id: string;
|
|
13
|
-
merchantId: string | null;
|
|
14
|
-
name: string;
|
|
15
|
-
description: string | null;
|
|
16
|
-
endpointBase: string;
|
|
17
|
-
authType: "none" | "api_key" | "bearer" | "basic" | "oauth2" | "custom";
|
|
18
|
-
defaultCostPerUse: number;
|
|
19
|
-
isActive: boolean;
|
|
20
|
-
openapiSpecUrl: string | null;
|
|
21
|
-
createdAt: string;
|
|
22
|
-
updatedAt: string;
|
|
23
|
-
matchScore?: number;
|
|
24
|
-
matchPercent?: number;
|
|
25
|
-
}>;
|
|
26
|
-
total: number;
|
|
27
|
-
page: number;
|
|
28
|
-
pageSize: number;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export const runQueryCommand = async (
|
|
32
|
-
http: HttpClient,
|
|
33
|
-
apiKey: string,
|
|
34
|
-
input: QueryCommandInput,
|
|
35
|
-
): Promise<QueryApiResponse> => {
|
|
36
|
-
if (input.pageSize < 1 || input.pageSize > 3) {
|
|
37
|
-
throw new CliError("pageSize must be between 1 and 3.");
|
|
38
|
-
}
|
|
39
|
-
const params = new URLSearchParams({
|
|
40
|
-
q: input.q,
|
|
41
|
-
page: String(input.page),
|
|
42
|
-
pageSize: String(input.pageSize),
|
|
43
|
-
});
|
|
44
|
-
return http.get<QueryApiResponse>(`/v1/query?${params.toString()}`, apiKey);
|
|
45
|
-
};
|