cogpit 2.1.2 → 2.2.1

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.
Files changed (29) hide show
  1. package/dist/cli.js +947 -425
  2. package/dist/web/assets/CommandPaletteHost--_g3tlgk.js +1 -0
  3. package/dist/web/assets/ConfigBrowser-DrAZQBx8.js +1 -0
  4. package/dist/web/assets/{HighlightedEditor-BYfv1SYX.js → HighlightedEditor-Byq8pOUC.js} +1 -1
  5. package/dist/web/assets/{KeyboardShortcutsDialog-YDYAOpa5.js → KeyboardShortcutsDialog-CFhdeGBw.js} +1 -1
  6. package/dist/web/assets/{MobileFileChanges-Bb3zEFSH.js → MobileFileChanges-uxpxh1W6.js} +1 -1
  7. package/dist/web/assets/{PreviewPanel-B9CQNmuh.js → PreviewPanel-DC6OTZfZ.js} +1 -1
  8. package/dist/web/assets/ProjectFilesPanel-ByyaiSGy.js +4 -0
  9. package/dist/web/assets/{ProjectSwitcherModal-CxbGrX7A.js → ProjectSwitcherModal-BRbFj3Dd.js} +1 -1
  10. package/dist/web/assets/{TerminalOutput-LwRcBZWA.js → TerminalOutput-Aavw8QCx.js} +1 -1
  11. package/dist/web/assets/{ThemeSelectorModal-BG_Jah0i.js → ThemeSelectorModal-CxGMhlkF.js} +1 -1
  12. package/dist/web/assets/WorkflowsPanel-Beg1AAtq.js +1 -0
  13. package/dist/web/assets/{WorktreePanel-gR-fCesa.js → WorktreePanel-B17_SlFC.js} +1 -1
  14. package/dist/web/assets/{index-EmT8Vg0x.js → index-C-5Kgwol.js} +4 -4
  15. package/dist/web/assets/{index-BaKcHvT2.js → index-CJcccHRp.js} +1 -1
  16. package/dist/web/assets/index-CTpAqQye.js +1 -0
  17. package/dist/web/assets/index-DB1wEO_I.js +148 -0
  18. package/dist/web/assets/index-DU9XxwBy.css +1 -0
  19. package/dist/web/assets/{session-parser.worker-DycJL0jJ.js → session-parser.worker-RMitC4X_.js} +17 -17
  20. package/dist/web/assets/{vendor-ui-Bg9q4lA4.js → vendor-ui-CyII6N5s.js} +152 -147
  21. package/dist/web/index.html +3 -3
  22. package/package.json +1 -1
  23. package/dist/web/assets/CommandPaletteHost-Bq0_FFr0.js +0 -1
  24. package/dist/web/assets/ConfigBrowser-DmOlS3cU.js +0 -1
  25. package/dist/web/assets/ProjectFilesPanel-C3jKD37-.js +0 -4
  26. package/dist/web/assets/WorkflowsPanel-CiZjrfSd.js +0 -1
  27. package/dist/web/assets/index-Bif4Kdtn.css +0 -1
  28. package/dist/web/assets/index-C_pb6rZe.js +0 -1
  29. package/dist/web/assets/index-D-Mg0tNr.js +0 -144
