tmux-ide 2.9.15 → 2.9.17

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 (41) hide show
  1. package/README.md +1 -1
  2. package/bin/cli.js +740 -185
  3. package/package.json +2 -1
  4. package/packages/contracts/src/tmux-server-scope.ts +54 -0
  5. package/packages/core/src/terminal-fnv64-wasm.ts +8 -1
  6. package/packages/core/src/terminal-replica-hash-cache.ts +108 -1
  7. package/packages/core/src/terminal-replica.ts +46 -10
  8. package/packages/daemon/dist/command-center/daemon-fleet-facts-observer.js +8 -1
  9. package/packages/daemon/dist/command-center/discovery.js +39 -6
  10. package/packages/daemon/dist/command-center/tmux-servers.js +46 -10
  11. package/packages/daemon/dist/lib/catalog-fleet-read.js +10 -0
  12. package/packages/daemon/dist/lib/daemon-embed.js +14 -3
  13. package/packages/daemon/dist/lib/daemon-service.js +13 -5
  14. package/packages/daemon/dist/lib/session-favicon.js +152 -0
  15. package/packages/daemon/dist/lib/skill-sync.js +3 -3
  16. package/packages/daemon/dist/lib/tmux-server-owner.js +16 -3
  17. package/packages/daemon/dist/lib/tmux-server-owners.js +20 -1
  18. package/packages/daemon/dist/lib/tui-binary.js +83 -17
  19. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/Info.plist +2 -2
  20. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/MacOS/tmux-ide-notifier +0 -0
  21. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/Resources/AppIcon.icns +0 -0
  22. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/Resources/Assets.car +0 -0
  23. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/_CodeSignature/CodeResources +4 -4
  24. package/packages/daemon/dist/open-browser.js +47 -3
  25. package/packages/daemon/dist/terminal/pane-stream/pane-stream-websocket.js +12 -0
  26. package/packages/daemon/dist/terminal/session-runtime/terminal-delivery-hub.js +16 -0
  27. package/packages/daemon/src/command-center/daemon-fleet-facts-observer.ts +9 -1
  28. package/packages/daemon/src/command-center/discovery.ts +72 -7
  29. package/packages/daemon/src/command-center/tmux-servers.ts +55 -11
  30. package/packages/daemon/src/lib/catalog-fleet-read.ts +40 -0
  31. package/packages/daemon/src/lib/daemon-embed.ts +27 -12
  32. package/packages/daemon/src/lib/daemon-service.ts +17 -4
  33. package/packages/daemon/src/lib/session-favicon.ts +185 -0
  34. package/packages/daemon/src/lib/skill-sync.ts +3 -3
  35. package/packages/daemon/src/lib/tmux-server-owner.ts +35 -6
  36. package/packages/daemon/src/lib/tmux-server-owners.ts +17 -1
  37. package/packages/daemon/src/lib/tui-binary.ts +114 -22
  38. package/packages/daemon/src/open-browser.ts +88 -3
  39. package/packages/daemon/src/terminal/pane-stream/pane-stream-websocket.ts +12 -0
  40. package/packages/daemon/src/terminal/session-runtime/terminal-delivery-hub.ts +25 -0
  41. package/skill/SKILL.md +6 -1
