limbo-ai 1.8.0 → 1.9.1

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.
Files changed (2) hide show
  1. package/cli.js +92 -24
  2. 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 in the current OpenClaw docs allowlist for this provider/auth path.',
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 open OpenClaw auth inside the container so you can complete Codex login.',
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 provider auth flow...',
258
- authFlowDone: 'Provider auth completed.',
259
- authFlowFailed: 'Provider auth did not complete successfully.',
260
- authStatusFailed: 'OpenClaw still reports missing or invalid auth for the selected provider.',
261
- configFlowStart: 'Applying OpenClaw config...',
262
- configFlowDone: 'OpenClaw config updated.',
263
- configFlowFailed: 'Could not update the OpenClaw config for Limbo.',
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 en la allowlist actual de OpenClaw para este provider y metodo.',
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 abrir la autenticacion de OpenClaw dentro del container para que completes el login de Codex.',
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 del provider...',
349
- authFlowDone: 'Autenticacion del provider completada.',
350
- authFlowFailed: 'La autenticacion del provider no termino correctamente.',
351
- authStatusFailed: 'OpenClaw sigue reportando auth faltante o invalida para el provider elegido.',
352
- configFlowStart: 'Aplicando configuracion de OpenClaw...',
353
- configFlowDone: 'Configuracion de OpenClaw actualizada.',
354
- configFlowFailed: 'No se pudo actualizar la configuracion de OpenClaw para Limbo.',
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,58 @@ function applyOpenClawConfig(cfg) {
807
809
  ok(t(cfg.language, 'configFlowDone'));
808
810
  }
809
811
 
810
- function runSubscriptionAuthFlow(cfg) {
812
+ // Strip ANSI escape sequences so URL/text matching works on TTY output.
813
+ const stripAnsi = (str) => str.replace(/\x1b\[[0-9;]*[A-Za-z]/g, '').replace(/\r/g, '');
814
+
815
+ // Spawn OpenClaw auth with filtered output: extract OAuth URLs, suppress branding.
816
+ // --tty is required so openclaw sees a TTY inside the container and runs the auth wizard.
817
+ // We pipe stdout/stderr to filter content while the container gets a proper PTY allocation.
818
+ function streamFilteredAuth(dockerArgs) {
819
+ return new Promise((resolve) => {
820
+ const proc = spawn('docker', dockerArgs, {
821
+ cwd: LIMBO_DIR,
822
+ stdio: ['inherit', 'pipe', 'pipe'],
823
+ });
824
+
825
+ const urlRe = /https?:\/\/[^\s"'<>\]]+/g;
826
+ const seenUrls = new Set();
827
+ let buf = '';
828
+
829
+ const handleData = (data) => {
830
+ buf += data.toString();
831
+ const lines = buf.split('\n');
832
+ buf = lines.pop(); // hold incomplete last line
833
+ for (const line of lines) emitLine(line);
834
+ };
835
+
836
+ const emitLine = (rawLine) => {
837
+ const line = stripAnsi(rawLine);
838
+ const urls = line.match(urlRe) || [];
839
+ if (urls.length > 0) {
840
+ for (const url of urls) {
841
+ if (!seenUrls.has(url)) {
842
+ seenUrls.add(url);
843
+ console.log(`\n ${c.cyan}${c.bold}→ ${url}${c.reset}\n`);
844
+ }
845
+ }
846
+ return;
847
+ }
848
+ // Suppress lines that only contain internal gateway/runtime branding
849
+ if (/openclaw/i.test(line)) return;
850
+ if (line.trim()) console.log(` ${line}`);
851
+ };
852
+
853
+ proc.stdout.on('data', handleData);
854
+ proc.stderr.on('data', handleData);
855
+ proc.on('close', (code) => {
856
+ if (buf.trim()) emitLine(buf);
857
+ resolve(code ?? 1);
858
+ });
859
+ proc.on('error', () => resolve(1));
860
+ });
861
+ }
862
+
863
+ async function runSubscriptionAuthFlow(cfg) {
811
864
  header(t(cfg.language, 'subscriptionSetup'));
812
865
  if (cfg.providerFamily === 'openai') {
813
866
  log(t(cfg.language, 'openaiSubscriptionIntro'));
@@ -820,8 +873,18 @@ function runSubscriptionAuthFlow(cfg) {
820
873
  ? ['models', 'auth', 'login', '--provider', 'openai-codex']
821
874
  : ['models', 'auth', 'paste-token', '--provider', 'anthropic'];
822
875
 
823
- const authResult = runOpenClaw(authArgs);
824
- if (authResult.status !== 0) die(t(cfg.language, 'authFlowFailed'));
876
+ let exitCode;
877
+ if (cfg.providerFamily === 'openai') {
878
+ // --tty allocates a PTY inside the container so openclaw's auth wizard runs correctly.
879
+ // We still pipe stdout/stderr to filter out branding and highlight the OAuth URL.
880
+ exitCode = await streamFilteredAuth(['compose', 'run', '--tty', '--rm', '--entrypoint', 'openclaw', 'limbo', ...authArgs]);
881
+ } else {
882
+ // Anthropic paste-token is interactive (user pastes a token); keep stdio inherited
883
+ const authResult = runOpenClaw(authArgs);
884
+ exitCode = authResult.status;
885
+ }
886
+
887
+ if (exitCode !== 0) die(t(cfg.language, 'authFlowFailed'));
825
888
 
826
889
  const statusResult = runOpenClaw(
827
890
  ['models', 'status', '--check', '--probe-provider', cfg.provider],
@@ -912,7 +975,7 @@ async function cmdStart() {
912
975
  pullOrBuildImage(cfg.language);
913
976
 
914
977
  if (cfg.authMode === 'subscription' && (process.argv.includes('--reconfigure') || !alreadyHasEnv)) {
915
- runSubscriptionAuthFlow(cfg);
978
+ await runSubscriptionAuthFlow(cfg);
916
979
  }
917
980
 
918
981
  applyOpenClawConfig({
@@ -922,7 +985,12 @@ async function cmdStart() {
922
985
  });
923
986
 
924
987
  header(t(cfg.language, 'starting'));
925
- run('docker compose up -d --remove-orphans');
988
+ log(t(cfg.language, 'composing'));
989
+ const upResult = runDockerCompose(['up', '-d', '--remove-orphans'], { stdio: 'pipe' });
990
+ if (upResult.status !== 0) {
991
+ process.stderr.write(upResult.stderr || '');
992
+ die('Container failed to start. Run `limbo logs` to investigate.');
993
+ }
926
994
 
927
995
  header(t(cfg.language, 'verifying'));
928
996
  const healthy = waitForHealthy(cfg.language);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "limbo-ai",
3
- "version": "1.8.0",
3
+ "version": "1.9.1",
4
4
  "description": "Your personal AI memory agent — install and manage Limbo via npx",
5
5
  "type": "commonjs",
6
6
  "bin": {