@sokeai/cli 1.0.11 → 1.0.14

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sokeai/cli",
3
- "version": "1.0.11",
3
+ "version": "1.0.14",
4
4
  "description": "授客AI官方CLI工具 - 支持AI Agent Skills",
5
5
  "bin": {
6
6
  "soke-cli": "scripts/run.js"
@@ -3,6 +3,7 @@ const path = require('path');
3
3
  const fs = require('fs');
4
4
  const https = require('https');
5
5
  const { execSync } = require('child_process');
6
+ const readline = require('readline');
6
7
 
7
8
  const platform = os.platform();
8
9
  const arch = os.arch();
@@ -188,6 +189,108 @@ function syncSkillsToWorkclawRegistry() {
188
189
  } catch (_) {}
189
190
  }
190
191
 
192
+ function isLikelyInteractiveInstall() {
193
+ if (!process.stdin.isTTY) return false;
194
+ if (!process.stdout.isTTY) return false;
195
+ if (process.env.CI) return false;
196
+ if (process.env.npm_config_yes === 'true') return false;
197
+ return true;
198
+ }
199
+
200
+ function promptYesNo(message) {
201
+ return new Promise((resolve) => {
202
+ const rl = readline.createInterface({
203
+ input: process.stdin,
204
+ output: process.stdout
205
+ });
206
+
207
+ rl.question(message, (answer) => {
208
+ rl.close();
209
+ const normalized = String(answer || '').trim().toLowerCase();
210
+ resolve(normalized === 'y' || normalized === 'yes');
211
+ });
212
+ });
213
+ }
214
+
215
+ function detectNpmGlobalBinDir() {
216
+ const prefix = process.env.npm_config_prefix;
217
+ if (typeof prefix === 'string' && prefix.length > 0) {
218
+ const binDir = platform === 'win32' ? prefix : path.join(prefix, 'bin');
219
+ return binDir;
220
+ }
221
+ return null;
222
+ }
223
+
224
+ function detectSokeCliShimPath() {
225
+ const binDir = detectNpmGlobalBinDir();
226
+ if (!binDir) return null;
227
+
228
+ const candidates =
229
+ platform === 'win32'
230
+ ? [path.join(binDir, 'soke-cli.cmd'), path.join(binDir, 'soke-cli.exe')]
231
+ : [path.join(binDir, 'soke-cli')];
232
+
233
+ for (const candidate of candidates) {
234
+ if (fs.existsSync(candidate)) return candidate;
235
+ }
236
+ return null;
237
+ }
238
+
239
+ function getPreferredLinkTargetPaths() {
240
+ if (platform === 'win32') return [];
241
+ return ['/usr/local/bin/soke-cli', '/opt/homebrew/bin/soke-cli'];
242
+ }
243
+
244
+ function ensureSymlink(targetPath, sourcePath) {
245
+ try {
246
+ const existing = fs.lstatSync(targetPath);
247
+ if (existing.isSymbolicLink()) {
248
+ const currentTarget = fs.readlinkSync(targetPath);
249
+ if (currentTarget === sourcePath) return { ok: true, changed: false };
250
+ fs.unlinkSync(targetPath);
251
+ } else {
252
+ return { ok: false, changed: false, reason: 'exists_non_symlink' };
253
+ }
254
+ } catch (_) {}
255
+
256
+ try {
257
+ fs.symlinkSync(sourcePath, targetPath);
258
+ return { ok: true, changed: true };
259
+ } catch (err) {
260
+ return { ok: false, changed: false, reason: err && err.message ? err.message : 'failed' };
261
+ }
262
+ }
263
+
264
+ async function maybeAssistGuiPath() {
265
+ const platform = os.platform();
266
+ const binDir = path.join(__dirname, '..', 'bin');
267
+ const binaryName = platform === 'win32' ? 'soke-cli.exe' : 'soke-cli';
268
+ const binaryPath = path.join(binDir, binaryName);
269
+
270
+ if (!fs.existsSync(binaryPath)) return Promise.resolve();
271
+
272
+ // 我们不再弹窗询问,而是直接以静默模式尝试执行 setup-gui-env
273
+ // 这会尝试创建软链,如果没权限,它会默默失败(不会报错打断安装)
274
+ // 用户如果后面遇到 command not found,仍可以手动执行 soke-cli setup-gui-env
275
+ return new Promise((resolve) => {
276
+ try {
277
+ const runJsPath = path.join(__dirname, 'run.js');
278
+ const { spawn } = require('child_process');
279
+ const child = spawn(process.execPath, [runJsPath, 'setup-gui-env', '--silent'], {
280
+ stdio: 'ignore', // 静默执行,不污染 npm install 输出
281
+ detached: true
282
+ });
283
+ child.on('error', () => resolve());
284
+ child.on('exit', () => resolve());
285
+
286
+ // 避免卡住
287
+ setTimeout(() => resolve(), 3000);
288
+ } catch (e) {
289
+ resolve();
290
+ }
291
+ });
292
+ }
293
+
191
294
  // 平台映射
192
295
  const platformMap = {
193
296
  'darwin': 'darwin',
@@ -297,6 +400,9 @@ downloadFile(downloadURL, binaryPath)
297
400
  syncSkillsToWorkclawRegistry();
298
401
  } catch (_) {}
299
402
 
