@gleapai/kai-bridge 0.2.4 → 0.2.5
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/package.json +1 -1
- package/src/daemon.mjs +53 -4
- package/src/workspace.mjs +9 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gleapai/kai-bridge",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
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
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/src/daemon.mjs
CHANGED
|
@@ -20,7 +20,7 @@ import { KAI_HOME, defaultConfig, loadConfig, saveConfig } from "./config.mjs";
|
|
|
20
20
|
import { runTurn } from "./executor.mjs";
|
|
21
21
|
import { createManagedProfile, describeProfiles, managedConfigDir, ambientConfigDir, openLoginTerminal, probeUsageLimits } from "./profiles.mjs";
|
|
22
22
|
import { defaultRoots, groupByRepo, preferredCloneRoot, scanRoots, toDeviceRepoReport } from "./repos.mjs";
|
|
23
|
-
import { collectChanges, commitAndPush, copyPrimaryEnvFiles, materializeBinding, sessionSlug, worktreePath, ensureCommitExcludes } from "./workspace.mjs";
|
|
23
|
+
import { collectChanges, commitAndPush, copyPrimaryEnvFiles, currentBranch, materializeBinding, sessionSlug, worktreePath, ensureCommitExcludes } from "./workspace.mjs";
|
|
24
24
|
import { ServiceRunner, detectDevConfig, previewMcpServer, readDevConfig } from "./preview.mjs";
|
|
25
25
|
import { describeHarnesses, installHarness, probeHarnessAuth } from "./harnesses.mjs";
|
|
26
26
|
import { probeHarnessModels } from "./models.mjs";
|
|
@@ -738,6 +738,53 @@ export class BridgeDaemon {
|
|
|
738
738
|
return bound;
|
|
739
739
|
}
|
|
740
740
|
|
|
741
|
+
/**
|
|
742
|
+
* The agent's repo brief: which checkouts are part of the session (only
|
|
743
|
+
* those get committed, pushed and turned into PRs), and the recipe for
|
|
744
|
+
* bringing another repo on this device into the session. Ticket #146130:
|
|
745
|
+
* the session was scoped to Desktop, the fix belonged in JavaScript-SDK,
|
|
746
|
+
* and the agent's work there was stranded because nothing told it how a
|
|
747
|
+
* repo joins a session.
|
|
748
|
+
*/
|
|
749
|
+
repoBrief(turn, bound) {
|
|
750
|
+
const slug = sessionSlug(turn.sessionId, turn.title);
|
|
751
|
+
const lines = ["", "", "Repositories for this task (changes here are committed, pushed and opened as pull requests when the turn ends — you cannot commit or push yourself):"];
|
|
752
|
+
for (const b of bound) lines.push(`- ${b.key}: ${b.cwd}`);
|
|
753
|
+
const others = this.repoGroups.filter((g) => !bound.some((b) => b.key === g.key));
|
|
754
|
+
if (others.length) {
|
|
755
|
+
lines.push(
|
|
756
|
+
"",
|
|
757
|
+
"If the fix belongs in a repository that is NOT listed above, do not edit its primary checkout. Create the session worktree for it with the command below and work there — it is picked up like the repositories above (branch kai/" + slug + ", pushed at turn end, PR opened by Gleap):",
|
|
758
|
+
);
|
|
759
|
+
for (const g of others.slice(0, 40)) {
|
|
760
|
+
const base = g.primary.defaultBranch || "main";
|
|
761
|
+
lines.push(`- ${g.key}: git -C ${g.primary.path} fetch origin ${base} --quiet && git -C ${g.primary.path} worktree add -b kai/${slug} ${worktreePath(this.kaiHome, g.name, slug)} origin/${base}`);
|
|
762
|
+
}
|
|
763
|
+
if (others.length > 40) lines.push(`- (${others.length - 40} more repositories on this device — same recipe, path ~/.kai/worktrees/<repo>/${slug})`);
|
|
764
|
+
}
|
|
765
|
+
return lines.join("\n");
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
/**
|
|
769
|
+
* Session worktrees the agent created for repos outside the turn's
|
|
770
|
+
* bindings (via the recipe in `repoBrief`). They are bound like the
|
|
771
|
+
* declared repos so their changes are committed, pushed and reported.
|
|
772
|
+
*/
|
|
773
|
+
adoptSessionWorktrees(turn, bound) {
|
|
774
|
+
const slug = sessionSlug(turn.sessionId, turn.title);
|
|
775
|
+
const adopted = [];
|
|
776
|
+
for (const g of this.repoGroups) {
|
|
777
|
+
if (bound.some((b) => b.key === g.key)) continue;
|
|
778
|
+
const cwd = worktreePath(this.kaiHome, g.name, slug);
|
|
779
|
+
if (!existsSync(cwd)) continue;
|
|
780
|
+
const branch = currentBranch(cwd);
|
|
781
|
+
if (!branch || branch === "HEAD") continue;
|
|
782
|
+
adopted.push({ key: g.key, cwd, mode: "worktree", branch, base: g.primary.defaultBranch || "main", adopted: true });
|
|
783
|
+
this.log("info", "worktree.adopted", { turnId: turn.turnId, repo: g.key, cwd, branch });
|
|
784
|
+
}
|
|
785
|
+
return adopted;
|
|
786
|
+
}
|
|
787
|
+
|
|
741
788
|
async startTurn(turn) {
|
|
742
789
|
const { turnId } = turn;
|
|
743
790
|
if (this.running.has(turnId)) return;
|
|
@@ -755,7 +802,7 @@ export class BridgeDaemon {
|
|
|
755
802
|
// local paths — the prompt lists them.
|
|
756
803
|
const workDir = bound[0]?.cwd;
|
|
757
804
|
if (!workDir) throw new Error("Turn has no repositories.");
|
|
758
|
-
const repoNote =
|
|
805
|
+
const repoNote = this.repoBrief(turn, bound);
|
|
759
806
|
// Previews are manual-only (dashboard "Start preview") — a turn
|
|
760
807
|
// never boots dev servers on its own. When the user already has a
|
|
761
808
|
// preview running for this session, describe it to the agent and
|
|
@@ -773,7 +820,7 @@ export class BridgeDaemon {
|
|
|
773
820
|
});
|
|
774
821
|
await batcher.flush();
|
|
775
822
|
const completed = !ctrl.signal.aborted && res.code === 0 && !res.rateLimited;
|
|
776
|
-
const changes = bound.map((b) => {
|
|
823
|
+
const changes = [...bound, ...this.adoptSessionWorktrees(turn, bound)].map((b) => {
|
|
777
824
|
ensureCommitExcludes(b.cwd, { allowDevConfig: !!turn.allowDevConfig });
|
|
778
825
|
const diff = collectChanges(b.cwd);
|
|
779
826
|
// Build turns in worktree mode publish the session branch so the
|
|
@@ -786,7 +833,9 @@ export class BridgeDaemon {
|
|
|
786
833
|
message: `${turn.title || "Kai Code changes"}\n\nSession ${turn.sessionId} · run on ${this.config.device?.name || "a paired device"}`,
|
|
787
834
|
})
|
|
788
835
|
: null;
|
|
789
|
-
|
|
836
|
+
// `cwd` travels with the change so the dashboard can point at
|
|
837
|
+
// work that stayed on this machine (files but no push).
|
|
838
|
+
return { key: b.key, mode: b.mode, branch: b.branch, base: b.base, cwd: b.cwd, adopted: b.adopted || undefined, ...diff, push };
|
|
790
839
|
});
|
|
791
840
|
// Built OUTSIDE the report call: if posting the result throws, the
|
|
792
841
|
// catch below must not turn a finished turn into a failed one. The
|
package/src/workspace.mjs
CHANGED
|
@@ -107,6 +107,15 @@ export function materializeBinding({ kaiHome, repo, binding, sessionId, title, b
|
|
|
107
107
|
return { cwd: dir, mode, branch, base, resumed: false };
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
/** The branch checked out at `cwd` (null when it is not a git checkout). */
|
|
111
|
+
export function currentBranch(cwd) {
|
|
112
|
+
try {
|
|
113
|
+
return git(cwd, ["rev-parse", "--abbrev-ref", "HEAD"]) || null;
|
|
114
|
+
} catch {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
110
119
|
/** Diff of what the turn changed (for the dashboard's file-changes panel). */
|
|
111
120
|
export function collectChanges(cwd) {
|
|
112
121
|
const status = git(cwd, ["status", "--porcelain"]);
|