deepminer-cli 0.1.24 → 0.1.26

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/README.md CHANGED
@@ -11,8 +11,10 @@ DeepMiner 系统的命令行工具,用于管理认证、配置和 AI 会话。
11
11
  - **配置管理**: API 端点配置
12
12
  - **安全存储**: Token 以受限权限存储在本地
13
13
  - **文件上传**: 上传文件到 OSS,带文件大小验证
14
- - **AI 会话**: 启动 AI 助手会话,支持自定义消息和 claw_param 参数
14
+ - **AI 会话**: 启动/继续/停止 AI 助手会话,支持自定义消息、会话追问和 claw_param 参数
15
15
  - **JSON 输出**: 所有命令支持 `--json` 结构化输出,适合 AI agent 和脚本调用
16
+ - **错误提示**: 错误响应包含 `hint` 字段,提供可操作的修复建议
17
+ - **Dry Run**: `--dry-run` 预览 HTTP 请求而不实际执行
16
18
  - **CLI Schema**: 内置 `schema` 命令输出机器可读的能力描述
17
19
  - **zsh 自动补全**: 安装后自动配置
18
20
 
@@ -48,9 +50,15 @@ dm-cli auth switch <space_id>
48
50
  # 4. 查看状态
49
51
  dm-cli auth status
50
52
 
51
- # 5. 启动 AI 会话
53
+ # 5. 启动 AI 会话(返回 agent_run_id、thread_id)
52
54
  dm-cli agent start-thread --message "你好"
53
55
 
56
+ # 5a. 在已有会话中继续追问
57
+ dm-cli agent start-thread --message "继续聊" --thread-id <thread_id>
58
+
59
+ # 5b. 停止正在运行的 Agent
60
+ dm-cli agent stop --agent-run-id <agent_run_id>
61
+
54
62
  # 6. 上传文件
55
63
  dm-cli file upload --file document.pdf
56
64
 
@@ -110,7 +118,9 @@ dm-cli file upload --file <路径> --id <ID> # 上传文件(指
110
118
  ```bash
111
119
  dm-cli agent start-thread # 使用默认消息 "你好"
112
120
  dm-cli agent start-thread --message <消息> # 自定义消息
113
- dm-cli agent start-thread --message <消息> --claw-param <JSON> # 完整参数
121
+ dm-cli agent start-thread --message <消息> --thread-id <会话ID> # 在已有会话中继续追问
122
+ dm-cli agent start-thread --message <消息> --claw-param <JSON> # 附带渠道参数
123
+ dm-cli agent stop --agent-run-id <运行ID> # 停止正在运行的 Agent
114
124
  ```
115
125
 
116
126
  #### claw_param 参数说明
@@ -154,18 +164,46 @@ dm-cli --help # 显示帮助信息
154
164
  | Flag | 说明 |
155
165
  |------|------|
156
166
  | `--json` | 以 JSON 格式输出结果 |
167
+ | `--dry-run` | 预览 HTTP 请求而不实际执行 |
157
168
  | `--help` | 显示帮助信息 |
158
169
 
159
- JSON 模式下成功输出格式:
170
+ ### JSON 输出格式
171
+
172
+ 成功:
160
173
  ```json
161
174
  {"ok": true, "data": {...}}
162
175
  ```
163
176
 
164
- JSON 模式下错误输出格式:
177
+ 错误(含修复建议):
165
178
  ```json
166
- {"ok": false, "error": {"type": "auth", "message": "未登陆"}}
179
+ {"ok": false, "error": {"type": "auth", "message": "未登陆", "hint": "运行 'dm-cli auth login --username <user> --password <pass>' 登录"}}
167
180
  ```
168
181
 
182
+ `hint` 字段提供可操作的修复建议,AI agent 可直接据此自动恢复。
183
+
184
+ ### Dry Run 模式
185
+
186
+ 使用 `--dry-run` 预览请求而不实际执行,适合调试和 AI agent 安全验证:
187
+
188
+ ```bash
189
+ dm-cli --dry-run auth login --username user --password pass
190
+ # [DRY RUN] POST https://api.example.com/api/auth/authentication/login
191
+ # Headers:
192
+ # Content-Type: application/json
193
+ # Body:
194
+ # {"username": "user", "password": "pass"}
195
+ ```
196
+
197
+ JSON 模式:
198
+ ```bash
199
+ dm-cli --dry-run --json agent start-thread --message "你好"
200
+ ```
201
+ ```json
202
+ {"ok": true, "dry_run": true, "data": {"method": "POST", "url": "...", "headers": {...}, "body": {...}}}
203
+ ```
204
+
205
+ 只读命令(`config show`、`auth status`、`version`、`schema`)和纯本地操作(`config init`、`auth logout`)不受 `--dry-run` 影响,正常执行。
206
+
169
207
  ## 文件说明
