limbo-ai 1.8.0 → 1.9.0
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/cli.js +86 -24
- package/package.json +1 -1
package/cli.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Orchestrates the Docker-based Limbo runtime.
|
|
4
4
|
'use strict';
|
|
5
5
|
|
|
6
|
-
const { execSync, spawnSync } = require('child_process');
|
|
6
|
+
const { execSync, spawn, spawnSync } = require('child_process');
|
|
7
7
|
const crypto = require('crypto');
|
|
8
8
|
const fs = require('fs');
|
|
9
9
|
const os = require('os');
|
|
@@ -212,7 +212,7 @@ const TEXT = {
|
|
|
212
212
|
modelQuestion: 'Model',
|
|
213
213
|
customModel: 'Add another supported model name',
|
|
214
214
|
customModelPrompt: ' Model name: ',
|
|
215
|
-
invalidModel: 'That model is not
|
|
215
|
+
invalidModel: 'That model is not supported for this provider and access method.',
|
|
216
216
|
supportedModels: 'Supported models:',
|
|
217
217
|
openAiApiKeyPrompt: ' OpenAI API key (sk-...): ',
|
|
218
218
|
anthropicApiKeyPrompt: ' Anthropic API key (sk-ant-...): ',
|
|
@@ -252,15 +252,16 @@ const TEXT = {
|
|
|
252
252
|
logsHint: 'Check logs with: limbo logs',
|
|
253
253
|
healthy: 'Container is healthy.',
|
|
254
254
|
subscriptionSetup: 'Provider authentication',
|
|
255
|
-
openaiSubscriptionIntro: 'Limbo will
|
|
255
|
+
openaiSubscriptionIntro: 'Limbo will authenticate with your AI provider. A URL will appear — open it in your browser to complete login.',
|
|
256
256
|
anthropicSubscriptionIntro: 'Generate a Claude setup-token on any machine with `claude setup-token`, then paste it into the next step.',
|
|
257
|
-
authFlowStart: 'Starting
|
|
258
|
-
authFlowDone: '
|
|
259
|
-
authFlowFailed: '
|
|
260
|
-
authStatusFailed: '
|
|
261
|
-
configFlowStart: 'Applying
|
|
262
|
-
configFlowDone: '
|
|
263
|
-
configFlowFailed: 'Could not
|
|
257
|
+
authFlowStart: 'Starting authentication...',
|
|
258
|
+
authFlowDone: 'Authentication complete.',
|
|
259
|
+
authFlowFailed: 'Authentication did not complete successfully.',
|
|
260
|
+
authStatusFailed: 'Provider auth is still missing or invalid. Try running with --reconfigure.',
|
|
261
|
+
configFlowStart: 'Applying configuration...',
|
|
262
|
+
configFlowDone: 'Configuration applied.',
|
|
263
|
+
configFlowFailed: 'Could not apply configuration. Check your settings and try again.',
|
|
264
|
+
composing: 'Initializing...',
|
|
264
265
|
success: 'Limbo is running!',
|
|
265
266
|
gateway: 'Gateway',
|
|
266
267
|
gatewayToken: 'Gateway token',
|
|
@@ -303,7 +304,7 @@ const TEXT = {
|
|
|
303
304
|
modelQuestion: 'Modelo',
|
|
304
305
|
customModel: 'Agregar otro nombre de modelo soportado',
|
|
305
306
|
customModelPrompt: ' Nombre del modelo: ',
|
|
306
|
-
invalidModel: 'Ese modelo no esta
|
|
307
|
+
invalidModel: 'Ese modelo no esta soportado para este provider y metodo de acceso.',
|
|
307
308
|
supportedModels: 'Modelos soportados:',
|
|
308
309
|
openAiApiKeyPrompt: ' OpenAI API key (sk-...): ',
|
|
309
310
|
anthropicApiKeyPrompt: ' Anthropic API key (sk-ant-...): ',
|
|
@@ -343,15 +344,16 @@ const TEXT = {
|
|
|
343
344
|
logsHint: 'Mira los logs con: limbo logs',
|
|
344
345
|
healthy: 'El container esta healthy.',
|
|
345
346
|
subscriptionSetup: 'Autenticacion del provider',
|
|
346
|
-
openaiSubscriptionIntro: 'Limbo va a
|
|
347
|
+
openaiSubscriptionIntro: 'Limbo va a autenticarse con tu proveedor de IA. Aparecera una URL — abrisla en el navegador para completar el login.',
|
|
347
348
|
anthropicSubscriptionIntro: 'Genera un Claude setup-token en cualquier maquina con `claude setup-token` y pegalo en el siguiente paso.',
|
|
348
|
-
authFlowStart: 'Iniciando autenticacion
|
|
349
|
-
authFlowDone: 'Autenticacion
|
|
350
|
-
authFlowFailed: 'La autenticacion
|
|
351
|
-
authStatusFailed: '
|
|
352
|
-
configFlowStart: 'Aplicando configuracion
|
|
353
|
-
configFlowDone: 'Configuracion
|
|
354
|
-
configFlowFailed: 'No se pudo
|
|
349
|
+
authFlowStart: 'Iniciando autenticacion...',
|
|
350
|
+
authFlowDone: 'Autenticacion completada.',
|
|
351
|
+
authFlowFailed: 'La autenticacion no termino correctamente.',
|
|
352
|
+
authStatusFailed: 'La autenticacion del provider sigue siendo invalida o no esta configurada. Proba con --reconfigure.',
|
|
353
|
+
configFlowStart: 'Aplicando configuracion...',
|
|
354
|
+
configFlowDone: 'Configuracion aplicada.',
|
|
355
|
+
configFlowFailed: 'No se pudo aplicar la configuracion. Revisa los ajustes e intenta de nuevo.',
|
|
356
|
+
composing: 'Inicializando...',
|
|
355
357
|
success: 'Limbo esta corriendo!',
|
|
356
358
|
gateway: 'Gateway',
|
|
357
359
|
gatewayToken: 'Token del gateway',
|
|
@@ -807,7 +809,53 @@ function applyOpenClawConfig(cfg) {
|
|
|
807
809
|
ok(t(cfg.language, 'configFlowDone'));
|
|
808
810
|
}
|
|
809
811
|
|
|
810
|
-
|
|
812
|
+
// Spawn OpenClaw auth with filtered output: extract OAuth URLs, suppress branding.
|
|
813
|
+
// Uses async spawn so URLs appear in real-time (spawnSync would buffer until exit).
|
|
814
|
+
function streamFilteredAuth(dockerArgs) {
|
|
815
|
+
return new Promise((resolve) => {
|
|
816
|
+
const proc = spawn('docker', dockerArgs, {
|
|
817
|
+
cwd: LIMBO_DIR,
|
|
818
|
+
stdio: ['inherit', 'pipe', 'pipe'],
|
|
819
|
+
});
|
|
820
|
+
|
|
821
|
+
const urlRe = /https?:\/\/[^\s"'<>\]]+/g;
|
|
822
|
+
const seenUrls = new Set();
|
|
823
|
+
let buf = '';
|
|
824
|
+
|
|
825
|
+
const handleData = (data) => {
|
|
826
|
+
buf += data.toString();
|
|
827
|
+
const lines = buf.split('\n');
|
|
828
|
+
buf = lines.pop(); // hold incomplete last line
|
|
829
|
+
for (const line of lines) emitLine(line);
|
|
830
|
+
};
|
|
831
|
+
|
|
832
|
+
const emitLine = (line) => {
|
|
833
|
+
const urls = line.match(urlRe) || [];
|
|
834
|
+
if (urls.length > 0) {
|
|
835
|
+
for (const url of urls) {
|
|
836
|
+
if (!seenUrls.has(url)) {
|
|
837
|
+
seenUrls.add(url);
|
|
838
|
+
console.log(`\n ${c.cyan}${c.bold}→ ${url}${c.reset}\n`);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
// Suppress lines that only contain internal gateway/runtime branding
|
|
844
|
+
if (/openclaw/i.test(line)) return;
|
|
845
|
+
if (line.trim()) console.log(` ${line}`);
|
|
846
|
+
};
|
|
847
|
+
|
|
848
|
+
proc.stdout.on('data', handleData);
|
|
849
|
+
proc.stderr.on('data', handleData);
|
|
850
|
+
proc.on('close', (code) => {
|
|
851
|
+
if (buf.trim()) emitLine(buf);
|
|
852
|
+
resolve(code ?? 1);
|
|
853
|
+
});
|
|
854
|
+
proc.on('error', () => resolve(1));
|
|
855
|
+
});
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
async function runSubscriptionAuthFlow(cfg) {
|
|
811
859
|
header(t(cfg.language, 'subscriptionSetup'));
|
|
812
860
|
if (cfg.providerFamily === 'openai') {
|
|
813
861
|
log(t(cfg.language, 'openaiSubscriptionIntro'));
|
|
@@ -820,8 +868,17 @@ function runSubscriptionAuthFlow(cfg) {
|
|
|
820
868
|
? ['models', 'auth', 'login', '--provider', 'openai-codex']
|
|
821
869
|
: ['models', 'auth', 'paste-token', '--provider', 'anthropic'];
|
|
822
870
|
|
|
823
|
-
|
|
824
|
-
if (
|
|
871
|
+
let exitCode;
|
|
872
|
+
if (cfg.providerFamily === 'openai') {
|
|
873
|
+
// Stream output and extract OAuth URL so the user never sees "openclaw"
|
|
874
|
+
exitCode = await streamFilteredAuth(['compose', 'run', '--rm', '--entrypoint', 'openclaw', 'limbo', ...authArgs]);
|
|
875
|
+
} else {
|
|
876
|
+
// Anthropic paste-token is interactive (user pastes a token); keep stdio inherited
|
|
877
|
+
const authResult = runOpenClaw(authArgs);
|
|
878
|
+
exitCode = authResult.status;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
if (exitCode !== 0) die(t(cfg.language, 'authFlowFailed'));
|
|
825
882
|
|
|
826
883
|
const statusResult = runOpenClaw(
|
|
827
884
|
['models', 'status', '--check', '--probe-provider', cfg.provider],
|
|
@@ -912,7 +969,7 @@ async function cmdStart() {
|
|
|
912
969
|
pullOrBuildImage(cfg.language);
|
|
913
970
|
|
|
914
971
|
if (cfg.authMode === 'subscription' && (process.argv.includes('--reconfigure') || !alreadyHasEnv)) {
|
|
915
|
-
runSubscriptionAuthFlow(cfg);
|
|
972
|
+
await runSubscriptionAuthFlow(cfg);
|
|
916
973
|
}
|
|
917
974
|
|
|
918
975
|
applyOpenClawConfig({
|
|
@@ -922,7 +979,12 @@ async function cmdStart() {
|
|
|
922
979
|
});
|
|
923
980
|
|
|
924
981
|
header(t(cfg.language, 'starting'));
|
|
925
|
-
|
|
982
|
+
log(t(cfg.language, 'composing'));
|
|
983
|
+
const upResult = runDockerCompose(['up', '-d', '--remove-orphans'], { stdio: 'pipe' });
|
|
984
|
+
if (upResult.status !== 0) {
|
|
985
|
+
process.stderr.write(upResult.stderr || '');
|
|
986
|
+
die('Container failed to start. Run `limbo logs` to investigate.');
|
|
987
|
+
}
|
|
926
988
|
|
|
927
989
|
header(t(cfg.language, 'verifying'));
|
|
928
990
|
const healthy = waitForHealthy(cfg.language);
|