@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,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared adapter plumbing: event fan-out, busy tracking, memory-file
|
|
3
|
+
* persistence, and small process/http helpers used by concrete adapters.
|
|
4
|
+
*/
|
|
5
|
+
import type { Adapter, AdapterEvent, AgentCapabilities, Bridge, SendInput } from "../types.js";
|
|
6
|
+
type EventCb = (e: AdapterEvent) => void;
|
|
7
|
+
export declare abstract class AgentBase {
|
|
8
|
+
readonly id: string;
|
|
9
|
+
readonly kind: string;
|
|
10
|
+
protected projectDir: string;
|
|
11
|
+
private listeners;
|
|
12
|
+
constructor(id: string, kind: string, projectDir: string);
|
|
13
|
+
onEvent(cb: EventCb): () => void;
|
|
14
|
+
protected emit(e: AdapterEvent): void;
|
|
15
|
+
injectMemory(projection: string): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
export declare abstract class AdapterBase extends AgentBase implements Adapter {
|
|
18
|
+
readonly capabilities: AgentCapabilities;
|
|
19
|
+
protected _busy: boolean;
|
|
20
|
+
busy(): boolean;
|
|
21
|
+
abstract available(): Promise<boolean>;
|
|
22
|
+
abstract start(): Promise<void>;
|
|
23
|
+
abstract stop(): Promise<void>;
|
|
24
|
+
abstract send(input: SendInput): Promise<void>;
|
|
25
|
+
abstract interrupt(): Promise<void>;
|
|
26
|
+
/** Default diff: `git status --porcelain` in the project dir. */
|
|
27
|
+
diff(): Promise<string>;
|
|
28
|
+
}
|
|
29
|
+
export declare abstract class BridgeBase extends AgentBase implements Bridge {
|
|
30
|
+
readonly capabilities: AgentCapabilities;
|
|
31
|
+
abstract available(): Promise<boolean>;
|
|
32
|
+
abstract start(): Promise<void>;
|
|
33
|
+
abstract stop(): Promise<void>;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Wrap a handoff briefing so a CLI with no system channel still treats it as
|
|
37
|
+
* authoritative.
|
|
38
|
+
*
|
|
39
|
+
* The claude adapter passes the briefing through `--append-system-prompt`, and
|
|
40
|
+
* grok through `--rules` — both real system channels. Codex and opencode have
|
|
41
|
+
* no per-turn system field, so the same briefing rides in the prompt. Framed
|
|
42
|
+
* loosely ("here's some context") a model skims past it; framed as a delimited,
|
|
43
|
+
* imperative directive block it doesn't. The pointer to the full `.loom/memory`
|
|
44
|
+
* file is preserved from the briefing itself, so the agent can pull detail on
|
|
45
|
+
* demand rather than being handed the whole store.
|
|
46
|
+
*/
|
|
47
|
+
export declare function frameBriefing(briefing: string): string;
|
|
48
|
+
/**
|
|
49
|
+
* Env for spawning agent CLIs. When Loom itself runs inside a Claude Code
|
|
50
|
+
* session (CLAUDECODE=1), the environment carries session-internal plumbing
|
|
51
|
+
* (CLAUDE_CODE_*, a session-scoped ANTHROPIC_BASE_URL, …) that breaks nested
|
|
52
|
+
* agent spawns — strip it so child agents auth like a fresh terminal.
|
|
53
|
+
*/
|
|
54
|
+
export declare function agentEnv(): NodeJS.ProcessEnv;
|
|
55
|
+
/**
|
|
56
|
+
* Is a CLI on PATH (exit 0 for `--version`)?
|
|
57
|
+
*
|
|
58
|
+
* Bounded: this sits in front of HTTP handlers (Tasks probes `gh` on every
|
|
59
|
+
* request), and a version probe that wedges would otherwise hang that request
|
|
60
|
+
* forever with no response. A CLI that can't say its own version inside the
|
|
61
|
+
* timeout is unavailable as far as callers are concerned.
|
|
62
|
+
*/
|
|
63
|
+
export declare function cliAvailable(cmd: string, args?: string[], timeoutMs?: number): Promise<boolean>;
|
|
64
|
+
/** Grab an ephemeral free TCP port. */
|
|
65
|
+
export declare function freePort(): Promise<number>;
|
|
66
|
+
export declare function waitFor(probe: () => Promise<boolean>, { timeoutMs, intervalMs }?: {
|
|
67
|
+
timeoutMs?: number | undefined;
|
|
68
|
+
intervalMs?: number | undefined;
|
|
69
|
+
}): Promise<void>;
|
|
70
|
+
export declare function fetchJson<T>(url: string, init?: RequestInit, timeoutMs?: number): Promise<T>;
|
|
71
|
+
export {};
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared adapter plumbing: event fan-out, busy tracking, memory-file
|
|
3
|
+
* persistence, and small process/http helpers used by concrete adapters.
|
|
4
|
+
*/
|
|
5
|
+
import { spawn } from "node:child_process";
|
|
6
|
+
import net from "node:net";
|
|
7
|
+
import { writeMemoryFile } from "../core/registry.js";
|
|
8
|
+
export class AgentBase {
|
|
9
|
+
id;
|
|
10
|
+
kind;
|
|
11
|
+
projectDir;
|
|
12
|
+
listeners = new Set();
|
|
13
|
+
constructor(id, kind, projectDir) {
|
|
14
|
+
this.id = id;
|
|
15
|
+
this.kind = kind;
|
|
16
|
+
this.projectDir = projectDir;
|
|
17
|
+
}
|
|
18
|
+
onEvent(cb) {
|
|
19
|
+
this.listeners.add(cb);
|
|
20
|
+
return () => this.listeners.delete(cb);
|
|
21
|
+
}
|
|
22
|
+
emit(e) {
|
|
23
|
+
for (const cb of this.listeners) {
|
|
24
|
+
try {
|
|
25
|
+
cb(e);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
// A broken subscriber must not break the stream.
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
async injectMemory(projection) {
|
|
33
|
+
writeMemoryFile(this.projectDir, this.id, projection);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export class AdapterBase extends AgentBase {
|
|
37
|
+
capabilities = {
|
|
38
|
+
tier: "adapter",
|
|
39
|
+
send: true,
|
|
40
|
+
stream: true,
|
|
41
|
+
injectMemory: true,
|
|
42
|
+
interrupt: true,
|
|
43
|
+
diff: true,
|
|
44
|
+
};
|
|
45
|
+
_busy = false;
|
|
46
|
+
busy() {
|
|
47
|
+
return this._busy;
|
|
48
|
+
}
|
|
49
|
+
/** Default diff: `git status --porcelain` in the project dir. */
|
|
50
|
+
async diff() {
|
|
51
|
+
return new Promise((resolve) => {
|
|
52
|
+
const child = spawn("git", ["status", "--porcelain"], { cwd: this.projectDir });
|
|
53
|
+
let out = "";
|
|
54
|
+
child.stdout.on("data", (d) => (out += d.toString()));
|
|
55
|
+
child.on("close", () => resolve(out.trim()));
|
|
56
|
+
child.on("error", () => resolve(""));
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
export class BridgeBase extends AgentBase {
|
|
61
|
+
capabilities = {
|
|
62
|
+
tier: "bridge",
|
|
63
|
+
send: false,
|
|
64
|
+
stream: true,
|
|
65
|
+
injectMemory: true,
|
|
66
|
+
interrupt: false,
|
|
67
|
+
diff: false,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
// Helpers
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
/**
|
|
74
|
+
* Wrap a handoff briefing so a CLI with no system channel still treats it as
|
|
75
|
+
* authoritative.
|
|
76
|
+
*
|
|
77
|
+
* The claude adapter passes the briefing through `--append-system-prompt`, and
|
|
78
|
+
* grok through `--rules` — both real system channels. Codex and opencode have
|
|
79
|
+
* no per-turn system field, so the same briefing rides in the prompt. Framed
|
|
80
|
+
* loosely ("here's some context") a model skims past it; framed as a delimited,
|
|
81
|
+
* imperative directive block it doesn't. The pointer to the full `.loom/memory`
|
|
82
|
+
* file is preserved from the briefing itself, so the agent can pull detail on
|
|
83
|
+
* demand rather than being handed the whole store.
|
|
84
|
+
*/
|
|
85
|
+
export function frameBriefing(briefing) {
|
|
86
|
+
const b = briefing.trim();
|
|
87
|
+
if (!b)
|
|
88
|
+
return "";
|
|
89
|
+
return [
|
|
90
|
+
"===== LOOM SESSION MEMORY — read this first; it is authoritative =====",
|
|
91
|
+
"You are continuing shared work handed to you through Loom. The context",
|
|
92
|
+
"below carries over from the previous agent(s): decisions, conventions and",
|
|
93
|
+
"constraints already settled for this project. Treat it as ground truth and",
|
|
94
|
+
"honor it — do not re-open settled choices. Where it points to a",
|
|
95
|
+
".loom/memory file, read that file if you need the detail.",
|
|
96
|
+
"",
|
|
97
|
+
b,
|
|
98
|
+
"===== end session memory — the user's message follows =====",
|
|
99
|
+
].join("\n");
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Env for spawning agent CLIs. When Loom itself runs inside a Claude Code
|
|
103
|
+
* session (CLAUDECODE=1), the environment carries session-internal plumbing
|
|
104
|
+
* (CLAUDE_CODE_*, a session-scoped ANTHROPIC_BASE_URL, …) that breaks nested
|
|
105
|
+
* agent spawns — strip it so child agents auth like a fresh terminal.
|
|
106
|
+
*/
|
|
107
|
+
export function agentEnv() {
|
|
108
|
+
if (!process.env.CLAUDECODE)
|
|
109
|
+
return process.env;
|
|
110
|
+
const env = { ...process.env };
|
|
111
|
+
for (const key of Object.keys(env)) {
|
|
112
|
+
if (key === "CLAUDECODE" ||
|
|
113
|
+
key.startsWith("CLAUDE_CODE_") ||
|
|
114
|
+
key === "CLAUDE_AGENT_SDK_VERSION" ||
|
|
115
|
+
key === "CLAUDE_EFFORT" ||
|
|
116
|
+
key === "ANTHROPIC_BASE_URL" ||
|
|
117
|
+
key === "BAGGAGE" ||
|
|
118
|
+
key === "AI_AGENT") {
|
|
119
|
+
delete env[key];
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return env;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Is a CLI on PATH (exit 0 for `--version`)?
|
|
126
|
+
*
|
|
127
|
+
* Bounded: this sits in front of HTTP handlers (Tasks probes `gh` on every
|
|
128
|
+
* request), and a version probe that wedges would otherwise hang that request
|
|
129
|
+
* forever with no response. A CLI that can't say its own version inside the
|
|
130
|
+
* timeout is unavailable as far as callers are concerned.
|
|
131
|
+
*/
|
|
132
|
+
export function cliAvailable(cmd, args = ["--version"], timeoutMs = 5_000) {
|
|
133
|
+
return new Promise((resolve) => {
|
|
134
|
+
try {
|
|
135
|
+
const child = spawn(cmd, args, { stdio: "ignore" });
|
|
136
|
+
const timer = setTimeout(() => {
|
|
137
|
+
child.kill("SIGKILL");
|
|
138
|
+
resolve(false);
|
|
139
|
+
}, timeoutMs);
|
|
140
|
+
timer.unref();
|
|
141
|
+
const done = (v) => {
|
|
142
|
+
clearTimeout(timer);
|
|
143
|
+
resolve(v);
|
|
144
|
+
};
|
|
145
|
+
child.on("close", (code) => done(code === 0));
|
|
146
|
+
child.on("error", () => done(false));
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
resolve(false);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
/** Grab an ephemeral free TCP port. */
|
|
154
|
+
export function freePort() {
|
|
155
|
+
return new Promise((resolve, reject) => {
|
|
156
|
+
const srv = net.createServer();
|
|
157
|
+
srv.listen(0, "127.0.0.1", () => {
|
|
158
|
+
const addr = srv.address();
|
|
159
|
+
if (addr && typeof addr === "object") {
|
|
160
|
+
const port = addr.port;
|
|
161
|
+
srv.close(() => resolve(port));
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
srv.close(() => reject(new Error("no port")));
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
srv.on("error", reject);
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
export async function waitFor(probe, { timeoutMs = 30_000, intervalMs = 300 } = {}) {
|
|
171
|
+
const deadline = Date.now() + timeoutMs;
|
|
172
|
+
while (Date.now() < deadline) {
|
|
173
|
+
if (await probe().catch(() => false))
|
|
174
|
+
return;
|
|
175
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
176
|
+
}
|
|
177
|
+
throw new Error("timed out waiting for condition");
|
|
178
|
+
}
|
|
179
|
+
export async function fetchJson(url, init, timeoutMs = 15_000) {
|
|
180
|
+
const res = await fetch(url, { ...init, signal: AbortSignal.timeout(timeoutMs) });
|
|
181
|
+
if (!res.ok) {
|
|
182
|
+
const body = await res.text().catch(() => "");
|
|
183
|
+
throw new Error(`${init?.method ?? "GET"} ${url} → ${res.status}: ${body.slice(0, 300)}`);
|
|
184
|
+
}
|
|
185
|
+
return (await res.json());
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=base.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.js","sourceRoot":"","sources":["../../src/adapters/base.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Antigravity bridge — presence, and now a way in.
|
|
3
|
+
*
|
|
4
|
+
* Antigravity is an Electron app with no API, no headless mode and no way to
|
|
5
|
+
* talk to it but the DevTools port it opens when you ask for one. Launched with
|
|
6
|
+
* --remote-debugging-port it speaks CDP, and CDP is enough to see it, type into
|
|
7
|
+
* its chat and read what comes back. The approach is the one
|
|
8
|
+
* krishnakanthb13/antigravity_phone_chat takes to put Antigravity on a phone:
|
|
9
|
+
* don't touch the provider APIs, don't lift the tokens — drive the app that's
|
|
10
|
+
* already signed in, exactly as a person would.
|
|
11
|
+
*
|
|
12
|
+
* It stays a BRIDGE rather than becoming an adapter, and that isn't timidity:
|
|
13
|
+
*
|
|
14
|
+
* - Antigravity edits the tree on its own schedule and knows nothing about
|
|
15
|
+
* Loom's baton. Handing it the baton would be a promise Loom can't keep,
|
|
16
|
+
* because nothing stops Antigravity from writing while another agent holds
|
|
17
|
+
* it. A bridge never holds the baton, so the lock keeps meaning something.
|
|
18
|
+
* - What comes back is the chat panel's text, not the model's response. Good
|
|
19
|
+
* enough to read; not something to hand to a router as a result.
|
|
20
|
+
*
|
|
21
|
+
* So: send with your own hands, read with your own eyes, from anywhere. What
|
|
22
|
+
* Loom adds is that the thread and the memory projection stay in one place.
|
|
23
|
+
*/
|
|
24
|
+
import { GuiChatDriver } from "./gui-chat.js";
|
|
25
|
+
import { BridgeBase } from "../base.js";
|
|
26
|
+
export declare class AntigravityBridge extends BridgeBase {
|
|
27
|
+
/**
|
|
28
|
+
* Sending is real here, unlike a plain bridge. The baton stays out of reach:
|
|
29
|
+
* tier is what decides that, and it's still "bridge".
|
|
30
|
+
*/
|
|
31
|
+
readonly capabilities: {
|
|
32
|
+
tier: "bridge";
|
|
33
|
+
send: boolean;
|
|
34
|
+
stream: boolean;
|
|
35
|
+
injectMemory: boolean;
|
|
36
|
+
interrupt: boolean;
|
|
37
|
+
diff: boolean;
|
|
38
|
+
};
|
|
39
|
+
protected driver: GuiChatDriver;
|
|
40
|
+
private pollTimer;
|
|
41
|
+
constructor(id: string, projectDir: string, options?: Record<string, unknown>, kind?: string, appName?: string);
|
|
42
|
+
available(): Promise<boolean>;
|
|
43
|
+
start(): Promise<void>;
|
|
44
|
+
stop(): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Type into the app's chat and report what the panel gained.
|
|
47
|
+
*
|
|
48
|
+
* Not part of the Bridge interface — the runtime won't route turns here, and
|
|
49
|
+
* that's deliberate. It's reachable through the daemon's bridge endpoint for
|
|
50
|
+
* a human who wants to drive Antigravity from their phone.
|
|
51
|
+
*/
|
|
52
|
+
ask(text: string): Promise<string>;
|
|
53
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Antigravity bridge — presence, and now a way in.
|
|
3
|
+
*
|
|
4
|
+
* Antigravity is an Electron app with no API, no headless mode and no way to
|
|
5
|
+
* talk to it but the DevTools port it opens when you ask for one. Launched with
|
|
6
|
+
* --remote-debugging-port it speaks CDP, and CDP is enough to see it, type into
|
|
7
|
+
* its chat and read what comes back. The approach is the one
|
|
8
|
+
* krishnakanthb13/antigravity_phone_chat takes to put Antigravity on a phone:
|
|
9
|
+
* don't touch the provider APIs, don't lift the tokens — drive the app that's
|
|
10
|
+
* already signed in, exactly as a person would.
|
|
11
|
+
*
|
|
12
|
+
* It stays a BRIDGE rather than becoming an adapter, and that isn't timidity:
|
|
13
|
+
*
|
|
14
|
+
* - Antigravity edits the tree on its own schedule and knows nothing about
|
|
15
|
+
* Loom's baton. Handing it the baton would be a promise Loom can't keep,
|
|
16
|
+
* because nothing stops Antigravity from writing while another agent holds
|
|
17
|
+
* it. A bridge never holds the baton, so the lock keeps meaning something.
|
|
18
|
+
* - What comes back is the chat panel's text, not the model's response. Good
|
|
19
|
+
* enough to read; not something to hand to a router as a result.
|
|
20
|
+
*
|
|
21
|
+
* So: send with your own hands, read with your own eyes, from anywhere. What
|
|
22
|
+
* Loom adds is that the thread and the memory projection stay in one place.
|
|
23
|
+
*/
|
|
24
|
+
import { GuiChatDriver } from "./gui-chat.js";
|
|
25
|
+
import { BridgeBase } from "../base.js";
|
|
26
|
+
import { cdpTargets } from "./cdp.js";
|
|
27
|
+
export class AntigravityBridge extends BridgeBase {
|
|
28
|
+
/**
|
|
29
|
+
* Sending is real here, unlike a plain bridge. The baton stays out of reach:
|
|
30
|
+
* tier is what decides that, and it's still "bridge".
|
|
31
|
+
*/
|
|
32
|
+
capabilities = {
|
|
33
|
+
tier: "bridge",
|
|
34
|
+
send: true,
|
|
35
|
+
stream: true,
|
|
36
|
+
injectMemory: true,
|
|
37
|
+
interrupt: false,
|
|
38
|
+
diff: false,
|
|
39
|
+
};
|
|
40
|
+
driver;
|
|
41
|
+
pollTimer = null;
|
|
42
|
+
constructor(id, projectDir, options = {}, kind = "antigravity", appName = "Antigravity") {
|
|
43
|
+
super(id, kind, projectDir);
|
|
44
|
+
// The kind picks the profile — which selectors, and which chat panel to
|
|
45
|
+
// scope the search to. See bridges/profiles.ts.
|
|
46
|
+
this.driver = new GuiChatDriver(appName, options, kind);
|
|
47
|
+
}
|
|
48
|
+
async available() {
|
|
49
|
+
return this.driver.reachable();
|
|
50
|
+
}
|
|
51
|
+
async start() {
|
|
52
|
+
if (!(await this.driver.reachable())) {
|
|
53
|
+
this.emit({ kind: "status", payload: { state: "unreachable", hint: this.driver.launchHint } });
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const targets = await cdpTargets(this.driver.endpoint);
|
|
57
|
+
// Reachable and driveable are different questions: a signed-out Antigravity
|
|
58
|
+
// answers CDP cheerfully and has no chat box in it. Say which one it is.
|
|
59
|
+
const drive = await this.driver.driveable();
|
|
60
|
+
this.emit({
|
|
61
|
+
kind: "status",
|
|
62
|
+
payload: {
|
|
63
|
+
state: drive.ok ? "ready" : "observed",
|
|
64
|
+
canSend: drive.ok,
|
|
65
|
+
...(drive.ok ? {} : { hint: drive.reason }),
|
|
66
|
+
targets: targets.filter((t) => t.type === "page").slice(0, 10).map((t) => ({ title: t.title, type: t.type })),
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
this.pollTimer = setInterval(() => {
|
|
70
|
+
void this.driver.reachable().then((ok) => {
|
|
71
|
+
if (!ok)
|
|
72
|
+
this.emit({ kind: "status", payload: { state: "unreachable" } });
|
|
73
|
+
});
|
|
74
|
+
}, 60_000);
|
|
75
|
+
this.pollTimer.unref?.();
|
|
76
|
+
}
|
|
77
|
+
async stop() {
|
|
78
|
+
if (this.pollTimer)
|
|
79
|
+
clearInterval(this.pollTimer);
|
|
80
|
+
this.pollTimer = null;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Type into the app's chat and report what the panel gained.
|
|
84
|
+
*
|
|
85
|
+
* Not part of the Bridge interface — the runtime won't route turns here, and
|
|
86
|
+
* that's deliberate. It's reachable through the daemon's bridge endpoint for
|
|
87
|
+
* a human who wants to drive Antigravity from their phone.
|
|
88
|
+
*/
|
|
89
|
+
async ask(text) {
|
|
90
|
+
const started = Date.now();
|
|
91
|
+
this.emit({ kind: "status", payload: { state: "turn_started", session: null } });
|
|
92
|
+
try {
|
|
93
|
+
const reply = await this.driver.ask(text);
|
|
94
|
+
if (reply.trim())
|
|
95
|
+
this.emit({ kind: "message", payload: { text: reply } });
|
|
96
|
+
this.emit({ kind: "run_complete", payload: { durationMs: Date.now() - started } });
|
|
97
|
+
return reply;
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
101
|
+
this.emit({ kind: "error", payload: { message } });
|
|
102
|
+
throw err;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=antigravity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"antigravity.js","sourceRoot":"","sources":["../../../src/adapters/bridges/antigravity.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A small Chrome DevTools Protocol client — enough to drive an Electron agent
|
|
3
|
+
* app, and nothing more.
|
|
4
|
+
*
|
|
5
|
+
* Antigravity and Kiro are Electron apps. Launched with a debugging port they
|
|
6
|
+
* speak CDP, which is the only interface either of them offers: no API, no
|
|
7
|
+
* headless mode, no way in but the one the browser gives us. The approach is
|
|
8
|
+
* the same one krishnakanthb13/antigravity_phone_chat uses to put Antigravity
|
|
9
|
+
* on a phone — connect over CDP, read the DOM, drive the real widgets.
|
|
10
|
+
*
|
|
11
|
+
* Typing goes through Input.insertText rather than assigning to `.value`.
|
|
12
|
+
* These UIs are React and Monaco: an assignment updates the DOM and the
|
|
13
|
+
* framework never hears about it, so the app still believes its composer is
|
|
14
|
+
* empty and sends nothing. insertText enters through the same pipeline a
|
|
15
|
+
* keyboard does, so the app cannot tell the difference.
|
|
16
|
+
*/
|
|
17
|
+
export interface CdpTarget {
|
|
18
|
+
id: string;
|
|
19
|
+
type: string;
|
|
20
|
+
title?: string;
|
|
21
|
+
url?: string;
|
|
22
|
+
webSocketDebuggerUrl?: string;
|
|
23
|
+
}
|
|
24
|
+
/** Is anything listening for CDP at host:port? */
|
|
25
|
+
export declare function cdpUp(endpoint: string, timeoutMs?: number): Promise<boolean>;
|
|
26
|
+
export declare function cdpTargets(endpoint: string, timeoutMs?: number): Promise<CdpTarget[]>;
|
|
27
|
+
/**
|
|
28
|
+
* The window that holds the app, not its splash screen.
|
|
29
|
+
*
|
|
30
|
+
* Antigravity shows a `data:` URL loader for the first seconds after launch and
|
|
31
|
+
* the workbench arrives later on a different target; picking the first page
|
|
32
|
+
* gets you an animated ellipsis with no chat in it.
|
|
33
|
+
*/
|
|
34
|
+
export declare function workbenchTarget(targets: CdpTarget[]): CdpTarget | null;
|
|
35
|
+
/**
|
|
36
|
+
* Every target that could hold a chat, best first.
|
|
37
|
+
*
|
|
38
|
+
* A page is not enough. Kiro keeps its chat in a `vscode-webview` target — a
|
|
39
|
+
* separate CDP target of type `iframe` — so a page-only filter finds the
|
|
40
|
+
* workbench, reports "no chat panel", and is wrong about the whole app.
|
|
41
|
+
* Antigravity keeps its chat in its page. Both have to be reachable, so this
|
|
42
|
+
* returns candidates and the driver asks each one whether a chat is in it.
|
|
43
|
+
*
|
|
44
|
+
* Ordering, not exclusion, is the tool here. AntiGravity-AutoAccept drops
|
|
45
|
+
* http(s) targets outright, because Antigravity's *Manager* is an https page
|
|
46
|
+
* behind a sign-in with no chat in it — a real trap, and it cost an afternoon.
|
|
47
|
+
* But "no chat" is a question the driver already asks, and answering it by URL
|
|
48
|
+
* scheme also throws away any app that legitimately serves its UI over http.
|
|
49
|
+
* So the Manager sorts last and eliminates itself by having no composer, which
|
|
50
|
+
* is both true and non-fatal.
|
|
51
|
+
*
|
|
52
|
+
* Workers are dropped for real: there is no DOM in one.
|
|
53
|
+
*/
|
|
54
|
+
export declare function chatTargets(targets: CdpTarget[]): CdpTarget[];
|
|
55
|
+
/** One CDP session against one target. */
|
|
56
|
+
export declare class CdpSession {
|
|
57
|
+
private readonly url;
|
|
58
|
+
private ws;
|
|
59
|
+
private nextId;
|
|
60
|
+
private pending;
|
|
61
|
+
private constructor();
|
|
62
|
+
static open(target: CdpTarget, timeoutMs?: number): Promise<CdpSession>;
|
|
63
|
+
private connect;
|
|
64
|
+
private onMessage;
|
|
65
|
+
send<T = unknown>(method: string, params?: Record<string, unknown>, timeoutMs?: number): Promise<T>;
|
|
66
|
+
/** Evaluate an expression in the page and return it by value. */
|
|
67
|
+
evaluate<T>(expression: string, timeoutMs?: number): Promise<T>;
|
|
68
|
+
/** Type text into whatever currently has focus, the way a keyboard would. */
|
|
69
|
+
insertText(text: string): Promise<void>;
|
|
70
|
+
/**
|
|
71
|
+
* Click where a person would.
|
|
72
|
+
*
|
|
73
|
+
* `el.focus()` is a request the page can decline — a re-render, an overlay, a
|
|
74
|
+
* framework that manages focus itself — and when it's declined, Input.insertText
|
|
75
|
+
* types into whatever else has focus, which is nowhere useful. A real mouse
|
|
76
|
+
* event at real coordinates is how a human gets the caret into a box, and it
|
|
77
|
+
* doesn't ask.
|
|
78
|
+
*/
|
|
79
|
+
clickAt(x: number, y: number): Promise<void>;
|
|
80
|
+
pressEnter(): Promise<void>;
|
|
81
|
+
close(): void;
|
|
82
|
+
}
|