@thinkingai/ae-cli 1.0.24 → 1.0.28

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 (106) hide show
  1. package/README.md +17 -2
  2. package/README.zh.md +18 -2
  3. package/dist/auth-5NSDQFQB.js +454 -0
  4. package/dist/{auth-4OPL4YWG.js → auth-H2376DEF.js} +2 -2
  5. package/dist/chunk-AD5V3ZPJ.js +217 -0
  6. package/dist/chunk-CAQYQA4R.js +399 -0
  7. package/dist/{chunk-JJCRURTR.js → chunk-CLJF7MQA.js} +1 -1
  8. package/dist/chunk-GSJGPNKK.js +49 -0
  9. package/dist/chunk-IMBKVXKY.js +478 -0
  10. package/dist/chunk-KAEZTSXN.js +99 -0
  11. package/dist/{chunk-MZW6NOVS.js → chunk-LU7SXK4Q.js} +27 -8
  12. package/dist/chunk-OJDNO5QY.js +63 -0
  13. package/dist/{chunk-DC66L5AM.js → chunk-SRJIAOBN.js} +76 -91
  14. package/dist/{chunk-OVHL4JAC.js → chunk-WCHI7725.js} +44 -47
  15. package/dist/{client-TDH6MUWM.js → client-FCMK3XEK.js} +5 -3
  16. package/dist/config-64G36TJL.js +130 -0
  17. package/dist/index.js +50 -17
  18. package/dist/model-EZ6K7FXI.js +134 -0
  19. package/dist/{raw-EM7AP3RT.js → raw-VEF3D6UD.js} +11 -6
  20. package/dist/sync-NRODEVNH.js +385 -0
  21. package/dist/te-agent-7U7JLJKU.js +524 -0
  22. package/dist/{te-analysis-3KFSNUCJ.js → te-analysis-A2OSGHQ4.js} +136 -20
  23. package/dist/{te-audience-GMYJFCSC.js → te-audience-LI67PPGO.js} +5 -4
  24. package/dist/{te-common-YLERCMQI.js → te-common-35WVU3C5.js} +19 -8
  25. package/dist/{te-community-PYDN5A6B.js → te-community-LEAP6PWT.js} +4 -3
  26. package/dist/{te-dataops-ETWS5O3X.js → te-dataops-N2LGZBOX.js} +4 -3
  27. package/dist/{te-engage-ROJVZHDA.js → te-engage-H7C72Z46.js} +105 -49
  28. package/dist/{te-kb-VUNS2CC7.js → te-kb-I2OKTBAI.js} +225 -29
  29. package/dist/{te-meta-3EFWLM3A.js → te-meta-HBTIEDRO.js} +4 -3
  30. package/dist/te-team-EM5OWRQL.js +529 -0
  31. package/package.json +10 -2
  32. package/skills/ae-agent/SKILL.md +134 -0
  33. package/skills/ae-analysis/SKILL.md +1 -1
  34. package/skills/ae-analysis/references/drilldown_user_events.md +1 -1
  35. package/skills/ae-analysis/references/drilldown_users.md +2 -2
  36. package/skills/ae-analysis/references/list_clusters.md +4 -2
  37. package/skills/ae-analysis/references/query_adhoc.md +3 -2
  38. package/skills/ae-analysis/references/query_entity_details.md +1 -1
  39. package/skills/ae-analysis-global/SKILL.md +51 -0
  40. package/skills/ae-analysis-global/references/list_query_clusters.md +67 -0
  41. package/skills/ae-community/SKILL.md +2 -3
  42. package/skills/ae-dataops/SKILL.md +1 -2
  43. package/skills/ae-engage/SKILL.md +26 -2
  44. package/skills/ae-engage/references/add-approver.md +0 -1
  45. package/skills/ae-engage/references/add-channel.md +0 -1
  46. package/skills/ae-engage/references/approver-list.md +0 -1
  47. package/skills/ae-engage/references/build-task-save-guide.md +283 -0
  48. package/skills/ae-engage/references/cancel-query-by-request-id.md +0 -1
  49. package/skills/ae-engage/references/channel-detail.md +0 -1
  50. package/skills/ae-engage/references/channel-list.md +0 -1
  51. package/skills/ae-engage/references/config-channel-detail.md +0 -1
  52. package/skills/ae-engage/references/config-channel-list.md +0 -1
  53. package/skills/ae-engage/references/config-item-analysis-report.md +0 -1
  54. package/skills/ae-engage/references/config-item-detail.md +0 -1
  55. package/skills/ae-engage/references/config-item-list.md +0 -1
  56. package/skills/ae-engage/references/config-item-strategy-comparison.md +0 -1
  57. package/skills/ae-engage/references/config-item-trigger-report.md +0 -1
  58. package/skills/ae-engage/references/copy-config-template.md +0 -1
  59. package/skills/ae-engage/references/delete-channel.md +0 -1
  60. package/skills/ae-engage/references/delete-config-channel.md +0 -1
  61. package/skills/ae-engage/references/delete-config-item.md +0 -1
  62. package/skills/ae-engage/references/delete-flow.md +0 -1
  63. package/skills/ae-engage/references/flow-ab-split-node-report.md +0 -1
  64. package/skills/ae-engage/references/flow-detail.md +0 -1
  65. package/skills/ae-engage/references/flow-list.md +0 -1
  66. package/skills/ae-engage/references/flow-node-config-schema.md +0 -1
  67. package/skills/ae-engage/references/flow-node-detail-report.md +0 -1
  68. package/skills/ae-engage/references/flow-node-overview-report.md +0 -1
  69. package/skills/ae-engage/references/flow-process-report.md +0 -1
  70. package/skills/ae-engage/references/manage-flow.md +0 -1
  71. package/skills/ae-engage/references/manage-strategy.md +0 -1
  72. package/skills/ae-engage/references/manage-task.md +0 -1
  73. package/skills/ae-engage/references/modify-flow-base-info.md +0 -1
  74. package/skills/ae-engage/references/save-task.md +304 -0
  75. package/skills/ae-engage/references/strategy-detail.md +0 -1
  76. package/skills/ae-engage/references/strategy-list.md +0 -1
  77. package/skills/ae-engage/references/task-data-detail.md +0 -1
  78. package/skills/ae-engage/references/task-data-overview.md +0 -1
  79. package/skills/ae-engage/references/task-detail.md +0 -1
  80. package/skills/ae-engage/references/task-experiment-report.md +0 -1
  81. package/skills/ae-engage/references/task-list.md +0 -1
  82. package/skills/ae-engage/references/task-metric-detail.md +0 -1
  83. package/skills/ae-engage/references/task-stats.md +0 -1
  84. package/skills/ae-engage/references/update-channel-status.md +0 -1
  85. package/skills/ae-engage/references/update-config-channel-status.md +0 -1
  86. package/skills/ae-engage/references/validate-flow-node-config.md +0 -1
  87. package/skills/ae-engage/references/whitelist-list.md +0 -1
  88. package/skills/ae-kb/SKILL.md +284 -0
  89. package/skills/ae-team/SKILL.md +164 -0
  90. package/skills/ae-team/references/ai-generate.md +39 -0
  91. package/skills/ae-team/references/create.md +94 -0
  92. package/skills/ae-team/references/delete.md +39 -0
  93. package/skills/ae-team/references/list-projects.md +45 -0
  94. package/skills/ae-team/references/list-templates.md +38 -0
  95. package/skills/ae-team/references/list.md +39 -0
  96. package/skills/ae-team/references/run-artifacts.md +51 -0
  97. package/skills/ae-team/references/run-cancel.md +38 -0
  98. package/skills/ae-team/references/run-chat.md +57 -0
  99. package/skills/ae-team/references/run-reply.md +41 -0
  100. package/skills/ae-team/references/run-result.md +75 -0
  101. package/skills/ae-team/references/run-start.md +73 -0
  102. package/skills/ae-team/references/run-watch.md +82 -0
  103. package/skills/ae-team/references/update.md +47 -0
  104. package/dist/auth-PBPAZQWP.js +0 -168
  105. package/dist/chunk-47MTN54I.js +0 -285
  106. package/dist/config-5ELRYAIH.js +0 -257