package/bin/cli.js CHANGED
@@ -7432,14 +7432,18 @@ var init_terminal_projection = __esm({
7432
7432
  var tmux_server_scope_exports = {};
7433
7433
  __export(tmux_server_scope_exports, {
7434
7434
  TMUX_SERVERS_API_PATH: () => TMUX_SERVERS_API_PATH,
7435
+ TMUX_SERVER_SESSION_FAVICON_MAX_BYTES: () => TMUX_SERVER_SESSION_FAVICON_MAX_BYTES,
7435
7436
  TmuxServerDescriptorSchemaZ: () => TmuxServerDescriptorSchemaZ,
7436
7437
  TmuxServerGenerationSchemaZ: () => TmuxServerGenerationSchemaZ,
7437
7438
  TmuxServerIdSchemaZ: () => TmuxServerIdSchemaZ,
7438
7439
  TmuxServerPaneTargetSchemaZ: () => TmuxServerPaneTargetSchemaZ,
7439
7440
  TmuxServerRegistrationRequestSchemaZ: () => TmuxServerRegistrationRequestSchemaZ,
7440
7441
  TmuxServerScopeSchemaZ: () => TmuxServerScopeSchemaZ,
7442
+ TmuxServerSessionFaviconMimeSchemaZ: () => TmuxServerSessionFaviconMimeSchemaZ,
7443
+ TmuxServerSessionFaviconSchemaZ: () => TmuxServerSessionFaviconSchemaZ,
7441
7444
  TmuxServerSessionTargetSchemaZ: () => TmuxServerSessionTargetSchemaZ,
7442
7445
  TmuxServerSessionsResourceSchemaZ: () => TmuxServerSessionsResourceSchemaZ,
7446
+ TmuxServerSessionsResourceV2SchemaZ: () => TmuxServerSessionsResourceV2SchemaZ,
7443
7447
  TmuxServerWindowTargetSchemaZ: () => TmuxServerWindowTargetSchemaZ,
7444
7448
  TmuxServersResourceSchemaZ: () => TmuxServersResourceSchemaZ,
7445
7449
  isTmuxServerPaneStreamPath: () => isTmuxServerPaneStreamPath,
@@ -7474,7 +7478,11 @@ function isTmuxServerPaneStreamPath(path2) {
7474
7478
  if (!match) return false;
7475
7479
  return TmuxServerScopeSchemaZ.safeParse({ serverId: match[1], generation: match[2] }).success;
7476
7480
  }
7477
- var descriptorShape, TmuxServerDescriptorSchemaZ, TMUX_SERVERS_API_PATH, TmuxServersResourceSchemaZ, TmuxServerSessionTargetSchemaZ, TmuxServerWindowTargetSchemaZ, TmuxServerPaneTargetSchemaZ, TmuxServerSessionsResourceSchemaZ, TmuxServerRegistrationRequestSchemaZ;
7481
+ function base64DecodedLength(data) {
7482
+ const padding = data.endsWith("==") ? 2 : data.endsWith("=") ? 1 : 0;
7483
+ return data.length / 4 * 3 - padding;
7484
+ }
7485
+ var descriptorShape, TmuxServerDescriptorSchemaZ, TMUX_SERVERS_API_PATH, TmuxServersResourceSchemaZ, TmuxServerSessionTargetSchemaZ, TmuxServerWindowTargetSchemaZ, TmuxServerPaneTargetSchemaZ, TmuxServerSessionsResourceSchemaZ, TMUX_SERVER_SESSION_FAVICON_MAX_BYTES, TmuxServerSessionFaviconMimeSchemaZ, TmuxServerSessionFaviconSchemaZ, TmuxServerSessionsResourceV2SchemaZ, TmuxServerRegistrationRequestSchemaZ;
7478
7486
  var init_tmux_server_scope = __esm({
7479
7487
  "packages/contracts/src/tmux-server-scope.ts"() {
7480
7488
  "use strict";
@@ -7537,6 +7545,37 @@ var init_tmux_server_scope = __esm({
7537
7545
  }).strict()
7538
7546
  )
7539
7547
  }).strict())();
7548
+ TMUX_SERVER_SESSION_FAVICON_MAX_BYTES = /* @__PURE__ */ (() => 32768)();
7549
+ TmuxServerSessionFaviconMimeSchemaZ = /* @__PURE__ */ (() => z47.enum([
7550
+ "image/png",
7551
+ "image/svg+xml",
7552
+ "image/x-icon",
7553
+ "image/jpeg",
7554
+ "image/webp",
7555
+ "image/gif"
7556
+ ]))();
7557
+ TmuxServerSessionFaviconSchemaZ = /* @__PURE__ */ (() => z47.object({
7558
+ mime: TmuxServerSessionFaviconMimeSchemaZ,
7559
+ data: z47.string().regex(/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u).refine(
7560
+ (data) => base64DecodedLength(data) <= TMUX_SERVER_SESSION_FAVICON_MAX_BYTES,
7561
+ "favicon exceeds 32 KiB"
7562
+ )
7563
+ }).strict())();
7564
+ TmuxServerSessionsResourceV2SchemaZ = /* @__PURE__ */ (() => z47.object({
7565
+ version: z47.literal(2),
7566
+ server: TmuxServerScopeSchemaZ,
7567
+ sessions: z47.array(
7568
+ z47.object({
7569
+ liveSessionId: WorkspaceCatalogLiveSessionIdSchemaZ,
7570
+ sessionName: z47.string().min(1).max(160),
7571
+ workspaceName: z47.string().min(1).max(160).nullable(),
7572
+ paneCount: z47.number().int().nonnegative(),
7573
+ windowCount: z47.number().int().min(1),
7574
+ lastActivity: z47.number().int().nonnegative().nullable(),
7575
+ favicon: TmuxServerSessionFaviconSchemaZ.nullable()
7576
+ }).strict()
7577
+ )
7578
+ }).strict())();
7540
7579
  TmuxServerRegistrationRequestSchemaZ = /* @__PURE__ */ (() => z47.object({
7541
7580
  label: descriptorShape.label.max(128),
7542
7581
  selector: z47.discriminatedUnion("kind", [
@@ -12364,7 +12403,10 @@ __export(tui_binary_exports, {
12364
12403
  MAX_TUI_COMPRESSED_BYTES: () => MAX_TUI_COMPRESSED_BYTES,
12365
12404
  MIN_TUI_BINARY_BYTES: () => MIN_TUI_BINARY_BYTES,
12366
12405
  RELEASE_REPO: () => RELEASE_REPO,
12367
- TUI_DOWNLOAD_TIMEOUT_MS: () => TUI_DOWNLOAD_TIMEOUT_MS,
12406
+ TUI_DOWNLOAD_MAX_ATTEMPTS: () => TUI_DOWNLOAD_MAX_ATTEMPTS,
12407
+ TUI_DOWNLOAD_RETRY_DELAY_MS: () => TUI_DOWNLOAD_RETRY_DELAY_MS,
12408
+ TUI_DOWNLOAD_STALL_TIMEOUT_MS: () => TUI_DOWNLOAD_STALL_TIMEOUT_MS,
12409
+ TUI_DOWNLOAD_TOTAL_TIMEOUT_MS: () => TUI_DOWNLOAD_TOTAL_TIMEOUT_MS,
12368
12410
  bunTargetForTag: () => bunTargetForTag,
12369
12411
  downloadTuiBinary: () => downloadTuiBinary,
12370
12412
  downloadedTuiPath: () => downloadedTuiPath,
@@ -12509,30 +12551,54 @@ function purgeCachedBinary(path2) {
12509
12551
  }
12510
12552
  }
12511
12553
  }
12512
- async function readBoundedResponse(fetchImpl, url, maxBytes, timeoutMs) {
12554
+ function isTransientHttpStatus(status2) {
12555
+ return status2 === 408 || status2 === 429 || status2 >= 500;
12556
+ }
12557
+ async function readBoundedResponse(fetchImpl, url, maxBytes, policy) {
12513
12558
  const controller = new AbortController();
12514
- const timer = setTimeout(() => controller.abort(), timeoutMs);
12559
+ let total = 0;
12560
+ let timeoutMessage = null;
12561
+ const abortWith = (message) => () => {
12562
+ timeoutMessage = message();
12563
+ controller.abort();
12564
+ };
12565
+ const overallTimer = setTimeout(
12566
+ abortWith(() => `download did not finish within ${policy.totalTimeoutMs}ms`),
12567
+ Math.max(0, policy.deadline - Date.now())
12568
+ );
12569
+ const onStall = abortWith(
12570
+ () => `download stalled: no data for ${policy.stallTimeoutMs}ms after ${total} bytes`
12571
+ );
12572
+ let stallTimer = setTimeout(onStall, policy.stallTimeoutMs);
12573
+ const markProgress = () => {
12574
+ clearTimeout(stallTimer);
12575
+ stallTimer = setTimeout(onStall, policy.stallTimeoutMs);
12576
+ };
12515
12577
  try {
12516
12578
  const response3 = await fetchImpl(url, { signal: controller.signal });
12517
12579
  if (!response3.ok) {
12518
- throw new Error(`HTTP ${response3.status} ${response3.statusText}`);
12580
+ const message = `HTTP ${response3.status} ${response3.statusText}`;
12581
+ if (isTransientHttpStatus(response3.status)) throw new Error(message);
12582
+ throw new PermanentDownloadError(message);
12519
12583
  }
12584
+ markProgress();
12520
12585
  const declaredLength = Number(response3.headers.get("content-length"));
12521
12586
  if (Number.isFinite(declaredLength) && declaredLength > maxBytes) {
12522
- throw new Error(`declared size ${declaredLength} exceeds ${maxBytes} bytes`);
12587
+ throw new PermanentDownloadError(`declared size ${declaredLength} exceeds ${maxBytes} bytes`);
12523
12588
  }
12524
12589
  if (!response3.body) throw new Error("response has no body");
12525
12590
  const chunks = [];
12526
- let total = 0;
12527
12591
  const reader = response3.body.getReader();
12528
12592
  try {
12529
12593
  while (true) {
12530
12594
  const { done, value } = await reader.read();
12531
12595
  if (done) break;
12596
+ if (value.byteLength === 0) continue;
12597
+ markProgress();
12532
12598
  total += value.byteLength;
12533
12599
  if (total > maxBytes) {
12534
12600
  await reader.cancel();
12535
- throw new Error(`download exceeds ${maxBytes} bytes`);
12601
+ throw new PermanentDownloadError(`download exceeds ${maxBytes} bytes`);
12536
12602
  }
12537
12603
  chunks.push(Buffer.from(value));
12538
12604
  }
@@ -12541,12 +12607,27 @@ async function readBoundedResponse(fetchImpl, url, maxBytes, timeoutMs) {
12541
12607
  }
12542
12608
  return Buffer.concat(chunks, total);
12543
12609
  } catch (error) {
12544
- if (controller.signal.aborted) {
12545
- throw new Error(`download timed out after ${timeoutMs}ms`, { cause: error });
12546
- }
12610
+ if (timeoutMessage) throw new Error(timeoutMessage, { cause: error });
12547
12611
  throw error;
12548
12612
  } finally {
12549
- clearTimeout(timer);
12613
+ clearTimeout(overallTimer);
12614
+ clearTimeout(stallTimer);
12615
+ controller.abort();
12616
+ }
12617
+ }
12618
+ async function readWithRetries(fetchImpl, url, maxBytes, policy, log) {
12619
+ for (let attempt = 1; ; attempt++) {
12620
+ try {
12621
+ return await readBoundedResponse(fetchImpl, url, maxBytes, policy);
12622
+ } catch (error) {
12623
+ const delayMs = policy.retryDelayMs * attempt;
12624
+ if (error instanceof PermanentDownloadError || attempt >= policy.maxAttempts || Date.now() + delayMs >= policy.deadline) {
12625
+ throw error;
12626
+ }
12627
+ const message = error instanceof Error ? error.message : String(error);
12628
+ log(`download attempt ${attempt}/${policy.maxAttempts} failed (${message}); retrying`);
12629
+ await new Promise((resolve45) => setTimeout(resolve45, delayMs));
12630
+ }
12550
12631
  }
12551
12632
  }
12552
12633
  async function downloadTuiBinary(opts = {}) {
@@ -12567,14 +12648,18 @@ async function downloadTuiBinary(opts = {}) {
12567
12648
  const limits = {
12568
12649
  minBinaryBytes: opts.limits?.minBinaryBytes ?? MIN_TUI_BINARY_BYTES,
12569
12650
  maxCompressedBytes: opts.limits?.maxCompressedBytes ?? MAX_TUI_COMPRESSED_BYTES,
12570
- maxBinaryBytes: opts.limits?.maxBinaryBytes ?? MAX_TUI_BINARY_BYTES,
12571
- timeoutMs: opts.timeoutMs ?? TUI_DOWNLOAD_TIMEOUT_MS
12651
+ maxBinaryBytes: opts.limits?.maxBinaryBytes ?? MAX_TUI_BINARY_BYTES
12572
12652
  };
12653
+ const totalTimeoutMs = opts.totalTimeoutMs ?? TUI_DOWNLOAD_TOTAL_TIMEOUT_MS;
12573
12654
  mkdirSync5(dirname9(dest), { recursive: true });
12655
+ const lockNotice = setTimeout(
12656
+ () => log("waiting for another tmux-ide download of this version to finish"),
12657
+ TUI_DOWNLOAD_LOCK_NOTICE_MS
12658
+ );
12574
12659
  const releaseLock = await acquirePrivateOperationLock(
12575
12660
  downloadLockPath(dest),
12576
- limits.timeoutMs * 2 + 5e3
12577
- );
12661
+ totalTimeoutMs + TUI_DOWNLOAD_LOCK_MARGIN_MS
12662
+ ).finally(() => clearTimeout(lockNotice));
12578
12663
  const suffix = `${process.pid}.${randomUUID2()}.tmp`;
12579
12664
  const binaryTmp = `${dest}.${suffix}`;
12580
12665
  const checksumTmp = `${checksumPath(dest)}.${suffix}`;
@@ -12583,21 +12668,31 @@ async function downloadTuiBinary(opts = {}) {
12583
12668
  if (cached2) return { path: dest, bytes: cached2.bytes };
12584
12669
  purgeCachedBinary(dest);
12585
12670
  const fetchImpl = opts.fetch ?? globalThis.fetch;
12671
+ const policy = {
12672
+ stallTimeoutMs: opts.stallTimeoutMs ?? TUI_DOWNLOAD_STALL_TIMEOUT_MS,
12673
+ deadline: Date.now() + totalTimeoutMs,
12674
+ totalTimeoutMs,
12675
+ maxAttempts: opts.maxAttempts ?? TUI_DOWNLOAD_MAX_ATTEMPTS,
12676
+ retryDelayMs: opts.retryDelayMs ?? TUI_DOWNLOAD_RETRY_DELAY_MS
12677
+ };
12586
12678
  log(`downloading checksum manifest ${checksumUrl}`);
12587
12679
  let manifestBytes;
12588
12680
  let gz;
12589
12681
  try {
12590
- manifestBytes = await readBoundedResponse(
12682
+ manifestBytes = await readWithRetries(
12591
12683
  fetchImpl,
12592
12684
  checksumUrl,
12593
12685
  MAX_CHECKSUM_MANIFEST_BYTES,
12594
- limits.timeoutMs
12686
+ policy,
12687
+ log
12595
12688
  );
12596
- gz = await readBoundedResponse(fetchImpl, url, limits.maxCompressedBytes, limits.timeoutMs);
12689
+ log(`downloading ${url}`);
12690
+ gz = await readWithRetries(fetchImpl, url, limits.maxCompressedBytes, policy, log);
12597
12691
  } catch (error) {
12598
12692
  const message = error instanceof Error ? error.message : String(error);
12693
+ const hint = error instanceof PermanentDownloadError ? "Check that the release published both exact-version assets." : "Check your network connection, then retry with `tmux-ide update --tui-binary`, or run the installer again if tmux-ide is not installed yet.";
12599
12694
  throw new Error(
12600
- `could not download the TUI binary and checksum for v${version} (${message}). Check that the release published both exact-version assets.`,
12695
+ `could not download the TUI binary and checksum for v${version} (${message}). ${hint}`,
12601
12696
  { cause: error }
12602
12697
  );
12603
12698
  }
@@ -12642,7 +12737,7 @@ async function downloadTuiBinary(opts = {}) {
12642
12737
  releaseLock();
12643
12738
  }
12644
12739
  }
12645
- var RELEASE_REPO, MIN_TUI_BINARY_BYTES, MAX_TUI_COMPRESSED_BYTES, MAX_TUI_BINARY_BYTES, TUI_DOWNLOAD_TIMEOUT_MS, MAX_CHECKSUM_MANIFEST_BYTES, SUPPORTED;
12740
+ var RELEASE_REPO, MIN_TUI_BINARY_BYTES, MAX_TUI_COMPRESSED_BYTES, MAX_TUI_BINARY_BYTES, TUI_DOWNLOAD_STALL_TIMEOUT_MS, TUI_DOWNLOAD_TOTAL_TIMEOUT_MS, TUI_DOWNLOAD_MAX_ATTEMPTS, TUI_DOWNLOAD_RETRY_DELAY_MS, MAX_CHECKSUM_MANIFEST_BYTES, TUI_DOWNLOAD_LOCK_NOTICE_MS, TUI_DOWNLOAD_LOCK_MARGIN_MS, SUPPORTED, PermanentDownloadError;
12646
12741
  var init_tui_binary = __esm({
12647
12742
  "packages/daemon/src/lib/tui-binary.ts"() {
12648
12743
  "use strict";
@@ -12653,13 +12748,20 @@ var init_tui_binary = __esm({
12653
12748
  MIN_TUI_BINARY_BYTES = 10 * 1024 * 1024;
12654
12749
  MAX_TUI_COMPRESSED_BYTES = 128 * 1024 * 1024;
12655
12750
  MAX_TUI_BINARY_BYTES = 256 * 1024 * 1024;
12656
- TUI_DOWNLOAD_TIMEOUT_MS = 3e4;
12751
+ TUI_DOWNLOAD_STALL_TIMEOUT_MS = 3e4;
12752
+ TUI_DOWNLOAD_TOTAL_TIMEOUT_MS = 15 * 6e4;
12753
+ TUI_DOWNLOAD_MAX_ATTEMPTS = 3;
12754
+ TUI_DOWNLOAD_RETRY_DELAY_MS = 1e3;
12657
12755
  MAX_CHECKSUM_MANIFEST_BYTES = 4 * 1024;
12756
+ TUI_DOWNLOAD_LOCK_NOTICE_MS = 2e3;
12757
+ TUI_DOWNLOAD_LOCK_MARGIN_MS = 5e3;
12658
12758
  SUPPORTED = {
12659
12759
  "darwin-arm64": "darwin-arm64",
12660
12760
  "linux-x64": "linux-x64",
12661
12761
  "linux-arm64": "linux-arm64"
12662
12762
  };
12763
+ PermanentDownloadError = class extends Error {
12764
+ };
12663
12765
  }
12664
12766
  });
12665
12767
 
@@ -13043,7 +13145,7 @@ var require_package = __commonJS({
13043
13145
  "package.json"(exports, module) {
13044
13146
  module.exports = {
13045
13147
  name: "tmux-ide",
13046
- version: "2.9.15",
13148
+ version: "2.9.17",
13047
13149
  description: "The open-source workspace for coding agents: run Claude Code, Codex and other agents in tmux with live status",
13048
13150
  type: "module",
13049
13151
  bin: {
@@ -13113,6 +13215,7 @@ var require_package = __commonJS({
13113
13215
  postinstall: "node scripts/postinstall.js",
13114
13216
  docs: "turbo run dev --filter=@tmux-ide/docs",
13115
13217
  "demo:tui": "bun --preload @opentui/solid/preload docs/scripts/render-tui-demo.tsx",
13218
+ "demo:themes": "bun --preload @opentui/solid/preload docs/scripts/render-theme-gallery.tsx && node docs/scripts/rasterize-theme-gallery.mjs",
13116
13219
  "demo:font": "node docs/scripts/tui-demo-font/build.mjs",
13117
13220
  "readme:media": "node docs/scripts/readme-media.mjs",
13118
13221
  "test:tui-renderer": "bun test --preload @opentui/solid/preload --preload ./packages/daemon/test-support/opentui-renderer-preload.ts ./packages/daemon/src/tui/mirror/automatic-contrast-renderer.test.tsx ./packages/daemon/src/tui/mirror/pane-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/native-grid-projection-renderer.test.tsx ./packages/daemon/src/tui/mirror/widget-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/missions-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/recipes-gallery-renderer.test.tsx ./packages/daemon/src/tui/mirror/shell-chrome-renderer.test.tsx ./packages/daemon/src/tui/mirror/sidebar-renderer.test.tsx ./packages/daemon/src/tui/mirror/home-files-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/changes-terminal-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/activity-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/features/files/session-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-terminal-workspace-renderer.test.tsx ./packages/daemon/src/tui/mirror/testing/styled-frame-oracle.test.tsx ./packages/daemon/src/tui/mirror/runtime/terminal-transient-frame-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/terminal-draw-failure-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-shell-view-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-root-error-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-backpressure-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-demand-cadence-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/files-optional-feature-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/changes-optional-feature-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/missions-activity-optional-feature-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/dialogs-optional-feature-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/optional-feature-registry-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/palette-optional-feature-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/pane-scoped-terminal-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/features/rich-preview/feature.test.ts ./packages/daemon/src/tui/mirror/runtime/rich-preview-optional-feature-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/application-shell-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/pane-frame-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/terminal-pane-chrome-view.test.tsx ./packages/daemon/src/tui/mirror/workspace/terminal-window-strip-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/workbench-shell-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/workbench-dock-dual-host-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/agent-terminal-canvas-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/command-palette-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/opentui-insertion-stability-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-shell-home-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/terminal-pane-header-polish-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-home-agent-roster-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-home-agent-flow-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/guided-tour-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-shell-sidebar-catalog-renderer.test.tsx ./packages/daemon/src/tui/mirror/ui/ui-renderer.test.tsx ./packages/daemon/src/tui/mirror/ui/pane-interaction-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-machine-sidebar-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-add-machine-dialog-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-new-agent-dialog-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-fleet-switcher-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-palette-preview-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-fleet-palette-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-fleet-session-actions-renderer.test.tsx ./packages/daemon/src/tui/mirror/ui/agent-team-renderer.test.tsx ./scripts/solid-conditional-props.test.tsx",
@@ -27034,10 +27137,13 @@ function isVisibleFleetSession(name) {
27034
27137
  function readAdoptedSessionNames(runTmux2 = _tmuxRunner) {
27035
27138
  let raw;
27036
27139
  try {
27037
- raw = runTmux2(["list-sessions", "-F", `#{session_name} #{${ADOPTED_OPTION}}`]);
27140
+ raw = runTmux2(ADOPTED_SESSIONS_ARGS);
27038
27141
  } catch {
27039
27142
  return null;
27040
27143
  }
27144
+ return parseAdoptedSessionNames(raw);
27145
+ }
27146
+ function parseAdoptedSessionNames(raw) {
27041
27147
  const names = [];
27042
27148
  for (const line of raw.split("\n")) {
27043
27149
  if (!line) continue;
@@ -27056,16 +27162,51 @@ function readAdoptedFleet(registry = getDefaultWorkspaceRegistry(), runTmux2 = _
27056
27162
  const nativeEpoch = nativeServerEpoch?.() ?? null;
27057
27163
  const adopted = readAdoptedSessionNames(runTmux2);
27058
27164
  if (adopted === null) return null;
27059
- const adoptedSet = new Set(adopted);
27060
- if (adoptedSet.size === 0) return [];
27061
- const appCreatedSessions = new Set(registry.list().map((workspace) => workspace.sessionName));
27062
- const panesBySession = /* @__PURE__ */ new Map();
27165
+ if (adopted.length === 0) return [];
27063
27166
  let panesRaw;
27064
27167
  try {
27065
- panesRaw = runTmux2(["list-panes", "-a", "-F", FLEET_PANE_FORMAT]);
27168
+ panesRaw = runTmux2(FLEET_PANES_ARGS);
27066
27169
  } catch {
27067
27170
  panesRaw = "";
27068
27171
  }
27172
+ return assembleAdoptedFleet(
27173
+ adopted,
27174
+ panesRaw,
27175
+ registry,
27176
+ nativeEpoch,
27177
+ resolveInteractionEndpoint,
27178
+ nativeServerEpoch
27179
+ );
27180
+ }
27181
+ async function readAdoptedFleetAsync(registry, runTmux2, resolveInteractionEndpoint, nativeServerEpoch) {
27182
+ const nativeEpoch = nativeServerEpoch?.() ?? null;
27183
+ let sessionsRaw;
27184
+ try {
27185
+ sessionsRaw = await runTmux2(ADOPTED_SESSIONS_ARGS);
27186
+ } catch {
27187
+ return null;
27188
+ }
27189
+ const adopted = parseAdoptedSessionNames(sessionsRaw);
27190
+ if (adopted.length === 0) return [];
27191
+ let panesRaw;
27192
+ try {
27193
+ panesRaw = await runTmux2(FLEET_PANES_ARGS);
27194
+ } catch {
27195
+ panesRaw = "";
27196
+ }
27197
+ return assembleAdoptedFleet(
27198
+ adopted,
27199
+ panesRaw,
27200
+ registry,
27201
+ nativeEpoch,
27202
+ resolveInteractionEndpoint,
27203
+ nativeServerEpoch
27204
+ );
27205
+ }
27206
+ function assembleAdoptedFleet(adopted, panesRaw, registry, nativeEpoch, resolveInteractionEndpoint, nativeServerEpoch) {
27207
+ const adoptedSet = new Set(adopted);
27208
+ const appCreatedSessions = new Set(registry.list().map((workspace) => workspace.sessionName));
27209
+ const panesBySession = /* @__PURE__ */ new Map();
27069
27210
  for (const line of panesRaw.split("\n")) {
27070
27211
  if (!line) continue;
27071
27212
  const fields = line.split(FLEET_FIELD_SEPARATOR);
@@ -27153,7 +27294,7 @@ function buildProjectDetail(info) {
27153
27294
  panes: info.panes.map(({ semanticPaneId: _semanticPaneId, ...pane }) => pane)
27154
27295
  };
27155
27296
  }
27156
- var _tmuxRunner, FLEET_FIELD_SEPARATOR, FLEET_LINE_SENTINEL, FLEET_PANE_FORMAT;
27297
+ var _tmuxRunner, ADOPTED_SESSIONS_ARGS, FLEET_FIELD_SEPARATOR, FLEET_LINE_SENTINEL, FLEET_PANE_FORMAT, FLEET_PANES_ARGS;
27157
27298
  var init_discovery = __esm({
27158
27299
  "packages/daemon/src/command-center/discovery.ts"() {
27159
27300
  "use strict";
@@ -27170,6 +27311,7 @@ var init_discovery = __esm({
27170
27311
  maxBuffer: 1024 * 1024,
27171
27312
  stdio: ["ignore", "pipe", "ignore"]
27172
27313
  }).trim();
27314
+ ADOPTED_SESSIONS_ARGS = ["list-sessions", "-F", `#{session_name} #{${ADOPTED_OPTION}}`];
27173
27315
  FLEET_FIELD_SEPARATOR = "|tmux-ide-fleet-field-v1|";
27174
27316
  FLEET_LINE_SENTINEL = "tmux-ide-fleet-v1";
27175
27317
  FLEET_PANE_FORMAT = [
@@ -27191,6 +27333,7 @@ var init_discovery = __esm({
27191
27333
  "#{@tmux_ide_team}",
27192
27334
  FLEET_LINE_SENTINEL
27193
27335
  ].join(FLEET_FIELD_SEPARATOR);
27336
+ FLEET_PANES_ARGS = ["list-panes", "-a", "-F", FLEET_PANE_FORMAT];
27194
27337
  }
27195
27338
  });
27196
27339
 
@@ -39850,7 +39993,7 @@ function createNativeTmuxSessionOpener(options) {
39850
39993
  options.assertOpen();
39851
39994
  if (disposed) throw new Error("Session admission is retired");
39852
39995
  };
39853
- const open4 = async (liveSessionId, observed) => {
39996
+ const open5 = async (liveSessionId, observed) => {
39854
39997
  assertOpen();
39855
39998
  const records = await options.run([
39856
39999
  "list-sessions",
@@ -39912,7 +40055,7 @@ function createNativeTmuxSessionOpener(options) {
39912
40055
  const previous = pending.get(liveSessionId);
39913
40056
  if (previous) return previous;
39914
40057
  if (pending.size >= 16) throw new Error("Session admission capacity reached");
39915
- const operation = tail.then(() => open4(liveSessionId)).finally(() => pending.delete(liveSessionId));
40058
+ const operation = tail.then(() => open5(liveSessionId)).finally(() => pending.delete(liveSessionId));
39916
40059
  pending.set(liveSessionId, operation);
39917
40060
  tail = operation.catch(() => {
39918
40061
  });
@@ -39925,7 +40068,7 @@ function createNativeTmuxSessionOpener(options) {
39925
40068
  if (pending.size >= 16) throw new Error("Session admission capacity reached");
39926
40069
  const key2 = randomUUID23();
39927
40070
  const operation = tail.then(async () => {
39928
- const result2 = await open4(liveSessionId, observed);
40071
+ const result2 = await open5(liveSessionId, observed);
39929
40072
  if (!result2.semanticPaneId) throw new Error("Agent admission returned no target");
39930
40073
  return { ...result2, semanticPaneId: result2.semanticPaneId };
39931
40074
  }).finally(() => pending.delete(key2));
@@ -43764,11 +43907,14 @@ function initialize() {
43764
43907
  }
43765
43908
  return native;
43766
43909
  }
43910
+ function nativeFnv64() {
43911
+ return initialize();
43912
+ }
43767
43913
  function createBufferedFnv64(initialState = OFFSET_BASIS) {
43768
43914
  const hasher = initialize();
43769
43915
  return hasher ? new BufferedHasher(hasher, initialState) : null;
43770
43916
  }
43771
- var SCRATCH_BYTES, OFFSET_BASIS, initialized, native, BufferedHasher;
43917
+ var SCRATCH_BYTES, OFFSET_BASIS, initialized, native, FNV64_OFFSET_BASIS, BufferedHasher;
43772
43918
  var init_terminal_fnv64_wasm = __esm({
43773
43919
  "packages/core/src/terminal-fnv64-wasm.ts"() {
43774
43920
  "use strict";
@@ -43777,6 +43923,7 @@ var init_terminal_fnv64_wasm = __esm({
43777
43923
  OFFSET_BASIS = 0xcbf29ce484222325n;
43778
43924
  initialized = false;
43779
43925
  native = null;
43926
+ FNV64_OFFSET_BASIS = OFFSET_BASIS;
43780
43927
  BufferedHasher = class {
43781
43928
  #native;
43782
43929
  #buffer = new Uint8Array(SCRATCH_BYTES);
@@ -43914,6 +44061,12 @@ function hashTerminalReplicaRowCached(row, onMiss, encodingCache) {
43914
44061
  const cached2 = ROW_HASH_CACHE.get(row);
43915
44062
  if (cached2) return cached2;
43916
44063
  onMiss?.();
44064
+ const native2 = nativeFnv64();
44065
+ if (native2) {
44066
+ const digest4 = hashRowBytes(row, native2);
44067
+ if (isTerminalReplicaRowDeeplyFrozen(row)) ROW_HASH_CACHE.set(row, digest4);
44068
+ return digest4;
44069
+ }
43917
44070
  const hash = encodingCache?.createHash() ?? new CanonicalFnv64();
43918
44071
  let canonicalBytes = 10 + String(row.cells.length).length;
43919
44072
  hash.ascii("a2:");
@@ -43977,6 +44130,10 @@ async function hashTerminalReplicaRowCooperatively(row, yieldControl, bytesPerSl
43977
44130
  }
43978
44131
  function isTerminalReplicaRowDeeplyFrozen(row) {
43979
44132
  if (DEEPLY_FROZEN_ROWS.has(row)) return true;
44133
+ if (isOwnedTerminalReplicaRow(row)) {
44134
+ DEEPLY_FROZEN_ROWS.add(row);
44135
+ return true;
44136
+ }
43980
44137
  if (!Object.isFrozen(row) || !Object.isFrozen(row.cells)) return false;
43981
44138
  for (const cell of row.cells) {
43982
44139
  if (!Object.isFrozen(cell) || !Object.isFrozen(cell.foreground) || !Object.isFrozen(cell.background))
@@ -43985,7 +44142,74 @@ function isTerminalReplicaRowDeeplyFrozen(row) {
43985
44142
  DEEPLY_FROZEN_ROWS.add(row);
43986
44143
  return true;
43987
44144
  }
43988
- var ROW_HASH_CACHE, DEEPLY_FROZEN_ROWS, UTF8_ENCODER, SHORT_ASCII_HEADERS, DEFAULT_COLOR_KEYS, DEFAULT_COLOR_KEY_ORDER, CanonicalFnv64, compactColorKey, TerminalReplicaRunEncodingCache, DECODED_DEFAULT_COLOR;
44145
+ function canonicalNumber(value) {
44146
+ const text2 = String(value);
44147
+ return `d${text2.length}:${text2};`;
44148
+ }
44149
+ function canonicalColor(color3) {
44150
+ if (color3.kind === "default") return "o1:s4:kind;s7:default;;";
44151
+ if (color3.kind === "indexed")
44152
+ return `o2:s5:index;${canonicalNumber(color3.index)}s4:kind;s7:indexed;;`;
44153
+ return `o2:s4:kind;s3:rgb;s5:value;${canonicalNumber(color3.value)};`;
44154
+ }
44155
+ function encodeCell(cell) {
44156
+ const grapheme = cell.grapheme;
44157
+ let length = grapheme.length;
44158
+ for (let index = 0; index < grapheme.length; index += 1)
44159
+ if (grapheme.charCodeAt(index) > 127) {
44160
+ length = UTF8_ENCODER.encode(grapheme).byteLength;
44161
+ break;
44162
+ }
44163
+ return UTF8_ENCODER.encode(
44164
+ `a5:s${length}:${grapheme};${canonicalNumber(cell.width)}${canonicalColor(
44165
+ cell.foreground
44166
+ )}${canonicalColor(cell.background)}${canonicalNumber(cell.attributes)};`
44167
+ );
44168
+ }
44169
+ function remember(cache3, key2, bytes) {
44170
+ if (cache3.size >= CELL_BYTES_LIMIT) cache3.clear();
44171
+ cache3.set(key2, bytes);
44172
+ return bytes;
44173
+ }
44174
+ function cellBytes(cell) {
44175
+ const { grapheme, width, attributes, foreground, background } = cell;
44176
+ if (width === 1 && attributes === 0 && foreground.kind === "default" && background.kind === "default")
44177
+ return PLAIN_CELL_BYTES.get(grapheme) ?? remember(PLAIN_CELL_BYTES, grapheme, encodeCell(cell));
44178
+ const key2 = `${width}\0${attributes}\0${canonicalColor(foreground)}\0${canonicalColor(
44179
+ background
44180
+ )}\0${grapheme}`;
44181
+ return STYLED_CELL_BYTES.get(key2) ?? remember(STYLED_CELL_BYTES, key2, encodeCell(cell));
44182
+ }
44183
+ function hashRowBytes(row, native2) {
44184
+ const scratch = native2.scratch;
44185
+ const capacity = scratch.byteLength;
44186
+ let state = FNV64_OFFSET_BASIS;
44187
+ let length = 0;
44188
+ const write2 = (bytes) => {
44189
+ if (bytes.byteLength <= capacity - length) {
44190
+ scratch.set(bytes, length);
44191
+ length += bytes.byteLength;
44192
+ return;
44193
+ }
44194
+ let offset = 0;
44195
+ while (offset < bytes.byteLength) {
44196
+ const count = Math.min(capacity - length, bytes.byteLength - offset);
44197
+ scratch.set(bytes.subarray(offset, offset + count), length);
44198
+ length += count;
44199
+ offset += count;
44200
+ if (length === capacity) {
44201
+ state = BigInt.asUintN(64, native2.update(state, length));
44202
+ length = 0;
44203
+ }
44204
+ }
44205
+ };
44206
+ write2(ASCII_ENCODER.encode(`a2:${row.wrapped ? "b1;" : "b0;"}a${row.cells.length}:`));
44207
+ for (const cell of row.cells) write2(cellBytes(cell));
44208
+ write2(ROW_TRAILER);
44209
+ state = BigInt.asUintN(64, native2.update(state, length));
44210
+ return state.toString(16).padStart(16, "0");
44211
+ }
44212
+ var ROW_HASH_CACHE, DEEPLY_FROZEN_ROWS, UTF8_ENCODER, SHORT_ASCII_HEADERS, DEFAULT_COLOR_KEYS, DEFAULT_COLOR_KEY_ORDER, CanonicalFnv64, compactColorKey, TerminalReplicaRunEncodingCache, DECODED_DEFAULT_COLOR, ASCII_ENCODER, CELL_BYTES_LIMIT, PLAIN_CELL_BYTES, STYLED_CELL_BYTES, ROW_TRAILER;
43989
44213
  var init_terminal_replica_hash_cache = __esm({
43990
44214
  "packages/core/src/terminal-replica-hash-cache.ts"() {
43991
44215
  "use strict";
@@ -44232,6 +44456,11 @@ var init_terminal_replica_hash_cache = __esm({
44232
44456
  }
44233
44457
  };
44234
44458
  DECODED_DEFAULT_COLOR = Object.freeze({ kind: "default" });
44459
+ ASCII_ENCODER = new TextEncoder();
44460
+ CELL_BYTES_LIMIT = 4096;
44461
+ PLAIN_CELL_BYTES = /* @__PURE__ */ new Map();
44462
+ STYLED_CELL_BYTES = /* @__PURE__ */ new Map();
44463
+ ROW_TRAILER = ASCII_ENCODER.encode(";;");
44235
44464
  }
44236
44465
  });
44237
44466
 
@@ -44563,8 +44792,13 @@ async function applyTerminalReplicaUpdateCooperatively(current, update, options)
44563
44792
  }
44564
44793
  const authenticated = options.authenticatedFrameHash;
44565
44794
  const frameStart = readProfileClock(options.instrumentation);
44566
- const frameHash = authenticated && /^[0-9a-f]{16}$/u.test(authenticated) ? authenticated : await hashCanonicalTerminalValueCooperatively(
44567
- { ...admitted, snapshot: snapshot3 },
44795
+ const reuseAuthenticated = Boolean(authenticated && /^[0-9a-f]{16}$/u.test(authenticated));
44796
+ if (!reuseAuthenticated) {
44797
+ await primeTerminalReplicaRowsHashCooperatively(snapshot3.grid, yieldControl);
44798
+ await primeTerminalReplicaRowsHashCooperatively(snapshot3.history, yieldControl);
44799
+ }
44800
+ const frameHash = reuseAuthenticated ? authenticated : await hashCanonicalTerminalValueCooperatively(
44801
+ terminalReplicaFrameIdentity({ ...admitted, snapshot: snapshot3 }),
44568
44802
  yieldControl,
44569
44803
  // Bound frame hashing without scheduling a task per few cells.
44570
44804
  32 * 1024
@@ -44841,10 +45075,30 @@ function resolveFrameHash(update, options, profile) {
44841
45075
  return authenticated;
44842
45076
  }
44843
45077
  const started = readProfileClock(options.instrumentation);
44844
- const hash = hashStable(update);
45078
+ const hash = hashStable(terminalReplicaFrameIdentity(update));
44845
45079
  addProfileDuration(profile, "updateHash", started, options.instrumentation);
44846
45080
  return hash;
44847
45081
  }
45082
+ function terminalReplicaFrameIdentity(update) {
45083
+ if (update.type === "terminal.patch") {
45084
+ const { rows, history: history2, historyDelta, ...rest2 } = update.patch;
45085
+ return [
45086
+ "terminal-replica-patch-frame-v2",
45087
+ { ...update, patch: rest2 },
45088
+ rows.map(({ index, row }) => [index, hashTerminalReplicaRow(row)]),
45089
+ history2 === void 0 ? null : hashTerminalReplicaRows(history2),
45090
+ historyDelta === void 0 ? null : [historyDelta.trim, hashTerminalReplicaRows(historyDelta.append)]
45091
+ ];
45092
+ }
45093
+ if (update.type !== "terminal.seed") return update;
45094
+ const { grid, history, ...rest } = update.snapshot;
45095
+ return [
45096
+ "terminal-replica-frame-v2",
45097
+ { ...update, snapshot: rest },
45098
+ hashTerminalReplicaRows(grid),
45099
+ hashTerminalReplicaRows(history)
45100
+ ];
45101
+ }
44848
45102
  function readProfileClock(instrumentation) {
44849
45103
  if (!instrumentation) return void 0;
44850
45104
  try {
@@ -62237,6 +62491,14 @@ function joinBytes(chunks) {
62237
62491
  }
62238
62492
  return joined;
62239
62493
  }
62494
+ function logDeliveryFault(client, reason, cause) {
62495
+ logger.info("terminal-delivery", "fault sent to client", {
62496
+ reason,
62497
+ ...cause ? { cause } : {},
62498
+ pane: client.paneId,
62499
+ workspace: client.deliveryWorkspaceName
62500
+ });
62501
+ }
62240
62502
  var MAX_CANONICAL_REVISIONS, MAX_RAW_JOURNAL_BYTES, MAX_REPRESENTATION_CACHE_ENTRIES, MAX_REPRESENTATION_CACHE_BYTES, MAX_CLIENTS, MAX_PANES, MAX_PENDING_CANONICAL_CELLS, MAX_CONNECTIONS, BACKGROUND_CADENCE_MS, DeferredProjectionEncoding, DeferredSeedEncoding, ExactSemanticRepresentationSelectionError, TerminalDeliveryRepresentationSelectionError, TerminalDeliveryPreaccountLimitError, SessionRuntimeTerminalDeliveryHub;
62241
62503
  var init_terminal_delivery_hub = __esm({
62242
62504
  "packages/daemon/src/terminal/session-runtime/terminal-delivery-hub.ts"() {
@@ -62245,6 +62507,7 @@ var init_terminal_delivery_hub = __esm({
62245
62507
  init_src2();
62246
62508
  init_src3();
62247
62509
  init_stock_capture_fidelity();
62510
+ init_log();
62248
62511
  init_native_seed_backing();
62249
62512
  init_src2();
62250
62513
  init_src3();
@@ -62426,6 +62689,11 @@ var init_terminal_delivery_hub = __esm({
62426
62689
  };
62427
62690
  } catch (error) {
62428
62691
  if (!(error instanceof StockCaptureTabUnavailableError)) throw error;
62692
+ logDeliveryFault(
62693
+ { paneId: semanticPaneId3, deliveryWorkspaceName: address },
62694
+ "source-closed",
62695
+ "capture-unavailable"
62696
+ );
62429
62697
  await accept({
62430
62698
  type: "terminal.delivery.fault",
62431
62699
  reason: "source-closed",
@@ -62584,6 +62852,7 @@ var init_terminal_delivery_hub = __esm({
62584
62852
  this.#cancelEncoding(client);
62585
62853
  client.outgoing.length = 0;
62586
62854
  client.inFlight = null;
62855
+ logDeliveryFault(client, "source-closed", "source-restarted");
62587
62856
  this.#enqueue(client, {
62588
62857
  type: "terminal.delivery.fault",
62589
62858
  reason: "source-closed",
@@ -63571,6 +63840,7 @@ var init_terminal_delivery_hub = __esm({
63571
63840
  this.#recordDeliveryStatus(client, this.#panes.get(client.paneId));
63572
63841
  }
63573
63842
  #fault(client, reason, message, selectionObservation = null) {
63843
+ logDeliveryFault(client, reason);
63574
63844
  if (this.#observability.enabled)
63575
63845
  try {
63576
63846
  const atMicros = this.#observability.nowMicros();
@@ -72667,6 +72937,7 @@ var init_pane_stream_websocket = __esm({
72667
72937
  init_src2();
72668
72938
  init_src2();
72669
72939
  init_src2();
72940
+ init_log();
72670
72941
  init_src2();
72671
72942
  init_admission_util();
72672
72943
  init_registry2();
@@ -73175,6 +73446,11 @@ var init_pane_stream_websocket = __esm({
73175
73446
  this.#open = false;
73176
73447
  this.#cancelDeadline();
73177
73448
  const socket = this.#socket;
73449
+ logger.info("pane-stream", "admission closed", {
73450
+ code: code2,
73451
+ reason,
73452
+ hostClientId: this.hostClientId
73453
+ });
73178
73454
  this.#detach();
73179
73455
  this.#onRelease(this);
73180
73456
  if (socket) safeCloseSocket(socket, code2, reason);
@@ -73426,6 +73702,12 @@ var init_pane_stream_websocket = __esm({
73426
73702
  close(code2 = 1e3, reason = "stream-closed") {
73427
73703
  if (this.#closed) return;
73428
73704
  this.#recordDiagnosticLifecycle("pane-stream-terminal", code2, reason);
73705
+ logger.info("pane-stream", "stream closed", {
73706
+ code: code2,
73707
+ reason,
73708
+ session: this.#descriptor.sessionName,
73709
+ clientId: this.#clientId
73710
+ });
73429
73711
  this.#closed = true;
73430
73712
  for (const demand of this.#surfaceDemands.values()) demand.close();
73431
73713
  this.#surfaceDemands.clear();
@@ -75719,7 +76001,8 @@ function createNativeTmuxServerCatalog(workspaceRegistry, run, assertOpen = () =
75719
76001
  "list-panes",
75720
76002
  "-a",
75721
76003
  "-F",
75722
- "#{pid} #{session_id} #{session_created} #{session_name} #{pane_id} #{session_path}"
76004
+ // session_path stays last: tmux rejects tabs in session names but not in paths.
76005
+ "#{pid} #{session_id} #{session_created} #{session_name} #{pane_id} #{window_id} #{session_activity} #{session_path}"
75723
76006
  ].map(shellEscape).join(" ")
75724
76007
  ],
75725
76008
  AbortSignal.timeout(2e3)
@@ -75728,17 +76011,21 @@ function createNativeTmuxServerCatalog(workspaceRegistry, run, assertOpen = () =
75728
76011
  const rows = /* @__PURE__ */ new Map();
75729
76012
  const lines = raw.trim() === "tmux-ide-empty-catalog-v1" ? [] : raw.trim().split("\n");
75730
76013
  for (const line of lines) {
75731
- const [pid, id2, created, name, pane, dir] = line.split(" ");
75732
- if (!pid || !/^\d+$/.test(pid) || !id2 || !/^\$\d+$/.test(id2) || !created || !/^\d+$/.test(created) || !name || !pane || !/^%\d+$/.test(pane) || !dir)
76014
+ const [pid, id2, created, name, pane, window2, activity, ...pathParts] = line.split(" ");
76015
+ const dir = pathParts.join(" ");
76016
+ if (!pid || !/^\d+$/.test(pid) || !id2 || !/^\$\d+$/.test(id2) || !created || !/^\d+$/.test(created) || !name || !pane || !/^%\d+$/.test(pane) || !window2 || !/^@\d+$/.test(window2) || !dir)
75733
76017
  throw new Error("Invalid native server catalog");
75734
76018
  if (!isVisibleFleetSession(name)) continue;
75735
76019
  const liveSessionId = liveSessionIdForNativeIdentity(pid, id2, created);
75736
76020
  const row = rows.get(liveSessionId) ?? {
75737
76021
  summary: { liveSessionId, sessionName: name, paneCount: 0 },
75738
76022
  panes: /* @__PURE__ */ new Set(),
75739
- dir
76023
+ windows: /* @__PURE__ */ new Set(),
76024
+ dir,
76025
+ activity: activity && /^\d+$/.test(activity) ? Number(activity) : null
75740
76026
  };
75741
76027
  row.panes.add(pane);
76028
+ row.windows.add(window2);
75742
76029
  rows.set(liveSessionId, row);
75743
76030
  }
75744
76031
  const names = new Set([...rows.values()].map(({ summary }) => summary.sessionName));
@@ -75753,7 +76040,13 @@ function createNativeTmuxServerCatalog(workspaceRegistry, run, assertOpen = () =
75753
76040
  projectDir: dir,
75754
76041
  persistence: "volatile"
75755
76042
  });
75756
- return [...rows.values()].map(({ summary, panes }) => ({ ...summary, paneCount: panes.size }));
76043
+ return [...rows.values()].map(({ summary, panes, windows, dir, activity }) => ({
76044
+ ...summary,
76045
+ paneCount: panes.size,
76046
+ windowCount: windows.size,
76047
+ lastActivity: activity,
76048
+ sessionPath: dir
76049
+ }));
75757
76050
  };
75758
76051
  }
75759
76052
  async function createNativeTmuxServerOwner(options) {
@@ -76334,6 +76627,8 @@ var init_tmux_server_owners = __esm({
76334
76627
  #tail = Promise.resolve();
76335
76628
  #disposed = false;
76336
76629
  #disposePromise = null;
76630
+ #queuedRefresh = null;
76631
+ #queuedRefreshTail = null;
76337
76632
  #serial(work) {
76338
76633
  const run = this.#tail.then(() => {
76339
76634
  if (this.#disposed) throw new TmuxServerScopeError("disposed");
@@ -76434,10 +76729,19 @@ var init_tmux_server_owners = __esm({
76434
76729
  this.#entries.set(scope2.serverId, { registration, scope: scope2, observation: observation2, owner });
76435
76730
  }
76436
76731
  refresh() {
76437
- return this.#serial(async () => {
76732
+ if (this.#queuedRefresh && this.#tail === this.#queuedRefreshTail) return this.#queuedRefresh;
76733
+ const queued = this.#serial(async () => {
76734
+ if (this.#queuedRefresh === queued) this.#queuedRefresh = null;
76438
76735
  for (const entry of this.#entries.values()) await this.#refresh(entry);
76439
76736
  return this.list();
76440
76737
  });
76738
+ this.#queuedRefresh = queued;
76739
+ this.#queuedRefreshTail = this.#tail;
76740
+ const settle = () => {
76741
+ if (this.#queuedRefresh === queued) this.#queuedRefresh = null;
76742
+ };
76743
+ queued.then(settle, settle);
76744
+ return queued;
76441
76745
  }
76442
76746
  current(scopeInput) {
76443
76747
  const scope2 = TmuxServerScopeSchemaZ.parse(scopeInput);
@@ -78728,7 +79032,7 @@ function createSharedFleetFactsReaders(run, isAbsentServer = () => false) {
78728
79032
  }
78729
79033
  };
78730
79034
  }
78731
- var DaemonFleetFactsObserver, SESSION_COMPOSITION_TMUX_ARGS, FLEET_FACTS_TMUX_ARGS;
79035
+ var DaemonFleetFactsObserver, FLEET_FACTS_POLL_MS, SESSION_COMPOSITION_TMUX_ARGS, FLEET_FACTS_TMUX_ARGS;
78732
79036
  var init_daemon_fleet_facts_observer = __esm({
78733
79037
  "packages/daemon/src/command-center/daemon-fleet-facts-observer.ts"() {
78734
79038
  "use strict";
@@ -78763,7 +79067,7 @@ var init_daemon_fleet_facts_observer = __esm({
78763
79067
  #diagnosticActiveOperations = 0;
78764
79068
  constructor(options) {
78765
79069
  this.#options = options;
78766
- this.#intervalMs = options.intervalMs ?? 2e3;
79070
+ this.#intervalMs = options.intervalMs ?? FLEET_FACTS_POLL_MS;
78767
79071
  this.#setTimer = options.setTimer ?? ((callback, delayMs) => {
78768
79072
  const timer = setTimeout(callback, delayMs);
78769
79073
  timer.unref?.();
@@ -79101,6 +79405,7 @@ var init_daemon_fleet_facts_observer = __esm({
79101
79405
  return this.#refs.has(demand) && (captured.get(demand) ?? 0) === (this.#demandEpochs.get(demand) ?? 0);
79102
79406
  }
79103
79407
  };
79408
+ FLEET_FACTS_POLL_MS = 500;
79104
79409
  SESSION_COMPOSITION_TMUX_ARGS = [
79105
79410
  "list-panes",
79106
79411
  "-a",
@@ -80592,6 +80897,22 @@ var init_daemon_shutdown = __esm({
80592
80897
  }
80593
80898
  });
80594
80899
 
80900
+ // packages/daemon/src/lib/catalog-fleet-read.ts
80901
+ async function readCatalogFleet(registry, runners, resolveInteractionEndpoint, nativeServerEpoch) {
80902
+ return runners.fence.active ? readAdoptedFleet(registry, runners.fencedSync, resolveInteractionEndpoint, nativeServerEpoch) : readAdoptedFleetAsync(
80903
+ registry,
80904
+ runners.generationFencedAsync,
80905
+ resolveInteractionEndpoint,
80906
+ nativeServerEpoch
80907
+ );
80908
+ }
80909
+ var init_catalog_fleet_read = __esm({
80910
+ "packages/daemon/src/lib/catalog-fleet-read.ts"() {
80911
+ "use strict";
80912
+ init_discovery();
80913
+ }
80914
+ });
80915
+
80595
80916
  // packages/daemon/src/lib/workspace-open.ts
80596
80917
  import { createHash as createHash25 } from "node:crypto";
80597
80918
  import { realpathSync as realpathSync19, statSync as statSync17 } from "node:fs";
@@ -84761,6 +85082,145 @@ var init_tmux_server_editor_context = __esm({
84761
85082
  }
84762
85083
  });
84763
85084
 
85085
+ // packages/daemon/src/lib/session-favicon.ts
85086
+ import { constants as constants16 } from "node:fs";
85087
+ import { lstat as lstat4, open as open4 } from "node:fs/promises";
85088
+ import { extname, isAbsolute as isAbsolute24, join as join41 } from "node:path";
85089
+ async function lstatOrNull(io, path2) {
85090
+ try {
85091
+ return await io.lstat(path2);
85092
+ } catch {
85093
+ return null;
85094
+ }
85095
+ }
85096
+ function createSessionFaviconResolver(options = {}) {
85097
+ const io = options.io ?? defaultSessionFaviconIo;
85098
+ const now = options.now ?? Date.now;
85099
+ const missTtlMs = options.missTtlMs ?? 3e4;
85100
+ const maxEntries = options.maxEntries ?? 256;
85101
+ const cache3 = /* @__PURE__ */ new Map();
85102
+ const remember2 = (sessionPath, entry) => {
85103
+ cache3.delete(sessionPath);
85104
+ cache3.set(sessionPath, entry);
85105
+ while (cache3.size > maxEntries) cache3.delete(cache3.keys().next().value);
85106
+ };
85107
+ const scan = async (sessionPath) => {
85108
+ const directories = /* @__PURE__ */ new Map();
85109
+ for (const candidate of SESSION_FAVICON_CANDIDATES) {
85110
+ const mime = MIME_BY_EXTENSION[extname(candidate)];
85111
+ if (!mime) continue;
85112
+ const slash = candidate.lastIndexOf("/");
85113
+ if (slash >= 0) {
85114
+ const parts = candidate.slice(0, slash).split("/");
85115
+ let real = true;
85116
+ for (let index = 1; index <= parts.length && real; index += 1) {
85117
+ const directory2 = parts.slice(0, index).join("/");
85118
+ let known = directories.get(directory2);
85119
+ if (known === void 0) {
85120
+ const stats2 = await lstatOrNull(io, join41(sessionPath, directory2));
85121
+ known = stats2 !== null && stats2.isDirectory() && !stats2.isSymbolicLink();
85122
+ directories.set(directory2, known);
85123
+ }
85124
+ real = known;
85125
+ }
85126
+ if (!real) continue;
85127
+ }
85128
+ const file = join41(sessionPath, candidate);
85129
+ const stats = await lstatOrNull(io, file);
85130
+ if (!stats || !stats.isFile() || stats.size > TMUX_SERVER_SESSION_FAVICON_MAX_BYTES) continue;
85131
+ const bytes = await io.readNoFollow(file, TMUX_SERVER_SESSION_FAVICON_MAX_BYTES);
85132
+ if (!bytes) continue;
85133
+ return {
85134
+ kind: "hit",
85135
+ file,
85136
+ mtimeMs: stats.mtimeMs,
85137
+ size: stats.size,
85138
+ favicon: { mime, data: bytes.toString("base64"), size: bytes.byteLength }
85139
+ };
85140
+ }
85141
+ return { kind: "miss", at: now() };
85142
+ };
85143
+ return async (sessionPath) => {
85144
+ if (!sessionPath || !isAbsolute24(sessionPath) || sessionPath.includes("\0")) return null;
85145
+ const cached2 = cache3.get(sessionPath);
85146
+ if (cached2?.kind === "miss" && now() - cached2.at < missTtlMs) {
85147
+ remember2(sessionPath, cached2);
85148
+ return null;
85149
+ }
85150
+ if (cached2?.kind === "hit") {
85151
+ const stats = await lstatOrNull(io, cached2.file);
85152
+ if (stats && stats.isFile() && stats.mtimeMs === cached2.mtimeMs && stats.size === cached2.size) {
85153
+ remember2(sessionPath, cached2);
85154
+ return cached2.favicon;
85155
+ }
85156
+ }
85157
+ const entry = await scan(sessionPath);
85158
+ remember2(sessionPath, entry);
85159
+ return entry.kind === "hit" ? entry.favicon : null;
85160
+ };
85161
+ }
85162
+ var SESSION_FAVICON_CANDIDATES, MIME_BY_EXTENSION, defaultSessionFaviconIo;
85163
+ var init_session_favicon = __esm({
85164
+ "packages/daemon/src/lib/session-favicon.ts"() {
85165
+ "use strict";
85166
+ init_src2();
85167
+ SESSION_FAVICON_CANDIDATES = [
85168
+ "favicon.svg",
85169
+ "favicon.png",
85170
+ "favicon.ico",
85171
+ "public/favicon.svg",
85172
+ "public/favicon.png",
85173
+ "public/favicon.ico",
85174
+ "app/icon.svg",
85175
+ "app/icon.png",
85176
+ "app/favicon.ico",
85177
+ "src/app/icon.svg",
85178
+ "src/app/icon.png",
85179
+ "src/app/favicon.ico",
85180
+ "static/favicon.svg",
85181
+ "static/favicon.png",
85182
+ "static/favicon.ico",
85183
+ "assets/favicon.svg",
85184
+ "assets/favicon.png",
85185
+ "assets/favicon.ico",
85186
+ "apple-touch-icon.png",
85187
+ "public/apple-touch-icon.png",
85188
+ "icon.svg",
85189
+ "icon.png"
85190
+ ];
85191
+ MIME_BY_EXTENSION = {
85192
+ ".png": "image/png",
85193
+ ".svg": "image/svg+xml",
85194
+ ".ico": "image/x-icon",
85195
+ ".jpg": "image/jpeg",
85196
+ ".jpeg": "image/jpeg",
85197
+ ".webp": "image/webp",
85198
+ ".gif": "image/gif"
85199
+ };
85200
+ defaultSessionFaviconIo = {
85201
+ lstat: (path2) => lstat4(path2),
85202
+ readNoFollow: async (path2, maxBytes) => {
85203
+ let handle;
85204
+ try {
85205
+ handle = await open4(path2, constants16.O_RDONLY | constants16.O_NOFOLLOW | constants16.O_NONBLOCK);
85206
+ } catch {
85207
+ return null;
85208
+ }
85209
+ try {
85210
+ const stats = await handle.stat();
85211
+ if (!stats.isFile() || stats.size > maxBytes) return null;
85212
+ const bytes = await handle.readFile();
85213
+ return bytes.byteLength > maxBytes ? null : bytes;
85214
+ } catch {
85215
+ return null;
85216
+ } finally {
85217
+ await handle.close().catch(() => void 0);
85218
+ }
85219
+ }
85220
+ };
85221
+ }
85222
+ });
85223
+
84764
85224
  // packages/daemon/src/command-center/pane-stream-issue.ts
84765
85225
  function issueError(code2, reason, retryable = false) {
84766
85226
  return PaneStreamIssueResultSchemaZ.parse({
@@ -85019,6 +85479,7 @@ function failure(c, error) {
85019
85479
  }
85020
85480
  function mountTmuxServerRoutes(app, options) {
85021
85481
  const base2 = TMUX_SERVERS_API_PATH;
85482
+ const resolveSessionFavicon = options.resolveSessionFavicon ?? createSessionFaviconResolver();
85022
85483
  const authorize = async (c, next) => {
85023
85484
  c.header("Cache-Control", "no-store");
85024
85485
  if (!ownerBearerMatches(c.req.header("Authorization"), options.ownerToken))
@@ -85191,19 +85652,54 @@ function mountTmuxServerRoutes(app, options) {
85191
85652
  `${scoped}/sessions`,
85192
85653
  route(async (c) => {
85193
85654
  const server = scope2(c);
85655
+ const v2 = c.req.query("version") === "2";
85656
+ const { rows, favicons } = await options.owners.withOwner(server, async (owner) => {
85657
+ const sessions = await owner.catalog();
85658
+ const workspaces = owner.workspaceRegistry.list();
85659
+ const rows2 = sessions.map((session) => {
85660
+ const aliases = workspaces.filter(
85661
+ (workspace) => workspace.sessionName === session.sessionName
85662
+ );
85663
+ return { session, workspaceName: aliases.length === 1 ? aliases[0].name : null };
85664
+ });
85665
+ const favicons2 = v2 ? await Promise.all(
85666
+ sessions.map(
85667
+ (session) => resolveSessionFavicon(session.sessionPath).catch(() => null)
85668
+ )
85669
+ ) : [];
85670
+ return { rows: rows2, favicons: favicons2 };
85671
+ });
85672
+ if (!v2)
85673
+ return c.json(
85674
+ TmuxServerSessionsResourceSchemaZ.parse({
85675
+ version: 1,
85676
+ server,
85677
+ sessions: rows.map(({ session, workspaceName }) => ({
85678
+ liveSessionId: session.liveSessionId,
85679
+ sessionName: session.sessionName,
85680
+ paneCount: session.paneCount,
85681
+ workspaceName
85682
+ }))
85683
+ })
85684
+ );
85685
+ let budget2 = SESSIONS_RESOURCE_FAVICON_BUDGET_BYTES;
85194
85686
  return c.json(
85195
- TmuxServerSessionsResourceSchemaZ.parse({
85196
- version: 1,
85687
+ TmuxServerSessionsResourceV2SchemaZ.parse({
85688
+ version: 2,
85197
85689
  server,
85198
- sessions: await options.owners.withOwner(server, async (owner) => {
85199
- const sessions = await owner.catalog();
85200
- const workspaces = owner.workspaceRegistry.list();
85201
- return sessions.map((session) => {
85202
- const aliases = workspaces.filter(
85203
- (workspace) => workspace.sessionName === session.sessionName
85204
- );
85205
- return { ...session, workspaceName: aliases.length === 1 ? aliases[0].name : null };
85206
- });
85690
+ sessions: rows.map(({ session, workspaceName }, index) => {
85691
+ const found = favicons[index] ?? null;
85692
+ const favicon = found && found.size <= budget2 ? found : null;
85693
+ if (favicon) budget2 -= favicon.size;
85694
+ return {
85695
+ liveSessionId: session.liveSessionId,
85696
+ sessionName: session.sessionName,
85697
+ workspaceName,
85698
+ paneCount: session.paneCount,
85699
+ windowCount: session.windowCount,
85700
+ lastActivity: session.lastActivity,
85701
+ favicon: favicon ? { mime: favicon.mime, data: favicon.data } : null
85702
+ };
85207
85703
  })
85208
85704
  })
85209
85705
  );
@@ -85469,7 +85965,7 @@ function mountTmuxServerRoutes(app, options) {
85469
85965
  })
85470
85966
  );
85471
85967
  }
85472
- var registrationSchema, workspaceSchema;
85968
+ var SESSIONS_RESOURCE_FAVICON_BUDGET_BYTES, registrationSchema, workspaceSchema;
85473
85969
  var init_tmux_servers = __esm({
85474
85970
  "packages/daemon/src/command-center/tmux-servers.ts"() {
85475
85971
  "use strict";
@@ -85484,11 +85980,13 @@ var init_tmux_servers = __esm({
85484
85980
  init_fleet_catalog2();
85485
85981
  init_src2();
85486
85982
  init_tmux_server_owners();
85983
+ init_session_favicon();
85487
85984
  init_workspace_multiplexer_verbs();
85488
85985
  init_workspace_pane_creation2();
85489
85986
  init_owner_authority();
85490
85987
  init_pane_stream_issue();
85491
85988
  init_terminal_runtime_inventory2();
85989
+ SESSIONS_RESOURCE_FAVICON_BUDGET_BYTES = 512 * 1024;
85492
85990
  registrationSchema = TmuxServerRegistrationSchemaZ.omit({ serverId: true }).strict();
85493
85991
  workspaceSchema = z125.string().min(1).max(160).refine((value) => !/[\0\r\n]/u.test(value));
85494
85992
  }
@@ -85665,9 +86163,9 @@ import {
85665
86163
  writeFileSync as writeFileSync22,
85666
86164
  watch
85667
86165
  } from "node:fs";
85668
- import { basename as basename15, dirname as dirname33, join as join41 } from "node:path";
86166
+ import { basename as basename15, dirname as dirname33, join as join42 } from "node:path";
85669
86167
  function savedMachinesPath() {
85670
- return runtimeOwnedPath(join41(resolveRuntimeNamespace().registryDir, "machines.json"));
86168
+ return runtimeOwnedPath(join42(resolveRuntimeNamespace().registryDir, "machines.json"));
85671
86169
  }
85672
86170
  function loadSavedMachines(path2 = savedMachinesPath()) {
85673
86171
  let fd;
@@ -85986,9 +86484,9 @@ import {
85986
86484
  writeFileSync as writeFileSync23
85987
86485
  } from "node:fs";
85988
86486
  import { randomUUID as randomUUID43 } from "node:crypto";
85989
- import { dirname as dirname34, join as join42 } from "node:path";
86487
+ import { dirname as dirname34, join as join43 } from "node:path";
85990
86488
  function fleetClientStatePath() {
85991
- return runtimeOwnedPath(join42(resolveRuntimeNamespace().registryDir, "fleet-view.json"));
86489
+ return runtimeOwnedPath(join43(resolveRuntimeNamespace().registryDir, "fleet-view.json"));
85992
86490
  }
85993
86491
  function loadFleetClientState(path2 = fleetClientStatePath()) {
85994
86492
  let fd;
@@ -86221,14 +86719,14 @@ var init_schemas = __esm({
86221
86719
  import { createHash as createHash28 } from "node:crypto";
86222
86720
  import { realpathSync as realpathSync21 } from "node:fs";
86223
86721
  import { existsSync as existsSync28, mkdirSync as mkdirSync27, readFileSync as readFileSync29, renameSync as renameSync17, writeFileSync as writeFileSync24 } from "node:fs";
86224
- import { dirname as dirname35, join as join43 } from "node:path";
86722
+ import { dirname as dirname35, join as join44 } from "node:path";
86225
86723
  function path(dir) {
86226
86724
  const namespace = resolveRuntimeNamespace();
86227
86725
  if (namespace.development) {
86228
86726
  const identity4 = createHash28("sha256").update(realpathSync21(dir)).digest("hex");
86229
- return runtimeOwnedPath(join43(namespace.stateHome, "projects", identity4, "terminals.json"));
86727
+ return runtimeOwnedPath(join44(namespace.stateHome, "projects", identity4, "terminals.json"));
86230
86728
  }
86231
- return join43(dir, TERMINALS_FILE);
86729
+ return join44(dir, TERMINALS_FILE);
86232
86730
  }
86233
86731
  function ensureDir(dir) {
86234
86732
  mkdirSync27(dirname35(path(dir)), { recursive: true });
@@ -86319,7 +86817,7 @@ __export(auth_service_exports, {
86319
86817
  });
86320
86818
  import * as crypto2 from "node:crypto";
86321
86819
  import { readFileSync as readFileSync30, existsSync as existsSync29 } from "node:fs";
86322
- import { join as join44 } from "node:path";
86820
+ import { join as join45 } from "node:path";
86323
86821
  import { homedir as homedir5 } from "node:os";
86324
86822
  function base64url(buf) {
86325
86823
  const b = typeof buf === "string" ? Buffer.from(buf) : buf;
@@ -86465,7 +86963,7 @@ var init_auth_service = __esm({
86465
86963
  if (resolveRuntimeNamespace().development) return false;
86466
86964
  try {
86467
86965
  const home = userId === process.env.USER ? homedir5() : `/home/${userId}`;
86468
- const authKeysPath = join44(home, ".ssh", "authorized_keys");
86966
+ const authKeysPath = join45(home, ".ssh", "authorized_keys");
86469
86967
  if (!existsSync29(authKeysPath)) return false;
86470
86968
  const authorizedKeys = readFileSync30(authKeysPath, "utf-8");
86471
86969
  const parts = publicKey.trim().split(" ");
@@ -89491,7 +89989,7 @@ var init_inspect = __esm({
89491
89989
  // packages/daemon/src/lib/filesystem-browser.ts
89492
89990
  import { realpathSync as realpathSync22, readdirSync as readdirSync6, statSync as statSync19 } from "node:fs";
89493
89991
  import { homedir as homedir6 } from "node:os";
89494
- import { isAbsolute as isAbsolute24, join as join45, resolve as resolve32, sep as sep11 } from "node:path";
89992
+ import { isAbsolute as isAbsolute25, join as join46, resolve as resolve32, sep as sep11 } from "node:path";
89495
89993
  function isUnderRoot(canonical, root) {
89496
89994
  if (canonical === root) return true;
89497
89995
  const prefix = root.endsWith(sep11) ? root : root + sep11;
@@ -89735,7 +90233,7 @@ var init_detect = __esm({
89735
90233
 
89736
90234
  // packages/daemon/src/lib/project-inspect.ts
89737
90235
  import { existsSync as existsSync32 } from "node:fs";
89738
- import { isAbsolute as isAbsolute25, resolve as resolve34 } from "node:path";
90236
+ import { isAbsolute as isAbsolute26, resolve as resolve34 } from "node:path";
89739
90237
  function narrowPackageManager(raw) {
89740
90238
  if (!raw) return null;
89741
90239
  return KNOWN_PACKAGE_MANAGERS.has(raw) ? raw : null;
@@ -89746,7 +90244,7 @@ function inferTestCommand(packageManager) {
89746
90244
  }
89747
90245
  async function inspectProject(dir, io = {}) {
89748
90246
  const exists = io.exists ?? existsSync32;
89749
- const absoluteDir = isAbsolute25(dir) ? dir : resolve34(dir);
90247
+ const absoluteDir = isAbsolute26(dir) ? dir : resolve34(dir);
89750
90248
  if (!exists(absoluteDir)) {
89751
90249
  throw new InspectDirNotFoundError(absoluteDir);
89752
90250
  }
@@ -90606,7 +91104,7 @@ var init_workspace_changes_git = __esm({
90606
91104
  // packages/daemon/src/command-center/resources/workspace-changes-authority.ts
90607
91105
  import { spawnSync } from "node:child_process";
90608
91106
  import { readFileSync as readFileSync33, realpathSync as realpathSync24, statSync as statSync20 } from "node:fs";
90609
- import { basename as basename18, isAbsolute as isAbsolute26, relative as relative8, resolve as resolvePath2 } from "node:path";
91107
+ import { basename as basename18, isAbsolute as isAbsolute27, relative as relative8, resolve as resolvePath2 } from "node:path";
90610
91108
  function runGit(args, cwd) {
90611
91109
  const result2 = spawnSync("git", args, {
90612
91110
  cwd,
@@ -90650,7 +91148,7 @@ function confineToWorkspace(realRoot, repoRoot, gitPath) {
90650
91148
  if (gitPath.length === 0 || gitPath.includes("\0")) return null;
90651
91149
  const abs = resolvePath2(repoRoot, gitPath);
90652
91150
  const rel = relative8(realRoot, abs);
90653
- if (rel.length === 0 || rel.startsWith("..") || isAbsolute26(rel)) return null;
91151
+ if (rel.length === 0 || rel.startsWith("..") || isAbsolute27(rel)) return null;
90654
91152
  const display = rel.split(/[\\/]+/u).join("/");
90655
91153
  return WorkspaceRelativeDisplayPathSchemaZ.safeParse(display).success ? display : null;
90656
91154
  }
@@ -92878,9 +93376,9 @@ import {
92878
93376
  rmSync as rmSync5,
92879
93377
  writeFileSync as writeFileSync25
92880
93378
  } from "node:fs";
92881
- import { join as join46 } from "node:path";
93379
+ import { join as join47 } from "node:path";
92882
93380
  function assetRoot() {
92883
- return runtimeOwnedPath(join46(stateHome(), ASSET_DIRECTORY));
93381
+ return runtimeOwnedPath(join47(stateHome(), ASSET_DIRECTORY));
92884
93382
  }
92885
93383
  function ensureAssetRoot() {
92886
93384
  const root = assetRoot();
@@ -92900,8 +93398,8 @@ function safeName2(name) {
92900
93398
  }
92901
93399
  function assetPaths(root, assetId) {
92902
93400
  return {
92903
- data: join46(root, `${assetId}.bin`),
92904
- metadata: join46(root, `${assetId}.json`)
93401
+ data: join47(root, `${assetId}.bin`),
93402
+ metadata: join47(root, `${assetId}.json`)
92905
93403
  };
92906
93404
  }
92907
93405
  function parseMetadata(raw) {
@@ -92926,7 +93424,7 @@ function parseMetadata(raw) {
92926
93424
  }
92927
93425
  function pruneAssets(root, now = Date.now()) {
92928
93426
  const metadataFiles = readdirSync8(root).filter((name) => /^[0-9a-f]{64}\.json$/u.test(name)).map((name) => {
92929
- const path2 = join46(root, name);
93427
+ const path2 = join47(root, name);
92930
93428
  try {
92931
93429
  const stat3 = lstatSync14(path2);
92932
93430
  return stat3.isFile() && !stat3.isSymbolicLink() ? { name, mtimeMs: stat3.mtimeMs } : null;
@@ -92937,8 +93435,8 @@ function pruneAssets(root, now = Date.now()) {
92937
93435
  for (const [index, entry] of metadataFiles.entries()) {
92938
93436
  if (index < MAX_ASSET_FILES && now - entry.mtimeMs <= WIDGET_ASSET_RETENTION_MS) continue;
92939
93437
  const assetId = entry.name.slice(0, -".json".length);
92940
- rmSync5(join46(root, `${assetId}.json`), { force: true });
92941
- rmSync5(join46(root, `${assetId}.bin`), { force: true });
93438
+ rmSync5(join47(root, `${assetId}.json`), { force: true });
93439
+ rmSync5(join47(root, `${assetId}.bin`), { force: true });
92942
93440
  }
92943
93441
  }
92944
93442
  function publishWidgetAsset(bytes, options) {
@@ -92967,11 +93465,11 @@ function publishWidgetAsset(bytes, options) {
92967
93465
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
92968
93466
  };
92969
93467
  if (!existsSync33(paths.data)) {
92970
- const temporary = join46(root, `.${assetId}.${randomUUID45()}.bin`);
93468
+ const temporary = join47(root, `.${assetId}.${randomUUID45()}.bin`);
92971
93469
  writeFileSync25(temporary, bytes, { mode: 384, flag: "wx" });
92972
93470
  renameSync18(temporary, paths.data);
92973
93471
  }
92974
- const metadataTemporary = join46(root, `.${assetId}.${randomUUID45()}.json`);
93472
+ const metadataTemporary = join47(root, `.${assetId}.${randomUUID45()}.json`);
92975
93473
  writeFileSync25(metadataTemporary, `${JSON.stringify(metadata)}
92976
93474
  `, { mode: 384, flag: "wx" });
92977
93475
  renameSync18(metadataTemporary, paths.metadata);
@@ -93033,7 +93531,7 @@ __export(server_exports2, {
93033
93531
  import { execFile as execFile14 } from "node:child_process";
93034
93532
  import { promisify as promisify4 } from "node:util";
93035
93533
  import { existsSync as existsSync34, readdirSync as readdirSync9 } from "node:fs";
93036
- import { join as join47, dirname as dirname38, basename as basename19 } from "node:path";
93534
+ import { join as join48, dirname as dirname38, basename as basename19 } from "node:path";
93037
93535
  import { fileURLToPath as fileURLToPath10 } from "node:url";
93038
93536
  import { Hono as Hono3 } from "hono";
93039
93537
  import { streamSSE as streamSSE3 } from "hono/streaming";
@@ -93042,7 +93540,7 @@ import { zValidator } from "@hono/zod-validator";
93042
93540
  import { z as z130 } from "zod";
93043
93541
  import { realpathSync as realpathSync25 } from "node:fs";
93044
93542
  import { homedir as homedir7 } from "node:os";
93045
- import { isAbsolute as isAbsolute27, resolve as pathResolve } from "node:path";
93543
+ import { isAbsolute as isAbsolute28, resolve as pathResolve } from "node:path";
93046
93544
  import { randomUUID as randomUUID46 } from "node:crypto";
93047
93545
  import { WebSocketServer as WebSocketServer4 } from "ws";
93048
93546
  function bearerToken(authHeader) {
@@ -93148,7 +93646,7 @@ function sandboxResolveDir(rawDir) {
93148
93646
  } else if (candidate.startsWith("~/")) {
93149
93647
  candidate = `${home.replace(/\/+$/, "")}/${candidate.slice(2)}`;
93150
93648
  }
93151
- if (!isAbsolute27(candidate)) {
93649
+ if (!isAbsolute28(candidate)) {
93152
93650
  return { error: "invalid-path", message: "Path must be absolute", status: 400 };
93153
93651
  }
93154
93652
  const resolved2 = pathResolve(candidate);
@@ -94285,7 +94783,7 @@ function listAvailableTemplates() {
94285
94783
  const __filename = fileURLToPath10(import.meta.url);
94286
94784
  const __dir = dirname38(__filename);
94287
94785
  const configuredTemplatesDir = process.env.TMUX_IDE_TEMPLATES_DIR;
94288
- const templatesDir = configuredTemplatesDir && isAbsolute27(configuredTemplatesDir) ? configuredTemplatesDir : join47(__dir, "..", "..", "..", "..", "templates");
94786
+ const templatesDir = configuredTemplatesDir && isAbsolute28(configuredTemplatesDir) ? configuredTemplatesDir : join48(__dir, "..", "..", "..", "..", "templates");
94289
94787
  if (!existsSync34(templatesDir)) return [];
94290
94788
  const labels = {
94291
94789
  default: { label: "Default", description: "Single Claude pane + dev/shell row" },
@@ -94482,7 +94980,7 @@ var init_types = __esm({
94482
94980
  });
94483
94981
 
94484
94982
  // packages/daemon/src/lib/daemon-embed.ts
94485
- import { dirname as dirname39, join as join48 } from "node:path";
94983
+ import { dirname as dirname39, join as join49 } from "node:path";
94486
94984
  import { execFileSync as execFileSync21 } from "node:child_process";
94487
94985
  import { randomBytes as randomBytes9, randomUUID as randomUUID47 } from "node:crypto";
94488
94986
  import { createWriteStream } from "node:fs";
@@ -95158,7 +95656,7 @@ async function startEmbeddedDaemonGeneration(opts) {
95158
95656
  execFileSync21(tmuxAuthority.executablePath, ["-V"], { encoding: "utf8", timeout: 3e3 })
95159
95657
  );
95160
95658
  const readTeamMemberships = createClaudeTeamMembershipReader(
95161
- join48(resolveRuntimeNamespace().claudeDir, "teams")
95659
+ join49(resolveRuntimeNamespace().claudeDir, "teams")
95162
95660
  );
95163
95661
  const catalogTmuxRunner = createPinnedWorkspaceTmuxRunner(tmuxAuthority);
95164
95662
  const initialTmuxProof = captureTmuxServerProof(catalogTmuxRunner);
@@ -95958,15 +96456,21 @@ async function startEmbeddedDaemonGeneration(opts) {
95958
96456
  paneStreamRuntime,
95959
96457
  catalogLiveSessions: () => discoverLiveSessionSummaries(catalogTmuxRunner),
95960
96458
  catalogFleet: async () => {
95961
- const fleet = readAdoptedFleet(
96459
+ const endpoint = (sessionName2, pane) => pane.semanticPaneId ? interactionEvidence?.captureInventoryEndpoint(
96460
+ sessionName2,
96461
+ pane.runtimePaneId,
96462
+ pane.semanticPaneId
96463
+ ) ?? null : null;
96464
+ const epoch = () => observationSelector?.nativeServerEpoch ?? null;
96465
+ const fleet = await readCatalogFleet(
95962
96466
  workspaceRegistry,
95963
- nativeGenerationTmuxRunner,
95964
- (sessionName2, pane) => pane.semanticPaneId ? interactionEvidence?.captureInventoryEndpoint(
95965
- sessionName2,
95966
- pane.runtimePaneId,
95967
- pane.semanticPaneId
95968
- ) ?? null : null,
95969
- () => observationSelector?.nativeServerEpoch ?? null
96467
+ {
96468
+ fence: sessionMutationFence,
96469
+ fencedSync: nativeGenerationTmuxRunner,
96470
+ generationFencedAsync: (args) => fleetFactsTmuxRunner(args)
96471
+ },
96472
+ endpoint,
96473
+ epoch
95970
96474
  );
95971
96475
  if (!fleet) return null;
95972
96476
  const names = await readTeamMemberships(
@@ -96418,6 +96922,7 @@ var init_daemon_embed = __esm({
96418
96922
  init_workspace_registry();
96419
96923
  init_workspace_pane_creation2();
96420
96924
  init_discovery();
96925
+ init_catalog_fleet_read();
96421
96926
  init_workspace_open2();
96422
96927
  init_workspace_promotion2();
96423
96928
  init_workspace_open_handoff2();
@@ -96825,7 +97330,7 @@ __export(development_owner_exports, {
96825
97330
  verifyManagedDevelopmentAdmission: () => verifyManagedDevelopmentAdmission
96826
97331
  });
96827
97332
  import { realpathSync as realpathSync26, writeFileSync as writeFileSync26 } from "node:fs";
96828
- import { join as join49 } from "node:path";
97333
+ import { join as join50 } from "node:path";
96829
97334
  async function claimManagedDevelopmentLaunch(instance, attempt) {
96830
97335
  if (!/^[a-f0-9-]{36}$/u.test(attempt)) throw new Error("Invalid development launch attempt");
96831
97336
  const owner = readDevelopmentOwner(instance);
@@ -96833,7 +97338,7 @@ async function claimManagedDevelopmentLaunch(instance, attempt) {
96833
97338
  throw new Error("A live or unknown managed owner is protected");
96834
97339
  requireDevelopmentNotSuspended(instance);
96835
97340
  writeFileSync26(
96836
- join49(instance.root, `launch-${attempt}.json`),
97341
+ join50(instance.root, `launch-${attempt}.json`),
96837
97342
  JSON.stringify({ version: 1, attempt, pid: process.pid }),
96838
97343
  { flag: "wx", mode: 384 }
96839
97344
  );
@@ -96842,7 +97347,7 @@ async function claimManagedDevelopmentLaunch(instance, attempt) {
96842
97347
  function verifyManagedDevelopmentAdmission(instance, attempt, expected) {
96843
97348
  requireDevelopmentNotSuspended(instance);
96844
97349
  const current = readPrivateDevelopmentRecord(
96845
- join49(instance.root, "startup.json")
97350
+ join50(instance.root, "startup.json")
96846
97351
  );
96847
97352
  if (!current || current.attempt !== attempt || current.attempt !== expected.attempt || current.generation !== expected.generation || current.manifestHash !== expected.manifestHash)
96848
97353
  throw new Error("Managed owner admission changed before startup");
@@ -96853,7 +97358,7 @@ async function runManagedDevelopmentOwner() {
96853
97358
  if (!instance) throw new Error("Managed development owner requires a development namespace");
96854
97359
  const identity4 = await readDevelopmentIdentity(instance);
96855
97360
  const attempt = process.env.TMUX_IDE_DEVELOPMENT_ATTEMPT;
96856
- const pending = readPrivateDevelopmentRecord(join49(instance.root, "startup.json"));
97361
+ const pending = readPrivateDevelopmentRecord(join50(instance.root, "startup.json"));
96857
97362
  const build = readDevelopmentBuild(instance);
96858
97363
  if (!identity4 || identity4.capability !== namespace.cleanupToken || !attempt || pending?.attempt !== attempt || pending.generation !== build.generation || pending.manifestHash !== process.env.TMUX_IDE_DEVELOPMENT_BUILD_HASH)
96859
97364
  throw new Error("Development launch admission does not match owner");
@@ -96863,7 +97368,7 @@ async function runManagedDevelopmentOwner() {
96863
97368
  if (!incarnation) throw new Error("Cannot establish managed owner incarnation");
96864
97369
  await claimManagedDevelopmentLaunch(instance, attempt);
96865
97370
  verifyManagedDevelopmentAdmission(instance, attempt, pending);
96866
- const log = createBoundedDevelopmentLog(join49(instance.root, "logs/owner.log"));
97371
+ const log = createBoundedDevelopmentLog(join50(instance.root, "logs/owner.log"));
96867
97372
  const originalOut = process.stdout.write;
96868
97373
  const originalErr = process.stderr.write;
96869
97374
  const writer = ((chunk, encodingOrCallback, callback) => {
@@ -96880,7 +97385,7 @@ async function runManagedDevelopmentOwner() {
96880
97385
  json: true,
96881
97386
  expectedVersion: build.packageVersion,
96882
97387
  onOwnedReady: () => {
96883
- writeDevelopmentRecord(join49(instance.root, "owner.json"), {
97388
+ writeDevelopmentRecord(join50(instance.root, "owner.json"), {
96884
97389
  version: 1,
96885
97390
  attempt,
96886
97391
  pid: process.pid,
@@ -96927,7 +97432,7 @@ import {
96927
97432
  readdirSync as readdirSync10,
96928
97433
  copyFileSync as copyFileSync2
96929
97434
  } from "node:fs";
96930
- import { resolve as resolve35, join as join50, basename as basename20, dirname as dirname40 } from "node:path";
97435
+ import { resolve as resolve35, join as join51, basename as basename20, dirname as dirname40 } from "node:path";
96931
97436
  import { fileURLToPath as fileURLToPath11 } from "node:url";
96932
97437
  function copyTemplateSkills(targetDir) {
96933
97438
  const created = [];
@@ -96936,20 +97441,20 @@ function copyTemplateSkills(targetDir) {
96936
97441
  mkdirSync29(targetDir, { recursive: true });
96937
97442
  for (const file of readdirSync10(templateSkillsDir)) {
96938
97443
  if (!file.endsWith(".md")) continue;
96939
- const destination = join50(targetDir, file);
96940
- copyFileSync2(join50(templateSkillsDir, file), destination);
97444
+ const destination = join51(targetDir, file);
97445
+ copyFileSync2(join51(templateSkillsDir, file), destination);
96941
97446
  created.push(destination);
96942
97447
  }
96943
97448
  return created;
96944
97449
  }
96945
97450
  function scaffoldLibraryStubs(dir) {
96946
97451
  const created = [];
96947
- const libraryDir = join50(dir, ".tmux-ide", "library");
97452
+ const libraryDir = join51(dir, ".tmux-ide", "library");
96948
97453
  if (!existsSync35(libraryDir)) {
96949
97454
  mkdirSync29(libraryDir, { recursive: true });
96950
97455
  created.push(libraryDir);
96951
97456
  }
96952
- const archPath = join50(libraryDir, "architecture.md");
97457
+ const archPath = join51(libraryDir, "architecture.md");
96953
97458
  if (!existsSync35(archPath)) {
96954
97459
  writeFileSync27(
96955
97460
  archPath,
@@ -96957,7 +97462,7 @@ function scaffoldLibraryStubs(dir) {
96957
97462
  );
96958
97463
  created.push(archPath);
96959
97464
  }
96960
- const learningsPath = join50(libraryDir, "learnings.md");
97465
+ const learningsPath = join51(libraryDir, "learnings.md");
96961
97466
  if (!existsSync35(learningsPath)) {
96962
97467
  writeFileSync27(
96963
97468
  learningsPath,
@@ -96969,11 +97474,11 @@ function scaffoldLibraryStubs(dir) {
96969
97474
  }
96970
97475
  function scaffoldValidationContract(dir) {
96971
97476
  const created = [];
96972
- const tasksDir = join50(dir, ".tasks");
97477
+ const tasksDir = join51(dir, ".tasks");
96973
97478
  if (!existsSync35(tasksDir)) {
96974
97479
  mkdirSync29(tasksDir, { recursive: true });
96975
97480
  }
96976
- const contractPath = join50(tasksDir, "validation-contract.md");
97481
+ const contractPath = join51(tasksDir, "validation-contract.md");
96977
97482
  if (!existsSync35(contractPath)) {
96978
97483
  writeFileSync27(
96979
97484
  contractPath,
@@ -96987,7 +97492,7 @@ function scaffoldAgentsMd(dir, name) {
96987
97492
  const created = [];
96988
97493
  const agentsTemplatePath = resolve35(__dirname4, "..", "..", "..", "templates", "AGENTS.md");
96989
97494
  if (existsSync35(agentsTemplatePath)) {
96990
- const agentsPath = join50(dir, "AGENTS.md");
97495
+ const agentsPath = join51(dir, "AGENTS.md");
96991
97496
  if (!existsSync35(agentsPath)) {
96992
97497
  const content = readFileSync35(agentsTemplatePath, "utf-8").replace(/{{name}}/g, name);
96993
97498
  writeFileSync27(agentsPath, content);
@@ -97008,7 +97513,7 @@ function scaffoldTeamWorkspace(dir, name) {
97008
97513
  }
97009
97514
  function scaffoldMissionsWorkspace(dir, name) {
97010
97515
  const created = [];
97011
- const skillsDir = join50(dir, ".tmux-ide", "skills");
97516
+ const skillsDir = join51(dir, ".tmux-ide", "skills");
97012
97517
  created.push(...copyTemplateSkills(skillsDir));
97013
97518
  created.push(...scaffoldTeamWorkspace(dir, name));
97014
97519
  return created;
@@ -97040,11 +97545,11 @@ async function init({
97040
97545
  created = scaffoldMissionsWorkspace(dir, name2);
97041
97546
  } else if (isTeamTemplate(template)) {
97042
97547
  created = [
97043
- ...copyTemplateSkills(join50(dir, ".tmux-ide", "skills")),
97548
+ ...copyTemplateSkills(join51(dir, ".tmux-ide", "skills")),
97044
97549
  ...scaffoldTeamWorkspace(dir, name2)
97045
97550
  ];
97046
97551
  } else {
97047
- created = copyTemplateSkills(join50(dir, ".tmux-ide", "skills"));
97552
+ created = copyTemplateSkills(join51(dir, ".tmux-ide", "skills"));
97048
97553
  }
97049
97554
  if (json3) {
97050
97555
  console.log(JSON.stringify({ created: true, template, name: name2, paths: created }));
@@ -97086,7 +97591,7 @@ async function init({
97086
97591
  console.log("Edit it to configure your workspace, then run: tmux-ide");
97087
97592
  }
97088
97593
  }
97089
- const skillsDir = join50(dir, ".tmux-ide", "skills");
97594
+ const skillsDir = join51(dir, ".tmux-ide", "skills");
97090
97595
  if (!existsSync35(skillsDir)) {
97091
97596
  const created = copyTemplateSkills(skillsDir);
97092
97597
  if (created.length > 0 && !json3) {
@@ -97141,19 +97646,21 @@ var init_stop = __esm({
97141
97646
  // packages/daemon/src/open-browser.ts
97142
97647
  var open_browser_exports = {};
97143
97648
  __export(open_browser_exports, {
97649
+ NO_NATIVE_CONTROLS_NOTICE: () => NO_NATIVE_CONTROLS_NOTICE,
97144
97650
  createBrowserPane: () => createBrowserPane,
97651
+ nativeControlsNotice: () => nativeControlsNotice,
97145
97652
  openBrowser: () => openBrowser,
97146
97653
  resolveTerminalBrowser: () => resolveTerminalBrowser,
97147
97654
  validateBrowserOpen: () => validateBrowserOpen
97148
97655
  });
97149
- import { accessSync as accessSync10, constants as constants16, realpathSync as realpathSync27, statSync as statSync21 } from "node:fs";
97150
- import { delimiter as delimiter4, isAbsolute as isAbsolute28, join as join51, resolve as resolve37 } from "node:path";
97656
+ import { accessSync as accessSync10, constants as constants17, realpathSync as realpathSync27, statSync as statSync21 } from "node:fs";
97657
+ import { delimiter as delimiter4, isAbsolute as isAbsolute29, join as join52, resolve as resolve37 } from "node:path";
97151
97658
  import { homedir as homedir8 } from "node:os";
97152
97659
  function resolveTerminalBrowser(environment = process.env, platform2 = process.platform) {
97153
97660
  const configured = environment.TMUX_IDE_TERMINAL_BROWSER_BIN;
97154
97661
  const home = environment.HOME ?? homedir8();
97155
- const bundled = platform2 === "darwin" && isAbsolute28(home) ? [
97156
- join51(
97662
+ const bundled = platform2 === "darwin" && isAbsolute29(home) ? [
97663
+ join52(
97157
97664
  home,
97158
97665
  "Library",
97159
97666
  "Application Support",
@@ -97168,12 +97675,12 @@ function resolveTerminalBrowser(environment = process.env, platform2 = process.p
97168
97675
  ] : [];
97169
97676
  const candidates = configured ? [configured] : [
97170
97677
  ...bundled,
97171
- ...(environment.PATH ?? "").split(delimiter4).filter(isAbsolute28).map((dir) => join51(dir, "terminal-browser"))
97678
+ ...(environment.PATH ?? "").split(delimiter4).filter(isAbsolute29).map((dir) => join52(dir, "terminal-browser"))
97172
97679
  ];
97173
97680
  for (const candidate of candidates) {
97174
97681
  try {
97175
- if (!isAbsolute28(candidate)) continue;
97176
- accessSync10(candidate, constants16.X_OK);
97682
+ if (!isAbsolute29(candidate)) continue;
97683
+ accessSync10(candidate, constants17.X_OK);
97177
97684
  const path2 = realpathSync27(candidate);
97178
97685
  if (statSync21(path2).isFile()) return path2;
97179
97686
  } catch {
@@ -97218,6 +97725,26 @@ function validateBrowserOpen(url, options) {
97218
97725
  exitCode: 2
97219
97726
  });
97220
97727
  }
97728
+ function isBundledBrowser(executable) {
97729
+ return /\/[^/]+\.app\/Contents\/Resources\/browser\//u.test(executable);
97730
+ }
97731
+ async function nativeControlsNotice(run, pane, executable, wait = {}) {
97732
+ if (isBundledBrowser(executable)) return null;
97733
+ const sleep3 = wait.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
97734
+ const now = wait.now ?? Date.now;
97735
+ const deadline = now() + STANDALONE_WAIT_MS;
97736
+ for (; ; ) {
97737
+ let control;
97738
+ try {
97739
+ control = run(["display-message", "-p", "-t", pane, "#{@tmux_ide_browser_control}"]).trim();
97740
+ } catch {
97741
+ return null;
97742
+ }
97743
+ if (control) return null;
97744
+ if (now() >= deadline) return NO_NATIVE_CONTROLS_NOTICE;
97745
+ await sleep3(250);
97746
+ }
97747
+ }
97221
97748
  function createBrowserPane(run, executable, url, options, environment, cwd, namespace) {
97222
97749
  validateBrowserOpen(url, options);
97223
97750
  const inheritedPane = /^%\d+$/u.test(environment.TMUX_PANE ?? "") ? environment.TMUX_PANE : void 0;
@@ -97289,9 +97816,21 @@ function createBrowserPane(run, executable, url, options, environment, cwd, name
97289
97816
  });
97290
97817
  return { pane, session: createdSession, window: createdWindow, status: "created" };
97291
97818
  }
97292
- function openBrowser(url, options = {}) {
97819
+ async function openBrowser(url, options = {}, dependencies = {}) {
97293
97820
  validateBrowserOpen(url, options);
97294
- const executable = resolveTerminalBrowser();
97821
+ const executable = (dependencies.executable ?? resolveTerminalBrowser)();
97822
+ const { result: result2, run } = (dependencies.create ?? createPinnedBrowserPane)(
97823
+ executable,
97824
+ url,
97825
+ options
97826
+ );
97827
+ const message = await nativeControlsNotice(run, result2.pane, executable, dependencies.wait);
97828
+ if (message)
97829
+ (dependencies.notice ?? ((line) => process.stderr.write(`${line}
97830
+ `)))(message);
97831
+ return result2;
97832
+ }
97833
+ function createPinnedBrowserPane(executable, url, options) {
97295
97834
  try {
97296
97835
  const namespace = { ...runtimeNamespaceEnvironment(resolveRuntimeNamespace()) };
97297
97836
  const authority2 = resolveWorkspacePaneTmuxAuthority();
@@ -97299,7 +97838,16 @@ function openBrowser(url, options = {}) {
97299
97838
  const run = createPinnedWorkspaceTmuxRunner(authority2, {
97300
97839
  timeoutMs: 1e4
97301
97840
  });
97302
- return createBrowserPane(run, executable, url, options, process.env, process.cwd(), namespace);
97841
+ const result2 = createBrowserPane(
97842
+ run,
97843
+ executable,
97844
+ url,
97845
+ options,
97846
+ process.env,
97847
+ process.cwd(),
97848
+ namespace
97849
+ );
97850
+ return { result: result2, run };
97303
97851
  } catch (error) {
97304
97852
  if (error instanceof IdeError) throw error;
97305
97853
  throw new IdeError(
@@ -97311,12 +97859,15 @@ function openBrowser(url, options = {}) {
97311
97859
  );
97312
97860
  }
97313
97861
  }
97862
+ var NO_NATIVE_CONTROLS_NOTICE, STANDALONE_WAIT_MS;
97314
97863
  var init_open_browser = __esm({
97315
97864
  "packages/daemon/src/open-browser.ts"() {
97316
97865
  "use strict";
97317
97866
  init_errors2();
97318
97867
  init_runtime_namespace();
97319
97868
  init_workspace_pane_creation2();
97869
+ NO_NATIVE_CONTROLS_NOTICE = "This browser has no tmux-ide controls; install the native app for back/forward/reload.";
97870
+ STANDALONE_WAIT_MS = 2e3;
97320
97871
  }
97321
97872
  });
97322
97873
 
@@ -97475,7 +98026,7 @@ var init_daemon_service_manager = __esm({
97475
98026
 
97476
98027
  // packages/daemon/src/lib/daemon-service-plan.ts
97477
98028
  import { createHash as createHash34 } from "node:crypto";
97478
- import { isAbsolute as isAbsolute29, join as join52 } from "node:path";
98029
+ import { isAbsolute as isAbsolute30, join as join53 } from "node:path";
97479
98030
  function safeValue(value, name) {
97480
98031
  if (!value || [...value].some((char) => char.charCodeAt(0) < 32 || char.charCodeAt(0) === 127))
97481
98032
  throw new TypeError(`${name} must be nonempty and contain no control characters`);
@@ -97483,7 +98034,7 @@ function safeValue(value, name) {
97483
98034
  }
97484
98035
  function absolute(value, name) {
97485
98036
  safeValue(value, name);
97486
- if (!isAbsolute29(value)) throw new TypeError(`${name} must be an absolute path`);
98037
+ if (!isAbsolute30(value)) throw new TypeError(`${name} must be an absolute path`);
97487
98038
  return value;
97488
98039
  }
97489
98040
  function xml(value) {
@@ -97507,8 +98058,8 @@ function planDaemonService(options) {
97507
98058
  const executable = absolute(options.executable, "Stable CLI executable");
97508
98059
  const identity4 = createHash34("sha256").update(absolute(namespace.daemonInfoDir, "Daemon state directory")).digest("hex").slice(0, 24);
97509
98060
  const supervisionId = `tmux-ide.${identity4}`;
97510
- const stdoutPath = join52(namespace.daemonInfoDir, "service.stdout.log");
97511
- const stderrPath = join52(namespace.daemonInfoDir, "service.stderr.log");
98061
+ const stdoutPath = join53(namespace.daemonInfoDir, "service.stdout.log");
98062
+ const stderrPath = join53(namespace.daemonInfoDir, "service.stderr.log");
97512
98063
  const environment = {
97513
98064
  HOME: home,
97514
98065
  PATH: safeValue(options.path, "Service PATH"),
@@ -97525,7 +98076,7 @@ function planDaemonService(options) {
97525
98076
  const shared = {
97526
98077
  supervisionId,
97527
98078
  executable,
97528
- recordPath: join52(namespace.daemonInfoDir, "service.json"),
98079
+ recordPath: join53(namespace.daemonInfoDir, "service.json"),
97529
98080
  stdoutPath,
97530
98081
  stderrPath
97531
98082
  };
@@ -97536,7 +98087,7 @@ function planDaemonService(options) {
97536
98087
  ...shared,
97537
98088
  manager: "launchd",
97538
98089
  target: `gui/${options.uid}/${label4}`,
97539
- unitPath: join52(home, "Library", "LaunchAgents", `${label4}.plist`),
98090
+ unitPath: join53(home, "Library", "LaunchAgents", `${label4}.plist`),
97540
98091
  contents: `<?xml version="1.0" encoding="UTF-8"?>
97541
98092
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
97542
98093
  <plist version="1.0"><dict>
@@ -97555,14 +98106,14 @@ function planDaemonService(options) {
97555
98106
  `
97556
98107
  });
97557
98108
  }
97558
- const configHome = absolute(options.configHome ?? join52(home, ".config"), "Config directory");
98109
+ const configHome = absolute(options.configHome ?? join53(home, ".config"), "Config directory");
97559
98110
  const target = `${supervisionId}.service`;
97560
98111
  const workingDirectory = `${home.replace(/%/gu, "%%")}/.`;
97561
98112
  return Object.freeze({
97562
98113
  ...shared,
97563
98114
  manager: "systemd",
97564
98115
  target,
97565
- unitPath: join52(configHome, "systemd", "user", target),
98116
+ unitPath: join53(configHome, "systemd", "user", target),
97566
98117
  contents: `[Unit]
97567
98118
  Description=tmux-ide canonical user daemon
97568
98119
  StartLimitIntervalSec=60
@@ -97600,7 +98151,7 @@ __export(daemon_service_exports, {
97600
98151
  });
97601
98152
  import {
97602
98153
  accessSync as accessSync11,
97603
- constants as constants17,
98154
+ constants as constants18,
97604
98155
  openSync as openSync11,
97605
98156
  closeSync as closeSync11,
97606
98157
  fstatSync as fstatSync9,
@@ -97614,7 +98165,7 @@ import {
97614
98165
  linkSync as linkSync4
97615
98166
  } from "node:fs";
97616
98167
  import { randomUUID as randomUUID48 } from "node:crypto";
97617
- import { dirname as dirname41, join as join53 } from "node:path";
98168
+ import { dirname as dirname41, join as join54 } from "node:path";
97618
98169
  import { homedir as homedir9 } from "node:os";
97619
98170
  import { z as z131 } from "zod";
97620
98171
  function defaults() {
@@ -97634,7 +98185,7 @@ function defaults() {
97634
98185
  reserve: reserveCanonicalDaemonSupervision,
97635
98186
  release: releaseCanonicalDaemonSupervision,
97636
98187
  probe: probeCanonicalDaemonIdentity,
97637
- recordPath: join53(dirname41(getCanonicalDaemonInfoPath()), "service.json"),
98188
+ recordPath: join54(dirname41(getCanonicalDaemonInfoPath()), "service.json"),
97638
98189
  waitMs: 3e4
97639
98190
  };
97640
98191
  }
@@ -97644,7 +98195,7 @@ function refused(message) {
97644
98195
  function ownedFile(path2) {
97645
98196
  let descriptor;
97646
98197
  try {
97647
- descriptor = openSync11(path2, constants17.O_RDONLY | constants17.O_NOFOLLOW | constants17.O_NONBLOCK);
98198
+ descriptor = openSync11(path2, constants18.O_RDONLY | constants18.O_NOFOLLOW | constants18.O_NONBLOCK);
97648
98199
  const stat3 = fstatSync9(descriptor);
97649
98200
  if (!stat3.isFile() || stat3.uid !== process.getuid?.() || (stat3.mode & 18) !== 0 || stat3.size > 128 * 1024)
97650
98201
  refused(`Service file is not a bounded, user-owned regular file: ${path2}`);
@@ -97691,13 +98242,13 @@ async function manageDaemonService(action, executable, deps2 = defaults()) {
97691
98242
  refused("The service manager loaded a different definition; refusing to control it");
97692
98243
  return state;
97693
98244
  };
97694
- const ready = async (previousInstance) => {
98245
+ const ready = async (previousInstance, retiredPid) => {
97695
98246
  const deadline = Date.now() + deps2.waitMs;
97696
98247
  do {
97697
98248
  const state = deps2.inspect();
97698
98249
  if (!boundTo(state, plan.supervisionId))
97699
98250
  refused("Service reservation changed during startup");
97700
- if (state.status === "valid" && state.info.instanceId !== previousInstance) {
98251
+ if (state.status === "valid" && state.info.instanceId !== previousInstance && state.info.pid !== retiredPid) {
97701
98252
  const identity4 = await deps2.probe(state.info);
97702
98253
  const service2 = await inspectManager();
97703
98254
  if (identity4 && service2.active && service2.pid === state.info.pid && identity4.pid === state.info.pid && identity4.instanceId === state.info.instanceId && identity4.startedAt === state.info.startedAt && identity4.protocolVersion === state.info.protocolVersion)
@@ -97737,7 +98288,7 @@ async function manageDaemonService(action, executable, deps2 = defaults()) {
97737
98288
  if (record2 && !boundTo(deps2.inspect(), plan.supervisionId) && !(record2.removing && action === "remove" && deps2.inspect().status === "missing"))
97738
98289
  refused("Service reservation is missing or belongs to another owner");
97739
98290
  if (!record2) {
97740
- accessSync11(plan.executable, constants17.X_OK);
98291
+ accessSync11(plan.executable, constants18.X_OK);
97741
98292
  deps2.reserve(plan.supervisionId);
97742
98293
  }
97743
98294
  const lock = `${plan.recordPath}.lock`;
@@ -97798,9 +98349,13 @@ async function manageDaemonService(action, executable, deps2 = defaults()) {
97798
98349
  }
97799
98350
  const before = deps2.inspect();
97800
98351
  if (action === "install" && service.active) return await ready();
97801
- if (service.loaded) await manager.restart(service.pid !== null);
98352
+ const current = service.loaded ? await inspectManager() : service;
98353
+ if (current.loaded) await manager.restart(current.pid !== null);
97802
98354
  else await manager.install();
97803
- return await ready(before.status === "valid" ? before.info.instanceId : void 0);
98355
+ return await ready(
98356
+ before.status === "valid" ? before.info.instanceId : void 0,
98357
+ current.loaded ? current.pid : null
98358
+ );
97804
98359
  } catch (error) {
97805
98360
  if (!activationAttempted && wroteRecord) {
97806
98361
  if (wroteUnit && ownedFile(plan.unitPath) === contents) unlinkSync9(plan.unitPath);
@@ -98353,23 +98908,23 @@ __export(skill_sync_exports, {
98353
98908
  versionMarker: () => versionMarker
98354
98909
  });
98355
98910
  import { existsSync as existsSync36, mkdirSync as mkdirSync31, readFileSync as readFileSync37, writeFileSync as writeFileSync29 } from "node:fs";
98356
- import { dirname as dirname42, join as join54 } from "node:path";
98911
+ import { dirname as dirname42, join as join55 } from "node:path";
98357
98912
  import { fileURLToPath as fileURLToPath12 } from "node:url";
98358
98913
  function claudeDir() {
98359
98914
  return resolveRuntimeNamespace().claudeDir;
98360
98915
  }
98361
98916
  function skillTargetDir() {
98362
- return runtimeOwnedPath(join54(claudeDir(), "skills", "tmux-ide"));
98917
+ return runtimeOwnedPath(join55(claudeDir(), "skills", "tmux-ide"));
98363
98918
  }
98364
98919
  function skillTargetFile() {
98365
- return join54(skillTargetDir(), "SKILL.md");
98920
+ return join55(skillTargetDir(), "SKILL.md");
98366
98921
  }
98367
98922
  function defaultSkillSource() {
98368
98923
  const here = dirname42(fileURLToPath12(import.meta.url));
98369
98924
  const candidates = [
98370
- join54(here, "../skill/SKILL.md"),
98925
+ join55(here, "../skill/SKILL.md"),
98371
98926
  // bundled bin/cli.js → repo root
98372
- join54(here, "../../../../skill/SKILL.md")
98927
+ join55(here, "../../../../skill/SKILL.md")
98373
98928
  // dev src/lib → repo root
98374
98929
  ];
98375
98930
  return candidates.find((c) => existsSync36(c)) ?? candidates[0];
@@ -98386,7 +98941,7 @@ function rewriteVersionMarker(content, version) {
98386
98941
  return content.replace(VERSION_MARKER_RE, versionMarker(version));
98387
98942
  }
98388
98943
  function installedSkillVersion(dir = skillTargetDir()) {
98389
- const file = join54(dir, "SKILL.md");
98944
+ const file = join55(dir, "SKILL.md");
98390
98945
  if (!existsSync36(file)) return null;
98391
98946
  try {
98392
98947
  return parseSkillVersion(readFileSync37(file, "utf-8"));
@@ -98402,7 +98957,7 @@ function syncSkill({
98402
98957
  throw new Error("Automatic skill sync is disabled in development instances");
98403
98958
  const rendered = rewriteVersionMarker(readFileSync37(source, "utf-8"), version);
98404
98959
  const dir = skillTargetDir();
98405
- const target = join54(dir, "SKILL.md");
98960
+ const target = join55(dir, "SKILL.md");
98406
98961
  const existing = existsSync36(target) ? readFileSync37(target, "utf-8") : null;
98407
98962
  if (existing === rendered) {
98408
98963
  return { action: "unchanged", path: target, to: version };
@@ -98435,7 +98990,7 @@ __export(doctor_exports, {
98435
98990
  workspaceConfigRow: () => workspaceConfigRow
98436
98991
  });
98437
98992
  import { execSync as execSync3, execFileSync as execFileSync22 } from "node:child_process";
98438
- import { accessSync as accessSync12, constants as constants18, existsSync as existsSync37 } from "node:fs";
98993
+ import { accessSync as accessSync12, constants as constants19, existsSync as existsSync37 } from "node:fs";
98439
98994
  import { resolve as resolve39, dirname as dirname43 } from "node:path";
98440
98995
  import { fileURLToPath as fileURLToPath13 } from "node:url";
98441
98996
  function agentIntegrationRows(agents) {
@@ -98650,7 +99205,7 @@ async function doctor({
98650
99205
  }
98651
99206
  let writable = false;
98652
99207
  try {
98653
- accessSync12(probe, constants18.W_OK);
99208
+ accessSync12(probe, constants19.W_OK);
98654
99209
  writable = true;
98655
99210
  } catch {
98656
99211
  }
@@ -101212,11 +101767,11 @@ __export(pane_team_exports, {
101212
101767
  assignPaneTeam: () => assignPaneTeam
101213
101768
  });
101214
101769
  import { execFileSync as execFileSync25 } from "node:child_process";
101215
- import { isAbsolute as isAbsolute30 } from "node:path";
101770
+ import { isAbsolute as isAbsolute31 } from "node:path";
101216
101771
  function assignPaneTeam(options, run = (args) => execFileSync25("tmux", args, { encoding: "utf8", timeout: 5e3 })) {
101217
101772
  if (!/^%\d+$/u.test(options.paneId)) throw new Error("Use an exact pane ID such as %3");
101218
101773
  if (options.socketPath && options.socketName) throw new Error("Choose one tmux socket selector");
101219
- if (options.socketPath && !isAbsolute30(options.socketPath))
101774
+ if (options.socketPath && !isAbsolute31(options.socketPath))
101220
101775
  throw new Error("Socket path must be absolute");
101221
101776
  if (options.socketName && !/^[a-zA-Z0-9_.-]+$/u.test(options.socketName))
101222
101777
  throw new Error("Invalid socket name");
@@ -101453,9 +102008,9 @@ import {
101453
102008
  unlinkSync as unlinkSync10,
101454
102009
  writeFileSync as writeFileSync30,
101455
102010
  accessSync as accessSync13,
101456
- constants as constants19
102011
+ constants as constants20
101457
102012
  } from "node:fs";
101458
- import { basename as basename21, delimiter as delimiter5, dirname as dirname45, join as join55 } from "node:path";
102013
+ import { basename as basename21, delimiter as delimiter5, dirname as dirname45, join as join56 } from "node:path";
101459
102014
  function isOptOut(value) {
101460
102015
  if (value === false || value === 0) return true;
101461
102016
  return typeof value === "string" && /^\s*(0|false|off|no)\s*$/iu.test(value);
@@ -101503,8 +102058,8 @@ function agentTeamsPaths() {
101503
102058
  function claudeOnPath2(path2 = process.env.PATH ?? "") {
101504
102059
  return path2.split(delimiter5).filter(Boolean).some((dir) => {
101505
102060
  try {
101506
- accessSync13(join55(dir, "claude"), constants19.X_OK);
101507
- return statSync23(join55(dir, "claude")).isFile();
102061
+ accessSync13(join56(dir, "claude"), constants20.X_OK);
102062
+ return statSync23(join56(dir, "claude")).isFile();
101508
102063
  } catch {
101509
102064
  return false;
101510
102065
  }
@@ -101529,7 +102084,7 @@ function existingBackup(settingsPath) {
101529
102084
  const prefix = `${basename21(settingsPath)}${BACKUP_INFIX}`;
101530
102085
  try {
101531
102086
  const found = readdirSync11(dir).find((name) => name.startsWith(prefix));
101532
- return found ? join55(dir, found) : null;
102087
+ return found ? join56(dir, found) : null;
101533
102088
  } catch {
101534
102089
  return null;
101535
102090
  }
@@ -101545,7 +102100,7 @@ function writeSettings(path2, settings, exists) {
101545
102100
  copyFileSync3(path2, backup);
101546
102101
  }
101547
102102
  }
101548
- const temporary = join55(dirname45(path2), `.${basename21(path2)}.tmux-ide-${process.pid}.tmp`);
102103
+ const temporary = join56(dirname45(path2), `.${basename21(path2)}.tmux-ide-${process.pid}.tmp`);
101549
102104
  try {
101550
102105
  writeFileSync30(temporary, `${JSON.stringify(settings, null, 2)}
101551
102106
  `, { mode: 384 });
@@ -101686,7 +102241,7 @@ __export(worktree_exports, {
101686
102241
  worktreeSessionName: () => worktreeSessionName
101687
102242
  });
101688
102243
  import { execFileSync as execFileSync26 } from "node:child_process";
101689
- import { basename as basename22, dirname as dirname46, isAbsolute as isAbsolute31, join as join56, resolve as resolve43 } from "node:path";
102244
+ import { basename as basename22, dirname as dirname46, isAbsolute as isAbsolute32, join as join57, resolve as resolve43 } from "node:path";
101690
102245
  function sanitizeForTmux(part) {
101691
102246
  return part.replace(/[.:/\s]+/g, "-");
101692
102247
  }
@@ -101695,11 +102250,11 @@ function worktreeSessionName(project, branch) {
101695
102250
  }
101696
102251
  function defaultWorktreeBaseDir(repoDir) {
101697
102252
  const abs = resolve43(repoDir);
101698
- return join56(dirname46(abs), `${basename22(abs)}-worktrees`);
102253
+ return join57(dirname46(abs), `${basename22(abs)}-worktrees`);
101699
102254
  }
101700
102255
  function worktreePath(repoDir, branch, configuredDir) {
101701
- const base2 = configuredDir && configuredDir.length > 0 ? isAbsolute31(configuredDir) ? configuredDir : resolve43(repoDir, configuredDir) : defaultWorktreeBaseDir(repoDir);
101702
- return join56(base2, branch);
102256
+ const base2 = configuredDir && configuredDir.length > 0 ? isAbsolute32(configuredDir) ? configuredDir : resolve43(repoDir, configuredDir) : defaultWorktreeBaseDir(repoDir);
102257
+ return join57(base2, branch);
101703
102258
  }
101704
102259
  function parseWorktreeList(porcelain) {
101705
102260
  const entries = [];
@@ -101834,7 +102389,7 @@ var init_worktree = __esm({
101834
102389
 
101835
102390
  // packages/daemon/src/lib/managed-installer-update.ts
101836
102391
  import { mkdtempSync, readFileSync as readFileSync40, realpathSync as realpathSync29, rmSync as rmSync7 } from "node:fs";
101837
- import { dirname as dirname47, join as join57 } from "node:path";
102392
+ import { dirname as dirname47, join as join58 } from "node:path";
101838
102393
  import { tmpdir } from "node:os";
101839
102394
  import { execFileSync as execFileSync27 } from "node:child_process";
101840
102395
  function managedInstallerPrefix(cliPath) {
@@ -101843,16 +102398,16 @@ function managedInstallerPrefix(cliPath) {
101843
102398
  );
101844
102399
  if (!match) return null;
101845
102400
  const prefix = match[1] || "/";
101846
- const root = join57(prefix, "share/tmux-ide");
101847
- const release = join57(root, "releases", match[2]);
102401
+ const root = join58(prefix, "share/tmux-ide");
102402
+ const release = join58(root, "releases", match[2]);
101848
102403
  try {
101849
- if (realpathSync29(join57(root, "current")) !== release) return null;
102404
+ if (realpathSync29(join58(root, "current")) !== release) return null;
101850
102405
  if (realpathSync29(release) !== release) return null;
101851
- if (readFileSync40(join57(root, "installer-v1"), "utf8") !== "1\n") return null;
101852
- if (readFileSync40(join57(release, ".installer-release-v1"), "utf8") !== "1\n") return null;
101853
- if (!readFileSync40(join57(prefix, "bin/tmux-ide"), "utf8").split("\n").includes("# tmux-ide universal installer v1"))
102406
+ if (readFileSync40(join58(root, "installer-v1"), "utf8") !== "1\n") return null;
102407
+ if (readFileSync40(join58(release, ".installer-release-v1"), "utf8") !== "1\n") return null;
102408
+ if (!readFileSync40(join58(prefix, "bin/tmux-ide"), "utf8").split("\n").includes("# tmux-ide universal installer v1"))
101854
102409
  return null;
101855
- if (JSON.parse(readFileSync40(join57(dirname47(cliPath), "package.json"), "utf8")).name !== "tmux-ide")
102410
+ if (JSON.parse(readFileSync40(join58(dirname47(cliPath), "package.json"), "utf8")).name !== "tmux-ide")
101856
102411
  return null;
101857
102412
  return prefix;
101858
102413
  } catch {
@@ -101860,8 +102415,8 @@ function managedInstallerPrefix(cliPath) {
101860
102415
  }
101861
102416
  }
101862
102417
  function runManagedInstallerUpdate(prefix, channel, json3, execute2 = execFileSync27) {
101863
- const temporary = mkdtempSync(join57(tmpdir(), "tmux-ide-update-"));
101864
- const script = join57(temporary, "install.sh");
102418
+ const temporary = mkdtempSync(join58(tmpdir(), "tmux-ide-update-"));
102419
+ const script = join58(temporary, "install.sh");
101865
102420
  try {
101866
102421
  execute2(
101867
102422
  "curl",
@@ -101904,7 +102459,7 @@ var init_managed_installer_update = __esm({
101904
102459
 
101905
102460
  // packages/daemon/src/lib/install-origin.ts
101906
102461
  import { existsSync as existsSync39, readFileSync as readFileSync41, realpathSync as realpathSync30 } from "node:fs";
101907
- import { dirname as dirname48, join as join58 } from "node:path";
102462
+ import { dirname as dirname48, join as join59 } from "node:path";
101908
102463
  import { fileURLToPath as fileURLToPath14 } from "node:url";
101909
102464
  function detectPackageManager(path2) {
101910
102465
  if (/\/share\/tmux-ide\/releases\/install-[^/]+\/npm\/lib\/node_modules\/tmux-ide\/bin$/.test(path2))
@@ -101921,9 +102476,9 @@ function findGitCheckoutRoot(startDir) {
101921
102476
  let dir = startDir;
101922
102477
  for (; ; ) {
101923
102478
  if (dir.endsWith("/node_modules")) return null;
101924
- if (existsSync39(join58(dir, ".git"))) {
102479
+ if (existsSync39(join59(dir, ".git"))) {
101925
102480
  try {
101926
- if (JSON.parse(readFileSync41(join58(dir, "package.json"), "utf8")).name === "tmux-ide")
102481
+ if (JSON.parse(readFileSync41(join59(dir, "package.json"), "utf8")).name === "tmux-ide")
101927
102482
  return dir;
101928
102483
  } catch {
101929
102484
  }
@@ -101944,7 +102499,7 @@ function installOrigin(cliDir = dirname48(fileURLToPath14(import.meta.url))) {
101944
102499
  let detected2 = detectPackageManager(path2);
101945
102500
  if (detected2 === "npm" || detected2 === "pnpm" || detected2 === "bun") {
101946
102501
  try {
101947
- if (JSON.parse(readFileSync41(join58(path2, "../package.json"), "utf8")).name !== "tmux-ide")
102502
+ if (JSON.parse(readFileSync41(join59(path2, "../package.json"), "utf8")).name !== "tmux-ide")
101948
102503
  detected2 = "unknown";
101949
102504
  } catch {
101950
102505
  detected2 = "unknown";
@@ -101970,7 +102525,7 @@ __export(update_exports, {
101970
102525
  runUpdate: () => runUpdate
101971
102526
  });
101972
102527
  import { realpathSync as realpathSync31 } from "node:fs";
101973
- import { dirname as dirname49, join as join59 } from "node:path";
102528
+ import { dirname as dirname49, join as join60 } from "node:path";
101974
102529
  import { execFileSync as execFileSync28 } from "node:child_process";
101975
102530
  function planUpdate(input) {
101976
102531
  const current = input.currentVersion ?? getCurrentVersion();
@@ -102060,7 +102615,7 @@ function runUpdate({ cliDir, dryRun, json: json3 = false }, dependencies = {}) {
102060
102615
  plan.executable === "bun" ? ["pm", "bin", "-g"] : ["root", "-g"]
102061
102616
  ).trim();
102062
102617
  if (!root.startsWith("/")) throw new Error("invalid manager path");
102063
- const target = plan.executable === "bun" ? dirname49(realpathSync31(join59(root, "tmux-ide"))) : realpathSync31(join59(root, "tmux-ide", "bin"));
102618
+ const target = plan.executable === "bun" ? dirname49(realpathSync31(join60(root, "tmux-ide"))) : realpathSync31(join60(root, "tmux-ide", "bin"));
102064
102619
  if (target !== source.path) throw new Error("different installation");
102065
102620
  } catch {
102066
102621
  plan = {
@@ -102122,9 +102677,9 @@ __export(pane_widget_exports, {
102122
102677
  paneWidgetId: () => paneWidgetId,
102123
102678
  paneWidgetIdForFile: () => paneWidgetIdForFile
102124
102679
  });
102125
- import { basename as basename23, extname } from "node:path";
102680
+ import { basename as basename23, extname as extname2 } from "node:path";
102126
102681
  function imageMediaTypeFor(fileName) {
102127
- return IMAGE_MEDIA_BY_EXTENSION.get(extname(fileName).toLowerCase()) ?? null;
102682
+ return IMAGE_MEDIA_BY_EXTENSION.get(extname2(fileName).toLowerCase()) ?? null;
102128
102683
  }
102129
102684
  function buildMarkdownAnnouncement(text2, title) {
102130
102685
  if (text2.trim().length === 0) {
@@ -102190,7 +102745,7 @@ function paneWidgetId(value) {
102190
102745
  );
102191
102746
  }
102192
102747
  function paneWidgetIdForFile(fileName) {
102193
- const extension = extname(fileName).toLowerCase();
102748
+ const extension = extname2(fileName).toLowerCase();
102194
102749
  if ([".md", ".markdown"].includes(extension)) return "markdown";
102195
102750
  if (IMAGE_MEDIA_BY_EXTENSION.has(extension)) return "image";
102196
102751
  if (extension === ".json") return "card";
@@ -102337,7 +102892,7 @@ var init_server3 = __esm({
102337
102892
  init_src();
102338
102893
  init_tmux_client_execution();
102339
102894
  import { parseArgs as parseArgs2 } from "node:util";
102340
- import { resolve as resolve44, dirname as dirname50, join as join60 } from "node:path";
102895
+ import { resolve as resolve44, dirname as dirname50, join as join61 } from "node:path";
102341
102896
  import { execFileSync as execFileSync29 } from "node:child_process";
102342
102897
  import { appendFileSync as appendFileSync2, existsSync as existsSync40, mkdirSync as mkdirSync33, writeFileSync as writeFileSync31 } from "node:fs";
102343
102898
  import { fileURLToPath as fileURLToPath15 } from "node:url";
@@ -102678,9 +103233,9 @@ Install bun (https://bun.sh) \u2014 the TUI surfaces run on it. Sources ship wit
102678
103233
  let automaticDiagnosticLog;
102679
103234
  if (surface === "app" && !process.env.TMUX_IDE_TUI_PERF_LOG && !process.env.TMUX_IDE_TUI_LOG) {
102680
103235
  try {
102681
- const logDirectory = join60(stateHome(), "logs");
103236
+ const logDirectory = join61(stateHome(), "logs");
102682
103237
  mkdirSync33(logDirectory, { recursive: true, mode: 448 });
102683
- automaticDiagnosticLog = join60(logDirectory, "tui-latest.jsonl");
103238
+ automaticDiagnosticLog = join61(logDirectory, "tui-latest.jsonl");
102684
103239
  writeFileSync31(
102685
103240
  automaticDiagnosticLog,
102686
103241
  `${JSON.stringify({
@@ -103052,7 +103607,7 @@ try {
103052
103607
  exitCode: 2
103053
103608
  });
103054
103609
  const { openBrowser: openBrowser2 } = await Promise.resolve().then(() => (init_open_browser(), open_browser_exports));
103055
- const result2 = openBrowser2(positionals[1], {
103610
+ const result2 = await openBrowser2(positionals[1], {
103056
103611
  session: values.session,
103057
103612
  pane: values.pane,
103058
103613
  split: values.split,