colana 1.0.0-beta.3 → 1.0.0-beta.30

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/colana.js CHANGED
@@ -11,7 +11,7 @@ import { execSync, spawn } from 'child_process';
11
11
  import fs from 'fs';
12
12
  import path from 'path';
13
13
  import readline from 'readline';
14
- import { fileURLToPath } from 'url';
14
+ import { fileURLToPath, pathToFileURL } from 'url';
15
15
  import { commandExistsSync, getCommandPath } from '../server/platform.js';
16
16
  import { c, icons, brandName, tagline, printBanner } from '../server/brand.js';
17
17
 
@@ -19,6 +19,9 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
19
19
  const ROOT = path.resolve(__dirname, '..');
20
20
  const DATA_DIR = path.join(ROOT, 'data');
21
21
 
22
+ /** Convert absolute path to import-safe specifier (Windows requires file:// URLs for ESM) */
23
+ const toImportPath = (p) => process.platform === 'win32' ? pathToFileURL(p).href : p;
24
+
22
25
  // ---------------------------------------------------------------------------
23
26
  // Provider definitions for detection & installation
24
27
  // ---------------------------------------------------------------------------
@@ -133,12 +136,63 @@ function runInstall(cmd) {
133
136
  const proc = spawn(parts[0], parts.slice(1), {
134
137
  stdio: 'inherit',
135
138
  env: { ...process.env },
139
+ ...(process.platform === 'win32' && { shell: true }),
136
140
  });
137
141
  proc.on('close', (code) => resolve(code === 0));
138
142
  proc.on('error', () => resolve(false));
139
143
  });
140
144
  }
141
145
 
146
+ /**
147
+ * Spawn a binary with an explicit args array (needed when arguments contain spaces,
148
+ * e.g. winget --override "..." where the value must stay as one argument).
149
+ */
150
+ function runInstallArgs(binary, args) {
151
+ return new Promise((resolve) => {
152
+ // No shell: true here — winget/xcode-select are real executables, not .cmd files.
153
+ // shell: true would cause cmd.exe to split quoted --override values into separate args.
154
+ const proc = spawn(binary, args, {
155
+ stdio: 'inherit',
156
+ env: { ...process.env },
157
+ });
158
+ proc.on('close', (code) => resolve(code === 0));
159
+ proc.on('error', () => resolve(false));
160
+ });
161
+ }
162
+
163
+ /**
164
+ * Attempt to auto-install C/C++ build tools needed for native npm modules.
165
+ * Returns true if tools were installed (or install was triggered), false otherwise.
166
+ */
167
+ async function tryInstallBuildTools() {
168
+ const platform = process.platform;
169
+
170
+ if (platform === 'win32') {
171
+ // Try winget (available on Windows 10 21H2+ / Windows 11)
172
+ if (commandExistsSync('winget')) {
173
+ print(` ${c.muted}Installing Visual Studio Build Tools via winget...${c.reset}`);
174
+ const ok = await runInstallArgs('winget', [
175
+ 'install', 'Microsoft.VisualStudio.2022.BuildTools',
176
+ '--override', '--add Microsoft.VisualStudio.Workload.VCTools --passive --norestart',
177
+ '--accept-source-agreements', '--accept-package-agreements',
178
+ ]);
179
+ return ok;
180
+ }
181
+ return false;
182
+ }
183
+
184
+ if (platform === 'darwin') {
185
+ print(` ${c.muted}Installing Xcode Command Line Tools...${c.reset}`);
186
+ // xcode-select --install opens a system dialog; returns non-zero if already installed
187
+ const ok = await runInstallArgs('xcode-select', ['--install']);
188
+ // Even if xcode-select exits non-zero (already installed), the tools may be present
189
+ return ok;
190
+ }
191
+
192
+ // Linux — requires sudo, skip auto-install
193
+ return false;
194
+ }
195
+
142
196
  // ---------------------------------------------------------------------------
143
197
  // Doctor Checks (standalone + used by wizard)
144
198
  // ---------------------------------------------------------------------------