package/README.md CHANGED
@@ -68,6 +68,13 @@ ae-cli kb +schema --name engineering-handbook
68
68
  ae-cli kb +compile --name engineering-handbook
69
69
  ae-cli kb +query -q "How to configure sandbox?" \
70
70
  --sources '[{"scope":"company","name":"engineering-handbook"}]'
71
+
72
+ # Retrieval primitives for external agents (read index → grep → read page)
73
+ ae-cli kb +index --sources '[{"scope":"company","name":"engineering-handbook"}]'
74
+ ae-cli kb +grep -q "sandbox config" \
75
+ --sources '[{"scope":"company","name":"engineering-handbook"}]'
76
+ ae-cli kb +read --source '{"scope":"company","name":"engineering-handbook"}' \
77
+ --path "wiki/sandbox.md"
71
78
  ```
72
79
 
73
80
  ## Authentication
@@ -105,21 +112,29 @@ ae-cli auth logout
105
112
  | `dataops_integration` | 20+ | Data integration: datasource management, sync solutions, data synchronization |
106
113
  | `community` | 10+ | Community analysis: posts search, sentiment analysis, topic trends, livestream data |
107
114
  | `analysis_common` | 2 | Cross-module common constraints: resource link completion, project ID gate |
108
- | `kb` | 7 | Knowledge base lifecycle: query / new / add (md/dir/url) / schema / compile / rm-source / remove |
115
+ | `team` | 14 | AI Agent Team: manage teams (list/create/update/delete/ai-generate/templates/projects) and execute TeamRuns (start/watch/chat/reply/cancel/result/artifacts) |
116
+ | `kb` | 10 | Knowledge base lifecycle: query / new / add (md/dir/url) / schema / compile / rm-source / remove; retrieval primitives: index / grep / read |
109
117
  | `auth` / `config` | 2 | Authentication and host configuration |
110
118
 
111
119
  ### kb
112
120
 
113
- Knowledge base lifecycle (`+new` → `+add` → `+schema` → `+compile` → `+query`):
121
+ Knowledge base lifecycle (`+new` → `+add` → `+schema` → `+compile` → `+status` → `+query`):
114
122
 
115
123
  - **Create** (`+new`): `--scope personal|company`, `--name`, optional `--description`, `--tags`, `--project-id`, `--project-name`
116
124
  - **Upload** (`+add`): `--name`, `--files` JSON array (`.md` file, directory non-recursive scan, or http(s) URL with HTML-to-Markdown conversion)
117
125
  - **Schema** (`+schema`): `--name`, optional `--force`, `--model`
118
126
  - **Compile** (`+compile`): `--name`, `--mode incremental|full` (default: incremental)
127
+ - **Status** (`+status`): `--name`
119
128
  - **Query** (`+query`): `--query` / `-q`, `--sources` JSON refs e.g. `[{"scope":"company","name":"engineering-handbook"}]`
120
129
  - **Remove source** (`+rm-source`): `--name`, `--display-name`
121
130
  - **Remove KB** (`+remove`): `--name`
122
131
 
132
+ Retrieval primitives for external agents (`+index` → `+grep` → `+read`); these are deterministic file-search endpoints with no server-side LLM, designed for agents (Claude Code / Codex / Cursor) to explore a KB the way they explore a codebase:
133
+
134
+ - **Index** (`+index`): optional `--sources` JSON refs (omit to list all accessible KBs), optional `--locale`. Returns each KB's metadata plus its `index.md` navigation map.
135
+ - **Grep** (`+grep`): `--query` / `-q`, optional `--sources`, `--top-k` (1-50, default 10), `--locale`. Returns matched lines with path, line number, breadcrumb and context snippet.
136
+ - **Read** (`+read`): `--source` JSON ref (exactly one KB), `--path` (page path relative to the KB root), optional `--offset` / `--limit` (line window), `--locale`. Returns the full page or a line window.
137
+
123
138
  ### Global Options
124
139
 
125
140
  | Option | Description | Default |
package/README.zh.md CHANGED
@@ -68,6 +68,13 @@ ae-cli kb +schema --name engineering-handbook
68
68
  ae-cli kb +compile --name engineering-handbook
69
69
  ae-cli kb +query -q "如何配置 sandbox 容器?" \
70
70
  --sources '[{"scope":"company","name":"engineering-handbook"}]'
71
+
72
+ # 面向外部 agent 的检索原语(读 index → grep 定位 → read 翻页)
73
+ ae-cli kb +index --sources '[{"scope":"company","name":"engineering-handbook"}]'
74
+ ae-cli kb +grep -q "sandbox 配置" \
75
+ --sources '[{"scope":"company","name":"engineering-handbook"}]'
76
+ ae-cli kb +read --source '{"scope":"company","name":"engineering-handbook"}' \
77
+ --path "wiki/sandbox.md"
71
78
  ```
