limbo-ai 1.9.2 → 1.9.4
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 +41 -9
- package/package.json +1 -1
package/cli.js
CHANGED
|
@@ -20,6 +20,8 @@ const COMPOSE_FILE = path.join(LIMBO_DIR, 'docker-compose.yml');
|
|
|
20
20
|
const GHCR_IMAGE = 'ghcr.io/tomasward1/limbo';
|
|
21
21
|
const DEFAULT_TAG = require('./package.json').version;
|
|
22
22
|
const PORT = 18789;
|
|
23
|
+
// OpenClaw's OAuth callback server port — must be exposed when running auth inside Docker
|
|
24
|
+
const OPENCLAW_AUTH_PORT = 1453;
|
|
23
25
|
|
|
24
26
|
// OpenClaw compatibility snapshots from official docs:
|
|
25
27
|
// - https://docs.openclaw.ai/providers/openai
|
|
@@ -811,13 +813,23 @@ function applyOpenClawConfig(cfg) {
|
|
|
811
813
|
ok(t(cfg.language, 'configFlowDone'));
|
|
812
814
|
}
|
|
813
815
|
|
|
814
|
-
// Strip ANSI escape sequences
|
|
815
|
-
|
|
816
|
+
// Strip all ANSI/VT100 escape sequences from a string.
|
|
817
|
+
// Uses standards-based ECMA-48 byte ranges:
|
|
818
|
+
// CSI: ESC [ <param bytes 0x30-0x3F>* <intermediate bytes 0x20-0x2F>* <final byte 0x40-0x7E>
|
|
819
|
+
// Two-char ESC: ESC <0x40-0x5F> (e.g. ESC M, ESC =, ESC 7/8 save/restore cursor)
|
|
820
|
+
// OSC: ESC ] <any> BEL|ST
|
|
821
|
+
// Covers private-mode sequences like \x1b[?25l (hide cursor) that the old [0-9;]* missed.
|
|
822
|
+
const stripAnsi = (str) => str
|
|
823
|
+
.replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, '') // CSI sequences (all parameter byte combos)
|
|
824
|
+
.replace(/\x1b[@-Z\\-_]/g, '') // two-char ESC sequences
|
|
825
|
+
.replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g, '') // OSC sequences
|
|
826
|
+
.replace(/\r/g, '');
|
|
816
827
|
|
|
817
828
|
// Spawn OpenClaw auth with filtered output: extract OAuth URLs, suppress branding.
|
|
818
829
|
// --tty is required so openclaw sees a TTY inside the container and runs the auth wizard.
|
|
819
830
|
// We pipe stdout/stderr to filter content while the container gets a proper PTY allocation.
|
|
820
|
-
|
|
831
|
+
// onUrl: optional callback invoked with each unique URL as it appears (e.g. to auto-open browser).
|
|
832
|
+
function streamFilteredAuth(dockerArgs, onUrl = null) {
|
|
821
833
|
return new Promise((resolve) => {
|
|
822
834
|
const proc = spawn('docker', dockerArgs, {
|
|
823
835
|
cwd: LIMBO_DIR,
|
|
@@ -839,13 +851,25 @@ function streamFilteredAuth(dockerArgs) {
|
|
|
839
851
|
const emitLine = (rawLine) => {
|
|
840
852
|
const line = stripAnsi(rawLine);
|
|
841
853
|
const urls = line.match(urlRe) || [];
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
854
|
+
if (urls.length > 0) {
|
|
855
|
+
for (const url of urls) {
|
|
856
|
+
if (!seenUrls.has(url)) {
|
|
857
|
+
seenUrls.add(url);
|
|
858
|
+
console.log(`\n ${c.cyan}${c.bold}→ ${url}${c.reset}\n`);
|
|
859
|
+
if (onUrl) onUrl(url);
|
|
860
|
+
}
|
|
847
861
|
}
|
|
862
|
+
return; // don't double-print the line containing the URL
|
|
848
863
|
}
|
|
864
|
+
// Suppress OpenClaw branding
|
|
865
|
+
if (/openclaw/i.test(line)) return;
|
|
866
|
+
// Suppress TUI chrome: lines that are only spinner/decoration/box-drawing chars.
|
|
867
|
+
// OpenClaw's TUI writes animation frames separated by \r — after our \r-split, each
|
|
868
|
+
// frame becomes a short line (often a single char). We filter these out so they don't
|
|
869
|
+
// scatter across the terminal as individual console.log lines.
|
|
870
|
+
const tuiChrome = /^[\s\u2500-\u257f\u2580-\u259f\u25a0-\u25ff\u2600-\u26ff◇◆●○◈→←↑↓⠀-\u28ff]*$/u;
|
|
871
|
+
if (tuiChrome.test(line)) return;
|
|
872
|
+
if (line.trim()) console.log(` ${line}`);
|
|
849
873
|
};
|
|
850
874
|
|
|
851
875
|
proc.stdout.on('data', handleData);
|
|
@@ -874,8 +898,16 @@ async function runSubscriptionAuthFlow(cfg) {
|
|
|
874
898
|
let exitCode;
|
|
875
899
|
if (cfg.providerFamily === 'openai') {
|
|
876
900
|
// --tty allocates a PTY inside the container so openclaw's auth wizard runs correctly.
|
|
901
|
+
// -p exposes the OAuth callback port so the browser redirect reaches the in-container server.
|
|
877
902
|
// We still pipe stdout/stderr to filter out branding and highlight the OAuth URL.
|
|
878
|
-
|
|
903
|
+
const opener = process.platform === 'darwin' ? 'open' : 'xdg-open';
|
|
904
|
+
exitCode = await streamFilteredAuth(
|
|
905
|
+
['compose', 'run', '--tty', '--rm', '-p', `${OPENCLAW_AUTH_PORT}:${OPENCLAW_AUTH_PORT}`, '--entrypoint', 'openclaw', 'limbo', ...authArgs],
|
|
906
|
+
(url) => {
|
|
907
|
+
// Auto-open the browser so the user doesn't need to copy/paste the URL
|
|
908
|
+
try { spawnSync(opener, [url], { stdio: 'ignore', timeout: 3000 }); } catch {}
|
|
909
|
+
},
|
|
910
|
+
);
|
|
879
911
|
} else {
|
|
880
912
|
// Anthropic paste-token is interactive (user pastes a token); keep stdio inherited
|
|
881
913
|
const authResult = runOpenClaw(authArgs);
|