@agentearth.ai/cli 0.1.0-beta.0

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.
Files changed (85) hide show
  1. package/README.md +89 -0
  2. package/bin/ae.mjs +31 -0
  3. package/dist/commands/detail.d.ts +2 -0
  4. package/dist/commands/detail.js +71 -0
  5. package/dist/commands/detail.js.map +1 -0
  6. package/dist/commands/execute.d.ts +2 -0
  7. package/dist/commands/execute.js +118 -0
  8. package/dist/commands/execute.js.map +1 -0
  9. package/dist/commands/list.d.ts +2 -0
  10. package/dist/commands/list.js +94 -0
  11. package/dist/commands/list.js.map +1 -0
  12. package/dist/commands/login.d.ts +2 -0
  13. package/dist/commands/login.js +96 -0
  14. package/dist/commands/login.js.map +1 -0
  15. package/dist/commands/recommend.d.ts +2 -0
  16. package/dist/commands/recommend.js +58 -0
  17. package/dist/commands/recommend.js.map +1 -0
  18. package/dist/commands/update.d.ts +2 -0
  19. package/dist/commands/update.js +67 -0
  20. package/dist/commands/update.js.map +1 -0
  21. package/dist/commands/updateCheck.d.ts +10 -0
  22. package/dist/commands/updateCheck.js +98 -0
  23. package/dist/commands/updateCheck.js.map +1 -0
  24. package/dist/commands/updateHint.d.ts +9 -0
  25. package/dist/commands/updateHint.js +25 -0
  26. package/dist/commands/updateHint.js.map +1 -0
  27. package/dist/commands/updateNpm.d.ts +13 -0
  28. package/dist/commands/updateNpm.js +64 -0
  29. package/dist/commands/updateNpm.js.map +1 -0
  30. package/dist/constants.d.ts +20 -0
  31. package/dist/constants.js +34 -0
  32. package/dist/constants.js.map +1 -0
  33. package/dist/core/args.d.ts +2 -0
  34. package/dist/core/args.js +110 -0
  35. package/dist/core/args.js.map +1 -0
  36. package/dist/core/backendErrors.d.ts +10 -0
  37. package/dist/core/backendErrors.js +69 -0
  38. package/dist/core/backendErrors.js.map +1 -0
  39. package/dist/core/errors.d.ts +20 -0
  40. package/dist/core/errors.js +44 -0
  41. package/dist/core/errors.js.map +1 -0
  42. package/dist/core/hiddenInput.d.ts +10 -0
  43. package/dist/core/hiddenInput.js +53 -0
  44. package/dist/core/hiddenInput.js.map +1 -0
  45. package/dist/core/http.d.ts +9 -0
  46. package/dist/core/http.js +144 -0
  47. package/dist/core/http.js.map +1 -0
  48. package/dist/core/output.d.ts +11 -0
  49. package/dist/core/output.js +33 -0
  50. package/dist/core/output.js.map +1 -0
  51. package/dist/main.d.ts +1 -0
  52. package/dist/main.js +300 -0
  53. package/dist/main.js.map +1 -0
  54. package/dist/storage/credentials.d.ts +9 -0
  55. package/dist/storage/credentials.js +97 -0
  56. package/dist/storage/credentials.js.map +1 -0
  57. package/dist/storage/fileProtection.d.ts +12 -0
  58. package/dist/storage/fileProtection.js +51 -0
  59. package/dist/storage/fileProtection.js.map +1 -0
  60. package/dist/storage/paths.d.ts +6 -0
  61. package/dist/storage/paths.js +55 -0
  62. package/dist/storage/paths.js.map +1 -0
  63. package/dist/storage/session.d.ts +23 -0
  64. package/dist/storage/session.js +175 -0
  65. package/dist/storage/session.js.map +1 -0
  66. package/dist/storage/telemetryQueue.d.ts +14 -0
  67. package/dist/storage/telemetryQueue.js +252 -0
  68. package/dist/storage/telemetryQueue.js.map +1 -0
  69. package/dist/storage/updateCache.d.ts +19 -0
  70. package/dist/storage/updateCache.js +80 -0
  71. package/dist/storage/updateCache.js.map +1 -0
  72. package/dist/telemetry/events.d.ts +42 -0
  73. package/dist/telemetry/events.js +80 -0
  74. package/dist/telemetry/events.js.map +1 -0
  75. package/dist/telemetry/uploader.d.ts +2 -0
  76. package/dist/telemetry/uploader.js +46 -0
  77. package/dist/telemetry/uploader.js.map +1 -0
  78. package/dist/types/api.d.ts +47 -0
  79. package/dist/types/api.js +9 -0
  80. package/dist/types/api.js.map +1 -0
  81. package/dist/types/cli.d.ts +42 -0
  82. package/dist/types/cli.js +9 -0
  83. package/dist/types/cli.js.map +1 -0
  84. package/package.json +29 -0
  85. package/skills/agentearth/SKILL.md +37 -0
