colana 1.0.0-beta.8 → 1.0.0-beta.80
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/bin/admin.js +20 -2
- package/bin/colana.js +193 -19
- package/package.json +10 -3
- package/public/app.js +960 -54
- package/public/index.html +13 -1
- package/public/styles.css +284 -0
- package/scripts/postinstall.cjs +27 -0
- package/server/agent-control-routes.js +28 -8
- package/server/auto-installer.js +590 -21
- package/server/config.js +7 -1
- package/server/diagnostics-routes.js +2 -1
- package/server/index.js +62 -16
- package/server/license.js +4 -1
- package/server/openclaw-config.js +216 -8
- package/server/personal-agent-routes.js +133 -9
- package/server/platform.js +56 -1
- package/server/preflight.js +63 -4
- package/server/pty-manager.js +483 -42
- package/server/server-control-routes.js +41 -3
- package/server/spawn-env.js +7 -1
- package/server/updater.js +2 -7
- package/server/webhook-routes.js +2 -1
package/bin/admin.js
CHANGED
|
@@ -156,12 +156,14 @@ async function createKey(flags) {
|
|
|
156
156
|
console.error(`\n ${icons.fail} ${c.error}ERROR: --name is required.${c.reset}\n`);
|
|
157
157
|
console.error(' Usage: colana admin create-key --name "Sarah" --type staff [--email x] [--expires 2026-12-31]\n');
|
|
158
158
|
process.exit(1);
|
|
159
|
+
return;
|
|
159
160
|
}
|
|
160
161
|
|
|
161
162
|
const validTypes = ['admin', 'staff', 'beta'];
|
|
162
163
|
if (!validTypes.includes(type)) {
|
|
163
164
|
console.error(`\n ${icons.fail} ${c.error}ERROR: Invalid type "${type}". Must be one of: ${validTypes.join(', ')}${c.reset}\n`);
|
|
164
165
|
process.exit(1);
|
|
166
|
+
return;
|
|
165
167
|
}
|
|
166
168
|
|
|
167
169
|
// Prefer server API (avoids sql.js in-memory DB conflict)
|
|
@@ -174,6 +176,7 @@ async function createKey(flags) {
|
|
|
174
176
|
} catch (err) {
|
|
175
177
|
console.error(`\n ${icons.fail} ${c.error}${err.message}${c.reset} ${c.muted}(via server)${c.reset}\n`);
|
|
176
178
|
process.exit(1);
|
|
179
|
+
return;
|
|
177
180
|
}
|
|
178
181
|
} else {
|
|
179
182
|
// Fallback: direct DB access (only safe when server is NOT running)
|
|
@@ -184,9 +187,16 @@ async function createKey(flags) {
|
|
|
184
187
|
} catch (err) {
|
|
185
188
|
console.error(`\n ${icons.fail} ${c.error}${err.message}${c.reset}\n`);
|
|
186
189
|
process.exit(1);
|
|
190
|
+
return;
|
|
187
191
|
}
|
|
188
192
|
}
|
|
189
193
|
|
|
194
|
+
if (!result || !result.name) {
|
|
195
|
+
console.error(`\n ${icons.fail} ${c.error}Key generation returned an unexpected response.${c.reset}\n`);
|
|
196
|
+
process.exit(1);
|
|
197
|
+
return; // Guard for test environments where process.exit is mocked
|
|
198
|
+
}
|
|
199
|
+
|
|
190
200
|
console.log('');
|
|
191
201
|
console.log(` ${icons.pass} Key generated successfully!`);
|
|
192
202
|
console.log(` ${c.muted}${'─'.repeat(40)}${c.reset}`);
|
|
@@ -219,6 +229,7 @@ async function listKeysCmd(flags) {
|
|
|
219
229
|
} catch (err) {
|
|
220
230
|
console.error(`\n ${icons.fail} ${c.error}${err.message}${c.reset} ${c.muted}(via server)${c.reset}\n`);
|
|
221
231
|
process.exit(1);
|
|
232
|
+
return;
|
|
222
233
|
}
|
|
223
234
|
} else {
|
|
224
235
|
await ensureDb();
|
|
@@ -226,7 +237,7 @@ async function listKeysCmd(flags) {
|
|
|
226
237
|
result = listKeys(filters);
|
|
227
238
|
}
|
|
228
239
|
|
|
229
|
-
if (!result.keys.length) {
|
|
240
|
+
if (!result || !result.keys || !result.keys.length) {
|
|
230
241
|
console.log('\n No keys found.\n');
|
|
231
242
|
return;
|
|
232
243
|
}
|
|
@@ -257,6 +268,7 @@ async function revokeKeyCmd(positional) {
|
|
|
257
268
|
console.error(`\n ${icons.fail} ${c.error}ERROR: Key ID is required.${c.reset}\n`);
|
|
258
269
|
console.error(' Usage: colana admin revoke-key <id>\n');
|
|
259
270
|
process.exit(1);
|
|
271
|
+
return;
|
|
260
272
|
}
|
|
261
273
|
|
|
262
274
|
// Prefer server API
|
|
@@ -269,6 +281,7 @@ async function revokeKeyCmd(positional) {
|
|
|
269
281
|
} catch (err) {
|
|
270
282
|
console.error(`\n ${icons.fail} ${c.error}${err.message}${c.reset} ${c.muted}(via server)${c.reset}\n`);
|
|
271
283
|
process.exit(1);
|
|
284
|
+
return;
|
|
272
285
|
}
|
|
273
286
|
} else {
|
|
274
287
|
await ensureDb();
|
|
@@ -279,6 +292,7 @@ async function revokeKeyCmd(positional) {
|
|
|
279
292
|
} catch (err) {
|
|
280
293
|
console.error(`\n ${icons.fail} ${c.error}${err.message}${c.reset}\n`);
|
|
281
294
|
process.exit(1);
|
|
295
|
+
return;
|
|
282
296
|
}
|
|
283
297
|
}
|
|
284
298
|
}
|
|
@@ -375,7 +389,11 @@ async function main() {
|
|
|
375
389
|
console.error(` ${icons.fail} Unknown command: ${c.value}${command}${c.reset}`);
|
|
376
390
|
console.error(` ${c.muted}Run "colana admin help" for usage.${c.reset}`);
|
|
377
391
|
process.exit(1);
|
|
392
|
+
return;
|
|
378
393
|
}
|
|
379
394
|
}
|
|
380
395
|
|
|
381
|
-
main()
|
|
396
|
+
main().catch((err) => {
|
|
397
|
+
console.error(` ${icons.fail} ${c.error}${err.message || err}${c.reset}`);
|
|
398
|
+
process.exit(1);
|
|
399
|
+
});
|
package/bin/colana.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* Normal run: start server → open browser
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { execSync, spawn } from 'child_process';
|
|
10
|
+
import { execSync, execFileSync, spawn } from 'child_process';
|
|
11
11
|
import fs from 'fs';
|
|
12
12
|
import path from 'path';
|
|
13
13
|
import readline from 'readline';
|
|
@@ -125,6 +125,79 @@ function detectProvider(provider) {
|
|
|
125
125
|
return commandExistsSync(provider.binary);
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
+
/**
|
|
129
|
+
* Detect the system Python version. Returns { major, minor } or null.
|
|
130
|
+
*/
|
|
131
|
+
function detectPythonVersion() {
|
|
132
|
+
for (const bin of ['python3', 'python']) {
|
|
133
|
+
try {
|
|
134
|
+
const out = execFileSync(bin, ['--version'], {
|
|
135
|
+
timeout: 5000, encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'],
|
|
136
|
+
}).trim();
|
|
137
|
+
const match = out.match(/Python\s+(\d+)\.(\d+)/);
|
|
138
|
+
if (match) return { major: parseInt(match[1]), minor: parseInt(match[2]) };
|
|
139
|
+
} catch { /* try next */ }
|
|
140
|
+
}
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Return platform-specific numbered next-steps for a failed install.
|
|
146
|
+
* @param {object} provider - Provider object from CLI_PROVIDERS
|
|
147
|
+
* @param {string} [hint] - Optional hint about why it failed
|
|
148
|
+
* @returns {string[]} Steps array
|
|
149
|
+
*/
|
|
150
|
+
function getCliNextSteps(provider, hint = '') {
|
|
151
|
+
const h = hint.toLowerCase();
|
|
152
|
+
const platform = process.platform;
|
|
153
|
+
|
|
154
|
+
// Build tools failures (npm providers)
|
|
155
|
+
if (provider.runtime === 'npm' && (h.includes('gyp') || h.includes('msbuild') || h.includes('native') || h.includes('build tools'))) {
|
|
156
|
+
if (platform === 'win32') {
|
|
157
|
+
return [
|
|
158
|
+
'Open PowerShell as Administrator (right-click > "Run as administrator")',
|
|
159
|
+
`Run: ${c.cmd}winget install Microsoft.VisualStudio.2022.BuildTools --override "--add Microsoft.VisualStudio.Workload.VCTools --includeRecommended --passive --norestart" --accept-source-agreements --accept-package-agreements${c.reset}`,
|
|
160
|
+
'Wait for the install to complete (2-5 minutes)',
|
|
161
|
+
'Close and reopen your terminal',
|
|
162
|
+
`Run: ${c.cmd}${provider.installCmd}${c.reset}`,
|
|
163
|
+
];
|
|
164
|
+
}
|
|
165
|
+
if (platform === 'darwin') {
|
|
166
|
+
return [
|
|
167
|
+
`Run: ${c.cmd}xcode-select --install${c.reset}`,
|
|
168
|
+
'Complete the system dialog that appears',
|
|
169
|
+
`Run: ${c.cmd}${provider.installCmd}${c.reset}`,
|
|
170
|
+
];
|
|
171
|
+
}
|
|
172
|
+
return [
|
|
173
|
+
`Run: ${c.cmd}sudo apt update && sudo apt install -y build-essential python3${c.reset}`,
|
|
174
|
+
`Run: ${c.cmd}${provider.installCmd}${c.reset}`,
|
|
175
|
+
];
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Python version issues (aider)
|
|
179
|
+
if (provider.runtime === 'pipx' && (h.includes('python') || h.includes('setuptools'))) {
|
|
180
|
+
if (platform === 'win32') {
|
|
181
|
+
return [
|
|
182
|
+
`Run: ${c.cmd}pip install uv${c.reset}`,
|
|
183
|
+
`Run: ${c.cmd}uv tool install --python python3.12 aider-chat${c.reset}`,
|
|
184
|
+
'Close and reopen your terminal',
|
|
185
|
+
];
|
|
186
|
+
}
|
|
187
|
+
return [
|
|
188
|
+
`Run: ${c.cmd}pip install uv${c.reset} (or: ${c.cmd}brew install uv${c.reset})`,
|
|
189
|
+
`Run: ${c.cmd}uv tool install --python python3.12 aider-chat${c.reset}`,
|
|
190
|
+
'Restart your terminal',
|
|
191
|
+
];
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Generic fallback
|
|
195
|
+
return [
|
|
196
|
+
`Run: ${c.cmd}${provider.installCmd}${c.reset}`,
|
|
197
|
+
'If that fails, check the error output above for details',
|
|
198
|
+
];
|
|
199
|
+
}
|
|
200
|
+
|
|
128
201
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
129
202
|
const ask = (q) => new Promise(r => rl.question(q, r));
|
|
130
203
|
|
|
@@ -136,12 +209,63 @@ function runInstall(cmd) {
|
|
|
136
209
|
const proc = spawn(parts[0], parts.slice(1), {
|
|
137
210
|
stdio: 'inherit',
|
|
138
211
|
env: { ...process.env },
|
|
212
|
+
...(process.platform === 'win32' && { shell: true }),
|
|
213
|
+
});
|
|
214
|
+
proc.on('close', (code) => resolve(code === 0));
|
|
215
|
+
proc.on('error', () => resolve(false));
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Spawn a binary with an explicit args array (needed when arguments contain spaces,
|
|
221
|
+
* e.g. winget --override "..." where the value must stay as one argument).
|
|
222
|
+
*/
|
|
223
|
+
function runInstallArgs(binary, args) {
|
|
224
|
+
return new Promise((resolve) => {
|
|
225
|
+
// No shell: true here — winget/xcode-select are real executables, not .cmd files.
|
|
226
|
+
// shell: true would cause cmd.exe to split quoted --override values into separate args.
|
|
227
|
+
const proc = spawn(binary, args, {
|
|
228
|
+
stdio: 'inherit',
|
|
229
|
+
env: { ...process.env },
|
|
139
230
|
});
|
|
140
231
|
proc.on('close', (code) => resolve(code === 0));
|
|
141
232
|
proc.on('error', () => resolve(false));
|
|
142
233
|
});
|
|
143
234
|
}
|
|
144
235
|
|
|
236
|
+
/**
|
|
237
|
+
* Attempt to auto-install C/C++ build tools needed for native npm modules.
|
|
238
|
+
* Returns true if tools were installed (or install was triggered), false otherwise.
|
|
239
|
+
*/
|
|
240
|
+
async function tryInstallBuildTools() {
|
|
241
|
+
const platform = process.platform;
|
|
242
|
+
|
|
243
|
+
if (platform === 'win32') {
|
|
244
|
+
// Try winget (available on Windows 10 21H2+ / Windows 11)
|
|
245
|
+
if (commandExistsSync('winget')) {
|
|
246
|
+
print(` ${c.muted}Installing Visual Studio Build Tools via winget...${c.reset}`);
|
|
247
|
+
const ok = await runInstallArgs('winget', [
|
|
248
|
+
'install', 'Microsoft.VisualStudio.2022.BuildTools',
|
|
249
|
+
'--override', '--add Microsoft.VisualStudio.Workload.VCTools --passive --norestart',
|
|
250
|
+
'--accept-source-agreements', '--accept-package-agreements',
|
|
251
|
+
]);
|
|
252
|
+
return ok;
|
|
253
|
+
}
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (platform === 'darwin') {
|
|
258
|
+
print(` ${c.muted}Installing Xcode Command Line Tools...${c.reset}`);
|
|
259
|
+
// xcode-select --install opens a system dialog; returns non-zero if already installed
|
|
260
|
+
const ok = await runInstallArgs('xcode-select', ['--install']);
|
|
261
|
+
// Even if xcode-select exits non-zero (already installed), the tools may be present
|
|
262
|
+
return ok;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Linux — requires sudo, skip auto-install
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
|
|
145
269
|
// ---------------------------------------------------------------------------
|
|
146
270
|
// Doctor Checks (standalone + used by wizard)
|
|
147
271
|
// ---------------------------------------------------------------------------
|
|
@@ -387,37 +511,87 @@ async function installProvider(provider) {
|
|
|
387
511
|
}
|
|
388
512
|
}
|
|
389
513
|
|
|
514
|
+
// Aider: detect Python 3.13+ and use uv with Python 3.12 pin
|
|
515
|
+
let installCmd = provider.installCmd;
|
|
516
|
+
let fallbackCmd = provider.fallbackCmd;
|
|
517
|
+
let failureHint = '';
|
|
518
|
+
|
|
519
|
+
if (provider.binary === 'aider') {
|
|
520
|
+
const pyVer = detectPythonVersion();
|
|
521
|
+
if (pyVer && pyVer.major >= 3 && pyVer.minor >= 13) {
|
|
522
|
+
print(` ${c.muted}Python ${pyVer.major}.${pyVer.minor} detected — aider requires Python 3.10-3.12${c.reset}`);
|
|
523
|
+
print(` ${c.muted}Using uv to install with Python 3.12...${c.reset}`);
|
|
524
|
+
failureHint = 'python 3.13';
|
|
525
|
+
|
|
526
|
+
// Try installing uv first if not available
|
|
527
|
+
let hasUv = commandExistsSync('uv');
|
|
528
|
+
if (!hasUv) {
|
|
529
|
+
print(` ${c.muted}Installing uv (universal Python package manager)...${c.reset}`);
|
|
530
|
+
hasUv = await runInstall('pip install uv');
|
|
531
|
+
if (!hasUv) hasUv = await runInstall('pip3 install uv');
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
if (hasUv) {
|
|
535
|
+
installCmd = 'uv tool install --python python3.12 aider-chat';
|
|
536
|
+
fallbackCmd = null;
|
|
537
|
+
} else {
|
|
538
|
+
print(` ${icons.fail} ${c.error}Could not install uv.${c.reset}`);
|
|
539
|
+
printNextSteps(provider, 'python setuptools');
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
390
545
|
print(` Installing ${provider.name}...`);
|
|
391
|
-
let success = await runInstall(
|
|
546
|
+
let success = await runInstall(installCmd);
|
|
392
547
|
|
|
393
|
-
if (!success &&
|
|
394
|
-
print(` Trying fallback: ${
|
|
395
|
-
success = await runInstall(
|
|
548
|
+
if (!success && fallbackCmd) {
|
|
549
|
+
print(` Trying fallback: ${fallbackCmd}...`);
|
|
550
|
+
success = await runInstall(fallbackCmd);
|
|
396
551
|
}
|
|
397
552
|
|
|
398
553
|
if (success) {
|
|
399
554
|
print(` ${icons.pass} ${provider.name} installed`);
|
|
400
555
|
} else {
|
|
401
|
-
|
|
402
|
-
// Platform-specific hints for native module failures (npm packages may need C/C++ build tools)
|
|
556
|
+
// For npm packages, try auto-installing build tools and retrying
|
|
403
557
|
if (provider.runtime === 'npm') {
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
print(`
|
|
409
|
-
|
|
410
|
-
print(` ${c.muted}If you see build errors, ensure Xcode CLT is installed:${c.reset}`);
|
|
411
|
-
print(` ${c.cmd}xcode-select --install${c.reset}`);
|
|
412
|
-
} else {
|
|
413
|
-
print(` ${c.muted}If you see build errors, ensure build tools are installed:${c.reset}`);
|
|
414
|
-
print(` ${c.cmd}sudo apt install python3 build-essential${c.reset}`);
|
|
558
|
+
print(` ${c.muted}Build tools may be needed. Attempting automatic install...${c.reset}`);
|
|
559
|
+
const toolsInstalled = await tryInstallBuildTools();
|
|
560
|
+
|
|
561
|
+
if (toolsInstalled) {
|
|
562
|
+
print(` ${c.muted}Retrying ${provider.name} install...${c.reset}`);
|
|
563
|
+
success = await runInstall(installCmd);
|
|
415
564
|
}
|
|
565
|
+
|
|
566
|
+
if (success) {
|
|
567
|
+
print(` ${icons.pass} ${provider.name} installed`);
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
failureHint = failureHint || 'build tools';
|
|
416
572
|
}
|
|
417
|
-
|
|
573
|
+
|
|
574
|
+
// Enterprise-grade: show numbered next-steps
|
|
575
|
+
print(` ${icons.fail} ${c.error}Installation failed.${c.reset}`);
|
|
576
|
+
printNextSteps(provider, failureHint);
|
|
418
577
|
}
|
|
419
578
|
}
|
|
420
579
|
|
|
580
|
+
/**
|
|
581
|
+
* Print numbered next-steps for a failed install.
|
|
582
|
+
* @param {object} provider - CLI_PROVIDERS entry
|
|
583
|
+
* @param {string} [hint] - Failure hint
|
|
584
|
+
*/
|
|
585
|
+
function printNextSteps(provider, hint = '') {
|
|
586
|
+
const steps = getCliNextSteps(provider, hint);
|
|
587
|
+
print('');
|
|
588
|
+
print(` ${c.heading}Next steps to resolve:${c.reset}`);
|
|
589
|
+
steps.forEach((step, i) => {
|
|
590
|
+
print(` ${c.muted}${i + 1}.${c.reset} ${step}`);
|
|
591
|
+
});
|
|
592
|
+
print('');
|
|
593
|
+
}
|
|
594
|
+
|
|
421
595
|
// ---------------------------------------------------------------------------
|
|
422
596
|
// Start Server
|
|
423
597
|
// ---------------------------------------------------------------------------
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "colana",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.80",
|
|
4
4
|
"description": "Agent-First. Multiplied. Multi-agent command center for AI coding agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "server/index.js",
|
|
@@ -86,8 +86,15 @@
|
|
|
86
86
|
],
|
|
87
87
|
"license": "SEE LICENSE IN LICENSE",
|
|
88
88
|
"private": false,
|
|
89
|
-
"os": [
|
|
90
|
-
|
|
89
|
+
"os": [
|
|
90
|
+
"linux",
|
|
91
|
+
"darwin",
|
|
92
|
+
"win32"
|
|
93
|
+
],
|
|
94
|
+
"cpu": [
|
|
95
|
+
"x64",
|
|
96
|
+
"arm64"
|
|
97
|
+
],
|
|
91
98
|
"devDependencies": {
|
|
92
99
|
"@playwright/test": "^1.58.2",
|
|
93
100
|
"@vitest/coverage-v8": "^4.0.18",
|