@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 +16 -2
- package/bin/cli.js +77 -3
- package/lib/cli-anything-manager.js +218 -0
- package/lib/config.js +66 -10
- package/package.json +13 -11
- package/python-runtime/cli_anything/__init__.py +3 -0
- package/python-runtime/cli_anything/__main__.py +5 -0
- package/python-runtime/cli_anything/_compat.py +195 -0
- package/python-runtime/cli_anything/analyze.py +10 -0
- package/python-runtime/cli_anything/design.py +10 -0
- package/python-runtime/cli_anything/generate.py +10 -0
- package/python-runtime/cli_anything/map.py +10 -0
- package/python-runtime/cli_anything/model.py +10 -0
- package/python-runtime/setup.py +14 -0
- package/scripts/postinstall.js +5 -0
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
|
|
27
|
-
const
|
|
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
|
-
.
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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-
|
|
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-
|
|
41
|
-
"@agent-webui/ai-desk-daemon-darwin-x64": "1.0.29-
|
|
42
|
-
"@agent-webui/ai-desk-daemon-linux-arm64": "1.0.29-
|
|
43
|
-
"@agent-webui/ai-desk-daemon-linux-x64": "1.0.29-
|
|
44
|
-
"@agent-webui/ai-desk-daemon-win32-x64": "1.0.29-
|
|
45
|
-
"@agent-webui/ai-desk-python-darwin-arm64": "1.0.29-
|
|
46
|
-
"@agent-webui/ai-desk-python-darwin-x64": "1.0.29-
|
|
47
|
-
"@agent-webui/ai-desk-python-linux-arm64": "1.0.29-
|
|
48
|
-
"@agent-webui/ai-desk-python-linux-x64": "1.0.29-
|
|
49
|
-
"@agent-webui/ai-desk-python-win32-x64": "1.0.29-
|
|
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,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,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
|
+
)
|
package/scripts/postinstall.js
CHANGED
|
@@ -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');
|