170
208
 
171
209
  ### 配置文件:`~/.dm-cli/config.json`
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepminer-cli",
3
- "version": "0.1.24",
3
+ "version": "0.1.26",
4
4
  "description": "DeepMiner CLI - Command-line tool for DM system with cross-platform support, JWT authentication, and workspace management",
5
5
  "keywords": [
6
6
  "deepminer",
package/scripts/_dm-cli CHANGED
@@ -1,43 +1,199 @@
1
1
  #compdef dm-cli
2
2
 
3
3
  _dm_cli() {
4
- local -a commands
5
- local -i idx
6
-
7
- # 找到当前位置
8
- for ((idx = 2; idx < $#words; idx++)); do
9
- if [[ "${words[idx]}" != -* ]]; then
10
- break
11
- fi
12
- done
13
-
14
- # 如果只有 dm-cli,显示顶级命令
15
- if [[ $idx -eq $#words ]]; then
16
- commands=(
17
- 'config:管理配置'
18
- 'auth:用户认证'
19
- 'agent:AI 助手相关命令'
20
- 'file:文件管理'
21
- 'schema:输出 CLI Schema 定义'
22
- 'version:显示版本号'
23
- )
24
- _describe 'command' commands
25
- return
26
- fi
27
-
28
- # 根据第一个参数显示子命令
29
- case "${words[$idx]}" in
30
- config)
31
- _describe 'config subcommand' '(init:初始化配置 show:显示配置)'
32
- ;;
33
- auth)
34
- _describe 'auth subcommand' '(login:登录 logout:登出 status:显示状态 switch:切换空间)'
35
- ;;
36
- agent)
37
- _describe 'agent subcommand' '(start-thread:发起AI会话)'
38
- ;;
39
- file)
40
- _describe 'file subcommand' '(upload:上传文件)'
4
+ local context 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
+ 'file:文件管理'
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
+ file) _dm_cli_file ;;
32
+ schema) _dm_cli_schema ;;
33
+ esac
34
+ ;;
35
+ esac
36
+ }
37
+
38
+ _dm_cli_config() {
39
+ local context 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
+ '--json[以 JSON 格式输出]' \
60
+ '--dry-run[预览请求]' \
61
+ '--help[显示帮助]'
62
+ ;;
63
+ show)
64
+ _arguments \
65
+ '--json[以 JSON 格式输出]' \
66
+ '--dry-run[预览请求]' \
67
+ '--help[显示帮助]'
68
+ ;;
69
+ esac
70
+ ;;
71
+ esac
72
+ }
73
+
74
+ _dm_cli_auth() {
75
+ local context state state_descr line
76
+ typeset -A opt_args
77
+
78
+ _arguments -C \
79
+ '(-): :->subcommand' \
80
+ '(-)*:: :->args'
81
+
82
+ case "$state" in
83
+ subcommand)
84
+ local -a subcommands=(
85
+ 'login:登录 DM 系统'
86
+ 'logout:退出登录'
87
+ 'status:显示登录状态'
88
+ 'switch:切换空间'
89
+ )
90
+ _describe -t subcommands 'subcommand' subcommands
91
+ ;;
92
+ args)
93
+ case "${line[1]}" in
94
+ login)
95
+ _arguments \
96
+ '--username=[用户名]:username' \
97
+ '--password=[密码]:password' \
98
+ '--json[以 JSON 格式输出]' \
99
+ '--dry-run[预览请求]' \
100
+ '--help[显示帮助]'
101
+ ;;
102
+ logout|status)
103
+ _arguments \
104
+ '--json[以 JSON 格式输出]' \
105
+ '--dry-run[预览请求]' \
106
+ '--help[显示帮助]'
107
+ ;;
108
+ switch)
109
+ _arguments \
110
+ '1:space_id' \
111
+ '--json[以 JSON 格式输出]' \
112
+ '--dry-run[预览请求]' \
113
+ '--help[显示帮助]'
114
+ ;;
115
+ esac
41
116
  ;;
42
117
  esac
43
118
  }
