@agent-webui/ai-desk-daemon 1.0.29-beta1 → 1.0.29-beta2

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
@@ -26,9 +26,15 @@
26
26
  # 全局安装
27
27
  npm install -g @agent-webui/ai-desk
28
28
 
29
- # 启动 daemon
29
+ # 启动 daemon(默认使用已保存模式)
30
30
  aidesk start
31
31
 
32
+ # 切到 Native mode
33
+ aidesk start --mode native
34
+
35
+ # 切到 Bundled CLI-Anything runtime mode
36
+ aidesk start --mode cli-anything
37
+
32
38
  # 查看状态
33
39
  aidesk status
34
40
  ```
@@ -37,6 +43,7 @@ aidesk status
37
43
  - ✅ AI Desk Daemon 后台服务
38
44
  - ✅ CLI 命令行管理工具
39
45
  - ✅ 默认 harness 运行时与安装脚本
46
+ - ✅ bundled CLI-Anything runtime(随包安装到 AI Desk runtime)
40
47
  - ✅ 按平台分发的内置 Python runtime
41
48
  - ✅ HTTP API (http://localhost:9527)
42
49
 
@@ -45,14 +52,21 @@ aidesk status
45
52
  - ❌ 桌面 GUI 应用
46
53
 
47
54
  **可用命令**:
48
- - `aidesk start` - 启动守护进程(后台运行)
55
+ - `aidesk start` - 启动守护进程(后台运行,沿用当前已保存 mode)
56
+ - `aidesk start --mode native` - 切到 Native mode 并启动
57
+ - `aidesk start --mode cli-anything` - 切到 Bundled CLI-Anything runtime mode 并启动
49
58
  - `aidesk start --log` - 启动守护进程(前台运行,跟随日志)
50
59
  - `aidesk stop` - 停止守护进程
51
60
  - `aidesk restart` - 重启守护进程
61
+ - `aidesk restart --mode cli-anything` - 切到 Bundled CLI-Anything runtime mode 并重启
52
62
  - `aidesk status` - 查看状态
53
63
  - `aidesk logs` - 查看日志
54
64
  - `aidesk logs -f` - 实时查看日志(不会停止守护进程)
55
65
 
66
+ **Mode 说明**:
67
+ - `Native mode`:检测、模型查询、命令执行走 daemon 原生实现。
68
+ - `Bundled CLI-Anything runtime mode`:使用 npm 安装时随包准备好的 Python runtime 和 `cli_anything` 入口;如果某个功能当前 runtime 不支持,daemon 会按配置回退到 native。
69
+
56
70
  📖 详细使用说明:[NPM_CLI.md](NPM_CLI.md)
57
71
 
58
72
  ---
package/bin/cli.js CHANGED
@@ -7,10 +7,65 @@
7
7
  const { program } = require('commander');
8
8
  const chalk = require('chalk');
9
9
  const fs = require('fs');
10
+ const path = require('path');
10
11
  const { start, stop, restart, status } = require('../lib/daemon-manager');
11
12
  const { getLogPath } = require('../lib/platform');
12
13
  const { VERSION } = require('../lib/platform');
13
14
 
15
+ function resolveRequestedMode(options) {
16
+ const hasExplicitMode = typeof options.mode === 'string' && options.mode.trim() !== '';
17
+
18
+ if (options.cliAnything && options.native) {
19
+ throw new Error('Use either --cli-anything or --native, not both');
20
+ }
21
+
22
+ if (hasExplicitMode && (options.cliAnything || options.native)) {
23
+ throw new Error('Use either --mode or the mode shortcut flags, not both');
24
+ }
25
+
26
+ if (options.cliAnything) {
27
+ return 'cli-anything';
28
+ }
29
+
30
+ if (options.native) {
31
+ return 'native';
32
+ }
33
+
34
+ if (!hasExplicitMode) {
35
+ return '';
36
+ }
37
+
38
+ const normalizedMode = options.mode.trim().toLowerCase();
39
+ if (normalizedMode !== 'native' && normalizedMode !== 'cli-anything') {
40
+ throw new Error(`Unsupported mode: ${options.mode}. Use "native" or "cli-anything".`);
41
+ }
42
+
43
+ return normalizedMode;
44
+ }
45
+
46
+ function configureRequestedMode(mode) {
47
+ const packageRoot = path.join(__dirname, '..');
48
+ const { loadConfig } = require('../lib/config');
49
+ const { syncCLIAnythingConfig } = require('../lib/cli-anything-manager');
50
+
51
+ if (!mode) {
52
+ const config = loadConfig();
53
+ if (config.cli_anything?.enabled) {
54
+ return syncCLIAnythingConfig(packageRoot, { enabled: true });
55
+ }
56
+ return null;
57
+ }
58
+
59
+ if (mode === 'cli-anything') {
60
+ return syncCLIAnythingConfig(packageRoot, { enabled: true });
61
+ }
62
+
63
+ return syncCLIAnythingConfig(packageRoot, {
64
+ enabled: false,
65
+ ensureInstalled: false,
66
+ });
67
+ }
68
+
14
69
  program
15
70
  .name('aidesk')
16
71
  .description('AI Desk Daemon - CLI tool for managing the AI Desk daemon service')
@@ -20,11 +75,20 @@ program
20
75
  program
21
76
  .command('start')
22
77
  .description('Start the daemon')
78
+ .option('-m, --mode <mode>', 'Implementation mode: native or cli-anything')
79
+ .option('--cli-anything', 'Enable CLI-Anything mode before starting')
80
+ .option('--native', 'Force native mode before starting')
23
81
  .option('--log', 'Follow daemon logs in foreground (Ctrl+C to stop daemon)')
24
82
  .action(async (options) => {
25
83
  try {
26
- const { getPidPath } = require('../lib/platform');
27
- const daemonPid = start();
84
+ const mode = resolveRequestedMode(options);
85
+ const modeResult = configureRequestedMode(mode);
86
+ start();
87
+ if (mode === 'cli-anything' && modeResult?.runtimeInfo?.cliAnythingPath) {
88
+ console.log(chalk.cyan(`CLI-Anything mode configured: ${modeResult.runtimeInfo.cliAnythingPath}`));
89
+ } else if (mode === 'native') {
90
+ console.log(chalk.cyan('Native mode configured'));
91
+ }
28
92
  console.log(chalk.green('✓ Daemon started successfully'));
29
93
 
30
94
  // Only follow logs if --log is specified
@@ -138,9 +202,19 @@ program
138
202
  program
139
203
  .command('restart')
140
204
  .description('Restart the daemon')
141
- .action(async () => {
205
+ .option('-m, --mode <mode>', 'Implementation mode: native or cli-anything')
206
+ .option('--cli-anything', 'Enable CLI-Anything mode before restarting')
207
+ .option('--native', 'Force native mode before restarting')
208
+ .action(async (options) => {
142
209
  try {
210
+ const mode = resolveRequestedMode(options);
211
+ const modeResult = configureRequestedMode(mode);
143
212
  restart();
213
+ if (mode === 'cli-anything' && modeResult?.runtimeInfo?.cliAnythingPath) {
214
+ console.log(chalk.cyan(`CLI-Anything mode configured: ${modeResult.runtimeInfo.cliAnythingPath}`));
215
+ } else if (mode === 'native') {
216
+ console.log(chalk.cyan('Native mode configured'));
217
+ }
144
218
  console.log(chalk.green('✓ Daemon restarted successfully'));
145
219
  } catch (error) {
146
220
  console.error(chalk.red('✗ Failed to restart daemon:'), error.message);
@@ -0,0 +1,218 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { ensureRuntime, readJSON, run, writeJSON } = require('./runtime-manager');
4
+ const { getConfigPath } = require('./platform');
5
+
6
+ const CLI_ANYTHING_MODULE = 'cli_anything';
7
+
8
+ function resolveCommandPath(binDir, command) {
9
+ const candidates = process.platform === 'win32'
10
+ ? [
11
+ path.join(binDir, `${command}.exe`),
12
+ path.join(binDir, `${command}.cmd`),
13
+ path.join(binDir, `${command}.bat`),
14
+ path.join(binDir, command),
15
+ ]
16
+ : [path.join(binDir, command)];
17
+
18
+ for (const candidate of candidates) {
19
+ if (fs.existsSync(candidate)) {
20
+ return candidate;
21
+ }
22
+ }
23
+
24
+ return candidates[0];
25
+ }
26
+
27
+ function resolveCLIAnythingRuntimeSource(packageRoot = '') {
28
+ const candidates = [];
29
+
30
+ if (process.env.AI_DESK_CLI_ANYTHING_RUNTIME_SOURCE) {
31
+ candidates.push(process.env.AI_DESK_CLI_ANYTHING_RUNTIME_SOURCE);
32
+ }
33
+
34
+ if (packageRoot) {
35
+ candidates.push(path.join(packageRoot, 'python-runtime'));
36
+ }
37
+
38
+ candidates.push(path.join(__dirname, '..', 'python-runtime'));
39
+
40
+ for (const candidate of candidates) {
41
+ if (!candidate) {
42
+ continue;
43
+ }
44
+
45
+ const setupPyPath = path.join(candidate, 'setup.py');
46
+ if (fs.existsSync(setupPyPath)) {
47
+ return candidate;
48
+ }
49
+ }
50
+
51
+ return '';
52
+ }
53
+
54
+ function cliAnythingRuntimeLooksHealthy(runtimeInfo, pythonModule = CLI_ANYTHING_MODULE) {
55
+ let importable = false;
56
+
57
+ try {
58
+ const importResult = run(runtimeInfo.pythonPath, ['-c', `import ${pythonModule}`]);
59
+ importable = importResult.status === 0;
60
+ } catch (error) {
61
+ importable = false;
62
+ }
63
+
64
+ const cliAnythingPath = resolveCommandPath(runtimeInfo.binDir, 'cli-anything');
65
+
66
+ return {
67
+ importable,
68
+ cliAnythingPath,
69
+ healthy: importable && fs.existsSync(cliAnythingPath),
70
+ };
71
+ }
72
+
73
+ function installCLIAnythingRuntime(runtimeInfo, packageRoot = '', options = {}) {
74
+ const pythonModule = options.pythonModule || CLI_ANYTHING_MODULE;
75
+ const runtimeSource = resolveCLIAnythingRuntimeSource(packageRoot);
76
+
77
+ if (!runtimeSource) {
78
+ throw new Error('CLI-Anything runtime source not found in the installed AI Desk package');
79
+ }
80
+
81
+ const args = ['-m', 'pip', 'install', '--upgrade'];
82
+ if (options.forceReinstall) {
83
+ args.push('--force-reinstall');
84
+ }
85
+ args.push(runtimeSource);
86
+
87
+ const installResult = run(runtimeInfo.pythonPath, args);
88
+ if (installResult.status !== 0) {
89
+ throw new Error(
90
+ installResult.stderr ||
91
+ installResult.stdout ||
92
+ `Failed to install CLI-Anything runtime from ${runtimeSource}`
93
+ );
94
+ }
95
+
96
+ const health = cliAnythingRuntimeLooksHealthy(runtimeInfo, pythonModule);
97
+ if (!health.importable) {
98
+ throw new Error(`CLI-Anything Python module "${pythonModule}" is not importable after installation`);
99
+ }
100
+
101
+ if (!fs.existsSync(health.cliAnythingPath)) {
102
+ throw new Error(`CLI-Anything executable not found after installation: ${health.cliAnythingPath}`);
103
+ }
104
+
105
+ return {
106
+ ...runtimeInfo,
107
+ cliAnythingPath: health.cliAnythingPath,
108
+ pythonModule,
109
+ runtimeSource,
110
+ };
111
+ }
112
+
113
+ function ensureCLIAnythingRuntime(packageRoot = '', options = {}) {
114
+ const pythonModule = options.pythonModule || CLI_ANYTHING_MODULE;
115
+ const runtimeInfo = ensureRuntime();
116
+ const health = cliAnythingRuntimeLooksHealthy(runtimeInfo, pythonModule);
117
+
118
+ if (health.healthy && !options.forceReinstall) {
119
+ return {
120
+ ...runtimeInfo,
121
+ cliAnythingPath: health.cliAnythingPath,
122
+ pythonModule,
123
+ runtimeSource: resolveCLIAnythingRuntimeSource(packageRoot),
124
+ };
125
+ }
126
+
127
+ return installCLIAnythingRuntime(runtimeInfo, packageRoot, options);
128
+ }
129
+
130
+ function syncCLIAnythingConfig(packageRoot = '', options = {}) {
131
+ const configPath = options.configPath || getConfigPath();
132
+ const config = readJSON(configPath);
133
+ const cliAnything = config.cli_anything || {};
134
+ const runtime = cliAnything.runtime || {};
135
+ const features = cliAnything.features || {};
136
+
137
+ let runtimeInfo = null;
138
+ if (options.ensureInstalled !== false) {
139
+ runtimeInfo = ensureCLIAnythingRuntime(packageRoot, options);
140
+ }
141
+
142
+ if (typeof options.enabled === 'boolean') {
143
+ cliAnything.enabled = options.enabled;
144
+ } else if (typeof cliAnything.enabled !== 'boolean') {
145
+ cliAnything.enabled = false;
146
+ }
147
+
148
+ if (typeof options.fallbackToNative === 'boolean') {
149
+ cliAnything.fallback_to_native = options.fallbackToNative;
150
+ } else if (typeof cliAnything.fallback_to_native !== 'boolean') {
151
+ cliAnything.fallback_to_native = true;
152
+ }
153
+
154
+ if (runtimeInfo) {
155
+ cliAnything.cli_anything_path = runtimeInfo.cliAnythingPath;
156
+ runtime.python_path = runtimeInfo.pythonPath;
157
+ runtime.python_module = runtimeInfo.pythonModule;
158
+ } else {
159
+ cliAnything.cli_anything_path = cliAnything.cli_anything_path || '';
160
+ runtime.python_path = runtime.python_path || '';
161
+ runtime.python_module = runtime.python_module || CLI_ANYTHING_MODULE;
162
+ }
163
+
164
+ if (typeof options.timeout === 'number') {
165
+ cliAnything.timeout = options.timeout;
166
+ } else if (typeof cliAnything.timeout !== 'number') {
167
+ cliAnything.timeout = 30;
168
+ }
169
+
170
+ if (typeof options.maxRetries === 'number') {
171
+ cliAnything.max_retries = options.maxRetries;
172
+ } else if (typeof cliAnything.max_retries !== 'number') {
173
+ cliAnything.max_retries = 3;
174
+ }
175
+
176
+ if (typeof options.generationTimeout === 'number') {
177
+ runtime.generation_timeout = options.generationTimeout;
178
+ } else if (typeof runtime.generation_timeout !== 'number') {
179
+ runtime.generation_timeout = 300;
180
+ }
181
+
182
+ const booleanFeatureDefaults = {
183
+ ai_cli_detection: true,
184
+ model_listing: true,
185
+ command_execution: true,
186
+ task_generation: true,
187
+ };
188
+
189
+ for (const [featureKey, defaultValue] of Object.entries(booleanFeatureDefaults)) {
190
+ if (featureKey === 'task_generation' && typeof options.taskGeneration === 'boolean') {
191
+ features[featureKey] = options.taskGeneration;
192
+ continue;
193
+ }
194
+
195
+ if (typeof features[featureKey] !== 'boolean') {
196
+ features[featureKey] = defaultValue;
197
+ }
198
+ }
199
+
200
+ cliAnything.runtime = runtime;
201
+ cliAnything.features = features;
202
+ config.cli_anything = cliAnything;
203
+ writeJSON(configPath, config);
204
+
205
+ return {
206
+ configPath,
207
+ config,
208
+ runtimeInfo,
209
+ };
210
+ }
211
+
212
+ module.exports = {
213
+ CLI_ANYTHING_MODULE,
214
+ ensureCLIAnythingRuntime,
215
+ resolveCLIAnythingRuntimeSource,
216
+ resolveCommandPath,
217
+ syncCLIAnythingConfig,
218
+ };
package/lib/config.js CHANGED
@@ -7,6 +7,65 @@ const path = require('path');
7
7
  const { getConfigDir, getConfigPath } = require('./platform');
8
8
 
9
9
  const DEFAULT_PORT = 9527;
10
+ const DEFAULT_CONFIG = {
11
+ port: DEFAULT_PORT,
12
+ logLevel: 'info',
13
+ autoStart: true,
14
+ cli_anything: {
15
+ enabled: false,
16
+ fallback_to_native: true,
17
+ cli_anything_path: '',
18
+ timeout: 30,
19
+ max_retries: 3,
20
+ features: {
21
+ ai_cli_detection: true,
22
+ model_listing: true,
23
+ command_execution: true,
24
+ task_generation: true,
25
+ },
26
+ runtime: {
27
+ python_path: '',
28
+ python_module: 'cli_anything',
29
+ generation_timeout: 300,
30
+ },
31
+ },
32
+ harness_runtime: {
33
+ venv_path: '',
34
+ python_path: '',
35
+ registry_path: '',
36
+ source: '',
37
+ runtime_home: '',
38
+ package_name: '',
39
+ package_version: '',
40
+ archive_sha256: '',
41
+ },
42
+ harnesses: {},
43
+ };
44
+
45
+ function cloneDefaultConfig() {
46
+ return JSON.parse(JSON.stringify(DEFAULT_CONFIG));
47
+ }
48
+
49
+ function mergeConfig(base, override) {
50
+ if (!override || typeof override !== 'object' || Array.isArray(override)) {
51
+ return base;
52
+ }
53
+
54
+ const merged = Array.isArray(base) ? [...base] : { ...base };
55
+ for (const [key, value] of Object.entries(override)) {
56
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
57
+ const nestedBase = merged[key] && typeof merged[key] === 'object' && !Array.isArray(merged[key])
58
+ ? merged[key]
59
+ : {};
60
+ merged[key] = mergeConfig(nestedBase, value);
61
+ continue;
62
+ }
63
+
64
+ merged[key] = value;
65
+ }
66
+
67
+ return merged;
68
+ }
10
69
 
11
70
  /**
12
71
  * Ensure config directory exists
@@ -33,22 +92,17 @@ function loadConfig() {
33
92
  const configPath = getConfigPath();
34
93
 
35
94
  if (!fs.existsSync(configPath)) {
36
- // Create default config
37
- const defaultConfig = {
38
- port: DEFAULT_PORT,
39
- logLevel: 'info',
40
- autoStart: true
41
- };
95
+ const defaultConfig = cloneDefaultConfig();
42
96
  saveConfig(defaultConfig);
43
97
  return defaultConfig;
44
98
  }
45
99
 
46
100
  try {
47
101
  const content = fs.readFileSync(configPath, 'utf8');
48
- return JSON.parse(content);
102
+ return mergeConfig(cloneDefaultConfig(), JSON.parse(content));
49
103
  } catch (error) {
50
104
  console.error('Failed to load config:', error.message);
51
- return { port: DEFAULT_PORT };
105
+ return cloneDefaultConfig();
52
106
  }
53
107
  }
54
108
 
@@ -59,7 +113,8 @@ function saveConfig(config) {
59
113
  ensureConfigDir();
60
114
 
61
115
  const configPath = getConfigPath();
62
- fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf8');
116
+ const mergedConfig = mergeConfig(cloneDefaultConfig(), config);
117
+ fs.writeFileSync(configPath, JSON.stringify(mergedConfig, null, 2), 'utf8');
63
118
  }
64
119
 
65
120
  /**
@@ -97,7 +152,8 @@ module.exports = {
97
152
  saveConfig,
98
153
  getPort,
99
154
  setPort,
155
+ cloneDefaultConfig,
156
+ mergeConfig,
100
157
  getConfigPath, // Re-export from platform.js
101
158
  DEFAULT_PORT
102
159
  };
103
-
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-webui/ai-desk-daemon",
3
- "version": "1.0.29-beta1",
3
+ "version": "1.0.29-beta2",
4
4
  "description": "AI Desk Daemon - CLI tool for managing the AI Desk daemon service",
5
5
  "workspaces": [
6
6
  "packages/*"
@@ -29,6 +29,8 @@
29
29
  "files": [
30
30
  "bin/",
31
31
  "lib/",
32
+ "python-runtime/setup.py",
33
+ "python-runtime/cli_anything/",
32
34
  "scripts/postinstall.js",
33
35
  "README.md"
34
36
  ],
@@ -37,16 +39,16 @@
37
39
  "chalk": "^4.1.2"
38
40
  },
39
41
  "optionalDependencies": {
40
- "@agent-webui/ai-desk-daemon-darwin-arm64": "1.0.29-beta1",
41
- "@agent-webui/ai-desk-daemon-darwin-x64": "1.0.29-beta1",
42
- "@agent-webui/ai-desk-daemon-linux-arm64": "1.0.29-beta1",
43
- "@agent-webui/ai-desk-daemon-linux-x64": "1.0.29-beta1",
44
- "@agent-webui/ai-desk-daemon-win32-x64": "1.0.29-beta1",
45
- "@agent-webui/ai-desk-python-darwin-arm64": "1.0.29-beta1",
46
- "@agent-webui/ai-desk-python-darwin-x64": "1.0.29-beta1",
47
- "@agent-webui/ai-desk-python-linux-arm64": "1.0.29-beta1",
48
- "@agent-webui/ai-desk-python-linux-x64": "1.0.29-beta1",
49
- "@agent-webui/ai-desk-python-win32-x64": "1.0.29-beta1"
42
+ "@agent-webui/ai-desk-daemon-darwin-arm64": "1.0.29-beta2",
43
+ "@agent-webui/ai-desk-daemon-darwin-x64": "1.0.29-beta2",
44
+ "@agent-webui/ai-desk-daemon-linux-arm64": "1.0.29-beta2",
45
+ "@agent-webui/ai-desk-daemon-linux-x64": "1.0.29-beta2",
46
+ "@agent-webui/ai-desk-daemon-win32-x64": "1.0.29-beta2",
47
+ "@agent-webui/ai-desk-python-darwin-arm64": "1.0.29-beta2",
48
+ "@agent-webui/ai-desk-python-darwin-x64": "1.0.29-beta2",
49
+ "@agent-webui/ai-desk-python-linux-arm64": "1.0.29-beta2",
50
+ "@agent-webui/ai-desk-python-linux-x64": "1.0.29-beta2",
51
+ "@agent-webui/ai-desk-python-win32-x64": "1.0.29-beta2"
50
52
  },
51
53
  "repository": {
52
54
  "type": "git",
@@ -0,0 +1,3 @@
1
+ __all__ = ["__version__"]
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,5 @@
1
+ from cli_anything._compat import main
2
+
3
+
4
+ if __name__ == "__main__":
5
+ raise SystemExit(main())
@@ -0,0 +1,195 @@
1
+ import argparse
2
+ import json
3
+ import os
4
+ import shutil
5
+ import subprocess
6
+ import sys
7
+ from pathlib import Path
8
+
9
+
10
+ KNOWN_AI_CLIS = [
11
+ {
12
+ "name": "Claude CLI",
13
+ "type": "claude",
14
+ "commands": ["claude"],
15
+ "capabilities": ["chat", "completion"],
16
+ },
17
+ {
18
+ "name": "Gemini CLI",
19
+ "type": "gemini",
20
+ "commands": ["gemini"],
21
+ "capabilities": ["chat", "completion"],
22
+ },
23
+ {
24
+ "name": "Cursor Agent CLI",
25
+ "type": "cursor",
26
+ "commands": ["agent", "cursor-agent"],
27
+ "capabilities": ["chat", "completion"],
28
+ },
29
+ {
30
+ "name": "Codex CLI",
31
+ "type": "codex",
32
+ "commands": ["codex"],
33
+ "capabilities": ["chat", "completion"],
34
+ },
35
+ ]
36
+
37
+
38
+ def _resolve_command(commands):
39
+ for command in commands:
40
+ path = shutil.which(command)
41
+ if path:
42
+ return command, path
43
+ return "", ""
44
+
45
+
46
+ def _read_version(path):
47
+ for flag in ("--version", "-v", "version"):
48
+ try:
49
+ result = subprocess.run(
50
+ [path, flag],
51
+ capture_output=True,
52
+ text=True,
53
+ timeout=2,
54
+ check=False,
55
+ )
56
+ except Exception:
57
+ continue
58
+
59
+ output = (result.stdout or result.stderr or "").strip()
60
+ if output:
61
+ return output.splitlines()[0][:200]
62
+ return ""
63
+
64
+
65
+ def _detect_known_ai_clis():
66
+ clis = []
67
+ for item in KNOWN_AI_CLIS:
68
+ command, path = _resolve_command(item["commands"])
69
+ if not path:
70
+ continue
71
+ clis.append(
72
+ {
73
+ "name": item["name"],
74
+ "type": item["type"],
75
+ "command": command,
76
+ "path": path,
77
+ "version": _read_version(path),
78
+ "capabilities": item["capabilities"],
79
+ }
80
+ )
81
+ return clis
82
+
83
+
84
+ def _detect_cli_anything_tools():
85
+ tools = []
86
+ seen = set()
87
+ for path_entry in os.environ.get("PATH", "").split(os.pathsep):
88
+ if not path_entry:
89
+ continue
90
+ path = Path(path_entry)
91
+ if not path.exists() or not path.is_dir():
92
+ continue
93
+ for entry in path.iterdir():
94
+ name = entry.name
95
+ if not name.startswith("cli-anything-"):
96
+ continue
97
+ if not entry.is_file() or not os.access(entry, os.X_OK):
98
+ continue
99
+ if name in seen:
100
+ continue
101
+ seen.add(name)
102
+ software_name = name[len("cli-anything-") :]
103
+ tools.append(
104
+ {
105
+ "name": f"CLI-Anything: {software_name.title()}",
106
+ "type": "cli-anything",
107
+ "command": name,
108
+ "path": str(entry),
109
+ "version": _read_version(str(entry)),
110
+ "capabilities": [],
111
+ }
112
+ )
113
+ return tools
114
+
115
+
116
+ def cmd_detect(args):
117
+ if args.detect_type != "ai-cli":
118
+ raise SystemExit("only '--type ai-cli' is supported by the bundled runtime")
119
+
120
+ clis = _detect_known_ai_clis() + _detect_cli_anything_tools()
121
+
122
+ if args.name:
123
+ for cli in clis:
124
+ if cli["type"] == args.name or cli["command"] == args.name:
125
+ print(json.dumps(cli))
126
+ return 0
127
+
128
+ print(
129
+ json.dumps(
130
+ {
131
+ "name": args.name.title(),
132
+ "type": args.name,
133
+ "path": "",
134
+ "version": "",
135
+ "capabilities": [],
136
+ }
137
+ )
138
+ )
139
+ return 0
140
+
141
+ print(json.dumps({"clis": clis}))
142
+ return 0
143
+
144
+
145
+ def cmd_list_models(args):
146
+ sys.stderr.write(
147
+ "bundled cli_anything runtime does not implement model discovery; "
148
+ "AI Desk daemon should fall back to native model listing\n"
149
+ )
150
+ return 2
151
+
152
+
153
+ def cmd_execute(args):
154
+ command = shutil.which(args.cli) or args.cli
155
+ if not shutil.which(args.cli) and not os.path.isabs(args.cli):
156
+ sys.stderr.write(f'command not found: {args.cli}\n')
157
+ return 127
158
+
159
+ child_args = list(args.command_args or [])
160
+ if child_args and child_args[0] == "--":
161
+ child_args = child_args[1:]
162
+
163
+ proc = subprocess.Popen([command] + child_args, cwd=args.cwd or None)
164
+ return proc.wait()
165
+
166
+
167
+ def build_parser():
168
+ parser = argparse.ArgumentParser(prog="cli-anything")
169
+ subparsers = parser.add_subparsers(dest="command", required=True)
170
+
171
+ detect_parser = subparsers.add_parser("detect")
172
+ detect_parser.add_argument("--type", dest="detect_type", required=True)
173
+ detect_parser.add_argument("--format", default="json")
174
+ detect_parser.add_argument("--name")
175
+ detect_parser.set_defaults(handler=cmd_detect)
176
+
177
+ list_models_parser = subparsers.add_parser("list-models")
178
+ list_models_parser.add_argument("--cli", required=True)
179
+ list_models_parser.add_argument("--format", default="json")
180
+ list_models_parser.set_defaults(handler=cmd_list_models)
181
+
182
+ execute_parser = subparsers.add_parser("execute")
183
+ execute_parser.add_argument("--cli", required=True)
184
+ execute_parser.add_argument("--cwd", default="")
185
+ execute_parser.add_argument("--stream", action="store_true")
186
+ execute_parser.add_argument("command_args", nargs=argparse.REMAINDER)
187
+ execute_parser.set_defaults(handler=cmd_execute)
188
+
189
+ return parser
190
+
191
+
192
+ def main(argv=None):
193
+ parser = build_parser()
194
+ args = parser.parse_args(argv)
195
+ return args.handler(args)
@@ -0,0 +1,10 @@
1
+ import sys
2
+
3
+
4
+ def main():
5
+ sys.stderr.write("bundled cli_anything runtime does not implement generator stage 'analyze'\n")
6
+ return 2
7
+
8
+
9
+ if __name__ == "__main__":
10
+ raise SystemExit(main())
@@ -0,0 +1,10 @@
1
+ import sys
2
+
3
+
4
+ def main():
5
+ sys.stderr.write("bundled cli_anything runtime does not implement generator stage 'design'\n")
6
+ return 2
7
+
8
+
9
+ if __name__ == "__main__":
10
+ raise SystemExit(main())
@@ -0,0 +1,10 @@
1
+ import sys
2
+
3
+
4
+ def main():
5
+ sys.stderr.write("bundled cli_anything runtime does not implement generator stage 'generate'\n")
6
+ return 2
7
+
8
+
9
+ if __name__ == "__main__":
10
+ raise SystemExit(main())
@@ -0,0 +1,10 @@
1
+ import sys
2
+
3
+
4
+ def main():
5
+ sys.stderr.write("bundled cli_anything runtime does not implement generator stage 'map'\n")
6
+ return 2
7
+
8
+
9
+ if __name__ == "__main__":
10
+ raise SystemExit(main())
@@ -0,0 +1,10 @@
1
+ import sys
2
+
3
+
4
+ def main():
5
+ sys.stderr.write("bundled cli_anything runtime does not implement generator stage 'model'\n")
6
+ return 2
7
+
8
+
9
+ if __name__ == "__main__":
10
+ raise SystemExit(main())
@@ -0,0 +1,14 @@
1
+ from setuptools import find_packages, setup
2
+
3
+
4
+ setup(
5
+ name="ai-desk-cli-anything-runtime",
6
+ version="0.1.0",
7
+ description="Compatibility runtime used by AI Desk daemon for CLI-Anything mode",
8
+ packages=find_packages(),
9
+ entry_points={
10
+ "console_scripts": [
11
+ "cli-anything=cli_anything.__main__:main",
12
+ ],
13
+ },
14
+ )
@@ -10,15 +10,20 @@ const chalk = require('chalk');
10
10
  const path = require('path');
11
11
  const { ensureRuntime } = require('../lib/runtime-manager');
12
12
  const { installHarnessesForPackageRoot } = require('../lib/harness-manager');
13
+ const { syncCLIAnythingConfig } = require('../lib/cli-anything-manager');
13
14
 
14
15
  console.log('');
15
16
 
16
17
  try {
18
+ const cliAnything = syncCLIAnythingConfig(path.join(__dirname, '..'));
17
19
  const runtime = ensureRuntime();
18
20
  const installResult = installHarnessesForPackageRoot(path.join(__dirname, '..'));
19
21
 
20
22
  console.log(chalk.green('✓ @agent-webui/ai-desk-daemon installed successfully'));
21
23
  console.log(chalk.cyan(`Runtime: ${runtime.pythonPath}`));
24
+ if (cliAnything.runtimeInfo?.cliAnythingPath) {
25
+ console.log(chalk.cyan(`CLI-Anything runtime: ${cliAnything.runtimeInfo.cliAnythingPath}`));
26
+ }
22
27
  console.log(chalk.cyan(`Harnesses installed: ${installResult.harnessCount}`));
23
28
  console.log('');
24
29
  console.log('Run ' + chalk.cyan('aidesk --help') + ' to get started');