loom-agent 1.2.7 → 1.2.9
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/bin/loom-tui.js +4 -1
- package/bunfig.toml +1 -1
- package/package.json +1 -1
- package/src/core/cli.js +4 -1
- package/src/tui/App.tsx +1 -1
- package/src/tui-open.tsx +4 -4
- package/src/tui-preload.js +51 -0
- package/src/tui-bootstrap.js +0 -72
package/bin/loom-tui.js
CHANGED
|
@@ -26,7 +26,7 @@ function findBun() {
|
|
|
26
26
|
return null;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
const entry = path.join(__dirname, '..', 'src', 'tui-
|
|
29
|
+
const entry = path.join(__dirname, '..', 'src', 'tui-open.tsx');
|
|
30
30
|
// Start bun from the package root so it discovers our bunfig.toml/tsconfig.json
|
|
31
31
|
// (Solid JSX preloader); the user's real cwd rides along in LOOM_START_CWD and
|
|
32
32
|
// is restored by the bootstrap shim before app code runs.
|
|
@@ -36,6 +36,9 @@ process.env.LOOM_BIN_NAME = "loom";
|
|
|
36
36
|
const bun = findBun();
|
|
37
37
|
|
|
38
38
|
if (bun) {
|
|
39
|
+
// Pin Solid to its CLIENT build: from this spawn context bun can resolve
|
|
40
|
+
// solid-js under the "node" export condition (= SSR build, renders one
|
|
41
|
+
// static frame then ignores all signal updates -> frozen splash).
|
|
39
42
|
const result = spawnSync(bun, ['run', entry, ...process.argv.slice(2)], {
|
|
40
43
|
stdio: 'inherit',
|
|
41
44
|
cwd: pkgRoot,
|
package/bunfig.toml
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
preload = ["@opentui/solid/preload"]
|
|
1
|
+
preload = ["@opentui/solid/preload", "./src/tui-preload.js"]
|
package/package.json
CHANGED
package/src/core/cli.js
CHANGED
|
@@ -557,7 +557,7 @@ if (args.includes('--help') || args.includes('-h')) {
|
|
|
557
557
|
if (canRaw) {
|
|
558
558
|
// Launch new OpenTUI TUI via bun
|
|
559
559
|
const bunPath = findBun();
|
|
560
|
-
const tuiEntry = path.join(__dirname, '..', 'tui-
|
|
560
|
+
const tuiEntry = path.join(__dirname, '..', 'tui-open.tsx');
|
|
561
561
|
if (bunPath && fs.existsSync(tuiEntry)) {
|
|
562
562
|
const { spawnSync } = require('child_process');
|
|
563
563
|
// Start bun from the package root so it discovers bunfig.toml /
|
|
@@ -566,6 +566,9 @@ if (args.includes('--help') || args.includes('-h')) {
|
|
|
566
566
|
const pkgRoot = path.join(__dirname, '..', '..');
|
|
567
567
|
process.env.LOOM_START_CWD = process.cwd();
|
|
568
568
|
process.env.LOOM_BIN_NAME = "loom";
|
|
569
|
+
// --conditions=browser pins Solid to its client build (see
|
|
570
|
+
// bin/loom-tui.js) — without it the SSR build loads and the TUI
|
|
571
|
+
// renders one static frame then never updates.
|
|
569
572
|
const tuiArgs = [tuiEntry];
|
|
570
573
|
if (sessionId) tuiArgs.push('-s', sessionId);
|
|
571
574
|
if (autoMode) tuiArgs.push('--auto');
|
package/src/tui/App.tsx
CHANGED
|
@@ -1506,7 +1506,7 @@ export function App(props: { initialPrompt?: string; resumeSession?: string; aut
|
|
|
1506
1506
|
_skillDoneOff = on("turn:end", function(d: any) {
|
|
1507
1507
|
if (d?.skills?.length) showToast("skill handled: " + d.skills.join(", "), "ok");
|
|
1508
1508
|
});
|
|
1509
|
-
(globalThis as any).__loomTrace("onMount-done", new Error("mount complete"));
|
|
1509
|
+
(globalThis as any).__loomTrace?.("onMount-done", new Error("mount complete"));
|
|
1510
1510
|
});
|
|
1511
1511
|
onCleanup(function() { persistUi(); if (_skillToastOff) _skillToastOff(); if (_skillDoneOff) _skillDoneOff(); });
|
|
1512
1512
|
|
package/src/tui-open.tsx
CHANGED
|
@@ -46,11 +46,11 @@ const sessionId = sIdx !== -1 ? args[sIdx + 1] : null;
|
|
|
46
46
|
const autoMode = args.includes("--auto") || args.includes("-a");
|
|
47
47
|
const initialPrompt = args.filter((a, i) => !a.startsWith("-") && (sIdx === -1 || i !== sIdx + 1)).join(" ");
|
|
48
48
|
|
|
49
|
-
globalThis.__loomTrace("mcp-start", new Error("defaultMcpInstall beginning"));
|
|
49
|
+
globalThis.__loomTrace?.("mcp-start", new Error("defaultMcpInstall beginning"));
|
|
50
50
|
try {
|
|
51
51
|
defaultMcpInstall();
|
|
52
52
|
} catch (e) {
|
|
53
|
-
globalThis.__loomTrace("mcp-install-failed", e);
|
|
53
|
+
globalThis.__loomTrace?.("mcp-install-failed", e);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
if (printMode) {
|
|
@@ -64,11 +64,11 @@ if (printMode) {
|
|
|
64
64
|
process.exit(resp.type === "error" ? 1 : 0);
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
globalThis.__loomTrace("render-start", new Error("pre-render"));
|
|
67
|
+
globalThis.__loomTrace?.("render-start", new Error("pre-render"));
|
|
68
68
|
render(() => <App initialPrompt={initialPrompt} resumeSession={sessionId} autoMode={autoMode} />, {
|
|
69
69
|
targetFps: 60,
|
|
70
70
|
useMouse: true,
|
|
71
71
|
autoFocus: true,
|
|
72
72
|
exitOnCtrlC: false,
|
|
73
73
|
});
|
|
74
|
-
globalThis.__loomTrace("render-returned", new Error("post-render"));
|
|
74
|
+
globalThis.__loomTrace?.("render-returned", new Error("post-render"));
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// Loaded via bunfig.toml preload BEFORE any app code, so this replaces the
|
|
2
|
+
// old CJS bootstrap hop entirely: the launch chain stays a plain direct
|
|
3
|
+
// "bun src/tui-open.tsx" (identical shape to repo runs) while we still get
|
|
4
|
+
// to restore the user's project directory early and keep the crash
|
|
5
|
+
// black-box watching.
|
|
6
|
+
const fs = require("fs");
|
|
7
|
+
const os = require("os");
|
|
8
|
+
const path = require("path");
|
|
9
|
+
|
|
10
|
+
if (process.platform === "win32") {
|
|
11
|
+
// UTF-8 console codepage: legacy CPs render the box-drawing logo as "?"
|
|
12
|
+
// garbage and can mangle VT input sequences.
|
|
13
|
+
try {
|
|
14
|
+
const { dlopen } = require("bun:ffi");
|
|
15
|
+
const k32 = dlopen("kernel32.dll", {
|
|
16
|
+
SetConsoleOutputCP: { args: ["uint"], returns: "int" },
|
|
17
|
+
SetConsoleCP: { args: ["uint"], returns: "int" },
|
|
18
|
+
});
|
|
19
|
+
k32.symbols.SetConsoleOutputCP(65001);
|
|
20
|
+
k32.symbols.SetConsoleCP(65001);
|
|
21
|
+
} catch {}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (process.env.LOOM_START_CWD) {
|
|
25
|
+
try { process.chdir(process.env.LOOM_START_CWD); } catch {}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const crashPath = () => path.join(os.homedir(), ".loom", "tui-crash.log");
|
|
29
|
+
function record(kind, err) {
|
|
30
|
+
try {
|
|
31
|
+
fs.mkdirSync(path.dirname(crashPath()), { recursive: true });
|
|
32
|
+
fs.appendFileSync(
|
|
33
|
+
crashPath(),
|
|
34
|
+
`[${new Date().toISOString()}] ${kind}: ${(err && (err.stack || err.message)) || String(err)}\n`
|
|
35
|
+
);
|
|
36
|
+
} catch {}
|
|
37
|
+
}
|
|
38
|
+
globalThis.__loomTrace = record;
|
|
39
|
+
process.on("uncaughtException", (e) => record("uncaughtException", e));
|
|
40
|
+
process.on("unhandledRejection", (r) => record("unhandledRejection", r));
|
|
41
|
+
|
|
42
|
+
const __hbStart = Date.now();
|
|
43
|
+
let __hbTick = 0;
|
|
44
|
+
let __hbScheduled = __hbStart;
|
|
45
|
+
const __hbTimer = setInterval(() => {
|
|
46
|
+
const now = Date.now();
|
|
47
|
+
record("heartbeat", new Error(`tick=${++__hbTick} lag=${now - __hbScheduled}ms uptime=${now - __hbStart}ms`));
|
|
48
|
+
__hbScheduled = now + 3000;
|
|
49
|
+
}, 3000);
|
|
50
|
+
try { __hbTimer.unref?.(); } catch {}
|
|
51
|
+
process.on("exit", () => { try { record("exit", new Error(`uptime=${Date.now() - __hbStart}ms ticks=${__hbTick}`)); } catch {} });
|
package/src/tui-bootstrap.js
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
// Bootstrap shim for globally-installed launches.
|
|
2
|
-
//
|
|
3
|
-
// Bun only discovers bunfig.toml / tsconfig.json by walking UP from its
|
|
4
|
-
// working directory. When installed via npm the package lives outside the
|
|
5
|
-
// user's project tree, so launchers start bun from the PACKAGE root (where
|
|
6
|
-
// those files ship) and hand the real project directory through
|
|
7
|
-
// LOOM_START_CWD. This file must stay import-free so the chdir happens
|
|
8
|
-
// before any Loom module (which may read cwd at import time) executes.
|
|
9
|
-
const fs = require("fs");
|
|
10
|
-
const os = require("os");
|
|
11
|
-
const path = require("path");
|
|
12
|
-
|
|
13
|
-
if (process.env.LOOM_START_CWD) {
|
|
14
|
-
try { process.chdir(process.env.LOOM_START_CWD); } catch {}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// Windows consoles default to a legacy codepage where the box-drawing /
|
|
18
|
-
// block glyphs used across the TUI render as "?" garbage, and VT input
|
|
19
|
-
// sequences (arrows, ctrl combos, bracketed paste) may never arrive. Switch
|
|
20
|
-
// both console CPs to UTF-8 before anything renders or reads stdin.
|
|
21
|
-
if (process.platform === "win32") {
|
|
22
|
-
try {
|
|
23
|
-
const { dlopen } = require("bun:ffi");
|
|
24
|
-
const k32 = dlopen("kernel32.dll", {
|
|
25
|
-
SetConsoleOutputCP: { args: ["uint"], returns: "int" },
|
|
26
|
-
SetConsoleCP: { args: ["uint"], returns: "int" },
|
|
27
|
-
});
|
|
28
|
-
k32.symbols.SetConsoleOutputCP(65001);
|
|
29
|
-
k32.symbols.SetConsoleCP(65001);
|
|
30
|
-
} catch {}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Crash black-box: OpenTUI swallows post-render errors, leaving users stuck
|
|
34
|
-
// on the splash with no clue why. Persist every uncaught failure to disk so
|
|
35
|
-
// a frozen TUI can be diagnosed after the fact.
|
|
36
|
-
const crashPath = () => path.join(os.homedir(), ".loom", "tui-crash.log");
|
|
37
|
-
function record(kind, err) {
|
|
38
|
-
try {
|
|
39
|
-
fs.mkdirSync(path.dirname(crashPath()), { recursive: true });
|
|
40
|
-
fs.appendFileSync(
|
|
41
|
-
crashPath(),
|
|
42
|
-
`[${new Date().toISOString()}] ${kind}: ${(err && (err.stack || err.message)) || String(err)}\n`
|
|
43
|
-
);
|
|
44
|
-
} catch {}
|
|
45
|
-
}
|
|
46
|
-
process.on("uncaughtException", (e) => { record("uncaughtException", e); });
|
|
47
|
-
process.on("unhandledRejection", (r) => { record("unhandledRejection", r); });
|
|
48
|
-
globalThis.__loomTrace = record;
|
|
49
|
-
|
|
50
|
-
// Watchdog: append a heartbeat every 3s carrying measured event-loop lag.
|
|
51
|
-
// A synchronous hang shows up as a timestamp gap exactly covering the
|
|
52
|
-
// blocking phase; markers around it name the guilty step.
|
|
53
|
-
const __hbStart = Date.now();
|
|
54
|
-
let __hbTick = 0;
|
|
55
|
-
let __hbScheduled = __hbStart;
|
|
56
|
-
const __hbTimer = setInterval(() => {
|
|
57
|
-
const now = Date.now();
|
|
58
|
-
const lag = now - __hbScheduled;
|
|
59
|
-
record("heartbeat", new Error(`tick=${++__hbTick} lag=${lag}ms uptime=${now - __hbStart}ms`));
|
|
60
|
-
__hbScheduled = now + 3000;
|
|
61
|
-
}, 3000);
|
|
62
|
-
try { __hbTimer.unref?.(); } catch {}
|
|
63
|
-
process.on("exit", () => { try { record("exit", new Error(`uptime=${Date.now() - __hbStart}ms ticks=${__hbTick}`)); } catch {} });
|
|
64
|
-
|
|
65
|
-
record("boot", new Error(
|
|
66
|
-
`argv=${JSON.stringify(process.argv.slice(1))} cwd=${process.cwd()} ` +
|
|
67
|
-
`startCwd=${process.env.LOOM_START_CWD || "-"} bun=${process.version} ` +
|
|
68
|
-
`node=${process.versions.node || "-"} platform=${process.platform}`
|
|
69
|
-
));
|
|
70
|
-
if (process.env.LOOM_TUI_TRACE) globalThis.__loomTrace = record;
|
|
71
|
-
|
|
72
|
-
await import("./tui-open.tsx");
|