colana 1.0.0-beta.26 → 1.0.0-beta.28

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "colana",
3
- "version": "1.0.0-beta.26",
3
+ "version": "1.0.0-beta.28",
4
4
  "description": "Agent-First. Multiplied. Multi-agent command center for AI coding agents.",
5
5
  "type": "module",
6
6
  "main": "server/index.js",
package/public/app.js CHANGED
@@ -6960,6 +6960,102 @@ async function loadOpenClawModelConfig() {
6960
6960
  currentRow.appendChild(value);
6961
6961
  container.appendChild(currentRow);
6962
6962
 
6963
+ // If no models available (fresh install, no API keys), show quick-setup for entering a key
6964
+ const hasModels = Array.isArray(data.allModels) && data.allModels.length > 0;
6965
+ if (!hasModels) {
6966
+ const providerEnvMap = data.providerEnvMap || {};
6967
+ const quickSetup = document.createElement('div');
6968
+ quickSetup.className = 'openclaw-quick-setup';
6969
+ quickSetup.style.cssText = 'margin-top:12px;padding:14px;background:var(--bg-secondary,#1a1a2e);border:1px solid var(--border-color,#2a2a4a);border-radius:8px;';
6970
+
6971
+ const heading = document.createElement('div');
6972
+ heading.style.cssText = 'font-size:0.9em;font-weight:600;margin-bottom:10px;color:var(--text-primary,#e0e0e0);';
6973
+ heading.textContent = 'Quick Setup — Enter an API key to get started';
6974
+ quickSetup.appendChild(heading);
6975
+
6976
+ const desc = document.createElement('div');
6977
+ desc.style.cssText = 'font-size:0.82em;color:var(--text-muted,#888);margin-bottom:12px;';
6978
+ desc.textContent = 'Paste an API key for any provider. Models will appear automatically once authenticated.';
6979
+ quickSetup.appendChild(desc);
6980
+
6981
+ // Provider buttons
6982
+ const commonProviders = [
6983
+ { id: 'anthropic', label: 'Anthropic', placeholder: 'sk-ant-...' },
6984
+ { id: 'openai', label: 'OpenAI', placeholder: 'sk-...' },
6985
+ { id: 'google', label: 'Google', placeholder: 'AIza...' },
6986
+ { id: 'groq', label: 'Groq', placeholder: 'gsk_...' },
6987
+ { id: 'xai', label: 'xAI', placeholder: 'xai-...' },
6988
+ { id: 'mistral', label: 'Mistral', placeholder: 'Paste key...' },
6989
+ ];
6990
+
6991
+ const providerRow = document.createElement('div');
6992
+ providerRow.style.cssText = 'display:flex;flex-wrap:wrap;gap:6px;margin-bottom:10px;';
6993
+
6994
+ let selectedProvider = commonProviders[0];
6995
+
6996
+ for (const p of commonProviders) {
6997
+ const chip = document.createElement('button');
6998
+ chip.type = 'button';
6999
+ chip.className = 'btn btn-sm' + (p === selectedProvider ? ' btn-primary' : '');
7000
+ chip.textContent = p.label;
7001
+ chip.style.cssText = 'font-size:0.8em;padding:4px 10px;';
7002
+ chip.onclick = () => {
7003
+ selectedProvider = p;
7004
+ providerRow.querySelectorAll('button').forEach(b => b.classList.remove('btn-primary'));
7005
+ chip.classList.add('btn-primary');
7006
+ keyInput.placeholder = p.placeholder;
7007
+ envLabel.textContent = providerEnvMap[p.id] || `${p.label} API Key`;
7008
+ };
7009
+ providerRow.appendChild(chip);
7010
+ }
7011
+ quickSetup.appendChild(providerRow);
7012
+
7013
+ const envLabel = document.createElement('div');
7014
+ envLabel.style.cssText = 'font-size:0.78em;color:var(--text-muted,#888);margin-bottom:4px;';
7015
+ envLabel.textContent = providerEnvMap[selectedProvider.id] || 'API Key';
7016
+ quickSetup.appendChild(envLabel);
7017
+
7018
+ const inputRow = document.createElement('div');
7019
+ inputRow.style.cssText = 'display:flex;gap:8px;';
7020
+
7021
+ const keyInput = document.createElement('input');
7022
+ keyInput.type = 'password';
7023
+ keyInput.placeholder = selectedProvider.placeholder;
7024
+ keyInput.style.cssText = 'flex:1;padding:7px 10px;border-radius:6px;border:1px solid var(--border-color,#2a2a4a);background:var(--bg-primary,#0d0d1a);color:var(--text-primary,#e0e0e0);font-size:0.85em;';
7025
+
7026
+ const saveBtn = document.createElement('button');
7027
+ saveBtn.type = 'button';
7028
+ saveBtn.className = 'btn btn-sm btn-primary';
7029
+ saveBtn.textContent = 'Save Key';
7030
+ saveBtn.onclick = async () => {
7031
+ const keyValue = keyInput.value.trim();
7032
+ if (!keyValue) { showToast('Enter an API key', 'error'); return; }
7033
+ saveBtn.disabled = true;
7034
+ saveBtn.textContent = 'Saving...';
7035
+ try {
7036
+ await api('/openclaw/models/auth', {
7037
+ method: 'POST',
7038
+ body: JSON.stringify({ provider: selectedProvider.id, apiKey: keyValue }),
7039
+ });
7040
+ showToast(`API key saved for ${selectedProvider.label}. Reloading models...`, 'success');
7041
+ // Reload model config to show newly available models
7042
+ setTimeout(() => loadOpenClawModelConfig(), 1500);
7043
+ } catch (err) {
7044
+ showToast('Failed to save: ' + err.message, 'error');
7045
+ } finally {
7046
+ saveBtn.disabled = false;
7047
+ saveBtn.textContent = 'Save Key';
7048
+ }
7049
+ };
7050
+
7051
+ inputRow.appendChild(keyInput);
7052
+ inputRow.appendChild(saveBtn);
7053
+ quickSetup.appendChild(inputRow);
7054
+
7055
+ container.appendChild(quickSetup);
7056
+ return; // Don't render the empty model selector
7057
+ }
7058
+
6963
7059
  // Build grouped <select> from allModels
6964
7060
  const inputGroup = document.createElement('div');
6965
7061
  inputGroup.className = 'openclaw-model-input-group';
@@ -193,7 +193,8 @@ export function createDiagnosticsRoutes(app, { clients, ptyManager, ptyAvailable
193
193
 
194
194
  // OpenClaw security audit (async, graceful fallback)
195
195
  try {
196
- const { stdout } = await execFileAsync('openclaw', ['security', 'audit', '--json'], { timeout: 20000 });
196
+ const execOpts = { timeout: 20000, ...(process.platform === 'win32' && { shell: true }) };
197
+ const { stdout } = await execFileAsync('openclaw', ['security', 'audit', '--json'], execOpts);
197
198
  const audit = JSON.parse(stdout);
198
199
  const critical = audit.summary?.critical || 0;
199
200
  const warn = audit.summary?.warn || 0;
@@ -291,7 +291,12 @@ export function registerPersonalAgentRoutes(app, { sensitiveLimiter }) {
291
291
  */
292
292
  function runOpenClawCli(args, timeoutMs = 8000) {
293
293
  return new Promise((resolve, reject) => {
294
- const child = execFile('openclaw', args, { timeout: timeoutMs }, (err, stdout, stderr) => {
294
+ // Windows: openclaw is a .cmd shim execFile needs shell:true to resolve it
295
+ const opts = {
296
+ timeout: timeoutMs,
297
+ ...(process.platform === 'win32' && { shell: true }),
298
+ };
299
+ const child = execFile('openclaw', args, opts, (err, stdout, stderr) => {
295
300
  if (err) return reject(err);
296
301
  resolve({ stdout: stdout.trim(), stderr: stderr.trim() });
297
302
  });