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

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
@@ -24,7 +24,7 @@
24
24
 
25
25
  ```bash
26
26
  # 全局安装
27
- npm install -g agent-webui/ai-desk-daemon
27
+ npm install -g @agent-webui/ai-desk
28
28
 
29
29
  # 启动 daemon
30
30
  aidesk start
@@ -36,6 +36,8 @@ aidesk status
36
36
  **包含内容**:
37
37
  - ✅ AI Desk Daemon 后台服务
38
38
  - ✅ CLI 命令行管理工具
39
+ - ✅ 默认 harness 运行时与安装脚本
40
+ - ✅ 按平台分发的内置 Python runtime
39
41
  - ✅ HTTP API (http://localhost:9527)
40
42
 
41
43
  **不包含**:
package/bin/cli.js CHANGED
@@ -12,7 +12,7 @@ const { getLogPath } = require('../lib/platform');
12
12
  const { VERSION } = require('../lib/platform');
13
13
 
14
14
  program
15
- .name('ai-desk-daemon')
15
+ .name('aidesk')
16
16
  .description('AI Desk Daemon - CLI tool for managing the AI Desk daemon service')
17
17
  .version(VERSION);
18
18
 
@@ -228,4 +228,3 @@ program.parse(process.argv);
228
228
  if (!process.argv.slice(2).length) {
229
229
  program.outputHelp();
230
230
  }
231
-
@@ -80,7 +80,7 @@ function start() {
80
80
  throw new Error(
81
81
  `Daemon binary not found at: ${binaryPath}\n` +
82
82
  `This might be a corrupted installation. Try reinstalling:\n` +
83
- ` npm install -g @ringcentral/ai-desk-daemon --force`
83
+ ` npm install -g @agent-webui/ai-desk --force`
84
84
  );
85
85
  }
86
86
 
@@ -186,4 +186,3 @@ module.exports = {
186
186
  restart,
187
187
  status
188
188
  };
189
-
@@ -0,0 +1,178 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { ensureRuntime, readJSON, run, syncHarnessConfig, writeJSON } = require('./runtime-manager');
4
+
5
+ function listDependencyNames(packageRoot) {
6
+ const packageJsonPath = path.join(packageRoot, 'package.json');
7
+ const packageJson = readJSON(packageJsonPath);
8
+
9
+ const allDeps = {
10
+ ...(packageJson.dependencies || {}),
11
+ ...(packageJson.optionalDependencies || {}),
12
+ };
13
+
14
+ return Object.keys(allDeps).filter((name) => name.startsWith('@agent-webui/ai-desk-harness-'));
15
+ }
16
+
17
+ function findWorkspaceSiblingPackage(packageRoot, packageName) {
18
+ const packagesRoot = path.resolve(packageRoot, '..');
19
+ if (!fs.existsSync(packagesRoot)) {
20
+ return null;
21
+ }
22
+
23
+ const children = fs.readdirSync(packagesRoot, { withFileTypes: true });
24
+ for (const child of children) {
25
+ if (!child.isDirectory()) {
26
+ continue;
27
+ }
28
+
29
+ const packageJsonPath = path.join(packagesRoot, child.name, 'package.json');
30
+ if (!fs.existsSync(packageJsonPath)) {
31
+ continue;
32
+ }
33
+
34
+ const pkg = readJSON(packageJsonPath);
35
+ if (pkg.name === packageName) {
36
+ return path.dirname(packageJsonPath);
37
+ }
38
+ }
39
+
40
+ return null;
41
+ }
42
+
43
+ function resolveHarnessPackageDir(packageRoot, packageName) {
44
+ try {
45
+ const packageJsonPath = require.resolve(`${packageName}/package.json`, {
46
+ paths: [packageRoot],
47
+ });
48
+ return path.dirname(packageJsonPath);
49
+ } catch (error) {
50
+ const workspaceMatch = findWorkspaceSiblingPackage(packageRoot, packageName);
51
+ if (workspaceMatch) {
52
+ return workspaceMatch;
53
+ }
54
+ throw new Error(`Failed to resolve harness package ${packageName} from ${packageRoot}: ${error.message}`);
55
+ }
56
+ }
57
+
58
+ function resolveHarnesses(packageRoot) {
59
+ const harnessNames = listDependencyNames(packageRoot);
60
+
61
+ return harnessNames.map((packageName) => {
62
+ const packageDir = resolveHarnessPackageDir(packageRoot, packageName);
63
+ const packageJson = readJSON(path.join(packageDir, 'package.json'));
64
+ const manifestPath = path.join(packageDir, 'manifest.json');
65
+ const manifest = readJSON(manifestPath);
66
+
67
+ return {
68
+ packageDir,
69
+ packageJson,
70
+ packageName,
71
+ manifest,
72
+ manifestPath,
73
+ sourcePath: path.resolve(packageDir, manifest.source_path),
74
+ };
75
+ });
76
+ }
77
+
78
+ function resolveCommandPath(binDir, command) {
79
+ const candidates = process.platform === 'win32'
80
+ ? [
81
+ path.join(binDir, `${command}.exe`),
82
+ path.join(binDir, `${command}.cmd`),
83
+ path.join(binDir, `${command}.bat`),
84
+ path.join(binDir, command),
85
+ ]
86
+ : [path.join(binDir, command)];
87
+
88
+ for (const candidate of candidates) {
89
+ if (fs.existsSync(candidate)) {
90
+ return candidate;
91
+ }
92
+ }
93
+
94
+ return candidates[0];
95
+ }
96
+
97
+ function installHarnessPackage(runtimeInfo, harness) {
98
+ if (!fs.existsSync(harness.sourcePath)) {
99
+ throw new Error(`Harness source not found: ${harness.sourcePath}`);
100
+ }
101
+
102
+ const installResult = run(runtimeInfo.pythonPath, ['-m', 'pip', 'install', '--upgrade', harness.sourcePath]);
103
+ if (installResult.status !== 0) {
104
+ throw new Error(
105
+ installResult.stderr ||
106
+ installResult.stdout ||
107
+ `Failed to install harness ${harness.packageName} from ${harness.sourcePath}`
108
+ );
109
+ }
110
+
111
+ for (const requirement of harness.manifest.python_requirements || []) {
112
+ const requirementResult = run(runtimeInfo.pythonPath, ['-m', 'pip', 'install', '--upgrade', requirement]);
113
+ if (requirementResult.status !== 0) {
114
+ throw new Error(
115
+ requirementResult.stderr ||
116
+ requirementResult.stdout ||
117
+ `Failed to install Python requirement ${requirement} for ${harness.packageName}`
118
+ );
119
+ }
120
+ }
121
+
122
+ return {
123
+ name: harness.manifest.name,
124
+ displayName: harness.manifest.display_name || harness.manifest.name,
125
+ module: harness.manifest.module,
126
+ commands: harness.manifest.commands || [],
127
+ capabilities: harness.manifest.capabilities || [],
128
+ commandPath: resolveCommandPath(runtimeInfo.binDir, (harness.manifest.commands || [])[0] || ''),
129
+ commandPaths: Object.fromEntries(
130
+ (harness.manifest.commands || []).map((command) => [command, resolveCommandPath(runtimeInfo.binDir, command)])
131
+ ),
132
+ packageName: harness.packageName,
133
+ packageVersion: harness.packageJson.version || '',
134
+ version: harness.manifest.version || harness.packageJson.version || '',
135
+ sourcePath: harness.sourcePath,
136
+ };
137
+ }
138
+
139
+ function writeHarnessRegistry(runtimeInfo, harnesses) {
140
+ const registry = {
141
+ version: 1,
142
+ generated_at: new Date().toISOString(),
143
+ runtime: {
144
+ python_path: runtimeInfo.pythonPath,
145
+ venv_path: runtimeInfo.venvDir,
146
+ registry_path: runtimeInfo.registryPath,
147
+ source: runtimeInfo.source || '',
148
+ runtime_home: runtimeInfo.runtimeHomeDir || '',
149
+ package_name: runtimeInfo.packageName || '',
150
+ package_version: runtimeInfo.packageVersion || '',
151
+ archive_sha256: runtimeInfo.archiveSHA256 || '',
152
+ },
153
+ harnesses,
154
+ };
155
+
156
+ writeJSON(runtimeInfo.registryPath, registry);
157
+ syncHarnessConfig(runtimeInfo, harnesses);
158
+ return registry;
159
+ }
160
+
161
+ function installHarnessesForPackageRoot(packageRoot) {
162
+ const runtimeInfo = ensureRuntime();
163
+ const harnessPackages = resolveHarnesses(packageRoot);
164
+ const installedHarnesses = harnessPackages.map((harness) => installHarnessPackage(runtimeInfo, harness));
165
+ const registry = writeHarnessRegistry(runtimeInfo, installedHarnesses);
166
+
167
+ return {
168
+ harnessCount: installedHarnesses.length,
169
+ harnesses: installedHarnesses,
170
+ registry,
171
+ runtimeInfo,
172
+ };
173
+ }
174
+
175
+ module.exports = {
176
+ installHarnessesForPackageRoot,
177
+ resolveHarnesses,
178
+ };
package/lib/platform.js CHANGED
@@ -4,8 +4,10 @@
4
4
 
5
5
  const os = require('os');
6
6
  const path = require('path');
7
+ const fs = require('fs');
8
+ const { getBinaryPackageByPlatformKey, resolveInstalledPackageDir } = require('./workspace-packages');
7
9
 
8
- const VERSION = '1.0.25';
10
+ const VERSION = require('../package.json').version;
9
11
 
10
12
  /**
11
13
  * Detect current platform and architecture
@@ -39,9 +41,19 @@ function detectPlatform() {
39
41
  */
40
42
  function getDaemonBinaryPath() {
41
43
  const { platform, platformKey } = detectPlatform();
44
+ const binaryPackage = getBinaryPackageByPlatformKey(platformKey);
42
45
 
43
- // Binary is in the npm package under dist/<platform>/
44
46
  const binaryName = platform === 'win32' ? 'ai-desk-daemon.exe' : 'ai-desk-daemon';
47
+ if (binaryPackage) {
48
+ const packageDir = resolveInstalledPackageDir(binaryPackage.packageName);
49
+ if (packageDir) {
50
+ const packageBinaryPath = path.join(packageDir, 'bin', binaryName);
51
+ if (fs.existsSync(packageBinaryPath)) {
52
+ return packageBinaryPath;
53
+ }
54
+ }
55
+ }
56
+
45
57
  const binaryPath = path.join(__dirname, '..', 'dist', platformKey, binaryName);
46
58
 
47
59
  return binaryPath;
@@ -54,6 +66,54 @@ function getConfigDir() {
54
66
  return path.join(os.homedir(), '.aidesktop');
55
67
  }
56
68
 
69
+ function getRuntimeDir() {
70
+ return path.join(getConfigDir(), 'runtime');
71
+ }
72
+
73
+ function getManagedPythonDir() {
74
+ return path.join(getRuntimeDir(), 'python');
75
+ }
76
+
77
+ function getManagedPythonBinDir() {
78
+ if (process.platform === 'win32') {
79
+ return getManagedPythonDir();
80
+ }
81
+ return path.join(getManagedPythonDir(), 'bin');
82
+ }
83
+
84
+ function getManagedPythonPath() {
85
+ if (process.platform === 'win32') {
86
+ return path.join(getManagedPythonDir(), 'python.exe');
87
+ }
88
+ return path.join(getManagedPythonBinDir(), 'python3');
89
+ }
90
+
91
+ function getManagedPythonMetadataPath() {
92
+ return path.join(getManagedPythonDir(), '.ai-desk-runtime.json');
93
+ }
94
+
95
+ function getRuntimeVenvDir() {
96
+ return path.join(getRuntimeDir(), 'venv');
97
+ }
98
+
99
+ function getRuntimeBinDir() {
100
+ if (process.platform === 'win32') {
101
+ return path.join(getRuntimeVenvDir(), 'Scripts');
102
+ }
103
+ return path.join(getRuntimeVenvDir(), 'bin');
104
+ }
105
+
106
+ function getRuntimePythonPath() {
107
+ if (process.platform === 'win32') {
108
+ return path.join(getRuntimeBinDir(), 'python.exe');
109
+ }
110
+ return path.join(getRuntimeBinDir(), 'python3');
111
+ }
112
+
113
+ function getHarnessRegistryPath() {
114
+ return path.join(getRuntimeDir(), 'installed-harnesses.json');
115
+ }
116
+
57
117
  /**
58
118
  * Get config file path
59
119
  */
@@ -79,9 +139,17 @@ module.exports = {
79
139
  detectPlatform,
80
140
  getDaemonBinaryPath,
81
141
  getConfigDir,
142
+ getRuntimeDir,
143
+ getManagedPythonDir,
144
+ getManagedPythonBinDir,
145
+ getManagedPythonPath,
146
+ getManagedPythonMetadataPath,
147
+ getRuntimeVenvDir,
148
+ getRuntimeBinDir,
149
+ getRuntimePythonPath,
150
+ getHarnessRegistryPath,
82
151
  getConfigPath,
83
152
  getLogPath,
84
153
  getPidPath,
85
154
  VERSION
86
155
  };
87
-
@@ -0,0 +1,320 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { spawnSync } = require('child_process');
4
+ const {
5
+ getConfigDir,
6
+ getConfigPath,
7
+ getHarnessRegistryPath,
8
+ getManagedPythonBinDir,
9
+ getManagedPythonDir,
10
+ getManagedPythonMetadataPath,
11
+ getManagedPythonPath,
12
+ getRuntimeBinDir,
13
+ getRuntimeDir,
14
+ getRuntimePythonPath,
15
+ getRuntimeVenvDir,
16
+ detectPlatform,
17
+ } = require('./platform');
18
+ const {
19
+ getPythonRuntimePackageByPlatformKey,
20
+ resolveInstalledPackageDir,
21
+ } = require('./workspace-packages');
22
+
23
+ function readJSON(filePath) {
24
+ if (!fs.existsSync(filePath)) {
25
+ return {};
26
+ }
27
+
28
+ try {
29
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
30
+ } catch (error) {
31
+ throw new Error(`Failed to parse JSON at ${filePath}: ${error.message}`);
32
+ }
33
+ }
34
+
35
+ function writeJSON(filePath, value) {
36
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
37
+ fs.writeFileSync(filePath, JSON.stringify(value, null, 2) + '\n', 'utf8');
38
+ }
39
+
40
+ function run(command, args, options = {}) {
41
+ const result = spawnSync(command, args, {
42
+ encoding: 'utf8',
43
+ stdio: 'pipe',
44
+ ...options,
45
+ });
46
+
47
+ if (result.error) {
48
+ throw result.error;
49
+ }
50
+
51
+ return result;
52
+ }
53
+
54
+ function findSystemPython() {
55
+ const candidates = [];
56
+
57
+ if (process.env.AI_DESK_PYTHON) {
58
+ candidates.push({ command: process.env.AI_DESK_PYTHON, args: [] });
59
+ }
60
+
61
+ candidates.push({ command: 'python3', args: [] });
62
+ candidates.push({ command: 'python', args: [] });
63
+
64
+ if (process.platform === 'win32') {
65
+ candidates.push({ command: 'py', args: ['-3'] });
66
+ }
67
+
68
+ for (const candidate of candidates) {
69
+ try {
70
+ const result = run(candidate.command, [...candidate.args, '--version']);
71
+ if (result.status === 0) {
72
+ return candidate;
73
+ }
74
+ } catch (error) {
75
+ // Try next candidate.
76
+ }
77
+ }
78
+
79
+ throw new Error('No usable Python interpreter found. Set AI_DESK_PYTHON to override.');
80
+ }
81
+
82
+ function ensureRuntimeDirectories() {
83
+ fs.mkdirSync(getConfigDir(), { recursive: true });
84
+ fs.mkdirSync(path.join(getConfigDir(), 'logs'), { recursive: true });
85
+ fs.mkdirSync(getRuntimeDir(), { recursive: true });
86
+ }
87
+
88
+ function getBundledPythonRuntime() {
89
+ const { platformKey } = detectPlatform();
90
+ const runtimePackage = getPythonRuntimePackageByPlatformKey(platformKey);
91
+ if (!runtimePackage) {
92
+ return null;
93
+ }
94
+
95
+ const packageDir = resolveInstalledPackageDir(runtimePackage.packageName, [path.join(__dirname, '..')]);
96
+ if (!packageDir) {
97
+ return null;
98
+ }
99
+
100
+ const packageJson = readJSON(path.join(packageDir, 'package.json'));
101
+ const sourceRuntimeDir = path.join(packageDir, 'python');
102
+ const sourcePythonPath = path.join(packageDir, runtimePackage.pythonRelativePath);
103
+ const sourceBinDir = path.join(packageDir, runtimePackage.binDirRelativePath);
104
+
105
+ if (!fs.existsSync(sourceRuntimeDir) || !fs.existsSync(sourcePythonPath)) {
106
+ return null;
107
+ }
108
+
109
+ return {
110
+ packageDir,
111
+ packageName: runtimePackage.packageName,
112
+ packageVersion: packageJson.version || '',
113
+ archiveName: runtimePackage.archiveName,
114
+ archiveSHA256: runtimePackage.sha256 || '',
115
+ sourceRuntimeDir,
116
+ sourcePythonPath,
117
+ sourceBinDir,
118
+ };
119
+ }
120
+
121
+ function copyDirectoryRecursive(sourceDir, targetDir) {
122
+ fs.mkdirSync(targetDir, { recursive: true });
123
+
124
+ for (const entry of fs.readdirSync(sourceDir, { withFileTypes: true })) {
125
+ const sourcePath = path.join(sourceDir, entry.name);
126
+ const targetPath = path.join(targetDir, entry.name);
127
+
128
+ if (entry.isDirectory()) {
129
+ copyDirectoryRecursive(sourcePath, targetPath);
130
+ continue;
131
+ }
132
+
133
+ if (entry.isSymbolicLink()) {
134
+ const linkTarget = fs.readlinkSync(sourcePath);
135
+ try {
136
+ fs.unlinkSync(targetPath);
137
+ } catch (error) {
138
+ if (error.code !== 'ENOENT') {
139
+ throw error;
140
+ }
141
+ }
142
+ fs.symlinkSync(linkTarget, targetPath);
143
+ continue;
144
+ }
145
+
146
+ fs.copyFileSync(sourcePath, targetPath);
147
+ try {
148
+ fs.chmodSync(targetPath, fs.statSync(sourcePath).mode);
149
+ } catch (error) {
150
+ // Ignore chmod failures on platforms that do not preserve permissions.
151
+ }
152
+ }
153
+ }
154
+
155
+ function packagedRuntimeLooksHealthy(pythonPath) {
156
+ if (!fs.existsSync(pythonPath)) {
157
+ return false;
158
+ }
159
+
160
+ try {
161
+ const result = run(pythonPath, ['-c', 'import sys; print(sys.executable)']);
162
+ return result.status === 0;
163
+ } catch (error) {
164
+ return false;
165
+ }
166
+ }
167
+
168
+ function ensurePackagedRuntime(bundledRuntime) {
169
+ const targetRuntimeDir = getManagedPythonDir();
170
+ const metadataPath = getManagedPythonMetadataPath();
171
+ const expectedMetadata = {
172
+ package_name: bundledRuntime.packageName,
173
+ package_version: bundledRuntime.packageVersion,
174
+ };
175
+
176
+ const existingMetadata = readJSON(metadataPath);
177
+ const targetPythonPath = getManagedPythonPath();
178
+ const targetBinDir = getManagedPythonBinDir();
179
+
180
+ const metadataMatches =
181
+ existingMetadata.package_name === expectedMetadata.package_name &&
182
+ existingMetadata.package_version === expectedMetadata.package_version;
183
+
184
+ if (!metadataMatches || !packagedRuntimeLooksHealthy(targetPythonPath)) {
185
+ fs.rmSync(targetRuntimeDir, { recursive: true, force: true });
186
+ copyDirectoryRecursive(bundledRuntime.sourceRuntimeDir, targetRuntimeDir);
187
+ writeJSON(metadataPath, expectedMetadata);
188
+ }
189
+
190
+ return {
191
+ pythonPath: targetPythonPath,
192
+ binDir: targetBinDir,
193
+ runtimeHomeDir: targetRuntimeDir,
194
+ packageName: bundledRuntime.packageName,
195
+ packageVersion: bundledRuntime.packageVersion,
196
+ archiveSHA256: bundledRuntime.archiveSHA256,
197
+ source: 'packaged',
198
+ };
199
+ }
200
+
201
+ function resolveRuntimePythonPath() {
202
+ const pythonPath = getRuntimePythonPath();
203
+ if (fs.existsSync(pythonPath)) {
204
+ return pythonPath;
205
+ }
206
+
207
+ const fallback = process.platform === 'win32'
208
+ ? path.join(getRuntimeBinDir(), 'python.exe')
209
+ : path.join(getRuntimeBinDir(), 'python');
210
+
211
+ return fallback;
212
+ }
213
+
214
+ function venvLooksHealthy(pythonPath) {
215
+ if (!fs.existsSync(pythonPath)) {
216
+ return false;
217
+ }
218
+
219
+ try {
220
+ const result = run(pythonPath, ['-c', 'import sys; print(sys.executable)']);
221
+ return result.status === 0;
222
+ } catch (error) {
223
+ return false;
224
+ }
225
+ }
226
+
227
+ function ensureRuntime() {
228
+ ensureRuntimeDirectories();
229
+
230
+ const bundledRuntime = getBundledPythonRuntime();
231
+ if (bundledRuntime) {
232
+ const packagedRuntime = ensurePackagedRuntime(bundledRuntime);
233
+ const pipBootstrap = run(packagedRuntime.pythonPath, ['-m', 'pip', 'install', '--upgrade', 'pip', 'setuptools', 'wheel']);
234
+ if (pipBootstrap.status !== 0) {
235
+ throw new Error(pipBootstrap.stderr || pipBootstrap.stdout || 'Failed to bootstrap packaged Python runtime');
236
+ }
237
+
238
+ return {
239
+ configPath: getConfigPath(),
240
+ pythonPath: packagedRuntime.pythonPath,
241
+ registryPath: getHarnessRegistryPath(),
242
+ runtimeDir: getRuntimeDir(),
243
+ runtimeHomeDir: packagedRuntime.runtimeHomeDir,
244
+ venvDir: '',
245
+ binDir: packagedRuntime.binDir,
246
+ packageName: packagedRuntime.packageName,
247
+ packageVersion: packagedRuntime.packageVersion,
248
+ archiveSHA256: packagedRuntime.archiveSHA256,
249
+ source: packagedRuntime.source,
250
+ };
251
+ }
252
+
253
+ const venvDir = getRuntimeVenvDir();
254
+ let pythonPath = resolveRuntimePythonPath();
255
+
256
+ if (!venvLooksHealthy(pythonPath)) {
257
+ const basePython = findSystemPython();
258
+ const createResult = run(basePython.command, [...basePython.args, '-m', 'venv', venvDir]);
259
+ if (createResult.status !== 0) {
260
+ throw new Error(createResult.stderr || createResult.stdout || 'Failed to create Python virtual environment');
261
+ }
262
+ pythonPath = resolveRuntimePythonPath();
263
+ }
264
+
265
+ const pipBootstrap = run(pythonPath, ['-m', 'pip', 'install', '--upgrade', 'pip', 'setuptools', 'wheel']);
266
+ if (pipBootstrap.status !== 0) {
267
+ throw new Error(pipBootstrap.stderr || pipBootstrap.stdout || 'Failed to bootstrap pip tooling');
268
+ }
269
+
270
+ return {
271
+ configPath: getConfigPath(),
272
+ pythonPath,
273
+ registryPath: getHarnessRegistryPath(),
274
+ runtimeDir: getRuntimeDir(),
275
+ runtimeHomeDir: venvDir,
276
+ venvDir,
277
+ binDir: getRuntimeBinDir(),
278
+ packageName: '',
279
+ packageVersion: '',
280
+ archiveSHA256: '',
281
+ source: 'system-venv',
282
+ };
283
+ }
284
+
285
+ function syncHarnessConfig(runtimeInfo, harnesses = []) {
286
+ const configPath = runtimeInfo.configPath || getConfigPath();
287
+ const config = readJSON(configPath);
288
+
289
+ config.harness_runtime = {
290
+ python_path: runtimeInfo.pythonPath,
291
+ venv_path: runtimeInfo.venvDir || '',
292
+ registry_path: runtimeInfo.registryPath,
293
+ source: runtimeInfo.source || '',
294
+ runtime_home: runtimeInfo.runtimeHomeDir || '',
295
+ package_name: runtimeInfo.packageName || '',
296
+ package_version: runtimeInfo.packageVersion || '',
297
+ archive_sha256: runtimeInfo.archiveSHA256 || '',
298
+ };
299
+
300
+ config.harnesses = {};
301
+ for (const harness of harnesses) {
302
+ config.harnesses[harness.name] = {
303
+ command: harness.commandPath || '',
304
+ module: harness.module,
305
+ package: harness.packageName,
306
+ version: harness.version,
307
+ };
308
+ }
309
+
310
+ writeJSON(configPath, config);
311
+ }
312
+
313
+ module.exports = {
314
+ ensureRuntime,
315
+ getBundledPythonRuntime,
316
+ readJSON,
317
+ run,
318
+ syncHarnessConfig,
319
+ writeJSON,
320
+ };
@@ -0,0 +1,172 @@
1
+ const DAEMON_BINARY_PACKAGES = [
2
+ {
3
+ dirName: 'ai-desk-daemon-darwin-arm64',
4
+ packageName: '@agent-webui/ai-desk-daemon-darwin-arm64',
5
+ platformKey: 'darwin-arm64',
6
+ os: ['darwin'],
7
+ cpu: ['arm64'],
8
+ binaryName: 'ai-desk-daemon',
9
+ },
10
+ {
11
+ dirName: 'ai-desk-daemon-darwin-x64',
12
+ packageName: '@agent-webui/ai-desk-daemon-darwin-x64',
13
+ platformKey: 'darwin-x64',
14
+ os: ['darwin'],
15
+ cpu: ['x64'],
16
+ binaryName: 'ai-desk-daemon',
17
+ },
18
+ {
19
+ dirName: 'ai-desk-daemon-linux-arm64',
20
+ packageName: '@agent-webui/ai-desk-daemon-linux-arm64',
21
+ platformKey: 'linux-arm64',
22
+ os: ['linux'],
23
+ cpu: ['arm64'],
24
+ binaryName: 'ai-desk-daemon',
25
+ },
26
+ {
27
+ dirName: 'ai-desk-daemon-linux-x64',
28
+ packageName: '@agent-webui/ai-desk-daemon-linux-x64',
29
+ platformKey: 'linux-x64',
30
+ os: ['linux'],
31
+ cpu: ['x64'],
32
+ binaryName: 'ai-desk-daemon',
33
+ },
34
+ {
35
+ dirName: 'ai-desk-daemon-win32-x64',
36
+ packageName: '@agent-webui/ai-desk-daemon-win32-x64',
37
+ platformKey: 'win32-x64',
38
+ os: ['win32'],
39
+ cpu: ['x64'],
40
+ binaryName: 'ai-desk-daemon.exe',
41
+ },
42
+ ];
43
+
44
+ const PYTHON_RUNTIME_RELEASE = '20260310';
45
+ const PYTHON_RUNTIME_VERSION = '3.13.12';
46
+
47
+ const PYTHON_RUNTIME_PACKAGES = [
48
+ {
49
+ dirName: 'ai-desk-python-darwin-arm64',
50
+ packageName: '@agent-webui/ai-desk-python-darwin-arm64',
51
+ platformKey: 'darwin-arm64',
52
+ os: ['darwin'],
53
+ cpu: ['arm64'],
54
+ archiveName: `cpython-${PYTHON_RUNTIME_VERSION}+${PYTHON_RUNTIME_RELEASE}-aarch64-apple-darwin-install_only.tar.gz`,
55
+ sha256: '8b49181b776a9ebc8323a645dc55126b389d62c50a0b9f072e37811a9391244d',
56
+ pythonRelativePath: 'python/bin/python3',
57
+ binDirRelativePath: 'python/bin',
58
+ },
59
+ {
60
+ dirName: 'ai-desk-python-darwin-x64',
61
+ packageName: '@agent-webui/ai-desk-python-darwin-x64',
62
+ platformKey: 'darwin-x64',
63
+ os: ['darwin'],
64
+ cpu: ['x64'],
65
+ archiveName: `cpython-${PYTHON_RUNTIME_VERSION}+${PYTHON_RUNTIME_RELEASE}-x86_64-apple-darwin-install_only.tar.gz`,
66
+ sha256: 'd778d46b49c640a54a13dc2bd356561b4d4f85466a2d21bc0ab1483a209bb05c',
67
+ pythonRelativePath: 'python/bin/python3',
68
+ binDirRelativePath: 'python/bin',
69
+ },
70
+ {
71
+ dirName: 'ai-desk-python-linux-arm64',
72
+ packageName: '@agent-webui/ai-desk-python-linux-arm64',
73
+ platformKey: 'linux-arm64',
74
+ os: ['linux'],
75
+ cpu: ['arm64'],
76
+ archiveName: `cpython-${PYTHON_RUNTIME_VERSION}+${PYTHON_RUNTIME_RELEASE}-aarch64-unknown-linux-gnu-install_only.tar.gz`,
77
+ sha256: '563bf262875fc0c6a22dbbb35ab7df3082184f5a587c16c534b8712e4e05c7c2',
78
+ pythonRelativePath: 'python/bin/python3',
79
+ binDirRelativePath: 'python/bin',
80
+ },
81
+ {
82
+ dirName: 'ai-desk-python-linux-x64',
83
+ packageName: '@agent-webui/ai-desk-python-linux-x64',
84
+ platformKey: 'linux-x64',
85
+ os: ['linux'],
86
+ cpu: ['x64'],
87
+ archiveName: `cpython-${PYTHON_RUNTIME_VERSION}+${PYTHON_RUNTIME_RELEASE}-x86_64-unknown-linux-gnu-install_only.tar.gz`,
88
+ sha256: 'a1d58266fede23e795b1b7d1dee3cc77470538fd14292a46cc96e735af030fec',
89
+ pythonRelativePath: 'python/bin/python3',
90
+ binDirRelativePath: 'python/bin',
91
+ },
92
+ {
93
+ dirName: 'ai-desk-python-win32-x64',
94
+ packageName: '@agent-webui/ai-desk-python-win32-x64',
95
+ platformKey: 'win32-x64',
96
+ os: ['win32'],
97
+ cpu: ['x64'],
98
+ archiveName: `cpython-${PYTHON_RUNTIME_VERSION}+${PYTHON_RUNTIME_RELEASE}-x86_64-pc-windows-msvc-install_only.tar.gz`,
99
+ sha256: '6204052c096536f9fa926f8312a1dcd5ac0846dc182a0c05ee0011d580546af0',
100
+ pythonRelativePath: 'python/python.exe',
101
+ binDirRelativePath: 'python',
102
+ },
103
+ ];
104
+
105
+ const DEFAULT_HARNESS_PACKAGES = ['@agent-webui/ai-desk-harness-gimp'];
106
+
107
+ function getBinaryPackageByPlatformKey(platformKey) {
108
+ return DAEMON_BINARY_PACKAGES.find((pkg) => pkg.platformKey === platformKey) || null;
109
+ }
110
+
111
+ function getPythonRuntimePackageByPlatformKey(platformKey) {
112
+ return PYTHON_RUNTIME_PACKAGES.find((pkg) => pkg.platformKey === platformKey) || null;
113
+ }
114
+
115
+ function getWorkspacePackagesRoot() {
116
+ return require('path').join(__dirname, '..', 'packages');
117
+ }
118
+
119
+ function findWorkspaceSiblingPackageDir(packageName) {
120
+ const fs = require('fs');
121
+ const path = require('path');
122
+ const packagesRoot = getWorkspacePackagesRoot();
123
+ if (!fs.existsSync(packagesRoot)) {
124
+ return null;
125
+ }
126
+
127
+ for (const child of fs.readdirSync(packagesRoot, { withFileTypes: true })) {
128
+ if (!child.isDirectory()) {
129
+ continue;
130
+ }
131
+
132
+ const packageJsonPath = path.join(packagesRoot, child.name, 'package.json');
133
+ if (!fs.existsSync(packageJsonPath)) {
134
+ continue;
135
+ }
136
+
137
+ try {
138
+ const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
139
+ if (pkg.name === packageName) {
140
+ return path.dirname(packageJsonPath);
141
+ }
142
+ } catch (error) {
143
+ // Ignore malformed workspace packages and keep searching.
144
+ }
145
+ }
146
+
147
+ return null;
148
+ }
149
+
150
+ function resolveInstalledPackageDir(packageName, searchPaths = []) {
151
+ const path = require('path');
152
+ const defaultPaths = [path.join(__dirname, '..'), process.cwd(), ...searchPaths];
153
+ try {
154
+ const packageJsonPath = require.resolve(`${packageName}/package.json`, {
155
+ paths: defaultPaths,
156
+ });
157
+ return path.dirname(packageJsonPath);
158
+ } catch (error) {
159
+ return findWorkspaceSiblingPackageDir(packageName);
160
+ }
161
+ }
162
+
163
+ module.exports = {
164
+ DAEMON_BINARY_PACKAGES,
165
+ PYTHON_RUNTIME_RELEASE,
166
+ PYTHON_RUNTIME_VERSION,
167
+ PYTHON_RUNTIME_PACKAGES,
168
+ DEFAULT_HARNESS_PACKAGES,
169
+ getBinaryPackageByPlatformKey,
170
+ getPythonRuntimePackageByPlatformKey,
171
+ resolveInstalledPackageDir,
172
+ };
package/package.json CHANGED
@@ -1,13 +1,18 @@
1
1
  {
2
2
  "name": "@agent-webui/ai-desk-daemon",
3
- "version": "1.0.25",
3
+ "version": "1.0.29-beta1",
4
4
  "description": "AI Desk Daemon - CLI tool for managing the AI Desk daemon service",
5
+ "workspaces": [
6
+ "packages/*"
7
+ ],
5
8
  "main": "lib/daemon-manager.js",
6
9
  "bin": {
7
10
  "aidesk": "bin/cli.js"
8
11
  },
9
12
  "scripts": {
10
13
  "postinstall": "node scripts/postinstall.js",
14
+ "prepare:packages": "node scripts/prepare-npm-packages.js",
15
+ "publish:npm:workspaces": "node scripts/publish-npm-packages.js",
11
16
  "test": "node bin/cli.js --help"
12
17
  },
13
18
  "keywords": [
@@ -24,11 +29,6 @@
24
29
  "files": [
25
30
  "bin/",
26
31
  "lib/",
27
- "dist/darwin-arm64/",
28
- "dist/darwin-x64/",
29
- "dist/linux-arm64/",
30
- "dist/linux-x64/",
31
- "dist/win32-x64/",
32
32
  "scripts/postinstall.js",
33
33
  "README.md"
34
34
  ],
@@ -36,6 +36,18 @@
36
36
  "commander": "^11.0.0",
37
37
  "chalk": "^4.1.2"
38
38
  },
39
+ "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"
50
+ },
39
51
  "repository": {
40
52
  "type": "git",
41
53
  "url": "git+https://github.com/agent-webui/ai-desk-daemon.git"
@@ -7,10 +7,24 @@
7
7
  */
8
8
 
9
9
  const chalk = require('chalk');
10
+ const path = require('path');
11
+ const { ensureRuntime } = require('../lib/runtime-manager');
12
+ const { installHarnessesForPackageRoot } = require('../lib/harness-manager');
10
13
 
11
14
  console.log('');
12
- console.log(chalk.green('✓ @agent-webui/ai-desk-daemon installed successfully'));
13
- console.log('');
14
- console.log('Run ' + chalk.cyan('aidesk --help') + ' to get started');
15
- console.log('');
16
15
 
16
+ try {
17
+ const runtime = ensureRuntime();
18
+ const installResult = installHarnessesForPackageRoot(path.join(__dirname, '..'));
19
+
20
+ console.log(chalk.green('✓ @agent-webui/ai-desk-daemon installed successfully'));
21
+ console.log(chalk.cyan(`Runtime: ${runtime.pythonPath}`));
22
+ console.log(chalk.cyan(`Harnesses installed: ${installResult.harnessCount}`));
23
+ console.log('');
24
+ console.log('Run ' + chalk.cyan('aidesk --help') + ' to get started');
25
+ console.log('');
26
+ } catch (error) {
27
+ console.log(chalk.yellow('! AI Desk daemon installed with runtime warnings'));
28
+ console.log(chalk.yellow(error.message));
29
+ console.log('');
30
+ }
Binary file
Binary file
Binary file
Binary file
Binary file