72
79
 
73
80
  ## 身份验证
@@ -105,21 +112,30 @@ ae-cli auth logout
105
112
  | `dataops_integration` | 20+ | 数据集成:数据源管理、同步方案、数据同步 |
106
113
  | `community` | 10+ | 社区分析:帖子搜索、情感分析、话题趋势、直播数据 |
107
114
  | `analysis_common` | 2 | 跨模块通用约束:资源链接补全、项目 ID 门控 |
108
- | `kb` | 7 | 知识库生命周期:查询 / 新建 / 添加源(md/目录/url)/ schema / 编译 / 删除源 / 删除 |
115
+ | `agent` | 15 | Agent 平台资源管理:模型、MCP 服务、Skill、附件库的增删查和启停 |
116
+ | `team` | 14 | AI Agent Team:团队管理(列表/创建/更新/删除/AI生成/模板/项目)和 TeamRun 执行(启动/监听/对话/回复/取消/结果/产物) |
117
+ | `kb` | 10 | 知识库生命周期:查询 / 新建 / 添加源(md/目录/url)/ schema / 编译 / 删除源 / 删除;检索原语:index / grep / read |
109
118
  | `auth` / `config` | 2 | 身份验证和主机配置 |
110
119
 
111
120
  ### kb(知识库)
112
121
 