119
+
120
+ _dm_cli_agent() {
121
+ local context state state_descr line
122
+ typeset -A opt_args
123
+
124
+ _arguments -C \
125
+ '(-): :->subcommand' \
126
+ '(-)*:: :->args'
127
+
128
+ case "$state" in
129
+ subcommand)
130
+ local -a subcommands=(
131
+ 'start-thread:发起 AI 会话'
132
+ 'stop:停止 Agent 运行'
133
+ )
134
+ _describe -t subcommands 'subcommand' subcommands
135
+ ;;
136
+ args)
137
+ case "${line[1]}" in
138
+ start-thread)
139
+ _arguments \
140
+ '--message=[消息内容]:message' \
141
+ '--thread-id=[会话 ID]:thread_id' \
142
+ '--claw-param=[JSON 参数]:json' \
143
+ '--json[以 JSON 格式输出]' \
144
+ '--dry-run[预览请求]' \
145
+ '--help[显示帮助]'
146
+ ;;
147
+ stop)
148
+ _arguments \
149
+ '--agent-run-id=[Agent 运行 ID]:run_id' \
150
+ '--json[以 JSON 格式输出]' \
151
+ '--dry-run[预览请求]' \
152
+ '--help[显示帮助]'
153
+ ;;
154
+ esac
155
+ ;;
156
+ esac
157
+ }
158
+
159
+ _dm_cli_file() {
160
+ local context state state_descr line
161
+ typeset -A opt_args
162
+
163
+ _arguments -C \
164
+ '(-): :->subcommand' \
165
+ '(-)*:: :->args'
166
+
167
+ case "$state" in
168
+ subcommand)
169
+ local -a subcommands=(
170
+ 'upload:上传文件'
171
+ )
172
+ _describe -t subcommands 'subcommand' subcommands
173
+ ;;
174
+ args)
175
+ case "${line[1]}" in
176
+ upload)
177
+ _arguments \
178
+ '--file=[文件路径]:file:_files' \
179
+ '--name=[远程文件名]:name' \
180
+ '--feature=[功能类型]:feature' \
181
+ '--id=[文件 ID]:id' \
182
+ '--json[以 JSON 格式输出]' \
183
+ '--dry-run[预览请求]' \
184
+ '--help[显示帮助]'
185
+ ;;
186
+ esac
187
+ ;;
188
+ esac
189
+ }
190
+
191
+ _dm_cli_schema() {
192
+ _arguments \
193
+ '--pretty[格式化输出 JSON]' \
194
+ '--json[以 JSON 格式输出]' \
195
+ '--dry-run[预览请求]' \
196
+ '--help[显示帮助]'
197
+ }
198
+
199
+ _dm_cli "$@"
package/scripts/run.js CHANGED
@@ -4,6 +4,9 @@ const { spawn, execSync } = require('child_process');
4
4
  const path = require('path');
5
5
  const os = require('os');
6
6
  const fs = require('fs');
7
+ const https = require('https');
8
+
9
+ const pkg = require('../package.json');
7
10
 
8
11
  const platform = os.platform();
9
12
  const arch = os.arch();
@@ -32,47 +35,170 @@ const isWindows = platform === 'win32';
32
35
  const binaryName = `dm-cli-${stdPlatform}-${stdArch}${isWindows ? '.exe' : ''}`;
33
36
  const binaryPath = path.join(__dirname, '..', 'bin', binaryName);
34
37
 
35
- // Ensure symlink exists on first run (for global installs)
36
- try {
37
- const npmPrefix = execSync('npm config get prefix', { encoding: 'utf-8', stdio: 'pipe' }).trim();
38
- const npmBinDir = path.join(npmPrefix, 'bin');
39
- const npmBinPath = path.join(npmBinDir, 'dm-cli');
40
- const thisFile = __filename;
41
-
42
- if (!fs.existsSync(npmBinPath) && platform !== 'win32') {
43
- try {
44
- execSync(`ln -sf "${thisFile}" "${npmBinPath}"`, { stdio: 'ignore' });
45
- } catch (e) {
46
- // Silent fail
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 });
47
57
  }
58
+ fs.writeFileSync(CACHE_FILE, JSON.stringify({
59
+ checkedAt: Date.now(),
60
+ latest
61
+ }));
62
+ } catch {
63
+ // silent
48
64
  }
49
- } catch (e) {
50
- // Silent fail
51
65
  }
52
66
 
53
- // Spawn the binary and forward all arguments
54
- const child = spawn(binaryPath, process.argv.slice(2), {
55
- stdio: 'inherit',
56
- windowsHide: true
57
- });
58
-
59
- child.on('error', (err) => {
60
- if (err.code === 'ENOENT') {
61
- console.error(`❌ Binary not found: ${binaryPath}`);
62
- } else {
63
- console.error(`❌ Error: ${err.message}`);
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;
64
91
  }
65
- process.exit(1);
66
- });
92
+ return 0;
93
+ }
67
94
 
68
- process.on('SIGINT', () => {
69
- child.kill('SIGINT');
70
- });
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
+ }
71
106
 
72
- process.on('SIGTERM', () => {
73
- child.kill('SIGTERM');
74
- });
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
+ }
75
203
 
76
- child.on('exit', (code) => {
77
- process.exit(code);
78
- });
204
+ main();