403
+ return maybeAssistGuiPath();
404
+ })
405
+ .then(() => {
300
406
  console.log('soke-cli 安装成功!');
301
407
  console.log(`二进制文件位置: ${binaryPath}`);
302
408
  console.log('\n使用方法:');
package/scripts/run.js CHANGED
@@ -1,9 +1,97 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const { spawn } = require('child_process');
3
+ const { spawn, execSync } = require('child_process');
4
4
  const path = require('path');
5
5
  const fs = require('fs');
6
6
 
7
+ // 特殊命令拦截:修复 sokeclaw 的 GUI PATH 问题
8
+ if (process.argv[2] === 'setup-gui-env') {
9
+ console.log('正在为 SokeClaw GUI 注入环境变量 PATH...');
10
+ try {
11
+ // 获取 node 和当前脚本的绝对路径目录,这通常也是 soke-cli 所在的 bin 目录
12
+ const nodeDir = path.dirname(process.execPath);
13
+ const cliDir = path.dirname(process.argv[1]);
14
+
15
+ let targetPath = '';
16
+ if (process.platform === 'darwin' || process.platform === 'linux') {
17
+ const binSource = path.join(nodeDir, 'soke-cli');
18
+ const binTarget = '/usr/local/bin/soke-cli';
19
+
20
+ if (fs.existsSync(binSource)) {
21
+ try {
22
+ // 尝试创建软链
23
+ if (fs.existsSync(binTarget)) {
24
+ try { fs.unlinkSync(binTarget); } catch(e) {}
25
+ }
26
+ fs.symlinkSync(binSource, binTarget);
27
+ console.log(`✅ 成功创建软链: ${binTarget} -> ${binSource}`);
28
+ } catch (linkError) {
29
+ console.log(`\n⚠️ 权限不足,无法自动创建 /usr/local/bin 软链。`);
30
+ console.log(`请手动在终端执行以下命令(可能需要输入密码):`);
31
+ console.log(`\n sudo ln -sf "${binSource}" "${binTarget}"\n`);
32
+
33
+ if (process.argv[3] !== '--silent') {
34
+ console.log(`执行上述命令后,再重新打开客户端即可。`);
35
+ }
36
+ }
37
+ } else {
38
+ console.log(`⚠️ 未找到源文件: ${binSource}`);
39
+ }
40
+
41
+ if (process.argv[3] === '--silent') {
42
+ process.exit(0);
43
+ }
44
+
45
+ console.log('尝试重启 SokeClaw / WorkClaw...');
46
+ try { execSync('killall SokeClaw 2>/dev/null'); } catch(e) {}
47
+ try { execSync('killall WorkClaw 2>/dev/null'); } catch(e) {}
48
+ try { execSync('killall Electron 2>/dev/null'); } catch(e) {}
49
+
50
+ // 等待进程退出
51
+ setTimeout(() => {
52
+ let launched = false;
53
+ if (process.platform === 'darwin') {
54
+ if (fs.existsSync('/Applications/SokeClaw.app')) {
55
+ execSync('open "/Applications/SokeClaw.app"');
56
+ launched = true;
57
+ } else if (fs.existsSync('/Applications/WorkClaw.app')) {
58
+ execSync('open "/Applications/WorkClaw.app"');
59
+ launched = true;
60
+ }
61
+ }
62
+
63
+ if (launched) {
64
+ console.log('\n✅ 修复完成!SokeClaw 已重启。现在你应该可以在里面使用 /skill soke-exam 了。');
65
+ } else {
66
+ console.log('\n✅ 环境变量已注入,但未自动重启应用。请手动从“启动台(Launchpad)”或“访达(Finder)”重新打开它。');
67
+ }
68
+ process.exit(0);
69
+ }, 1500);
70
+ return; // 异步等待中
71
+ } else if (process.platform === 'win32') {
72
+ console.log('检测到当前为 Windows 系统。');
73
+
74
+ if (process.argv[3] === '--silent') {
75
+ process.exit(0);
76
+ }
77
+
78
+ console.log('正在尝试重启 SokeClaw / WorkClaw...');
79
+ try { execSync('taskkill /F /IM SokeClaw.exe 2>nul'); } catch(e) {}
80
+ try { execSync('taskkill /F /IM WorkClaw.exe 2>nul'); } catch(e) {}
81
+ try { execSync('taskkill /F /IM Electron.exe 2>nul'); } catch(e) {}
82
+
83
+ console.log('\n✅ Windows 环境下的全局包通常已在 PATH 中。客户端已被关闭,请手动重新打开它即可生效。');
84
+ process.exit(0);
85
+ } else {
86
+ console.log('\n⚠️ 目前 setup-gui-env 主要支持 macOS/Linux/Windows。');
87
+ process.exit(0);
88
+ }
89
+ } catch (error) {
90
+ console.error('修复过程中出现错误:', error.message);
91
+ process.exit(1);
92
+ }
93
+ }
94
+
7
95
  // 获取二进制文件路径
8
96
  const platform = process.platform;
9
97
  const binaryName = platform === 'win32' ? 'soke-cli.exe' : 'soke-cli';
@@ -50,4 +138,4 @@ child.on('exit', (code, signal) => {
50
138
  child.on('error', (err) => {
51
139
  console.error('执行 soke-cli 时出错:', err.message);
52
140
  process.exit(1);
53
- });
141
+ });