113
- 知识库生命周期(`+new` → `+add` → `+schema` → `+compile` → `+query`):
122
+ 知识库生命周期(`+new` → `+add` → `+schema` → `+compile` → `+status` → `+query`):
114
123
 
115
124
  - **新建** (`+new`):`--scope personal|company`,`--name`,可选 `--description`、`--tags`、`--project-id`、`--project-name`
116
125
  - **上传源** (`+add`):`--name`,`--files` JSON 数组(`.md` 文件、目录非递归扫描、或 http(s) URL 自动转 Markdown)
117
126
  - **生成 schema** (`+schema`):`--name`,可选 `--force`、`--model`
118
127
  - **编译** (`+compile`):`--name`,`--mode incremental|full`(默认 incremental)
128
+ - **查询状态** (`+status`):`--name`
119
129
  - **查询** (`+query`):`--query` / `-q`,`--sources` JSON 引用,如 `[{"scope":"company","name":"engineering-handbook"}]`
120
130
  - **删除源** (`+rm-source`):`--name`,`--display-name`
121
131
  - **删除知识库** (`+remove`):`--name`
122
132
 
133
+ 面向外部 agent 的检索原语(`+index` → `+grep` → `+read`);均为确定性文件检索端点、服务端零 LLM 调用,让 agent(Claude Code / Codex / Cursor)像翻代码库一样探索知识库:
134
+
135
+ - **索引** (`+index`):可选 `--sources` JSON 引用(省略则列出全部可访问知识库),可选 `--locale`。返回每个知识库的元信息及其 `index.md` 导航地图。
136
+ - **检索** (`+grep`):`--query` / `-q`,可选 `--sources`、`--top-k`(1-50,默认 10)、`--locale`。返回命中行的路径、行号、面包屑与上下文片段。
137
+ - **读取** (`+read`):`--source` JSON 引用(精确指向一个知识库),`--path`(相对知识库根目录的页面路径),可选 `--offset` / `--limit`(行窗口)、`--locale`。返回页面全文或行窗口。
138
+
123
139
  ### 全局选项
124
140
 
125
141
  | 选项 | 描述 | 默认值 |