@@ -349,7 +403,7 @@ async function firstRunWizard() {
349
403
  const serviceAnswer = await ask(' Start Colana automatically on login? [Y/n] ');
350
404
  if (serviceAnswer.toLowerCase() !== 'n') {
351
405
  try {
352
- const { installService } = await import(path.join(ROOT, 'server', 'service-manager.js'));
406
+ const { installService } = await import(toImportPath(path.join(ROOT, 'server', 'service-manager.js')));
353
407
  const result = installService();
354
408
  if (result.success) {
355
409
  print(` ${icons.pass} Auto-start enabled. ${result.message}`);
@@ -395,8 +449,30 @@ async function installProvider(provider) {
395
449
  if (success) {
396
450
  print(` ${icons.pass} ${provider.name} installed`);
397
451
  } else {
398
- print(` ${icons.fail} ${c.error}Installation failed.${c.reset} You can install manually:`);
399
- print(` ${provider.installCmd}`);
452
+ // For npm packages, try auto-installing build tools and retrying
453
+ if (provider.runtime === 'npm') {
454
+ print(` ${c.muted}Build tools may be needed. Attempting automatic install...${c.reset}`);
455
+ const toolsInstalled = await tryInstallBuildTools();
456
+
457
+ if (toolsInstalled) {
458
+ print(` ${c.muted}Retrying ${provider.name} install...${c.reset}`);
459
+ success = await runInstall(provider.installCmd);
460
+ }
461
+
462
+ if (success) {
463
+ print(` ${icons.pass} ${provider.name} installed`);
464
+ return;
465
+ }
466
+
467
+ // Build tools auto-install failed or not available — show minimal fallback
468
+ print(` ${icons.fail} ${c.error}Installation failed.${c.reset}`);
469
+ if (process.platform === 'linux') {
470
+ print(` ${c.muted}Build tools required:${c.reset} ${c.cmd}sudo apt install python3 build-essential${c.reset}`);
471
+ }
472
+ } else {
473
+ print(` ${icons.fail} ${c.error}Installation failed.${c.reset}`);
474
+ }
475
+ print(` ${c.muted}Install manually:${c.reset} ${provider.installCmd}`);
400
476
  }
401
477
  }
402
478
 
@@ -410,7 +486,7 @@ async function startServer() {
410
486
 
411
487
  // Use dynamic import to start the server
412
488
  try {
413
- const server = await import(serverPath);
489
+ const server = await import(toImportPath(serverPath));
414
490
  // The server module starts listening on import
415
491
  // If it exports a URL or port, we could use it
416
492
  return server;
@@ -513,14 +589,14 @@ ${c.muted}v${pkg.version}${c.reset} ${c.muted}—${c.reset} ${c.url}https://cola
513
589
  if (process.argv[2] === 'admin') {
514
590
  const adminPath = path.join(__dirname, 'admin.js');
515
591
  process.argv.splice(2, 1); // remove 'admin' from args
516
- await import(adminPath);
592
+ await import(toImportPath(adminPath));
517
593
  return;
518
594
  }
519
595
 
520
596
  // Handle service subcommand
521
597
  if (process.argv[2] === 'service') {
522
598
  const { installService, uninstallService, getServiceStatus, getServiceLogs } =
523
- await import(path.join(ROOT, 'server', 'service-manager.js'));
599
+ await import(toImportPath(path.join(ROOT, 'server', 'service-manager.js')));
524
600
 
525
601
  const action = process.argv[3];
526
602
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "colana",
3
- "version": "1.0.0-beta.3",
3
+ "version": "1.0.0-beta.30",
4
4
  "description": "Agent-First. Multiplied. Multi-agent command center for AI coding agents.",
5
5
  "type": "module",
6
6
  "main": "server/index.js",
@@ -16,11 +16,9 @@
16
16
  "scripts/postinstall.cjs",
17
17
  "public/*.html",
18
18
  "public/*.css",
19
- "public/app.js",
19
+ "public/*.js",
20
20
  "public/vendor/**",
21
21
  "public/manifest.json",
22
- "public/service-worker.js",
23
- "public/sw-register.js",
24
22
  "public/icons/*",
25
23
  "data/.gitkeep"
26
24
  ],