@gleapai/kai-bridge 0.2.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/README.md +45 -0
- package/bin/kai-bridge.mjs +275 -0
- package/package.json +47 -0
- package/runner/acp-runner.mjs +671 -0
- package/runner/lib/acp/harnesses.mjs +342 -0
- package/runner/lib/acp/mapper.mjs +575 -0
- package/runner/lib/acp/transcripts.mjs +238 -0
- package/runner/lib/contract.mjs +1122 -0
- package/runner/lib/wire-proxy.mjs +200 -0
- package/runner/personas/claude/kai-asker.md +69 -0
- package/runner/personas/claude/kai-doc-explorer.md +130 -0
- package/runner/personas/claude/kai-documentarian.md +205 -0
- package/runner/personas/claude/kai-researcher.md +68 -0
- package/runner/personas/claude/kai-resolution-analyst.md +164 -0
- package/runner/personas/codex/kai-asker.md +68 -0
- package/runner/personas/codex/kai-doc-explorer.md +130 -0
- package/runner/personas/codex/kai-documentarian.md +211 -0
- package/runner/personas/codex/kai-researcher.md +67 -0
- package/runner/personas/codex/kai-resolution-analyst.md +164 -0
- package/runner/tools/ask-user-mcp.mjs +130 -0
- package/runner/tools/todo-mcp.mjs +116 -0
- package/scripts/postinstall.mjs +24 -0
- package/src/api.mjs +141 -0
- package/src/config.mjs +76 -0
- package/src/daemon.mjs +904 -0
- package/src/executor.mjs +156 -0
- package/src/harnesses.mjs +252 -0
- package/src/preview.mjs +337 -0
- package/src/profiles.mjs +250 -0
- package/src/repos.mjs +182 -0
- package/src/service.mjs +162 -0
- package/src/setup.mjs +261 -0
- package/src/workspace.mjs +175 -0
package/README.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# kai-bridge
|
|
2
|
+
|
|
3
|
+
Run [Gleap Kai Code](https://gleap.io) sessions on your own machine — with your own
|
|
4
|
+
Claude Code / Codex login — and preview your real dev servers from the dashboard or the
|
|
5
|
+
Gleap app on your phone.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm i -g @gleapai/kai-bridge # one install, so the background service has a stable path
|
|
9
|
+
kai-bridge login # pair this machine with your Gleap account
|
|
10
|
+
kai-bridge install # run at login (launchd / systemd / Task Scheduler)
|
|
11
|
+
kai-bridge status
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
(`npx @gleapai/kai-bridge login` works for a quick look, but `install` needs the
|
|
15
|
+
global install — npm garbage-collects the npx cache, which would leave the
|
|
16
|
+
background service pointing at a deleted file.)
|
|
17
|
+
|
|
18
|
+
Then pick **Run on → your machine** in any Kai Code session. Repos are discovered
|
|
19
|
+
automatically under `~/Documents`, `~/code`, `~/dev`, `~/Projects`, `~/src` (add more with
|
|
20
|
+
`kai-bridge repo roots add <dir>`); each session runs in an isolated worktree under
|
|
21
|
+
`~/.kai/worktrees` unless you choose "Work in my checkout".
|
|
22
|
+
|
|
23
|
+
## Accounts
|
|
24
|
+
|
|
25
|
+
The bridge runs the official `claude` / `codex` binaries installed on your machine under
|
|
26
|
+
**your** login (`ambient` profile). Add a second account with a managed profile:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
kai-bridge profile add personal --harness claude
|
|
30
|
+
kai-bridge profile login personal
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Your harness logins never leave the machine; the bridge stores only its pairing token
|
|
34
|
+
(`~/.kai/config.json`).
|
|
35
|
+
|
|
36
|
+
## Local dry run
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
kai-bridge run --task "Explain the auth flow in this repo" --cwd ~/code/my-app
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Development
|
|
43
|
+
|
|
44
|
+
`npm run sync-runner` copies the ACP runner from a sibling `gleap_code_analyzer` checkout;
|
|
45
|
+
`npm test` runs the unit suite (uses real `git`).
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// kai-bridge — run Gleap Kai Code on this machine.
|
|
3
|
+
//
|
|
4
|
+
// kai-bridge setup guided onboarding: pair · background service · harness sign-ins
|
|
5
|
+
// (a bare `kai-bridge` on an unpaired machine runs it too)
|
|
6
|
+
// kai-bridge login | logout pair this device with your Gleap account / disconnect it
|
|
7
|
+
// kai-bridge install | uninstall run at login (launchd / systemd / Task Scheduler)
|
|
8
|
+
// kai-bridge start run in the foreground (what the service runs)
|
|
9
|
+
// kai-bridge status device, service, profiles, repos
|
|
10
|
+
// kai-bridge harness list|install <id>|login <id> Claude Code + Codex are bundled; Cursor is downloaded
|
|
11
|
+
// kai-bridge profile list|add <id> --harness claude|codex|cursor [--label …]|login <id>|remove <id>
|
|
12
|
+
// kai-bridge repo scan|roots [add <dir>|remove <dir>]|primary <repoKey> <path>
|
|
13
|
+
// kai-bridge run --task "…" [--model …] [--profile …] [--cwd …] (local turn, no server)
|
|
14
|
+
// kai-bridge doctor
|
|
15
|
+
|
|
16
|
+
import { spawn } from "node:child_process";
|
|
17
|
+
import { mkdtempSync } from "node:fs";
|
|
18
|
+
import { tmpdir } from "node:os";
|
|
19
|
+
import { dirname, join, resolve } from "node:path";
|
|
20
|
+
import { fileURLToPath } from "node:url";
|
|
21
|
+
|
|
22
|
+
import { KAI_HOME, loadConfig, saveConfig } from "../src/config.mjs";
|
|
23
|
+
import { logout, pairDevice, runSetup } from "../src/setup.mjs";
|
|
24
|
+
import { BridgeDaemon, createLogger, resolveProfiles } from "../src/daemon.mjs";
|
|
25
|
+
import { runTurn } from "../src/executor.mjs";
|
|
26
|
+
import { HARNESSES, createManagedProfile, describeProfiles, findBinary, loginCommand } from "../src/profiles.mjs";
|
|
27
|
+
import { defaultRoots, groupByRepo, scanRoots, toDeviceRepoReport } from "../src/repos.mjs";
|
|
28
|
+
import { install, isInstalled, uninstall, isEphemeralBinPath } from "../src/service.mjs";
|
|
29
|
+
import { HARNESS_INFO, describeHarnesses, installHarness } from "../src/harnesses.mjs";
|
|
30
|
+
|
|
31
|
+
const BIN = fileURLToPath(import.meta.url);
|
|
32
|
+
const argv = process.argv.slice(2);
|
|
33
|
+
const cmd = argv[0];
|
|
34
|
+
const flags = {};
|
|
35
|
+
const positional = [];
|
|
36
|
+
for (let i = 1; i < argv.length; i++) {
|
|
37
|
+
const a = argv[i];
|
|
38
|
+
if (a.startsWith("--")) {
|
|
39
|
+
const [k, v] = a.slice(2).split("=");
|
|
40
|
+
flags[k] = v ?? (argv[i + 1] && !argv[i + 1].startsWith("--") ? argv[++i] : true);
|
|
41
|
+
} else positional.push(a);
|
|
42
|
+
}
|
|
43
|
+
const out = (s) => process.stdout.write(s + "\n");
|
|
44
|
+
const fail = (s) => {
|
|
45
|
+
process.stderr.write(`error: ${s}\n`);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
async function login() {
|
|
50
|
+
const config = loadConfig();
|
|
51
|
+
await pairDevice({ config, name: flags.name, print: out });
|
|
52
|
+
out(`Next: \`kai-bridge install\` keeps it running at login. \`kai-bridge start\` runs it right now.`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function status() {
|
|
56
|
+
const config = loadConfig();
|
|
57
|
+
out(`home: ${KAI_HOME}`);
|
|
58
|
+
out(`device: ${config.device ? `${config.device.name} (${config.device.id})` : "not paired — run `kai-bridge login`"}`);
|
|
59
|
+
out(`service: ${isInstalled() ? "installed" : "not installed — run `kai-bridge install`"}`);
|
|
60
|
+
out("harnesses:");
|
|
61
|
+
for (const h of describeHarnesses(KAI_HOME)) out(` ${h.id.padEnd(8)} ${h.installed ? `installed ${h.version ?? ""}` : "not installed — kai-bridge harness install " + h.id}${h.bundled ? " (bundled)" : ""}`);
|
|
62
|
+
const profiles = await describeProfiles(resolveProfiles(config));
|
|
63
|
+
out("profiles:");
|
|
64
|
+
for (const p of profiles) out(` ${p.id.padEnd(18)} ${p.harness.padEnd(7)} ${p.authState.padEnd(14)} ${p.account ?? ""} ${p.version ? `(${p.version})` : ""}`);
|
|
65
|
+
const groups = groupByRepo(scanRoots([...defaultRoots(), ...(config.roots || [])]), config.primaryOverrides);
|
|
66
|
+
out(`repos (${groups.length}):`);
|
|
67
|
+
for (const r of toDeviceRepoReport(groups)) out(` ${r.key.padEnd(48)} ${r.primaryPath}${r.checkouts > 1 ? ` (+${r.checkouts - 1})` : ""} ${r.dirtyCount ? `· ${r.dirtyCount} dirty` : ""}`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function profile() {
|
|
71
|
+
const config = loadConfig();
|
|
72
|
+
const sub = positional[0];
|
|
73
|
+
if (sub === "list" || !sub) {
|
|
74
|
+
for (const p of await describeProfiles(resolveProfiles(config))) out(`${p.id}\t${p.harness}\t${p.kind}\t${p.authState}\t${p.account ?? ""}`);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (sub === "add") {
|
|
78
|
+
const id = positional[1];
|
|
79
|
+
const harness = flags.harness;
|
|
80
|
+
if (!id || !HARNESSES.includes(harness)) fail("usage: profile add <id> --harness claude|codex [--label …]");
|
|
81
|
+
if (config.profiles.some((p) => p.id === id)) fail(`profile ${id} exists`);
|
|
82
|
+
createManagedProfile(harness, id, KAI_HOME);
|
|
83
|
+
config.profiles.push({ id, harness, kind: "managed", label: flags.label || `${harness} (${id})` });
|
|
84
|
+
saveConfig(config);
|
|
85
|
+
out(`Added managed profile ${id}. Sign in with: kai-bridge profile login ${id}`);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (sub === "login") {
|
|
89
|
+
const p = resolveProfiles(config).find((x) => x.id === positional[1]);
|
|
90
|
+
if (!p) fail("unknown profile");
|
|
91
|
+
const c = loginCommand(p.harness, p.configDir);
|
|
92
|
+
if (!c) fail(`${p.harness} is not installed`);
|
|
93
|
+
const child = spawn(c.cmd, c.args, { env: c.env, stdio: "inherit" });
|
|
94
|
+
child.on("close", (code) => process.exit(code ?? 0));
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (sub === "remove") {
|
|
98
|
+
config.profiles = config.profiles.filter((p) => p.id !== positional[1]);
|
|
99
|
+
saveConfig(config);
|
|
100
|
+
out("removed (the login dir is kept under ~/.kai/accounts)");
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
fail("usage: profile list|add|login|remove");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function harness() {
|
|
107
|
+
const sub = positional[0] || "list";
|
|
108
|
+
if (sub === "list") {
|
|
109
|
+
for (const h of describeHarnesses(KAI_HOME)) out(`${h.id.padEnd(8)} ${h.label.padEnd(12)} ${h.installed ? "installed" : "not installed"} ${h.version ?? ""}${h.bundled ? " (bundled)" : ""}${h.localOnly ? " · local only" : ""}`);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const id = positional[1];
|
|
113
|
+
if (!HARNESS_INFO[id]) fail("usage: harness list|install <claude|codex|cursor>|login <id>");
|
|
114
|
+
if (sub === "install" || sub === "update") {
|
|
115
|
+
const res = await installHarness(id, { kaiHome: KAI_HOME, onLog: out });
|
|
116
|
+
if (!res.ok) fail(`${id} is not usable`);
|
|
117
|
+
out(`${HARNESS_INFO[id].label} ${res.version} → ${res.binary}`);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (sub === "login") {
|
|
121
|
+
positional[1] = `${id}-ambient`;
|
|
122
|
+
positional[0] = "login";
|
|
123
|
+
return profile();
|
|
124
|
+
}
|
|
125
|
+
fail("usage: harness list|install <id>|login <id>");
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async function repo() {
|
|
129
|
+
const config = loadConfig();
|
|
130
|
+
const sub = positional[0] || "scan";
|
|
131
|
+
if (sub === "scan") {
|
|
132
|
+
const groups = groupByRepo(scanRoots([...defaultRoots(), ...(config.roots || [])]), config.primaryOverrides);
|
|
133
|
+
for (const r of toDeviceRepoReport(groups)) out(`${r.key}\t${r.primaryPath}\t${r.branch}`);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (sub === "roots") {
|
|
137
|
+
if (positional[1] === "add") {
|
|
138
|
+
config.roots = [...new Set([...(config.roots || []), resolve(positional[2])])];
|
|
139
|
+
saveConfig(config);
|
|
140
|
+
} else if (positional[1] === "remove") {
|
|
141
|
+
config.roots = (config.roots || []).filter((r) => r !== resolve(positional[2]));
|
|
142
|
+
saveConfig(config);
|
|
143
|
+
}
|
|
144
|
+
for (const r of [...defaultRoots(), ...(config.roots || [])]) out(r);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (sub === "primary") {
|
|
148
|
+
config.primaryOverrides = { ...(config.primaryOverrides || {}), [positional[1]]: resolve(positional[2]) };
|
|
149
|
+
saveConfig(config);
|
|
150
|
+
out("ok");
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
fail("usage: repo scan|roots [add|remove <dir>]|primary <repoKey> <path>");
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async function runLocal() {
|
|
157
|
+
if (!flags.task) fail("usage: run --task \"…\" [--model anthropic/claude-sonnet-4-6] [--profile claude-ambient] [--cwd .]");
|
|
158
|
+
const config = loadConfig();
|
|
159
|
+
const profile = resolveProfiles(config).find((p) => p.id === (flags.profile || "claude-ambient"));
|
|
160
|
+
if (!profile) fail("unknown profile");
|
|
161
|
+
const model = flags.model || (profile.harness === "codex" ? "openai/gpt-5.5" : "anthropic/claude-sonnet-4-6");
|
|
162
|
+
const cwd = resolve(flags.cwd || process.cwd());
|
|
163
|
+
const res = await runTurn({
|
|
164
|
+
turn: { task: flags.task, model, engineModelSlug: model.split("/").slice(1).join("/"), effort: flags.effort, agent: flags.agent, harness: profile.harness },
|
|
165
|
+
profile,
|
|
166
|
+
workDir: cwd,
|
|
167
|
+
onEvent: (ev) => {
|
|
168
|
+
if (ev.type === "text" || ev.type === "plan") out(`\n${ev.message}`);
|
|
169
|
+
else if (ev.type === "tool_status" && ev.toolStatus === "running") out(`▸ ${ev.message}`);
|
|
170
|
+
else if (ev.type === "error") process.stderr.write(`✖ ${ev.message}\n`);
|
|
171
|
+
else if (ev.type === "result") out(`\n— ${ev.inputTokens ?? 0} in / ${ev.outputTokens ?? 0} out${ev.costUsd != null ? ` · $${ev.costUsd.toFixed(4)}` : ""} · session ${ev.sessionId}`);
|
|
172
|
+
},
|
|
173
|
+
onLog: (l) => {
|
|
174
|
+
if (process.env.KAI_RUNNER_DEBUG === "1") process.stderr.write(l);
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
process.exit(res.code ?? 0);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async function doctor() {
|
|
181
|
+
out(`node: ${process.version}`);
|
|
182
|
+
out(`claude: ${findBinary("claude") ?? "not found (npm i -g @anthropic-ai/claude-code)"}`);
|
|
183
|
+
out(`codex: ${findBinary("codex") ?? "not found (npm i -g @openai/codex)"}`);
|
|
184
|
+
out(`git: ${findBinary("git") ?? "not found"}`);
|
|
185
|
+
out(`home: ${KAI_HOME}`);
|
|
186
|
+
await status();
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
try {
|
|
190
|
+
switch (cmd) {
|
|
191
|
+
case "login":
|
|
192
|
+
await login();
|
|
193
|
+
break;
|
|
194
|
+
case "setup":
|
|
195
|
+
await runSetup({ binPath: BIN });
|
|
196
|
+
break;
|
|
197
|
+
case "logout":
|
|
198
|
+
await logout();
|
|
199
|
+
break;
|
|
200
|
+
case "install": {
|
|
201
|
+
if (isEphemeralBinPath(BIN)) {
|
|
202
|
+
fail(
|
|
203
|
+
"Running from npx, so there's no stable path to install from.\n" +
|
|
204
|
+
"Install it once, then this works:\n\n" +
|
|
205
|
+
" npm i -g @gleapai/kai-bridge\n kai-bridge install\n",
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
const res = install({ binPath: BIN, logDir: join(KAI_HOME, "logs") });
|
|
209
|
+
out(`Installed (${res.kind}): ${res.path}. It runs at login and restarts if it crashes.`);
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
case "uninstall":
|
|
213
|
+
out(uninstall() ? "Removed." : "Nothing to remove.");
|
|
214
|
+
break;
|
|
215
|
+
case "start": {
|
|
216
|
+
process.env.KAI_BRIDGE_FOREGROUND = process.stdout.isTTY ? "1" : "0";
|
|
217
|
+
const daemon = new BridgeDaemon({ log: createLogger() });
|
|
218
|
+
await daemon.start();
|
|
219
|
+
const stop = () => {
|
|
220
|
+
// Give in-flight turns a moment to post their result before the
|
|
221
|
+
// process dies; a hard exit here is what left sessions spinning.
|
|
222
|
+
daemon.stop();
|
|
223
|
+
setTimeout(() => process.exit(0), 3_000).unref?.();
|
|
224
|
+
};
|
|
225
|
+
process.on("SIGINT", stop);
|
|
226
|
+
process.on("SIGTERM", stop);
|
|
227
|
+
break;
|
|
228
|
+
}
|
|
229
|
+
case "status":
|
|
230
|
+
await status();
|
|
231
|
+
break;
|
|
232
|
+
case "profile":
|
|
233
|
+
await profile();
|
|
234
|
+
break;
|
|
235
|
+
case "harness":
|
|
236
|
+
await harness();
|
|
237
|
+
break;
|
|
238
|
+
case "repo":
|
|
239
|
+
await repo();
|
|
240
|
+
break;
|
|
241
|
+
case "run":
|
|
242
|
+
await runLocal();
|
|
243
|
+
break;
|
|
244
|
+
case "doctor":
|
|
245
|
+
await doctor();
|
|
246
|
+
break;
|
|
247
|
+
default: {
|
|
248
|
+
// A bare `kai-bridge` in a terminal IS the setup — first run pairs,
|
|
249
|
+
// later runs open with keep / reconnect / log out, so it doubles as
|
|
250
|
+
// "manage this machine". Help lives on `kai-bridge help` (and on any
|
|
251
|
+
// non-TTY invocation, where a wizard could never prompt).
|
|
252
|
+
if (!cmd && process.stdin.isTTY) {
|
|
253
|
+
await runSetup({ binPath: BIN });
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
256
|
+
out(`kai-bridge — run Gleap Kai Code on this machine
|
|
257
|
+
|
|
258
|
+
setup guided onboarding (pair · service · sign-ins)
|
|
259
|
+
login | logout | install | uninstall | start | status | doctor
|
|
260
|
+
harness list|install <id>|login <id>
|
|
261
|
+
profile list|add <id> --harness claude|codex|cursor|login <id>|remove <id>
|
|
262
|
+
repo scan|roots [add|remove <dir>]|primary <repoKey> <path>
|
|
263
|
+
run --task "…" [--model …] [--profile …] [--cwd …]`);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
} catch (err) {
|
|
267
|
+
// Revoked pairing under the background service: exit 0 so launchd
|
|
268
|
+
// records a deliberate stop instead of crash-restarting into an
|
|
269
|
+
// endless loop of 403s. Interactively it stays a visible failure.
|
|
270
|
+
if (err?.code === "REVOKED" && !process.stdout.isTTY) {
|
|
271
|
+
process.stderr.write(`${err.message}\n`);
|
|
272
|
+
process.exit(0);
|
|
273
|
+
}
|
|
274
|
+
fail(err?.message ?? String(err));
|
|
275
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gleapai/kai-bridge",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Run Gleap Kai Code sessions on your own machine with your own Claude Code / Codex login — and preview your real dev servers from the dashboard or the phone.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"bin": {
|
|
8
|
+
"kai-bridge": "bin/kai-bridge.mjs"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"bin",
|
|
12
|
+
"src",
|
|
13
|
+
"runner",
|
|
14
|
+
"scripts/postinstall.mjs",
|
|
15
|
+
"README.md"
|
|
16
|
+
],
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=20"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"test": "node --test 'test/**/*.test.mjs'",
|
|
22
|
+
"sync-runner": "node scripts/sync-runner.mjs",
|
|
23
|
+
"postinstall": "node scripts/postinstall.mjs"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@agentclientprotocol/claude-agent-acp": "0.70.0",
|
|
27
|
+
"@agentclientprotocol/codex-acp": "1.6.2",
|
|
28
|
+
"@agentclientprotocol/sdk": "1.4.0",
|
|
29
|
+
"@playwright/mcp": "^0.0.79",
|
|
30
|
+
"@sockudo/client": "^2.0.0",
|
|
31
|
+
"chokidar": "^4.0.1",
|
|
32
|
+
"yaml": "^2.9.0"
|
|
33
|
+
},
|
|
34
|
+
"overrides": {
|
|
35
|
+
"@anthropic-ai/claude-agent-sdk": "0.3.245"
|
|
36
|
+
},
|
|
37
|
+
"exports": {
|
|
38
|
+
".": "./src/daemon.mjs",
|
|
39
|
+
"./src/*": "./src/*",
|
|
40
|
+
"./runner/*": "./runner/*"
|
|
41
|
+
},
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "git+https://github.com/GleapSDK/kai-bridge.git"
|
|
45
|
+
},
|
|
46
|
+
"homepage": "https://gleap.io"
|
|
47
|
+
}
|