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
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
|
|
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
|
-
|
|
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
|
});
|