package/dist/cli.js CHANGED
@@ -486,8 +486,8 @@ async function mint(device, generation2) {
486
486
  if (prev?.generation === generation2 && prev.error && Date.now() - prev.at < MINT_COOLDOWN_MS) {
487
487
  throw prev.error;
488
488
  }
489
- const record2 = { at: Date.now(), generation: generation2 };
490
- lastAttempt.set(id, record2);
489
+ const record3 = { at: Date.now(), generation: generation2 };
490
+ lastAttempt.set(id, record3);
491
491
  const url = `${device.tls ? "https" : "http"}://${device.host}:${device.port}/api/auth/verify`;
492
492
  const credential = device.username ? `${device.username}:${device.password ?? ""}` : device.password ?? "";
493
493
  let res;
@@ -503,7 +503,7 @@ async function mint(device, generation2) {
503
503
  `Could not reach device "${device.name}" at ${device.host}:${device.port}`,
504
504
  { cause: err }
505
505
  );
506
- record2.error = error;
506
+ record3.error = error;
507
507
  if (generationIsCurrent(id, generation2)) {
508
508
  setDeviceRuntime(id, { lastProbe: Date.now() });
509
509
  }
@@ -511,7 +511,7 @@ async function mint(device, generation2) {
511
511
  }
512
512
  if (res.status === 401 || res.status === 403) {
513
513
  const error = new DeviceAuthError(id, `Device "${device.name}" rejected the password`, res.status);
514
- record2.error = error;
514
+ record3.error = error;
515
515
  if (generationIsCurrent(id, generation2)) {
516
516
  setDeviceRuntime(id, { authState: "bad-password", lastProbe: Date.now() });
517
517
  }
@@ -522,7 +522,7 @@ async function mint(device, generation2) {
522
522
  id,
523
523
  `Device "${device.name}" returned HTTP ${res.status} while minting a token`
524
524
  );
525
- record2.error = error;
525
+ record3.error = error;
526
526
  if (generationIsCurrent(id, generation2)) {
527
527
  setDeviceRuntime(id, { lastProbe: Date.now() });
528
528
  }
@@ -536,7 +536,7 @@ async function mint(device, generation2) {
536
536
  }
537
537
  if (!body || body.valid === false || typeof body.token !== "string" || !body.token) {
538
538
  const error = new DeviceAuthError(id, `Device "${device.name}" did not return a valid token`, res.status);
539
- record2.error = error;
539
+ record3.error = error;
540
540
  if (generationIsCurrent(id, generation2)) {
541
541
  setDeviceRuntime(id, { authState: "bad-password", lastProbe: Date.now() });
542
542
  }
@@ -779,6 +779,7 @@ var init_agent_descriptors = __esm({
779
779
  args: ["update"],
780
780
  matches: (path) => path.endsWith("/.local/bin/claude") || path.endsWith("/.local/bin/claude.exe") || path.includes("/.local/share/claude/")
781
781
  },
782
+ bundledBySdk: true,
782
783
  homeDirName: ".claude",
783
784
  homeEnvVar: null,
784
785
  installMarker: "projects",
@@ -915,6 +916,7 @@ var init_agent_descriptors = __esm({
915
916
  // Codex ships no self-updater; a hand-placed binary has to be replaced by
916
917
  // whatever put it there.
917
918
  selfUpdate: null,
919
+ bundledBySdk: false,
918
920
  homeDirName: ".codex",
919
921
  homeEnvVar: "CODEX_HOME",
920
922
  installMarker: "",
@@ -1025,6 +1027,7 @@ var init_agent_descriptors = __esm({
1025
1027
  // only do because the caller passes the realpath in as well.
1026
1028
  matches: (path) => path.endsWith("/.local/bin/copilot") || path === "/usr/local/bin/copilot"
1027
1029
  },
1030
+ bundledBySdk: false,
1028
1031
  homeDirName: ".copilot",
1029
1032
  homeEnvVar: "COPILOT_HOME",
1030
1033
  installMarker: "session-state",
@@ -1236,6 +1239,9 @@ function hasExtension(binName, extensions) {
1236
1239
  function hasExecutableExtension(fileName, env = process.env) {
1237
1240
  return hasExtension(fileName, pathExtensions({ env }));
1238
1241
  }
1242
+ function isDirectlyLaunchable(fileName) {
1243
+ return DIRECT_EXTENSIONS.includes(win32.extname(fileName).toLowerCase());
1244
+ }
1239
1245
  function findExecutableOnPath(binName, options = {}) {
1240
1246
  const windows = isWindows(options);
1241
1247
  const path = windows ? win32 : posix;
@@ -1280,7 +1286,7 @@ function resolveAgentCommand(binName, args = [], options = {}) {
1280
1286
  if (!isWindows(options)) return { command: binName, args, spawnOptions: {} };
1281
1287
  const resolved = /[\\/]/.test(binName) ? binName : findExecutableOnPath(binName, options);
1282
1288
  if (!resolved) return { command: binName, args, spawnOptions: {} };
1283
- if (DIRECT_EXTENSIONS.includes(win32.extname(resolved).toLowerCase())) {
1289
+ if (isDirectlyLaunchable(resolved)) {
1284
1290
  return { command: resolved, args, spawnOptions: {} };
1285
1291
  }
1286
1292
  return throughCmdExe(resolved, args, options);
@@ -1297,6 +1303,38 @@ var init_binaryResolver = __esm({
1297
1303
  }
1298
1304
  });
1299
1305
 
1306
+ // ../../shared/contracts/agentExecutable.ts
1307
+ function isExecutableSource(value) {
1308
+ return EXECUTABLE_SOURCES.includes(value);
1309
+ }
1310
+ function parseExecutableChoice(value) {
1311
+ if (!value || typeof value !== "object") return void 0;
1312
+ const { source, path } = value;
1313
+ if (!isExecutableSource(source) || source === "auto") return void 0;
1314
+ if (source === "custom") {
1315
+ return typeof path === "string" && path.trim() ? { source, path: path.trim() } : void 0;
1316
+ }
1317
+ return { source };
1318
+ }
1319
+ var EXECUTABLE_SOURCES, DEFAULT_EXECUTABLE_CHOICE, DETECTED_EXECUTABLE_SOURCES;
1320
+ var init_agentExecutable = __esm({
1321
+ "../../shared/contracts/agentExecutable.ts"() {
1322
+ EXECUTABLE_SOURCES = [
1323
+ "auto",
1324
+ "path",
1325
+ "npm",
1326
+ "bundled",
1327
+ "custom"
1328
+ ];
1329
+ DEFAULT_EXECUTABLE_CHOICE = { source: "auto" };
1330
+ DETECTED_EXECUTABLE_SOURCES = [
1331
+ "path",
1332
+ "npm",
1333
+ "bundled"
1334
+ ];
1335
+ }
1336
+ });
1337
+
1300
1338
  // ../../server/config.ts
1301
1339
  import { readFile as readFile2, stat, readdir, chmod as chmod3 } from "node:fs/promises";
1302
1340
  import { join as join2, resolve } from "node:path";
@@ -1405,7 +1443,8 @@ async function loadConfig() {
1405
1443
  networkPassword,
1406
1444
  terminalApp: parsed.terminalApp || void 0,
1407
1445
  editorApp: parsed.editorApp || void 0,
1408
- useBuiltInEditor: !!parsed.useBuiltInEditor
1446
+ useBuiltInEditor: !!parsed.useBuiltInEditor,
1447
+ agentExecutable: parseExecutableChoice(parsed.agentExecutable)
1409
1448
  };
1410
1449
  return cachedConfig;
1411
1450
  }
@@ -1460,6 +1499,7 @@ var init_config = __esm({
1460
1499
  init_password_utils();
1461
1500
  init_atomicJsonFile();
1462
1501
  init_binaryResolver();
1502
+ init_agentExecutable();
1463
1503
  CONFIG_FILE_MODE = 384;
1464
1504
  __dirname = fileURLToPath(new URL(".", import.meta.url));
1465
1505
  PROJECT_ROOT = resolve(__dirname, "..");
@@ -3959,7 +3999,7 @@ async function createShare(input) {
3959
3999
  const passwordHash = hashPassword(passphrase);
3960
4000
  const share = await commitShareMutation((draft) => {
3961
4001
  const existing = draft.get(input.sessionId);
3962
- const record2 = {
4002
+ const record3 = {
3963
4003
  sessionId: input.sessionId,
3964
4004
  dirName: input.dirName,
3965
4005
  fileName: input.fileName,
@@ -3968,8 +4008,8 @@ async function createShare(input) {
3968
4008
  // No guest has ever used this passphrase; the UI renders 0 as "never".
3969
4009
  lastAccessAt: 0
3970
4010
  };
3971
- draft.set(record2.sessionId, record2);
3972
- return { changed: true, value: toPublicShare(record2) };
4011
+ draft.set(record3.sessionId, record3);
4012
+ return { changed: true, value: toPublicShare(record3) };
3973
4013
  });
3974
4014
  return { share, passphrase };
3975
4015
  }
@@ -4819,6 +4859,7 @@ var init_policy = __esm({
4819
4859
  "project-files": admin("/api/project-files"),
4820
4860
  "project-file": admin("/api/project-file"),
4821
4861
  "git-status": admin("/api/git-status"),
4862
+ "github-actions": admin("/api/github-actions"),
4822
4863
  "project-icon": admin("/api/project-icon"),
4823
4864
  "git-diff": admin("/api/git-diff"),
4824
4865
  mcp: admin("/api/mcp-servers"),
@@ -4847,6 +4888,10 @@ var init_policy = __esm({
4847
4888
  "provider-updates": [
4848
4889
  { prefix: "/api/provider-updates", methods: ["GET"], requires: "authed" },
4849
4890
  { prefix: "/api/provider-updates/run", requires: "admin" }
4891
+ ],
4892
+ // Reports which binary the server spawns; the choice itself is saved through /api/config.
4893
+ "agent-executable": [
4894
+ { prefix: "/api/agent-executable", methods: ["GET"], requires: "authed" }
4850
4895
  ]
4851
4896
  };
4852
4897
  ALL_RULES = Object.values(ROUTE_POLICIES).flat();
@@ -5619,6 +5664,274 @@ var init_subagentWatcher = __esm({
5619
5664
  }
5620
5665
  });
5621
5666
 
5667
+ // ../../shared/versions.ts
5668
+ function parseVersion(raw) {
5669
+ const match = raw.trim().replace(/^v/i, "").match(/^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?/);
5670
+ if (!match) return null;
5671
+ return {
5672
+ parts: [Number(match[1]), Number(match[2]), Number(match[3])],
5673
+ prerelease: match[4] ? match[4].split(".") : []
5674
+ };
5675
+ }
5676
+ function extractVersion(output) {
5677
+ const match = output.match(/\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?/);
5678
+ return match ? match[0] : null;
5679
+ }
5680
+ function comparePrerelease(a, b) {
5681
+ if (a.length === 0 && b.length === 0) return 0;
5682
+ if (a.length === 0) return 1;
5683
+ if (b.length === 0) return -1;
5684
+ for (let i = 0; i < Math.max(a.length, b.length); i += 1) {
5685
+ const left = a[i];
5686
+ const right = b[i];
5687
+ if (left === void 0) return -1;
5688
+ if (right === void 0) return 1;
5689
+ const leftNumeric = /^\d+$/.test(left);
5690
+ const rightNumeric = /^\d+$/.test(right);
5691
+ if (leftNumeric && rightNumeric) {
5692
+ if (Number(left) !== Number(right)) return Number(left) < Number(right) ? -1 : 1;
5693
+ continue;
5694
+ }
5695
+ if (leftNumeric !== rightNumeric) return leftNumeric ? -1 : 1;
5696
+ if (left !== right) return left < right ? -1 : 1;
5697
+ }
5698
+ return 0;
5699
+ }
5700
+ function compareVersions(a, b) {
5701
+ const left = parseVersion(a);
5702
+ const right = parseVersion(b);
5703
+ if (!left || !right) return null;
5704
+ for (let i = 0; i < 3; i += 1) {
5705
+ if (left.parts[i] !== right.parts[i]) return left.parts[i] < right.parts[i] ? -1 : 1;
5706
+ }
5707
+ return comparePrerelease(left.prerelease, right.prerelease);
5708
+ }
5709
+ var init_versions = __esm({
5710
+ "../../shared/versions.ts"() {
5711
+ }
5712
+ });
5713
+
5714
+ // ../../server/lib/cliProcess.ts
5715
+ import { spawn } from "node:child_process";
5716
+ function runCli(executable, args, timeoutMs) {
5717
+ return new Promise((resolve21) => {
5718
+ const resolved = resolveAgentCommand(executable, [...args]);
5719
+ const child = spawn(resolved.command, resolved.args, {
5720
+ stdio: ["ignore", "pipe", "pipe"],
5721
+ ...resolved.spawnOptions
5722
+ });
5723
+ let stdout = "";
5724
+ let stderr = "";
5725
+ let timedOut = false;
5726
+ const timer = setTimeout(() => {
5727
+ timedOut = true;
5728
+ child.kill("SIGKILL");
5729
+ }, timeoutMs);
5730
+ const append = (buffer, chunk) => buffer.length >= CLI_OUTPUT_MAX_CHARS ? buffer : buffer + chunk.toString();
5731
+ child.stdout?.on("data", (chunk) => {
5732
+ stdout = append(stdout, chunk);
5733
+ });
5734
+ child.stderr?.on("data", (chunk) => {
5735
+ stderr = append(stderr, chunk);
5736
+ });
5737
+ child.on("error", () => {
5738
+ clearTimeout(timer);
5739
+ resolve21({ code: null, stdout, stderr, timedOut });
5740
+ });
5741
+ child.on("close", (code) => {
5742
+ clearTimeout(timer);
5743
+ resolve21({ code, stdout, stderr, timedOut });
5744
+ });
5745
+ });
5746
+ }
5747
+ async function probeCliVersion(command, versionArgs, timeoutMs = DEFAULT_PROBE_TIMEOUT_MS) {
5748
+ const probe = await runCli(command, versionArgs, timeoutMs);
5749
+ return extractVersion(`${probe.stdout}
5750
+ ${probe.stderr}`);
5751
+ }
5752
+ var DEFAULT_PROBE_TIMEOUT_MS, CLI_OUTPUT_MAX_CHARS;
5753
+ var init_cliProcess = __esm({
5754
+ "../../server/lib/cliProcess.ts"() {
5755
+ init_versions();
5756
+ init_binaryResolver();
5757
+ DEFAULT_PROBE_TIMEOUT_MS = 5e3;
5758
+ CLI_OUTPUT_MAX_CHARS = 1e4;
5759
+ }
5760
+ });
5761
+
5762
+ // ../../server/agents/claudeExecutable.ts
5763
+ import { execFileSync } from "node:child_process";
5764
+ import { readFileSync, statSync as statSync2 } from "node:fs";
5765
+ import { createRequire } from "node:module";
5766
+ import { posix as posix2, win32 as win322 } from "node:path";
5767
+ function realIsFile(path) {
5768
+ try {
5769
+ return statSync2(path).isFile();
5770
+ } catch {
5771
+ return false;
5772
+ }
5773
+ }
5774
+ function realReadFile(path) {
5775
+ try {
5776
+ return readFileSync(path, "utf-8");
5777
+ } catch {
5778
+ return void 0;
5779
+ }
5780
+ }
5781
+ function binField(manifest) {
5782
+ let bin;
5783
+ try {
5784
+ bin = JSON.parse(manifest).bin;
5785
+ } catch {
5786
+ return void 0;
5787
+ }
5788
+ if (typeof bin === "string") return bin;
5789
+ if (!bin || typeof bin !== "object") return void 0;
5790
+ const entry = bin[BIN_NAME];
5791
+ return typeof entry === "string" ? entry : void 0;
5792
+ }
5793
+ function findNpmClaude(options = {}) {
5794
+ const platform2 = options.platform ?? process.platform;
5795
+ const windows = platform2 === "win32";
5796
+ const path = windows ? win322 : posix2;
5797
+ const isFile = options.isFile ?? realIsFile;
5798
+ const readFile21 = options.readFile ?? realReadFile;
5799
+ const shim = findExecutableOnPath(BIN_NAME, { platform: platform2, env: options.env, isExecutable: isFile });
5800
+ if (!shim) return void 0;
5801
+ const shimDir = path.dirname(shim);
5802
+ const packageDirs = [
5803
+ path.join(shimDir, "node_modules", NPM_PACKAGE),
5804
+ path.join(shimDir, "..", "lib", "node_modules", NPM_PACKAGE)
5805
+ ];
5806
+ for (const packageDir of packageDirs) {
5807
+ const manifest = readFile21(path.join(packageDir, "package.json"));
5808
+ if (!manifest) continue;
5809
+ const relative11 = binField(manifest);
5810
+ if (!relative11) continue;
5811
+ const target = path.normalize(path.join(packageDir, relative11));
5812
+ if (windows && !isDirectlyLaunchable(target)) continue;
5813
+ if (isFile(target)) return target;
5814
+ }
5815
+ return void 0;
5816
+ }
5817
+ function findInstalledClaude() {
5818
+ return findExecutableOnPath(BIN_NAME, { directOnly: true });
5819
+ }
5820
+ function findBundledClaude(resolveModule2) {
5821
+ try {
5822
+ const platformPkg = `@anthropic-ai/claude-agent-sdk-${process.platform}-${process.arch}`;
5823
+ return resolveModule2(`${platformPkg}/package.json`).replace(/package\.json$/, CLI_BIN_NAME).replace(ASAR_SEGMENT, "$1app.asar.unpacked$2");
5824
+ } catch {
5825
+ return void 0;
5826
+ }
5827
+ }
5828
+ function discoverClaudeExecutables(resolveModule2, probes = {}) {
5829
+ return {
5830
+ installed: (probes.findOnPath ?? findInstalledClaude)(),
5831
+ npm: (probes.findNpm ?? findNpmClaude)(),
5832
+ bundled: findBundledClaude(resolveModule2)
5833
+ };
5834
+ }
5835
+ function readCliVersionSync(binPath) {
5836
+ try {
5837
+ const output = execFileSync(binPath, ["--version"], {
5838
+ encoding: "utf-8",
5839
+ timeout: 5e3,
5840
+ stdio: ["ignore", "pipe", "ignore"]
5841
+ });
5842
+ const match = output.match(/(\d+)\.(\d+)\.(\d+)/);
5843
+ return match ? [Number(match[1]), Number(match[2]), Number(match[3])] : void 0;
5844
+ } catch {
5845
+ return void 0;
5846
+ }
5847
+ }
5848
+ function isAtLeast(version, floor) {
5849
+ for (let i = 0; i < 3; i += 1) {
5850
+ if (version[i] !== floor[i]) return version[i] > floor[i];
5851
+ }
5852
+ return true;
5853
+ }
5854
+ function chooseAutomatically(found, readVersion) {
5855
+ const bundled = found.bundled ? { source: "bundled", path: found.bundled } : void 0;
5856
+ if (!found.installed) return bundled;
5857
+ const installed = { source: "path", path: found.installed };
5858
+ const installedVersion = readVersion(found.installed);
5859
+ if (!installedVersion) return bundled ?? installed;
5860
+ const bundledVersion = bundled ? readVersion(bundled.path) : void 0;
5861
+ if (bundledVersion && !isAtLeast(installedVersion, bundledVersion)) return bundled;
5862
+ return installed;
5863
+ }
5864
+ function chooseClaudeExecutable(choice, found, readVersion) {
5865
+ switch (choice.source) {
5866
+ case "auto":
5867
+ return chooseAutomatically(found, readVersion);
5868
+ case "custom":
5869
+ return choice.path ? { source: "custom", path: choice.path } : void 0;
5870
+ case "path":
5871
+ return found.installed ? { source: "path", path: found.installed } : void 0;
5872
+ case "npm":
5873
+ return found.npm ? { source: "npm", path: found.npm } : void 0;
5874
+ case "bundled":
5875
+ return found.bundled ? { source: "bundled", path: found.bundled } : void 0;
5876
+ }
5877
+ }
5878
+ function configuredChoice() {
5879
+ return getConfig()?.agentExecutable ?? DEFAULT_EXECUTABLE_CHOICE;
5880
+ }
5881
+ function resolveClaudeExecutable() {
5882
+ const choice = configuredChoice();
5883
+ const key2 = JSON.stringify(choice);
5884
+ if (memo?.key !== key2) {
5885
+ memo = {
5886
+ key: key2,
5887
+ resolved: chooseClaudeExecutable(choice, discoverClaudeExecutables(resolveModule), readCliVersionSync)
5888
+ };
5889
+ }
5890
+ return memo.resolved;
5891
+ }
5892
+ function claudeCliPath() {
5893
+ return resolveClaudeExecutable()?.path;
5894
+ }
5895
+ async function describeClaudeExecutable() {
5896
+ const choice = configuredChoice();
5897
+ const found = discoverClaudeExecutables(resolveModule);
5898
+ const active = resolveClaudeExecutable();
5899
+ const pathBySource = {
5900
+ path: found.installed,
5901
+ npm: found.npm,
5902
+ bundled: found.bundled
5903
+ };
5904
+ const detected = DETECTED_EXECUTABLE_SOURCES.filter((source) => pathBySource[source] !== void 0).map((source) => ({ source, path: pathBySource[source] }));
5905
+ const paths = /* @__PURE__ */ new Set([...detected.map(({ path }) => path), ...active ? [active.path] : []]);
5906
+ const versions = new Map(
5907
+ await Promise.all(
5908
+ [...paths].map(async (path) => [path, await probeCliVersion(path, ["--version"])])
5909
+ )
5910
+ );
5911
+ return {
5912
+ choice,
5913
+ candidates: detected.map((candidate) => ({
5914
+ ...candidate,
5915
+ version: versions.get(candidate.path) ?? null
5916
+ })),
5917
+ active: active ? { ...active, version: versions.get(active.path) ?? null } : null
5918
+ };
5919
+ }
5920
+ var BIN_NAME, NPM_PACKAGE, CLI_BIN_NAME, ASAR_SEGMENT, resolveModule, memo;
5921
+ var init_claudeExecutable = __esm({
5922
+ "../../server/agents/claudeExecutable.ts"() {
5923
+ init_agentExecutable();
5924
+ init_config();
5925
+ init_binaryResolver();
5926
+ init_cliProcess();
5927
+ BIN_NAME = "claude";
5928
+ NPM_PACKAGE = "@anthropic-ai/claude-code";
5929
+ CLI_BIN_NAME = nativeBinaryName(BIN_NAME);
5930
+ ASAR_SEGMENT = /([\\/])app\.asar([\\/])/;
5931
+ resolveModule = (id) => createRequire(import.meta.url).resolve(id);
5932
+ }
5933
+ });
5934
+
5622
5935
  // ../../server/lib/streamBus.ts
5623
5936
  function getOrCreate(sessionId) {
5624
5937
  let state = sessions2.get(sessionId);
@@ -5887,58 +6200,6 @@ var init_streamBus = __esm({
5887
6200
 
5888
6201
  // ../../server/sdk-session.ts
5889
6202
  import { query } from "@anthropic-ai/claude-agent-sdk";
5890
- import { execFileSync } from "node:child_process";
5891
- import { createRequire } from "node:module";
5892
- function findClaudeOnPath() {
5893
- return findExecutableOnPath("claude", { directOnly: true });
5894
- }
5895
- function readCliVersion(binPath) {
5896
- try {
5897
- const output = execFileSync(binPath, ["--version"], {
5898
- encoding: "utf-8",
5899
- timeout: 5e3,
5900
- stdio: ["ignore", "pipe", "ignore"]
5901
- });
5902
- const match = output.match(/(\d+)\.(\d+)\.(\d+)/);
5903
- return match ? [Number(match[1]), Number(match[2]), Number(match[3])] : void 0;
5904
- } catch {
5905
- return void 0;
5906
- }
5907
- }
5908
- function isAtLeast(version, floor) {
5909
- for (let i = 0; i < 3; i += 1) {
5910
- if (version[i] !== floor[i]) return version[i] > floor[i];
5911
- }
5912
- return true;
5913
- }
5914
- function resolveClaudeCliPath(resolveModule, probes = {}) {
5915
- const findOnPath = probes.findOnPath ?? findClaudeOnPath;
5916
- const readVersion = probes.readVersion ?? readCliVersion;
5917
- let vendored;
5918
- try {
5919
- const platformPkg = `@anthropic-ai/claude-agent-sdk-${process.platform}-${process.arch}`;
5920
- vendored = resolveModule(`${platformPkg}/package.json`).replace(/package\.json$/, CLI_BIN_NAME);
5921
- } catch {
5922
- vendored = void 0;
5923
- }
5924
- const insideAsar = vendored !== void 0 && ASAR_SEGMENT.test(vendored);
5925
- const vendoredExecutable = insideAsar ? vendored.replace(ASAR_SEGMENT, "$1app.asar.unpacked$2") : vendored;
5926
- const fallback = insideAsar ? vendoredExecutable : void 0;
5927
- const installed = findOnPath();
5928
- if (!installed) return fallback;
5929
- const installedVersion = readVersion(installed);
5930
- if (!installedVersion) return vendoredExecutable ? fallback : installed;
5931
- const vendoredVersion = vendoredExecutable ? readVersion(vendoredExecutable) : void 0;
5932
- if (vendoredVersion && !isAtLeast(installedVersion, vendoredVersion)) return fallback;
5933
- return installed;
5934
- }
5935
- function claudeCliPath() {
5936
- if (!cliPathProbed) {
5937
- cliPath = resolveClaudeCliPath((id) => createRequire(import.meta.url).resolve(id));
5938
- cliPathProbed = true;
5939
- }
5940
- return cliPath;
5941
- }
5942
6203
  function streamingEnabled() {
5943
6204
  const raw = process.env.COGPIT_STREAM_PARTIAL;
5944
6205
  if (raw === void 0) return true;
@@ -6824,15 +7085,12 @@ function cleanupAllSDKSessions() {
6824
7085
  sdkSessions.clear();
6825
7086
  return killed;
6826
7087
  }
6827
- var CLI_BIN_NAME, ASAR_SEGMENT, cliPathProbed, cliPath, sdkSessions, REFUSAL_FALLBACK_DIALOG, DIALOG_CHOICES, SUPPORTED_DIALOG_KINDS, SDKMessageStream, EDIT_TOOL_NAMES;
7088
+ var sdkSessions, REFUSAL_FALLBACK_DIALOG, DIALOG_CHOICES, SUPPORTED_DIALOG_KINDS, SDKMessageStream, EDIT_TOOL_NAMES;
6828
7089
  var init_sdk_session = __esm({
6829
7090
  "../../server/sdk-session.ts"() {
6830
7091
  init_subagentWatcher();
6831
- init_binaryResolver();
7092
+ init_claudeExecutable();
6832
7093
  init_streamBus();
6833
- CLI_BIN_NAME = nativeBinaryName("claude");
6834
- ASAR_SEGMENT = /([\\/])app\.asar([\\/])/;
6835
- cliPathProbed = false;
6836
7094
  sdkSessions = /* @__PURE__ */ new Map();
6837
7095
  REFUSAL_FALLBACK_DIALOG = "refusal_fallback_prompt";
6838
7096
  DIALOG_CHOICES = {
@@ -8320,9 +8578,9 @@ function safeParseLine(line) {
8320
8578
  function isObject2(value) {
8321
8579
  return typeof value === "object" && value !== null;
8322
8580
  }
8323
- function isCodexRecord(record2) {
8324
- if (!record2 || typeof record2.type !== "string") return false;
8325
- return record2.type === "session_meta" || record2.type === "turn_context" || record2.type === "event_msg" || record2.type === "response_item" || record2.type === "inter_agent_communication_metadata" || record2.type === "compacted" || record2.type === "world_state";
8581
+ function isCodexRecord(record3) {
8582
+ if (!record3 || typeof record3.type !== "string") return false;
8583
+ return record3.type === "session_meta" || record3.type === "turn_context" || record3.type === "event_msg" || record3.type === "response_item" || record3.type === "inter_agent_communication_metadata" || record3.type === "compacted" || record3.type === "world_state";
8326
8584
  }
8327
8585
  function extractMessageText(payload, blockType) {
8328
8586
  const content = payload?.content;
@@ -8523,22 +8781,22 @@ function createTurn(turnId, timestamp, model) {
8523
8781
  model
8524
8782
  };
8525
8783
  }
8526
- function extractPromptFromRecord(record2) {
8527
- if (record2.type === "event_msg" && isObject2(record2.payload) && record2.payload.type === "user_message" && typeof record2.payload.message === "string") {
8528
- return normalizePromptText(record2.payload.message);
8784
+ function extractPromptFromRecord(record3) {
8785
+ if (record3.type === "event_msg" && isObject2(record3.payload) && record3.payload.type === "user_message" && typeof record3.payload.message === "string") {
8786
+ return normalizePromptText(record3.payload.message);
8529
8787
  }
8530
- if (record2.type === "response_item" && isObject2(record2.payload) && record2.payload.type === "message" && record2.payload.role === "user") {
8531
- return normalizePromptText(extractMessageText(record2.payload, "input_text"));
8788
+ if (record3.type === "response_item" && isObject2(record3.payload) && record3.payload.type === "message" && record3.payload.role === "user") {
8789
+ return normalizePromptText(extractMessageText(record3.payload, "input_text"));
8532
8790
  }
8533
8791
  return "";
8534
8792
  }
8535
- function recordOpensTurn(record2, payload) {
8536
- if (record2.type === "session_meta") return false;
8537
- if (record2.type === "event_msg" && payload?.type === "task_started") return false;
8538
- if (record2.type === "response_item" && payload?.type === "message") {
8793
+ function recordOpensTurn(record3, payload) {
8794
+ if (record3.type === "session_meta") return false;
8795
+ if (record3.type === "event_msg" && payload?.type === "task_started") return false;
8796
+ if (record3.type === "response_item" && payload?.type === "message") {
8539
8797
  if (payload.role === "developer" || payload.role === "system") return false;
8540
8798
  if (payload.role === "user") {
8541
- return extractPromptFromRecord(record2) !== "" || extractResponseMessageImages(payload).length > 0 || extractResponseMessageAudio(payload).length > 0;
8799
+ return extractPromptFromRecord(record3) !== "" || extractResponseMessageImages(payload).length > 0 || extractResponseMessageAudio(payload).length > 0;
8542
8800
  }
8543
8801
  }
8544
8802
  return true;
@@ -8559,22 +8817,22 @@ function extractMetadataFromRecords(records) {
8559
8817
  let parentSessionId = null;
8560
8818
  let agentPath = "/root";
8561
8819
  let previousPrompt = "";
8562
- for (const record2 of records) {
8563
- if (!isObject2(record2.payload)) continue;
8564
- if (record2.type === "session_meta") {
8565
- sessionId ||= typeof record2.payload.id === "string" ? record2.payload.id : "";
8566
- version ||= typeof record2.payload.cli_version === "string" ? record2.payload.cli_version : "";
8567
- cwd ||= typeof record2.payload.cwd === "string" ? record2.payload.cwd : "";
8568
- if (!branchedFrom && isObject2(record2.payload.branchedFrom)) {
8569
- const sourceId = typeof record2.payload.branchedFrom.sessionId === "string" ? record2.payload.branchedFrom.sessionId : "";
8820
+ for (const record3 of records) {
8821
+ if (!isObject2(record3.payload)) continue;
8822
+ if (record3.type === "session_meta") {
8823
+ sessionId ||= typeof record3.payload.id === "string" ? record3.payload.id : "";
8824
+ version ||= typeof record3.payload.cli_version === "string" ? record3.payload.cli_version : "";
8825
+ cwd ||= typeof record3.payload.cwd === "string" ? record3.payload.cwd : "";
8826
+ if (!branchedFrom && isObject2(record3.payload.branchedFrom)) {
8827
+ const sourceId = typeof record3.payload.branchedFrom.sessionId === "string" ? record3.payload.branchedFrom.sessionId : "";
8570
8828
  if (sourceId) {
8571
8829
  branchedFrom = {
8572
8830
  sessionId: sourceId,
8573
- turnIndex: typeof record2.payload.branchedFrom.turnIndex === "number" ? record2.payload.branchedFrom.turnIndex : null
8831
+ turnIndex: typeof record3.payload.branchedFrom.turnIndex === "number" ? record3.payload.branchedFrom.turnIndex : null
8574
8832
  };
8575
8833
  }
8576
8834
  }
8577
- const source = isObject2(record2.payload.source) ? record2.payload.source : null;
8835
+ const source = isObject2(record3.payload.source) ? record3.payload.source : null;
8578
8836
  if (source && isObject2(source.subagent)) {
8579
8837
  isSubagent = true;
8580
8838
  const threadSpawn = isObject2(source.subagent.thread_spawn) ? source.subagent.thread_spawn : null;
@@ -8582,25 +8840,25 @@ function extractMetadataFromRecords(records) {
8582
8840
  agentPath = threadSpawn.agent_path;
8583
8841
  }
8584
8842
  }
8585
- if (typeof record2.payload.forked_from_id === "string" && record2.payload.forked_from_id) {
8586
- parentSessionId = record2.payload.forked_from_id;
8843
+ if (typeof record3.payload.forked_from_id === "string" && record3.payload.forked_from_id) {
8844
+ parentSessionId = record3.payload.forked_from_id;
8587
8845
  }
8588
- const git = isObject2(record2.payload.git) ? record2.payload.git : null;
8846
+ const git = isObject2(record3.payload.git) ? record3.payload.git : null;
8589
8847
  gitBranch ||= git && typeof git.branch === "string" ? git.branch : "";
8590
8848
  }
8591
- if (record2.type === "turn_context") {
8592
- model ||= typeof record2.payload.model === "string" ? record2.payload.model : "";
8593
- cwd ||= typeof record2.payload.cwd === "string" ? record2.payload.cwd : "";
8849
+ if (record3.type === "turn_context") {
8850
+ model ||= typeof record3.payload.model === "string" ? record3.payload.model : "";
8851
+ cwd ||= typeof record3.payload.cwd === "string" ? record3.payload.cwd : "";
8594
8852
  }
8595
- const prompt = extractPromptFromRecord(record2);
8853
+ const prompt = extractPromptFromRecord(record3);
8596
8854
  if (!prompt) continue;
8597
8855
  if (prompt === previousPrompt) continue;
8598
8856
  if (!firstUserMessage) firstUserMessage = prompt;
8599
8857
  lastUserMessage = prompt;
8600
8858
  previousPrompt = prompt;
8601
8859
  turnCount++;
8602
- if (!timestamp) timestamp = record2.timestamp ?? "";
8603
- lastTimestamp = record2.timestamp ?? lastTimestamp;
8860
+ if (!timestamp) timestamp = record3.timestamp ?? "";
8861
+ lastTimestamp = record3.timestamp ?? lastTimestamp;
8604
8862
  }
8605
8863
  if (lastTimestamp === "") lastTimestamp = timestamp;
8606
8864
  return {
@@ -8758,19 +9016,19 @@ function walkCodexRecords(records, options) {
8758
9016
  appendToolCall(targetTurn, toolCall, timestamp);
8759
9017
  }
8760
9018
  for (let index = 0; index < records.length; index++) {
8761
- const record2 = records[index];
8762
- if (!isCodexRecord(record2)) continue;
9019
+ const record3 = records[index];
9020
+ if (!isCodexRecord(record3)) continue;
8763
9021
  recordIndex = index;
8764
- const payload = isObject2(record2.payload) ? record2.payload : void 0;
8765
- const timestamp = record2.timestamp ?? "";
8766
- if (record2.type === "compacted") {
9022
+ const payload = isObject2(record3.payload) ? record3.payload : void 0;
9023
+ const timestamp = record3.timestamp ?? "";
9024
+ if (record3.type === "compacted") {
8767
9025
  current = commitTurn(current);
8768
9026
  pendingCompaction = "Conversation compacted";
8769
9027
  lastTurnTimestamp = timestamp;
8770
9028
  continue;
8771
9029
  }
8772
- if (record2.type === "world_state") continue;
8773
- if (record2.type === "turn_context") {
9030
+ if (record3.type === "world_state") continue;
9031
+ if (record3.type === "turn_context") {
8774
9032
  current = commitTurn(current);
8775
9033
  pendingContextIndex = index;
8776
9034
  currentTurnId = typeof payload?.turn_id === "string" ? payload.turn_id : null;
@@ -8779,7 +9037,7 @@ function walkCodexRecords(records, options) {
8779
9037
  sawTurnStart = true;
8780
9038
  continue;
8781
9039
  }
8782
- if (record2.type === "event_msg" && payload?.type === "user_message" && typeof payload.message === "string") {
9040
+ if (record3.type === "event_msg" && payload?.type === "user_message" && typeof payload.message === "string") {
8783
9041
  if (current && (current.assistantText.length > 0 || current.toolCalls.length > 0 || current.thinking.length > 0)) {
8784
9042
  current = commitTurn(current);
8785
9043
  }
@@ -8803,12 +9061,12 @@ function walkCodexRecords(records, options) {
8803
9061
  lastTurnTimestamp = timestamp;
8804
9062
  continue;
8805
9063
  }
8806
- if (!current && !recordOpensTurn(record2, payload)) continue;
9064
+ if (!current && !recordOpensTurn(record3, payload)) continue;
8807
9065
  current ??= createActiveTurn(currentTurnId, timestamp, currentModel);
8808
9066
  if (!current.model && currentModel) current.model = currentModel;
8809
9067
  if (!current.timestamp) current.timestamp = timestamp;
8810
9068
  if (timestamp) lastTurnTimestamp = timestamp;
8811
- if (record2.type === "event_msg" && payload?.type === "token_count") {
9069
+ if (record3.type === "event_msg" && payload?.type === "token_count") {
8812
9070
  const info = isObject2(payload.info) ? payload.info : null;
8813
9071
  const lastUsage = info ? parseTokenUsage(info.last_token_usage) : null;
8814
9072
  if (lastUsage) {
@@ -8816,14 +9074,14 @@ function walkCodexRecords(records, options) {
8816
9074
  }
8817
9075
  continue;
8818
9076
  }
8819
- if (record2.type === "event_msg" && payload?.type === "web_search_end" && typeof payload.call_id === "string") {
9077
+ if (record3.type === "event_msg" && payload?.type === "web_search_end" && typeof payload.call_id === "string") {
8820
9078
  const input = {};
8821
9079
  if (typeof payload.query === "string" && payload.query) input.query = payload.query;
8822
9080
  if (isObject2(payload.action)) input.action = payload.action;
8823
9081
  upsertWebSearchCall(current, payload.call_id, input, "completed", timestamp);
8824
9082
  continue;
8825
9083
  }
8826
- if (record2.type === "event_msg" && payload?.type === "mcp_tool_call_end" && typeof payload.call_id === "string" && isObject2(payload.invocation)) {
9084
+ if (record3.type === "event_msg" && payload?.type === "mcp_tool_call_end" && typeof payload.call_id === "string" && isObject2(payload.invocation)) {
8827
9085
  const invocation = payload.invocation;
8828
9086
  const server = typeof invocation.server === "string" ? invocation.server : "mcp";
8829
9087
  const tool = typeof invocation.tool === "string" ? invocation.tool : "tool";
@@ -8846,7 +9104,7 @@ function walkCodexRecords(records, options) {
8846
9104
  }
8847
9105
  continue;
8848
9106
  }
8849
- if (record2.type === "event_msg" && payload?.type === "sub_agent_activity" && typeof payload.agent_thread_id === "string" && typeof payload.agent_path === "string") {
9107
+ if (record3.type === "event_msg" && payload?.type === "sub_agent_activity" && typeof payload.agent_thread_id === "string" && typeof payload.agent_path === "string") {
8850
9108
  const agentId = payload.agent_thread_id;
8851
9109
  const eventId2 = typeof payload.event_id === "string" ? payload.event_id : "";
8852
9110
  const kind = typeof payload.kind === "string" ? payload.kind : "started";
@@ -8880,7 +9138,7 @@ function walkCodexRecords(records, options) {
8880
9138
  upsertAgentMessage(agentId, current, timestamp, { status });
8881
9139
  continue;
8882
9140
  }
8883
- if (record2.type !== "response_item" || !payload) continue;
9141
+ if (record3.type !== "response_item" || !payload) continue;
8884
9142
  if (payload.type === "reasoning") {
8885
9143
  const summary = Array.isArray(payload.summary) ? payload.summary.filter((item) => typeof item === "string").join("\n") : "";
8886
9144
  appendThinking(current, summary.trim(), timestamp);
@@ -9150,7 +9408,7 @@ function parseCodexSession(jsonlText, options) {
9150
9408
  return session;
9151
9409
  }
9152
9410
  function appendCodexSession(existing, newJsonlText) {
9153
- const prefix = existing.rawMessages.map((record2) => JSON.stringify(record2)).join("\n");
9411
+ const prefix = existing.rawMessages.map((record3) => JSON.stringify(record3)).join("\n");
9154
9412
  return parseCodexSession(prefix ? `${prefix}
9155
9413
  ${newJsonlText}` : newJsonlText);
9156
9414
  }
@@ -9345,16 +9603,16 @@ async function readCodexSessionIdentity(filePath) {
9345
9603
  let parentSessionId = null;
9346
9604
  let sawSessionMeta = false;
9347
9605
  for (const line of await readHeadLines(filePath, HEAD_BYTES)) {
9348
- let record2;
9606
+ let record3;
9349
9607
  try {
9350
- record2 = JSON.parse(line);
9608
+ record3 = JSON.parse(line);
9351
9609
  } catch {
9352
9610
  continue;
9353
9611
  }
9354
- if (!isRecord2(record2) || !isRecord2(record2.payload)) continue;
9355
- if (record2.type === "session_meta") {
9612
+ if (!isRecord2(record3) || !isRecord2(record3.payload)) continue;
9613
+ if (record3.type === "session_meta") {
9356
9614
  sawSessionMeta = true;
9357
- const payload = record2.payload;
9615
+ const payload = record3.payload;
9358
9616
  if (!sessionId && typeof payload.id === "string") sessionId = payload.id;
9359
9617
  if (!cwd && typeof payload.cwd === "string") cwd = payload.cwd;
9360
9618
  if (!parentSessionId && typeof payload.forked_from_id === "string") {
@@ -9364,8 +9622,8 @@ async function readCodexSessionIdentity(filePath) {
9364
9622
  if (source && isRecord2(source.subagent)) isSubagent = true;
9365
9623
  const git = isRecord2(payload.git) ? payload.git : null;
9366
9624
  if (!gitBranch && git && typeof git.branch === "string") gitBranch = git.branch;
9367
- } else if (record2.type === "turn_context") {
9368
- if (!cwd && typeof record2.payload.cwd === "string") cwd = record2.payload.cwd;
9625
+ } else if (record3.type === "turn_context") {
9626
+ if (!cwd && typeof record3.payload.cwd === "string") cwd = record3.payload.cwd;
9369
9627
  }
9370
9628
  }
9371
9629
  if (!sawSessionMeta || !cwd) return null;
@@ -10387,7 +10645,7 @@ function parseCopilotSession(jsonlText, options) {
10387
10645
  };
10388
10646
  }
10389
10647
  function appendCopilotSession(existing, newJsonlText) {
10390
- const prefix = existing.rawMessages.map((record2) => JSON.stringify(record2)).join("\n");
10648
+ const prefix = existing.rawMessages.map((record3) => JSON.stringify(record3)).join("\n");
10391
10649
  return parseCopilotSession(prefix ? `${prefix}
10392
10650
  ${newJsonlText}` : newJsonlText);
10393
10651
  }
@@ -10495,11 +10753,11 @@ function brandCopilotBranch() {
10495
10753
  function copilotTurnBoundaries(records) {
10496
10754
  const boundaries = [];
10497
10755
  for (let index = 0; index < records.length; index++) {
10498
- const record2 = records[index];
10499
- if (record2.type !== "user.message" || typeof record2.agentId === "string") continue;
10500
- const data = record2.data;
10756
+ const record3 = records[index];
10757
+ if (record3.type !== "user.message" || typeof record3.agentId === "string") continue;
10758
+ const data = record3.data;
10501
10759
  if (!data || typeof data !== "object" || Array.isArray(data)) continue;
10502
- if (typeof record2.id !== "string" || !record2.id) continue;
10760
+ if (typeof record3.id !== "string" || !record3.id) continue;
10503
10761
  boundaries.push(index);
10504
10762
  }
10505
10763
  return boundaries;
@@ -10556,14 +10814,14 @@ async function readCopilotSessionIdentity(filePath) {
10556
10814
  let cwd = "";
10557
10815
  let gitBranch = "";
10558
10816
  for (const line of await readHeadLines(filePath, HEAD_BYTES)) {
10559
- let record2;
10817
+ let record3;
10560
10818
  try {
10561
- record2 = JSON.parse(line);
10819
+ record3 = JSON.parse(line);
10562
10820
  } catch {
10563
10821
  continue;
10564
10822
  }
10565
- if (!isRecord3(record2) || record2.type !== "session.start" && record2.type !== "session.resume") continue;
10566
- const data = isRecord3(record2.data) ? record2.data : null;
10823
+ if (!isRecord3(record3) || record3.type !== "session.start" && record3.type !== "session.resume") continue;
10824
+ const data = isRecord3(record3.data) ? record3.data : null;
10567
10825
  if (!data) continue;
10568
10826
  if (typeof data.sessionId === "string" && data.sessionId) sessionId = data.sessionId;
10569
10827
  const context = isRecord3(data.context) ? data.context : null;
@@ -11926,9 +12184,9 @@ function nonEmpty(value) {
11926
12184
  function stripTaggedBlocks(text) {
11927
12185
  return text.replace(/<[^>]+>[\s\S]*?<\/[^>]+>/g, "").trim();
11928
12186
  }
11929
- function effortFromRecord(record2) {
12187
+ function effortFromRecord(record3) {
11930
12188
  for (const kind of AGENT_KINDS) {
11931
- const effort = READERS[kind].effort(record2);
12189
+ const effort = READERS[kind].effort(record3);
11932
12190
  if (effort) return effort;
11933
12191
  }
11934
12192
  return null;
@@ -11938,15 +12196,15 @@ function userMessageTextFromLine(line) {
11938
12196
  (kind) => READERS[kind].userMessageMarkers.some((marker) => line.includes(marker))
11939
12197
  );
11940
12198
  if (candidates.length === 0) return null;
11941
- let record2;
12199
+ let record3;
11942
12200
  try {
11943
- record2 = JSON.parse(line);
12201
+ record3 = JSON.parse(line);
11944
12202
  } catch {
11945
12203
  return null;
11946
12204
  }
11947
- if (!isRecord4(record2)) return null;
12205
+ if (!isRecord4(record3)) return null;
11948
12206
  for (const kind of candidates) {
11949
- const text = READERS[kind].userMessageText(record2);
12207
+ const text = READERS[kind].userMessageText(record3);
11950
12208
  if (text !== null) return text;
11951
12209
  }
11952
12210
  return null;
@@ -11958,11 +12216,11 @@ var init_recordReaders = __esm({
11958
12216
  claudeReader = {
11959
12217
  kind: "claude",
11960
12218
  // Every assistant record is tagged with the effort that turn ran at.
11961
- effort: (record2) => record2.type === "assistant" ? nonEmpty(record2.effort) : null,
12219
+ effort: (record3) => record3.type === "assistant" ? nonEmpty(record3.effort) : null,
11962
12220
  userMessageMarkers: ['"user"'],
11963
- userMessageText(record2) {
11964
- if (record2.type !== "user" || record2.isMeta) return null;
11965
- const content = isRecord4(record2.message) ? record2.message.content : void 0;
12221
+ userMessageText(record3) {
12222
+ if (record3.type !== "user" || record3.isMeta) return null;
12223
+ const content = isRecord4(record3.message) ? record3.message.content : void 0;
11966
12224
  if (typeof content === "string") return stripTaggedBlocks(content);
11967
12225
  if (!Array.isArray(content)) return "";
11968
12226
  let text = "";
@@ -11977,32 +12235,32 @@ var init_recordReaders = __esm({
11977
12235
  codexReader = {
11978
12236
  kind: "codex",
11979
12237
  // Effort is recorded per turn; newer CLI versions moved it under `thread_settings`.
11980
- effort(record2) {
11981
- if (record2.type !== "turn_context" || !isRecord4(record2.payload)) return null;
11982
- const { payload } = record2;
12238
+ effort(record3) {
12239
+ if (record3.type !== "turn_context" || !isRecord4(record3.payload)) return null;
12240
+ const { payload } = record3;
11983
12241
  const direct = nonEmpty(payload.effort);
11984
12242
  if (direct) return direct;
11985
12243
  const settings = isRecord4(payload.thread_settings) ? payload.thread_settings : null;
11986
12244
  return nonEmpty(settings?.reasoning_effort);
11987
12245
  },
11988
12246
  userMessageMarkers: ['"event_msg"'],
11989
- userMessageText(record2) {
11990
- if (record2.type !== "event_msg" || !isRecord4(record2.payload)) return null;
11991
- const { payload } = record2;
12247
+ userMessageText(record3) {
12248
+ if (record3.type !== "event_msg" || !isRecord4(record3.payload)) return null;
12249
+ const { payload } = record3;
11992
12250
  return payload.type === "user_message" && typeof payload.message === "string" ? payload.message.trim() : null;
11993
12251
  }
11994
12252
  };
11995
12253
  copilotReader = {
11996
12254
  kind: "copilot",
11997
- effort(record2) {
11998
- if (record2.type !== "session.start" && record2.type !== "session.resume" && record2.type !== "session.model_change" || !isRecord4(record2.data)) return null;
11999
- return nonEmpty(record2.data.reasoningEffort ?? record2.data.effort);
12255
+ effort(record3) {
12256
+ if (record3.type !== "session.start" && record3.type !== "session.resume" && record3.type !== "session.model_change" || !isRecord4(record3.data)) return null;
12257
+ return nonEmpty(record3.data.reasoningEffort ?? record3.data.effort);
12000
12258
  },
12001
12259
  userMessageMarkers: ['"user.message"'],
12002
- userMessageText(record2) {
12003
- if (record2.type !== "user.message") return null;
12004
- if (typeof record2.agentId === "string" && record2.agentId) return null;
12005
- const data = isRecord4(record2.data) ? record2.data : null;
12260
+ userMessageText(record3) {
12261
+ if (record3.type !== "user.message") return null;
12262
+ if (typeof record3.agentId === "string" && record3.agentId) return null;
12263
+ const data = isRecord4(record3.data) ? record3.data : null;
12006
12264
  return typeof data?.content === "string" ? data.content.trim() : "";
12007
12265
  }
12008
12266
  };
@@ -12015,16 +12273,16 @@ var init_recordReaders = __esm({
12015
12273
  });
12016
12274
 
12017
12275
  // ../../server/agents/tailRecords.ts
12018
- function isSubagentLifecycle(record2) {
12019
- return record2.type.startsWith("subagent.") && typeof record2.agentId === "string" && record2.agentId.length > 0;
12276
+ function isSubagentLifecycle(record3) {
12277
+ return record3.type.startsWith("subagent.") && typeof record3.agentId === "string" && record3.agentId.length > 0;
12020
12278
  }
12021
- function isSubagentNoise(record2) {
12022
- return typeof record2.agentId === "string" && record2.agentId.length > 0;
12279
+ function isSubagentNoise(record3) {
12280
+ return typeof record3.agentId === "string" && record3.agentId.length > 0;
12023
12281
  }
12024
- function classifyTailRecord(record2) {
12282
+ function classifyTailRecord(record3) {
12025
12283
  for (const kind of AGENT_KINDS) {
12026
12284
  const format = TAIL_FORMATS[kind];
12027
- const verdict = format.classify(record2);
12285
+ const verdict = format.classify(record3);
12028
12286
  if (verdict.kind !== "ignore") return { format, verdict };
12029
12287
  }
12030
12288
  return null;
@@ -12041,31 +12299,31 @@ var init_tailRecords = __esm({
12041
12299
  // Background agent/workflow launches, task notifications and TaskStop calls
12042
12300
  // can all still change a verdict that a turn-ending line looked to settle.
12043
12301
  markers: ["async_launched", "task-notification", '"TaskStop"'],
12044
- classify(record2) {
12045
- if (record2.type === "system" && record2.subtype === "terminal_reason") {
12046
- const reason = record2.reason;
12302
+ classify(record3) {
12303
+ if (record3.type === "system" && record3.subtype === "terminal_reason") {
12304
+ const reason = record3.reason;
12047
12305
  return reason ? { kind: "final", status: { status: "completed", terminalReason: reason } } : IGNORE;
12048
12306
  }
12049
- if (record2.type === "progress") {
12050
- const data = record2.data;
12307
+ if (record3.type === "progress") {
12308
+ const data = record3.data;
12051
12309
  if (data?.type !== "hook_progress") return IGNORE;
12052
12310
  const decision = data.decision ?? data.hookSpecificOutput?.permissionDecision;
12053
12311
  return decision === "defer" ? ANSWER : IGNORE;
12054
12312
  }
12055
- if (record2.type === "attachment") return KEEP;
12056
- if (record2.type === "assistant" || record2.type === "user" || record2.type === "queue-operation") {
12057
- const stopReason = record2.message?.stop_reason;
12058
- if (record2.type === "assistant" && stopReason === "end_turn") {
12313
+ if (record3.type === "attachment") return KEEP;
12314
+ if (record3.type === "assistant" || record3.type === "user" || record3.type === "queue-operation") {
12315
+ const stopReason = record3.message?.stop_reason;
12316
+ if (record3.type === "assistant" && stopReason === "end_turn") {
12059
12317
  return { kind: "turn-end", awaitUserActivity: true };
12060
12318
  }
12061
- const canDerive = record2.type === "assistant" || record2.type === "user" && !record2.isMeta;
12319
+ const canDerive = record3.type === "assistant" || record3.type === "user" && !record3.isMeta;
12062
12320
  return canDerive ? ANSWER : KEEP;
12063
12321
  }
12064
12322
  return IGNORE;
12065
12323
  },
12066
- classifyAfterTurnEnd(record2) {
12067
- if (record2.type !== "user" && record2.type !== "assistant" && record2.type !== "queue-operation" && record2.type !== "attachment") return IGNORE;
12068
- const isRealUserTurn = record2.type === "user" && !record2.isMeta;
12324
+ classifyAfterTurnEnd(record3) {
12325
+ if (record3.type !== "user" && record3.type !== "assistant" && record3.type !== "queue-operation" && record3.type !== "attachment") return IGNORE;
12326
+ const isRealUserTurn = record3.type === "user" && !record3.isMeta;
12069
12327
  return { kind: "keep", sawUserActivity: isRealUserTurn };
12070
12328
  }
12071
12329
  };
@@ -12084,9 +12342,9 @@ var init_tailRecords = __esm({
12084
12342
  "agent_message",
12085
12343
  "function_call_output"
12086
12344
  ],
12087
- classify(record2) {
12088
- if (record2.type === "event_msg") {
12089
- const payload = record2.payload;
12345
+ classify(record3) {
12346
+ if (record3.type === "event_msg") {
12347
+ const payload = record3.payload;
12090
12348
  switch (payload?.type) {
12091
12349
  case "task_complete":
12092
12350
  return { kind: "turn-end" };
@@ -12098,32 +12356,32 @@ var init_tailRecords = __esm({
12098
12356
  return IGNORE;
12099
12357
  }
12100
12358
  }
12101
- if (record2.type === "response_item") {
12102
- const payload = record2.payload;
12359
+ if (record3.type === "response_item") {
12360
+ const payload = record3.payload;
12103
12361
  const decides = payload?.type === "function_call" || payload?.type === "message" && (payload.role === "assistant" || payload.role === "user");
12104
12362
  return decides ? ANSWER : IGNORE;
12105
12363
  }
12106
12364
  return IGNORE;
12107
12365
  },
12108
- classifyAfterTurnEnd(record2) {
12109
- return record2.type === "event_msg" || record2.type === "response_item" ? KEEP : IGNORE;
12366
+ classifyAfterTurnEnd(record3) {
12367
+ return record3.type === "event_msg" || record3.type === "response_item" ? KEEP : IGNORE;
12110
12368
  }
12111
12369
  };
12112
12370
  copilotTail = {
12113
12371
  kind: "copilot",
12114
12372
  markers: ['"abort"', '"assistant.message"', '"user.message"', '"subagent.'],
12115
- classify(record2) {
12116
- if (isSubagentLifecycle(record2)) return KEEP;
12117
- const decidable = record2.type === "abort" || record2.type === "user.message" || record2.type.startsWith("assistant.") || record2.type.startsWith("permission.") || record2.type.startsWith("session.") || record2.type.startsWith("tool.") || record2.type.startsWith("user_input.");
12118
- if (!decidable || isSubagentNoise(record2)) return IGNORE;
12119
- if (record2.type === "assistant.turn_end") return { kind: "turn-end" };
12120
- if (record2.type === "session.start" || record2.type === "session.resume") return ANSWER;
12373
+ classify(record3) {
12374
+ if (isSubagentLifecycle(record3)) return KEEP;
12375
+ const decidable = record3.type === "abort" || record3.type === "user.message" || record3.type.startsWith("assistant.") || record3.type.startsWith("permission.") || record3.type.startsWith("session.") || record3.type.startsWith("tool.") || record3.type.startsWith("user_input.");
12376
+ if (!decidable || isSubagentNoise(record3)) return IGNORE;
12377
+ if (record3.type === "assistant.turn_end") return { kind: "turn-end" };
12378
+ if (record3.type === "session.start" || record3.type === "session.resume") return ANSWER;
12121
12379
  return { kind: "answer-if-active" };
12122
12380
  },
12123
- classifyAfterTurnEnd(record2) {
12124
- if (isSubagentLifecycle(record2)) return KEEP;
12125
- if (isSubagentNoise(record2)) return IGNORE;
12126
- const decides = record2.type === "abort" || record2.type === "assistant.message" || record2.type === "user.message";
12381
+ classifyAfterTurnEnd(record3) {
12382
+ if (isSubagentLifecycle(record3)) return KEEP;
12383
+ if (isSubagentNoise(record3)) return IGNORE;
12384
+ const decides = record3.type === "abort" || record3.type === "assistant.message" || record3.type === "user.message";
12127
12385
  return decides ? ANSWER : IGNORE;
12128
12386
  }
12129
12387
  };
@@ -12178,14 +12436,14 @@ async function readTranscriptEffort(filePath) {
12178
12436
  for (let i = lines.length - 1; i >= 0; i--) {
12179
12437
  const line = lines[i];
12180
12438
  if (!line || !line.includes("effort") && !line.includes("Effort")) continue;
12181
- let record2;
12439
+ let record3;
12182
12440
  try {
12183
- record2 = JSON.parse(line);
12441
+ record3 = JSON.parse(line);
12184
12442
  } catch {
12185
12443
  continue;
12186
12444
  }
12187
- if (typeof record2 !== "object" || record2 === null) continue;
12188
- const effort = effortFromRecord(record2);
12445
+ if (typeof record3 !== "object" || record3 === null) continue;
12446
+ const effort = effortFromRecord(record3);
12189
12447
  if (effort) return effort;
12190
12448
  }
12191
12449
  }
@@ -12221,14 +12479,14 @@ async function getSessionStatus(filePath) {
12221
12479
  const line = lines[i];
12222
12480
  if (!line) continue;
12223
12481
  if (turnEnded && !isTailCandidate(line, turnEnded)) continue;
12224
- let record2;
12482
+ let record3;
12225
12483
  try {
12226
- record2 = JSON.parse(line);
12484
+ record3 = JSON.parse(line);
12227
12485
  } catch {
12228
12486
  continue;
12229
12487
  }
12230
- if (typeof record2?.type !== "string") continue;
12231
- const match = turnEnded ? { format: turnEnded, verdict: turnEnded.classifyAfterTurnEnd(record2) } : classifyTailRecord(record2);
12488
+ if (typeof record3?.type !== "string") continue;
12489
+ const match = turnEnded ? { format: turnEnded, verdict: turnEnded.classifyAfterTurnEnd(record3) } : classifyTailRecord(record3);
12232
12490
  if (!match) continue;
12233
12491
  const { verdict } = match;
12234
12492
  switch (verdict.kind) {
@@ -12237,20 +12495,20 @@ async function getSessionStatus(filePath) {
12237
12495
  case "final":
12238
12496
  return verdict.status;
12239
12497
  case "keep":
12240
- meaningful.unshift(record2);
12498
+ meaningful.unshift(record3);
12241
12499
  if (verdict.sawUserActivity) needUserActivity = false;
12242
12500
  continue;
12243
12501
  case "answer":
12244
- meaningful.unshift(record2);
12502
+ meaningful.unshift(record3);
12245
12503
  return deriveSessionStatus(meaningful);
12246
12504
  case "answer-if-active": {
12247
- meaningful.unshift(record2);
12505
+ meaningful.unshift(record3);
12248
12506
  const status = deriveSessionStatus(meaningful);
12249
12507
  if (status.status !== "idle") return status;
12250
12508
  continue;
12251
12509
  }
12252
12510
  case "turn-end":
12253
- meaningful.unshift(record2);
12511
+ meaningful.unshift(record3);
12254
12512
  turnEnded = match.format;
12255
12513
  needUserActivity = verdict.awaitUserActivity === true;
12256
12514
  continue;
@@ -12352,7 +12610,7 @@ var init_sessionPaths = __esm({
12352
12610
  });
12353
12611
 
12354
12612
  // ../../server/helpers.ts
12355
- import { spawn } from "node:child_process";
12613
+ import { spawn as spawn2 } from "node:child_process";
12356
12614
  import { homedir as homedir4 } from "node:os";
12357
12615
  import { randomUUID as randomUUID2 } from "node:crypto";
12358
12616
  import { createInterface } from "node:readline";
@@ -12414,6 +12672,35 @@ var init_timeout = __esm({
12414
12672
  }
12415
12673
  });
12416
12674
 
12675
+ // ../../shared/session/model-names.ts
12676
+ function capitalize(word) {
12677
+ return word.charAt(0).toUpperCase() + word.slice(1);
12678
+ }
12679
+ function shortenModel(model) {
12680
+ if (!model) return "unknown";
12681
+ const lower = model.toLowerCase();
12682
+ const context = /\[1m\]$/.test(lower) ? " 1M" : "";
12683
+ const id = lower.replace(/\[[^\]]*\]$/, "");
12684
+ for (const family of MODEL_FAMILIES) {
12685
+ const match = id.match(new RegExp(`${family}(?:-(\\d+)(?:-(\\d{1,2}))?(?!\\d))?`));
12686
+ if (!match) continue;
12687
+ const version = match[1] ? ` ${match[1]}${match[2] ? `.${match[2]}` : ""}` : "";
12688
+ return `${capitalize(family)}${version}${context}`;
12689
+ }
12690
+ if (id.startsWith("gpt-")) {
12691
+ const [version, ...rest] = id.slice(4).split("-");
12692
+ const suffix = rest.map(capitalize).join(" ");
12693
+ return `GPT-${version}${suffix ? ` ${suffix}` : ""}${context}`;
12694
+ }
12695
+ return model.length > 20 ? model.slice(0, 20) + "..." : model;
12696
+ }
12697
+ var MODEL_FAMILIES;
12698
+ var init_model_names = __esm({
12699
+ "../../shared/session/model-names.ts"() {
12700
+ MODEL_FAMILIES = ["fable", "mythos", "opus", "sonnet", "haiku"];
12701
+ }
12702
+ });
12703
+
12417
12704
  // ../../server/agents/claudeModels.ts
12418
12705
  import { query as query2 } from "@anthropic-ai/claude-agent-sdk";
12419
12706
  function mapClaudeModels(models) {
@@ -12448,6 +12735,7 @@ function mapClaudeModels(models) {
12448
12735
  ...capabilities
12449
12736
  });
12450
12737
  }
12738
+ disambiguateLabels(options);
12451
12739
  if (!options.some((o) => o.value === "")) {
12452
12740
  options.unshift({ value: "", label: "Default" });
12453
12741
  } else {
@@ -12455,6 +12743,20 @@ function mapClaudeModels(models) {
12455
12743
  }
12456
12744
  return options.length > 1 ? options : null;
12457
12745
  }
12746
+ function disambiguateLabels(options) {
12747
+ const byLabel = /* @__PURE__ */ new Map();
12748
+ for (const option of options) {
12749
+ byLabel.set(option.label, [...byLabel.get(option.label) ?? [], option]);
12750
+ }
12751
+ for (const group of byLabel.values()) {
12752
+ if (group.length < 2) continue;
12753
+ const slots = group.map((option) => options.indexOf(option));
12754
+ const relabelled = group.map((option) => ({ ...option, label: shortenModel(option.resolvedModel ?? option.value) })).sort((a, b) => b.label.localeCompare(a.label, void 0, { numeric: true }));
12755
+ slots.forEach((slot, index) => {
12756
+ options[slot] = relabelled[index];
12757
+ });
12758
+ }
12759
+ }
12458
12760
  async function fetchClaudeModels() {
12459
12761
  const abort = new AbortController();
12460
12762
  try {
@@ -12481,9 +12783,10 @@ async function fetchClaudeModels() {
12481
12783
  }
12482
12784
  var init_claudeModels = __esm({
12483
12785
  "../../server/agents/claudeModels.ts"() {
12484
- init_sdk_session();
12786
+ init_claudeExecutable();
12485
12787
  init_modelCatalog();
12486
12788
  init_timeout();
12789
+ init_model_names();
12487
12790
  }
12488
12791
  });
12489
12792
 
@@ -12589,7 +12892,7 @@ function startOneShot(req) {
12589
12892
  ]);
12590
12893
  const cleanEnv = { ...process.env };
12591
12894
  delete cleanEnv.CLAUDECODE;
12592
- const child = spawn(cli.command, cli.args, {
12895
+ const child = spawn2(cli.command, cli.args, {
12593
12896
  cwd: req.cwd,
12594
12897
  env: cleanEnv,
12595
12898
  stdio: ["ignore", "pipe", "pipe"],
@@ -12750,6 +13053,7 @@ var init_claudeRuntime = __esm({
12750
13053
  "../../server/agents/claudeRuntime.ts"() {
12751
13054
  init_agent_descriptors();
12752
13055
  init_helpers();
13056
+ init_claudeExecutable();
12753
13057
  init_claudeModels();
12754
13058
  init_spawnError();
12755
13059
  init_binaryResolver();
@@ -12896,7 +13200,7 @@ var init_package = __esm({
12896
13200
  package_default = {
12897
13201
  name: "cogpit",
12898
13202
  private: true,
12899
- version: "2.1.2",
13203
+ version: "2.2.1",
12900
13204
  description: "Control center for Claude Code, OpenAI Codex, and GitHub Copilot CLI sessions",
12901
13205
  license: "MIT",
12902
13206
  author: {
@@ -13006,11 +13310,11 @@ var init_package = __esm({
13006
13310
  overrides: {
13007
13311
  "@babel/core": "7.29.7",
13008
13312
  "@hono/node-server": "1.19.15",
13009
- "@xmldom/xmldom": "0.8.13",
13313
+ "@xmldom/xmldom": "0.8.15",
13010
13314
  "app-builder-lib": "26.15.3",
13011
13315
  "body-parser": "2.3.0",
13012
13316
  "builder-util-runtime": "9.7.0",
13013
- "fast-uri": "3.1.5",
13317
+ "fast-uri": "4.1.4",
13014
13318
  flatted: "3.4.2",
13015
13319
  "form-data": "4.0.6",
13016
13320
  hono: "4.13.1",
@@ -13019,7 +13323,7 @@ var init_package = __esm({
13019
13323
  lodash: "4.18.1",
13020
13324
  nanoid: "3.3.18",
13021
13325
  postcss: "8.5.26",
13022
- qs: "6.15.3",
13326
+ qs: "6.16.0",
13023
13327
  tar: "7.5.21",
13024
13328
  tmp: "0.2.7",
13025
13329
  undici: "7.29.0"
@@ -13135,101 +13439,6 @@ var init_codexStreamAdapter = __esm({
13135
13439
  }
13136
13440
  });
13137
13441
 
13138
- // ../../shared/versions.ts
13139
- function parseVersion(raw) {
13140
- const match = raw.trim().replace(/^v/i, "").match(/^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?/);
13141
- if (!match) return null;
13142
- return {
13143
- parts: [Number(match[1]), Number(match[2]), Number(match[3])],
13144
- prerelease: match[4] ? match[4].split(".") : []
13145
- };
13146
- }
13147
- function extractVersion(output) {
13148
- const match = output.match(/\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?/);
13149
- return match ? match[0] : null;
13150
- }
13151
- function comparePrerelease(a, b) {
13152
- if (a.length === 0 && b.length === 0) return 0;
13153
- if (a.length === 0) return 1;
13154
- if (b.length === 0) return -1;
13155
- for (let i = 0; i < Math.max(a.length, b.length); i += 1) {
13156
- const left = a[i];
13157
- const right = b[i];
13158
- if (left === void 0) return -1;
13159
- if (right === void 0) return 1;
13160
- const leftNumeric = /^\d+$/.test(left);
13161
- const rightNumeric = /^\d+$/.test(right);
13162
- if (leftNumeric && rightNumeric) {
13163
- if (Number(left) !== Number(right)) return Number(left) < Number(right) ? -1 : 1;
13164
- continue;
13165
- }
13166
- if (leftNumeric !== rightNumeric) return leftNumeric ? -1 : 1;
13167
- if (left !== right) return left < right ? -1 : 1;
13168
- }
13169
- return 0;
13170
- }
13171
- function compareVersions(a, b) {
13172
- const left = parseVersion(a);
13173
- const right = parseVersion(b);
13174
- if (!left || !right) return null;
13175
- for (let i = 0; i < 3; i += 1) {
13176
- if (left.parts[i] !== right.parts[i]) return left.parts[i] < right.parts[i] ? -1 : 1;
13177
- }
13178
- return comparePrerelease(left.prerelease, right.prerelease);
13179
- }
13180
- var init_versions = __esm({
13181
- "../../shared/versions.ts"() {
13182
- }
13183
- });
13184
-
13185
- // ../../server/lib/cliProcess.ts
13186
- import { spawn as spawn2 } from "node:child_process";
13187
- function runCli(executable, args, timeoutMs) {
13188
- return new Promise((resolve21) => {
13189
- const resolved = resolveAgentCommand(executable, [...args]);
13190
- const child = spawn2(resolved.command, resolved.args, {
13191
- stdio: ["ignore", "pipe", "pipe"],
13192
- ...resolved.spawnOptions
13193
- });
13194
- let stdout = "";
13195
- let stderr = "";
13196
- let timedOut = false;
13197
- const timer = setTimeout(() => {
13198
- timedOut = true;
13199
- child.kill("SIGKILL");
13200
- }, timeoutMs);
13201
- const append = (buffer, chunk) => buffer.length >= CLI_OUTPUT_MAX_CHARS ? buffer : buffer + chunk.toString();
13202
- child.stdout?.on("data", (chunk) => {
13203
- stdout = append(stdout, chunk);
13204
- });
13205
- child.stderr?.on("data", (chunk) => {
13206
- stderr = append(stderr, chunk);
13207
- });
13208
- child.on("error", () => {
13209
- clearTimeout(timer);
13210
- resolve21({ code: null, stdout, stderr, timedOut });
13211
- });
13212
- child.on("close", (code) => {
13213
- clearTimeout(timer);
13214
- resolve21({ code, stdout, stderr, timedOut });
13215
- });
13216
- });
13217
- }
13218
- async function probeCliVersion(command, versionArgs, timeoutMs = DEFAULT_PROBE_TIMEOUT_MS) {
13219
- const probe = await runCli(command, versionArgs, timeoutMs);
13220
- return extractVersion(`${probe.stdout}
13221
- ${probe.stderr}`);
13222
- }
13223
- var DEFAULT_PROBE_TIMEOUT_MS, CLI_OUTPUT_MAX_CHARS;
13224
- var init_cliProcess = __esm({
13225
- "../../server/lib/cliProcess.ts"() {
13226
- init_versions();
13227
- init_binaryResolver();
13228
- DEFAULT_PROBE_TIMEOUT_MS = 5e3;
13229
- CLI_OUTPUT_MAX_CHARS = 1e4;
13230
- }
13231
- });
13232
-
13233
13442
  // ../../server/lib/codexVersion.ts
13234
13443
  function parseUserAgentVersion(userAgent) {
13235
13444
  if (typeof userAgent !== "string") return null;
@@ -14460,7 +14669,7 @@ async function startLegacy(req, knownPaths) {
14460
14669
  ...args.turnArgs,
14461
14670
  ...args.promptArgs
14462
14671
  ]);
14463
- const child = spawn(cli.command, cli.args, {
14672
+ const child = spawn2(cli.command, cli.args, {
14464
14673
  cwd: req.cwd,
14465
14674
  env: process.env,
14466
14675
  stdio: ["ignore", "pipe", "pipe"],
@@ -14552,7 +14761,7 @@ async function sendLegacy(sessionId, req) {
14552
14761
  sessionId,
14553
14762
  ...args.promptArgs
14554
14763
  ]);
14555
- const child = spawn(cli.command, cli.args, {
14764
+ const child = spawn2(cli.command, cli.args, {
14556
14765
  cwd: req.cwd,
14557
14766
  env: process.env,
14558
14767
  stdio: ["ignore", "pipe", "pipe"],
@@ -15995,17 +16204,17 @@ function parseJsonRecord(line) {
15995
16204
  }
15996
16205
  }
15997
16206
  function parseClaudeUsageLine(line) {
15998
- const record2 = parseJsonRecord(line);
15999
- if (!record2 || record2.type !== "assistant") return null;
16000
- const message = asRecord3(record2.message);
16207
+ const record3 = parseJsonRecord(line);
16208
+ if (!record3 || record3.type !== "assistant") return null;
16209
+ const message = asRecord3(record3.message);
16001
16210
  const usage = message ? asRecord3(message.usage) : null;
16002
16211
  if (!message || !usage) return null;
16003
- const timestampMs = parseTimestampMs(record2.timestamp);
16212
+ const timestampMs = parseTimestampMs(record3.timestamp);
16004
16213
  if (timestampMs === null) return null;
16005
16214
  const model = typeof message.model === "string" ? message.model : "";
16006
16215
  if (model.length === 0) return null;
16007
16216
  const messageId = typeof message.id === "string" ? message.id : null;
16008
- const requestId = typeof record2.requestId === "string" ? record2.requestId : null;
16217
+ const requestId = typeof record3.requestId === "string" ? record3.requestId : null;
16009
16218
  const dedupeKey = messageId === null && requestId === null ? null : `${messageId ?? ""}:${requestId ?? ""}`;
16010
16219
  const cacheCreationTokens = int(usage.cache_creation_input_tokens);
16011
16220
  const byTtl = asRecord3(usage.cache_creation);
@@ -16024,12 +16233,12 @@ function parseClaudeUsageLine(line) {
16024
16233
  reasoningTokens: 0
16025
16234
  };
16026
16235
  if (totalUsageCostTokens(totals) === 0) return null;
16027
- const cost = record2.costUSD;
16236
+ const cost = record3.costUSD;
16028
16237
  return {
16029
16238
  provider: "claude",
16030
16239
  timestampMs,
16031
16240
  model,
16032
- sessionId: typeof record2.sessionId === "string" ? record2.sessionId : "",
16241
+ sessionId: typeof record3.sessionId === "string" ? record3.sessionId : "",
16033
16242
  totals,
16034
16243
  reportedCostUsd: typeof cost === "number" && Number.isFinite(cost) ? cost : null,
16035
16244
  dedupeKey
@@ -16051,30 +16260,30 @@ function isForkedSessionMeta(payload) {
16051
16260
  return typeof spawn5?.parent_thread_id === "string";
16052
16261
  }
16053
16262
  function parseCodexUsageLine(line, state) {
16054
- const record2 = parseJsonRecord(line);
16055
- if (!record2) return null;
16056
- const payload = asRecord3(record2.payload);
16263
+ const record3 = parseJsonRecord(line);
16264
+ if (!record3) return null;
16265
+ const payload = asRecord3(record3.payload);
16057
16266
  if (!payload) return null;
16058
- if (record2.type === "session_meta") {
16267
+ if (record3.type === "session_meta") {
16059
16268
  if (state.sawSessionMeta) return null;
16060
16269
  state.sawSessionMeta = true;
16061
16270
  const id = payload.id ?? payload.session_id;
16062
16271
  if (typeof id === "string") state.sessionId = id;
16063
- const metaTimestampMs = parseTimestampMs(record2.timestamp);
16272
+ const metaTimestampMs = parseTimestampMs(record3.timestamp);
16064
16273
  if (metaTimestampMs !== null && isForkedSessionMeta(payload)) {
16065
16274
  state.suppressingForkCopies = true;
16066
16275
  state.forkCopyAnchorMs = metaTimestampMs;
16067
16276
  }
16068
16277
  return null;
16069
16278
  }
16070
- if (record2.type === "turn_context") {
16279
+ if (record3.type === "turn_context") {
16071
16280
  if (typeof payload.model === "string") state.model = payload.model;
16072
16281
  return null;
16073
16282
  }
16074
16283
  if (payload.type !== "token_count") return null;
16075
16284
  const last = asRecord3(asRecord3(payload.info)?.last_token_usage);
16076
16285
  if (!last) return null;
16077
- const timestampMs = parseTimestampMs(record2.timestamp);
16286
+ const timestampMs = parseTimestampMs(record3.timestamp);
16078
16287
  if (timestampMs === null) return null;
16079
16288
  if (state.model.length === 0) return null;
16080
16289
  const signature = JSON.stringify(last);
@@ -16173,17 +16382,17 @@ function parseCopilotUsageMetrics(value, state, timestampMs) {
16173
16382
  return records;
16174
16383
  }
16175
16384
  function parseCopilotUsageLine(line, state) {
16176
- const record2 = parseJsonRecord(line);
16177
- if (!record2 || typeof record2.agentId === "string" && record2.agentId.length > 0) return [];
16178
- const data = asRecord3(record2.data);
16385
+ const record3 = parseJsonRecord(line);
16386
+ if (!record3 || typeof record3.agentId === "string" && record3.agentId.length > 0) return [];
16387
+ const data = asRecord3(record3.data);
16179
16388
  if (!data) return [];
16180
- if (record2.type === "session.start") {
16389
+ if (record3.type === "session.start") {
16181
16390
  const sessionId = data.sessionId;
16182
16391
  if (typeof sessionId === "string" && sessionId.length > 0) state.sessionId = sessionId;
16183
16392
  return [];
16184
16393
  }
16185
- if (record2.type !== "session.shutdown") return [];
16186
- const timestampMs = parseTimestampMs(record2.timestamp);
16394
+ if (record3.type !== "session.shutdown") return [];
16395
+ const timestampMs = parseTimestampMs(record3.timestamp);
16187
16396
  return timestampMs === null ? [] : parseCopilotUsageMetrics(data, state, timestampMs);
16188
16397
  }
16189
16398
  function createUsageScanner(provider, filePath) {
@@ -16200,8 +16409,8 @@ var init_usageScanners = __esm({
16200
16409
  // object, so a line is either self-contained or of no interest.
16201
16410
  wantsLine: (line) => line.includes('"usage"'),
16202
16411
  accept: (line) => {
16203
- const record2 = parseClaudeUsageLine(line);
16204
- return record2 === null ? [] : [record2];
16412
+ const record3 = parseClaudeUsageLine(line);
16413
+ return record3 === null ? [] : [record3];
16205
16414
  }
16206
16415
  }),
16207
16416
  codex: () => {
@@ -16212,8 +16421,8 @@ var init_usageScanners = __esm({
16212
16421
  // at all but still have to reach the reducer.
16213
16422
  wantsLine: (line) => line.includes('"token_count"') || line.includes('"turn_context"') || line.includes('"session_meta"'),
16214
16423
  accept: (line) => {
16215
- const record2 = parseCodexUsageLine(line, state);
16216
- return record2 === null ? [] : [record2];
16424
+ const record3 = parseCodexUsageLine(line, state);
16425
+ return record3 === null ? [] : [record3];
16217
16426
  }
16218
16427
  };
16219
16428
  },
@@ -17095,11 +17304,11 @@ function registerRunningProcessesRoute(use) {
17095
17304
  if (url.pathname.split("/").filter(Boolean).length > 0) return next();
17096
17305
  const isWindows2 = process.platform === "win32";
17097
17306
  const nameFilter = allDescriptors().map(windowsProcessNameFilter).join(" or ");
17098
- const child = isWindows2 ? spawn("powershell", [
17307
+ const child = isWindows2 ? spawn2("powershell", [
17099
17308
  "-NoProfile",
17100
17309
  "-Command",
17101
17310
  `Get-CimInstance Win32_Process -Filter "${nameFilter}" | Select-Object ProcessId, WorkingSetSize, CommandLine | ConvertTo-Json -Compress`
17102
- ]) : spawn("ps", ["aux"]);
17311
+ ]) : spawn2("ps", ["aux"]);
17103
17312
  let stdout = "";
17104
17313
  let responded = false;
17105
17314
  child.stdout?.on("data", (data) => {
@@ -17462,12 +17671,12 @@ function registerBranchSessionRoute(use) {
17462
17671
  }
17463
17672
  }
17464
17673
  const newSessionId = randomUUID2();
17465
- const { record: record2, originalId } = format.brandBranch(
17674
+ const { record: record3, originalId } = format.brandBranch(
17466
17675
  JSON.parse(lines[0]),
17467
17676
  newSessionId,
17468
17677
  turnIndex ?? null
17469
17678
  );
17470
- lines[0] = JSON.stringify(record2);
17679
+ lines[0] = JSON.stringify(record3);
17471
17680
  const target = storeForDirName(dirName).transcriptPath(dirName, newSessionId);
17472
17681
  if (!target) {
17473
17682
  res.statusCode = 500;
@@ -18010,11 +18219,11 @@ import {
18010
18219
  dirname as dirname5,
18011
18220
  isAbsolute as isAbsolute3,
18012
18221
  join as join13,
18013
- posix as posix2,
18222
+ posix as posix3,
18014
18223
  relative as relative4,
18015
18224
  resolve as resolve9,
18016
18225
  sep as sep5,
18017
- win32 as win322
18226
+ win32 as win323
18018
18227
  } from "node:path";
18019
18228
  function configuredAgentHomes() {
18020
18229
  return new Set(AGENT_KINDS.map((kind) => agentHomeDir(kind)));
@@ -18123,7 +18332,7 @@ async function resolveConfigBrowserPath(filePath, options = {}) {
18123
18332
  function isSafeConfigName(name) {
18124
18333
  if (!name || name === "." || name === ".." || name.includes("\0")) return false;
18125
18334
  if (name.includes("/") || name.includes("\\")) return false;
18126
- if (isAbsolute3(name) || posix2.parse(name).root || win322.parse(name).root) return false;
18335
+ if (isAbsolute3(name) || posix3.parse(name).root || win323.parse(name).root) return false;
18127
18336
  return true;
18128
18337
  }
18129
18338
  function isUnderDir(parentDir, dirName) {
@@ -19366,7 +19575,8 @@ function registerConfigRoutes(use) {
19366
19575
  networkPassword: config.networkPassword ? "set" : null,
19367
19576
  terminalApp: config.terminalApp || null,
19368
19577
  editorApp: config.editorApp || null,
19369
- useBuiltInEditor: config.useBuiltInEditor || false
19578
+ useBuiltInEditor: config.useBuiltInEditor || false,
19579
+ agentExecutable: config.agentExecutable ?? DEFAULT_EXECUTABLE_CHOICE
19370
19580
  } : null));
19371
19581
  return;
19372
19582
  }
@@ -19429,7 +19639,8 @@ function registerConfigRoutes(use) {
19429
19639
  networkPassword: finalPassword,
19430
19640
  terminalApp: parsed.terminalApp || void 0,
19431
19641
  editorApp: parsed.editorApp || void 0,
19432
- useBuiltInEditor: !!parsed.useBuiltInEditor
19642
+ useBuiltInEditor: !!parsed.useBuiltInEditor,
19643
+ agentExecutable: parseExecutableChoice(parsed.agentExecutable)
19433
19644
  });
19434
19645
  refreshDirs();
19435
19646
  if (currentConfig && resolve11(resolvedClaudeDir) !== resolve11(currentConfig.claudeDir)) {
@@ -19464,12 +19675,13 @@ var init_config2 = __esm({
19464
19675
  init_users();
19465
19676
  init_config();
19466
19677
  init_agent_descriptors();
19678
+ init_agentExecutable();
19467
19679
  init_sessionPersistence();
19468
19680
  }
19469
19681
  });
19470
19682
 
19471
19683
  // ../../server/routes/hello.ts
19472
- import { readFileSync } from "node:fs";
19684
+ import { readFileSync as readFileSync2 } from "node:fs";
19473
19685
  import { fileURLToPath as fileURLToPath2 } from "node:url";
19474
19686
  import { hostname } from "node:os";
19475
19687
  import { join as join18 } from "node:path";
@@ -19518,7 +19730,7 @@ var init_hello = __esm({
19518
19730
  join18(__dirname2, "../package.json")
19519
19731
  ]) {
19520
19732
  try {
19521
- const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
19733
+ const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
19522
19734
  if (typeof pkg.version === "string") return pkg.version;
19523
19735
  } catch {
19524
19736
  }
@@ -20749,7 +20961,7 @@ function registerFileRoutes(use) {
20749
20961
  try {
20750
20962
  let spawnLines = function(args, cwd) {
20751
20963
  return new Promise((resolve21) => {
20752
- const proc = spawn("git", args, { cwd });
20964
+ const proc = spawn2("git", args, { cwd });
20753
20965
  let out = "";
20754
20966
  proc.stdout.on("data", (d) => out += d.toString());
20755
20967
  proc.on("close", (code) => {
@@ -20760,7 +20972,7 @@ function registerFileRoutes(use) {
20760
20972
  });
20761
20973
  }, spawnOutput = function(args, cwd) {
20762
20974
  return new Promise((resolve21) => {
20763
- const proc = spawn("git", args, { cwd });
20975
+ const proc = spawn2("git", args, { cwd });
20764
20976
  let out = "";
20765
20977
  proc.stdout.on("data", (d) => out += d.toString());
20766
20978
  proc.on("close", () => resolve21(out.trim()));
@@ -20789,7 +21001,7 @@ function registerFileRoutes(use) {
20789
21001
  }
20790
21002
  }
20791
21003
  return new Promise((resolve21) => {
20792
- const proc = spawn("git", ["rev-parse", "--show-toplevel"], { cwd });
21004
+ const proc = spawn2("git", ["rev-parse", "--show-toplevel"], { cwd });
20793
21005
  let out = "";
20794
21006
  proc.stdout.on("data", (d) => out += d.toString());
20795
21007
  proc.on("close", (code) => {
@@ -21034,11 +21246,11 @@ function parseGitStatus(output) {
21034
21246
  const branch = parseBranchHeader(header);
21035
21247
  const files = [];
21036
21248
  for (let index = 0; index < records.length; index += 1) {
21037
- const record2 = records[index];
21038
- if (!record2 || record2.length < 3) continue;
21039
- const indexStatus = record2[0];
21040
- const workTreeStatus = record2[1];
21041
- const path = record2.slice(3);
21249
+ const record3 = records[index];
21250
+ if (!record3 || record3.length < 3) continue;
21251
+ const indexStatus = record3[0];
21252
+ const workTreeStatus = record3[1];
21253
+ const path = record3.slice(3);
21042
21254
  const renamedOrCopied = /[RC]/.test(`${indexStatus}${workTreeStatus}`);
21043
21255
  const originalPath = renamedOrCopied ? records[index += 1] || void 0 : void 0;
21044
21256
  files.push({ path, originalPath, indexStatus, workTreeStatus });
@@ -21093,6 +21305,272 @@ var init_git_status = __esm({
21093
21305
  }
21094
21306
  });
21095
21307
 
21308
+ // ../../server/routes/github-actions.ts
21309
+ import { execFile as execFileCallback2 } from "node:child_process";
21310
+ import { promisify as promisify2 } from "node:util";
21311
+ function record2(value) {
21312
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
21313
+ }
21314
+ function stringValue(value, fallback = "") {
21315
+ return typeof value === "string" ? value : fallback;
21316
+ }
21317
+ function numberValue(value) {
21318
+ return typeof value === "number" && Number.isSafeInteger(value) ? value : null;
21319
+ }
21320
+ function nullableDate(value) {
21321
+ return typeof value === "string" && value ? value : null;
21322
+ }
21323
+ function workflowStatus(value) {
21324
+ return typeof value === "string" && RUN_STATUSES.has(value) ? value : "pending";
21325
+ }
21326
+ function workflowConclusion(value) {
21327
+ return typeof value === "string" && RUN_CONCLUSIONS.has(value) ? value : null;
21328
+ }
21329
+ function parseGitHubRemote(remoteUrl) {
21330
+ const trimmed = remoteUrl.trim();
21331
+ let host = "";
21332
+ let pathname = "";
21333
+ try {
21334
+ const parsed = new URL(trimmed);
21335
+ host = parsed.hostname;
21336
+ pathname = parsed.pathname;
21337
+ } catch {
21338
+ const scp = trimmed.match(/^(?:[^@\s]+@)?([^:/\s]+):(.+)$/);
21339
+ if (!scp) return null;
21340
+ host = scp[1];
21341
+ pathname = scp[2];
21342
+ }
21343
+ const parts = pathname.replace(/^\/+/, "").replace(/\.git$/, "").split("/");
21344
+ if (parts.length !== 2 || !GITHUB_HOST.test(host) || host.toLowerCase() !== "github.com" && !host.toLowerCase().startsWith("github.") || !GITHUB_NAME.test(parts[0]) || !GITHUB_NAME.test(parts[1])) {
21345
+ return null;
21346
+ }
21347
+ return { host: host.toLowerCase(), owner: parts[0], name: parts[1] };
21348
+ }
21349
+ function parseRun(value) {
21350
+ const source = record2(value);
21351
+ if (!source) return null;
21352
+ const id = numberValue(source.id);
21353
+ const url = stringValue(source.html_url);
21354
+ if (id === null || !url.startsWith("https://")) return null;
21355
+ const actor = record2(source.actor);
21356
+ return {
21357
+ id,
21358
+ name: stringValue(source.name, "Workflow"),
21359
+ displayTitle: stringValue(source.display_title, stringValue(source.name, "Workflow run")),
21360
+ status: workflowStatus(source.status),
21361
+ conclusion: workflowConclusion(source.conclusion),
21362
+ url,
21363
+ runNumber: numberValue(source.run_number) ?? 0,
21364
+ event: stringValue(source.event, "workflow"),
21365
+ branch: stringValue(source.head_branch),
21366
+ commitSha: stringValue(source.head_sha),
21367
+ createdAt: stringValue(source.created_at),
21368
+ updatedAt: stringValue(source.updated_at),
21369
+ actor: stringValue(actor?.login)
21370
+ };
21371
+ }
21372
+ function parseStep(value) {
21373
+ const source = record2(value);
21374
+ if (!source) return null;
21375
+ const number = numberValue(source.number);
21376
+ if (number === null) return null;
21377
+ return {
21378
+ number,
21379
+ name: stringValue(source.name, `Step ${number}`),
21380
+ status: workflowStatus(source.status),
21381
+ conclusion: workflowConclusion(source.conclusion),
21382
+ startedAt: nullableDate(source.started_at),
21383
+ completedAt: nullableDate(source.completed_at)
21384
+ };
21385
+ }
21386
+ function parseJob(value) {
21387
+ const source = record2(value);
21388
+ if (!source) return null;
21389
+ const id = numberValue(source.id);
21390
+ const url = stringValue(source.html_url);
21391
+ if (id === null || !url.startsWith("https://")) return null;
21392
+ return {
21393
+ id,
21394
+ name: stringValue(source.name, "Job"),
21395
+ status: workflowStatus(source.status),
21396
+ conclusion: workflowConclusion(source.conclusion),
21397
+ url,
21398
+ startedAt: nullableDate(source.started_at),
21399
+ completedAt: nullableDate(source.completed_at),
21400
+ steps: Array.isArray(source.steps) ? source.steps.map(parseStep).filter((step) => step !== null) : []
21401
+ };
21402
+ }
21403
+ function parseRunsResponse(value) {
21404
+ const source = record2(value);
21405
+ if (!source || !Array.isArray(source.workflow_runs)) {
21406
+ throw new GitHubActionsRouteError(502, "invalid_response", "GitHub returned an invalid workflow response");
21407
+ }
21408
+ return source.workflow_runs.map(parseRun).filter((run2) => run2 !== null);
21409
+ }
21410
+ function parseJobsResponse(value) {
21411
+ const source = record2(value);
21412
+ if (!source || !Array.isArray(source.jobs)) {
21413
+ throw new GitHubActionsRouteError(502, "invalid_response", "GitHub returned an invalid jobs response");
21414
+ }
21415
+ return source.jobs.map(parseJob).filter((job) => job !== null);
21416
+ }
21417
+ async function runGitHubApi(repository, endpoint) {
21418
+ try {
21419
+ const result = await execFile3("gh", [
21420
+ "api",
21421
+ endpoint,
21422
+ "--hostname",
21423
+ repository.host,
21424
+ "-H",
21425
+ "Accept: application/vnd.github+json"
21426
+ ], {
21427
+ encoding: "utf-8",
21428
+ env: { ...process.env, GH_PAGER: "cat", NO_COLOR: "1" },
21429
+ maxBuffer: 8 * 1024 * 1024,
21430
+ timeout: 2e4,
21431
+ windowsHide: true
21432
+ });
21433
+ return JSON.parse(result.stdout);
21434
+ } catch (error) {
21435
+ const failure = error;
21436
+ if (failure.code === "ENOENT") {
21437
+ throw new GitHubActionsRouteError(503, "gh_missing", "Install the GitHub CLI to view workflow runs");
21438
+ }
21439
+ const detail = `${failure.message ?? ""}
21440
+ ${failure.stderr ?? ""}`;
21441
+ if (/auth login|not logged|authentication|oauth token/i.test(detail)) {
21442
+ throw new GitHubActionsRouteError(503, "gh_auth_required", "Sign in with `gh auth login` to view workflow runs");
21443
+ }
21444
+ throw new GitHubActionsRouteError(502, "github_api_failed", "GitHub workflow data is unavailable");
21445
+ }
21446
+ }
21447
+ async function resolveRepository(cwd, dependencies) {
21448
+ const project = await dependencies.resolveProject(cwd);
21449
+ if (!project.ok) throw new GitHubActionsRouteError(project.status, "no_git_repository", project.error);
21450
+ if (!project.root) {
21451
+ throw new GitHubActionsRouteError(400, "no_git_repository", "This project is not a Git repository");
21452
+ }
21453
+ let remoteUrl = "";
21454
+ try {
21455
+ remoteUrl = (await dependencies.git(project.root, ["remote", "get-url", "origin"])).stdout;
21456
+ } catch {
21457
+ throw new GitHubActionsRouteError(400, "no_github_remote", "This repository has no origin remote");
21458
+ }
21459
+ const repository = parseGitHubRemote(remoteUrl);
21460
+ if (!repository) {
21461
+ throw new GitHubActionsRouteError(400, "no_github_remote", "The origin remote is not a supported GitHub repository");
21462
+ }
21463
+ const branchResult = await dependencies.git(project.root, ["branch", "--show-current"]);
21464
+ return {
21465
+ repository,
21466
+ branch: branchResult.stdout.trim() || null
21467
+ };
21468
+ }
21469
+ function repositoryName(repository) {
21470
+ return `${repository.owner}/${repository.name}`;
21471
+ }
21472
+ function repositoryUrl(repository) {
21473
+ return `https://${repository.host}/${repository.owner}/${repository.name}`;
21474
+ }
21475
+ function sendRouteError2(res, error) {
21476
+ if (error instanceof GitHubActionsRouteError) {
21477
+ sendJson(res, error.status, { error: error.message, code: error.code });
21478
+ return;
21479
+ }
21480
+ sendJson(res, 500, { error: "Unable to load GitHub Actions", code: "github_api_failed" });
21481
+ }
21482
+ function registerGitHubActionsRoutes(use, dependencies = defaultDependencies) {
21483
+ use("/api/github-actions", async (req, res, next) => {
21484
+ if (req.method !== "GET") return next();
21485
+ const url = new URL(req.url || "", "http://localhost");
21486
+ if (url.pathname !== "/" && url.pathname !== "") return next();
21487
+ const cwd = url.searchParams.get("cwd") ?? "";
21488
+ const requestedLimit = Number(url.searchParams.get("limit") ?? DEFAULT_RUNS);
21489
+ const limit = Number.isInteger(requestedLimit) ? Math.min(MAX_RUNS, Math.max(1, requestedLimit)) : DEFAULT_RUNS;
21490
+ try {
21491
+ const { repository, branch } = await resolveRepository(cwd, dependencies);
21492
+ const endpoint = `repos/${repository.owner}/${repository.name}/actions/runs?per_page=${limit}`;
21493
+ const runs = parseRunsResponse(await dependencies.githubApi(repository, endpoint));
21494
+ const response = {
21495
+ repository: repositoryName(repository),
21496
+ repositoryUrl: repositoryUrl(repository),
21497
+ branch,
21498
+ runs
21499
+ };
21500
+ sendJson(res, 200, response);
21501
+ } catch (error) {
21502
+ sendRouteError2(res, error);
21503
+ }
21504
+ });
21505
+ use("/api/github-actions/jobs", async (req, res, next) => {
21506
+ if (req.method !== "GET") return next();
21507
+ const url = new URL(req.url || "", "http://localhost");
21508
+ if (url.pathname !== "/" && url.pathname !== "") return next();
21509
+ const cwd = url.searchParams.get("cwd") ?? "";
21510
+ const runIdText = url.searchParams.get("runId") ?? "";
21511
+ if (!/^\d+$/.test(runIdText)) {
21512
+ return sendJson(res, 400, { error: "runId must be a positive integer", code: "github_api_failed" });
21513
+ }
21514
+ try {
21515
+ const { repository } = await resolveRepository(cwd, dependencies);
21516
+ const runId = Number(runIdText);
21517
+ const endpoint = `repos/${repository.owner}/${repository.name}/actions/runs/${runId}/jobs?filter=latest&per_page=100`;
21518
+ const jobs = parseJobsResponse(await dependencies.githubApi(repository, endpoint));
21519
+ const response = {
21520
+ repository: repositoryName(repository),
21521
+ runId,
21522
+ jobs
21523
+ };
21524
+ sendJson(res, 200, response);
21525
+ } catch (error) {
21526
+ sendRouteError2(res, error);
21527
+ }
21528
+ });
21529
+ }
21530
+ var execFile3, MAX_RUNS, DEFAULT_RUNS, GITHUB_NAME, GITHUB_HOST, RUN_STATUSES, RUN_CONCLUSIONS, GitHubActionsRouteError, defaultDependencies;
21531
+ var init_github_actions = __esm({
21532
+ "../../server/routes/github-actions.ts"() {
21533
+ init_http();
21534
+ init_gitProject();
21535
+ execFile3 = promisify2(execFileCallback2);
21536
+ MAX_RUNS = 30;
21537
+ DEFAULT_RUNS = 20;
21538
+ GITHUB_NAME = /^[A-Za-z0-9_.-]+$/;
21539
+ GITHUB_HOST = /^[A-Za-z0-9.-]+$/;
21540
+ RUN_STATUSES = /* @__PURE__ */ new Set([
21541
+ "completed",
21542
+ "in_progress",
21543
+ "pending",
21544
+ "queued",
21545
+ "requested",
21546
+ "waiting"
21547
+ ]);
21548
+ RUN_CONCLUSIONS = /* @__PURE__ */ new Set([
21549
+ "action_required",
21550
+ "cancelled",
21551
+ "failure",
21552
+ "neutral",
21553
+ "skipped",
21554
+ "stale",
21555
+ "startup_failure",
21556
+ "success",
21557
+ "timed_out"
21558
+ ]);
21559
+ GitHubActionsRouteError = class extends Error {
21560
+ constructor(status, code, message) {
21561
+ super(message);
21562
+ this.status = status;
21563
+ this.code = code;
21564
+ }
21565
+ };
21566
+ defaultDependencies = {
21567
+ resolveProject: resolveGitProject,
21568
+ git: runGit,
21569
+ githubApi: runGitHubApi
21570
+ };
21571
+ }
21572
+ });
21573
+
21096
21574
  // ../../server/lib/projectIcon.ts
21097
21575
  import { realpath as realpath8, stat as stat13 } from "node:fs/promises";
21098
21576
  import { basename as basename9, extname, join as join20, relative as relative8, sep as sep10 } from "node:path";
@@ -21276,7 +21754,7 @@ var init_local_file = __esm({
21276
21754
  });
21277
21755
 
21278
21756
  // ../../server/routes/mcp.ts
21279
- import { execFile as execFile3 } from "node:child_process";
21757
+ import { execFile as execFile4 } from "node:child_process";
21280
21758
  import { isAbsolute as isAbsolute9, join as join21 } from "node:path";
21281
21759
  import { readFile as readFile15 } from "node:fs/promises";
21282
21760
  import { homedir as homedir7 } from "node:os";
@@ -21357,7 +21835,7 @@ async function getMcpServers(cwd) {
21357
21835
  const env = { ...process.env };
21358
21836
  delete env.CLAUDECODE;
21359
21837
  const cli = resolveAgentCommand("claude", ["mcp", "list"]);
21360
- execFile3(cli.command, cli.args, { cwd, env, timeout: 15e3, ...cli.spawnOptions }, (err, stdout) => {
21838
+ execFile4(cli.command, cli.args, { cwd, env, timeout: 15e3, ...cli.spawnOptions }, (err, stdout) => {
21361
21839
  if (err) {
21362
21840
  resolve21({ servers: cached?.servers ?? [], configs });
21363
21841
  return;
@@ -22156,8 +22634,8 @@ function copilotText(value) {
22156
22634
  if (!Array.isArray(value)) return "";
22157
22635
  return value.flatMap((part) => {
22158
22636
  if (typeof part === "string") return part ? [part] : [];
22159
- const record2 = asRecord5(part);
22160
- const text = str(record2.text) || str(record2.content);
22637
+ const record3 = asRecord5(part);
22638
+ const text = str(record3.text) || str(record3.content);
22161
22639
  return text ? [text] : [];
22162
22640
  }).join("\n");
22163
22641
  }
@@ -22860,7 +23338,7 @@ var init_notifications = __esm({
22860
23338
  });
22861
23339
 
22862
23340
  // ../../server/lib/systemProcesses.ts
22863
- import { execFile as execFile4 } from "node:child_process";
23341
+ import { execFile as execFile5 } from "node:child_process";
22864
23342
  function parseEtimeSeconds(etime) {
22865
23343
  const match = /^(?:(\d+)-)?(?:(\d+):)?(\d+):(\d+)$/.exec(etime.trim());
22866
23344
  if (!match) return 0;
@@ -22982,7 +23460,7 @@ function classifyProcesses(rows, selfPid) {
22982
23460
  }
22983
23461
  function listSystemProcesses() {
22984
23462
  return new Promise((resolve21, reject) => {
22985
- execFile4(
23463
+ execFile5(
22986
23464
  "ps",
22987
23465
  ["-axo", "pid=,ppid=,pcpu=,rss=,etime=,args="],
22988
23466
  { maxBuffer: 8 * 1024 * 1024 },
@@ -23020,7 +23498,7 @@ var init_systemProcesses = __esm({
23020
23498
  });
23021
23499
 
23022
23500
  // ../../server/lib/desktopNotify.ts
23023
- import { execFile as execFile5 } from "node:child_process";
23501
+ import { execFile as execFile6 } from "node:child_process";
23024
23502
  function parentMessagePort() {
23025
23503
  const { parentPort } = process;
23026
23504
  return parentPort && typeof parentPort.postMessage === "function" ? parentPort : null;
@@ -23033,7 +23511,7 @@ function showNotification(title, body, nav, historyId) {
23033
23511
  return;
23034
23512
  }
23035
23513
  if (process.platform === "darwin") {
23036
- execFile5(
23514
+ execFile6(
23037
23515
  "osascript",
23038
23516
  ["-e", `display notification "${escapeAppleScript(body)}" with title "${escapeAppleScript(title)}"`],
23039
23517
  // This is the standalone server's only desktop channel, so a silent
@@ -23074,7 +23552,7 @@ var init_notifications2 = __esm({
23074
23552
  });
23075
23553
 
23076
23554
  // ../../server/lib/pushConfig.ts
23077
- import { readFileSync as readFileSync2, statSync as statSync2 } from "node:fs";
23555
+ import { readFileSync as readFileSync3, statSync as statSync3 } from "node:fs";
23078
23556
  import { homedir as homedir9 } from "node:os";
23079
23557
  import { join as join23 } from "node:path";
23080
23558
  function warnOnce(message) {
@@ -23087,7 +23565,7 @@ function asString(value) {
23087
23565
  }
23088
23566
  function fileMtime(path) {
23089
23567
  try {
23090
- return statSync2(path).mtimeMs;
23568
+ return statSync3(path).mtimeMs;
23091
23569
  } catch {
23092
23570
  return null;
23093
23571
  }
@@ -23102,8 +23580,8 @@ function warnIfExposed(path, mode) {
23102
23580
  function readConfigFile(path) {
23103
23581
  let raw;
23104
23582
  try {
23105
- warnIfExposed(path, statSync2(path).mode);
23106
- raw = readFileSync2(path, "utf8");
23583
+ warnIfExposed(path, statSync3(path).mode);
23584
+ raw = readFileSync3(path, "utf8");
23107
23585
  } catch (err) {
23108
23586
  const code = err.code;
23109
23587
  if (code !== "ENOENT") warnOnce(`Could not read ${path}: ${code ?? String(err)}`);
@@ -23906,7 +24384,7 @@ var init_backgroundAgents = __esm({
23906
24384
  // ../../server/routes/ports/listeningProcesses.ts
23907
24385
  function runCapture(command, args) {
23908
24386
  return new Promise((resolve21, reject) => {
23909
- const child = spawn(command, args);
24387
+ const child = spawn2(command, args);
23910
24388
  let stdout = "";
23911
24389
  child.stdout?.on("data", (data) => {
23912
24390
  stdout += data.toString();
@@ -24193,13 +24671,13 @@ var init_project_files_ranking = __esm({
24193
24671
  });
24194
24672
 
24195
24673
  // ../../server/routes/project-files.ts
24196
- import { execFile as execFileCallback2 } from "node:child_process";
24674
+ import { execFile as execFileCallback3 } from "node:child_process";
24197
24675
  import { readdir as readdir11, realpath as realpath10, stat as stat16 } from "node:fs/promises";
24198
24676
  import { isAbsolute as isAbsolute11, relative as relative9, resolve as resolve17, sep as sep11 } from "node:path";
24199
- import { promisify as promisify2 } from "node:util";
24677
+ import { promisify as promisify3 } from "node:util";
24200
24678
  async function listGitProjectFiles(root) {
24201
24679
  try {
24202
- const result = await execFile6(
24680
+ const result = await execFile7(
24203
24681
  "git",
24204
24682
  ["ls-files", "--cached", "--others", "--exclude-standard", "-z", "--", "."],
24205
24683
  {
@@ -24275,7 +24753,7 @@ function registerProjectFileRoutes(use) {
24275
24753
  }
24276
24754
  });
24277
24755
  }
24278
- var MAX_FILES, CACHE_TTL_MS3, IGNORED_DIRECTORIES, fileCache, execFile6;
24756
+ var MAX_FILES, CACHE_TTL_MS3, IGNORED_DIRECTORIES, fileCache, execFile7;
24279
24757
  var init_project_files = __esm({
24280
24758
  "../../server/routes/project-files.ts"() {
24281
24759
  init_http();
@@ -24296,7 +24774,7 @@ var init_project_files = __esm({
24296
24774
  "target"
24297
24775
  ]);
24298
24776
  fileCache = /* @__PURE__ */ new Map();
24299
- execFile6 = promisify2(execFileCallback2);
24777
+ execFile7 = promisify3(execFileCallback3);
24300
24778
  }
24301
24779
  });
24302
24780
 
@@ -24559,12 +25037,12 @@ function createPullRequestScanner() {
24559
25037
  const command = findCreateCommand(input);
24560
25038
  if (command) openCreates.set(id, { command, timestamp });
24561
25039
  }
24562
- function recordPrLink(record2, timestamp) {
24563
- const url = record2.prUrl;
24564
- const number = record2.prNumber;
25040
+ function recordPrLink(record3, timestamp) {
25041
+ const url = record3.prUrl;
25042
+ const number = record3.prNumber;
24565
25043
  if (typeof url !== "string" || !url) return;
24566
25044
  if (typeof number !== "number" || !Number.isInteger(number) || number <= 0) return;
24567
- const repo = typeof record2.prRepository === "string" ? record2.prRepository : "";
25045
+ const repo = typeof record3.prRepository === "string" ? record3.prRepository : "";
24568
25046
  collector.add({
24569
25047
  url,
24570
25048
  number,
@@ -24599,19 +25077,19 @@ function createPullRequestScanner() {
24599
25077
  if (!line.includes(GH_PR_HINT) && !line.includes(URL_HINT) && !line.includes(PR_LINK_HINT)) {
24600
25078
  return;
24601
25079
  }
24602
- let record2;
25080
+ let record3;
24603
25081
  try {
24604
- record2 = JSON.parse(line);
25082
+ record3 = JSON.parse(line);
24605
25083
  } catch {
24606
25084
  return;
24607
25085
  }
24608
- if (!isRecord5(record2)) return;
24609
- const timestamp = typeof record2.timestamp === "string" ? record2.timestamp : "";
24610
- if (record2.type === "pr-link") {
24611
- recordPrLink(record2, timestamp);
25086
+ if (!isRecord5(record3)) return;
25087
+ const timestamp = typeof record3.timestamp === "string" ? record3.timestamp : "";
25088
+ if (record3.type === "pr-link") {
25089
+ recordPrLink(record3, timestamp);
24612
25090
  return;
24613
25091
  }
24614
- const payload = record2.payload;
25092
+ const payload = record3.payload;
24615
25093
  if (isRecord5(payload)) {
24616
25094
  const type = payload.type;
24617
25095
  if (type === "function_call" || type === "custom_tool_call") {
@@ -24621,15 +25099,15 @@ function createPullRequestScanner() {
24621
25099
  }
24622
25100
  return;
24623
25101
  }
24624
- const data = record2.data;
25102
+ const data = record3.data;
24625
25103
  if (isRecord5(data)) {
24626
- if (record2.type === "tool.execution_start") {
25104
+ if (record3.type === "tool.execution_start") {
24627
25105
  recordToolCall(
24628
25106
  data.toolCallId,
24629
25107
  parseArgs2(data.arguments ?? data.input),
24630
25108
  timestamp
24631
25109
  );
24632
- } else if (record2.type === "tool.execution_complete") {
25110
+ } else if (record3.type === "tool.execution_complete") {
24633
25111
  recordResult(
24634
25112
  data.toolCallId,
24635
25113
  data.result ?? data.output ?? data.error,
@@ -24638,7 +25116,7 @@ function createPullRequestScanner() {
24638
25116
  }
24639
25117
  return;
24640
25118
  }
24641
- const message = record2.message;
25119
+ const message = record3.message;
24642
25120
  if (!isRecord5(message) || !Array.isArray(message.content)) return;
24643
25121
  for (const block of message.content) {
24644
25122
  if (!isRecord5(block)) continue;
@@ -25502,6 +25980,19 @@ var init_projects2 = __esm({
25502
25980
  }
25503
25981
  });
25504
25982
 
25983
+ // ../../server/agents/executables.ts
25984
+ function activeExecutableFor(kind) {
25985
+ return kind === "claude" ? claudeCliPath() : void 0;
25986
+ }
25987
+ function describeExecutableFor(kind) {
25988
+ return kind === "claude" ? describeClaudeExecutable() : null;
25989
+ }
25990
+ var init_executables = __esm({
25991
+ "../../server/agents/executables.ts"() {
25992
+ init_claudeExecutable();
25993
+ }
25994
+ });
25995
+
25505
25996
  // ../../server/lib/providerUpdates.ts
25506
25997
  import { realpathSync } from "node:fs";
25507
25998
  function deriveStatus(current, latest) {
@@ -25604,8 +26095,8 @@ function buildUpdateCommand(provider, method) {
25604
26095
  function formatCommand(command) {
25605
26096
  return [command.executable, ...command.args].join(" ");
25606
26097
  }
25607
- function resolveBinaryPaths(binName) {
25608
- const onPath = findExecutableOnPath(binName);
26098
+ function resolveBinaryPaths(descriptor7) {
26099
+ const onPath = activeExecutableFor(descriptor7.kind) ?? findExecutableOnPath(descriptor7.binName);
25609
26100
  if (!onPath) return [];
25610
26101
  try {
25611
26102
  const real = realpathSync(onPath);
@@ -25654,7 +26145,7 @@ function fetchLatestVersion(packageName) {
25654
26145
  });
25655
26146
  }
25656
26147
  async function probeProvider(descriptor7) {
25657
- const paths = resolveBinaryPaths(descriptor7.binName);
26148
+ const paths = resolveBinaryPaths(descriptor7);
25658
26149
  const base = {
25659
26150
  provider: descriptor7.kind,
25660
26151
  displayName: descriptor7.displayName,
@@ -25763,6 +26254,7 @@ var init_providerUpdates = __esm({
25763
26254
  "../../server/lib/providerUpdates.ts"() {
25764
26255
  init_agent_descriptors();
25765
26256
  init_versions();
26257
+ init_executables();
25766
26258
  init_binaryResolver();
25767
26259
  init_cliProcess();
25768
26260
  REGISTRY_TIMEOUT_MS = 4e3;
@@ -25819,6 +26311,32 @@ var init_provider_updates = __esm({
25819
26311
  }
25820
26312
  });
25821
26313
 
26314
+ // ../../server/routes/agent-executable.ts
26315
+ function registerAgentExecutableRoutes(use) {
26316
+ use("/api/agent-executable", async (req, res, next) => {
26317
+ if (req.method !== "GET") return next();
26318
+ const kind = new URL(req.url || "/", "http://localhost").pathname.slice(1);
26319
+ if (!AGENT_KINDS.includes(kind)) return next();
26320
+ const report = describeExecutableFor(kind);
26321
+ if (!report) {
26322
+ sendJson(res, 404, { error: `${kind} has a single executable; nothing to choose` });
26323
+ return;
26324
+ }
26325
+ try {
26326
+ sendJson(res, 200, await report);
26327
+ } catch (err) {
26328
+ sendJson(res, 500, { error: String(err) });
26329
+ }
26330
+ });
26331
+ }
26332
+ var init_agent_executable = __esm({
26333
+ "../../server/routes/agent-executable.ts"() {
26334
+ init_agent_descriptors();
26335
+ init_executables();
26336
+ init_http();
26337
+ }
26338
+ });
26339
+
25822
26340
  // ../../server/routes/scripts/discovery.ts
25823
26341
  import { readdir as readdir12, readFile as readFile19 } from "node:fs/promises";
25824
26342
  import { join as join25, relative as relative10 } from "node:path";
@@ -26734,7 +27252,7 @@ function normalizeCopilotFileChangeEvents(jsonlContent) {
26734
27252
  }
26735
27253
  });
26736
27254
  }
26737
- return records.map((record2) => JSON.stringify(record2)).join("\n");
27255
+ return records.map((record3) => JSON.stringify(record3)).join("\n");
26738
27256
  }
26739
27257
  async function parseSessionFileChanges(jsonlContent, includeContent) {
26740
27258
  if (formatForText(jsonlContent).kind === "copilot") {
@@ -28703,20 +29221,20 @@ var init_aggregate = __esm({
28703
29221
  this.#toDay = makeDayFormatter(options.timeZone);
28704
29222
  }
28705
29223
  /** Folds one record in; returns whether it actually contributed. */
28706
- add(record2) {
28707
- if (record2.dedupeKey !== null) {
28708
- if (this.#seen.has(record2.dedupeKey)) return false;
28709
- this.#seen.add(record2.dedupeKey);
29224
+ add(record3) {
29225
+ if (record3.dedupeKey !== null) {
29226
+ if (this.#seen.has(record3.dedupeKey)) return false;
29227
+ this.#seen.add(record3.dedupeKey);
28710
29228
  }
28711
- const day = this.#toDay(record2.timestampMs);
29229
+ const day = this.#toDay(record3.timestampMs);
28712
29230
  if (day < this.#options.sinceDay || day > this.#options.untilDay) return false;
28713
- const key2 = [day, record2.provider, record2.model].join(KEY_SEPARATOR);
29231
+ const key2 = [day, record3.provider, record3.model].join(KEY_SEPARATOR);
28714
29232
  let bucket = this.#buckets.get(key2);
28715
29233
  if (bucket === void 0) {
28716
29234
  bucket = {
28717
29235
  day,
28718
- provider: record2.provider,
28719
- model: record2.model,
29236
+ provider: record3.provider,
29237
+ model: record3.model,
28720
29238
  totals: emptyUsageCostTotals(),
28721
29239
  costUsd: 0,
28722
29240
  cacheSavingsUsd: 0,
@@ -28729,19 +29247,19 @@ var init_aggregate = __esm({
28729
29247
  }
28730
29248
  const priced = priceUsage(
28731
29249
  this.#options.rates,
28732
- record2.model,
28733
- record2.totals,
28734
- record2.reportedCostUsd
29250
+ record3.model,
29251
+ record3.totals,
29252
+ record3.reportedCostUsd
28735
29253
  );
28736
- bucket.totals = addUsageCostTotals(bucket.totals, record2.totals);
29254
+ bucket.totals = addUsageCostTotals(bucket.totals, record3.totals);
28737
29255
  bucket.costUsd += priced.costUsd;
28738
- bucket.cacheSavingsUsd += cacheSavingsUsd(this.#options.rates, record2.model, record2.totals);
29256
+ bucket.cacheSavingsUsd += cacheSavingsUsd(this.#options.rates, record3.model, record3.totals);
28739
29257
  bucket.records += 1;
28740
29258
  if (priced.costSource === "unpriced") bucket.unpricedRecords += 1;
28741
29259
  if (priced.costSource === "providerReported") bucket.providerReportedRecords += 1;
28742
- if (record2.sessionId.length > 0) {
28743
- bucket.sessions.add(record2.sessionId);
28744
- this.#sessions.add(record2.sessionId);
29260
+ if (record3.sessionId.length > 0) {
29261
+ bucket.sessions.add(record3.sessionId);
29262
+ this.#sessions.add(record3.sessionId);
28745
29263
  }
28746
29264
  return true;
28747
29265
  }
@@ -28822,17 +29340,17 @@ async function readTranscriptRecords(filePath, provider) {
28822
29340
  function dedupeWithinFile(records) {
28823
29341
  const slotByKey = /* @__PURE__ */ new Map();
28824
29342
  const kept = [];
28825
- for (const record2 of records) {
28826
- if (record2.dedupeKey === null) {
28827
- kept.push(record2);
29343
+ for (const record3 of records) {
29344
+ if (record3.dedupeKey === null) {
29345
+ kept.push(record3);
28828
29346
  continue;
28829
29347
  }
28830
- const slot = slotByKey.get(record2.dedupeKey);
29348
+ const slot = slotByKey.get(record3.dedupeKey);
28831
29349
  if (slot === void 0) {
28832
- slotByKey.set(record2.dedupeKey, kept.push(record2) - 1);
29350
+ slotByKey.set(record3.dedupeKey, kept.push(record3) - 1);
28833
29351
  continue;
28834
29352
  }
28835
- if (record2.totals.outputTokens > kept[slot].totals.outputTokens) kept[slot] = record2;
29353
+ if (record3.totals.outputTokens > kept[slot].totals.outputTokens) kept[slot] = record3;
28836
29354
  }
28837
29355
  return kept;
28838
29356
  }
@@ -28941,23 +29459,23 @@ async function readUsageCostSummary(input) {
28941
29459
  for (const file of files) {
28942
29460
  const records = await readFileRecords(file, provider);
28943
29461
  scannedFiles += 1;
28944
- for (const record2 of records) {
28945
- aggregator.add(record2);
28946
- if (!liveSessionIds.has(record2.sessionId)) continue;
28947
- const byModel = durableTotals.get(record2.sessionId) ?? /* @__PURE__ */ new Map();
29462
+ for (const record3 of records) {
29463
+ aggregator.add(record3);
29464
+ if (!liveSessionIds.has(record3.sessionId)) continue;
29465
+ const byModel = durableTotals.get(record3.sessionId) ?? /* @__PURE__ */ new Map();
28948
29466
  byModel.set(
28949
- record2.model,
29467
+ record3.model,
28950
29468
  addUsageCostTotals(
28951
- byModel.get(record2.model) ?? emptyUsageCostTotals(),
28952
- record2.totals
29469
+ byModel.get(record3.model) ?? emptyUsageCostTotals(),
29470
+ record3.totals
28953
29471
  )
28954
29472
  );
28955
- durableTotals.set(record2.sessionId, byModel);
29473
+ durableTotals.set(record3.sessionId, byModel);
28956
29474
  }
28957
29475
  }
28958
29476
  }
28959
29477
  for (const runtime of runtimes2) {
28960
- for (const record2 of await runtime.liveUsageRecords(durableTotals)) aggregator.add(record2);
29478
+ for (const record3 of await runtime.liveUsageRecords(durableTotals)) aggregator.add(record3);
28961
29479
  }
28962
29480
  const retentionCutoffMs = startedAtMs - 90 * 24 * 60 * 60 * 1e3;
28963
29481
  for (const [path, entry] of fileCache2) {
@@ -29684,8 +30202,8 @@ var init_workflows2 = __esm({
29684
30202
  });
29685
30203
 
29686
30204
  // ../../server/routes/worktrees/worktreeIo.ts
29687
- import { execFile as execFile7 } from "node:child_process";
29688
- import { promisify as promisify3 } from "node:util";
30205
+ import { execFile as execFile8 } from "node:child_process";
30206
+ import { promisify as promisify4 } from "node:util";
29689
30207
  async function runWorktreeCommand(file, args, { cwd, timeoutMs = WORKTREE_COMMAND_TIMEOUT_MS }) {
29690
30208
  const result = await execFileAsync(file, [...args], {
29691
30209
  cwd,
@@ -29724,7 +30242,7 @@ var init_worktreeIo = __esm({
29724
30242
  WORKTREE_COMMAND_MAX_BUFFER = 4 * 1024 * 1024;
29725
30243
  WORKTREE_SCAN_CONCURRENCY = 4;
29726
30244
  SESSION_HEADER_CONCURRENCY = 8;
29727
- execFileAsync = promisify3(execFile7);
30245
+ execFileAsync = promisify4(execFile8);
29728
30246
  NONINTERACTIVE_ENV = {
29729
30247
  ...process.env,
29730
30248
  GCM_INTERACTIVE: "Never",
@@ -30312,6 +30830,7 @@ var init_api_routes = __esm({
30312
30830
  init_files();
30313
30831
  init_git_diff();
30314
30832
  init_git_status();
30833
+ init_github_actions();
30315
30834
  init_project_icon();
30316
30835
  init_hello();
30317
30836
  init_local_file();
@@ -30326,6 +30845,7 @@ var init_api_routes = __esm({
30326
30845
  init_project_files();
30327
30846
  init_projects2();
30328
30847
  init_provider_updates();
30848
+ init_agent_executable();
30329
30849
  init_scripts();
30330
30850
  init_session_config();
30331
30851
  init_session_context();
@@ -30380,6 +30900,7 @@ var init_api_routes = __esm({
30380
30900
  apiRoute("project-files", registerProjectFileRoutes),
30381
30901
  apiRoute("project-file", registerProjectFileContentRoutes),
30382
30902
  apiRoute("git-status", registerGitStatusRoutes),
30903
+ apiRoute("github-actions", registerGitHubActionsRoutes),
30383
30904
  apiRoute("project-icon", registerProjectIconRoutes),
30384
30905
  apiRoute("git-diff", registerGitDiffRoutes),
30385
30906
  apiRoute("mcp", registerMcpRoutes),
@@ -30393,7 +30914,8 @@ var init_api_routes = __esm({
30393
30914
  apiRoute("models", registerModelRoutes),
30394
30915
  apiRoute("codex-threads", registerCodexThreadRoutes),
30395
30916
  apiRoute("agent-runtime", registerAgentRuntimeRoutes),
30396
- apiRoute("provider-updates", registerProviderUpdateRoutes)
30917
+ apiRoute("provider-updates", registerProviderUpdateRoutes),
30918
+ apiRoute("agent-executable", registerAgentExecutableRoutes)
30397
30919
  ];
30398
30920
  }
30399
30921
  });
@@ -31221,8 +31743,8 @@ var init_sessionActivityMonitor = __esm({
31221
31743
  });
31222
31744
 
31223
31745
  // ../../server/lib/standalone-bootstrap.ts
31224
- import { readFileSync as readFileSync3 } from "node:fs";
31225
- function resolveEnvPassword(env = process.env, readFileFn = (p) => readFileSync3(p, "utf-8")) {
31746
+ import { readFileSync as readFileSync4 } from "node:fs";
31747
+ function resolveEnvPassword(env = process.env, readFileFn = (p) => readFileSync4(p, "utf-8")) {
31226
31748
  const file = env.COGPIT_NETWORK_PASSWORD_FILE;
31227
31749
  if (file && file.length > 0) {
31228
31750
  const trimmed = readFileFn(file).replace(/\r?\n$/, "");
@@ -31491,7 +32013,7 @@ async function waitForShutdownSignal() {
31491
32013
  for (const signal of signals) process.once(signal, done);
31492
32014
  });
31493
32015
  }
31494
- var defaultDependencies = {
32016
+ var defaultDependencies2 = {
31495
32017
  fetch,
31496
32018
  openUrl: openExternalUrl,
31497
32019
  startServer: startPackagedServer,
@@ -31518,7 +32040,7 @@ async function verifyServer(server, command, deps) {
31518
32040
  }
31519
32041
  }
31520
32042
  async function run(argv, overrides = {}) {
31521
- const deps = { ...defaultDependencies, ...overrides };
32043
+ const deps = { ...defaultDependencies2, ...overrides };
31522
32044
  let command;
31523
32045
  try {
31524
32046
  command = parseArgs(argv);