package/README.md ADDED
@@ -0,0 +1,89 @@
1
+ # AgentEarth CLI
2
+
3
+ AgentEarth CLI is an agent-first command line interface that helps AI agents discover, inspect, and execute AgentEarth tools from any shell.
4
+
5
+ ## Commands
6
+
7
+ ```text
8
+ ae login
9
+ ae recommend
10
+ ae list
11
+ ae detail
12
+ ae execute
13
+ ae update
14
+ ```
15
+
16
+ `ae update` is already a stable command entry. Version discovery uses `npm view @agentearth.ai/cli version --json`; applying an update runs `npm install -g @agentearth.ai/cli@latest`, then verifies the active `ae --version`. If npm registry version discovery fails, the command returns `version_check_failed`.
17
+
18
+ ## Install And Login
19
+
20
+ ```text
21
+ npm install -g @agentearth.ai/cli
22
+ ae login --api-key-stdin
23
+ ```
24
+
25
+ `ae login --api-key-stdin` verifies the API key and saves it to the CLI credentials file. After login, read the JSON `skill.source` field and install or reference that skill in the agent before calling tools.
26
+
27
+ `--api-key` and `AGENT_EARTH_API_KEY` are supported for compatibility and debugging, but `--api-key-stdin` is the recommended install path because it avoids putting the key in command arguments.
28
+
29
+ ## Agent Workflows
30
+
31
+ Use `ae list` when the task category is already clear:
32
+
33
+ ```text
34
+ list -> detail -> execute
35
+ ```
36
+
37
+ Use `ae recommend` when the agent is not sure which tool category fits the task:
38
+
39
+ ```text
40
+ recommend -> detail -> execute
41
+ ```
42
+
43
+ The agent should use `tool_name` from `ae list` or `ae recommend`, then call:
44
+
45
+ ```text
46
+ ae detail <tool_name>
47
+ ae execute <tool_name> '<json>'
48
+ ```
49
+
50
+ ## Output Contract
51
+
52
+ Core commands default to JSON. Success is written to stdout, failure is written to stderr.
53
+
54
+ Success and failure are determined by exit code:
55
+
56
+ ```text
57
+ exit code = 0: success
58
+ exit code != 0: failure
59
+ ```
60
+
61
+ Failure JSON is stable:
62
+
63
+ ```json
64
+ {
65
+ "error": {
66
+ "code": "params_invalid",
67
+ "message": "Params must be valid JSON and no larger than 1MB.",
68
+ "hint": "Run ae detail mock_tool and provide params that match the schema."
69
+ }
70
+ }
71
+ ```
72
+
73
+ See `docs/Agent输出格式.md` for the agent-facing stdout/stderr format.
74
+
75
+ ## Development
76
+
77
+ ```text
78
+ npm install
79
+ npm run typecheck
80
+ npm run build
81
+ npm test
82
+ node bin/ae.mjs --help
83
+ ```
84
+
85
+ Package name is set to `@agentearth.ai/cli` for publishing.
86
+
87
+ ## Documentation
88
+
89
+ Project documentation lives in `docs/`.
package/bin/ae.mjs ADDED
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * 这是 npm 暴露给系统的真正可执行入口。
5
+ *
6
+ * 用户或 agent 在终端里输入 `ae ...` 时,npm 会把命令转到这个文件。
7
+ * 这个文件只做一件事:把命令行参数交给编译后的 `dist/main.js`。
8
+ * 业务逻辑全部放在 TypeScript 源码里,这样入口文件保持很薄,也方便排查问题。
9
+ */
10
+ import { main } from "../dist/main.js";
11
+
12
+ // `process.argv` 是 Node.js 提供的原始命令行参数数组。
13
+ // 第 1 项是 node 可执行文件路径,第 2 项是当前脚本路径。
14
+ // 从第 3 项开始,才是用户真正传给 `ae` 的参数。
15
+ main(process.argv.slice(2)).catch((error) => {
16
+ // 正常业务错误会在 `main.ts` 中被转换成稳定的 CLI 错误对象。
17
+ // 能走到这里,说明启动阶段发生了意外错误,例如 dist 文件不存在。
18
+ const message = error instanceof Error ? error.message : String(error);
19
+
20
+ // 即使是兜底错误,也保持 JSON 结构,避免 agent 收到无法解析的纯文本。
21
+ console.error(JSON.stringify({
22
+ error: {
23
+ code: "unknown",
24
+ message,
25
+ hint: "Run ae --help."
26
+ }
27
+ }, null, 2));
28
+
29
+ // 非 0 退出码表示命令失败;调用方可以用退出码判断流程是否中断。
30
+ process.exitCode = 1;
31
+ });
@@ -0,0 +1,2 @@
1
+ import type { CommandContext, CommandSuccess } from "../types/cli.js";
2
+ export declare function runDetail(context: CommandContext, args: string[]): Promise<CommandSuccess>;
@@ -0,0 +1,71 @@
1
+ /**
2
+ * `ae detail` 命令实现。
3
+ *
4
+ * detail 代替旧的 schema 命令。agent 通过 tool_name 请求某个工具的详细信息,
5
+ * CLI 调用后端 detail 接口,拿到最新工具元数据和 input_schema,然后输出给 agent。
6
+ * 同时,detail 返回的完整 tool_url 也会刷新本地缓存,方便后续 execute 使用。
7
+ */
8
+ import { TOOL_DETAIL_PATH } from "../constants.js";
9
+ import { mapBackendError } from "../core/backendErrors.js";
10
+ import { CliError, usageError } from "../core/errors.js";
11
+ import { getJson } from "../core/http.js";
12
+ import { resolveApiKey } from "../storage/credentials.js";
13
+ import { extractToolName, writeSession } from "../storage/session.js";
14
+ export async function runDetail(context, args) {
15
+ // detail 只接受一个位置参数:tool_name。
16
+ // tool_name 来自 shell 文本,先 trim,避免前后空白污染后端查询参数。
17
+ const toolName = args[0]?.trim();
18
+ // 缺 tool_name 或多传参数都属于用法错误。
19
+ if (!toolName || args.length !== 1) {
20
+ throw usageError("Missing or invalid detail arguments.", "Run ae detail <tool_name>.");
21
+ }
22
+ // 读取 API key;如果本地没 login,会在这里抛 auth_missing。
23
+ const apiKey = await resolveApiKey(context.options);
24
+ // detail 是 GET 接口,tool_name 放到 query string。
25
+ const response = await getJson(TOOL_DETAIL_PATH, { tool_name: toolName }, {
26
+ apiKey,
27
+ baseUrl: context.options.baseUrl,
28
+ timeoutMs: context.options.timeoutMs
29
+ });
30
+ // 后端返回业务错误时,统一映射成 CLI 错误码。
31
+ if (response.error_no !== 0) {
32
+ throw mapBackendError("detail", response.error_msg);
33
+ }
34
+ // error_no=0 后仍要校验 tools 是数组,否则后续 find 会抛 unknown,agent 无法稳定处理。
35
+ if (!Array.isArray(response.tools)) {
36
+ throw new CliError("backend_response_invalid", "Detail response is missing tools array.", "Retry later.");
37
+ }
38
+ // detail 成功时必须至少返回一个工具。
39
+ const tools = response.tools;
40
+ if (tools.length === 0) {
41
+ throw new CliError("backend_response_invalid", "Detail response contains no tool.", "Retry later.");
42
+ }
43
+ // 先在写缓存前确认后端返回的是请求的工具,避免契约异常污染本地 session。
44
+ const parsedTools = tools.map((tool) => ({
45
+ tool,
46
+ parsedToolName: extractToolName(tool.tool_url)
47
+ }));
48
+ const targetTool = parsedTools.find((item) => item.parsedToolName === toolName)?.tool;
49
+ if (!targetTool) {
50
+ // 没找到目标工具时,要区分 tool_url 解析失败和可解析但 tool_name 不匹配。
51
+ if (parsedTools.some((item) => !item.parsedToolName)) {
52
+ throw new CliError("tool_name_parse_failed", "Tool response is missing tool_name or tool_url.", "Retry later.");
53
+ }
54
+ throw new CliError("backend_response_invalid", "Detail response does not match requested tool_name.", "Retry later.");
55
+ }
56
+ // detail 的核心价值是拿 schema;缺 schema 时 agent 无法正确拼 execute params。
57
+ if (targetTool.input_schema == null) {
58
+ throw new CliError("schema_missing", "Detail response is missing input_schema.", "Retry later.");
59
+ }
60
+ // 校验通过后只写入目标工具,避免无关工具进入本地缓存。
61
+ const [tool] = await writeSession([targetTool]);
62
+ // 输出给 agent 的字段不包含 tool_url,只包含选择和拼参需要的信息。
63
+ return {
64
+ tool_name: tool.tool_name,
65
+ description: tool.description,
66
+ when_to_use: tool.when_to_use,
67
+ credit: tool.credit,
68
+ schema: tool.input_schema
69
+ };
70
+ }
71
+ //# sourceMappingURL=detail.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detail.js","sourceRoot":"","sources":["../../src/commands/detail.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { CommandContext, CommandSuccess } from "../types/cli.js";
2
+ export declare function runExecute(context: CommandContext, args: string[]): Promise<CommandSuccess>;
@@ -0,0 +1,118 @@
1
+ /**
2
+ * `ae execute` 命令实现。
3
+ *
4
+ * execute 是真正执行工具的命令。agent 只需要传 tool_name 和 params,
5
+ * CLI 会从本地缓存中找到该 tool_name 对应的完整 tool_url,然后向后端发送
6
+ * `{ params }`。这样 agent 不需要知道 tool_url、recommend_id 或其他内部细节。
7
+ */
8
+ import { readFile, stat } from "node:fs/promises";
9
+ import { mapBackendError } from "../core/backendErrors.js";
10
+ import { CliError, usageError } from "../core/errors.js";
11
+ import { postJson } from "../core/http.js";
12
+ import { resolveApiKey } from "../storage/credentials.js";
13
+ import { readToolByName } from "../storage/session.js";
14
+ // 文档规定 @file/stdin 的参数体最大 1MB,避免 agent 误把超大内容塞给 CLI。
15
+ const MAX_PARAMS_BYTES = 1024 * 1024;
16
+ export async function runExecute(context, args) {
17
+ // execute 只支持两个位置参数:tool_name 和 params。
18
+ // tool_name 来自 shell 文本,先 trim;params 原文不能 trim,否则可能改变 inline JSON 内容。
19
+ const toolName = args[0]?.trim();
20
+ const rawParams = args[1];
21
+ // 少参数或多参数都拒绝,避免继续兼容旧的 recommend_id + index 语法。
22
+ if (!toolName || args.length !== 2) {
23
+ throw usageError("Missing or invalid execute arguments.", "Run ae execute <tool_name> '<json>'.");
24
+ }
25
+ // 把 agent 传入的 params 来源解析成 JS 值,准备放进请求体。
26
+ const params = await readParams(rawParams, toolName);
27
+ // params 可能来自 @file 或 stdin,main.ts 不能为了埋点再读一次;这里把已解析结果暂存给失败埋点使用。
28
+ context.telemetry.executeParams = params;
29
+ // 从本地 cache 找到工具;找不到时返回 tool_name_invalid,不请求后端。
30
+ const tool = await readToolByName(toolName);
31
+ // 读取 API key;execute 请求必须鉴权。
32
+ const apiKey = await resolveApiKey(context.options);
33
+ // 注意这里用的是缓存中的完整 tool_url,不自己用 tool_name 拼 URL。
34
+ const response = await postJson(tool.tool_url, { params }, {
35
+ apiKey,
36
+ baseUrl: context.options.baseUrl,
37
+ timeoutMs: context.options.timeoutMs
38
+ });
39
+ // 后端业务失败时,根据 error_msg 映射成稳定 CLI 错误。
40
+ if (response.error_no !== 0) {
41
+ throw mapBackendError("execute", response.error_msg);
42
+ }
43
+ // error_no=0 只说明后端认为执行成功;result 字段仍必须存在,否则 agent 会收到不可用的空成功。
44
+ if (!Object.prototype.hasOwnProperty.call(response, "result")) {
45
+ throw new CliError("backend_response_invalid", "Execute response is missing result.", "Retry later.");
46
+ }
47
+ // execute 成功后只把 agent 需要处理的结果输出出去。
48
+ return {
49
+ tool_name: tool.tool_name,
50
+ result: response.result,
51
+ agentearth_suggestion: response.agentearth_suggestion
52
+ };
53
+ }
54
+ async function readParams(raw, toolName) {
55
+ try {
56
+ // 先按 inline/@file/stdin 三种形式取到 JSON 文本。
57
+ const jsonText = await readParamsText(raw, toolName);
58
+ // JSON.parse 可以得到 object、array、string、number 等 JS 值。
59
+ const value = JSON.parse(jsonText);
60
+ if (!isRecord(value)) {
61
+ throw paramsInvalidError(toolName);
62
+ }
63
+ return value;
64
+ }
65
+ catch (error) {
66
+ // 如果前面已经明确归因为 params_invalid,直接抛出,保留更准确 message。
67
+ if (error instanceof CliError) {
68
+ throw error;
69
+ }
70
+ // 文件读取失败、stdin 读取失败、JSON 解析失败都属于本地 params_invalid。
71
+ throw paramsInvalidError(toolName);
72
+ }
73
+ }
74
+ async function readParamsText(raw, toolName) {
75
+ // 以 @ 开头表示从文件读取 JSON,主要解决 shell 引号复杂的问题。
76
+ if (raw.startsWith("@")) {
77
+ return readParamsFile(raw.slice(1), toolName);
78
+ }
79
+ if (raw === "-") {
80
+ // 单独的 - 表示从 stdin 读取 JSON,适合管道调用。
81
+ return readStdin(toolName);
82
+ }
83
+ // 默认把 raw 当作 inline JSON,例如 '{"city":"110101"}'。
84
+ return raw;
85
+ }
86
+ async function readParamsFile(filePath, toolName) {
87
+ // 文件形式可以在读取前先看字节大小,避免把超大文件读入内存。
88
+ const fileStat = await stat(filePath);
89
+ if (fileStat.size > MAX_PARAMS_BYTES) {
90
+ throw paramsInvalidError(toolName);
91
+ }
92
+ return readFile(filePath, "utf8");
93
+ }
94
+ async function readStdin(toolName) {
95
+ // stdin 是流,可能分多块到达,所以先收集到数组。
96
+ const chunks = [];
97
+ let totalBytes = 0;
98
+ for await (const chunk of process.stdin) {
99
+ // chunk 可能是 Buffer,也可能是字符串;统一转成 Buffer。
100
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
101
+ totalBytes += buffer.byteLength;
102
+ if (totalBytes > MAX_PARAMS_BYTES) {
103
+ throw paramsInvalidError(toolName);
104
+ }
105
+ chunks.push(buffer);
106
+ }
107
+ // 所有块拼接后按 utf8 转回字符串。
108
+ return Buffer.concat(chunks).toString("utf8");
109
+ }
110
+ function paramsInvalidError(toolName) {
111
+ // 错误文案不能包含参数原文,避免把敏感或超大内容写进 stderr/埋点。
112
+ return new CliError("params_invalid", "Params must be valid JSON and no larger than 1MB.", `Run ae detail ${toolName} and provide params that match the schema.`);
113
+ }
114
+ function isRecord(value) {
115
+ // execute params 必须是普通对象;数组、null 和标量都不能作为工具参数对象。
116
+ return typeof value === "object" && value !== null && !Array.isArray(value);
117
+ }
118
+ //# sourceMappingURL=execute.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"execute.js","sourceRoot":"","sources":["../../src/commands/execute.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAIvD,qDAAqD;AACrD,MAAM,gBAAgB,GAAG,IAAI,GAAG,IAAI,CAAC;AAErC,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAAuB,EAAE,IAAc;IACtE,wCAAwC;IACxC,uEAAuE;IACvE,MAAM,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;IACjC,MAAM,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IAE1B,+CAA+C;IAC/C,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnC,MAAM,UAAU,CAAC,uCAAuC,EAAE,sCAAsC,CAAC,CAAC;IACpG,CAAC;IAED,yCAAyC;IACzC,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IACrD,kEAAkE;IAClE,OAAO,CAAC,SAAS,CAAC,aAAa,GAAG,MAAM,CAAC;IACzC,iDAAiD;IACjD,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,QAAQ,CAAC,CAAC;IAC5C,6BAA6B;IAC7B,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAEpD,+CAA+C;IAC/C,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAC7B,IAAI,CAAC,QAAQ,EACb,EAAE,MAAM,EAAE,EACV;QACE,MAAM;QACN,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,OAAO;QAChC,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,SAAS;KACrC,CACF,CAAC;IAEF,qCAAqC;IACrC,IAAI,QAAQ,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;QAC5B,MAAM,eAAe,CAAC,SAAS,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;IACvD,CAAC;IAED,6DAA6D;IAC7D,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC;QAC9D,MAAM,IAAI,QAAQ,CAAC,0BAA0B,EAAE,qCAAqC,EAAE,cAAc,CAAC,CAAC;IACxG,CAAC;IAED,mCAAmC;IACnC,OAAO;QACL,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,qBAAqB,EAAE,QAAQ,CAAC,qBAAqB;KACtD,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,GAAW,EAAE,QAAgB;IACrD,IAAI,CAAC;QACH,wCAAwC;QACxC,MAAM,QAAQ,GAAG,MAAM,cAAc,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QACrD,qDAAqD;QACrD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAY,CAAC;QAC9C,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YACrB,MAAM,kBAAkB,CAAC,QAAQ,CAAC,CAAC;QACrC,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,iDAAiD;QACjD,IAAI,KAAK,YAAY,QAAQ,EAAE,CAAC;YAC9B,MAAM,KAAK,CAAC;QACd,CAAC;QAED,mDAAmD;QACnD,MAAM,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IACrC,CAAC;AACH,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,GAAW,EAAE,QAAgB;IACzD,yCAAyC;IACzC,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,OAAO,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;IAChD,CAAC;IAED,IAAI,GAAG,KAAK,GAAG,EAAE,CAAC;QAChB,kCAAkC;QAClC,OAAO,SAAS,CAAC,QAAQ,CAAC,CAAC;IAC7B,CAAC;IAED,iDAAiD;IACjD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,QAAgB,EAAE,QAAgB;IAC9D,gCAAgC;IAChC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC;IACtC,IAAI,QAAQ,CAAC,IAAI,GAAG,gBAAgB,EAAE,CAAC;QACrC,MAAM,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IACrC,CAAC;IAED,OAAO,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;AACpC,CAAC;AAED,KAAK,UAAU,SAAS,CAAC,QAAgB;IACvC,6BAA6B;IAC7B,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,UAAU,GAAG,CAAC,CAAC;IAEnB,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QACxC,wCAAwC;QACxC,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACnE,UAAU,IAAI,MAAM,CAAC,UAAU,CAAC;QAChC,IAAI,UAAU,GAAG,gBAAgB,EAAE,CAAC;YAClC,MAAM,kBAAkB,CAAC,QAAQ,CAAC,CAAC;QACrC,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACtB,CAAC;IAED,sBAAsB;IACtB,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AAChD,CAAC;AAED,SAAS,kBAAkB,CAAC,QAAgB;IAC1C,uCAAuC;IACvC,OAAO,IAAI,QAAQ,CACjB,gBAAgB,EAChB,mDAAmD,EACnD,iBAAiB,QAAQ,4CAA4C,CACtE,CAAC;AACJ,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,iDAAiD;IACjD,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { CommandContext, CommandSuccess } from "../types/cli.js";
2
+ export declare function runList(context: CommandContext, args: string[]): Promise<CommandSuccess>;
@@ -0,0 +1,94 @@
1
+ /**
2
+ * `ae list` 命令实现。
3
+ *
4
+ * list 是“agent 已经知道大致工具类别”时的入口。它调用后端工具列表接口,
5
+ * 可以接收一个可选 JSON object,把其中的一层标量字段转成 URL query 参数。
6
+ * 这样后端以后新增筛选字段时,CLI 不需要马上改命令语法。
7
+ */
8
+ import { TOOLS_LIST_PATH } from "../constants.js";
9
+ import { mapBackendError } from "../core/backendErrors.js";
10
+ import { CliError, usageError } from "../core/errors.js";
11
+ import { getJson } from "../core/http.js";
12
+ import { resolveApiKey } from "../storage/credentials.js";
13
+ import { writeSession } from "../storage/session.js";
14
+ import { readUpdateHint } from "./updateHint.js";
15
+ export async function runList(context, args) {
16
+ // list 最多接收一个 JSON 参数;多了就说明 agent 用法不对。
17
+ if (args.length > 1) {
18
+ throw usageError("Missing or invalid list arguments.", "Run ae list or ae list '<json>'.");
19
+ }
20
+ // 没传 JSON 时使用默认分页;传了 JSON 时解析成 query。
21
+ const query = parseListQuery(args[0]);
22
+ // list 是后端业务请求,需要 API key。
23
+ const apiKey = await resolveApiKey(context.options);
24
+ // GET 请求会把 query 中的字段拼到 URL 后面。
25
+ const response = await getJson(TOOLS_LIST_PATH, query, {
26
+ apiKey,
27
+ baseUrl: context.options.baseUrl,
28
+ timeoutMs: context.options.timeoutMs
29
+ });
30
+ // 后端业务失败统一走错误映射。
31
+ if (response.error_no !== 0) {
32
+ throw mapBackendError("list", response.error_msg);
33
+ }
34
+ // error_no=0 后仍要校验 tools 是数组,避免结构损坏的成功响应污染后续缓存。
35
+ if (!Array.isArray(response.tools)) {
36
+ throw new CliError("backend_response_invalid", "List response is missing tools array.", "Retry later.");
37
+ }
38
+ // list 返回的工具也写入本地缓存,后续 execute 可以直接通过 tool_name 找到 tool_url。
39
+ const cachedTools = await writeSession(response.tools);
40
+ const result = {
41
+ total: response.total ?? cachedTools.length,
42
+ page: response.page ?? query.page,
43
+ page_size: response.page_size ?? query.page_size,
44
+ tools: cachedTools.map((tool) => ({
45
+ tool_name: tool.tool_name,
46
+ description: tool.description,
47
+ when_to_use: tool.when_to_use,
48
+ credit: tool.credit
49
+ }))
50
+ };
51
+ const update = await readUpdateHint();
52
+ if (update) {
53
+ result.update = update;
54
+ }
55
+ return result;
56
+ }
57
+ function parseListQuery(raw) {
58
+ // 没有传筛选条件时,默认请求第一页,每页 20 条。
59
+ if (raw === undefined) {
60
+ return { page: 1, page_size: 20 };
61
+ }
62
+ // agent 一旦传了 list 参数,该参数就必须是非空 JSON object,否则属于命令用法错误。
63
+ if (!raw.trim()) {
64
+ throw usageError("List query must be valid JSON.", "Run ae list '{\"page\":1,\"page_size\":20}'.");
65
+ }
66
+ let value;
67
+ try {
68
+ // raw 必须是 JSON 字符串,例如 {"page":1,"page_size":20}。
69
+ value = JSON.parse(raw);
70
+ }
71
+ catch {
72
+ throw usageError("List query must be valid JSON.", "Run ae list '{\"page\":1,\"page_size\":20}'.");
73
+ }
74
+ // list 参数必须是 object,不能是数组、字符串或数字。
75
+ if (!isRecord(value)) {
76
+ throw usageError("List query must be a JSON object.", "Run ae list '{\"page\":1,\"page_size\":20}'.");
77
+ }
78
+ const query = {};
79
+ for (const [key, fieldValue] of Object.entries(value)) {
80
+ // 为了避免复杂对象序列化歧义,只允许一层 string/number/boolean。
81
+ if (typeof fieldValue !== "string"
82
+ && typeof fieldValue !== "number"
83
+ && typeof fieldValue !== "boolean") {
84
+ throw usageError("List query only supports first-level scalar fields.", "Use string, number, or boolean values in ae list '<json>'.");
85
+ }
86
+ query[key] = fieldValue;
87
+ }
88
+ return query;
89
+ }
90
+ function isRecord(value) {
91
+ // TypeScript 里 object 也包含数组,所以这里显式排除 Array。
92
+ return typeof value === "object" && value !== null && !Array.isArray(value);
93
+ }
94
+ //# sourceMappingURL=list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,OAAO,EAAmB,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAIjD,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,OAAuB,EAAE,IAAc;IACnE,wCAAwC;IACxC,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpB,MAAM,UAAU,CAAC,oCAAoC,EAAE,kCAAkC,CAAC,CAAC;IAC7F,CAAC;IAED,sCAAsC;IACtC,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACtC,2BAA2B;IAC3B,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAEpD,gCAAgC;IAChC,MAAM,QAAQ,GAAG,MAAM,OAAO,CAC5B,eAAe,EACf,KAAK,EACL;QACE,MAAM;QACN,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,OAAO;QAChC,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,SAAS;KACrC,CACF,CAAC;IAEF,iBAAiB;IACjB,IAAI,QAAQ,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;QAC5B,MAAM,eAAe,CAAC,MAAM,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;IACpD,CAAC;IAED,gDAAgD;IAChD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,QAAQ,CAAC,0BAA0B,EAAE,uCAAuC,EAAE,cAAc,CAAC,CAAC;IAC1G,CAAC;IAED,6DAA6D;IAC7D,MAAM,WAAW,GAAG,MAAM,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAEvD,MAAM,MAAM,GAAmB;QAC7B,KAAK,EAAE,QAAQ,CAAC,KAAK,IAAI,WAAW,CAAC,MAAM;QAC3C,IAAI,EAAE,QAAQ,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI;QACjC,SAAS,EAAE,QAAQ,CAAC,SAAS,IAAI,KAAK,CAAC,SAAS;QAChD,KAAK,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAChC,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,MAAM,EAAE,IAAI,CAAC,MAAM;SACpB,CAAC,CAAC;KACJ,CAAC;IACF,MAAM,MAAM,GAAG,MAAM,cAAc,EAAE,CAAC;IACtC,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC;IACzB,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,cAAc,CAAC,GAAY;IAClC,4BAA4B;IAC5B,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;IACpC,CAAC;IAED,uDAAuD;IACvD,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC;QAChB,MAAM,UAAU,CAAC,gCAAgC,EAAE,8CAA8C,CAAC,CAAC;IACrG,CAAC;IAED,IAAI,KAAc,CAAC;IACnB,IAAI,CAAC;QACH,iDAAiD;QACjD,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,UAAU,CAAC,gCAAgC,EAAE,8CAA8C,CAAC,CAAC;IACrG,CAAC;IAED,kCAAkC;IAClC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACrB,MAAM,UAAU,CAAC,mCAAmC,EAAE,8CAA8C,CAAC,CAAC;IACxG,CAAC;IAED,MAAM,KAAK,GAA+B,EAAE,CAAC;IAC7C,KAAK,MAAM,CAAC,GAAG,EAAE,UAAU,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACtD,6CAA6C;QAC7C,IACE,OAAO,UAAU,KAAK,QAAQ;eAC3B,OAAO,UAAU,KAAK,QAAQ;eAC9B,OAAO,UAAU,KAAK,SAAS,EAClC,CAAC;YACD,MAAM,UAAU,CACd,qDAAqD,EACrD,4DAA4D,CAC7D,CAAC;QACJ,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC;IAC1B,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,4CAA4C;IAC5C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { CommandContext, CommandSuccess } from "../types/cli.js";
2
+ export declare function runLogin(context: CommandContext): Promise<CommandSuccess>;
@@ -0,0 +1,96 @@
1
+ /**
2
+ * `ae login` 命令实现。
3
+ *
4
+ * login 只负责第一次安装或用户主动换 key 时的本地初始化:
5
+ * 1. 从 `--api-key-stdin`、`--api-key`、`AGENT_EARTH_API_KEY` 或 TTY 隐藏输入读取 API key;
6
+ * 2. 调用 recommend 接口做一次轻量验证;
7
+ * 3. 验证成功后,把 key 写入本机 credentials.json。
8
+ * 4. 输出 npm 包内置 skill 的 source,让 agent 知道如何安装或引用使用说明。
9
+ *
10
+ * 注意:这里不生成普通 config,也不生成机器身份 ID。后续命令会自动读取
11
+ * credentials.json,agent 不需要每次自己携带 API key。
12
+ */
13
+ import { fileURLToPath } from "node:url";
14
+ import { CLI_VERSION, RECOMMEND_PATH } from "../constants.js";
15
+ import { mapBackendError } from "../core/backendErrors.js";
16
+ import { CliError, usageError } from "../core/errors.js";
17
+ import { readHiddenInput } from "../core/hiddenInput.js";
18
+ import { postJson } from "../core/http.js";
19
+ import { resolveLoginApiKey, writeCredentials } from "../storage/credentials.js";
20
+ // 编译后本文件位于 dist/commands/login.js,向上两级就是 npm 包根目录。
21
+ // skill 文件不复制进 dist,而是作为 package files 中的独立资源发布。
22
+ const SKILL_SOURCE = fileURLToPath(new URL("../../skills/agentearth/SKILL.md", import.meta.url));
23
+ export async function runLogin(context) {
24
+ // login 不读旧 credentials,必须拿到本次传入的新 key。
25
+ const apiKey = await resolveLoginApiKeyForLogin(context);
26
+ // 复用 recommend 接口验证 key,避免为了 login 单独开发验证接口。
27
+ const response = await postJson(RECOMMEND_PATH,
28
+ // 固定 query 和 limit=1 只是为了让验证请求足够轻量。
29
+ { query: "weather tool", limit: 1 }, {
30
+ apiKey,
31
+ baseUrl: context.options.baseUrl,
32
+ timeoutMs: context.options.timeoutMs
33
+ });
34
+ // 后端业务失败时,转换成 CLI 稳定错误码。
35
+ if (response.error_no !== 0) {
36
+ throw mapBackendError("login", response.error_msg);
37
+ }
38
+ try {
39
+ // 只有后端验证通过后才落盘,避免把明显错误的 key 保存起来。
40
+ await writeCredentials(apiKey);
41
+ }
42
+ catch {
43
+ // credentials 写失败是后端看不到的本地错误,需要用专门错误码。
44
+ throw new CliError("credentials_write_failed", "Failed to write local credentials.", "Check write permission for AgentEarth credentials directory.");
45
+ }
46
+ // 成功输出不包含 key,也不包含本地路径,只返回 agent/用户需要知道的信息。
47
+ return {
48
+ version: CLI_VERSION,
49
+ skill: {
50
+ required: true,
51
+ phase: 1,
52
+ source: SKILL_SOURCE,
53
+ message: "Install this skill into your agent before using AgentEarth CLI."
54
+ },
55
+ next: "Install the skill, then run ae list or ae recommend.",
56
+ message: "AgentEarth CLI login succeeded."
57
+ };
58
+ }
59
+ async function resolveLoginApiKeyForLogin(context) {
60
+ // --api-key 和 --api-key-stdin 不能同时使用,否则 key 来源不明确。
61
+ if (context.options.apiKey && context.options.apiKeyStdin) {
62
+ throw usageError("Use either --api-key or --api-key-stdin, not both.");
63
+ }
64
+ if (context.options.apiKeyStdin) {
65
+ // stdin 模式适合 agent/脚本:key 不会出现在 shell history 或进程参数里。
66
+ const apiKey = (await readStdin()).trim();
67
+ if (!apiKey) {
68
+ throw new CliError("auth_missing", "No API key provided on stdin.", "Pipe the API key into ae login --api-key-stdin.");
69
+ }
70
+ return apiKey;
71
+ }
72
+ try {
73
+ // 非 stdin 模式优先沿用原有来源:--api-key 或 AGENT_EARTH_API_KEY。
74
+ return resolveLoginApiKey(context.options);
75
+ }
76
+ catch (error) {
77
+ // 如果没有任何显式 key 来源,人工 TTY 场景进入隐藏输入;非 TTY 会继续返回 auth_missing。
78
+ if (error instanceof CliError && error.code === "auth_missing") {
79
+ const apiKey = (await readHiddenInput("AgentEarth API key: ")).trim();
80
+ if (!apiKey) {
81
+ throw new CliError("auth_missing", "No API key provided.", "Pipe the API key into ae login --api-key-stdin.");
82
+ }
83
+ return apiKey;
84
+ }
85
+ throw error;
86
+ }
87
+ }
88
+ async function readStdin() {
89
+ // stdin 是流,可能分多块到达,所以先收集所有 chunk。
90
+ const chunks = [];
91
+ for await (const chunk of process.stdin) {
92
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
93
+ }
94
+ return Buffer.concat(chunks).toString("utf8");
95
+ }
96
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { CommandContext, CommandSuccess } from "../types/cli.js";
2
+ export declare function runRecommend(context: CommandContext, args: string[]): Promise<CommandSuccess>;
@@ -0,0 +1,58 @@
1
+ /**
2
+ * `ae recommend` 命令实现。
3
+ *
4
+ * recommend 是“不明确工具类别”时的入口。agent 把用户任务原文传给 CLI,
5
+ * CLI 请求后端推荐工具。后端返回的 tool_url 中带有全局唯一 tool_name,
6
+ * CLI 会解析 tool_name 并把完整 tool_url 缓存在本地,最终只把 tool_name
7
+ * 和工具摘要输出给 agent。
8
+ */
9
+ import { RECOMMEND_PATH } from "../constants.js";
10
+ import { mapBackendError } from "../core/backendErrors.js";
11
+ import { CliError, usageError } from "../core/errors.js";
12
+ import { postJson } from "../core/http.js";
13
+ import { resolveApiKey } from "../storage/credentials.js";
14
+ import { writeSession } from "../storage/session.js";
15
+ import { readUpdateHint } from "./updateHint.js";
16
+ export async function runRecommend(context, args) {
17
+ // recommend 的 query 允许由多个位置参数组成,例如 ae recommend weather in Beijing。
18
+ const query = args.join(" ").trim();
19
+ // 没有 query 时,后端无法推荐工具,直接返回用法错误。
20
+ if (!query) {
21
+ throw usageError("Missing recommend query.", "Run ae recommend \"weather in Beijing\".");
22
+ }
23
+ // 业务命令读取 key 的顺序是 --api-key -> 环境变量 -> credentials.json。
24
+ const apiKey = await resolveApiKey(context.options);
25
+ // 调用真实 recommend API。当前主流程不让 agent 指定 limit。
26
+ const response = await postJson(RECOMMEND_PATH, { query }, {
27
+ apiKey,
28
+ baseUrl: context.options.baseUrl,
29
+ timeoutMs: context.options.timeoutMs
30
+ });
31
+ // error_no 非 0 表示后端业务失败,需要映射成稳定 CLI 错误。
32
+ if (response.error_no !== 0) {
33
+ throw mapBackendError("recommend", response.error_msg);
34
+ }
35
+ // error_no=0 只能说明业务成功,CLI 仍要校验响应结构,避免坏响应被当成空推荐结果。
36
+ if (!Array.isArray(response.tools)) {
37
+ throw new CliError("backend_response_invalid", "Recommend response is missing tools array.", "Retry later.");
38
+ }
39
+ // 本地缓存保存完整 tool_url/input_schema;输出给 agent 时不暴露 tool_url。
40
+ const cachedTools = await writeSession(response.tools);
41
+ // agent 只需要 tool_name 和选择信息;后续 detail/execute 都用 tool_name。
42
+ const result = {
43
+ // total 是后端返回的匹配总量;缺失时才用本次缓存工具数兜底。
44
+ total: response.total ?? cachedTools.length,
45
+ tools: cachedTools.map((tool) => ({
46
+ tool_name: tool.tool_name,
47
+ description: tool.description,
48
+ when_to_use: tool.when_to_use,
49
+ credit: tool.credit
50
+ }))
51
+ };
52
+ const update = await readUpdateHint();
53
+ if (update) {
54
+ result.update = update;
55
+ }
56
+ return result;
57
+ }
58
+ //# sourceMappingURL=recommend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recommend.js","sourceRoot":"","sources":["../../src/commands/recommend.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAIjD,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAAuB,EAAE,IAAc;IACxE,oEAAoE;IACpE,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAEpC,gCAAgC;IAChC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,UAAU,CAAC,0BAA0B,EAAE,0CAA0C,CAAC,CAAC;IAC3F,CAAC;IAED,yDAAyD;IACzD,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAEpD,6CAA6C;IAC7C,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAC7B,cAAc,EACd,EAAE,KAAK,EAAE,EACT;QACE,MAAM;QACN,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,OAAO;QAChC,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,SAAS;KACrC,CACF,CAAC;IAEF,wCAAwC;IACxC,IAAI,QAAQ,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;QAC5B,MAAM,eAAe,CAAC,WAAW,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;IACzD,CAAC;IAED,kDAAkD;IAClD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,QAAQ,CAAC,0BAA0B,EAAE,4CAA4C,EAAE,cAAc,CAAC,CAAC;IAC/G,CAAC;IAED,0DAA0D;IAC1D,MAAM,WAAW,GAAG,MAAM,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAEvD,4DAA4D;IAC5D,MAAM,MAAM,GAAmB;QAC7B,mCAAmC;QACnC,KAAK,EAAE,QAAQ,CAAC,KAAK,IAAI,WAAW,CAAC,MAAM;QAC3C,KAAK,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAChC,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,MAAM,EAAE,IAAI,CAAC,MAAM;SACpB,CAAC,CAAC;KACJ,CAAC;IACF,MAAM,MAAM,GAAG,MAAM,cAAc,EAAE,CAAC;IACtC,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC;IACzB,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { CommandContext, CommandSuccess } from "../types/cli.js";
2
+ export declare function runUpdate(context: CommandContext): Promise<CommandSuccess>;