@lidianai/cli 0.1.0 → 0.1.2

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 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,43 @@ Bun CLI for Lidian core REST endpoints.
5
5
  ## Install
6
6
 
7
7
  ```bash
8
- npm install -g @lidian/cli
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 query --q "<term>" [--page 1] [--pageSize 1..3] [--api-key <key>] [--json]
17
- lidian act --endpoint-id <uuid> --params '<json>' [--payment-rail prepaid_credits|x402] [--network base|ethereum] [--api-key <key>] [--json]
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 feedback --execution-id <uuid> --rank <0..10> [--feedback "<text>"] [--api-key <key>] [--env production|staging] [--api-base <url>] [--json]
17
+ lidian account [--api-key <key>] [--env production|staging] [--api-base <url>] [--json]
19
18
  lidian login [--key ld_...] [--json]
20
19
  lidian --help
21
20
  ```
22
21
 
23
- `query` returns API matches (`items[]`) from `/v1/query` with metadata and confidence fields (`matchScore`, `matchPercent`).
22
+ `discover` returns API matches (`items[]`) from `/v1/discover` with metadata and confidence fields (`matchScore`, `matchPercent`).
24
23
 
25
24
  ## Auth
26
25
 
27
26
  - Store key locally: `lidian login --key ld_...` (writes `~/.lidian/config.json`)
28
27
  - Or run `lidian login` and paste your key after browser auth flow.
29
28
  - Resolution order: `--api-key` -> `LIDIAN_API_KEY` -> `~/.lidian/config.json`.
30
- - Optional: set `LIDIAN_API_BASE` (default `https://api.lidian.ai`).
29
+ - API base resolution order:
30
+ - `--api-base`
31
+ - `LIDIAN_API_BASE`
32
+ - `--env` (`production` or `staging`)
33
+ - `LIDIAN_ENV` (`production` or `staging`)
34
+ - default `https://api.lidian.ai`
31
35
 
32
36
  ## x402
33
37
 
34
38
  When `--payment-rail x402` is used, CLI performs:
35
39
  1. `POST /v1/payments/requirements`
36
40
  2. `POST /v1/payments/verify`
37
- 3. `POST /v1/act`
41
+ 3. `POST /v1/consume` (returns `executionId`)
42
+
43
+ Submit feedback later:
44
+ 4. `POST /v1/consume/feedback` with `executionId`, `rank`, optional `feedback`
38
45
 
39
46
  ## Dev
40
47
 
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/act.ts
28
- var runActCommand = async (http, apiKey, input) => {
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/act", input, apiKey);
35
+ const execution2 = await http.post("/v1/consume", input, apiKey);
36
36
  return {
37
37
  execution: execution2,
38
38
  payment: {
@@ -41,13 +41,52 @@ var runActCommand = async (http, apiKey, input) => {
41
41
  }
42
42
  };
43
43
  }
44
- const execution = await http.post("/v1/act", input, apiKey);
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
+
73
+ // src/commands/feedback.ts
74
+ var runFeedbackCommand = async (http, apiKey, input) => {
75
+ if (!isUuid2(input.executionId)) {
76
+ throw new CliError("executionId must be a valid UUID.");
77
+ }
78
+ if (!Number.isInteger(input.rank) || input.rank < 0 || input.rank > 10) {
79
+ throw new CliError("rank must be an integer between 0 and 10.");
80
+ }
81
+ if (typeof input.feedback === "string" && input.feedback.length > 1000) {
82
+ throw new CliError("feedback cannot exceed 1000 characters.");
83
+ }
84
+ return http.post("/v1/consume/feedback", input, apiKey);
85
+ };
86
+ var isUuid2 = (value) => {
87
+ 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);
88
+ };
89
+
51
90
  // src/commands/login.ts
52
91
  import { stdin, stdout } from "process";
53
92
  import { createInterface } from "readline/promises";
@@ -93,7 +132,7 @@ var printResult = (result, asJson) => {
93
132
  }
94
133
  print(formatForHuman(result));
95
134
  };
