@buildautomaton/cli 0.1.69 → 0.1.71

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/dist/cli.js CHANGED
@@ -7720,14 +7720,14 @@ var init_access = __esm({
7720
7720
 
7721
7721
  // src/sqlite/read-migration-sql.ts
7722
7722
  import { existsSync, readFileSync } from "node:fs";
7723
- import { dirname, join as join2 } from "node:path";
7723
+ import { dirname, join as join3 } from "node:path";
7724
7724
  import { fileURLToPath as fileURLToPath2 } from "node:url";
7725
7725
  function readMigrationSql(filename, ...searchSubdirs) {
7726
7726
  const moduleDir = dirname(fileURLToPath2(import.meta.url));
7727
7727
  const candidates = searchSubdirs.flatMap((sub) => [
7728
- join2(moduleDir, sub, filename),
7729
- join2(moduleDir, "..", sub, filename),
7730
- join2(moduleDir, "..", "..", "dist", sub, filename)
7728
+ join3(moduleDir, sub, filename),
7729
+ join3(moduleDir, "..", sub, filename),
7730
+ join3(moduleDir, "..", "..", "dist", sub, filename)
7731
7731
  ]);
7732
7732
  const resolved = candidates.find((path84) => existsSync(path84));
7733
7733
  if (!resolved) {
@@ -10608,26 +10608,26 @@ var init_member_access = __esm({
10608
10608
 
10609
10609
  // src/code-nav/tree-sitter-assets-dir.ts
10610
10610
  import { existsSync as existsSync2 } from "node:fs";
10611
- import { dirname as dirname2, join as join3 } from "node:path";
10611
+ import { dirname as dirname2, join as join4 } from "node:path";
10612
10612
  import { fileURLToPath as fileURLToPath3 } from "node:url";
10613
10613
  function repoNodeModulesDir(fromDir) {
10614
- return join3(fromDir, "..", "..", "..", "node_modules");
10614
+ return join4(fromDir, "..", "..", "..", "node_modules");
10615
10615
  }
10616
10616
  function firstDirWithWebTreeSitterWasm(candidates) {
10617
10617
  for (const dir of candidates) {
10618
- if (existsSync2(join3(dir, "web-tree-sitter.wasm"))) return dir;
10618
+ if (existsSync2(join4(dir, "web-tree-sitter.wasm"))) return dir;
10619
10619
  }
10620
10620
  return null;
10621
10621
  }
10622
10622
  function getTreeSitterAssetsDir() {
10623
10623
  const moduleDir = dirname2(fileURLToPath3(import.meta.url));
10624
10624
  const fromModule = firstDirWithWebTreeSitterWasm([
10625
- join3(moduleDir, "tree-sitter"),
10626
- join3(moduleDir, "..", "tree-sitter"),
10627
- join3(moduleDir, "..", "..", "dist", "tree-sitter")
10625
+ join4(moduleDir, "tree-sitter"),
10626
+ join4(moduleDir, "..", "tree-sitter"),
10627
+ join4(moduleDir, "..", "..", "dist", "tree-sitter")
10628
10628
  ]);
10629
10629
  if (fromModule) return fromModule;
10630
- const fromNodeModules = firstDirWithWebTreeSitterWasm([join3(repoNodeModulesDir(moduleDir), "web-tree-sitter")]);
10630
+ const fromNodeModules = firstDirWithWebTreeSitterWasm([join4(repoNodeModulesDir(moduleDir), "web-tree-sitter")]);
10631
10631
  if (fromNodeModules) return fromNodeModules;
10632
10632
  throw new Error(
10633
10633
  "Missing web-tree-sitter.wasm. Run `pnpm build` in packages/cli to copy tree-sitter assets into dist/tree-sitter."
@@ -10641,7 +10641,7 @@ var init_tree_sitter_assets_dir = __esm({
10641
10641
 
10642
10642
  // src/code-nav/parser/tree-sitter-runtime.ts
10643
10643
  import { existsSync as existsSync3, readFileSync as readFileSync2 } from "node:fs";
10644
- import { dirname as dirname3, join as join4 } from "node:path";
10644
+ import { dirname as dirname3, join as join5 } from "node:path";
10645
10645
  import { fileURLToPath as fileURLToPath4 } from "node:url";
10646
10646
  import { Parser, Language, Query, LANGUAGE_VERSION, MIN_COMPATIBLE_VERSION } from "web-tree-sitter";
10647
10647
  function withTreeSitterRuntimeLock(fn) {
@@ -10656,7 +10656,7 @@ async function ensureParserInit() {
10656
10656
  if (!initPromise2) {
10657
10657
  const assetsDir = getTreeSitterAssetsDir();
10658
10658
  initPromise2 = Parser.init({
10659
- locateFile: (scriptName) => join4(assetsDir, scriptName)
10659
+ locateFile: (scriptName) => join5(assetsDir, scriptName)
10660
10660
  }).catch((e) => {
10661
10661
  initPromise2 = null;
10662
10662
  throw e;
@@ -10666,9 +10666,9 @@ async function ensureParserInit() {
10666
10666
  }
10667
10667
  function resolveWasmPath(assetsDir, wasmFile) {
10668
10668
  const candidates = [
10669
- join4(assetsDir, wasmFile),
10670
- join4(assetsDir, "..", "..", "dist", "tree-sitter", wasmFile),
10671
- join4(assetsDir, "..", "..", "..", "node_modules", wasmFile.replace(".wasm", ""), wasmFile)
10669
+ join5(assetsDir, wasmFile),
10670
+ join5(assetsDir, "..", "..", "dist", "tree-sitter", wasmFile),
10671
+ join5(assetsDir, "..", "..", "..", "node_modules", wasmFile.replace(".wasm", ""), wasmFile)
10672
10672
  ];
10673
10673
  const hit = candidates.find((p) => existsSync3(p));
10674
10674
  if (!hit) throw new Error(`Missing tree-sitter wasm: ${wasmFile}`);
@@ -10677,10 +10677,10 @@ function resolveWasmPath(assetsDir, wasmFile) {
10677
10677
  function resolveTagsPath(assetsDir, tagsQueryFile) {
10678
10678
  const pkgName = tagsQueryFile.split("/")[0] ?? "";
10679
10679
  const candidates = [
10680
- join4(CODE_NAV_QUERIES_ROOT, tagsQueryFile),
10681
- join4(assetsDir, tagsQueryFile),
10682
- join4(assetsDir, "..", "..", "dist", "tree-sitter", tagsQueryFile),
10683
- join4(assetsDir, "..", "..", "..", "node_modules", pkgName, "queries", "tags.scm")
10680
+ join5(CODE_NAV_QUERIES_ROOT, tagsQueryFile),
10681
+ join5(assetsDir, tagsQueryFile),
10682
+ join5(assetsDir, "..", "..", "dist", "tree-sitter", tagsQueryFile),
10683
+ join5(assetsDir, "..", "..", "..", "node_modules", pkgName, "queries", "tags.scm")
10684
10684
  ];
10685
10685
  const hit = candidates.find((p) => existsSync3(p));
10686
10686
  if (!hit) throw new Error(`Missing tags query: ${tagsQueryFile}`);
@@ -10718,7 +10718,7 @@ var init_tree_sitter_runtime = __esm({
10718
10718
  loadedByKey = /* @__PURE__ */ new Map();
10719
10719
  failedLanguageKeys = /* @__PURE__ */ new Set();
10720
10720
  runtimeChain = Promise.resolve();
10721
- CODE_NAV_QUERIES_ROOT = join4(dirname3(fileURLToPath4(import.meta.url)), "..", "queries");
10721
+ CODE_NAV_QUERIES_ROOT = join5(dirname3(fileURLToPath4(import.meta.url)), "..", "queries");
10722
10722
  }
10723
10723
  });
10724
10724
 
@@ -12289,7 +12289,7 @@ var init_in_process_bridge = __esm({
12289
12289
  });
12290
12290
 
12291
12291
  // src/code-nav/symbol-index/worker/lifecycle/state.ts
12292
- import { dirname as dirname4, join as join5 } from "node:path";
12292
+ import { dirname as dirname4, join as join6 } from "node:path";
12293
12293
  import { fileURLToPath as fileURLToPath5 } from "node:url";
12294
12294
  function getWorker() {
12295
12295
  return worker;
@@ -12308,7 +12308,7 @@ function shouldUseInProcessWorker() {
12308
12308
  }
12309
12309
  function resolveWorkerScriptPath() {
12310
12310
  const moduleDir = dirname4(fileURLToPath5(import.meta.url));
12311
- return join5(moduleDir, "..", "worker.js");
12311
+ return join6(moduleDir, "..", "worker.js");
12312
12312
  }
12313
12313
  function resetLifecycleStateForTests() {
12314
12314
  terminatePromise = null;
@@ -31064,7 +31064,7 @@ var {
31064
31064
  } = import_index.default;
31065
31065
 
31066
31066
  // src/cli-version.ts
31067
- var CLI_VERSION = "0.1.69".length > 0 ? "0.1.69" : "0.0.0-dev";
31067
+ var CLI_VERSION = "0.1.71".length > 0 ? "0.1.71" : "0.0.0-dev";
31068
31068
 
31069
31069
  // src/cli/defaults.ts
31070
31070
  var DEFAULT_API_URL = process.env.BUILDAUTOMATON_API_URL ?? "https://api.buildautomaton.com";
@@ -32816,6 +32816,46 @@ function runPendingAuth(options) {
32816
32816
  // src/run-bridge-connected.ts
32817
32817
  init_log();
32818
32818
 
32819
+ // src/agents/acp/clients/bridge-agent-path.ts
32820
+ import { execFileSync as execFileSync2 } from "node:child_process";
32821
+ import { homedir } from "node:os";
32822
+ import { join as join2 } from "node:path";
32823
+ function getBridgeAgentPathEntries(home = process.env.HOME ?? homedir()) {
32824
+ const entries = [
32825
+ join2(home, ".local", "bin"),
32826
+ join2(home, ".npm-global", "bin"),
32827
+ "/usr/local/bin"
32828
+ ];
32829
+ const prefix = process.env.NPM_CONFIG_PREFIX;
32830
+ if (prefix) entries.push(join2(prefix, "bin"));
32831
+ try {
32832
+ const npmBin = execFileSync2("npm", ["bin", "-g"], { encoding: "utf8", timeout: 2e3 }).trim();
32833
+ if (npmBin) entries.push(npmBin);
32834
+ } catch {
32835
+ }
32836
+ return entries;
32837
+ }
32838
+ function augmentPath(pathValue, extra) {
32839
+ const current = pathValue ?? "";
32840
+ const parts = current.split(":").filter(Boolean);
32841
+ const seen = new Set(parts);
32842
+ const prefix = [];
32843
+ for (const entry of extra) {
32844
+ if (!entry || seen.has(entry)) continue;
32845
+ seen.add(entry);
32846
+ prefix.push(entry);
32847
+ }
32848
+ if (prefix.length === 0) return current;
32849
+ return current ? `${prefix.join(":")}:${current}` : prefix.join(":");
32850
+ }
32851
+ function ensureBridgeAgentPathInProcessEnv() {
32852
+ const extra = getBridgeAgentPathEntries();
32853
+ process.env.PATH = augmentPath(process.env.PATH, extra);
32854
+ }
32855
+ function bridgeAgentPathEnv(base = process.env) {
32856
+ return { ...base, PATH: augmentPath(base.PATH, getBridgeAgentPathEntries()) };
32857
+ }
32858
+
32819
32859
  // src/files/watch-file-index.ts
32820
32860
  import path25 from "node:path";
32821
32861
 
@@ -34902,6 +34942,18 @@ function getAgentModelFromAgentConfig(config2) {
34902
34942
  return t !== "" ? t : null;
34903
34943
  }
34904
34944
 
34945
+ // ../types/src/overview/heatmap/constants.ts
34946
+ var HOUR_MS = 36e5;
34947
+ var DAY_MS = 24 * HOUR_MS;
34948
+
34949
+ // ../types/src/bridges/agents/types.ts
34950
+ var INSTALLABLE_BRIDGE_AGENTS = [
34951
+ { value: "claude-code", label: "Anthropic Claude Code", tokenLabel: "Anthropic API key", tokenEnvVar: "ANTHROPIC_API_KEY" },
34952
+ { value: "codex-acp", label: "Codex", tokenLabel: "OpenAI API key", tokenEnvVar: "OPENAI_API_KEY" },
34953
+ { value: "cursor-cli", label: "Cursor CLI agent", tokenLabel: "Cursor API key", tokenEnvVar: "CURSOR_API_KEY" },
34954
+ { value: "opencode", label: "OpenCode agent", tokenLabel: "Provider API key", tokenEnvVar: "OPENCODE_API_KEY" }
34955
+ ];
34956
+
34905
34957
  // src/paths/session-layout-paths.ts
34906
34958
  import * as path26 from "node:path";
34907
34959
  function resolveIsolatedSessionParentPathFromCheckouts(worktreePaths) {
@@ -34958,7 +35010,10 @@ async function execFileShutdownAware(file2, args, timeoutMs) {
34958
35010
  async function isCommandOnPath(command, timeoutMs = COMMAND_ON_PATH_PROBE_TIMEOUT_MS) {
34959
35011
  if (isCliImmediateShutdownRequested()) return false;
34960
35012
  try {
34961
- await execFileShutdownAware("which", [command], timeoutMs);
35013
+ await execFileAsync5("which", [command], {
35014
+ timeout: timeoutMs,
35015
+ env: bridgeAgentPathEnv()
35016
+ });
34962
35017
  return true;
34963
35018
  } catch {
34964
35019
  return false;
@@ -36673,15 +36728,15 @@ function resolveAgentCommand(preferredAgentType) {
36673
36728
  }
36674
36729
 
36675
36730
  // src/agents/acp/session-file-change-path-kind.ts
36676
- import { execFileSync as execFileSync4 } from "node:child_process";
36731
+ import { execFileSync as execFileSync5 } from "node:child_process";
36677
36732
  import { existsSync as existsSync4, statSync } from "node:fs";
36678
36733
 
36679
36734
  // src/git/get-git-repo-root-sync.ts
36680
- import { execFileSync as execFileSync2 } from "node:child_process";
36735
+ import { execFileSync as execFileSync3 } from "node:child_process";
36681
36736
  import * as path28 from "node:path";
36682
36737
  function getGitRepoRootSync(startDir) {
36683
36738
  try {
36684
- const out = execFileSync2("git", ["rev-parse", "--show-toplevel"], {
36739
+ const out = execFileSync3("git", ["rev-parse", "--show-toplevel"], {
36685
36740
  cwd: path28.resolve(startDir),
36686
36741
  encoding: "utf8",
36687
36742
  stdio: ["ignore", "pipe", "ignore"],
@@ -36694,7 +36749,7 @@ function getGitRepoRootSync(startDir) {
36694
36749
  }
36695
36750
 
36696
36751
  // src/agents/acp/workspace-files.ts
36697
- import { execFileSync as execFileSync3 } from "node:child_process";
36752
+ import { execFileSync as execFileSync4 } from "node:child_process";
36698
36753
  import { readFileSync as readFileSync4 } from "node:fs";
36699
36754
  import * as path29 from "node:path";
36700
36755
  function resolveWorkspaceFilePath(sessionParentPath, rawPath) {
@@ -36755,7 +36810,7 @@ function readGitHeadBlob(sessionParentPath, displayPath) {
36755
36810
  const gitRoot = getGitRepoRootSync(sessionParentPath);
36756
36811
  const execCwd = gitRoot ?? sessionParentPath;
36757
36812
  try {
36758
- return execFileSync3("git", ["show", `HEAD:${displayPath}`], {
36813
+ return execFileSync4("git", ["show", `HEAD:${displayPath}`], {
36759
36814
  cwd: execCwd,
36760
36815
  encoding: "utf8",
36761
36816
  maxBuffer: 50 * 1024 * 1024
@@ -36771,7 +36826,7 @@ function gitHeadPathObjectType(sessionParentPath, displayPath) {
36771
36826
  const gitRoot = getGitRepoRootSync(sessionParentPath);
36772
36827
  if (!gitRoot) return null;
36773
36828
  try {
36774
- return execFileSync4("git", ["cat-file", "-t", `HEAD:${displayPath}`], {
36829
+ return execFileSync5("git", ["cat-file", "-t", `HEAD:${displayPath}`], {
36775
36830
  cwd: gitRoot,
36776
36831
  encoding: "utf8"
36777
36832
  }).trim();
@@ -37626,11 +37681,11 @@ init_cli_process_interrupt();
37626
37681
  var BRIDGE_MCP_SERVER_NAME2 = "buildautomaton-bridge";
37627
37682
 
37628
37683
  // src/mcp/resolve-bridge-mcp-server-path.ts
37629
- import { dirname as dirname7, join as join6 } from "node:path";
37684
+ import { dirname as dirname7, join as join7 } from "node:path";
37630
37685
  import { fileURLToPath as fileURLToPath7 } from "node:url";
37631
37686
  function resolveBridgeMcpServerScriptPath() {
37632
37687
  const cliDir = dirname7(fileURLToPath7(import.meta.url));
37633
- return join6(cliDir, "bridge-mcp-server.js");
37688
+ return join7(cliDir, "bridge-mcp-server.js");
37634
37689
  }
37635
37690
 
37636
37691
  // src/mcp/build-acp-mcp-servers.ts
@@ -51489,6 +51544,7 @@ var API_TO_BRIDGE_MESSAGE_TYPES = [
51489
51544
  "code_nav",
51490
51545
  "skill_layout_request",
51491
51546
  "install_skills",
51547
+ "install_agent",
51492
51548
  "refresh_local_skills",
51493
51549
  "bridge_git_context_request",
51494
51550
  "list_repo_branches_request"
@@ -51564,6 +51620,100 @@ var handleBridgeHeartbeatAck = (msg, deps) => {
51564
51620
  deps.onBridgeHeartbeatAck?.(Math.trunc(raw));
51565
51621
  };
51566
51622
 
51623
+ // src/routing/dispatch/connection.ts
51624
+ function dispatchBridgeConnectionMessage(msg, deps) {
51625
+ switch (msg.type) {
51626
+ case "auth_token":
51627
+ handleAuthToken(msg, deps);
51628
+ break;
51629
+ case "bridge_identified":
51630
+ handleBridgeIdentified(msg, deps);
51631
+ break;
51632
+ case "ha":
51633
+ handleBridgeHeartbeatAck(msg, deps);
51634
+ break;
51635
+ }
51636
+ }
51637
+
51638
+ // src/routing/handlers/preview-environment-control.ts
51639
+ var handlePreviewEnvironmentControl = (msg, deps) => {
51640
+ let wire;
51641
+ try {
51642
+ wire = deps.e2ee ? deps.e2ee.decryptMessage(msg) : msg;
51643
+ } catch (e) {
51644
+ deps.log(`[E2EE] Could not decrypt preview environment command: ${e instanceof Error ? e.message : String(e)}`);
51645
+ return;
51646
+ }
51647
+ const environmentId = typeof wire.environmentId === "string" ? wire.environmentId : "";
51648
+ const action = wire.action === "start" || wire.action === "stop" ? wire.action : null;
51649
+ if (!environmentId || !action) return;
51650
+ deps.previewEnvironmentManager?.handleControl(environmentId, action);
51651
+ };
51652
+
51653
+ // src/routing/handlers/preview-environments-config.ts
51654
+ var VALID_PORT = (p) => typeof p === "number" && Number.isInteger(p) && p > 0 && p < 65536;
51655
+ var handlePreviewEnvironmentsConfig = (msg, deps) => {
51656
+ const previewEnvironments = msg.previewEnvironments;
51657
+ const proxyPorts = Array.isArray(msg.proxyPorts) ? msg.proxyPorts.filter(VALID_PORT) : void 0;
51658
+ setImmediate(() => {
51659
+ deps.previewEnvironmentManager?.applyConfig(previewEnvironments ?? []);
51660
+ const environmentIds = parsePreviewEnvironmentDefs(previewEnvironments ?? []).map((d) => d.environmentId);
51661
+ void deps.previewWorktreeManager?.syncConfiguredEnvironments(environmentIds);
51662
+ if (proxyPorts !== void 0) {
51663
+ deps.updateFirehoseProxyPorts?.(proxyPorts);
51664
+ }
51665
+ });
51666
+ };
51667
+
51668
+ // src/routing/handlers/send-deploy-session-to-preview-result.ts
51669
+ function sendDeploySessionToPreviewResult(ws, id, payload) {
51670
+ if (!ws) return;
51671
+ sendWsMessage(ws, { type: "deploy_session_to_preview_result", id, ...payload });
51672
+ }
51673
+
51674
+ // src/routing/handlers/deploy-session-to-preview.ts
51675
+ var handleDeploySessionToPreviewMessage = (msg, deps) => {
51676
+ if (typeof msg.id !== "string") return;
51677
+ const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
51678
+ const environmentId = typeof msg.environmentId === "string" ? msg.environmentId : "";
51679
+ if (!sessionId || !environmentId) return;
51680
+ void (async () => {
51681
+ const ws = deps.getWs();
51682
+ const reply = (payload) => sendDeploySessionToPreviewResult(ws, msg.id, payload);
51683
+ try {
51684
+ if (!deps.previewWorktreeManager) {
51685
+ reply({ ok: false, error: "Preview worktree manager unavailable" });
51686
+ return;
51687
+ }
51688
+ const result = await deploySessionToPreviewEnvironment({
51689
+ sessionWorktreeManager: deps.sessionWorktreeManager,
51690
+ previewWorktreeManager: deps.previewWorktreeManager,
51691
+ sessionId,
51692
+ environmentId,
51693
+ log: deps.log
51694
+ });
51695
+ reply(result);
51696
+ } catch (e) {
51697
+ reply({ ok: false, error: e instanceof Error ? e.message : String(e) });
51698
+ }
51699
+ })();
51700
+ };
51701
+
51702
+ // src/routing/dispatch/preview.ts
51703
+ function dispatchBridgePreviewMessage(msg, deps) {
51704
+ switch (msg.type) {
51705
+ case "preview_environments_config":
51706
+ handlePreviewEnvironmentsConfig(msg, deps);
51707
+ break;
51708
+ case "preview_environment_control":
51709
+ handlePreviewEnvironmentControl(msg, deps);
51710
+ break;
51711
+ case "deploy_session_to_preview":
51712
+ handleDeploySessionToPreviewMessage(msg, deps);
51713
+ break;
51714
+ }
51715
+ }
51716
+
51567
51717
  // src/agents/acp/from-bridge/handle-bridge-agent-config.ts
51568
51718
  function handleBridgeAgentConfig(msg, { acpManager }) {
51569
51719
  if (!Array.isArray(msg.agents) || msg.agents.length === 0) return;
@@ -52202,6 +52352,308 @@ var handleSessionRequestResponseMessage = (msg, deps) => {
52202
52352
  handleBridgeSessionRequestResponse(msg, deps);
52203
52353
  };
52204
52354
 
52355
+ // src/routing/handlers/git/handle-session-git-commit.ts
52356
+ async function handleSessionGitCommitAction(deps, sessionId, msg, reply) {
52357
+ const branch = typeof msg.branch === "string" ? msg.branch : "";
52358
+ const message = typeof msg.message === "string" ? msg.message : "";
52359
+ const pushAfterCommit = msg.pushAfterCommit === true;
52360
+ if (!branch.trim() || !message.trim()) {
52361
+ reply({ ok: false, error: "branch and message are required for commit" });
52362
+ return;
52363
+ }
52364
+ const commitRes = await deps.sessionWorktreeManager.commitSession({
52365
+ sessionId,
52366
+ branch: branch.trim(),
52367
+ message: message.trim(),
52368
+ push: pushAfterCommit
52369
+ });
52370
+ if (!commitRes.ok) {
52371
+ reply({ ok: false, error: commitRes.error ?? "Commit failed" });
52372
+ return;
52373
+ }
52374
+ const st = await deps.sessionWorktreeManager.getSessionWorkingTreeStatus(sessionId);
52375
+ reply({
52376
+ ok: true,
52377
+ hasUncommittedChanges: st.hasUncommittedChanges,
52378
+ hasUnpushedCommits: st.hasUnpushedCommits,
52379
+ uncommittedFileCount: st.uncommittedFileCount
52380
+ });
52381
+ }
52382
+
52383
+ // src/routing/handlers/git/session-git-changes-params.ts
52384
+ function readString(value) {
52385
+ return typeof value === "string" ? value.trim() : "";
52386
+ }
52387
+ function readChangesObject(msg) {
52388
+ const changes = msg.changes;
52389
+ if (!changes || typeof changes !== "object" || Array.isArray(changes)) return null;
52390
+ return changes;
52391
+ }
52392
+ function parseSessionGitChangesParams(msg) {
52393
+ const nested = readChangesObject(msg);
52394
+ const file2 = nested?.file && typeof nested.file === "object" && !Array.isArray(nested.file) ? nested.file : null;
52395
+ const repoRelPath = readString(nested?.repoRelPath) || readString(msg.changesRepoRelPath);
52396
+ const viewRaw = nested?.view ?? msg.changesView;
52397
+ const view = viewRaw === "commit" ? "commit" : "working";
52398
+ const commitSha = readString(nested?.commitSha) || readString(msg.changesCommitSha);
52399
+ const recentCommitsLimit = nested?.recentCommitsLimit ?? msg.changesRecentCommitsLimit;
52400
+ return {
52401
+ repoRelPath,
52402
+ view,
52403
+ commitSha,
52404
+ recentCommitsLimit: typeof recentCommitsLimit === "number" || typeof recentCommitsLimit === "string" ? recentCommitsLimit : void 0,
52405
+ fileWorkspaceRelPath: readString(file2?.workspaceRelPath),
52406
+ fileChange: readString(file2?.change),
52407
+ fileMovedFromWorkspaceRelPath: readString(file2?.movedFromWorkspaceRelPath)
52408
+ };
52409
+ }
52410
+
52411
+ // src/routing/handlers/git/handle-session-git-get-change-file-patch.ts
52412
+ async function handleSessionGitGetChangeFilePatchAction(deps, msg, reply) {
52413
+ const changes = parseSessionGitChangesParams(msg);
52414
+ const { repoRelPath: repoRel, view, commitSha, fileWorkspaceRelPath, fileChange, fileMovedFromWorkspaceRelPath } = changes;
52415
+ const change = parseWorkingTreeChangeKind(fileChange);
52416
+ if (!repoRel || !fileWorkspaceRelPath || !change) {
52417
+ reply({
52418
+ ok: false,
52419
+ error: "changes.repoRelPath, changes.file.workspaceRelPath, and changes.file.change are required"
52420
+ });
52421
+ return;
52422
+ }
52423
+ if (view === "commit" && !commitSha) {
52424
+ reply({ ok: false, error: "changes.commitSha is required for commit file patch" });
52425
+ return;
52426
+ }
52427
+ const patch = await deps.sessionWorktreeManager.getSessionWorkingTreeChangeFilePatch(msg.sessionId, {
52428
+ repoRelPath: repoRel,
52429
+ workspaceRelPath: fileWorkspaceRelPath,
52430
+ change,
52431
+ movedFromWorkspaceRelPath: fileMovedFromWorkspaceRelPath || null,
52432
+ basis: view === "commit" ? { kind: "commit", sha: commitSha } : { kind: "working" }
52433
+ });
52434
+ reply(
52435
+ {
52436
+ ok: true,
52437
+ patchContent: patch.patchContent ?? null,
52438
+ totalLines: patch.totalLines
52439
+ },
52440
+ ["patchContent"]
52441
+ );
52442
+ }
52443
+
52444
+ // src/routing/handlers/git/coalesce-list-changes.ts
52445
+ var listChangesInflight = /* @__PURE__ */ new Map();
52446
+ function normalizeListChangesRepoPathRelativeToWorkspaceRoot(repoRelPath) {
52447
+ if (!repoRelPath?.trim()) return "";
52448
+ return normalizeRepoPathRelativeToWorkspaceRoot(repoRelPath.trim());
52449
+ }
52450
+ function listChangesInflightKey(sessionId, opts) {
52451
+ const basis = opts?.basis;
52452
+ return [
52453
+ sessionId,
52454
+ normalizeListChangesRepoPathRelativeToWorkspaceRoot(opts?.repoRelPath),
52455
+ basis?.kind === "commit" ? `commit:${basis.sha}` : "working",
52456
+ String(opts?.recentCommitsLimit ?? "")
52457
+ ].join("|");
52458
+ }
52459
+ function getSessionWorkingTreeChangeDetailsCoalesced(sessionWorktreeManager, sessionId, opts) {
52460
+ const key = listChangesInflightKey(sessionId, opts);
52461
+ const existing = listChangesInflight.get(key);
52462
+ if (existing) return existing;
52463
+ const promise2 = sessionWorktreeManager.getSessionWorkingTreeChangeDetails(sessionId, opts).finally(() => {
52464
+ if (listChangesInflight.get(key) === promise2) listChangesInflight.delete(key);
52465
+ });
52466
+ listChangesInflight.set(key, promise2);
52467
+ return promise2;
52468
+ }
52469
+
52470
+ // src/routing/handlers/git/handle-session-git-list-changes.ts
52471
+ async function handleSessionGitListChangesAction(deps, msg, reply) {
52472
+ const changes = parseSessionGitChangesParams(msg);
52473
+ const { repoRelPath: repoRel, view, commitSha, recentCommitsLimit } = changes;
52474
+ if (view === "commit") {
52475
+ if (!repoRel || !commitSha) {
52476
+ reply({
52477
+ ok: false,
52478
+ error: "changes.repoRelPath and changes.commitSha are required for commit view"
52479
+ });
52480
+ return;
52481
+ }
52482
+ }
52483
+ const opts = repoRel && view === "commit" && commitSha ? {
52484
+ repoRelPath: repoRel,
52485
+ basis: { kind: "commit", sha: commitSha },
52486
+ recentCommitsLimit
52487
+ } : repoRel ? { repoRelPath: repoRel, basis: { kind: "working" }, recentCommitsLimit } : recentCommitsLimit !== void 0 ? { recentCommitsLimit } : void 0;
52488
+ const repos = await getSessionWorkingTreeChangeDetailsCoalesced(
52489
+ deps.sessionWorktreeManager,
52490
+ msg.sessionId,
52491
+ opts
52492
+ );
52493
+ reply({ ok: true, repos }, ["repos"]);
52494
+ }
52495
+
52496
+ // src/routing/handlers/git/handle-session-git-push.ts
52497
+ async function handleSessionGitPushAction(deps, sessionId, reply) {
52498
+ const pushRes = await deps.sessionWorktreeManager.pushSessionUpstream(sessionId);
52499
+ if (!pushRes.ok) {
52500
+ reply({ ok: false, error: pushRes.error ?? "Push failed" });
52501
+ return;
52502
+ }
52503
+ const st = await deps.sessionWorktreeManager.getSessionWorkingTreeStatus(sessionId);
52504
+ reply({
52505
+ ok: true,
52506
+ hasUncommittedChanges: st.hasUncommittedChanges,
52507
+ hasUnpushedCommits: st.hasUnpushedCommits,
52508
+ uncommittedFileCount: st.uncommittedFileCount
52509
+ });
52510
+ }
52511
+
52512
+ // src/routing/handlers/git/handle-session-git-status.ts
52513
+ async function handleSessionGitStatusAction(deps, sessionId, reply) {
52514
+ const r = await deps.sessionWorktreeManager.getSessionWorkingTreeStatus(sessionId);
52515
+ reply({
52516
+ ok: true,
52517
+ hasUncommittedChanges: r.hasUncommittedChanges,
52518
+ hasUnpushedCommits: r.hasUnpushedCommits,
52519
+ uncommittedFileCount: r.uncommittedFileCount
52520
+ });
52521
+ }
52522
+
52523
+ // src/routing/handlers/git/send-session-git-result.ts
52524
+ function sendSessionGitResult(ws, id, payload, e2ee, encryptedFields = []) {
52525
+ if (!ws) return;
52526
+ const message = { type: "session_git_result", id, ...payload };
52527
+ let wire = message;
52528
+ if (e2ee && encryptedFields.length > 0) {
52529
+ wire = e2ee.encryptFields(message, encryptedFields);
52530
+ }
52531
+ sendWsMessage(ws, wire);
52532
+ }
52533
+
52534
+ // src/routing/handlers/git/session-git-request.ts
52535
+ var handleSessionGitRequestMessage = (msg, deps) => {
52536
+ if (typeof msg.id !== "string") return;
52537
+ const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
52538
+ const action = msg.action;
52539
+ if (!sessionId || action !== "status" && action !== "push" && action !== "commit" && action !== "list_changes" && action !== "file_diff")
52540
+ return;
52541
+ void (async () => {
52542
+ const ws = deps.getWs();
52543
+ const reply = (payload, encryptedFields = []) => sendSessionGitResult(ws, msg.id, payload, deps.e2ee, encryptedFields);
52544
+ try {
52545
+ if (action === "status") {
52546
+ await handleSessionGitStatusAction(deps, sessionId, reply);
52547
+ return;
52548
+ }
52549
+ if (action === "list_changes") {
52550
+ await handleSessionGitListChangesAction(deps, { ...msg, sessionId }, reply);
52551
+ return;
52552
+ }
52553
+ if (action === "file_diff") {
52554
+ await handleSessionGitGetChangeFilePatchAction(deps, { ...msg, sessionId }, reply);
52555
+ return;
52556
+ }
52557
+ if (action === "push") {
52558
+ await handleSessionGitPushAction(deps, sessionId, reply);
52559
+ return;
52560
+ }
52561
+ await handleSessionGitCommitAction(deps, sessionId, msg, reply);
52562
+ } catch (e) {
52563
+ reply({ ok: false, error: e instanceof Error ? e.message : String(e) });
52564
+ }
52565
+ })();
52566
+ };
52567
+
52568
+ // src/routing/handlers/rename-session-branch.ts
52569
+ var handleRenameSessionBranchMessage = (msg, deps) => {
52570
+ const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
52571
+ const newBranch = typeof msg.newBranch === "string" ? msg.newBranch : "";
52572
+ if (!sessionId || !newBranch) return;
52573
+ void deps.sessionWorktreeManager.renameSessionBranch(sessionId, newBranch);
52574
+ };
52575
+
52576
+ // src/routing/handlers/session-archived.ts
52577
+ var handleSessionArchivedMessage = (msg, deps) => {
52578
+ const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
52579
+ if (!sessionId) return;
52580
+ void deps.sessionWorktreeManager.removeSessionWorktrees(sessionId);
52581
+ };
52582
+
52583
+ // src/routing/handlers/session-discarded.ts
52584
+ var handleSessionDiscardedMessage = (msg, deps) => {
52585
+ const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
52586
+ if (!sessionId) return;
52587
+ void deps.sessionWorktreeManager.removeSessionWorktrees(sessionId);
52588
+ };
52589
+
52590
+ // src/routing/handlers/revert-turn-snapshot.ts
52591
+ import * as fs60 from "node:fs";
52592
+ var handleRevertTurnSnapshotMessage = (msg, deps) => {
52593
+ const id = typeof msg.id === "string" ? msg.id : "";
52594
+ const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
52595
+ const turnId = typeof msg.turnId === "string" ? msg.turnId : "";
52596
+ if (!id || !sessionId || !turnId) return;
52597
+ const { getWs, log: log2, sessionWorktreeManager } = deps;
52598
+ void (async () => {
52599
+ const s = getWs();
52600
+ if (!s) return;
52601
+ const agentBase = sessionWorktreeManager.getSessionWorktreeRootForSession(sessionId) ?? getBridgeRoot();
52602
+ const file2 = snapshotFilePath(agentBase, turnId);
52603
+ try {
52604
+ await fs60.promises.access(file2, fs60.constants.F_OK);
52605
+ } catch {
52606
+ sendWsMessage(s, {
52607
+ type: "revert_turn_snapshot_result",
52608
+ id,
52609
+ ok: false,
52610
+ error: "No snapshot found for this turn (git state may be unavailable)."
52611
+ });
52612
+ return;
52613
+ }
52614
+ const res = await applyPreTurnSnapshot(file2, log2);
52615
+ sendWsMessage(s, {
52616
+ type: "revert_turn_snapshot_result",
52617
+ id,
52618
+ ok: res.ok,
52619
+ ...res.error ? { error: res.error } : {}
52620
+ });
52621
+ })();
52622
+ };
52623
+
52624
+ // src/routing/dispatch/session.ts
52625
+ function dispatchBridgeSessionMessage(msg, deps) {
52626
+ switch (msg.type) {
52627
+ case "agent_config":
52628
+ handleAgentConfigMessage(msg, deps);
52629
+ break;
52630
+ case "prompt_queue_state":
52631
+ handlePromptQueueStateMessage(msg, deps);
52632
+ break;
52633
+ case "prompt":
52634
+ handlePromptMessage(msg, deps);
52635
+ break;
52636
+ case "session_git_request":
52637
+ handleSessionGitRequestMessage(msg, deps);
52638
+ break;
52639
+ case "rename_session_branch":
52640
+ handleRenameSessionBranchMessage(msg, deps);
52641
+ break;
52642
+ case "session_archived":
52643
+ handleSessionArchivedMessage(msg, deps);
52644
+ break;
52645
+ case "session_discarded":
52646
+ handleSessionDiscardedMessage(msg, deps);
52647
+ break;
52648
+ case "revert_turn_snapshot":
52649
+ handleRevertTurnSnapshotMessage(msg, deps);
52650
+ break;
52651
+ case "cursor_request_response":
52652
+ handleSessionRequestResponseMessage(msg, deps);
52653
+ break;
52654
+ }
52655
+ }
52656
+
52205
52657
  // src/skills/preview.ts
52206
52658
  import { spawn as spawn10 } from "node:child_process";
52207
52659
  var PREVIEW_API_BASE_PATH = "/__preview";
@@ -52400,6 +52852,271 @@ var handleSkillCallMessage = (msg, { getWs, log: log2 }) => {
52400
52852
  );
52401
52853
  };
52402
52854
 
52855
+ // src/routing/handlers/skill-layout-request.ts
52856
+ function handleSkillLayoutRequest(msg, deps) {
52857
+ const id = typeof msg.id === "string" ? msg.id : "";
52858
+ void (async () => {
52859
+ const socket = deps.getWs();
52860
+ const roots = await discoverSkillLayoutRootsAsync(getBridgeRoot());
52861
+ if (socket) {
52862
+ sendWsMessage(socket, { type: "skill_layout_response", id, roots });
52863
+ }
52864
+ })();
52865
+ }
52866
+
52867
+ // src/skills/install/is-valid-install-item.ts
52868
+ function isValidRemoteSkillInstallItem(item) {
52869
+ if (item === null || typeof item !== "object") return false;
52870
+ const o = item;
52871
+ return typeof o.sourceId === "string" && typeof o.skillName === "string" && typeof o.versionHash === "string" && Array.isArray(o.files);
52872
+ }
52873
+
52874
+ // src/skills/install/install-remote-skills-async.ts
52875
+ init_yield_to_event_loop();
52876
+ import fs61 from "node:fs";
52877
+ import path80 from "node:path";
52878
+
52879
+ // src/skills/install/constants.ts
52880
+ var INSTALL_SKILLS_YIELD_EVERY = 16;
52881
+
52882
+ // src/skills/install/install-remote-skills-async.ts
52883
+ async function writeInstallFileAsync(skillDir, f, filesWritten) {
52884
+ if (typeof f.path !== "string" || !f.text && !f.base64) return;
52885
+ if (++filesWritten.count % INSTALL_SKILLS_YIELD_EVERY === 0) {
52886
+ await yieldToEventLoop();
52887
+ }
52888
+ const dest = path80.join(skillDir, f.path);
52889
+ await fs61.promises.mkdir(path80.dirname(dest), { recursive: true });
52890
+ if (f.text !== void 0) {
52891
+ await fs61.promises.writeFile(dest, f.text, "utf8");
52892
+ } else if (f.base64) {
52893
+ await fs61.promises.writeFile(dest, Buffer.from(f.base64, "base64"));
52894
+ }
52895
+ }
52896
+ async function installRemoteSkillsAsync(cwd, targetDir, items) {
52897
+ const installed2 = [];
52898
+ if (!Array.isArray(items)) {
52899
+ return { success: false, error: "Invalid items" };
52900
+ }
52901
+ const filesWritten = { count: 0 };
52902
+ try {
52903
+ for (const item of items) {
52904
+ if (!isValidRemoteSkillInstallItem(item)) continue;
52905
+ const skillDir = path80.join(cwd, targetDir, item.skillName);
52906
+ for (const f of item.files) {
52907
+ await writeInstallFileAsync(skillDir, f, filesWritten);
52908
+ }
52909
+ installed2.push({
52910
+ sourceId: item.sourceId,
52911
+ skillName: item.skillName,
52912
+ versionHash: item.versionHash
52913
+ });
52914
+ }
52915
+ return { success: true, installed: installed2 };
52916
+ } catch (e) {
52917
+ return { success: false, error: e instanceof Error ? e.message : String(e) };
52918
+ }
52919
+ }
52920
+
52921
+ // src/routing/handlers/install-skills.ts
52922
+ var handleInstallSkillsMessage = (msg, deps) => {
52923
+ const id = typeof msg.id === "string" ? msg.id : "";
52924
+ const targetDir = typeof msg.targetDir === "string" && msg.targetDir.trim() ? msg.targetDir.trim() : ".agents/skills";
52925
+ const rawItems = msg.items;
52926
+ const cwd = getBridgeRoot();
52927
+ void (async () => {
52928
+ const socket = deps.getWs();
52929
+ const result = await installRemoteSkillsAsync(cwd, targetDir, rawItems);
52930
+ if (!result.success) {
52931
+ const err = result.error ?? "Invalid items";
52932
+ deps.log(`[Bridge service] Install skills failed: ${err}`);
52933
+ if (socket) {
52934
+ sendWsMessage(socket, { type: "install_skills_result", id, success: false, error: err });
52935
+ }
52936
+ return;
52937
+ }
52938
+ if (socket) {
52939
+ sendWsMessage(socket, { type: "install_skills_result", id, success: true, installed: result.installed });
52940
+ }
52941
+ })();
52942
+ };
52943
+
52944
+ // src/agents/install/commands/run-npm-global-install.ts
52945
+ import { execFile as execFile9 } from "node:child_process";
52946
+ import { promisify as promisify10 } from "node:util";
52947
+ var execFileAsync8 = promisify10(execFile9);
52948
+ async function runNpmGlobalInstall(packageName, env, timeoutMs = 3e5) {
52949
+ await execFileAsync8("npm", ["install", "-g", packageName], { timeout: timeoutMs, env: bridgeAgentPathEnv(env) });
52950
+ }
52951
+
52952
+ // src/agents/install/commands/claude-code.ts
52953
+ var claudeCodeInstallCommand = {
52954
+ agentType: "claude-code",
52955
+ detectCommand: "claude",
52956
+ async install(ctx) {
52957
+ ctx.onProgress?.("Installing Anthropic Claude Code");
52958
+ await runNpmGlobalInstall("@anthropic-ai/claude-code", {
52959
+ ...ctx.env,
52960
+ ANTHROPIC_API_KEY: ctx.authToken
52961
+ });
52962
+ }
52963
+ };
52964
+
52965
+ // src/agents/install/commands/codex-acp.ts
52966
+ var codexAcpInstallCommand = {
52967
+ agentType: "codex-acp",
52968
+ detectCommand: "codex",
52969
+ async install(ctx) {
52970
+ ctx.onProgress?.("Installing Codex");
52971
+ await runNpmGlobalInstall("@openai/codex", {
52972
+ ...ctx.env,
52973
+ OPENAI_API_KEY: ctx.authToken
52974
+ });
52975
+ }
52976
+ };
52977
+
52978
+ // src/agents/install/commands/cursor-cli.ts
52979
+ import { execFile as execFile10 } from "node:child_process";
52980
+ import { promisify as promisify11 } from "node:util";
52981
+ var execFileAsync9 = promisify11(execFile10);
52982
+ var cursorCliInstallCommand = {
52983
+ agentType: "cursor-cli",
52984
+ detectCommand: "agent",
52985
+ async install(ctx) {
52986
+ ctx.onProgress?.("Installing Cursor CLI");
52987
+ await execFileAsync9("bash", ["-lc", "curl -fsSL https://cursor.com/install | bash"], {
52988
+ timeout: 3e5,
52989
+ env: { ...bridgeAgentPathEnv(ctx.env), CURSOR_API_KEY: ctx.authToken }
52990
+ });
52991
+ }
52992
+ };
52993
+
52994
+ // src/agents/install/commands/opencode.ts
52995
+ var opencodeInstallCommand = {
52996
+ agentType: "opencode",
52997
+ detectCommand: "opencode",
52998
+ async install(ctx) {
52999
+ ctx.onProgress?.("Installing OpenCode");
53000
+ await runNpmGlobalInstall("opencode-ai", {
53001
+ ...ctx.env,
53002
+ OPENCODE_API_KEY: ctx.authToken
53003
+ });
53004
+ }
53005
+ };
53006
+
53007
+ // src/agents/install/commands/index.ts
53008
+ var COMMANDS = [
53009
+ claudeCodeInstallCommand,
53010
+ codexAcpInstallCommand,
53011
+ cursorCliInstallCommand,
53012
+ opencodeInstallCommand
53013
+ ];
53014
+ var byType = new Map(COMMANDS.map((c) => [c.agentType, c]));
53015
+ function getAgentInstallCommand(agentType) {
53016
+ return byType.get(agentType);
53017
+ }
53018
+
53019
+ // src/agents/install/install-local-agent.ts
53020
+ async function installLocalAgentOnBridge(params) {
53021
+ const spec = INSTALLABLE_BRIDGE_AGENTS.find((a) => a.value === params.agentType);
53022
+ if (!spec) return { success: false, error: `Unsupported agent type: ${params.agentType}` };
53023
+ const command = getAgentInstallCommand(params.agentType);
53024
+ if (!command) return { success: false, error: `No install command for ${params.agentType}` };
53025
+ params.onProgress?.(`Configuring ${spec.label} credentials`);
53026
+ try {
53027
+ await command.install({
53028
+ authToken: params.authToken,
53029
+ onProgress: params.onProgress,
53030
+ env: { ...process.env, [spec.tokenEnvVar]: params.authToken }
53031
+ });
53032
+ } catch (e) {
53033
+ const msg = e instanceof Error ? e.message : String(e);
53034
+ return { success: false, error: msg };
53035
+ }
53036
+ ensureBridgeAgentPathInProcessEnv();
53037
+ const found = await isCommandOnPath(command.detectCommand);
53038
+ if (!found) {
53039
+ return { success: false, error: `${command.detectCommand} not found on PATH after install` };
53040
+ }
53041
+ return { success: true };
53042
+ }
53043
+
53044
+ // src/routing/handlers/install-agent.ts
53045
+ var handleInstallAgentMessage = (msg, deps) => {
53046
+ const processId = typeof msg.processId === "string" ? msg.processId : "";
53047
+ const agentId = typeof msg.agentId === "string" ? msg.agentId : "";
53048
+ const tokenId = typeof msg.tokenId === "string" ? msg.tokenId : "";
53049
+ const agentType = typeof msg.agentType === "string" ? msg.agentType : "";
53050
+ const authToken = typeof msg.authToken === "string" ? msg.authToken : "";
53051
+ if (!processId || !agentId || !tokenId || !agentType || !authToken) return;
53052
+ void (async () => {
53053
+ const socket = deps.getWs();
53054
+ const report = (step, message) => {
53055
+ if (socket) {
53056
+ sendWsMessage(socket, {
53057
+ type: "install_agent_progress",
53058
+ processId,
53059
+ agentId,
53060
+ tokenId,
53061
+ agentType,
53062
+ step,
53063
+ message
53064
+ });
53065
+ }
53066
+ };
53067
+ const result = await installLocalAgentOnBridge({
53068
+ agentType,
53069
+ authToken,
53070
+ onProgress: (message) => report("agent_install_package", message)
53071
+ });
53072
+ if (socket) {
53073
+ sendWsMessage(socket, {
53074
+ type: "install_agent_result",
53075
+ processId,
53076
+ agentId,
53077
+ tokenId,
53078
+ agentType,
53079
+ success: result.success,
53080
+ error: result.error
53081
+ });
53082
+ }
53083
+ if (result.success) {
53084
+ await deps.reportAutoDetectedAgents?.();
53085
+ }
53086
+ if (!result.success) {
53087
+ deps.log(`[Bridge service] Install agent failed: ${result.error ?? "unknown"}`);
53088
+ }
53089
+ })();
53090
+ };
53091
+
53092
+ // src/routing/handlers/refresh-local-skills.ts
53093
+ var handleRefreshLocalSkills = (_msg, deps) => {
53094
+ setImmediate(() => {
53095
+ deps.sendLocalSkillsReport?.();
53096
+ });
53097
+ };
53098
+
53099
+ // src/routing/dispatch/skills.ts
53100
+ function dispatchBridgeSkillsMessage(msg, deps) {
53101
+ switch (msg.type) {
53102
+ case "skill_call":
53103
+ handleSkillCallMessage(msg, deps);
53104
+ break;
53105
+ case "skill_layout_request":
53106
+ handleSkillLayoutRequest(msg, deps);
53107
+ break;
53108
+ case "install_skills":
53109
+ handleInstallSkillsMessage(msg, deps);
53110
+ break;
53111
+ case "install_agent":
53112
+ handleInstallAgentMessage(msg, deps);
53113
+ break;
53114
+ case "refresh_local_skills":
53115
+ handleRefreshLocalSkills(msg, deps);
53116
+ break;
53117
+ }
53118
+ }
53119
+
52403
53120
  // src/files/browser/send-file-browser-message.ts
52404
53121
  function sendFileBrowserMessage(socket, e2ee, payload) {
52405
53122
  sendWsMessage(socket, e2ee ? e2ee.encryptFields(payload, ["entries", "content", "totalLines", "size", "lineOffset"]) : payload);
@@ -52461,13 +53178,13 @@ function handleFileBrowserRequest(msg, socket, e2ee, sessionWorktreeManager) {
52461
53178
 
52462
53179
  // src/files/handle-file-browser-search.ts
52463
53180
  init_yield_to_event_loop();
52464
- import path80 from "node:path";
53181
+ import path81 from "node:path";
52465
53182
  var SEARCH_LIMIT = 100;
52466
53183
  function handleFileBrowserSearch(msg, socket, e2ee, sessionWorktreeManager) {
52467
53184
  void (async () => {
52468
53185
  await yieldToEventLoop();
52469
53186
  const q = typeof msg.q === "string" ? msg.q : "";
52470
- const sessionParentPath = path80.resolve(
53187
+ const sessionParentPath = path81.resolve(
52471
53188
  sessionWorktreeManager != null ? await resolveFileBrowserSessionParent(sessionWorktreeManager, msg.sessionId) : getBridgeRoot()
52472
53189
  );
52473
53190
  if (!await bridgeFileIndexIsPopulated(sessionParentPath)) {
@@ -52564,435 +53281,6 @@ function handleFileBrowserCodeNavMessage(msg, { getWs, e2ee, sessionWorktreeMana
52564
53281
  );
52565
53282
  }
52566
53283
 
52567
- // src/routing/handlers/skill-layout-request.ts
52568
- function handleSkillLayoutRequest(msg, deps) {
52569
- const id = typeof msg.id === "string" ? msg.id : "";
52570
- void (async () => {
52571
- const socket = deps.getWs();
52572
- const roots = await discoverSkillLayoutRootsAsync(getBridgeRoot());
52573
- if (socket) {
52574
- sendWsMessage(socket, { type: "skill_layout_response", id, roots });
52575
- }
52576
- })();
52577
- }
52578
-
52579
- // src/skills/install/is-valid-install-item.ts
52580
- function isValidRemoteSkillInstallItem(item) {
52581
- if (item === null || typeof item !== "object") return false;
52582
- const o = item;
52583
- return typeof o.sourceId === "string" && typeof o.skillName === "string" && typeof o.versionHash === "string" && Array.isArray(o.files);
52584
- }
52585
-
52586
- // src/skills/install/install-remote-skills-async.ts
52587
- init_yield_to_event_loop();
52588
- import fs60 from "node:fs";
52589
- import path81 from "node:path";
52590
-
52591
- // src/skills/install/constants.ts
52592
- var INSTALL_SKILLS_YIELD_EVERY = 16;
52593
-
52594
- // src/skills/install/install-remote-skills-async.ts
52595
- async function writeInstallFileAsync(skillDir, f, filesWritten) {
52596
- if (typeof f.path !== "string" || !f.text && !f.base64) return;
52597
- if (++filesWritten.count % INSTALL_SKILLS_YIELD_EVERY === 0) {
52598
- await yieldToEventLoop();
52599
- }
52600
- const dest = path81.join(skillDir, f.path);
52601
- await fs60.promises.mkdir(path81.dirname(dest), { recursive: true });
52602
- if (f.text !== void 0) {
52603
- await fs60.promises.writeFile(dest, f.text, "utf8");
52604
- } else if (f.base64) {
52605
- await fs60.promises.writeFile(dest, Buffer.from(f.base64, "base64"));
52606
- }
52607
- }
52608
- async function installRemoteSkillsAsync(cwd, targetDir, items) {
52609
- const installed2 = [];
52610
- if (!Array.isArray(items)) {
52611
- return { success: false, error: "Invalid items" };
52612
- }
52613
- const filesWritten = { count: 0 };
52614
- try {
52615
- for (const item of items) {
52616
- if (!isValidRemoteSkillInstallItem(item)) continue;
52617
- const skillDir = path81.join(cwd, targetDir, item.skillName);
52618
- for (const f of item.files) {
52619
- await writeInstallFileAsync(skillDir, f, filesWritten);
52620
- }
52621
- installed2.push({
52622
- sourceId: item.sourceId,
52623
- skillName: item.skillName,
52624
- versionHash: item.versionHash
52625
- });
52626
- }
52627
- return { success: true, installed: installed2 };
52628
- } catch (e) {
52629
- return { success: false, error: e instanceof Error ? e.message : String(e) };
52630
- }
52631
- }
52632
-
52633
- // src/routing/handlers/install-skills.ts
52634
- var handleInstallSkillsMessage = (msg, deps) => {
52635
- const id = typeof msg.id === "string" ? msg.id : "";
52636
- const targetDir = typeof msg.targetDir === "string" && msg.targetDir.trim() ? msg.targetDir.trim() : ".agents/skills";
52637
- const rawItems = msg.items;
52638
- const cwd = getBridgeRoot();
52639
- void (async () => {
52640
- const socket = deps.getWs();
52641
- const result = await installRemoteSkillsAsync(cwd, targetDir, rawItems);
52642
- if (!result.success) {
52643
- const err = result.error ?? "Invalid items";
52644
- deps.log(`[Bridge service] Install skills failed: ${err}`);
52645
- if (socket) {
52646
- sendWsMessage(socket, { type: "install_skills_result", id, success: false, error: err });
52647
- }
52648
- return;
52649
- }
52650
- if (socket) {
52651
- sendWsMessage(socket, { type: "install_skills_result", id, success: true, installed: result.installed });
52652
- }
52653
- })();
52654
- };
52655
-
52656
- // src/routing/handlers/refresh-local-skills.ts
52657
- var handleRefreshLocalSkills = (_msg, deps) => {
52658
- setImmediate(() => {
52659
- deps.sendLocalSkillsReport?.();
52660
- });
52661
- };
52662
-
52663
- // src/routing/handlers/git/handle-session-git-commit.ts
52664
- async function handleSessionGitCommitAction(deps, sessionId, msg, reply) {
52665
- const branch = typeof msg.branch === "string" ? msg.branch : "";
52666
- const message = typeof msg.message === "string" ? msg.message : "";
52667
- const pushAfterCommit = msg.pushAfterCommit === true;
52668
- if (!branch.trim() || !message.trim()) {
52669
- reply({ ok: false, error: "branch and message are required for commit" });
52670
- return;
52671
- }
52672
- const commitRes = await deps.sessionWorktreeManager.commitSession({
52673
- sessionId,
52674
- branch: branch.trim(),
52675
- message: message.trim(),
52676
- push: pushAfterCommit
52677
- });
52678
- if (!commitRes.ok) {
52679
- reply({ ok: false, error: commitRes.error ?? "Commit failed" });
52680
- return;
52681
- }
52682
- const st = await deps.sessionWorktreeManager.getSessionWorkingTreeStatus(sessionId);
52683
- reply({
52684
- ok: true,
52685
- hasUncommittedChanges: st.hasUncommittedChanges,
52686
- hasUnpushedCommits: st.hasUnpushedCommits,
52687
- uncommittedFileCount: st.uncommittedFileCount
52688
- });
52689
- }
52690
-
52691
- // src/routing/handlers/git/session-git-changes-params.ts
52692
- function readString(value) {
52693
- return typeof value === "string" ? value.trim() : "";
52694
- }
52695
- function readChangesObject(msg) {
52696
- const changes = msg.changes;
52697
- if (!changes || typeof changes !== "object" || Array.isArray(changes)) return null;
52698
- return changes;
52699
- }
52700
- function parseSessionGitChangesParams(msg) {
52701
- const nested = readChangesObject(msg);
52702
- const file2 = nested?.file && typeof nested.file === "object" && !Array.isArray(nested.file) ? nested.file : null;
52703
- const repoRelPath = readString(nested?.repoRelPath) || readString(msg.changesRepoRelPath);
52704
- const viewRaw = nested?.view ?? msg.changesView;
52705
- const view = viewRaw === "commit" ? "commit" : "working";
52706
- const commitSha = readString(nested?.commitSha) || readString(msg.changesCommitSha);
52707
- const recentCommitsLimit = nested?.recentCommitsLimit ?? msg.changesRecentCommitsLimit;
52708
- return {
52709
- repoRelPath,
52710
- view,
52711
- commitSha,
52712
- recentCommitsLimit: typeof recentCommitsLimit === "number" || typeof recentCommitsLimit === "string" ? recentCommitsLimit : void 0,
52713
- fileWorkspaceRelPath: readString(file2?.workspaceRelPath),
52714
- fileChange: readString(file2?.change),
52715
- fileMovedFromWorkspaceRelPath: readString(file2?.movedFromWorkspaceRelPath)
52716
- };
52717
- }
52718
-
52719
- // src/routing/handlers/git/handle-session-git-get-change-file-patch.ts
52720
- async function handleSessionGitGetChangeFilePatchAction(deps, msg, reply) {
52721
- const changes = parseSessionGitChangesParams(msg);
52722
- const { repoRelPath: repoRel, view, commitSha, fileWorkspaceRelPath, fileChange, fileMovedFromWorkspaceRelPath } = changes;
52723
- const change = parseWorkingTreeChangeKind(fileChange);
52724
- if (!repoRel || !fileWorkspaceRelPath || !change) {
52725
- reply({
52726
- ok: false,
52727
- error: "changes.repoRelPath, changes.file.workspaceRelPath, and changes.file.change are required"
52728
- });
52729
- return;
52730
- }
52731
- if (view === "commit" && !commitSha) {
52732
- reply({ ok: false, error: "changes.commitSha is required for commit file patch" });
52733
- return;
52734
- }
52735
- const patch = await deps.sessionWorktreeManager.getSessionWorkingTreeChangeFilePatch(msg.sessionId, {
52736
- repoRelPath: repoRel,
52737
- workspaceRelPath: fileWorkspaceRelPath,
52738
- change,
52739
- movedFromWorkspaceRelPath: fileMovedFromWorkspaceRelPath || null,
52740
- basis: view === "commit" ? { kind: "commit", sha: commitSha } : { kind: "working" }
52741
- });
52742
- reply(
52743
- {
52744
- ok: true,
52745
- patchContent: patch.patchContent ?? null,
52746
- totalLines: patch.totalLines
52747
- },
52748
- ["patchContent"]
52749
- );
52750
- }
52751
-
52752
- // src/routing/handlers/git/coalesce-list-changes.ts
52753
- var listChangesInflight = /* @__PURE__ */ new Map();
52754
- function normalizeListChangesRepoPathRelativeToWorkspaceRoot(repoRelPath) {
52755
- if (!repoRelPath?.trim()) return "";
52756
- return normalizeRepoPathRelativeToWorkspaceRoot(repoRelPath.trim());
52757
- }
52758
- function listChangesInflightKey(sessionId, opts) {
52759
- const basis = opts?.basis;
52760
- return [
52761
- sessionId,
52762
- normalizeListChangesRepoPathRelativeToWorkspaceRoot(opts?.repoRelPath),
52763
- basis?.kind === "commit" ? `commit:${basis.sha}` : "working",
52764
- String(opts?.recentCommitsLimit ?? "")
52765
- ].join("|");
52766
- }
52767
- function getSessionWorkingTreeChangeDetailsCoalesced(sessionWorktreeManager, sessionId, opts) {
52768
- const key = listChangesInflightKey(sessionId, opts);
52769
- const existing = listChangesInflight.get(key);
52770
- if (existing) return existing;
52771
- const promise2 = sessionWorktreeManager.getSessionWorkingTreeChangeDetails(sessionId, opts).finally(() => {
52772
- if (listChangesInflight.get(key) === promise2) listChangesInflight.delete(key);
52773
- });
52774
- listChangesInflight.set(key, promise2);
52775
- return promise2;
52776
- }
52777
-
52778
- // src/routing/handlers/git/handle-session-git-list-changes.ts
52779
- async function handleSessionGitListChangesAction(deps, msg, reply) {
52780
- const changes = parseSessionGitChangesParams(msg);
52781
- const { repoRelPath: repoRel, view, commitSha, recentCommitsLimit } = changes;
52782
- if (view === "commit") {
52783
- if (!repoRel || !commitSha) {
52784
- reply({
52785
- ok: false,
52786
- error: "changes.repoRelPath and changes.commitSha are required for commit view"
52787
- });
52788
- return;
52789
- }
52790
- }
52791
- const opts = repoRel && view === "commit" && commitSha ? {
52792
- repoRelPath: repoRel,
52793
- basis: { kind: "commit", sha: commitSha },
52794
- recentCommitsLimit
52795
- } : repoRel ? { repoRelPath: repoRel, basis: { kind: "working" }, recentCommitsLimit } : recentCommitsLimit !== void 0 ? { recentCommitsLimit } : void 0;
52796
- const repos = await getSessionWorkingTreeChangeDetailsCoalesced(
52797
- deps.sessionWorktreeManager,
52798
- msg.sessionId,
52799
- opts
52800
- );
52801
- reply({ ok: true, repos }, ["repos"]);
52802
- }
52803
-
52804
- // src/routing/handlers/git/handle-session-git-push.ts
52805
- async function handleSessionGitPushAction(deps, sessionId, reply) {
52806
- const pushRes = await deps.sessionWorktreeManager.pushSessionUpstream(sessionId);
52807
- if (!pushRes.ok) {
52808
- reply({ ok: false, error: pushRes.error ?? "Push failed" });
52809
- return;
52810
- }
52811
- const st = await deps.sessionWorktreeManager.getSessionWorkingTreeStatus(sessionId);
52812
- reply({
52813
- ok: true,
52814
- hasUncommittedChanges: st.hasUncommittedChanges,
52815
- hasUnpushedCommits: st.hasUnpushedCommits,
52816
- uncommittedFileCount: st.uncommittedFileCount
52817
- });
52818
- }
52819
-
52820
- // src/routing/handlers/git/handle-session-git-status.ts
52821
- async function handleSessionGitStatusAction(deps, sessionId, reply) {
52822
- const r = await deps.sessionWorktreeManager.getSessionWorkingTreeStatus(sessionId);
52823
- reply({
52824
- ok: true,
52825
- hasUncommittedChanges: r.hasUncommittedChanges,
52826
- hasUnpushedCommits: r.hasUnpushedCommits,
52827
- uncommittedFileCount: r.uncommittedFileCount
52828
- });
52829
- }
52830
-
52831
- // src/routing/handlers/git/send-session-git-result.ts
52832
- function sendSessionGitResult(ws, id, payload, e2ee, encryptedFields = []) {
52833
- if (!ws) return;
52834
- const message = { type: "session_git_result", id, ...payload };
52835
- let wire = message;
52836
- if (e2ee && encryptedFields.length > 0) {
52837
- wire = e2ee.encryptFields(message, encryptedFields);
52838
- }
52839
- sendWsMessage(ws, wire);
52840
- }
52841
-
52842
- // src/routing/handlers/git/session-git-request.ts
52843
- var handleSessionGitRequestMessage = (msg, deps) => {
52844
- if (typeof msg.id !== "string") return;
52845
- const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
52846
- const action = msg.action;
52847
- if (!sessionId || action !== "status" && action !== "push" && action !== "commit" && action !== "list_changes" && action !== "file_diff")
52848
- return;
52849
- void (async () => {
52850
- const ws = deps.getWs();
52851
- const reply = (payload, encryptedFields = []) => sendSessionGitResult(ws, msg.id, payload, deps.e2ee, encryptedFields);
52852
- try {
52853
- if (action === "status") {
52854
- await handleSessionGitStatusAction(deps, sessionId, reply);
52855
- return;
52856
- }
52857
- if (action === "list_changes") {
52858
- await handleSessionGitListChangesAction(deps, { ...msg, sessionId }, reply);
52859
- return;
52860
- }
52861
- if (action === "file_diff") {
52862
- await handleSessionGitGetChangeFilePatchAction(deps, { ...msg, sessionId }, reply);
52863
- return;
52864
- }
52865
- if (action === "push") {
52866
- await handleSessionGitPushAction(deps, sessionId, reply);
52867
- return;
52868
- }
52869
- await handleSessionGitCommitAction(deps, sessionId, msg, reply);
52870
- } catch (e) {
52871
- reply({ ok: false, error: e instanceof Error ? e.message : String(e) });
52872
- }
52873
- })();
52874
- };
52875
-
52876
- // src/routing/handlers/rename-session-branch.ts
52877
- var handleRenameSessionBranchMessage = (msg, deps) => {
52878
- const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
52879
- const newBranch = typeof msg.newBranch === "string" ? msg.newBranch : "";
52880
- if (!sessionId || !newBranch) return;
52881
- void deps.sessionWorktreeManager.renameSessionBranch(sessionId, newBranch);
52882
- };
52883
-
52884
- // src/routing/handlers/session-archived.ts
52885
- var handleSessionArchivedMessage = (msg, deps) => {
52886
- const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
52887
- if (!sessionId) return;
52888
- void deps.sessionWorktreeManager.removeSessionWorktrees(sessionId);
52889
- };
52890
-
52891
- // src/routing/handlers/session-discarded.ts
52892
- var handleSessionDiscardedMessage = (msg, deps) => {
52893
- const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
52894
- if (!sessionId) return;
52895
- void deps.sessionWorktreeManager.removeSessionWorktrees(sessionId);
52896
- };
52897
-
52898
- // src/routing/handlers/revert-turn-snapshot.ts
52899
- import * as fs61 from "node:fs";
52900
- var handleRevertTurnSnapshotMessage = (msg, deps) => {
52901
- const id = typeof msg.id === "string" ? msg.id : "";
52902
- const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
52903
- const turnId = typeof msg.turnId === "string" ? msg.turnId : "";
52904
- if (!id || !sessionId || !turnId) return;
52905
- const { getWs, log: log2, sessionWorktreeManager } = deps;
52906
- void (async () => {
52907
- const s = getWs();
52908
- if (!s) return;
52909
- const agentBase = sessionWorktreeManager.getSessionWorktreeRootForSession(sessionId) ?? getBridgeRoot();
52910
- const file2 = snapshotFilePath(agentBase, turnId);
52911
- try {
52912
- await fs61.promises.access(file2, fs61.constants.F_OK);
52913
- } catch {
52914
- sendWsMessage(s, {
52915
- type: "revert_turn_snapshot_result",
52916
- id,
52917
- ok: false,
52918
- error: "No snapshot found for this turn (git state may be unavailable)."
52919
- });
52920
- return;
52921
- }
52922
- const res = await applyPreTurnSnapshot(file2, log2);
52923
- sendWsMessage(s, {
52924
- type: "revert_turn_snapshot_result",
52925
- id,
52926
- ok: res.ok,
52927
- ...res.error ? { error: res.error } : {}
52928
- });
52929
- })();
52930
- };
52931
-
52932
- // src/routing/handlers/preview-environment-control.ts
52933
- var handlePreviewEnvironmentControl = (msg, deps) => {
52934
- let wire;
52935
- try {
52936
- wire = deps.e2ee ? deps.e2ee.decryptMessage(msg) : msg;
52937
- } catch (e) {
52938
- deps.log(`[E2EE] Could not decrypt preview environment command: ${e instanceof Error ? e.message : String(e)}`);
52939
- return;
52940
- }
52941
- const environmentId = typeof wire.environmentId === "string" ? wire.environmentId : "";
52942
- const action = wire.action === "start" || wire.action === "stop" ? wire.action : null;
52943
- if (!environmentId || !action) return;
52944
- deps.previewEnvironmentManager?.handleControl(environmentId, action);
52945
- };
52946
-
52947
- // src/routing/handlers/preview-environments-config.ts
52948
- var VALID_PORT = (p) => typeof p === "number" && Number.isInteger(p) && p > 0 && p < 65536;
52949
- var handlePreviewEnvironmentsConfig = (msg, deps) => {
52950
- const previewEnvironments = msg.previewEnvironments;
52951
- const proxyPorts = Array.isArray(msg.proxyPorts) ? msg.proxyPorts.filter(VALID_PORT) : void 0;
52952
- setImmediate(() => {
52953
- deps.previewEnvironmentManager?.applyConfig(previewEnvironments ?? []);
52954
- const environmentIds = parsePreviewEnvironmentDefs(previewEnvironments ?? []).map((d) => d.environmentId);
52955
- void deps.previewWorktreeManager?.syncConfiguredEnvironments(environmentIds);
52956
- if (proxyPorts !== void 0) {
52957
- deps.updateFirehoseProxyPorts?.(proxyPorts);
52958
- }
52959
- });
52960
- };
52961
-
52962
- // src/routing/handlers/send-deploy-session-to-preview-result.ts
52963
- function sendDeploySessionToPreviewResult(ws, id, payload) {
52964
- if (!ws) return;
52965
- sendWsMessage(ws, { type: "deploy_session_to_preview_result", id, ...payload });
52966
- }
52967
-
52968
- // src/routing/handlers/deploy-session-to-preview.ts
52969
- var handleDeploySessionToPreviewMessage = (msg, deps) => {
52970
- if (typeof msg.id !== "string") return;
52971
- const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
52972
- const environmentId = typeof msg.environmentId === "string" ? msg.environmentId : "";
52973
- if (!sessionId || !environmentId) return;
52974
- void (async () => {
52975
- const ws = deps.getWs();
52976
- const reply = (payload) => sendDeploySessionToPreviewResult(ws, msg.id, payload);
52977
- try {
52978
- if (!deps.previewWorktreeManager) {
52979
- reply({ ok: false, error: "Preview worktree manager unavailable" });
52980
- return;
52981
- }
52982
- const result = await deploySessionToPreviewEnvironment({
52983
- sessionWorktreeManager: deps.sessionWorktreeManager,
52984
- previewWorktreeManager: deps.previewWorktreeManager,
52985
- sessionId,
52986
- environmentId,
52987
- log: deps.log
52988
- });
52989
- reply(result);
52990
- } catch (e) {
52991
- reply({ ok: false, error: e instanceof Error ? e.message : String(e) });
52992
- }
52993
- })();
52994
- };
52995
-
52996
53284
  // src/git/bridge-git-context.ts
52997
53285
  import * as path82 from "node:path";
52998
53286
  init_yield_to_event_loop();
@@ -53138,80 +53426,64 @@ function handleListRepoBranchesRequestMessage(msg, getWs) {
53138
53426
  })();
53139
53427
  }
53140
53428
 
53141
- // src/routing/dispatch-bridge-message.ts
53429
+ // src/routing/dispatch/file-browser.ts
53430
+ function dispatchBridgeFileBrowserMessage(msg, deps) {
53431
+ switch (msg.type) {
53432
+ case "file_browser_request":
53433
+ handleFileBrowserRequestMessage(msg, deps);
53434
+ break;
53435
+ case "file_browser_search":
53436
+ handleFileBrowserSearchMessage(msg, deps);
53437
+ break;
53438
+ case "code_nav":
53439
+ handleFileBrowserCodeNavMessage(msg, deps);
53440
+ break;
53441
+ case "bridge_git_context_request":
53442
+ handleBridgeGitContextRequestMessage(msg, deps.getWs);
53443
+ break;
53444
+ case "list_repo_branches_request":
53445
+ handleListRepoBranchesRequestMessage(msg, deps.getWs);
53446
+ break;
53447
+ }
53448
+ }
53449
+
53450
+ // src/routing/dispatch/index.ts
53142
53451
  function dispatchBridgeMessage(msg, deps) {
53143
53452
  switch (msg.type) {
53144
53453
  case "auth_token":
53145
- handleAuthToken(msg, deps);
53146
- break;
53147
53454
  case "bridge_identified":
53148
- handleBridgeIdentified(msg, deps);
53149
- break;
53150
53455
  case "ha":
53151
- handleBridgeHeartbeatAck(msg, deps);
53456
+ dispatchBridgeConnectionMessage(msg, deps);
53152
53457
  break;
53153
53458
  case "preview_environments_config":
53154
- handlePreviewEnvironmentsConfig(msg, deps);
53155
- break;
53156
53459
  case "preview_environment_control":
53157
- handlePreviewEnvironmentControl(msg, deps);
53158
- break;
53159
53460
  case "deploy_session_to_preview":
53160
- handleDeploySessionToPreviewMessage(msg, deps);
53461
+ dispatchBridgePreviewMessage(msg, deps);
53161
53462
  break;
53162
53463
  case "agent_config":
53163
- handleAgentConfigMessage(msg, deps);
53164
- break;
53165
53464
  case "prompt_queue_state":
53166
- handlePromptQueueStateMessage(msg, deps);
53167
- break;
53168
53465
  case "prompt":
53169
- handlePromptMessage(msg, deps);
53170
- break;
53171
53466
  case "session_git_request":
53172
- handleSessionGitRequestMessage(msg, deps);
53173
- break;
53174
53467
  case "rename_session_branch":
53175
- handleRenameSessionBranchMessage(msg, deps);
53176
- break;
53177
53468
  case "session_archived":
53178
- handleSessionArchivedMessage(msg, deps);
53179
- break;
53180
53469
  case "session_discarded":
53181
- handleSessionDiscardedMessage(msg, deps);
53182
- break;
53183
53470
  case "revert_turn_snapshot":
53184
- handleRevertTurnSnapshotMessage(msg, deps);
53185
- break;
53186
53471
  case "cursor_request_response":
53187
- handleSessionRequestResponseMessage(msg, deps);
53472
+ dispatchBridgeSessionMessage(msg, deps);
53188
53473
  break;
53189
53474
  case "skill_call":
53190
- handleSkillCallMessage(msg, deps);
53191
- break;
53192
- case "file_browser_request":
53193
- handleFileBrowserRequestMessage(msg, deps);
53194
- break;
53195
- case "file_browser_search":
53196
- handleFileBrowserSearchMessage(msg, deps);
53197
- break;
53198
- case "code_nav":
53199
- handleFileBrowserCodeNavMessage(msg, deps);
53200
- break;
53201
53475
  case "skill_layout_request":
53202
- handleSkillLayoutRequest(msg, deps);
53203
- break;
53204
53476
  case "install_skills":
53205
- handleInstallSkillsMessage(msg, deps);
53206
- break;
53477
+ case "install_agent":
53207
53478
  case "refresh_local_skills":
53208
- handleRefreshLocalSkills(msg, deps);
53479
+ dispatchBridgeSkillsMessage(msg, deps);
53209
53480
  break;
53481
+ case "file_browser_request":
53482
+ case "file_browser_search":
53483
+ case "code_nav":
53210
53484
  case "bridge_git_context_request":
53211
- handleBridgeGitContextRequestMessage(msg, deps.getWs);
53212
- break;
53213
53485
  case "list_repo_branches_request":
53214
- handleListRepoBranchesRequestMessage(msg, deps.getWs);
53486
+ dispatchBridgeFileBrowserMessage(msg, deps);
53215
53487
  break;
53216
53488
  }
53217
53489
  }
@@ -53496,6 +53768,7 @@ init_log();
53496
53768
  async function createBridgeConnection(options) {
53497
53769
  const { apiUrl, workspaceId, justAuthenticated, onAuthInvalid, persistTokens } = options;
53498
53770
  const logFn = options.log ?? log;
53771
+ ensureBridgeAgentPathInProcessEnv();
53499
53772
  await ensureBridgeConnectionDatabases(logFn);
53500
53773
  const { state, getWs } = createMainBridgeReconnectState();
53501
53774
  const runtime = await createBridgeConnectionRuntime(options, { state, getWs, logFn });