@@ -0,0 +1,454 @@
1
+ import {
2
+ printError,
3
+ printOutput
4
+ } from "./chunk-CLJF7MQA.js";
5
+ import {
6
+ clearMcpToken,
7
+ setMcpTokenManual,
8
+ validateMcpToken
9
+ } from "./chunk-WCHI7725.js";
10
+ import "./chunk-GSJGPNKK.js";
11
+ import {
12
+ clear,
13
+ clearToken,
14
+ getAuthStatus,
15
+ load,
16
+ resolveHost,
17
+ save,
18
+ setTokenManual,
19
+ validateToken
20
+ } from "./chunk-CAQYQA4R.js";
21
+ import {
22
+ loadConfig,
23
+ logger,
24
+ saveConfig
25
+ } from "./chunk-SRJIAOBN.js";
26
+
27
+ // src/core/device-auth.ts
28
+ import { execFileSync } from "child_process";
29
+ var DeviceFlowUnsupportedError = class extends Error {
30
+ constructor() {
31
+ super("This AE server does not support device code login (it may be an older server version).");
32
+ this.name = "DeviceFlowUnsupportedError";
33
+ }
34
+ };
35
+ function looksLikeMissingEndpoint(status, contentType, body) {
36
+ if (status === 404) return true;
37
+ if (contentType && contentType.toLowerCase().includes("text/html")) return true;
38
+ const head = body.trimStart().toLowerCase();
39
+ return head.startsWith("<!doctype html") || head.startsWith("<html");
40
+ }
41
+ function openBrowser(url, _execFile) {
42
+ let parsedUrl;
43
+ try {
44
+ parsedUrl = new URL(url);
45
+ } catch {
46
+ logger.warn(`openBrowser: invalid URL, not opening`);
47
+ return false;
48
+ }
49
+ if (!["http:", "https:"].includes(parsedUrl.protocol)) {
50
+ logger.warn(`openBrowser: unsafe protocol "${parsedUrl.protocol}", not opening`);
51
+ return false;
52
+ }
53
+ const safeLogUrl = `${parsedUrl.origin}${parsedUrl.pathname}`;
54
+ const exec = _execFile ?? execFileSync;
55
+ try {
56
+ if (process.platform === "darwin") {
57
+ exec("open", [url], { timeout: 5e3 });
58
+ return true;
59
+ }
60
+ if (process.platform === "win32") {
61
+ exec("cmd", ["/c", "start", "", url], { timeout: 5e3 });
62
+ return true;
63
+ }
64
+ exec("xdg-open", [url], { timeout: 5e3 });
65
+ return true;
66
+ } catch (e) {
67
+ logger.warn(`openBrowser: failed to open ${safeLogUrl}: ${e.message}`);
68
+ return false;
69
+ }
70
+ }
71
+ async function authorizeDevice(host) {
72
+ const base = host.replace(/\/+$/, "");
73
+ const url = `${base}/api/auth/device/authorize`;
74
+ let resp;
75
+ try {
76
+ resp = await fetch(url, {
77
+ method: "POST",
78
+ headers: { "Content-Type": "application/json" }
79
+ });
80
+ } catch (e) {
81
+ throw new Error(`Device authorize request failed: ${e.message}`);
82
+ }
83
+ const contentType = resp.headers.get("content-type");
84
+ const text = await resp.text().catch(() => "");
85
+ if (looksLikeMissingEndpoint(resp.status, contentType, text)) {
86
+ throw new DeviceFlowUnsupportedError();
87
+ }
88
+ if (!resp.ok) {
89
+ throw new Error(`Device authorize returned HTTP ${resp.status}: ${text}`);
90
+ }
91
+ let body;
92
+ try {
93
+ body = JSON.parse(text);
94
+ } catch {
95
+ throw new Error("Device authorize returned invalid JSON");
96
+ }
97
+ if (!body.device_code || !body.user_code) {
98
+ throw new Error("Device authorize response missing required fields");
99
+ }
100
+ return body;
101
+ }
102
+ async function pollDeviceToken(host, deviceCode) {
103
+ const base = host.replace(/\/+$/, "");
104
+ const url = `${base}/api/auth/device/token`;
105
+ let resp;
106
+ try {
107
+ resp = await fetch(url, {
108
+ method: "POST",
109
+ headers: { "Content-Type": "application/json" },
110
+ body: JSON.stringify({ device_code: deviceCode })
111
+ });
112
+ } catch (e) {
113
+ return { status: "error", message: `Network error: ${e.message}` };
114
+ }
115
+ if (resp.status === 200) {
116
+ let body;
117
+ try {
118
+ body = await resp.json();
119
+ } catch {
120
+ return { status: "error", message: "Token response is not valid JSON" };
121
+ }
122
+ if (!body.access_token) {
123
+ return { status: "error", message: "Token response missing access_token" };
124
+ }
125
+ return { status: "approved", tokens: body };
126
+ }
127
+ if (resp.status === 400) {
128
+ let errorBody;
129
+ try {
130
+ errorBody = await resp.json();
131
+ } catch {
132
+ errorBody = {};
133
+ }
134
+ const err = errorBody.error;
135
+ if (err === "authorization_pending") return { status: "pending" };
136
+ if (err === "slow_down") return { status: "slow_down" };
137
+ if (err === "expired_token") return { status: "expired" };
138
+ return { status: "error", message: `Unknown error: ${err ?? "unknown"}` };
139
+ }
140
+ const contentType = resp.headers.get("content-type");
141
+ const text = await resp.text().catch(() => "");
142
+ if (looksLikeMissingEndpoint(resp.status, contentType, text)) {
143
+ return { status: "unsupported" };
144
+ }
145
+ return { status: "error", message: `Unexpected HTTP ${resp.status} from token endpoint` };
146
+ }
147
+ function buildVerificationUrl(host, userCode) {
148
+ return `${host.replace(/\/+$/, "")}/device-activate?user_code=${encodeURIComponent(userCode)}`;
149
+ }
150
+ async function pollDeviceFlow(host, deviceCode, options = {}, onStatus) {
151
+ const emit = (msg) => {
152
+ if (onStatus) onStatus(msg);
153
+ };
154
+ let intervalMs = options.intervalMs ?? 5e3;
155
+ const expiresIn = options.expiresIn ?? 300;
156
+ const deadline = Date.now() + expiresIn * 1e3;
157
+ let pollCount = 0;
158
+ while (Date.now() < deadline) {
159
+ await sleep(intervalMs);
160
+ pollCount++;
161
+ const result = await pollDeviceToken(host, deviceCode);
162
+ if (result.status === "approved") {
163
+ emit(`Authorization successful! (polled ${pollCount} time(s))`);
164
+ return result.tokens;
165
+ }
166
+ if (result.status === "pending") {
167
+ if (pollCount % 5 === 0) {
168
+ const remaining = Math.max(0, Math.round((deadline - Date.now()) / 1e3));
169
+ emit(`Waiting for authorization... (expires in ${remaining}s)`);
170
+ }
171
+ logger.info(`device-auth: poll #${pollCount} pending`);
172
+ continue;
173
+ }
174
+ if (result.status === "slow_down") {
175
+ intervalMs += 5e3;
176
+ logger.info(`device-auth: slow_down received, new interval=${intervalMs}ms`);
177
+ continue;
178
+ }
179
+ if (result.status === "expired") {
180
+ throw new Error(
181
+ `Device code has expired. Please run ae-cli auth login again to restart the authorization flow.`
182
+ );
183
+ }
184
+ if (result.status === "unsupported") {
185
+ throw new DeviceFlowUnsupportedError();
186
+ }
187
+ if (result.status === "error") {
188
+ logger.warn(`device-auth: poll error: ${result.message}`);
189
+ emit(`Poll error: ${result.message}`);
190
+ if (result.message.includes("HTTP 401") || result.message.includes("HTTP 403")) {
191
+ throw new Error(`Authorization denied (${result.message}). Please check your configuration or run ae-cli auth login again.`);
192
+ }
193
+ continue;
194
+ }
195
+ }
196
+ throw new Error(
197
+ `Authorization timed out (not completed within ${expiresIn}s). Please run ae-cli auth login again.`
198
+ );
199
+ }
200
+ async function runDeviceFlow(host, options = {}, onStatus) {
201
+ const emit = (msg) => {
202
+ if (onStatus) onStatus(msg);
203
+ };
204
+ emit("Requesting authorization code from server...");
205
+ const auth = await authorizeDevice(host);
206
+ const userCode = auth.user_code;
207
+ const verifyUrl = buildVerificationUrl(host, userCode);
208
+ const expiresIn = auth.expires_in ?? 300;
209
+ emit("");
210
+ emit(`Verification code: ${userCode}`);
211
+ emit(`Open the following link in your browser to complete authorization:`);
212
+ emit(` ${verifyUrl}`);
213
+ emit("");
214
+ if (!options.noBrowser) {
215
+ const opened = openBrowser(verifyUrl);
216
+ if (opened) {
217
+ emit("(Attempted to open the link in your browser)");
218
+ } else {
219
+ emit("(Could not open browser automatically; please copy the link above manually)");
220
+ }
221
+ emit("");
222
+ }
223
+ return pollDeviceFlow(host, auth.device_code, { intervalMs: (auth.interval ?? 5) * 1e3, expiresIn }, onStatus);
224
+ }
225
+ function sleep(ms) {
226
+ return new Promise((resolve) => setTimeout(resolve, ms));
227
+ }
228
+
229
+ // src/commands/auth.ts
230
+ var HOST_OPTION_DESC = "Override active AE host URL (e.g., https://ta.thinkingdata.cn)";
231
+ function resolveAuthHost(program, opts) {
232
+ return resolveHost(opts.host || program.opts().host);
233
+ }
234
+ function persistDeviceTokens(host, tokens) {
235
+ const DEFAULT_EXPIRES_IN = 72e3;
236
+ const MAX_EXPIRES_IN = 86400 * 365;
237
+ let expiresInSeconds = tokens.expires_in ?? DEFAULT_EXPIRES_IN;
238
+ if (!Number.isFinite(expiresInSeconds) || expiresInSeconds <= 0 || expiresInSeconds > MAX_EXPIRES_IN) {
239
+ logger.warn(`auth login: expires_in=${tokens.expires_in} is out of range, falling back to ${DEFAULT_EXPIRES_IN}s`);
240
+ expiresInSeconds = DEFAULT_EXPIRES_IN;
241
+ }
242
+ const expiresAt = new Date(Date.now() + expiresInSeconds * 1e3).toISOString();
243
+ save(host, {
244
+ accessToken: tokens.access_token,
245
+ refreshToken: tokens.refresh_token ?? "",
246
+ accessExpiresAt: expiresAt,
247
+ mcpToken: tokens.mcp_token ?? void 0
248
+ });
249
+ }
250
+ function loginSummary(host, tokens) {
251
+ return {
252
+ authenticated: true,
253
+ host,
254
+ token: tokens.access_token.slice(0, 8) + "...",
255
+ mcpToken: tokens.mcp_token ? tokens.mcp_token.slice(0, 8) + "..." : null
256
+ };
257
+ }
258
+ function resolveTeClaudeBase(host) {
259
+ const override = process.env.TE_CLAUDE_BASE_URL;
260
+ if (override) return override.replace(/\/+$/, "");
261
+ const base = host.replace(/\/+$/, "");
262
+ const basePath = process.env.TE_CLAUDE_BASE_PATH || "/agent";
263
+ return base.endsWith(basePath) ? base : base + basePath;
264
+ }
265
+ function registerAuth(program) {
266
+ const auth = program.command("auth").description("Authentication management");
267
+ auth.command("login").description("Log in to AE using the device code flow (cross-platform; --no-browser to print URL only)").option("--host <url>", HOST_OPTION_DESC).option("--no-browser", "Do not auto-open the browser; only print the authorization URL (headless / Linux)").option("--no-wait", "Request the device code and print the authorization URL as JSON, then exit without polling (for AI agents / split-flow). Resume with --device-code").option("--device-code <code>", "Resume a split-flow login: poll for the given device code until authorized, then save the token").action(async (opts) => {
268
+ const host = resolveAuthHost(program, opts);
269
+ if (!host) {
270
+ printError("config", "No AE host configured.", "Run: ae-cli config set-host <url>");
271
+ process.exit(1);
272
+ }
273
+ const config = loadConfig();
274
+ if (!config.hosts[host]) {
275
+ config.hosts[host] = { label: host };
276
+ if (!config.activeHost) {
277
+ config.activeHost = host;
278
+ }
279
+ saveConfig(config);
280
+ logger.info(`Host config auto-saved for ${host}`);
281
+ process.stderr.write(`[ae-cli] Config saved for ${host}
282
+ `);
283
+ }
284
+ const noBrowser = !opts.browser;
285
+ const fmt = program.opts().format || "json";
286
+ const teClaudeBase = resolveTeClaudeBase(host);
287
+ const emit = (msg) => process.stderr.write(`[ae-cli] ${msg}
288
+ `);
289
+ try {
290
+ if (opts.deviceCode) {
291
+ const tokens2 = await pollDeviceFlow(teClaudeBase, opts.deviceCode, {}, emit);
292
+ persistDeviceTokens(host, tokens2);
293
+ logger.info(`Device flow login successful for ${host} (resumed)`);
294
+ emit("Login successful! Token saved securely.");
295
+ printOutput(loginSummary(host, tokens2), fmt);
296
+ return;
297
+ }
298
+ if (opts.wait === false) {
299
+ const authResp = await authorizeDevice(teClaudeBase);
300
+ printOutput(
301
+ {
302
+ device_code: authResp.device_code,
303
+ user_code: authResp.user_code,
304
+ verification_url: buildVerificationUrl(teClaudeBase, authResp.user_code),
305
+ interval: authResp.interval,
306
+ expires_in: authResp.expires_in,
307
+ next: `Open verification_url in a browser to authorize, then run: ae-cli auth login --device-code ${authResp.device_code}`
308
+ },
309
+ fmt
310
+ );
311
+ return;
312
+ }
313
+ const tokens = await runDeviceFlow(teClaudeBase, { noBrowser }, emit);
314
+ persistDeviceTokens(host, tokens);
315
+ logger.info(`Device flow login successful for ${host}`);
316
+ emit("Login successful! Token saved securely.");
317
+ printOutput(loginSummary(host, tokens), fmt);
318
+ } catch (err) {
319
+ if (err instanceof DeviceFlowUnsupportedError) {
320
+ printError(
321
+ "auth",
322
+ err.message,
323
+ "Use `ae-cli auth set-token <token>` to authenticate, or upgrade the AE server to a version that supports device login."
324
+ );
325
+ process.exit(1);
326
+ }
327
+ printError("auth", err.message);
328
+ process.exit(1);
329
+ }
330
+ });
331
+ auth.command("set-token <token>").description("Manually set authentication token for a host (--host or active host)").option("--host <url>", HOST_OPTION_DESC).action(async (token, opts) => {
332
+ const host = resolveAuthHost(program, opts);
333
+ if (!host) {
334
+ printError("config", "No AE host configured.", "Run: ae-cli config set-host <url>");
335
+ process.exit(1);
336
+ }
337
+ const config = loadConfig();
338
+ if (!config.hosts[host]) {
339
+ config.hosts[host] = { label: host };
340
+ if (!config.activeHost) {
341
+ config.activeHost = host;
342
+ }
343
+ saveConfig(config);
344
+ process.stderr.write(`[ae-cli] Config saved for ${host}
345
+ `);
346
+ printOutput({ saved: true, config }, program.opts().format || "json");
347
+ }
348
+ process.stderr.write(`[ae-cli] Validating token...
349
+ `);
350
+ const isValid = await validateToken(token, host);
351
+ if (!isValid) {
352
+ logger.warn(`Token validation failed for ${host}`);
353
+ process.stderr.write(`[ae-cli] Token validation failed
354
+ `);
355
+ printError("auth", "Invalid token", "Please check your token and try again");
356
+ process.exit(1);
357
+ }
358
+ setTokenManual(token, host);
359
+ logger.info(`Token manually set and validated for ${host}`);
360
+ process.stderr.write(`[ae-cli] Token verified and saved for ${host}
361
+ `);
362
+ printOutput({ saved: true, host, verified: true }, program.opts().format || "json");
363
+ });
364
+ auth.command("status").description("Show authentication status for a host (--host or active host)").option("--host <url>", HOST_OPTION_DESC).action(async (opts) => {
365
+ const host = resolveAuthHost(program, opts);
366
+ if (!host) {
367
+ printOutput({ authenticated: false, host: "(none)", hint: "Run: ae-cli config set-host <url>" }, program.opts().format || "json");
368
+ return;
369
+ }
370
+ const secureEntry = load(host);
371
+ if (secureEntry) {
372
+ const pastStaticExpiry = new Date(secureEntry.accessExpiresAt).getTime() < Date.now();
373
+ printOutput(
374
+ {
375
+ authenticated: true,
376
+ host,
377
+ source: "secure-store",
378
+ accessExpiresAt: secureEntry.accessExpiresAt,
379
+ accessExpiresAtNote: "advisory: access token auto-slides on use server-side; re-login only if a command returns an auth error",
380
+ pastStaticExpiry,
381
+ hasMcpToken: !!secureEntry.mcpToken
382
+ },
383
+ program.opts().format || "json"
384
+ );
385
+ return;
386
+ }
387
+ const tokenStatus = getAuthStatus(host);
388
+ const authenticated = tokenStatus.authenticated;
389
+ printOutput({ authenticated, host, source: tokenStatus.source ?? "none" }, program.opts().format || "json");
390
+ });
391
+ auth.command("logout").description("Clear stored token for a host (--host or active host)").option("--host <url>", HOST_OPTION_DESC).action((opts) => {
392
+ const host = resolveAuthHost(program, opts);
393
+ if (!host) {
394
+ printError("config", "No AE host configured.", "Run: ae-cli config set-host <url>");
395
+ process.exit(1);
396
+ }
397
+ clearToken(host);
398
+ clear(host);
399
+ process.stderr.write(`[ae-cli] Token cleared for ${host}
400
+ `);
401
+ printOutput({ cleared: true, host }, program.opts().format || "json");
402
+ });
403
+ auth.command("set-mcp-token <token>").description("Manually set MCP token for a host (--host or active host)").option("--host <url>", HOST_OPTION_DESC).action(async (token, opts) => {
404
+ const host = resolveAuthHost(program, opts);
405
+ if (!host) {
406
+ printError("config", "No AE host configured.", "Use --host <url> or run: ae-cli config set-host <url>");
407
+ process.exit(1);
408
+ }
409
+ const config = loadConfig();
410
+ if (!config.hosts[host]) {
411
+ config.hosts[host] = { label: host };
412
+ if (!config.activeHost) {
413
+ config.activeHost = host;
414
+ }
415
+ saveConfig(config);
416
+ process.stderr.write(`[ae-cli] Host config saved for ${host}
417
+ `);
418
+ }
419
+ process.stderr.write(`[ae-cli] Validating MCP token...
420
+ `);
421
+ const isValid = await validateMcpToken(token, host);
422
+ if (!isValid) {
423
+ process.stderr.write(`[ae-cli] MCP token validation failed
424
+ `);
425
+ printError("auth", "Invalid MCP token", "Please check your MCP token and try again");
426
+ process.exit(1);
427
+ }
428
+ setMcpTokenManual(token, host);
429
+ process.stderr.write(`[ae-cli] MCP token verified and saved for ${host}
430
+ `);
431
+ printOutput({ saved: true, host, verified: true, type: "mcp-token" }, program.opts().format || "json");
432
+ });
433
+ auth.command("clear-mcp-token").description("Clear stored MCP token for a host (--host or active host; --all clears all)").option("--host <url>", HOST_OPTION_DESC).option("--all", "Clear MCP tokens for all hosts").action((opts) => {
434
+ if (opts.all) {
435
+ clearMcpToken();
436
+ process.stderr.write(`[ae-cli] All MCP tokens cleared
437
+ `);
438
+ printOutput({ cleared: true, type: "all-mcp-tokens" }, program.opts().format || "json");
439
+ return;
440
+ }
441
+ const host = resolveAuthHost(program, opts);
442
+ if (!host) {
443
+ printError("config", "No AE host configured.", "Use --host <url> or run: ae-cli config set-host <url>");
444
+ process.exit(1);
445
+ }
446
+ clearMcpToken(host);
447
+ process.stderr.write(`[ae-cli] MCP token cleared for ${host}
448
+ `);
449
+ printOutput({ cleared: true, host }, program.opts().format || "json");
450
+ });
451
+ }
452
+ export {
453
+ registerAuth
454
+ };
@@ -7,8 +7,8 @@ import {
7
7
  saveToken,
8
8
  setTokenManual,
9
9
  validateToken
10
- } from "./chunk-47MTN54I.js";
11
- import "./chunk-DC66L5AM.js";
10
+ } from "./chunk-CAQYQA4R.js";
11
+ import "./chunk-SRJIAOBN.js";
12
12
  export {
13
13
  clearToken,
14
14
  getAuthStatus,