96
- var printQueryResult = (result, asJson) => {
135
+ var printDiscoverResult = (result, asJson) => {
97
136
  if (asJson) {
98
137
  printResult(result, true);
99
138
  return;
@@ -105,10 +144,10 @@ var printQueryResult = (result, asJson) => {
105
144
  print(`Found ${result.items.length} of ${result.total} APIs (page ${result.page}).`);
106
145
  for (const item of result.items) {
107
146
  const confidence = item.matchPercent ? ` confidence=${item.matchPercent.toFixed(1)}%` : "";
108
- print(`- ${item.name} (${item.id}) auth=${item.authType} cost=${item.defaultCostPerUse}c${confidence}`);
147
+ print(`- ${item.name} (${item.id}) auth=${item.authType} cost=${item.defaultCostPerUse}c (${formatUsd(item.defaultCostPerUse)})${confidence}`);
109
148
  }
110
149
  };
111
- var printActResult = (result, asJson) => {
150
+ var printConsumeResult = (result, asJson) => {
112
151
  if (asJson) {
113
152
  printResult(result.execution, true);
114
153
  return;
@@ -124,10 +163,21 @@ var printAccountResult = (result, asJson) => {
124
163
  return;
125
164
  }
126
165
  print(`Account: ${result.user.id}`);
127
- print(`Balance: ${result.balance.balance} credits`);
166
+ print(`Balance: ${result.balance.balance} cents (${formatUsd(result.balance.balance)})`);
167
+ };
168
+ var printFeedbackResult = (result, asJson) => {
169
+ if (asJson) {
170
+ printResult(result, true);
171
+ return;
172
+ }
173
+ const feedback = result.feedback ?? "<none>";
174
+ print(`Feedback saved for execution=${result.executionId} rank=${result.rank} submittedBy=${result.submittedBy}`);
175
+ print(`Updated at: ${result.updatedAt}`);
176
+ print(`Comment: ${feedback}`);
128
177
  };
129
178
  var printExecutionResult = (result) => {
130
- print(`Execution succeeded. Spent=${result.credits.spent} balance=${result.credits.balance}`);
179
+ print(`Execution ID: ${result.executionId}`);
180
+ print(`Execution succeeded. Spent=${result.credits.spent} cents (${formatUsd(result.credits.spent)}) balance=${result.credits.balance} cents (${formatUsd(result.credits.balance)})`);
131
181
  print(JSON.stringify(result.data, null, 2));
132
182
  };
133
183
  var formatForHuman = (value) => {
@@ -136,6 +186,9 @@ var formatForHuman = (value) => {
136
186
  }
137
187
  return JSON.stringify(value, null, 2);
138
188
  };
189
+ var formatUsd = (cents) => {
190
+ return `$${(cents / 100).toFixed(2)}`;
191
+ };
139
192
  var fail = (error) => {
140
193
  if (error instanceof CliError) {
141
194
  printError(`Error: ${error.message}`);
@@ -198,19 +251,6 @@ var openUrl = (url) => {
198
251
  } catch {}
199
252
  };
200
253
 
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
254
  // src/lib/auth.ts
215
255
  var resolveApiKey = async (argsApiKey) => {
216
256
  const config = await readConfig();
@@ -264,10 +304,24 @@ var handleResponse = async (response) => {
264
304
 
265
305
  // src/index.ts
266
306
  var DEFAULT_API_BASE = "https://api.lidian.ai";
267
- var GLOBAL_OPTIONS = new Set(["api-key", "api-base", "json", "help"]);
307
+ var API_BASE_BY_ENV = {
308
+ production: "https://api.lidian.ai",
309
+ staging: "https://staging-api.lidian.ai"
310
+ };
311
+ var GLOBAL_OPTIONS = new Set(["api-key", "api-base", "env", "json", "help"]);
312
+ var BOOLEAN_OPTIONS = new Set(["json", "help"]);
268
313
  var COMMAND_OPTIONS = {
269
- query: new Set(["q", "page", "pageSize"]),
270
- act: new Set(["endpoint-id", "params", "payment-rail", "network"]),
314
+ discover: new Set([
315
+ "q",
316
+ "page",
317
+ "pageSize",
318
+ "category",
319
+ "auth-type",
320
+ "min-price",
321
+ "max-price"
322
+ ]),
323
+ consume: new Set(["endpoint-id", "params", "payment-rail", "network"]),
324
+ feedback: new Set(["execution-id", "rank", "feedback"]),
271
325
  account: new Set([]),
272
326
  login: new Set(["key"])
273
327
  };
@@ -277,7 +331,7 @@ var main = async () => {
277
331
  return;
278
332
  }
279
333
  const parsed = parseArgs(process.argv.slice(2));
280
- const apiBase = String(parsed.options["api-base"] ?? process.env.LIDIAN_API_BASE ?? DEFAULT_API_BASE);
334
+ const apiBase = resolveApiBase(asString(parsed.options["api-base"]), asEnvironment(asString(parsed.options.env) ?? process.env.LIDIAN_ENV));
281
335
  const asJson = Boolean(parsed.options.json);
282
336
  const http = createHttpClient(apiBase);
283
337
  switch (parsed.command) {
@@ -291,39 +345,46 @@ var main = async () => {
291
345
  }
292
346
  return;
293
347
  }
294
- case "query": {
348
+ case "discover": {
295
349
  const apiKey = await resolveApiKey(asString(parsed.options["api-key"]));
296
350
  const qValue = asString(parsed.options.q);
297
351
  if (!qValue) {
298
- throw new CliError("Missing --q for query command.");
352
+ throw new CliError("Missing --q for discover command.");
299
353
  }
300
354
  const page = toInt(asString(parsed.options.page), 1);
301
355
  const pageSize = toInt(asString(parsed.options.pageSize), 1);
302
- const result = await runQueryCommand(http, apiKey, {
356
+ const authType = asAuthType(asString(parsed.options["auth-type"]));
357
+ const minPrice = toNumber(asString(parsed.options["min-price"]), "min-price");
358
+ const maxPrice = toNumber(asString(parsed.options["max-price"]), "max-price");
359
+ const result = await runDiscoverCommand(http, apiKey, {
303
360
  q: qValue,
304
361
  page,
305
- pageSize
362
+ pageSize,
363
+ category: asString(parsed.options.category),
364
+ ...authType ? { authType } : {},
365
+ ...typeof minPrice === "number" ? { minPrice } : {},
366
+ ...typeof maxPrice === "number" ? { maxPrice } : {}
306
367
  });
307
- printQueryResult(result, asJson);
368
+ printDiscoverResult(result, asJson);
308
369
  return;
309
370
  }
310
- case "act": {
371
+ case "consume": {
311
372
  const apiKey = await resolveApiKey(asString(parsed.options["api-key"]));
312
373
  const endpointIdValue = asString(parsed.options["endpoint-id"]);
313
374
  if (!endpointIdValue) {
314
- throw new CliError("Missing --endpoint-id for act command.");
375
+ throw new CliError("Missing --endpoint-id for consume command.");
315
376
  }
316
377
  const paramsRaw = asString(parsed.options.params) ?? "{}";
317
378
  const params = parseJsonObject(paramsRaw, "--params");
318
379
  const paymentRail = asPaymentRail(asString(parsed.options["payment-rail"]) ?? "prepaid_credits");
319
- const network = asString(parsed.options.network);
320
- const result = await runActCommand(http, apiKey, {
380
+ const network = asNetwork(asString(parsed.options.network));
381
+ const result = await runConsumeCommand(http, apiKey, {
321
382
  endpointId: endpointIdValue,
322
383
  params,
323
384
  paymentRail,
324
385
  ...network ? { network } : {}
325
386
  });
326
- printActResult(result, asJson);
387
+ printConsumeResult(result, asJson);
327
388
  return;
328
389
  }
329
390
  case "account": {
@@ -332,15 +393,35 @@ var main = async () => {
332
393
  printAccountResult(result, asJson);
333
394
  return;
334
395
  }
396
+ case "feedback": {
397
+ const apiKey = await resolveApiKey(asString(parsed.options["api-key"]));
398
+ const executionId = asString(parsed.options["execution-id"]);
399
+ if (!executionId) {
400
+ throw new CliError("Missing --execution-id for feedback command.");
401
+ }
402
+ const rankRaw = asString(parsed.options.rank);
403
+ if (!rankRaw) {
404
+ throw new CliError("Missing --rank for feedback command.");
405
+ }
406
+ const rank = toIntInRange(rankRaw, "rank", 0, 10);
407
+ const feedback = asString(parsed.options.feedback);
408
+ const result = await runFeedbackCommand(http, apiKey, {
409
+ executionId,
410
+ rank,
411
+ ...feedback ? { feedback } : {}
412
+ });
413
+ printFeedbackResult(result, asJson);
414
+ return;
415
+ }
335
416
  default:
336
417
  throw new CliError("Unknown command.", 1);
337
418
  }
338
419
  };
339
420
  var parseArgs = (argv) => {
340
421
  const command = argv[0];
341
- if (command !== "query" && command !== "act" && command !== "account" && command !== "login") {
422
+ if (command !== "discover" && command !== "consume" && command !== "feedback" && command !== "account" && command !== "login") {
342
423
  printUsage();
343
- throw new CliError("Invalid command. Use one of: login, query, act, account.", 1);
424
+ throw new CliError("Invalid command. Use one of: login, discover, consume, feedback, account.", 1);
344
425
  }
345
426
  const options = {};
346
427
  let index = 1;
@@ -356,6 +437,9 @@ var parseArgs = (argv) => {
356
437
  }
357
438
  const next = argv[index + 1];
358
439
  if (!next || next.startsWith("--")) {
440
+ if (!BOOLEAN_OPTIONS.has(key)) {
441
+ throw new CliError(`Missing value for --${key}`);
442
+ }
359
443
  options[key] = true;
360
444
  index += 1;
361
445
  continue;
@@ -371,10 +455,17 @@ var parseArgs = (argv) => {
371
455
  var printUsage = () => {
372
456
  print("Usage:");
373
457
  print(" lidian login [--key ld_...] [--json]");
374
- print(' lidian query --q "<term>" [--page 1] [--pageSize 1..3] [--api-key <key>] [--json]');
375
- print(" lidian act --endpoint-id <uuid> --params '<json>' [--payment-rail prepaid_credits|x402] [--api-key <key>] [--json]");
458
+ print(' lidian discover --q "<term>" [--page 1] [--pageSize 1..3] [--category <name>] [--auth-type none|api_key|bearer|basic|oauth2|custom]');
459
+ print(" [--min-price <cents>] [--max-price <cents>] [--api-key <key>] [--env production|staging] [--api-base <url>] [--json]");
460
+ print(" lidian consume --endpoint-id <uuid> --params '<json>' [--payment-rail prepaid_credits|x402] [--api-key <key>] [--env production|staging] [--api-base <url>] [--json]");
376
461
  print(" [--network base|ethereum]");
377
- print(" lidian account [--api-key <key>] [--json]");
462
+ print(' lidian feedback --execution-id <uuid> --rank <0..10> [--feedback "<text>"] [--api-key <key>] [--env production|staging] [--api-base <url>] [--json]');
463
+ print(" lidian account [--api-key <key>] [--env production|staging] [--api-base <url>] [--json]");
464
+ print("");
465
+ print("Env resolution:");
466
+ print(" --api-base > LIDIAN_API_BASE > --env > LIDIAN_ENV > production");
467
+ print(` production=${API_BASE_BY_ENV.production}`);
468
+ print(` staging=${API_BASE_BY_ENV.staging}`);
378
469
  };
379
470
  var asString = (value) => {
380
471
  if (typeof value === "string")
@@ -385,13 +476,34 @@ var toInt = (value, fallback) => {
385
476
  if (!value)
386
477
  return fallback;
387
478
  const parsed = Number.parseInt(value, 10);
388
- if (Number.isNaN(parsed)) {
479
+ if (Number.isNaN(parsed) || parsed < 1) {
389
480
  throw new CliError(`Invalid integer value: ${value}`);
390
481
  }
391
482
  return parsed;
392
483
  };
484
+ var toIntInRange = (value, flagName, min, max) => {
485
+ const parsed = Number.parseInt(value, 10);
486
+ if (Number.isNaN(parsed) || String(parsed) !== value || parsed < min || parsed > max) {
487
+ throw new CliError(`Invalid --${flagName} value: ${value}. Expected integer ${min}..${max}.`);
488
+ }
489
+ return parsed;
490
+ };
491
+ var toNumber = (value, flagName) => {
492
+ if (!value)
493
+ return;
494
+ const parsed = Number(value);
495
+ if (Number.isNaN(parsed)) {
496
+ throw new CliError(`Invalid --${flagName} value: ${value}`);
497
+ }
498
+ return parsed;
499
+ };
393
500
  var parseJsonObject = (value, flagName) => {
394
- const parsed = JSON.parse(value);
501
+ let parsed;
502
+ try {
503
+ parsed = JSON.parse(value);
504
+ } catch {
505
+ throw new CliError(`${flagName} must be valid JSON.`);
506
+ }
395
507
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
396
508
  throw new CliError(`${flagName} must be a JSON object.`);
397
509
  }
@@ -402,4 +514,43 @@ var asPaymentRail = (value) => {
402
514
  return value;
403
515
  throw new CliError("Invalid --payment-rail. Use prepaid_credits or x402.");
404
516
  };
517
+ var asAuthType = (value) => {
518
+ if (!value)
519
+ return;
520
+ const valid = new Set([
521
+ "none",
522
+ "api_key",
523
+ "bearer",
524
+ "basic",
525
+ "oauth2",
526
+ "custom"
527
+ ]);
528
+ if (valid.has(value)) {
529
+ return value;
530
+ }
531
+ throw new CliError("Invalid --auth-type. Use none, api_key, bearer, basic, oauth2, or custom.");
532
+ };
533
+ var asNetwork = (value) => {
534
+ if (!value)
535
+ return;
536
+ if (value === "base" || value === "ethereum")
537
+ return value;
538
+ throw new CliError("Invalid --network. Use base or ethereum.");
539
+ };
540
+ var asEnvironment = (value) => {
541
+ if (!value)
542
+ return;
543
+ if (value === "production" || value === "staging")
544
+ return value;
545
+ throw new CliError("Invalid --env. Use production or staging.");
546
+ };
547
+ var resolveApiBase = (cliApiBase, environment) => {
548
+ if (cliApiBase)
549
+ return cliApiBase;
550
+ if (process.env.LIDIAN_API_BASE)
551
+ return process.env.LIDIAN_API_BASE;
552
+ if (environment)
553
+ return API_BASE_BY_ENV[environment];
554
+ return DEFAULT_API_BASE;
555
+ };
405
556
  main().catch(fail);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lidianai/cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -8,14 +8,15 @@ import {
8
8
 
9
9
  export type PaymentRail = "prepaid_credits" | "x402";
10
10
 
11
- export interface ActCommandInput {
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 ActApiResponse {
18
+ export interface ConsumeApiResponse {
19
+ executionId: string;
19
20
  data: unknown;
20
21
  credits: {
21
22
  spent: number;
@@ -23,19 +24,19 @@ export interface ActApiResponse {
23
24
  };
24
25
  }
25
26
 
26
- export interface ActCommandResult {
27
- execution: ActApiResponse;
27
+ export interface ConsumeCommandResult {
28
+ execution: ConsumeApiResponse;
28
29
  payment?: {
29
30
  requirements: PaymentRequirementsResponse;
30
31
  verified: boolean;
31
32
  };
32
33
  }
33
34
 
34
- export const runActCommand = async (
35
+ export const runConsumeCommand = async (
35
36
  http: HttpClient,
36
37
  apiKey: string,
37
- input: ActCommandInput,
38
- ): Promise<ActCommandResult> => {
38
+ input: ConsumeCommandInput,
39
+ ): Promise<ConsumeCommandResult> => {
39
40
  if (!isUuid(input.endpointId)) {
40
41
  throw new CliError("endpointId must be a valid UUID.");
41
42
  }
@@ -53,8 +54,8 @@ export const runActCommand = async (
53
54
  requirements.payTo,
54
55
  );
55
56
 
56
- const execution = await http.post<ActApiResponse, ActCommandInput>(
57
- "/v1/act",
57
+ const execution = await http.post<ConsumeApiResponse, ConsumeCommandInput>(
58
+ "/v1/consume",
58
59
  input,
59
60
  apiKey,
60
61
  );
@@ -68,8 +69,8 @@ export const runActCommand = async (
68
69
  };
69
70
  }
70
71
 
71
- const execution = await http.post<ActApiResponse, ActCommandInput>(
72
- "/v1/act",
72
+ const execution = await http.post<ConsumeApiResponse, ConsumeCommandInput>(
73
+ "/v1/consume",
73
74
  input,
74
75
  apiKey,
75
76
  );
@@ -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
+ };
@@ -0,0 +1,44 @@
1
+ import { CliError } from "@/lib/errors";
2
+ import type { HttpClient } from "@/lib/http";
3
+
4
+ export interface FeedbackCommandInput {
5
+ executionId: string;
6
+ rank: number;
7
+ feedback?: string;
8
+ }
9
+
10
+ export interface FeedbackApiResponse {
11
+ executionId: string;
12
+ rank: number;
13
+ feedback: string | null;
14
+ submittedBy: "agent" | "human";
15
+ updatedAt: string;
16
+ }
17
+
18
+ export const runFeedbackCommand = async (
19
+ http: HttpClient,
20
+ apiKey: string,
21
+ input: FeedbackCommandInput,
22
+ ): Promise<FeedbackApiResponse> => {
23
+ if (!isUuid(input.executionId)) {
24
+ throw new CliError("executionId must be a valid UUID.");
25
+ }
26
+ if (!Number.isInteger(input.rank) || input.rank < 0 || input.rank > 10) {
27
+ throw new CliError("rank must be an integer between 0 and 10.");
28
+ }
29
+ if (typeof input.feedback === "string" && input.feedback.length > 1000) {
30
+ throw new CliError("feedback cannot exceed 1000 characters.");
31
+ }
32
+
33
+ return http.post<FeedbackApiResponse, FeedbackCommandInput>(
34
+ "/v1/consume/feedback",
35
+ input,
36
+ apiKey,
37
+ );
38
+ };
39
+
40
+ const isUuid = (value: string): boolean => {
41
+ 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(
42
+ value,
43
+ );
44
+ };
package/src/index.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  #!/usr/bin/env bun
2
2
 
3
3
  import { runAccountCommand } from "@/commands/account";
4
- import { type PaymentRail, runActCommand } from "@/commands/act";
4
+ import { type PaymentRail, runConsumeCommand } from "@/commands/consume";
5
+ import { runDiscoverCommand } from "@/commands/discover";
6
+ import { runFeedbackCommand } from "@/commands/feedback";
5
7
  import { runLoginCommand } from "@/commands/login";
6
- import { runQueryCommand } from "@/commands/query";
7
8
  import { resolveApiKey } from "@/lib/auth";
8
9
  import { CliError } from "@/lib/errors";
9
10
  import { createHttpClient } from "@/lib/http";
@@ -11,20 +12,37 @@ import {
11
12
  fail,
12
13
  print,
13
14
  printAccountResult,
14
- printActResult,
15
- printQueryResult,
15
+ printConsumeResult,
16
+ printDiscoverResult,
17
+ printFeedbackResult,
16
18
  } from "@/lib/output";
17
19
 
18
20
  interface ParsedArgs {
19
- command: "query" | "act" | "account" | "login";
21
+ command: "discover" | "consume" | "feedback" | "account" | "login";
20
22
  options: Record<string, string | boolean>;
21
23
  }
22
24
 
23
25
  const DEFAULT_API_BASE = "https://api.lidian.ai";
24
- const GLOBAL_OPTIONS = new Set(["api-key", "api-base", "json", "help"]);
26
+ const API_BASE_BY_ENV = {
27
+ production: "https://api.lidian.ai",
28
+ staging: "https://staging-api.lidian.ai",
29
+ } as const;
30
+ type Environment = keyof typeof API_BASE_BY_ENV;
31
+
32
+ const GLOBAL_OPTIONS = new Set(["api-key", "api-base", "env", "json", "help"]);
33
+ const BOOLEAN_OPTIONS = new Set(["json", "help"]);
25
34
  const COMMAND_OPTIONS: Record<ParsedArgs["command"], Set<string>> = {
26
- query: new Set(["q", "page", "pageSize"]),
27
- act: new Set(["endpoint-id", "params", "payment-rail", "network"]),
35
+ discover: new Set([
36
+ "q",
37
+ "page",
38
+ "pageSize",
39
+ "category",
40
+ "auth-type",
41
+ "min-price",
42
+ "max-price",
43
+ ]),
44
+ consume: new Set(["endpoint-id", "params", "payment-rail", "network"]),
45
+ feedback: new Set(["execution-id", "rank", "feedback"]),
28
46
  account: new Set([]),
29
47
  login: new Set(["key"]),
30
48
  };
@@ -35,10 +53,9 @@ const main = async (): Promise<void> => {
35
53
  return;
36
54
  }
37
55
  const parsed = parseArgs(process.argv.slice(2));
38
- const apiBase = String(
39
- parsed.options["api-base"] ??
40
- process.env.LIDIAN_API_BASE ??
41
- DEFAULT_API_BASE,
56
+ const apiBase = resolveApiBase(
57
+ asString(parsed.options["api-base"]),
58
+ asEnvironment(asString(parsed.options.env) ?? process.env.LIDIAN_ENV),
42
59
  );
43
60
  const asJson = Boolean(parsed.options.json);
44
61
  const http = createHttpClient(apiBase);
@@ -54,41 +71,54 @@ const main = async (): Promise<void> => {
54
71
  }
55
72
  return;
56
73
  }
57
- case "query": {
74
+ case "discover": {
58
75
  const apiKey = await resolveApiKey(asString(parsed.options["api-key"]));
59
76
  const qValue = asString(parsed.options.q);
60
77
  if (!qValue) {
61
- throw new CliError("Missing --q for query command.");
78
+ throw new CliError("Missing --q for discover command.");
62
79
  }
63
80
  const page = toInt(asString(parsed.options.page), 1);
64
81
  const pageSize = toInt(asString(parsed.options.pageSize), 1);
65
- const result = await runQueryCommand(http, apiKey, {
82
+ const authType = asAuthType(asString(parsed.options["auth-type"]));
83
+ const minPrice = toNumber(
84
+ asString(parsed.options["min-price"]),
85
+ "min-price",
86
+ );
87
+ const maxPrice = toNumber(
88
+ asString(parsed.options["max-price"]),
89
+ "max-price",
90
+ );
91
+ const result = await runDiscoverCommand(http, apiKey, {
66
92
  q: qValue,
67
93
  page,
68
94
  pageSize,
95
+ category: asString(parsed.options.category),
96
+ ...(authType ? { authType } : {}),
97
+ ...(typeof minPrice === "number" ? { minPrice } : {}),
98
+ ...(typeof maxPrice === "number" ? { maxPrice } : {}),
69
99
  });
70
- printQueryResult(result, asJson);
100
+ printDiscoverResult(result, asJson);
71
101
  return;
72
102
  }
73
- case "act": {
103
+ case "consume": {
74
104
  const apiKey = await resolveApiKey(asString(parsed.options["api-key"]));
75
105
  const endpointIdValue = asString(parsed.options["endpoint-id"]);
76
106
  if (!endpointIdValue) {
77
- throw new CliError("Missing --endpoint-id for act command.");
107
+ throw new CliError("Missing --endpoint-id for consume command.");
78
108
  }
79
109
  const paramsRaw = asString(parsed.options.params) ?? "{}";
80
110
  const params = parseJsonObject(paramsRaw, "--params");
81
111
  const paymentRail = asPaymentRail(
82
112
  asString(parsed.options["payment-rail"]) ?? "prepaid_credits",
83
113
  );
84
- const network = asString(parsed.options.network);
85
- const result = await runActCommand(http, apiKey, {
114
+ const network = asNetwork(asString(parsed.options.network));
115
+ const result = await runConsumeCommand(http, apiKey, {
86
116
  endpointId: endpointIdValue,
87
117
  params,
88
118
  paymentRail,
89
119
  ...(network ? { network } : {}),
90
120
  });
91
- printActResult(result, asJson);
121
+ printConsumeResult(result, asJson);
92
122
  return;
93
123
  }
94
124
  case "account": {
@@ -97,6 +127,26 @@ const main = async (): Promise<void> => {
97
127
  printAccountResult(result, asJson);
98
128
  return;
99
129
  }
130
+ case "feedback": {
131
+ const apiKey = await resolveApiKey(asString(parsed.options["api-key"]));
132
+ const executionId = asString(parsed.options["execution-id"]);
133
+ if (!executionId) {
134
+ throw new CliError("Missing --execution-id for feedback command.");
135
+ }
136
+ const rankRaw = asString(parsed.options.rank);
137
+ if (!rankRaw) {
138
+ throw new CliError("Missing --rank for feedback command.");
139
+ }
140
+ const rank = toIntInRange(rankRaw, "rank", 0, 10);
141
+ const feedback = asString(parsed.options.feedback);
142
+ const result = await runFeedbackCommand(http, apiKey, {
143
+ executionId,
144
+ rank,
145
+ ...(feedback ? { feedback } : {}),
146
+ });
147
+ printFeedbackResult(result, asJson);
148
+ return;
149
+ }
100
150
  default:
101
151
  throw new CliError("Unknown command.", 1);
102
152
  }
@@ -105,14 +155,15 @@ const main = async (): Promise<void> => {
105
155
  const parseArgs = (argv: string[]): ParsedArgs => {
106
156
  const command = argv[0];
107
157
  if (
108
- command !== "query" &&
109
- command !== "act" &&
158
+ command !== "discover" &&
159
+ command !== "consume" &&
160
+ command !== "feedback" &&
110
161
  command !== "account" &&
111
162
  command !== "login"
112
163
  ) {
113
164
  printUsage();
114
165
  throw new CliError(
115
- "Invalid command. Use one of: login, query, act, account.",
166
+ "Invalid command. Use one of: login, discover, consume, feedback, account.",
116
167
  1,
117
168
  );
118
169
  }
@@ -132,6 +183,9 @@ const parseArgs = (argv: string[]): ParsedArgs => {
132
183
  }
133
184
  const next = argv[index + 1];
134
185
  if (!next || next.startsWith("--")) {
186
+ if (!BOOLEAN_OPTIONS.has(key)) {
187
+ throw new CliError(`Missing value for --${key}`);
188
+ }
135
189
  options[key] = true;
136
190
  index += 1;
137
191
  continue;
@@ -151,13 +205,26 @@ const printUsage = (): void => {
151
205
  print("Usage:");
152
206
  print(" lidian login [--key ld_...] [--json]");
153
207
  print(
154
- ' lidian query --q "<term>" [--page 1] [--pageSize 1..3] [--api-key <key>] [--json]',
208
+ ' lidian discover --q "<term>" [--page 1] [--pageSize 1..3] [--category <name>] [--auth-type none|api_key|bearer|basic|oauth2|custom]',
209
+ );
210
+ print(
211
+ " [--min-price <cents>] [--max-price <cents>] [--api-key <key>] [--env production|staging] [--api-base <url>] [--json]",
155
212
  );
156
213
  print(
157
- " lidian act --endpoint-id <uuid> --params '<json>' [--payment-rail prepaid_credits|x402] [--api-key <key>] [--json]",
214
+ " lidian consume --endpoint-id <uuid> --params '<json>' [--payment-rail prepaid_credits|x402] [--api-key <key>] [--env production|staging] [--api-base <url>] [--json]",
158
215
  );
159
216
  print(" [--network base|ethereum]");
160
- print(" lidian account [--api-key <key>] [--json]");
217
+ print(
218
+ ' lidian feedback --execution-id <uuid> --rank <0..10> [--feedback "<text>"] [--api-key <key>] [--env production|staging] [--api-base <url>] [--json]',
219
+ );
220
+ print(
221
+ " lidian account [--api-key <key>] [--env production|staging] [--api-base <url>] [--json]",
222
+ );
223
+ print("");
224
+ print("Env resolution:");
225
+ print(" --api-base > LIDIAN_API_BASE > --env > LIDIAN_ENV > production");
226
+ print(` production=${API_BASE_BY_ENV.production}`);
227
+ print(` staging=${API_BASE_BY_ENV.staging}`);
161
228
  };
162
229
 
163
230
  const asString = (value: string | boolean | undefined): string | undefined => {
@@ -168,17 +235,54 @@ const asString = (value: string | boolean | undefined): string | undefined => {
168
235
  const toInt = (value: string | undefined, fallback: number): number => {
169
236
  if (!value) return fallback;
170
237
  const parsed = Number.parseInt(value, 10);
171
- if (Number.isNaN(parsed)) {
238
+ if (Number.isNaN(parsed) || parsed < 1) {
172
239
  throw new CliError(`Invalid integer value: ${value}`);
173
240
  }
174
241
  return parsed;
175
242
  };
176
243
 
244
+ const toIntInRange = (
245
+ value: string,
246
+ flagName: string,
247
+ min: number,
248
+ max: number,
249
+ ): number => {
250
+ const parsed = Number.parseInt(value, 10);
251
+ if (
252
+ Number.isNaN(parsed) ||
253
+ String(parsed) !== value ||
254
+ parsed < min ||
255
+ parsed > max
256
+ ) {
257
+ throw new CliError(
258
+ `Invalid --${flagName} value: ${value}. Expected integer ${min}..${max}.`,
259
+ );
260
+ }
261
+ return parsed;
262
+ };
263
+
264
+ const toNumber = (
265
+ value: string | undefined,
266
+ flagName: string,
267
+ ): number | undefined => {
268
+ if (!value) return undefined;
269
+ const parsed = Number(value);
270
+ if (Number.isNaN(parsed)) {
271
+ throw new CliError(`Invalid --${flagName} value: ${value}`);
272
+ }
273
+ return parsed;
274
+ };
275
+
177
276
  const parseJsonObject = (
178
277
  value: string,
179
278
  flagName: string,
180
279
  ): Record<string, unknown> => {
181
- const parsed = JSON.parse(value) as unknown;
280
+ let parsed: unknown;
281
+ try {
282
+ parsed = JSON.parse(value) as unknown;
283
+ } catch {
284
+ throw new CliError(`${flagName} must be valid JSON.`);
285
+ }
182
286
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
183
287
  throw new CliError(`${flagName} must be a JSON object.`);
184
288
  }
@@ -190,4 +294,59 @@ const asPaymentRail = (value: string): PaymentRail => {
190
294
  throw new CliError("Invalid --payment-rail. Use prepaid_credits or x402.");
191
295
  };
192
296
 
297
+ const asAuthType = (
298
+ value: string | undefined,
299
+ ):
300
+ | "none"
301
+ | "api_key"
302
+ | "bearer"
303
+ | "basic"
304
+ | "oauth2"
305
+ | "custom"
306
+ | undefined => {
307
+ if (!value) return undefined;
308
+ const valid = new Set([
309
+ "none",
310
+ "api_key",
311
+ "bearer",
312
+ "basic",
313
+ "oauth2",
314
+ "custom",
315
+ ]);
316
+ if (valid.has(value)) {
317
+ return value as
318
+ | "none"
319
+ | "api_key"
320
+ | "bearer"
321
+ | "basic"
322
+ | "oauth2"
323
+ | "custom";
324
+ }
325
+ throw new CliError(
326
+ "Invalid --auth-type. Use none, api_key, bearer, basic, oauth2, or custom.",
327
+ );
328
+ };
329
+
330
+ const asNetwork = (value: string | undefined): string | undefined => {
331
+ if (!value) return undefined;
332
+ if (value === "base" || value === "ethereum") return value;
333
+ throw new CliError("Invalid --network. Use base or ethereum.");
334
+ };
335
+
336
+ const asEnvironment = (value: string | undefined): Environment | undefined => {
337
+ if (!value) return undefined;
338
+ if (value === "production" || value === "staging") return value;
339
+ throw new CliError("Invalid --env. Use production or staging.");
340
+ };
341
+
342
+ const resolveApiBase = (
343
+ cliApiBase: string | undefined,
344
+ environment: Environment | undefined,
345
+ ): string => {
346
+ if (cliApiBase) return cliApiBase;
347
+ if (process.env.LIDIAN_API_BASE) return process.env.LIDIAN_API_BASE;
348
+ if (environment) return API_BASE_BY_ENV[environment];
349
+ return DEFAULT_API_BASE;
350
+ };
351
+
193
352
  main().catch(fail);
package/src/lib/output.ts CHANGED
@@ -1,6 +1,10 @@
1
1
  import type { AccountApiResponse } from "@/commands/account";
2
- import type { ActApiResponse, ActCommandResult } from "@/commands/act";
3
- import type { QueryApiResponse } from "@/commands/query";
2
+ import type {
3
+ ConsumeApiResponse,
4
+ ConsumeCommandResult,
5
+ } from "@/commands/consume";
6
+ import type { DiscoverApiResponse } from "@/commands/discover";
7
+ import type { FeedbackApiResponse } from "@/commands/feedback";
4
8
  import { CliError } from "@/lib/errors";
5
9
 
6
10
  export const print = (message: string): void => {
@@ -19,8 +23,8 @@ export const printResult = (result: unknown, asJson: boolean): void => {
19
23
  print(formatForHuman(result));
20
24
  };
21
25
 
22
- export const printQueryResult = (
23
- result: QueryApiResponse,
26
+ export const printDiscoverResult = (
27
+ result: DiscoverApiResponse,
24
28
  asJson: boolean,
25
29
  ): void => {
26
30
  if (asJson) {
@@ -39,13 +43,13 @@ export const printQueryResult = (
39
43
  ? ` confidence=${item.matchPercent.toFixed(1)}%`
40
44
  : "";
41
45
  print(
42
- `- ${item.name} (${item.id}) auth=${item.authType} cost=${item.defaultCostPerUse}c${confidence}`,
46
+ `- ${item.name} (${item.id}) auth=${item.authType} cost=${item.defaultCostPerUse}c (${formatUsd(item.defaultCostPerUse)})${confidence}`,
43
47
  );
44
48
  }
45
49
  };
46
50
 
47
- export const printActResult = (
48
- result: ActCommandResult,
51
+ export const printConsumeResult = (
52
+ result: ConsumeCommandResult,
49
53
  asJson: boolean,
50
54
  ): void => {
51
55
  if (asJson) {
@@ -69,12 +73,31 @@ export const printAccountResult = (
69
73
  return;
70
74
  }
71
75
  print(`Account: ${result.user.id}`);
72
- print(`Balance: ${result.balance.balance} credits`);
76
+ print(
77
+ `Balance: ${result.balance.balance} cents (${formatUsd(result.balance.balance)})`,
78
+ );
79
+ };
80
+
81
+ export const printFeedbackResult = (
82
+ result: FeedbackApiResponse,
83
+ asJson: boolean,
84
+ ): void => {
85
+ if (asJson) {
86
+ printResult(result, true);
87
+ return;
88
+ }
89
+ const feedback = result.feedback ?? "<none>";
90
+ print(
91
+ `Feedback saved for execution=${result.executionId} rank=${result.rank} submittedBy=${result.submittedBy}`,
92
+ );
93
+ print(`Updated at: ${result.updatedAt}`);
94
+ print(`Comment: ${feedback}`);
73
95
  };
74
96
 
75
- const printExecutionResult = (result: ActApiResponse): void => {
97
+ const printExecutionResult = (result: ConsumeApiResponse): void => {
98
+ print(`Execution ID: ${result.executionId}`);
76
99
  print(
77
- `Execution succeeded. Spent=${result.credits.spent} balance=${result.credits.balance}`,
100
+ `Execution succeeded. Spent=${result.credits.spent} cents (${formatUsd(result.credits.spent)}) balance=${result.credits.balance} cents (${formatUsd(result.credits.balance)})`,
78
101
  );
79
102
  print(JSON.stringify(result.data, null, 2));
80
103
  };
@@ -86,6 +109,10 @@ const formatForHuman = (value: unknown): string => {
86
109
  return JSON.stringify(value, null, 2);
87
110
  };
88
111
 
112
+ const formatUsd = (cents: number): string => {
113
+ return `$${(cents / 100).toFixed(2)}`;
114
+ };
115
+
89
116
  export const fail = (error: unknown): never => {
90
117
  if (error instanceof CliError) {
91
118
  printError(`Error: ${error.message}`);
@@ -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
- };