deepminer-cli 0.1.3-2.new

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 XMingAI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,275 @@
1
+ # DeepMiner CLI
2
+
3
+ DeepMiner 系统的命令行工具,用于管理认证、配置和 AI 会话。
4
+
5
+ ## 功能特性
6
+
7
+ - **跨平台支持**: macOS、Linux、Windows
8
+ - **AccessKey 鉴权**: 通过 AccessKey 进行 API 认证
9
+ - **配置管理**: API 端点和 AccessKey 配置
10
+ - **AI 会话**: 启动/继续/停止 AI 助手会话,支持自定义消息、文件附件、会话追问和 claw_param 参数
11
+ - **会话结果**: 获取会话的最终结果和状态
12
+ - **JSON 输出**: 所有命令支持 `--json` 结构化输出,适合 AI agent 和脚本调用
13
+ - **错误提示**: 错误响应包含 `hint` 字段,提供可操作的修复建议
14
+ - **Dry Run**: `--dry-run` 预览 HTTP 请求而不实际执行
15
+ - **CLI Schema**: 内置 `schema` 命令输出机器可读的能力描述
16
+ - **zsh 自动补全**: 安装后自动配置
17
+
18
+ ## 快速安装
19
+
20
+ ### 方式一:使用 npm(推荐)
21
+
22
+ ```bash
23
+ npm install -g deepminer-cli
24
+ dm-cli version
25
+ ```
26
+
27
+ ### 方式二:从源码编译
28
+
29
+ ```bash
30
+ git clone https://code.mlamp.cn/dm/packages.git
31
+ cd dm-cli
32
+ go build -o dm-cli .
33
+ ```
34
+
35
+ ## 快速开始
36
+
37
+ ```bash
38
+ # 1. 初始化配置
39
+ dm-cli config init --endpoint https://dm-dev.xmingai.com --accesskey <your_access_key>
40
+
41
+ # 2. 启动 AI 会话(返回 agent_run_id、thread_id)
42
+ dm-cli agent start-thread --message "你好"
43
+
44
+ # 3. 带文件附件发送
45
+ dm-cli agent start-thread --message "请分析这个报告" --file ./report.ppt
46
+
47
+ # 4. 在已有会话中继续追问
48
+ dm-cli agent start-thread --message "继续聊" --thread-id <thread_id>
49
+
50
+ # 5. 获取会话结果
51
+ dm-cli thread result --thread-id <thread_id>
52
+
53
+ # 6. 停止正在运行的 Agent
54
+ dm-cli agent stop --thread-id <thread_id>
55
+
56
+ # 7. 查看用户打开的 agent
57
+ dm-cli agent user-open
58
+ ```
59
+
60
+ ### JSON 模式(适合 AI agent / 脚本)
61
+
62
+ 所有命令加 `--json` 输出结构化 JSON:
63
+
64
+ ```bash
65
+ dm-cli auth status --json
66
+ # {"ok": true, "data": {"auth_mode": "accesskey", "access_key": "..."}}
67
+
68
+ dm-cli agent start-thread --message "分析" --file ./data.xlsx --json
69
+ # {"ok": true, "data": {"agent_run_id": "...", "thread_id": "...", ...}}
70
+ ```
71
+
72
+ ## 命令列表
73
+
74
+ ### 配置
75
+
76
+ ```bash
77
+ dm-cli config init --endpoint <url> # 初始化配置(设置 API 端点)
78
+ dm-cli config init --endpoint <url> --accesskey <key> # 初始化配置并设置 AccessKey
79
+ dm-cli config init --poll-timeout 30 # 设置轮询超时为 30 分钟(默认 60)
80
+ dm-cli config show # 显示当前配置
81
+ ```
82
+
83
+ ### 认证
84
+
85
+ ```bash
86
+ dm-cli auth status # 显示鉴权状态
87
+ ```
88
+
89
+ ### AI 会话
90
+
91
+ ```bash
92
+ dm-cli agent start-thread --message <消息> # 发送文本消息
93
+ dm-cli agent start-thread --message <消息> --file <路径> # 附带文件
94
+ dm-cli agent start-thread --message <消息> --file <路径1> --file <路径2> # 附带多个文件
95
+ dm-cli agent start-thread --message <消息> --thread-id <会话ID> # 在已有会话中继续追问
96
+ dm-cli agent start-thread --message <消息> --claw-param <JSON> # 附带渠道参数
97
+ dm-cli agent start-thread --message <消息> --agent-mode <模式> # 指定 agent_mode(默认 auto)
98
+ dm-cli agent start-thread --message <消息> --force # 会话运行中强制发送新问题
99
+ dm-cli agent stop --thread-id <会话ID> # 停止正在运行的 Agent
100
+ dm-cli agent user-open # 列出用户打开的 agent
101
+ ```
102
+
103
+ #### 文件附件说明
104
+
105
+ 使用 `--file` 参数附带本地文件,CLI 会自动上传并拼接到消息中。可多次指定以附带多个文件。
106
+
107
+ **文件大小限制**:
108
+
109
+ | 类型 | 扩展名 | 最大大小 |
110
+ |------|--------|----------|
111
+ | 视频 | mp4 | 500 MB |
112
+ | 音频 | mp3/wav/aac | 100 MB |
113
+ | 其他 | * | 300 MB |
114
+
115
+ #### claw_param 参数说明
116
+
117
+ 可选的 `--claw-param` 用于指定 DM 系统的渠道和发送者信息:
118
+
119
+ ```json
120
+ {
121
+ "channel": "dmwork",
122
+ "sender_account_id": "bot_account_id",
123
+ "source_user_id": "user_id",
124
+ "targets": [
125
+ {
126
+ "channel": "dmwork",
127
+ "chat_type": "direct",
128
+ "channel_id": "target_channel_id",
129
+ "sender_bot_token": "bot_token"
130
+ }
131
+ ]
132
+ }
133
+ ```
134
+
135
+ ### 会话结果
136
+
137
+ ```bash
138
+ dm-cli thread result --thread-id <会话ID> # 获取会话结果
139
+ ```
140
+
141
+ ### Schema
142
+
143
+ ```bash
144
+ dm-cli schema # 输出 CLI Schema(压缩 JSON)
145
+ dm-cli schema --pretty # 输出 CLI Schema(格式化 JSON)
146
+ ```
147
+
148
+ Schema 命令输出 dm-cli 的完整能力描述,供 AI 系统或自动化工具解析。
149
+
150
+ ### 其他
151
+
152
+ ```bash
153
+ dm-cli version # 显示版本号
154
+ dm-cli --help # 显示帮助信息
155
+ ```
156
+
157
+ ## 全局 Flag
158
+
159
+ | Flag | 说明 |
160
+ |------|------|
161
+ | `--json` | 以 JSON 格式输出结果 |
162
+ | `--dry-run` | 预览 HTTP 请求而不实际执行 |
163
+ | `--help` | 显示帮助信息 |
164
+
165
+ ### JSON 输出格式
166
+
167
+ 成功:
168
+ ```json
169
+ {"ok": true, "data": {...}}
170
+ ```
171
+
172
+ 错误(含修复建议):
173
+ ```json
174
+ {"ok": false, "error": {"type": "auth", "message": "未配置 AccessKey", "hint": "运行 'dm-cli config init --endpoint <url> --accesskey <key>' 配置"}}
175
+ ```
176
+
177
+ `hint` 字段提供可操作的修复建议,AI agent 可直接据此自动恢复。
178
+
179
+ ### Dry Run 模式
180
+
181
+ 使用 `--dry-run` 预览请求而不实际执行,适合调试和 AI agent 安全验证:
182
+
183
+ ```bash
184
+ dm-cli --dry-run --json agent start-thread --message "你好"
185
+ ```
186
+ ```json
187
+ {"ok": true, "dry_run": true, "data": {"method": "POST", "url": "...", "headers": {...}, "body": {...}}}
188
+ ```
189
+
190
+ 只读命令(`config show`、`auth status`、`version`、`schema`)和纯本地操作(`config init`)不受 `--dry-run` 影响,正常执行。
191
+
192
+ ## 文件说明
193
+
194
+ ### 配置文件:`~/.dm-cli/config.json`
195
+
196
+ ```json
197
+ {
198
+ "api_endpoint": "https://dm-dev.xmingai.com/api",
199
+ "access_key": "your_access_key"
200
+ }
201
+ ```
202
+
203
+ ## 项目结构
204
+
205
+ ```
206
+ dm-cli/
207
+ ├── cmd/ # 命令定义
208
+ │ ├── root.go # 根命令
209
+ │ ├── auth/ # 鉴权状态命令
210
+ │ ├── config/ # 配置命令
211
+ │ ├── agent/ # AI 命令
212
+ │ ├── thread/ # 会话命令
213
+ │ └── schema/ # Schema 输出命令
214
+ ├── internal/
215
+ │ ├── client/ # HTTP 客户端
216
+ │ ├── config/ # 配置管理
217
+ │ ├── cmdutil/ # 命令工具
218
+ │ ├── fileutil/ # 文件上传共享逻辑
219
+ │ ├── output/ # 输出处理
220
+ │ └── validate/ # 输入验证
221
+ ├── schema/ # CLI Schema 定义(嵌入)
222
+ ├── skills/ # AI Agent Skill 定义
223
+ ├── main.go # 入口点
224
+ ├── go.mod # 依赖定义
225
+ └── Makefile # 构建脚本
226
+ ```
227
+
228
+ ## 开发
229
+
230
+ ### 本地编译和测试
231
+
232
+ ```bash
233
+ # 编译
234
+ go build -o dm-cli .
235
+
236
+ # 初始化配置
237
+ ./dm-cli config init --endpoint https://dm-dev.xmingai.com --accesskey <your_access_key>
238
+
239
+ # 查看鉴权状态
240
+ ./dm-cli auth status
241
+ ```
242
+
243
+ ### 跨平台编译
244
+
245
+ ```bash
246
+ make build-all
247
+ # 输出: dm-cli-linux-amd64, dm-cli-darwin-amd64, dm-cli-darwin-arm64, dm-cli-windows-amd64.exe
248
+ ```
249
+
250
+ ## 文档
251
+
252
+ - [npm 发布指南](./NPM_RELEASE.md) - 项目维护者发布新版本
253
+ - [快速开始](./QUICK_START_FOR_USERS.md) - 用户快速上手
254
+ - [安装指南](./INSTALL.md) - 详细安装说明
255
+
256
+ ## 安全考虑
257
+
258
+ - 配置文件存储在 `~/.dm-cli/` 目录,权限为 0600
259
+ - AccessKey 以明文存储在配置文件中,请确保文件权限安全
260
+
261
+ ## zsh 补全
262
+
263
+ 安装后 zsh 补全会自动配置。尝试:
264
+
265
+ ```bash
266
+ dm-cli <TAB> # 显示所有命令
267
+ dm-cli auth <TAB> # 显示 auth 子命令
268
+ dm-cli config <TAB> # 显示 config 子命令
269
+ dm-cli agent <TAB> # 显示 agent 子命令
270
+ dm-cli thread <TAB> # 显示 thread 子命令
271
+ ```
272
+
273
+ ## 许可证
274
+
275
+ MIT
Binary file
Binary file
Binary file
Binary file
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "deepminer-cli",
3
+ "version": "0.1.32.new",
4
+ "description": "DeepMiner CLI - Command-line tool for DM system with cross-platform support, JWT authentication, and workspace management",
5
+ "keywords": [
6
+ "deepminer",
7
+ "dm",
8
+ "cli",
9
+ "command-line",
10
+ "authentication",
11
+ "jwt",
12
+ "api-client"
13
+ ],
14
+ "homepage": "https://www.npmjs.com/package/deepminer-cli",
15
+ "license": "MIT",
16
+ "author": "XMingAI",
17
+ "bin": {
18
+ "dm-cli": "scripts/run.js"
19
+ },
20
+ "files": [
21
+ "bin/dm-cli-darwin-amd64",
22
+ "bin/dm-cli-darwin-arm64",
23
+ "bin/dm-cli-linux-amd64",
24
+ "bin/dm-cli-windows-amd64.exe",
25
+ "scripts/run.js",
26
+ "scripts/install-completion.js",
27
+ "scripts/_dm-cli",
28
+ "README.md",
29
+ "LICENSE"
30
+ ],
31
+ "scripts": {
32
+ "postinstall": "node scripts/install-completion.js",
33
+ "build": "make build-all",
34
+ "test": "go test ./..."
35
+ },
36
+ "engines": {
37
+ "node": ">=14.0.0"
38
+ },
39
+ "preferGlobal": true
40
+ }
@@ -0,0 +1,189 @@
1
+ #compdef dm-cli
2
+
3
+ _dm_cli() {
4
+ local curcontext="$curcontext" state state_descr line
5
+ typeset -A opt_args
6
+
7
+ _arguments -C \
8
+ '--json[以 JSON 格式输出]' \
9
+ '--dry-run[预览请求而不执行]' \
10
+ '(- *)--help[显示帮助]' \
11
+ '(-): :->command' \
12
+ '(-)*:: :->args'
13
+
14
+ case "$state" in
15
+ command)
16
+ local -a commands=(
17
+ 'config:管理配置'
18
+ 'auth:用户认证'
19
+ 'agent:AI 助手相关命令'
20
+ 'thread:会话相关命令'
21
+ 'schema:输出 CLI Schema 定义'
22
+ 'version:显示版本号'
23
+ )
24
+ _describe -t commands 'command' commands
25
+ ;;
26
+ args)
27
+ case "${line[1]}" in
28
+ config) _dm_cli_config ;;
29
+ auth) _dm_cli_auth ;;
30
+ agent) _dm_cli_agent ;;
31
+ thread) _dm_cli_thread ;;
32
+ schema) _dm_cli_schema ;;
33
+ esac
34
+ ;;
35
+ esac
36
+ }
37
+
38
+ _dm_cli_config() {
39
+ local curcontext="$curcontext" state state_descr line
40
+ typeset -A opt_args
41
+
42
+ _arguments -C \
43
+ '(-): :->subcommand' \
44
+ '(-)*:: :->args'
45
+
46
+ case "$state" in
47
+ subcommand)
48
+ local -a subcommands=(
49
+ 'init:初始化配置'
50
+ 'show:显示当前配置'
51
+ )
52
+ _describe -t subcommands 'subcommand' subcommands
53
+ ;;
54
+ args)
55
+ case "${line[1]}" in
56
+ init)
57
+ _arguments \
58
+ '--endpoint=[API 端点 URL]:url' \
59
+ '--accesskey=[Access Key]:key' \
60
+ '--poll-timeout=[轮询超时(分钟)]:minutes' \
61
+ '--json[以 JSON 格式输出]' \
62
+ '--dry-run[预览请求]' \
63
+ '--help[显示帮助]'
64
+ ;;
65
+ show)
66
+ _arguments \
67
+ '--json[以 JSON 格式输出]' \
68
+ '--dry-run[预览请求]' \
69
+ '--help[显示帮助]'
70
+ ;;
71
+ esac
72
+ ;;
73
+ esac
74
+ }
75
+
76
+ _dm_cli_auth() {
77
+ local curcontext="$curcontext" state state_descr line
78
+ typeset -A opt_args
79
+
80
+ _arguments -C \
81
+ '(-): :->subcommand' \
82
+ '(-)*:: :->args'
83
+
84
+ case "$state" in
85
+ subcommand)
86
+ local -a subcommands=(
87
+ 'status:显示鉴权状态'
88
+ )
89
+ _describe -t subcommands 'subcommand' subcommands
90
+ ;;
91
+ args)
92
+ case "${line[1]}" in
93
+ status)
94
+ _arguments \
95
+ '--json[以 JSON 格式输出]' \
96
+ '--dry-run[预览请求]' \
97
+ '--help[显示帮助]'
98
+ ;;
99
+ esac
100
+ ;;
101
+ esac
102
+ }
103
+
104
+ _dm_cli_agent() {
105
+ local curcontext="$curcontext" state state_descr line
106
+ typeset -A opt_args
107
+
108
+ _arguments -C \
109
+ '(-): :->subcommand' \
110
+ '(-)*:: :->args'
111
+
112
+ case "$state" in
113
+ subcommand)
114
+ local -a subcommands=(
115
+ 'start-thread:发起 AI 会话'
116
+ 'stop:停止 Agent 运行'
117
+ 'user-open:列出用户打开的 agent'
118
+ )
119
+ _describe -t subcommands 'subcommand' subcommands
120
+ ;;
121
+ args)
122
+ case "${line[1]}" in
123
+ start-thread)
124
+ _arguments \
125
+ '--message=[消息内容]:message' \
126
+ '*--file=[本地文件路径]:file:_files' \
127
+ '--agent-mode=[Agent 模式]:mode:(auto cooperation)' \
128
+ '--thread-id=[会话 ID]:thread_id' \
129
+ '--claw-param=[JSON 参数]:json' \
130
+ '--json[以 JSON 格式输出]' \
131
+ '--dry-run[预览请求]' \
132
+ '--help[显示帮助]'
133
+ ;;
134
+ stop)
135
+ _arguments \
136
+ '--agent-run-id=[Agent 运行 ID]:run_id' \
137
+ '--json[以 JSON 格式输出]' \
138
+ '--dry-run[预览请求]' \
139
+ '--help[显示帮助]'
140
+ ;;
141
+ user-open)
142
+ _arguments \
143
+ '--json[以 JSON 格式输出]' \
144
+ '--dry-run[预览请求]' \
145
+ '--help[显示帮助]'
146
+ ;;
147
+ esac
148
+ ;;
149
+ esac
150
+ }
151
+
152
+ _dm_cli_thread() {
153
+ local curcontext="$curcontext" state state_descr line
154
+ typeset -A opt_args
155
+
156
+ _arguments -C \
157
+ '(-): :->subcommand' \
158
+ '(-)*:: :->args'
159
+
160
+ case "$state" in
161
+ subcommand)
162
+ local -a subcommands=(
163
+ 'result:获取会话结果'
164
+ )
165
+ _describe -t subcommands 'subcommand' subcommands
166
+ ;;
167
+ args)
168
+ case "${line[1]}" in
169
+ result)
170
+ _arguments \
171
+ '--thread-id=[会话 ID]:thread_id' \
172
+ '--json[以 JSON 格式输出]' \
173
+ '--dry-run[预览请求]' \
174
+ '--help[显示帮助]'
175
+ ;;
176
+ esac
177
+ ;;
178
+ esac
179
+ }
180
+
181
+ _dm_cli_schema() {
182
+ _arguments \
183
+ '--pretty[格式化输出 JSON]' \
184
+ '--json[以 JSON 格式输出]' \
185
+ '--dry-run[预览请求]' \
186
+ '--help[显示帮助]'
187
+ }
188
+
189
+ _dm_cli "$@"
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const os = require('os');
6
+ const { execSync } = require('child_process');
7
+
8
+ // Only for zsh users
9
+ if (process.env.SHELL && !process.env.SHELL.includes('zsh')) {
10
+ process.exit(0);
11
+ }
12
+
13
+ const homeDir = os.homedir();
14
+ const completionDir = path.join(homeDir, '.zsh', 'completions');
15
+ const completionFile = path.join(completionDir, '_dm-cli');
16
+ const sourceFile = path.join(__dirname, '_dm-cli');
17
+ const zshrcFile = path.join(homeDir, '.zshrc');
18
+
19
+ try {
20
+ // Create completion directory if it doesn't exist
21
+ if (!fs.existsSync(completionDir)) {
22
+ fs.mkdirSync(completionDir, { recursive: true });
23
+ }
24
+
25
+ // Copy completion script
26
+ if (fs.existsSync(sourceFile)) {
27
+ fs.copyFileSync(sourceFile, completionFile);
28
+ fs.chmodSync(completionFile, 0o644);
29
+
30
+ // Clear zsh completion cache so new completions take effect
31
+ const zcompdumpFiles = fs.readdirSync(homeDir).filter(f => f.startsWith('.zcompdump'));
32
+ for (const f of zcompdumpFiles) {
33
+ try { fs.unlinkSync(path.join(homeDir, f)); } catch (_) {}
34
+ }
35
+ }
36
+
37
+ // Add fpath to .zshrc if not already there
38
+ if (fs.existsSync(zshrcFile)) {
39
+ const zshrcContent = fs.readFileSync(zshrcFile, 'utf8');
40
+ const fpathLine = "fpath=(~/.zsh/completions $fpath)";
41
+
42
+ // Check if fpath is already configured
43
+ if (!zshrcContent.includes('~/.zsh/completions')) {
44
+ // Append fpath and compinit configuration
45
+ const newConfig = `\n# dm-cli completion\n${fpathLine}\nautoload -Uz compinit && compinit\n`;
46
+ fs.appendFileSync(zshrcFile, newConfig);
47
+ }
48
+ }
49
+ } catch (err) {
50
+ // Silent fail - completion is optional
51
+ }
package/scripts/run.js ADDED
@@ -0,0 +1,204 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { spawn, execSync } = require('child_process');
4
+ const path = require('path');
5
+ const os = require('os');
6
+ const fs = require('fs');
7
+ const https = require('https');
8
+
9
+ const pkg = require('../package.json');
10
+
11
+ const platform = os.platform();
12
+ const arch = os.arch();
13
+
14
+ // Map node's platform/arch to standard names
15
+ const PLATFORM_MAP = {
16
+ 'darwin': 'darwin',
17
+ 'linux': 'linux',
18
+ 'win32': 'windows'
19
+ };
20
+
21
+ const ARCH_MAP = {
22
+ 'x64': 'amd64',
23
+ 'arm64': 'arm64'
24
+ };
25
+
26
+ const stdPlatform = PLATFORM_MAP[platform];
27
+ const stdArch = ARCH_MAP[arch];
28
+
29
+ if (!stdPlatform || !stdArch) {
30
+ console.error(`❌ Unsupported platform: ${platform}/${arch}`);
31
+ process.exit(1);
32
+ }
33
+
34
+ const isWindows = platform === 'win32';
35
+ const binaryName = `dm-cli-${stdPlatform}-${stdArch}${isWindows ? '.exe' : ''}`;
36
+ const binaryPath = path.join(__dirname, '..', 'bin', binaryName);
37
+
38
+ // --- Auto-update logic ---
39
+
40
+ const UPDATE_CHECK_INTERVAL = 60 * 60 * 1000; // 1 hour
41
+ const CACHE_DIR = path.join(os.homedir(), '.dm-cli');
42
+ const CACHE_FILE = path.join(CACHE_DIR, '.update-check');
43
+
44
+ function readCache() {
45
+ try {
46
+ const data = JSON.parse(fs.readFileSync(CACHE_FILE, 'utf-8'));
47
+ return data;
48
+ } catch {
49
+ return null;
50
+ }
51
+ }
52
+
53
+ function writeCache(latest) {
54
+ try {
55
+ if (!fs.existsSync(CACHE_DIR)) {
56
+ fs.mkdirSync(CACHE_DIR, { recursive: true });
57
+ }
58
+ fs.writeFileSync(CACHE_FILE, JSON.stringify({
59
+ checkedAt: Date.now(),
60
+ latest
61
+ }));
62
+ } catch {
63
+ // silent
64
+ }
65
+ }
66
+
67
+ function fetchLatestVersion() {
68
+ return new Promise((resolve, reject) => {
69
+ const req = https.get('https://registry.npmjs.org/deepminer-cli/latest', { timeout: 5000 }, res => {
70
+ let data = '';
71
+ res.on('data', chunk => data += chunk);
72
+ res.on('end', () => {
73
+ try {
74
+ resolve(JSON.parse(data).version);
75
+ } catch {
76
+ reject(new Error('parse error'));
77
+ }
78
+ });
79
+ });
80
+ req.on('error', reject);
81
+ req.on('timeout', () => { req.destroy(); reject(new Error('timeout')); });
82
+ });
83
+ }
84
+
85
+ function compareVersions(a, b) {
86
+ const pa = a.split('.').map(Number);
87
+ const pb = b.split('.').map(Number);
88
+ for (let i = 0; i < 3; i++) {
89
+ if ((pa[i] || 0) < (pb[i] || 0)) return -1;
90
+ if ((pa[i] || 0) > (pb[i] || 0)) return 1;
91
+ }
92
+ return 0;
93
+ }
94
+
95
+ function doUpdate() {
96
+ console.error(`⬆️ 正在自动更新 dm-cli ...`);
97
+ try {
98
+ execSync('npm install -g deepminer-cli', { stdio: 'inherit', timeout: 60000 });
99
+ console.error(`✅ dm-cli 已更新到最新版本`);
100
+ return true;
101
+ } catch {
102
+ console.error(`⚠️ 自动更新失败,继续使用当前版本`);
103
+ return false;
104
+ }
105
+ }
106
+
107
+ async function checkAndUpdate() {
108
+ const currentVersion = pkg.version;
109
+
110
+ // Check cache first
111
+ const cache = readCache();
112
+ if (cache && (Date.now() - cache.checkedAt) < UPDATE_CHECK_INTERVAL) {
113
+ // Within cache window, use cached result
114
+ if (cache.latest && compareVersions(currentVersion, cache.latest) < 0) {
115
+ return doUpdate();
116
+ }
117
+ return false;
118
+ }
119
+
120
+ // Fetch latest version from npm
121
+ try {
122
+ const latest = await fetchLatestVersion();
123
+ writeCache(latest);
124
+ if (compareVersions(currentVersion, latest) < 0) {
125
+ return doUpdate();
126
+ }
127
+ } catch {
128
+ // Network error, skip update check
129
+ }
130
+ return false;
131
+ }
132
+
133
+ // --- Main execution ---
134
+
135
+ async function main() {
136
+ // Skip update check for version/help commands or if DM_CLI_NO_UPDATE is set
137
+ const args = process.argv.slice(2);
138
+ const skipUpdate = process.env.DM_CLI_NO_UPDATE === '1'
139
+ || args.includes('version')
140
+ || args.includes('--help')
141
+ || args.includes('-h');
142
+
143
+ if (!skipUpdate) {
144
+ const updated = await checkAndUpdate();
145
+ if (updated) {
146
+ // After update, re-exec with the new binary
147
+ const child = spawn(process.argv[0], process.argv.slice(1), {
148
+ stdio: 'inherit',
149
+ env: { ...process.env, DM_CLI_NO_UPDATE: '1' },
150
+ windowsHide: true
151
+ });
152
+ child.on('exit', code => process.exit(code || 0));
153
+ child.on('error', () => process.exit(1));
154
+ return;
155
+ }
156
+ }
157
+
158
+ // Ensure symlink exists on first run (for global installs)
159
+ try {
160
+ const npmPrefix = execSync('npm config get prefix', { encoding: 'utf-8', stdio: 'pipe' }).trim();
161
+ const npmBinDir = path.join(npmPrefix, 'bin');
162
+ const npmBinPath = path.join(npmBinDir, 'dm-cli');
163
+ const thisFile = __filename;
164
+
165
+ if (!fs.existsSync(npmBinPath) && platform !== 'win32') {
166
+ try {
167
+ execSync(`ln -sf "${thisFile}" "${npmBinPath}"`, { stdio: 'ignore' });
168
+ } catch (e) {
169
+ // Silent fail
170
+ }
171
+ }
172
+ } catch (e) {
173
+ // Silent fail
174
+ }
175
+
176
+ // Spawn the binary and forward all arguments
177
+ const child = spawn(binaryPath, args, {
178
+ stdio: 'inherit',
179
+ windowsHide: true
180
+ });
181
+
182
+ child.on('error', (err) => {
183
+ if (err.code === 'ENOENT') {
184
+ console.error(`❌ Binary not found: ${binaryPath}`);
185
+ } else {
186
+ console.error(`❌ Error: ${err.message}`);
187
+ }
188
+ process.exit(1);
189
+ });
190
+
191
+ process.on('SIGINT', () => {
192
+ child.kill('SIGINT');
193
+ });
194
+
195
+ process.on('SIGTERM', () => {
196
+ child.kill('SIGTERM');
197
+ });
198
+
199
+ child.on('exit', (code) => {
200
+ process.exit(code);
201
+ });
202
+ }
203
+
204
+ main();