@loompad/cli 0.1.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/ARCHITECTURE.md +211 -0
- package/CHANGELOG.md +470 -0
- package/LICENSE +21 -0
- package/README.md +630 -0
- package/dist/adapters/base.d.ts +71 -0
- package/dist/adapters/base.js +187 -0
- package/dist/adapters/base.js.map +1 -0
- package/dist/adapters/bridges/antigravity.d.ts +53 -0
- package/dist/adapters/bridges/antigravity.js +106 -0
- package/dist/adapters/bridges/antigravity.js.map +1 -0
- package/dist/adapters/bridges/cdp.d.ts +82 -0
- package/dist/adapters/bridges/cdp.js +222 -0
- package/dist/adapters/bridges/cdp.js.map +1 -0
- package/dist/adapters/bridges/gui-chat.d.ts +113 -0
- package/dist/adapters/bridges/gui-chat.js +498 -0
- package/dist/adapters/bridges/gui-chat.js.map +1 -0
- package/dist/adapters/bridges/kiro.d.ts +19 -0
- package/dist/adapters/bridges/kiro.js +22 -0
- package/dist/adapters/bridges/kiro.js.map +1 -0
- package/dist/adapters/bridges/profiles.d.ts +82 -0
- package/dist/adapters/bridges/profiles.js +125 -0
- package/dist/adapters/bridges/profiles.js.map +1 -0
- package/dist/adapters/claude-code.d.ts +26 -0
- package/dist/adapters/claude-code.js +209 -0
- package/dist/adapters/claude-code.js.map +1 -0
- package/dist/adapters/codex.d.ts +51 -0
- package/dist/adapters/codex.js +291 -0
- package/dist/adapters/codex.js.map +1 -0
- package/dist/adapters/echo.d.ts +20 -0
- package/dist/adapters/echo.js +79 -0
- package/dist/adapters/echo.js.map +1 -0
- package/dist/adapters/grok.d.ts +61 -0
- package/dist/adapters/grok.js +217 -0
- package/dist/adapters/grok.js.map +1 -0
- package/dist/adapters/index.d.ts +9 -0
- package/dist/adapters/index.js +33 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/opencode.d.ts +64 -0
- package/dist/adapters/opencode.js +426 -0
- package/dist/adapters/opencode.js.map +1 -0
- package/dist/cli/common.d.ts +11 -0
- package/dist/cli/common.js +40 -0
- package/dist/cli/common.js.map +1 -0
- package/dist/cli/doctor.d.ts +12 -0
- package/dist/cli/doctor.js +181 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/index.d.ts +8 -0
- package/dist/cli/index.js +641 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/tui-model.d.ts +73 -0
- package/dist/cli/tui-model.js +312 -0
- package/dist/cli/tui-model.js.map +1 -0
- package/dist/cli/tui.d.ts +15 -0
- package/dist/cli/tui.js +451 -0
- package/dist/cli/tui.js.map +1 -0
- package/dist/cli/ui.d.ts +10 -0
- package/dist/cli/ui.js +135 -0
- package/dist/cli/ui.js.map +1 -0
- package/dist/core/ades.d.ts +79 -0
- package/dist/core/ades.js +130 -0
- package/dist/core/ades.js.map +1 -0
- package/dist/core/baton.d.ts +37 -0
- package/dist/core/baton.js +92 -0
- package/dist/core/baton.js.map +1 -0
- package/dist/core/brain-extract.d.ts +111 -0
- package/dist/core/brain-extract.js +248 -0
- package/dist/core/brain-extract.js.map +1 -0
- package/dist/core/brain-index.d.ts +128 -0
- package/dist/core/brain-index.js +338 -0
- package/dist/core/brain-index.js.map +1 -0
- package/dist/core/brain.d.ts +214 -0
- package/dist/core/brain.js +425 -0
- package/dist/core/brain.js.map +1 -0
- package/dist/core/claude-cli.d.ts +12 -0
- package/dist/core/claude-cli.js +47 -0
- package/dist/core/claude-cli.js.map +1 -0
- package/dist/core/distill.d.ts +19 -0
- package/dist/core/distill.js +88 -0
- package/dist/core/distill.js.map +1 -0
- package/dist/core/eventlog.d.ts +43 -0
- package/dist/core/eventlog.js +211 -0
- package/dist/core/eventlog.js.map +1 -0
- package/dist/core/git.d.ts +203 -0
- package/dist/core/git.js +460 -0
- package/dist/core/git.js.map +1 -0
- package/dist/core/logbook.d.ts +59 -0
- package/dist/core/logbook.js +118 -0
- package/dist/core/logbook.js.map +1 -0
- package/dist/core/memory.d.ts +34 -0
- package/dist/core/memory.js +124 -0
- package/dist/core/memory.js.map +1 -0
- package/dist/core/notify.d.ts +11 -0
- package/dist/core/notify.js +30 -0
- package/dist/core/notify.js.map +1 -0
- package/dist/core/projection.d.ts +20 -0
- package/dist/core/projection.js +96 -0
- package/dist/core/projection.js.map +1 -0
- package/dist/core/registry.d.ts +76 -0
- package/dist/core/registry.js +147 -0
- package/dist/core/registry.js.map +1 -0
- package/dist/core/router.d.ts +42 -0
- package/dist/core/router.js +99 -0
- package/dist/core/router.js.map +1 -0
- package/dist/core/routes.d.ts +93 -0
- package/dist/core/routes.js +399 -0
- package/dist/core/routes.js.map +1 -0
- package/dist/core/search.d.ts +63 -0
- package/dist/core/search.js +0 -0
- package/dist/core/search.js.map +1 -0
- package/dist/core/setup.d.ts +70 -0
- package/dist/core/setup.js +250 -0
- package/dist/core/setup.js.map +1 -0
- package/dist/core/suggestions.d.ts +15 -0
- package/dist/core/suggestions.js +38 -0
- package/dist/core/suggestions.js.map +1 -0
- package/dist/core/worktree.d.ts +32 -0
- package/dist/core/worktree.js +83 -0
- package/dist/core/worktree.js.map +1 -0
- package/dist/daemon/app-page.d.ts +32 -0
- package/dist/daemon/app-page.js +6363 -0
- package/dist/daemon/app-page.js.map +1 -0
- package/dist/daemon/auth.d.ts +51 -0
- package/dist/daemon/auth.js +135 -0
- package/dist/daemon/auth.js.map +1 -0
- package/dist/daemon/board.d.ts +117 -0
- package/dist/daemon/board.js +202 -0
- package/dist/daemon/board.js.map +1 -0
- package/dist/daemon/brand-icons.d.ts +29 -0
- package/dist/daemon/brand-icons.js +44 -0
- package/dist/daemon/brand-icons.js.map +1 -0
- package/dist/daemon/client.d.ts +127 -0
- package/dist/daemon/client.js +237 -0
- package/dist/daemon/client.js.map +1 -0
- package/dist/daemon/geist-font.d.ts +10 -0
- package/dist/daemon/geist-font.js +783 -0
- package/dist/daemon/geist-font.js.map +1 -0
- package/dist/daemon/guards.d.ts +18 -0
- package/dist/daemon/guards.js +40 -0
- package/dist/daemon/guards.js.map +1 -0
- package/dist/daemon/linear.d.ts +59 -0
- package/dist/daemon/linear.js +117 -0
- package/dist/daemon/linear.js.map +1 -0
- package/dist/daemon/push.d.ts +22 -0
- package/dist/daemon/push.js +58 -0
- package/dist/daemon/push.js.map +1 -0
- package/dist/daemon/runtime.d.ts +299 -0
- package/dist/daemon/runtime.js +1016 -0
- package/dist/daemon/runtime.js.map +1 -0
- package/dist/daemon/server.d.ts +125 -0
- package/dist/daemon/server.js +1728 -0
- package/dist/daemon/server.js.map +1 -0
- package/dist/daemon/tasks.d.ts +153 -0
- package/dist/daemon/tasks.js +264 -0
- package/dist/daemon/tasks.js.map +1 -0
- package/dist/daemon/terminals.d.ts +82 -0
- package/dist/daemon/terminals.js +372 -0
- package/dist/daemon/terminals.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/sdk.d.ts +17 -0
- package/dist/sdk.js +17 -0
- package/dist/sdk.js.map +1 -0
- package/dist/types.d.ts +236 -0
- package/dist/types.js +18 -0
- package/dist/types.js.map +1 -0
- package/docs/SETUP.md +265 -0
- package/docs/adapters.md +132 -0
- package/docs/design-system.md +245 -0
- package/docs/img/board-linear.png +0 -0
- package/docs/img/board-projects.png +0 -0
- package/docs/img/brain.png +0 -0
- package/docs/img/settings.png +0 -0
- package/docs/img/workspace.png +0 -0
- package/docs/integration-notes.md +76 -0
- package/docs/proposals/brain.md +262 -0
- package/package.json +84 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal sessions for the workspace, in two flavours behind one interface.
|
|
3
|
+
*
|
|
4
|
+
* `pty` — a real pseudo-terminal via node-pty. The shell believes it's on a
|
|
5
|
+
* tty, so you get everything a terminal is supposed to do: its own prompt,
|
|
6
|
+
* echo, job control (^C/^Z), colour, `less`/`vim`/`htop`, and window size.
|
|
7
|
+
* node-pty is a native module, so it's an *optional* dependency.
|
|
8
|
+
*
|
|
9
|
+
* `pipe` — the fallback when node-pty isn't available (notably Linux without
|
|
10
|
+
* build tools, since node-pty ships prebuilds only for macOS/Windows). A
|
|
11
|
+
* long-lived shell on plain pipes: `cd` and variables persist and ^C works
|
|
12
|
+
* via the process group, but there's no echo or job control, so the client
|
|
13
|
+
* drives it a line at a time and draws its own prompt. See `docs/`.
|
|
14
|
+
*
|
|
15
|
+
* Both keep a scrollback buffer so a client that reloads can be replayed the
|
|
16
|
+
* session it left behind.
|
|
17
|
+
*/
|
|
18
|
+
/** Sentinel the pipe shell prints after each command: `<MARK><rc>\t<cwd>\n`. */
|
|
19
|
+
export declare const TERM_MARK = "__LOOM_END__";
|
|
20
|
+
export type TermMode = "pty" | "pipe";
|
|
21
|
+
export interface TermEvents {
|
|
22
|
+
onData(projectId: string, termId: string, chunk: string): void;
|
|
23
|
+
/** pipe mode only: a command finished, with its exit code and new cwd. */
|
|
24
|
+
onCommandEnd(projectId: string, termId: string, exit: number, cwd: string): void;
|
|
25
|
+
onExit(projectId: string, termId: string): void;
|
|
26
|
+
onTitle(projectId: string, termId: string, title: string): void;
|
|
27
|
+
}
|
|
28
|
+
export interface TerminalSession {
|
|
29
|
+
readonly projectId: string;
|
|
30
|
+
readonly termId: string;
|
|
31
|
+
readonly mode: TermMode;
|
|
32
|
+
cwd: string;
|
|
33
|
+
/** Raw input. In pty mode these are keystrokes; in pipe mode, a command. */
|
|
34
|
+
write(data: string): void;
|
|
35
|
+
resize(cols: number, rows: number): void;
|
|
36
|
+
interrupt(): void;
|
|
37
|
+
kill(): void;
|
|
38
|
+
scrollback(): string;
|
|
39
|
+
}
|
|
40
|
+
export interface PtyProcess {
|
|
41
|
+
pid: number;
|
|
42
|
+
onData(cb: (d: string) => void): void;
|
|
43
|
+
onExit(cb: () => void): void;
|
|
44
|
+
write(d: string): void;
|
|
45
|
+
resize(cols: number, rows: number): void;
|
|
46
|
+
kill(signal?: string): void;
|
|
47
|
+
}
|
|
48
|
+
interface PtyModule {
|
|
49
|
+
spawn(file: string, args: string[], opts: {
|
|
50
|
+
name: string;
|
|
51
|
+
cols: number;
|
|
52
|
+
rows: number;
|
|
53
|
+
cwd: string;
|
|
54
|
+
env: NodeJS.ProcessEnv;
|
|
55
|
+
}): PtyProcess;
|
|
56
|
+
}
|
|
57
|
+
/** Load node-pty once, proving it can actually spawn before we rely on it. */
|
|
58
|
+
export declare function loadPty(): PtyModule | null;
|
|
59
|
+
export declare function ptyUnavailableReason(): string | null;
|
|
60
|
+
/**
|
|
61
|
+
* The session cap, distinct from every other reason a shell won't start. A
|
|
62
|
+
* spawn failure and "you have too many tabs open" are different facts and the
|
|
63
|
+
* caller answers them with different status codes.
|
|
64
|
+
*/
|
|
65
|
+
export declare class TooManySessionsError extends Error {
|
|
66
|
+
constructor(max: number);
|
|
67
|
+
}
|
|
68
|
+
export declare class TerminalManager {
|
|
69
|
+
private events;
|
|
70
|
+
private maxSessions;
|
|
71
|
+
private sessions;
|
|
72
|
+
constructor(events: TermEvents, maxSessions?: number);
|
|
73
|
+
get mode(): TermMode;
|
|
74
|
+
private key;
|
|
75
|
+
get(projectId: string, termId: string): TerminalSession | undefined;
|
|
76
|
+
open(projectId: string, termId: string, dir: string, cols?: number, rows?: number): TerminalSession;
|
|
77
|
+
close(projectId: string, termId: string): void;
|
|
78
|
+
/** Drop a session from the registry once its process is gone. */
|
|
79
|
+
forget(projectId: string, termId: string): void;
|
|
80
|
+
closeAll(): void;
|
|
81
|
+
}
|
|
82
|
+
export {};
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal sessions for the workspace, in two flavours behind one interface.
|
|
3
|
+
*
|
|
4
|
+
* `pty` — a real pseudo-terminal via node-pty. The shell believes it's on a
|
|
5
|
+
* tty, so you get everything a terminal is supposed to do: its own prompt,
|
|
6
|
+
* echo, job control (^C/^Z), colour, `less`/`vim`/`htop`, and window size.
|
|
7
|
+
* node-pty is a native module, so it's an *optional* dependency.
|
|
8
|
+
*
|
|
9
|
+
* `pipe` — the fallback when node-pty isn't available (notably Linux without
|
|
10
|
+
* build tools, since node-pty ships prebuilds only for macOS/Windows). A
|
|
11
|
+
* long-lived shell on plain pipes: `cd` and variables persist and ^C works
|
|
12
|
+
* via the process group, but there's no echo or job control, so the client
|
|
13
|
+
* drives it a line at a time and draws its own prompt. See `docs/`.
|
|
14
|
+
*
|
|
15
|
+
* Both keep a scrollback buffer so a client that reloads can be replayed the
|
|
16
|
+
* session it left behind.
|
|
17
|
+
*/
|
|
18
|
+
import { spawn } from "node:child_process";
|
|
19
|
+
import fs from "node:fs";
|
|
20
|
+
import { createRequire } from "node:module";
|
|
21
|
+
import path from "node:path";
|
|
22
|
+
const require_ = createRequire(import.meta.url);
|
|
23
|
+
/** Sentinel the pipe shell prints after each command: `<MARK><rc>\t<cwd>\n`. */
|
|
24
|
+
export const TERM_MARK = "__LOOM_END__";
|
|
25
|
+
const isWin = process.platform === "win32";
|
|
26
|
+
/** Keep the last of a session's output for replay after a client reload. */
|
|
27
|
+
const SCROLLBACK_MAX = 256 * 1024;
|
|
28
|
+
/** Per-command output budget in pipe mode (a pty is interactive, so unbounded). */
|
|
29
|
+
const PIPE_OUTPUT_MAX = 2_000_000;
|
|
30
|
+
let ptyModule;
|
|
31
|
+
let ptyLoadError = null;
|
|
32
|
+
/**
|
|
33
|
+
* node-pty's macOS/Linux prebuilds ship `spawn-helper` without the executable
|
|
34
|
+
* bit (npm doesn't preserve it through the prebuild archive), and node-pty
|
|
35
|
+
* then fails every spawn with a bare "posix_spawnp failed". Repair it rather
|
|
36
|
+
* than making every user chmod a file inside node_modules.
|
|
37
|
+
*/
|
|
38
|
+
function fixSpawnHelper(moduleDir) {
|
|
39
|
+
if (isWin)
|
|
40
|
+
return;
|
|
41
|
+
const roots = [path.join(moduleDir, "prebuilds"), path.join(moduleDir, "build", "Release")];
|
|
42
|
+
for (const root of roots) {
|
|
43
|
+
let entries;
|
|
44
|
+
try {
|
|
45
|
+
entries = fs.readdirSync(root);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
for (const entry of entries) {
|
|
51
|
+
const candidates = [path.join(root, entry, "spawn-helper"), path.join(root, entry)];
|
|
52
|
+
for (const file of candidates) {
|
|
53
|
+
try {
|
|
54
|
+
const st = fs.statSync(file);
|
|
55
|
+
if (!st.isFile() || path.basename(file) !== "spawn-helper")
|
|
56
|
+
continue;
|
|
57
|
+
if (st.mode & 0o111)
|
|
58
|
+
continue; // already executable
|
|
59
|
+
fs.chmodSync(file, st.mode | 0o755);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
/* not present, or not ours to chmod — the probe below decides */
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/** Load node-pty once, proving it can actually spawn before we rely on it. */
|
|
69
|
+
export function loadPty() {
|
|
70
|
+
if (ptyModule !== undefined)
|
|
71
|
+
return ptyModule;
|
|
72
|
+
// An escape hatch: forces the pipe path on a machine that has node-pty, so
|
|
73
|
+
// the fallback stays exercisable (it is what Linux without a toolchain gets).
|
|
74
|
+
if (process.env.LOOM_NO_PTY === "1") {
|
|
75
|
+
ptyLoadError = "disabled by LOOM_NO_PTY=1";
|
|
76
|
+
ptyModule = null;
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
const dir = path.dirname(require_.resolve("node-pty/package.json"));
|
|
81
|
+
fixSpawnHelper(dir);
|
|
82
|
+
const mod = require_("node-pty");
|
|
83
|
+
// A load isn't proof: the native binary can resolve and still fail to
|
|
84
|
+
// spawn (missing helper, hardened runtime, seccomp). Try it for real.
|
|
85
|
+
const probe = mod.spawn(isWin ? "cmd.exe" : "/bin/sh", [], {
|
|
86
|
+
name: "xterm-256color",
|
|
87
|
+
cols: 80,
|
|
88
|
+
rows: 24,
|
|
89
|
+
cwd: process.cwd(),
|
|
90
|
+
env: process.env,
|
|
91
|
+
});
|
|
92
|
+
probe.kill();
|
|
93
|
+
ptyModule = mod;
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
ptyLoadError = err.message;
|
|
97
|
+
ptyModule = null;
|
|
98
|
+
}
|
|
99
|
+
return ptyModule;
|
|
100
|
+
}
|
|
101
|
+
export function ptyUnavailableReason() {
|
|
102
|
+
loadPty();
|
|
103
|
+
return ptyModule ? null : (ptyLoadError ?? "node-pty is not installed");
|
|
104
|
+
}
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
// sessions
|
|
107
|
+
// ---------------------------------------------------------------------------
|
|
108
|
+
function baseEnv() {
|
|
109
|
+
return {
|
|
110
|
+
...process.env,
|
|
111
|
+
TERM: "xterm-256color",
|
|
112
|
+
COLORTERM: "truecolor",
|
|
113
|
+
// The pager would sit there waiting for a key we can't easily send in
|
|
114
|
+
// pipe mode; in pty mode `less` works, but git's default pager still
|
|
115
|
+
// fights the small pane, so keep output inline in both.
|
|
116
|
+
PAGER: "cat",
|
|
117
|
+
GIT_PAGER: "cat",
|
|
118
|
+
LOOM_TERMINAL: "1",
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
class PtySession {
|
|
122
|
+
projectId;
|
|
123
|
+
termId;
|
|
124
|
+
cwd;
|
|
125
|
+
mode = "pty";
|
|
126
|
+
buf = "";
|
|
127
|
+
proc;
|
|
128
|
+
constructor(projectId, termId, cwd, mod, events, cols, rows) {
|
|
129
|
+
this.projectId = projectId;
|
|
130
|
+
this.termId = termId;
|
|
131
|
+
this.cwd = cwd;
|
|
132
|
+
const shell = isWin ? "cmd.exe" : process.env.SHELL || "/bin/sh";
|
|
133
|
+
this.proc = mod.spawn(shell, [], {
|
|
134
|
+
name: "xterm-256color",
|
|
135
|
+
cols,
|
|
136
|
+
rows,
|
|
137
|
+
cwd,
|
|
138
|
+
env: baseEnv(),
|
|
139
|
+
});
|
|
140
|
+
this.proc.onData((d) => {
|
|
141
|
+
this.buf = (this.buf + d).slice(-SCROLLBACK_MAX);
|
|
142
|
+
// OSC 7 (cwd) and OSC 0/2 (title) are how a shell reports where it is;
|
|
143
|
+
// read them if present, but never strip them — the emulator wants them.
|
|
144
|
+
const cwdMatch = /\u001b\]7;file:\/\/[^/]*([^\u0007\u001b]*)(?:\u0007|\u001b\\)/.exec(d);
|
|
145
|
+
if (cwdMatch?.[1])
|
|
146
|
+
this.cwd = decodeURIComponent(cwdMatch[1]);
|
|
147
|
+
const titleMatch = /\u001b\][02];([^\u0007\u001b]*)(?:\u0007|\u001b\\)/.exec(d);
|
|
148
|
+
if (titleMatch?.[1])
|
|
149
|
+
events.onTitle(projectId, termId, titleMatch[1]);
|
|
150
|
+
events.onData(projectId, termId, d);
|
|
151
|
+
});
|
|
152
|
+
this.proc.onExit(() => events.onExit(projectId, termId));
|
|
153
|
+
}
|
|
154
|
+
write(data) {
|
|
155
|
+
this.proc.write(data);
|
|
156
|
+
}
|
|
157
|
+
resize(cols, rows) {
|
|
158
|
+
try {
|
|
159
|
+
this.proc.resize(Math.max(2, cols), Math.max(1, rows));
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
/* the pty went away mid-resize */
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
interrupt() {
|
|
166
|
+
// A real tty turns ^C into SIGINT for the foreground job itself.
|
|
167
|
+
this.write("\u0003");
|
|
168
|
+
}
|
|
169
|
+
kill() {
|
|
170
|
+
try {
|
|
171
|
+
this.proc.kill();
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
/* already gone */
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
scrollback() {
|
|
178
|
+
return this.buf;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
class PipeSession {
|
|
182
|
+
projectId;
|
|
183
|
+
termId;
|
|
184
|
+
cwd;
|
|
185
|
+
events;
|
|
186
|
+
mode = "pipe";
|
|
187
|
+
child;
|
|
188
|
+
pending = "";
|
|
189
|
+
buf = "";
|
|
190
|
+
sent = 0;
|
|
191
|
+
constructor(projectId, termId, cwd, events) {
|
|
192
|
+
this.projectId = projectId;
|
|
193
|
+
this.termId = termId;
|
|
194
|
+
this.cwd = cwd;
|
|
195
|
+
this.events = events;
|
|
196
|
+
const shell = isWin ? "cmd.exe" : process.env.SHELL || "/bin/sh";
|
|
197
|
+
this.child = spawn(shell, isWin ? [] : ["-s"], {
|
|
198
|
+
cwd,
|
|
199
|
+
detached: !isWin, // own process group, so ^C reaches the foreground job
|
|
200
|
+
env: baseEnv(),
|
|
201
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
202
|
+
});
|
|
203
|
+
// A non-interactive shell dies on SIGINT by default, taking the session
|
|
204
|
+
// with it. A handled signal resets to default in children, so the shell
|
|
205
|
+
// survives while the foreground job still dies.
|
|
206
|
+
if (!isWin)
|
|
207
|
+
this.child.stdin?.write("trap ':' INT\n");
|
|
208
|
+
const onData = (b) => this.pump(b.toString("utf8"));
|
|
209
|
+
this.child.stdout?.on("data", onData);
|
|
210
|
+
this.child.stderr?.on("data", onData);
|
|
211
|
+
this.child.on("error", (err) => events.onData(projectId, termId, `loom: ${err.message}\n`));
|
|
212
|
+
this.child.on("close", () => events.onExit(projectId, termId));
|
|
213
|
+
}
|
|
214
|
+
write(data) {
|
|
215
|
+
this.sent = 0;
|
|
216
|
+
const probe = isWin
|
|
217
|
+
? `\r\necho ${TERM_MARK}%ERRORLEVEL%\t%CD%\r\n`
|
|
218
|
+
: `\n__loom_rc=$?; printf '\\n${TERM_MARK}%s\\t%s\\n' "$__loom_rc" "$PWD"\n`;
|
|
219
|
+
this.child.stdin?.write(data + probe);
|
|
220
|
+
}
|
|
221
|
+
resize() {
|
|
222
|
+
/* no tty to resize */
|
|
223
|
+
}
|
|
224
|
+
interrupt() {
|
|
225
|
+
if (!this.child.pid)
|
|
226
|
+
return;
|
|
227
|
+
try {
|
|
228
|
+
process.kill(-this.child.pid, "SIGINT");
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
try {
|
|
232
|
+
this.child.kill("SIGINT");
|
|
233
|
+
}
|
|
234
|
+
catch {
|
|
235
|
+
/* gone */
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
kill() {
|
|
240
|
+
try {
|
|
241
|
+
if (this.child.pid && !isWin)
|
|
242
|
+
process.kill(-this.child.pid, "SIGKILL");
|
|
243
|
+
else
|
|
244
|
+
this.child.kill("SIGKILL");
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
/* gone */
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
scrollback() {
|
|
251
|
+
return this.buf;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Strip the end-of-command sentinel out of the stream and report it. It can
|
|
255
|
+
* straddle two reads, so hold back any trailing partial match.
|
|
256
|
+
*/
|
|
257
|
+
pump(text) {
|
|
258
|
+
this.pending += text;
|
|
259
|
+
for (;;) {
|
|
260
|
+
const idx = this.pending.indexOf(TERM_MARK);
|
|
261
|
+
if (idx === -1)
|
|
262
|
+
break;
|
|
263
|
+
const nl = this.pending.indexOf("\n", idx);
|
|
264
|
+
if (nl === -1) {
|
|
265
|
+
this.emit(this.pending.slice(0, idx));
|
|
266
|
+
this.pending = this.pending.slice(idx);
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
// the sentinel is printed with a leading newline so it lands on its own
|
|
270
|
+
// line; drop that one back off so output isn't padded by a blank line
|
|
271
|
+
let pre = this.pending.slice(0, idx);
|
|
272
|
+
if (pre.endsWith("\n"))
|
|
273
|
+
pre = pre.slice(0, -1);
|
|
274
|
+
this.emit(pre);
|
|
275
|
+
const line = this.pending.slice(idx + TERM_MARK.length, nl);
|
|
276
|
+
const tab = line.indexOf("\t");
|
|
277
|
+
const code = Number(tab === -1 ? line : line.slice(0, tab));
|
|
278
|
+
const cwd = tab === -1 ? "" : line.slice(tab + 1).trim();
|
|
279
|
+
if (cwd)
|
|
280
|
+
this.cwd = cwd;
|
|
281
|
+
this.pending = this.pending.slice(nl + 1);
|
|
282
|
+
this.events.onCommandEnd(this.projectId, this.termId, Number.isFinite(code) ? code : 0, this.cwd);
|
|
283
|
+
}
|
|
284
|
+
let hold = 0;
|
|
285
|
+
for (let i = Math.min(TERM_MARK.length - 1, this.pending.length); i > 0; i--) {
|
|
286
|
+
if (this.pending.endsWith(TERM_MARK.slice(0, i))) {
|
|
287
|
+
hold = i;
|
|
288
|
+
break;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
const emit = this.pending.slice(0, this.pending.length - hold);
|
|
292
|
+
this.pending = this.pending.slice(this.pending.length - hold);
|
|
293
|
+
this.emit(emit);
|
|
294
|
+
}
|
|
295
|
+
emit(chunk) {
|
|
296
|
+
if (!chunk)
|
|
297
|
+
return;
|
|
298
|
+
if (this.sent >= PIPE_OUTPUT_MAX)
|
|
299
|
+
return;
|
|
300
|
+
let out = chunk;
|
|
301
|
+
if (this.sent + out.length > PIPE_OUTPUT_MAX) {
|
|
302
|
+
out = out.slice(0, PIPE_OUTPUT_MAX - this.sent) + "\n…output truncated…\n";
|
|
303
|
+
}
|
|
304
|
+
this.sent += out.length;
|
|
305
|
+
this.buf = (this.buf + out).slice(-SCROLLBACK_MAX);
|
|
306
|
+
this.events.onData(this.projectId, this.termId, out);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
// ---------------------------------------------------------------------------
|
|
310
|
+
// manager
|
|
311
|
+
// ---------------------------------------------------------------------------
|
|
312
|
+
/**
|
|
313
|
+
* The session cap, distinct from every other reason a shell won't start. A
|
|
314
|
+
* spawn failure and "you have too many tabs open" are different facts and the
|
|
315
|
+
* caller answers them with different status codes.
|
|
316
|
+
*/
|
|
317
|
+
export class TooManySessionsError extends Error {
|
|
318
|
+
constructor(max) {
|
|
319
|
+
super(`too many terminal sessions (max ${max})`);
|
|
320
|
+
this.name = "TooManySessionsError";
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
export class TerminalManager {
|
|
324
|
+
events;
|
|
325
|
+
maxSessions;
|
|
326
|
+
sessions = new Map();
|
|
327
|
+
constructor(events, maxSessions = 12) {
|
|
328
|
+
this.events = events;
|
|
329
|
+
this.maxSessions = maxSessions;
|
|
330
|
+
}
|
|
331
|
+
get mode() {
|
|
332
|
+
return loadPty() ? "pty" : "pipe";
|
|
333
|
+
}
|
|
334
|
+
key(projectId, termId) {
|
|
335
|
+
return `${projectId}:${termId}`;
|
|
336
|
+
}
|
|
337
|
+
get(projectId, termId) {
|
|
338
|
+
return this.sessions.get(this.key(projectId, termId));
|
|
339
|
+
}
|
|
340
|
+
open(projectId, termId, dir, cols = 80, rows = 24) {
|
|
341
|
+
const existing = this.get(projectId, termId);
|
|
342
|
+
if (existing)
|
|
343
|
+
return existing;
|
|
344
|
+
if (this.sessions.size >= this.maxSessions)
|
|
345
|
+
throw new TooManySessionsError(this.maxSessions);
|
|
346
|
+
const mod = loadPty();
|
|
347
|
+
const sess = mod
|
|
348
|
+
? new PtySession(projectId, termId, dir, mod, this.events, cols, rows)
|
|
349
|
+
: new PipeSession(projectId, termId, dir, this.events);
|
|
350
|
+
this.sessions.set(this.key(projectId, termId), sess);
|
|
351
|
+
return sess;
|
|
352
|
+
}
|
|
353
|
+
close(projectId, termId) {
|
|
354
|
+
const key = this.key(projectId, termId);
|
|
355
|
+
const sess = this.sessions.get(key);
|
|
356
|
+
if (!sess)
|
|
357
|
+
return;
|
|
358
|
+
this.sessions.delete(key);
|
|
359
|
+
sess.kill();
|
|
360
|
+
}
|
|
361
|
+
/** Drop a session from the registry once its process is gone. */
|
|
362
|
+
forget(projectId, termId) {
|
|
363
|
+
this.sessions.delete(this.key(projectId, termId));
|
|
364
|
+
}
|
|
365
|
+
closeAll() {
|
|
366
|
+
for (const [key, sess] of this.sessions) {
|
|
367
|
+
this.sessions.delete(key);
|
|
368
|
+
sess.kill();
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
//# sourceMappingURL=terminals.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminals.js","sourceRoot":"","sources":["../../src/daemon/terminals.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loom — library entry point.
|
|
3
|
+
* Everything a programmatic consumer (or the future iOS app's dev tooling)
|
|
4
|
+
* needs to talk to a loom daemon or embed the core.
|
|
5
|
+
*/
|
|
6
|
+
export * from "./types.js";
|
|
7
|
+
export { EventLog } from "./core/eventlog.js";
|
|
8
|
+
export { BatonManager, NotHolderError } from "./core/baton.js";
|
|
9
|
+
export { buildBriefing, buildProjection } from "./core/projection.js";
|
|
10
|
+
export { buildUnifiedMemory, readNativeMemory, nativeMemoryFiles } from "./core/memory.js";
|
|
11
|
+
export { suggestHandoff } from "./core/suggestions.js";
|
|
12
|
+
export { RouteEngine, RouteActiveError, resolveSteps, stepName } from "./core/routes.js";
|
|
13
|
+
export { rulesRouter, llmRouter } from "./core/router.js";
|
|
14
|
+
export type { HopDecision, RouterContext } from "./core/router.js";
|
|
15
|
+
export { renderProjection } from "./core/distill.js";
|
|
16
|
+
export { claudeText } from "./core/claude-cli.js";
|
|
17
|
+
export { listProjects, findProject, registerProject, readProjectConfig, writeProjectConfig, loomHome, } from "./core/registry.js";
|
|
18
|
+
export { ProjectRuntime } from "./daemon/runtime.js";
|
|
19
|
+
export { LoomDaemon, DEFAULT_PORT, tailscaleIp } from "./daemon/server.js";
|
|
20
|
+
export { DaemonClient, ensureDaemon, daemonRunning, stopDaemon } from "./daemon/client.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loom — library entry point.
|
|
3
|
+
* Everything a programmatic consumer (or the future iOS app's dev tooling)
|
|
4
|
+
* needs to talk to a loom daemon or embed the core.
|
|
5
|
+
*/
|
|
6
|
+
export * from "./types.js";
|
|
7
|
+
export { EventLog } from "./core/eventlog.js";
|
|
8
|
+
export { BatonManager, NotHolderError } from "./core/baton.js";
|
|
9
|
+
export { buildBriefing, buildProjection } from "./core/projection.js";
|
|
10
|
+
export { buildUnifiedMemory, readNativeMemory, nativeMemoryFiles } from "./core/memory.js";
|
|
11
|
+
export { suggestHandoff } from "./core/suggestions.js";
|
|
12
|
+
export { RouteEngine, RouteActiveError, resolveSteps, stepName } from "./core/routes.js";
|
|
13
|
+
export { rulesRouter, llmRouter } from "./core/router.js";
|
|
14
|
+
export { renderProjection } from "./core/distill.js";
|
|
15
|
+
export { claudeText } from "./core/claude-cli.js";
|
|
16
|
+
export { listProjects, findProject, registerProject, readProjectConfig, writeProjectConfig, loomHome, } from "./core/registry.js";
|
|
17
|
+
export { ProjectRuntime } from "./daemon/runtime.js";
|
|
18
|
+
export { LoomDaemon, DEFAULT_PORT, tailscaleIp } from "./daemon/server.js";
|
|
19
|
+
export { DaemonClient, ensureDaemon, daemonRunning, stopDaemon } from "./daemon/client.js";
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC3F,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACzF,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EACL,YAAY,EACZ,WAAW,EACX,eAAe,EACf,iBAAiB,EACjB,kBAAkB,EAClB,QAAQ,GACT,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAC3E,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dist/sdk.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loom Adapter SDK — build an adapter (full-duplex) or a bridge (read-mostly)
|
|
3
|
+
* for a new agent and register it with `registerAgentKind`.
|
|
4
|
+
*
|
|
5
|
+
* Contract (see ARCHITECTURE.md):
|
|
6
|
+
* - Adapters MUST implement send / stream (onEvent) / injectMemory /
|
|
7
|
+
* interrupt / diff, and may hold the baton.
|
|
8
|
+
* - Bridges only observe and receive projections; they never hold the baton.
|
|
9
|
+
*/
|
|
10
|
+
export type { Adapter, AdapterEvent, AgentCapabilities, AgentConfig, AgentRole, AgentTier, AnyAgent, Bridge, EventKind, SendInput, } from "./types.js";
|
|
11
|
+
export { isAdapter } from "./types.js";
|
|
12
|
+
export { AdapterBase, AgentBase, BridgeBase, cliAvailable, fetchJson, freePort, waitFor, } from "./adapters/base.js";
|
|
13
|
+
export { registerAgentKind, createAgent, knownAgentKinds } from "./adapters/index.js";
|
|
14
|
+
export { EchoAdapter } from "./adapters/echo.js";
|
|
15
|
+
export { ClaudeCodeAdapter } from "./adapters/claude-code.js";
|
|
16
|
+
export { OpenCodeAdapter } from "./adapters/opencode.js";
|
|
17
|
+
export { AntigravityBridge } from "./adapters/bridges/antigravity.js";
|
package/dist/sdk.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loom Adapter SDK — build an adapter (full-duplex) or a bridge (read-mostly)
|
|
3
|
+
* for a new agent and register it with `registerAgentKind`.
|
|
4
|
+
*
|
|
5
|
+
* Contract (see ARCHITECTURE.md):
|
|
6
|
+
* - Adapters MUST implement send / stream (onEvent) / injectMemory /
|
|
7
|
+
* interrupt / diff, and may hold the baton.
|
|
8
|
+
* - Bridges only observe and receive projections; they never hold the baton.
|
|
9
|
+
*/
|
|
10
|
+
export { isAdapter } from "./types.js";
|
|
11
|
+
export { AdapterBase, AgentBase, BridgeBase, cliAvailable, fetchJson, freePort, waitFor, } from "./adapters/base.js";
|
|
12
|
+
export { registerAgentKind, createAgent, knownAgentKinds } from "./adapters/index.js";
|
|
13
|
+
export { EchoAdapter } from "./adapters/echo.js";
|
|
14
|
+
export { ClaudeCodeAdapter } from "./adapters/claude-code.js";
|
|
15
|
+
export { OpenCodeAdapter } from "./adapters/opencode.js";
|
|
16
|
+
export { AntigravityBridge } from "./adapters/bridges/antigravity.js";
|
|
17
|
+
//# sourceMappingURL=sdk.js.map
|
package/dist/sdk.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sdk.js","sourceRoot":"","sources":["../src/sdk.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAcH,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EACL,WAAW,EACX,SAAS,EACT,UAAU,EACV,YAAY,EACZ,SAAS,EACT,QAAQ,EACR,OAAO,GACR,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtF,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC"}
|