wherdr 1.3.1 → 1.3.3

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 (180) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/_nuxt/{AgentView.C4YOwvZG.css → AgentView.DzFWBd2A.css} +1 -1
  3. package/.output/public/_nuxt/B4Yk2tB1.js +23 -0
  4. package/.output/public/_nuxt/BBw_0a4M.js +6 -0
  5. package/.output/public/_nuxt/{B6qBWHWB.js → BSy6OCIY.js} +1 -1
  6. package/.output/public/_nuxt/BX5PM2JQ.js +1 -0
  7. package/.output/public/_nuxt/BgPDnf2S.js +1 -0
  8. package/.output/public/_nuxt/BkOxQzbS.js +2 -0
  9. package/.output/public/_nuxt/{WLQjK8uJ.js → CKhgznq4.js} +45 -46
  10. package/.output/public/_nuxt/{C2T9fQ4f.js → CP8o925U.js} +1 -1
  11. package/.output/public/_nuxt/CkbmecwS.js +1 -0
  12. package/.output/public/_nuxt/Cr3SrGky.js +1 -0
  13. package/.output/public/_nuxt/DAg9fiVh.js +89 -0
  14. package/.output/public/_nuxt/DT8qZ0Wv.js +1 -0
  15. package/.output/public/_nuxt/DaaJIRB3.js +1 -0
  16. package/.output/public/_nuxt/DcJi16XA.js +1 -0
  17. package/.output/public/_nuxt/VpjCgSXu.js +36 -0
  18. package/.output/public/_nuxt/builds/latest.json +1 -1
  19. package/.output/public/_nuxt/builds/meta/c735b6c0-1bd0-47d7-a208-d310f0a06287.json +1 -0
  20. package/.output/public/_nuxt/dPxB2L94.js +89 -0
  21. package/.output/public/_nuxt/{entry.cKR16VqG.css → entry.CGWKmYU3.css} +1 -1
  22. package/.output/public/_nuxt/hePW80VL.js +1 -0
  23. package/.output/public/_nuxt/xYX85SPI.js +1 -0
  24. package/.output/public/sw.js +1 -1
  25. package/.output/server/chunks/_/branding.mjs +1 -1
  26. package/.output/server/chunks/_/filePaths.mjs +25 -0
  27. package/.output/server/chunks/_/files.mjs +204 -0
  28. package/.output/server/chunks/_/machineOrder.mjs +1 -1
  29. package/.output/server/chunks/_/onboarding.mjs +1 -1
  30. package/.output/server/chunks/_/package.mjs +97 -0
  31. package/.output/server/chunks/_/pluginPolicy.mjs +93 -0
  32. package/.output/server/chunks/_/plugins.mjs +4 -94
  33. package/.output/server/chunks/_/projectBoard.mjs +120 -29
  34. package/.output/server/chunks/_/projectDocs.mjs +106 -0
  35. package/.output/server/chunks/_/projectInbox.mjs +135 -0
  36. package/.output/server/chunks/_/projectThreads.mjs +46 -0
  37. package/.output/server/chunks/_/projectThreads2.mjs +61 -0
  38. package/.output/server/chunks/_/projectsActions.mjs +1 -1
  39. package/.output/server/chunks/_/projectsPlugin.mjs +1 -1
  40. package/.output/server/chunks/_/restart.mjs +2 -312
  41. package/.output/server/chunks/_/reveal.mjs +2 -24
  42. package/.output/server/chunks/_/selfupdate.mjs +83 -0
  43. package/.output/server/chunks/_/updates.mjs +91 -0
  44. package/.output/server/chunks/_/worktrees.mjs +1 -1
  45. package/.output/server/chunks/_/ws.mjs +1 -1
  46. package/.output/server/chunks/nitro/nitro.mjs +2660 -1061
  47. package/.output/server/chunks/raw/bump-homebrew.mjs +1 -1
  48. package/.output/server/chunks/raw/herdr-plugin.mjs +1 -1
  49. package/.output/server/chunks/raw/release-ready.mjs +5 -0
  50. package/.output/server/chunks/routes/api/_..._.mjs +6 -2
  51. package/.output/server/chunks/routes/api/agents.post.mjs +6 -2
  52. package/.output/server/chunks/routes/api/ask-tab.post.mjs +6 -2
  53. package/.output/server/chunks/routes/api/attach.post.mjs +6 -2
  54. package/.output/server/chunks/routes/api/auth/disable.post.mjs +6 -2
  55. package/.output/server/chunks/routes/api/auth/lock-all.post.mjs +6 -2
  56. package/.output/server/chunks/routes/api/auth/lock.post.mjs +6 -2
  57. package/.output/server/chunks/routes/api/auth/login/options.post.mjs +6 -2
  58. package/.output/server/chunks/routes/api/auth/login/verify.post.mjs +6 -2
  59. package/.output/server/chunks/routes/api/auth/max-session.post.mjs +6 -2
  60. package/.output/server/chunks/routes/api/auth/register/options.post.mjs +6 -2
  61. package/.output/server/chunks/routes/api/auth/register/verify.post.mjs +6 -2
  62. package/.output/server/chunks/routes/api/auth/status.get.mjs +6 -2
  63. package/.output/server/chunks/routes/api/board.get.mjs +147 -0
  64. package/.output/server/chunks/routes/api/changes.get.mjs +7 -3
  65. package/.output/server/chunks/routes/api/chat/image.get.mjs +7 -3
  66. package/.output/server/chunks/routes/api/chat.get.mjs +7 -3
  67. package/.output/server/chunks/routes/api/choose.post.mjs +34 -4
  68. package/.output/server/chunks/routes/api/claude-statusline.post.mjs +7 -3
  69. package/.output/server/chunks/routes/api/close/status.get.mjs +7 -3
  70. package/.output/server/chunks/routes/api/close.post.mjs +6 -2
  71. package/.output/server/chunks/routes/api/codex-update.post.mjs +7 -3
  72. package/.output/server/chunks/routes/api/commands.get.mjs +7 -3
  73. package/.output/server/chunks/routes/api/config.get.mjs +8 -3
  74. package/.output/server/chunks/routes/api/dirs.get.mjs +7 -3
  75. package/.output/server/chunks/routes/api/dismiss-stopped.post.mjs +43 -0
  76. package/.output/server/chunks/routes/api/dismiss.post.mjs +7 -3
  77. package/.output/server/chunks/routes/api/effort.post.mjs +7 -3
  78. package/.output/server/chunks/routes/api/efforts.get.mjs +7 -3
  79. package/.output/server/chunks/routes/api/file.post.mjs +7 -3
  80. package/.output/server/chunks/routes/api/files/download.get.mjs +53 -0
  81. package/.output/server/chunks/routes/api/files/read.get.mjs +45 -0
  82. package/.output/server/chunks/routes/api/files.get.mjs +44 -0
  83. package/.output/server/chunks/routes/api/gitroot.get.mjs +7 -3
  84. package/.output/server/chunks/routes/api/health.get.mjs +36 -0
  85. package/.output/server/chunks/routes/api/index.get.mjs +6 -2
  86. package/.output/server/chunks/routes/api/input.post.mjs +18 -4
  87. package/.output/server/chunks/routes/api/interrupt.post.mjs +7 -3
  88. package/.output/server/chunks/routes/api/isgit.get.mjs +7 -3
  89. package/.output/server/chunks/routes/api/machine/awake.get.mjs +7 -3
  90. package/.output/server/chunks/routes/api/machine/awake.post.mjs +7 -3
  91. package/.output/server/chunks/routes/api/machine/order.get.mjs +6 -2
  92. package/.output/server/chunks/routes/api/machine/order.post.mjs +6 -2
  93. package/.output/server/chunks/routes/api/machine/rename.post.mjs +7 -3
  94. package/.output/server/chunks/routes/api/menu.post.mjs +7 -3
  95. package/.output/server/chunks/routes/api/model.post.mjs +7 -3
  96. package/.output/server/chunks/routes/api/models.get.mjs +7 -3
  97. package/.output/server/chunks/routes/api/nav.post.mjs +10 -5
  98. package/.output/server/chunks/routes/api/onboarding.get.mjs +6 -2
  99. package/.output/server/chunks/routes/api/onboarding.post.mjs +6 -2
  100. package/.output/server/chunks/routes/api/phone.get.mjs +6 -7
  101. package/.output/server/chunks/routes/api/phone.post.mjs +6 -7
  102. package/.output/server/chunks/routes/api/plugins/actions.get.mjs +7 -2
  103. package/.output/server/chunks/routes/api/plugins/invoke.post.mjs +7 -2
  104. package/.output/server/chunks/routes/api/plugins/profiles.get.mjs +7 -2
  105. package/.output/server/chunks/routes/api/plugins/projects.get.mjs +7 -3
  106. package/.output/server/chunks/routes/api/plugins/projects.post.mjs +7 -3
  107. package/.output/server/chunks/routes/api/plugins/thread-limits.get.mjs +7 -3
  108. package/.output/server/chunks/routes/api/plugins/thread-limits.post.mjs +7 -3
  109. package/.output/server/chunks/routes/api/project/inbox.get.mjs +43 -0
  110. package/.output/server/chunks/routes/api/project/inbox.post.mjs +43 -0
  111. package/.output/server/chunks/routes/api/project/memory.get.mjs +42 -0
  112. package/.output/server/chunks/routes/api/project/report.get.mjs +7 -3
  113. package/.output/server/chunks/routes/api/project/routines.get.mjs +43 -0
  114. package/.output/server/chunks/routes/api/project/settings.get.mjs +41 -0
  115. package/.output/server/chunks/routes/api/project/thread.get.mjs +44 -0
  116. package/.output/server/chunks/routes/api/project/thread.post.mjs +45 -0
  117. package/.output/server/chunks/routes/api/prompt.post.mjs +26 -4
  118. package/.output/server/chunks/routes/api/push/quiet.post.mjs +7 -3
  119. package/.output/server/chunks/routes/api/push/subscribe.post.mjs +7 -3
  120. package/.output/server/chunks/routes/api/push/test.post.mjs +7 -3
  121. package/.output/server/chunks/routes/api/push/unsubscribe.post.mjs +7 -3
  122. package/.output/server/chunks/routes/api/quotas.get.mjs +7 -3
  123. package/.output/server/chunks/routes/api/rename.post.mjs +6 -2
  124. package/.output/server/chunks/routes/api/requeue.post.mjs +7 -3
  125. package/.output/server/chunks/routes/api/restart.get.mjs +6 -2
  126. package/.output/server/chunks/routes/api/restart.post.mjs +6 -2
  127. package/.output/server/chunks/routes/api/reveal.post.mjs +7 -2
  128. package/.output/server/chunks/routes/api/screen-key.post.mjs +7 -3
  129. package/.output/server/chunks/routes/api/screen.get.mjs +10 -4
  130. package/.output/server/chunks/routes/api/search.get.mjs +7 -3
  131. package/.output/server/chunks/routes/api/seen.post.mjs +7 -3
  132. package/.output/server/chunks/routes/api/sessions.get.mjs +7 -3
  133. package/.output/server/chunks/routes/api/sessions.post.mjs +7 -3
  134. package/.output/server/chunks/routes/api/space.post.mjs +19 -15
  135. package/.output/server/chunks/routes/api/state.get.mjs +7 -3
  136. package/.output/server/chunks/routes/api/unqueue.post.mjs +7 -3
  137. package/.output/server/chunks/routes/api/update/status.get.mjs +38 -0
  138. package/.output/server/chunks/routes/api/update.get.mjs +12 -51
  139. package/.output/server/chunks/routes/api/update.post.mjs +41 -0
  140. package/.output/server/chunks/routes/api/upload.post.mjs +7 -3
  141. package/.output/server/chunks/routes/api/worktrees/remove.post.mjs +6 -2
  142. package/.output/server/chunks/routes/api/worktrees.get.mjs +6 -2
  143. package/.output/server/chunks/routes/favicon.ico.get.mjs +6 -2
  144. package/.output/server/chunks/routes/icons/_name_.get.mjs +7 -3
  145. package/.output/server/chunks/routes/renderer.mjs +8 -4
  146. package/.output/server/chunks/routes/uploads/_...name_.get.mjs +7 -3
  147. package/.output/server/chunks/routes/ws/_..._.mjs +7 -3
  148. package/.output/server/chunks/routes/ws/events.mjs +7 -3
  149. package/.output/server/chunks/routes/ws/term.mjs +9 -5
  150. package/.output/server/chunks/virtual/precomputed.mjs +1 -1
  151. package/.output/server/index.mjs +7 -3
  152. package/.output/server/package.json +1 -1
  153. package/README.md +440 -46
  154. package/bin/install-docker-skill.sh +20 -0
  155. package/bin/lib/commands.mjs +47 -9
  156. package/bin/lib/core.mjs +18 -2
  157. package/bin/lib/install-skill.sh +100 -0
  158. package/bin/lib/skill.mjs +16 -0
  159. package/bin/lib/tailnet.d.mts +12 -3
  160. package/bin/lib/tailnet.mjs +103 -25
  161. package/bin/lib/updater.mjs +320 -0
  162. package/bin/lib/verify-release.mjs +20 -0
  163. package/package.json +4 -2
  164. package/skills/wherdr/SKILL.md +180 -0
  165. package/.output/public/_nuxt/BDNMzG2s.js +0 -1
  166. package/.output/public/_nuxt/BMIe8p_3.js +0 -2
  167. package/.output/public/_nuxt/BpQH8Ho1.js +0 -1
  168. package/.output/public/_nuxt/BrAeJIZR.js +0 -30
  169. package/.output/public/_nuxt/C12eXl5z.js +0 -1
  170. package/.output/public/_nuxt/CXG-cOdh.js +0 -1
  171. package/.output/public/_nuxt/CYxHnMNH.js +0 -83
  172. package/.output/public/_nuxt/CctS2Xn6.js +0 -65
  173. package/.output/public/_nuxt/CiYar6hg.js +0 -6
  174. package/.output/public/_nuxt/DLLQgP0d.js +0 -1
  175. package/.output/public/_nuxt/Dwh-l2rQ.js +0 -17
  176. package/.output/public/_nuxt/bLP7Jj-b.js +0 -1
  177. package/.output/public/_nuxt/builds/meta/6466b0ab-7935-460c-abdf-2ea632dae6d0.json +0 -1
  178. package/.output/public/_nuxt/whFLy_Ne.js +0 -23
  179. package/.output/server/chunks/_/phone.mjs +0 -343
  180. package/.output/server/chunks/routes/ws/mirror.mjs +0 -157
@@ -19,10 +19,14 @@ import webpush from 'web-push';
19
19
  import os from 'node:os';
20
20
  import { spawn, execFile } from 'node:child_process';
21
21
  import net from 'node:net';
22
- import readline from 'node:readline';
23
22
  import fs$1 from 'node:fs/promises';
24
- import { fileURLToPath } from 'node:url';
23
+ import readline from 'node:readline';
24
+ import dns from 'node:dns';
25
+ import { promisify } from 'node:util';
26
+ import QRCode from 'qrcode-terminal/vendor/QRCode/index.js';
27
+ import QRErrorCorrectLevel from 'qrcode-terminal/vendor/QRCode/QRErrorCorrectLevel.js';
25
28
  import { verifyAuthenticationResponse, generateAuthenticationOptions, verifyRegistrationResponse, generateRegistrationOptions } from '@simplewebauthn/server';
29
+ import { fileURLToPath } from 'node:url';
26
30
 
27
31
  crypto.webcrypto?.subtle || {};
28
32
  const randomUUID = () => {
@@ -5892,12 +5896,12 @@ function createRouter$1(options = {}) {
5892
5896
  }
5893
5897
  return {
5894
5898
  ctx,
5895
- lookup: (path) => lookup(ctx, normalizeTrailingSlash(path)),
5899
+ lookup: (path) => lookup$1(ctx, normalizeTrailingSlash(path)),
5896
5900
  insert: (path, data) => insert(ctx, normalizeTrailingSlash(path), data),
5897
5901
  remove: (path) => remove(ctx, normalizeTrailingSlash(path))
5898
5902
  };
5899
5903
  }
5900
- function lookup(ctx, path) {
5904
+ function lookup$1(ctx, path) {
5901
5905
  const staticPathNode = ctx.staticRoutesMap[path];
5902
5906
  if (staticPathNode) {
5903
5907
  return staticPathNode.data;
@@ -8286,7 +8290,7 @@ function createStorage(options = {}) {
8286
8290
  }
8287
8291
  context.watching = true;
8288
8292
  for (const mountpoint in context.mounts) {
8289
- context.unwatch[mountpoint] = await watch(
8293
+ context.unwatch[mountpoint] = await watch$1(
8290
8294
  context.mounts[mountpoint],
8291
8295
  onChange,
8292
8296
  mountpoint
@@ -8582,7 +8586,7 @@ function createStorage(options = {}) {
8582
8586
  }
8583
8587
  context.mounts[base] = driver;
8584
8588
  if (context.watching) {
8585
- Promise.resolve(watch(driver, onChange, base)).then((unwatcher) => {
8589
+ Promise.resolve(watch$1(driver, onChange, base)).then((unwatcher) => {
8586
8590
  context.unwatch[base] = unwatcher;
8587
8591
  }).catch(console.error);
8588
8592
  }
@@ -8629,7 +8633,7 @@ function createStorage(options = {}) {
8629
8633
  };
8630
8634
  return storage;
8631
8635
  }
8632
- function watch(driver, onChange, base) {
8636
+ function watch$1(driver, onChange, base) {
8633
8637
  return driver.watch ? driver.watch((event, key) => onChange(event, base + key)) : () => {
8634
8638
  };
8635
8639
  }
@@ -8642,83 +8646,87 @@ async function dispose(driver) {
8642
8646
  const _assets = {
8643
8647
  ["scripts:build-nerd-font.py"]: {
8644
8648
  import: () => import('../raw/build-nerd-font.mjs').then(r => r.default || r),
8645
- meta: {"type":"text/plain; charset=utf-8","etag":"\"13aa-ys9YUI0kyQcBWrUDXjdBVLF8Z3E\"","mtime":"2026-10-08T11:43:41.732Z"}
8649
+ meta: {"type":"text/plain; charset=utf-8","etag":"\"13aa-ys9YUI0kyQcBWrUDXjdBVLF8Z3E\"","mtime":"2026-10-12T02:42:41.115Z"}
8646
8650
  },
8647
8651
  ["scripts:build-terminal-font.py"]: {
8648
8652
  import: () => import('../raw/build-terminal-font.mjs').then(r => r.default || r),
8649
- meta: {"type":"text/plain; charset=utf-8","etag":"\"cf5-BDqHg2xDFu+6dg84HBWtyBtrEoU\"","mtime":"2026-10-08T11:43:41.732Z"}
8653
+ meta: {"type":"text/plain; charset=utf-8","etag":"\"cf5-BDqHg2xDFu+6dg84HBWtyBtrEoU\"","mtime":"2026-10-12T02:42:41.115Z"}
8650
8654
  },
8651
8655
  ["scripts:bump-homebrew.sh"]: {
8652
8656
  import: () => import('../raw/bump-homebrew.mjs').then(r => r.default || r),
8653
- meta: {"type":"application/x-sh","etag":"\"61b-suAgb/6azMZO/UmEjog8x0wZsQA\"","mtime":"2026-10-08T11:43:41.732Z"}
8657
+ meta: {"type":"application/x-sh","etag":"\"8b5-n/EiMEIUoHPuQl2XTJU0tSNn7FY\"","mtime":"2026-10-12T02:42:41.115Z"}
8654
8658
  },
8655
8659
  ["scripts:check-leaks.mjs"]: {
8656
8660
  import: () => import('../raw/check-leaks.mjs').then(r => r.default || r),
8657
- meta: {"type":"text/javascript; charset=utf-8","etag":"\"35b3-41HZueHfw7xuicx4q83qwdTEpbc\"","mtime":"2026-10-08T11:43:41.732Z"}
8661
+ meta: {"type":"text/javascript; charset=utf-8","etag":"\"35b3-41HZueHfw7xuicx4q83qwdTEpbc\"","mtime":"2026-10-12T02:42:41.115Z"}
8658
8662
  },
8659
8663
  ["scripts:dev.wherdr.open.plist"]: {
8660
8664
  import: () => import('../raw/dev.wherdr.open.mjs').then(r => r.default || r),
8661
- meta: {"type":"text/plain; charset=utf-8","etag":"\"1de-12VO2xq63mciP26rEzRh3cGfEkI\"","mtime":"2026-10-08T11:43:41.732Z"}
8665
+ meta: {"type":"text/plain; charset=utf-8","etag":"\"1de-12VO2xq63mciP26rEzRh3cGfEkI\"","mtime":"2026-10-12T02:42:41.115Z"}
8662
8666
  },
8663
8667
  ["scripts:docker-entrypoint.sh"]: {
8664
8668
  import: () => import('../raw/docker-entrypoint.mjs').then(r => r.default || r),
8665
- meta: {"type":"application/x-sh","etag":"\"3f3-8om3/mMriIIJYO+YcdXNJsv8tkI\"","mtime":"2026-10-08T11:43:41.732Z"}
8669
+ meta: {"type":"application/x-sh","etag":"\"3f3-8om3/mMriIIJYO+YcdXNJsv8tkI\"","mtime":"2026-10-12T02:42:41.115Z"}
8666
8670
  },
8667
8671
  ["scripts:herdr-plugin.sh"]: {
8668
8672
  import: () => import('../raw/herdr-plugin.mjs').then(r => r.default || r),
8669
- meta: {"type":"application/x-sh","etag":"\"89ea-WMEExeYOqeXl5Lacbu/zP6mb8fY\"","mtime":"2026-10-08T11:43:41.732Z"}
8673
+ meta: {"type":"application/x-sh","etag":"\"9b85-YfAcqPKGuMgKmGSVqEIuA91sAE4\"","mtime":"2026-10-12T02:42:41.115Z"}
8670
8674
  },
8671
8675
  ["scripts:install-claude-statusline.sh"]: {
8672
8676
  import: () => import('../raw/install-claude-statusline.mjs').then(r => r.default || r),
8673
- meta: {"type":"application/x-sh","etag":"\"db6-p7KGlvbZ+Qjt+JiT5yVtU4w3kfg\"","mtime":"2026-10-08T11:43:41.732Z"}
8677
+ meta: {"type":"application/x-sh","etag":"\"db6-p7KGlvbZ+Qjt+JiT5yVtU4w3kfg\"","mtime":"2026-10-12T02:42:41.115Z"}
8674
8678
  },
8675
8679
  ["scripts:install-host-open.sh"]: {
8676
8680
  import: () => import('../raw/install-host-open.mjs').then(r => r.default || r),
8677
- meta: {"type":"application/x-sh","etag":"\"103a-/zplHeQSS471/Vz3quwajHmzFQs\"","mtime":"2026-10-08T11:43:41.732Z"}
8681
+ meta: {"type":"application/x-sh","etag":"\"103a-/zplHeQSS471/Vz3quwajHmzFQs\"","mtime":"2026-10-12T02:42:41.115Z"}
8678
8682
  },
8679
8683
  ["scripts:pack-output.mjs"]: {
8680
8684
  import: () => import('../raw/pack-output.mjs').then(r => r.default || r),
8681
- meta: {"type":"text/javascript; charset=utf-8","etag":"\"54b-eU4eNNIxIecG1kwaLdtiTYODSSI\"","mtime":"2026-10-08T11:43:41.732Z"}
8685
+ meta: {"type":"text/javascript; charset=utf-8","etag":"\"54b-eU4eNNIxIecG1kwaLdtiTYODSSI\"","mtime":"2026-10-12T02:42:41.115Z"}
8686
+ },
8687
+ ["scripts:release-ready.mjs"]: {
8688
+ import: () => import('../raw/release-ready.mjs').then(r => r.default || r),
8689
+ meta: {"type":"text/javascript; charset=utf-8","etag":"\"a44-ceu/5BQO5TUWzE/HNW2C/LvdCqw\"","mtime":"2026-10-12T02:42:41.115Z"}
8682
8690
  },
8683
8691
  ["scripts:reveal.sh"]: {
8684
8692
  import: () => import('../raw/reveal.mjs').then(r => r.default || r),
8685
- meta: {"type":"application/x-sh","etag":"\"72e-h/LTEU7exXdiR3GKqhj71UOwAqQ\"","mtime":"2026-10-08T11:43:41.732Z"}
8693
+ meta: {"type":"application/x-sh","etag":"\"72e-h/LTEU7exXdiR3GKqhj71UOwAqQ\"","mtime":"2026-10-12T02:42:41.115Z"}
8686
8694
  },
8687
8695
  ["scripts:wherdr-open.sh"]: {
8688
8696
  import: () => import('../raw/wherdr-open.mjs').then(r => r.default || r),
8689
- meta: {"type":"application/x-sh","etag":"\"b5b-U5OyQk3hziU9KiUyB/1FT5TkOxk\"","mtime":"2026-10-08T11:43:41.732Z"}
8697
+ meta: {"type":"application/x-sh","etag":"\"b5b-U5OyQk3hziU9KiUyB/1FT5TkOxk\"","mtime":"2026-10-12T02:42:41.115Z"}
8690
8698
  },
8691
8699
  ["server:branding:favicon.ico"]: {
8692
8700
  import: () => import('../raw/favicon.mjs').then(r => r.default || r),
8693
- meta: {"type":"image/vnd.microsoft.icon","etag":"\"3aee-Oem7XTWv+9HEwv9u/3K5QcIjS3E\"","mtime":"2026-10-08T11:43:41.734Z"}
8701
+ meta: {"type":"image/vnd.microsoft.icon","etag":"\"3aee-Oem7XTWv+9HEwv9u/3K5QcIjS3E\"","mtime":"2026-10-12T02:42:41.120Z"}
8694
8702
  },
8695
8703
  ["server:branding:icons:apple-touch-icon.png"]: {
8696
8704
  import: () => import('../raw/apple-touch-icon.mjs').then(r => r.default || r),
8697
- meta: {"type":"image/png","etag":"\"1f8-AVRrBnAhalXrGkFPGdvl0fM7DH8\"","mtime":"2026-10-08T11:43:41.734Z"}
8705
+ meta: {"type":"image/png","etag":"\"1f8-AVRrBnAhalXrGkFPGdvl0fM7DH8\"","mtime":"2026-10-12T02:42:41.120Z"}
8698
8706
  },
8699
8707
  ["server:branding:icons:favicon-32.png"]: {
8700
8708
  import: () => import('../raw/favicon-32.mjs').then(r => r.default || r),
8701
- meta: {"type":"image/png","etag":"\"a3-hAEc+Ymq1aYS3kFLyZK/H18Pge8\"","mtime":"2026-10-08T11:43:41.734Z"}
8709
+ meta: {"type":"image/png","etag":"\"a3-hAEc+Ymq1aYS3kFLyZK/H18Pge8\"","mtime":"2026-10-12T02:42:41.120Z"}
8702
8710
  },
8703
8711
  ["server:branding:icons:icon-192.png"]: {
8704
8712
  import: () => import('../raw/icon-192.mjs').then(r => r.default || r),
8705
- meta: {"type":"image/png","etag":"\"222-kioUoPaSCUaRhKKWw0PRtFbAhxk\"","mtime":"2026-10-08T11:43:41.734Z"}
8713
+ meta: {"type":"image/png","etag":"\"222-kioUoPaSCUaRhKKWw0PRtFbAhxk\"","mtime":"2026-10-12T02:42:41.120Z"}
8706
8714
  },
8707
8715
  ["server:branding:icons:icon-512-maskable.png"]: {
8708
8716
  import: () => import('../raw/icon-512-maskable.mjs').then(r => r.default || r),
8709
- meta: {"type":"image/png","etag":"\"84b-mCXgbBkXk7sqOhmJ8hjp80SN3Qk\"","mtime":"2026-10-08T11:43:41.734Z"}
8717
+ meta: {"type":"image/png","etag":"\"84b-mCXgbBkXk7sqOhmJ8hjp80SN3Qk\"","mtime":"2026-10-12T02:42:41.120Z"}
8710
8718
  },
8711
8719
  ["server:branding:icons:icon-512.png"]: {
8712
8720
  import: () => import('../raw/icon-512.mjs').then(r => r.default || r),
8713
- meta: {"type":"image/png","etag":"\"8fc-ZwRMqAqQZeLLx4iNTZxs09C4Q1I\"","mtime":"2026-10-08T11:43:41.734Z"}
8721
+ meta: {"type":"image/png","etag":"\"8fc-ZwRMqAqQZeLLx4iNTZxs09C4Q1I\"","mtime":"2026-10-12T02:42:41.120Z"}
8714
8722
  },
8715
8723
  ["server:branding:icons:icon-maskable.svg"]: {
8716
8724
  import: () => import('../raw/icon-maskable.mjs').then(r => r.default || r),
8717
- meta: {"type":"image/svg+xml","etag":"\"4a1-7XtnuN5ZOwiwIjpgpj+/Rg9LssY\"","mtime":"2026-10-08T11:43:41.734Z"}
8725
+ meta: {"type":"image/svg+xml","etag":"\"4a1-7XtnuN5ZOwiwIjpgpj+/Rg9LssY\"","mtime":"2026-10-12T02:42:41.120Z"}
8718
8726
  },
8719
8727
  ["server:branding:icons:icon.svg"]: {
8720
8728
  import: () => import('../raw/icon.mjs').then(r => r.default || r),
8721
- meta: {"type":"image/svg+xml","etag":"\"49c-O1XxkWwKTf6VBoC0i4TEPdGPsCw\"","mtime":"2026-10-08T11:43:41.734Z"}
8729
+ meta: {"type":"image/svg+xml","etag":"\"49c-O1XxkWwKTf6VBoC0i4TEPdGPsCw\"","mtime":"2026-10-12T02:42:41.120Z"}
8722
8730
  }
8723
8731
  };
8724
8732
 
@@ -9621,7 +9629,7 @@ function _expandFromEnv(value) {
9621
9629
  const _inlineRuntimeConfig = {
9622
9630
  "app": {
9623
9631
  "baseURL": "/",
9624
- "buildId": "6466b0ab-7935-460c-abdf-2ea632dae6d0",
9632
+ "buildId": "c735b6c0-1bd0-47d7-a208-d310f0a06287",
9625
9633
  "buildAssetsDir": "/_nuxt/",
9626
9634
  "cdnURL": ""
9627
9635
  },
@@ -10197,6 +10205,7 @@ const HERDR_CHILD_ENV = Object.fromEntries(
10197
10205
  const AGENT_KINDS = (env.AGENT_KINDS || "pi,claude,codex,gemini,cursor,devin,agy,cline,omp,mastracode,opencode,copilot,kimi,kiro,droid,amp,grok,hermes,kilo,qodercli,qwen,letta,maki,muse").split(",").map((s) => s.trim()).filter(Boolean);
10198
10206
  const APP_URL_FILE = path$1.join(DATA_DIR, "app-url.json");
10199
10207
  const ENV_APP_URL = /^https:\/\//i.test(env.APP_URL || "") ? env.APP_URL : "";
10208
+ const BOOT_APP_URL = env.APP_URL || "";
10200
10209
  function savedAppUrl() {
10201
10210
  var _a;
10202
10211
  try {
@@ -10209,8 +10218,13 @@ function savedAppUrl() {
10209
10218
  if (!ENV_APP_URL && savedAppUrl()) env.APP_URL = savedAppUrl();
10210
10219
  const appUrl = () => env.APP_URL || "mailto:herdr-web@localhost";
10211
10220
  const HOST_LABEL = (env.HOST_LABEL || os.hostname()).trim().slice(0, 40);
10221
+ function machineLabel(tailnetName2, fallback = HOST_LABEL, containerName = IN_DOCKER && !(env.HOST_LABEL || "").trim()) {
10222
+ const short = (tailnetName2 || "").split(".")[0].trim().slice(0, 40);
10223
+ return containerName && short ? short : fallback;
10224
+ }
10212
10225
  const PASSKEY_USER = (env.PASSKEY_USER || "wherdr").trim().slice(0, 64);
10213
10226
  const POLL_MS = Number(env.POLL_MS || 1e3);
10227
+ const TAILNET_REFRESH_MS = Number(env.TAILNET_REFRESH_MS || 3e4);
10214
10228
  const NOTIFY_SETTLE_MS = Number(env.NOTIFY_SETTLE_MS || 4e3);
10215
10229
  const UPLOAD_DIR = env.UPLOAD_DIR || path$1.join(HOME, ".cache/herdr-web/uploads");
10216
10230
  const UPLOAD_TTL_MS = 7 * 864e5;
@@ -10542,20 +10556,20 @@ function herdrSock(path, method, params = {}, timeoutMs = 15e3) {
10542
10556
  sock.on("close", () => finish(new HerdrError("closed", "Herdr connection closed without a response")));
10543
10557
  });
10544
10558
  }
10545
- async function agentPrompt(target, text) {
10559
+ async function agentPrompt(target2, text) {
10546
10560
  try {
10547
- await herdr("agent.prompt", { target, text });
10561
+ await herdr("agent.prompt", { target: target2, text });
10548
10562
  } catch (e) {
10549
10563
  if (!(e instanceof HerdrError) || e.code !== "agent_not_ready") throw e;
10550
10564
  try {
10551
- await herdr("agent.get", { target });
10565
+ await herdr("agent.get", { target: target2 });
10552
10566
  } catch {
10553
10567
  throw e;
10554
10568
  }
10555
- await herdr("agent.prompt", { target, text });
10569
+ await herdr("agent.prompt", { target: target2, text });
10556
10570
  }
10557
10571
  }
10558
- const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
10572
+ const sleep$1 = (ms) => new Promise((r) => setTimeout(r, ms));
10559
10573
 
10560
10574
  var __defProp$5 = Object.defineProperty;
10561
10575
  var __defNormalProp$5 = (obj, key, value) => key in obj ? __defProp$5(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
@@ -10707,7 +10721,7 @@ class SessionGate {
10707
10721
  stats() {
10708
10722
  return { execs: this.execs, streams: this.streams, queued: this.waiting.length, peak: this.peak, refusals: this.refusals };
10709
10723
  }
10710
- // A long-lived session (terminal, mirror) opens; call the result when it closes.
10724
+ // A long-lived session (terminal) opens; call the result when it closes.
10711
10725
  openStream() {
10712
10726
  this.streams++;
10713
10727
  this.track();
@@ -12835,6 +12849,61 @@ async function ompCommandTemplates(fs, home) {
12835
12849
  return out;
12836
12850
  }
12837
12851
 
12852
+ const PASTE_MIN_LINES = 12;
12853
+ const PASTE_MIN_CHARS = 1500;
12854
+ const lineCount = (s) => s ? s.split("\n").length : 0;
12855
+ const isLongPaste = (s) => {
12856
+ const t = s.trim();
12857
+ return lineCount(t) > PASTE_MIN_LINES || t.length > PASTE_MIN_CHARS;
12858
+ };
12859
+ const BLOCK = /<pasted_content(?:\s[^>]*)?>\n?([\s\S]*?)\n?<\/pasted_content(?:\s[^>]*)?>/g;
12860
+ function pastedBlocks(raw) {
12861
+ if (!raw.includes("<pasted_content")) return [];
12862
+ if (!raw.replace(BLOCK, "").replace(/\[Image #\d+[^\]]*\]/g, "").trim()) return [];
12863
+ return [...raw.matchAll(BLOCK)].map((m) => m[1].trim()).filter(isLongPaste);
12864
+ }
12865
+ const QUEUED_CHARS = 4e3;
12866
+ const cutShort = (text) => text.endsWith("\u2026") || text.length === QUEUED_CHARS;
12867
+ function cut(text, block, open) {
12868
+ const body = open ? text.replace(/…$/, "") : null;
12869
+ let at = text.indexOf(block);
12870
+ let end = at + block.length;
12871
+ let part = block;
12872
+ if (at < 0) {
12873
+ if (body === null) return null;
12874
+ at = body.indexOf(block.slice(0, 200));
12875
+ if (at < 0 || !block.startsWith(body.slice(at))) return null;
12876
+ part = body.slice(at).trimEnd();
12877
+ if (!isLongPaste(part)) return null;
12878
+ end = text.length;
12879
+ }
12880
+ const short = body !== null && end >= body.trimEnd().length;
12881
+ if (short) end = text.length;
12882
+ return { rest: (text.slice(0, at).replace(/\s+$/, "") + "\n" + text.slice(end).replace(/^\s+/, "")).trim(), part, short };
12883
+ }
12884
+ function splitPasted(text, listed = [], known = [], whole = false) {
12885
+ let rest = text;
12886
+ const pastes = [];
12887
+ const longKnown = known.filter(isLongPaste).sort((a, b) => b.length - a.length);
12888
+ let open = cutShort(text);
12889
+ const take = (block) => {
12890
+ const r = cut(rest, block, open);
12891
+ if (r === null) return;
12892
+ rest = r.rest;
12893
+ if (r.short) open = false;
12894
+ const sent = whole && r.short ? longKnown.filter((k) => k.startsWith(r.part)) : [];
12895
+ pastes.push(sent.length === 1 ? sent[0] : r.part);
12896
+ };
12897
+ for (const b of listed) {
12898
+ if (longKnown.some((k) => k !== b && b.includes(k))) continue;
12899
+ take(b);
12900
+ }
12901
+ for (const k of longKnown) take(k);
12902
+ if (!pastes.length) return { text, pastes };
12903
+ pastes.sort((a, b) => text.indexOf(a.slice(0, 200)) - text.indexOf(b.slice(0, 200)));
12904
+ return { text: rest, pastes };
12905
+ }
12906
+
12838
12907
  const WINDOW_BYTES = 2 * 1024 * 1024;
12839
12908
  const MAX_READ_BYTES = 24 * 1024 * 1024;
12840
12909
  const PAGE_ITEMS = 120;
@@ -12853,6 +12922,10 @@ function humanText(t) {
12853
12922
  if (u !== s) return u;
12854
12923
  return isNoise(s) ? null : s;
12855
12924
  }
12925
+ const pastedOf = (...parts) => {
12926
+ const b = parts.flatMap(pastedBlocks).map((s) => clip(s));
12927
+ return b.length ? { pasted: b } : {};
12928
+ };
12856
12929
  const normMsg = (t) => stripImageTags(t).replace(/\s+/g, " ").trim().toLowerCase();
12857
12930
  function sameMsg(queued, said) {
12858
12931
  const q = normMsg(queued).slice(0, 60);
@@ -13030,10 +13103,11 @@ function parseClaude(lines, home = "") {
13030
13103
  const ts = d.timestamp || null;
13031
13104
  if (d.type === "attachment" && d.attachment && d.attachment.type === "queued_command" && d.attachment.commandMode === "prompt" && (d.attachment.origin || {}).kind === "human") {
13032
13105
  const parts = Array.isArray(d.attachment.prompt) ? d.attachment.prompt : [{ type: "text", text: String(d.attachment.prompt || "") }];
13033
- const text = stripImageTags(unwrapPasted(parts.filter((p) => p.type === "text").map((p) => p.text).join("\n")));
13106
+ const texts = parts.filter((p) => p.type === "text").map((p) => String(p.text || ""));
13107
+ const text = stripImageTags(unwrapPasted(texts.join("\n")));
13034
13108
  const images = parts.filter((p) => p.type === "image").length;
13035
13109
  if (text || images) {
13036
- items.push({ role: "user", text: clip(text), images, ref: images ? ref : void 0, ts: d.attachment.timestamp || ts });
13110
+ items.push({ role: "user", text: clip(text), images, ref: images ? ref : void 0, ts: d.attachment.timestamp || ts, ...pastedOf(...texts) });
13037
13111
  pendingCmd = null;
13038
13112
  said(text, d.attachment.timestamp || ts, images);
13039
13113
  }
@@ -13084,7 +13158,7 @@ function parseClaude(lines, home = "") {
13084
13158
  } else {
13085
13159
  const text2 = humanText(content);
13086
13160
  if (text2) {
13087
- items.push({ role: "user", text: clip(text2), ts });
13161
+ items.push({ role: "user", text: clip(text2), ts, ...pastedOf(content) });
13088
13162
  pendingCmd = null;
13089
13163
  said(text2, ts);
13090
13164
  noteSaid(d);
@@ -13094,6 +13168,7 @@ function parseClaude(lines, home = "") {
13094
13168
  }
13095
13169
  if (!Array.isArray(content)) continue;
13096
13170
  let text = "";
13171
+ const raw = [];
13097
13172
  let images = 0;
13098
13173
  let toolImages = content.filter((p) => p && p.type === "image").length;
13099
13174
  for (const part of content) {
@@ -13111,12 +13186,13 @@ function parseClaude(lines, home = "") {
13111
13186
  if (special(String(part.text || ""), ts)) continue;
13112
13187
  const t = humanText(part.text);
13113
13188
  if (t === null) continue;
13189
+ raw.push(String(part.text || ""));
13114
13190
  if (/^\[Request interrupted/.test(t)) items.push({ role: "system", text: "Interrupted", ts });
13115
13191
  else text += (text ? "\n" : "") + unwrapPasted(t.replace(/\[Image #\d+\]\s*/g, ""));
13116
13192
  } else if (part.type === "image") images++;
13117
13193
  }
13118
13194
  if (text.trim() || images) {
13119
- items.push({ role: "user", text: clip(text.trim()), images, ref: images ? ref : void 0, ts });
13195
+ items.push({ role: "user", text: clip(text.trim()), images, ref: images ? ref : void 0, ts, ...pastedOf(...raw) });
13120
13196
  pendingCmd = null;
13121
13197
  said(text, ts, images);
13122
13198
  noteSaid(d);
@@ -13651,7 +13727,7 @@ function createTranscripts({ home, herdr: herdr2, fs = localFs }) {
13651
13727
  return { start, items, queue: queue || [] };
13652
13728
  }
13653
13729
  async function tail(file, kind, size, from) {
13654
- const key = `${file}|${from}`;
13730
+ const key = `${file}|${kind}|${from}`;
13655
13731
  const c = parseCache.get(key);
13656
13732
  if (c && c.size === size) return c.r;
13657
13733
  let r;
@@ -13688,15 +13764,16 @@ function createTranscripts({ home, herdr: herdr2, fs = localFs }) {
13688
13764
  }
13689
13765
  const fileId = path$1.basename(loc.file);
13690
13766
  const base = { available: true, file: fileId, session: loc.session, guessed: Boolean(loc.guessed) };
13691
- if (pane.agent === "omp") await refreshOmpTemplates();
13767
+ const kind = transcriptKind(loc.file) || pane.agent;
13768
+ if (kind === "omp") await refreshOmpTemplates();
13692
13769
  if (opts.before !== null && opts.before !== void 0) {
13693
- const r2 = await readBackwards(loc.file, Math.min(opts.before, st.size), pane.agent);
13770
+ const r2 = await readBackwards(loc.file, Math.min(opts.before, st.size), kind);
13694
13771
  return { ...base, older: true, items: r2.items, start: r2.start };
13695
13772
  }
13696
13773
  const from = opts.from === void 0 ? null : opts.from;
13697
13774
  const token = `${fileId}:${st.size}:${from}`;
13698
13775
  if (opts.since && opts.since === token) return { ...base, unchanged: true, token };
13699
- const r = await tail(loc.file, pane.agent, st.size, from);
13776
+ const r = await tail(loc.file, kind, st.size, from);
13700
13777
  return { ...base, token, items: r.items, start: r.start, queue: r.queue || [] };
13701
13778
  }
13702
13779
  async function preview(pane) {
@@ -13964,14 +14041,25 @@ var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key,
13964
14041
  var __publicField$3 = (obj, key, value) => __defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value);
13965
14042
  const fsp$3 = fs.promises;
13966
14043
  const LOCAL_LABEL_FILE = path$1.join(DATA_DIR, "machine-label.txt");
13967
- function readLocalLabel() {
14044
+ function renamedLabel() {
13968
14045
  try {
13969
- return fs.readFileSync(LOCAL_LABEL_FILE, "utf8").trim().slice(0, 40) || HOST_LABEL;
14046
+ return fs.readFileSync(LOCAL_LABEL_FILE, "utf8").trim().slice(0, 40);
13970
14047
  } catch {
13971
- return HOST_LABEL;
14048
+ return "";
13972
14049
  }
13973
14050
  }
13974
14051
  const localMachineLabel = () => localMachine.label;
14052
+ let tailnetLabel = "";
14053
+ function adoptTailnetName(name) {
14054
+ const label = machineLabel(name);
14055
+ if (label === HOST_LABEL || label === tailnetLabel) return;
14056
+ tailnetLabel = label;
14057
+ if (renamedLabel() || localMachine.label === label) return;
14058
+ localMachine.label = label;
14059
+ for (const other of sessions.values()) if (!(other instanceof RemoteMachine)) other.label = label;
14060
+ log(`machine name: ${label} (from Tailscale)`);
14061
+ changed();
14062
+ }
13975
14063
  function localOs() {
13976
14064
  const p = os.platform();
13977
14065
  return p === "darwin" ? "Darwin" : p === "linux" ? "Linux" : p;
@@ -13979,7 +14067,7 @@ function localOs() {
13979
14067
  const localMachine = {
13980
14068
  key: LOCAL,
13981
14069
  profileId: null,
13982
- label: readLocalLabel(),
14070
+ label: renamedLabel() || HOST_LABEL,
13983
14071
  local: true,
13984
14072
  target: null,
13985
14073
  session: HERDR_SESSION || "default",
@@ -14015,7 +14103,7 @@ function localMachineId() {
14015
14103
  }
14016
14104
  return selfId;
14017
14105
  }
14018
- const selfNames = () => [HOST_LABEL, os.hostname(), ...SELF_HOSTS].filter(Boolean);
14106
+ const selfNames = () => [HOST_LABEL, os.hostname(), tailnetLabel, ...SELF_HOSTS].filter(Boolean);
14019
14107
  class SkipMachine extends Error {
14020
14108
  }
14021
14109
  const PROBE_SCRIPT = `echo "home=$HOME"
@@ -14168,7 +14256,7 @@ ${cmd}`;
14168
14256
  child.stdin.end(opts.input || void 0);
14169
14257
  });
14170
14258
  }
14171
- // Long-lived session (terminal, mirror): counted in the budget, never queued.
14259
+ // Long-lived session (terminal): counted in the budget, never queued.
14172
14260
  spawnHerdr(args) {
14173
14261
  const sess = this.session && this.session !== "default" ? ["--session", this.session] : [];
14174
14262
  const cmd = `exec ${[this.bin || "herdr", ...sess, ...args].map(shq).join(" ")}`;
@@ -14657,16 +14745,6 @@ function splitPath(id) {
14657
14745
  }
14658
14746
  const PANE_DIRECTIONS = ["left", "right", "up", "down"];
14659
14747
 
14660
- function mirrorSize(o) {
14661
- var _a, _b;
14662
- const clamp = (v, lo, hi) => Math.min(hi, Math.max(lo, Math.round(v)));
14663
- const longest = Math.max(0, ...String(o.text || "").split("\n").map((l) => [...l.replace(/\s+$/, "")].length));
14664
- return {
14665
- cols: clamp(Math.max(longest, ((_a = o.rect) == null ? void 0 : _a.width) || 0, 20), 20, 300),
14666
- rows: clamp(o.rows || ((_b = o.rect) == null ? void 0 : _b.height) || 24, 5, 200)
14667
- };
14668
- }
14669
-
14670
14748
  const SLUG$1 = /^[a-z0-9][a-z0-9._-]*$/i;
14671
14749
  const validProject = (v) => typeof v === "string" && SLUG$1.test(v.trim()) ? v.trim() : void 0;
14672
14750
  function projectToken(tokens) {
@@ -14689,6 +14767,16 @@ function projectOf(p) {
14689
14767
  function isCoordinator(p) {
14690
14768
  return Boolean(p.agent && projectOf(p) && !isProjectThread(p));
14691
14769
  }
14770
+ function pickCoordinator(panes, slug, machine) {
14771
+ const key = slug.toLowerCase();
14772
+ const rank = (p) => (0) + (norm$4(p.cwd).toLowerCase().endsWith(`/.herdr-projects/${key}`) ? 0 : 1);
14773
+ let best = null;
14774
+ for (const p of panes) {
14775
+ if (!isCoordinator(p) || projectOf(p).toLowerCase() !== key) continue;
14776
+ if (!best || rank(p) < rank(best)) best = p;
14777
+ }
14778
+ return best;
14779
+ }
14692
14780
 
14693
14781
  function cleanLabel(label) {
14694
14782
  return (label != null ? label : "").replace(/[\u200B-\u200D\u2060\uFEFF]/g, "").replace(/[\u2800\s]+/g, " ").trim();
@@ -14833,7 +14921,7 @@ function foregroundCommand(info) {
14833
14921
  const SCREEN_MS$1 = 3e3;
14834
14922
  function keepReading(c, rev, strict, watch, now) {
14835
14923
  if (!c || c.rev !== rev || c.strict !== strict) return false;
14836
- const live = watch || !strict || Boolean(c.choices || c.screen || c.menu);
14924
+ const live = watch || !strict && !c.usageLimit || Boolean(c.choices || c.screen || c.menu || c.interaction === "terminal");
14837
14925
  return !(live && now - c.at >= SCREEN_MS$1);
14838
14926
  }
14839
14927
 
@@ -15202,6 +15290,65 @@ function clickMovesOnly(menu) {
15202
15290
  return Boolean(enter && !/^(?:select|confirm|choose|open|resume|continue)$/i.test(enter.label.trim()));
15203
15291
  }
15204
15292
 
15293
+ function parseUsageLimit(text, agent) {
15294
+ var _a, _b;
15295
+ if (agent !== "claude") return null;
15296
+ const lines = text.replace(/\x1b\[[0-9;?]*[ -/]*[@-~]|\r/g, "").split("\n");
15297
+ const field = lines.findLastIndex((l, i) => /^\s*❯(?:\s|$)/.test(l) && /^\s*─{3,}/.test(lines[i - 1] || ""));
15298
+ if (field < 0) return null;
15299
+ const rule = lines.findIndex((l, i) => i > field && /^\s*─{3,}/.test(l));
15300
+ if (rule < 0) return null;
15301
+ const tail = lines.slice(rule + 1);
15302
+ if (tail.length > 10 || tail.some((l) => /[╭┌]─|\benter to (?:select|confirm)\b|^\s*[❯›]\s*\d+\./i.test(l))) return null;
15303
+ const footer = tail.join("\n");
15304
+ const limit = footer.match(/^\s*(?:⚠\s*)?Usage limit reached(?:\s*·\s*limit resets ([^\n]+))?\s*$/m);
15305
+ if (!limit) return null;
15306
+ const resume = footer.match(/^\s*Continuing automatically (.+?)\s*·\s*esc to cancel\b/im);
15307
+ return {
15308
+ resets: ((_a = limit[1]) == null ? void 0 : _a.trim()) || ((_b = resume == null ? void 0 : resume[1]) == null ? void 0 : _b.trim()) || null,
15309
+ automatic: Boolean(resume),
15310
+ credits: /\/usage-credits to continue now\b/.test(footer),
15311
+ resetLink: /Use your limit reset to reset it now: clau\.de\/reset\b/.test(lines.slice(Math.max(0, field - 16), field).join("\n"))
15312
+ };
15313
+ }
15314
+
15315
+ function terminalException(text) {
15316
+ var _a;
15317
+ const lines = text.trimEnd().split("\n");
15318
+ const at = lines.findLastIndex((l) => /^\s*(?:Agent Command Center|Usage)\s*$/.test(l));
15319
+ if (at < 0) return false;
15320
+ const body = lines.slice(at + 1).filter((l) => l.trim());
15321
+ const legend = (_a = body.pop()) == null ? void 0 : _a.trim();
15322
+ if (lines[at].trim() === "Agent Command Center") {
15323
+ return legend === "Use arrows to navigate, Enter to open, Escape to return." && body.some((l) => /^\s*›\s+\S/.test(l)) && body.every((l) => /^(?:›\s+)?\S.* {2,}\S/.test(l.trim()));
15324
+ }
15325
+ return legend === "enter open \xB7 esc back" && body.length === 2 && /^[ \t›]*1\. View analytics {2,}Usage history[ \t]*$/.test(body[0]) && /^[ \t›]*2\. Redeem reset {2,}Check availability[ \t]*$/.test(body[1]);
15326
+ }
15327
+ function usesTerminalException(o) {
15328
+ return o.agent === "codex" && o.status !== "unknown" && o.status !== "working" && terminalException(o.text);
15329
+ }
15330
+ function confirmedInput(text) {
15331
+ const lines = text.trimEnd().split("\n");
15332
+ const at = lines.findLastIndex((l) => /^\s*[❯›](?:\s|$)/.test(l) || /^!(?:\s|$)/.test(l) || /^╰─(?:\s|$)/.test(l));
15333
+ if (at < 0) return false;
15334
+ const line = lines[at];
15335
+ const below = lines.slice(at + 1).join("\n");
15336
+ if (/\b(?:enter|esc|escape)\s+(?:to\s+)?(?:select|open|back|cancel|confirm|close)\b/i.test(below)) return false;
15337
+ if (/^\s*[❯›]\s+\d{1,2}\.\s/.test(line)) return false;
15338
+ if (/^╰─(?:\s|$)/.test(line)) return true;
15339
+ if (/^\s*›(?:\s|$)/.test(line)) return /for shortcuts|context left|\b(?:gpt-[\w.-]+|o[1-9](?:-[\w.-]+)?)\b.*·/i.test(below);
15340
+ return /^\s*─{3,}/.test(lines[at - 1] || "");
15341
+ }
15342
+ function classifyScreen(o) {
15343
+ if (o.status === "working") return "working";
15344
+ if (usesTerminalException(o)) return "terminal";
15345
+ if (o.status !== "unknown" && parseUsageLimit(o.text, o.agent)) return "native";
15346
+ if (o.choices || o.menu || o.screen || o.settings) return "native";
15347
+ if (o.status === "unknown" || !hasTranscript(o.agent) || !o.text.trim()) return "unknown";
15348
+ if (confirmedInput(o.text)) return "input";
15349
+ return "terminal";
15350
+ }
15351
+
15205
15352
  const GLYPHS = "\xB7\u2722\u2733\u2736\u273B\u273D\u273A\u2739\u2737\u2738*\u2217";
15206
15353
  const LINE_RE = new RegExp(`^([${GLYPHS}])\\s+(\\p{L}[\\p{L}'\u2019\\-]*(?: \\p{L}[\\p{L}'\u2019\\-]*){0,3})(?:\u2026|\\.\\.\\.)(?:\\s+\\((.*)\\))?\\s*$`, "u");
15207
15354
  const RULE_RE$1 = /^\s*[─━]{8,}\s*$/;
@@ -15434,20 +15581,28 @@ function checkQueue(list, status, now, menu = true) {
15434
15581
  }
15435
15582
  return changed;
15436
15583
  }
15584
+ function giveBackHeld(list) {
15585
+ for (const q of list || []) if (q.held) {
15586
+ q.failed = true;
15587
+ q.back = true;
15588
+ }
15589
+ }
15590
+ const leftForGood = (p, before) => !p.agent && !p.leaving && Boolean(before == null ? void 0 : before.leaving);
15437
15591
  const publicEntry = (q) => ({
15438
15592
  id: q.id,
15439
15593
  text: q.text,
15440
15594
  at: q.at,
15441
15595
  ...q.held && !q.failed ? { state: "held" } : {},
15442
15596
  ...q.failed ? { state: "failed" } : {},
15443
- ...(q.held || q.failed) && q.busy ? { reason: "busy" } : q.failed && q.noInput ? { reason: "no_input" } : {}
15597
+ ...(q.held || q.failed) && q.busy ? { reason: "busy" } : q.failed && q.noInput ? { reason: "no_input" } : {},
15598
+ ...q.back ? { back: true } : {}
15444
15599
  });
15445
15600
  function loadQueued(raw, now) {
15446
15601
  if (!raw || typeof raw !== "object") return [];
15447
15602
  const out = [];
15448
15603
  for (const [pane, list] of Object.entries(raw)) {
15449
15604
  if (!Array.isArray(list)) continue;
15450
- const ok = list.filter((q) => Boolean(q) && typeof q.id === "string" && typeof q.text === "string" && typeof q.at === "number" && now - q.at < QUEUED_TTL_MS).map((q) => ({ id: q.id, text: q.text, at: q.at, ...q.held ? { held: true } : {}, ...q.failed ? { failed: true } : {}, ...q.busy ? { busy: true } : {} }));
15605
+ const ok = list.filter((q) => Boolean(q) && typeof q.id === "string" && typeof q.text === "string" && typeof q.at === "number" && now - q.at < QUEUED_TTL_MS).map((q) => ({ id: q.id, text: q.text, at: q.at, ...q.held ? { held: true } : {}, ...q.failed ? { failed: true } : {}, ...q.busy ? { busy: true } : {}, ...q.back ? { back: true } : {} }));
15451
15606
  if (ok.length) out.push([pane, ok]);
15452
15607
  }
15453
15608
  return out;
@@ -15760,6 +15915,114 @@ async function interruptAgent(d, p) {
15760
15915
  return res;
15761
15916
  }
15762
15917
 
15918
+ const PASTES_MAX = 100;
15919
+ const PASTES_MAX_CHARS = 2e6;
15920
+ const PASTES_TTL_MS = 30 * 864e5;
15921
+ const live = (p, now) => now - p.at < PASTES_TTL_MS;
15922
+ const PER_MESSAGE = 20;
15923
+ function pastesAt(text, ranges) {
15924
+ if (!Array.isArray(ranges)) return [];
15925
+ const out = [];
15926
+ for (const r of ranges.slice(0, PER_MESSAGE)) {
15927
+ if (!Array.isArray(r)) continue;
15928
+ const [at, len] = r;
15929
+ if (typeof at !== "number" || typeof len !== "number" || !Number.isInteger(at) || !Number.isInteger(len)) continue;
15930
+ if (at < 0 || len <= 0 || at + len > text.length) continue;
15931
+ const b = text.slice(at, at + len).trim();
15932
+ if (isLongPaste(b) && !out.includes(b)) out.push(b);
15933
+ }
15934
+ return out;
15935
+ }
15936
+ function addPastes(list, blocks, now) {
15937
+ if (!blocks.length) return list;
15938
+ let total = 0;
15939
+ return [...blocks.map((text) => ({ text, at: now })), ...list.filter((p) => !blocks.includes(p.text) && live(p, now))].slice(0, PASTES_MAX).filter((p) => {
15940
+ total += p.text.length;
15941
+ return total <= PASTES_MAX_CHARS;
15942
+ });
15943
+ }
15944
+ function loadPastes(raw, now) {
15945
+ if (!Array.isArray(raw)) return [];
15946
+ const out = [];
15947
+ let total = 0;
15948
+ for (const r of raw) {
15949
+ const p = typeof r === "string" ? { text: r, at: now } : r;
15950
+ if (!p || typeof p.text !== "string" || typeof p.at !== "number" || !Number.isFinite(p.at)) continue;
15951
+ if (!isLongPaste(p.text) || !live(p, now) || out.some((o) => o.text === p.text)) continue;
15952
+ if (out.length >= PASTES_MAX || (total += p.text.length) > PASTES_MAX_CHARS) break;
15953
+ out.push({ text: p.text, at: Math.min(p.at, now) });
15954
+ }
15955
+ return out;
15956
+ }
15957
+ function pastedIn(list, text, listed = []) {
15958
+ if (!list.length || !isLongPaste(text)) return listed;
15959
+ const known = list.filter((b) => !listed.includes(b)).sort((a, b) => b.length - a.length);
15960
+ return splitPasted(text, listed, known).pastes;
15961
+ }
15962
+ function withPasted(items, list) {
15963
+ if (!list.length) return items;
15964
+ return items.map((i) => {
15965
+ if (i.role !== "user") return i;
15966
+ const pasted = pastedIn(list, i.text, i.pasted);
15967
+ return pasted.length && pasted !== i.pasted ? { ...i, pasted } : i;
15968
+ });
15969
+ }
15970
+ function withQueuedPasted(q, list) {
15971
+ const pasted = pastedIn(list, q.text);
15972
+ return pasted.length ? { ...q, pasted } : q;
15973
+ }
15974
+ const PASTES_FILE = path$1.join(DATA_DIR, "pastes.json");
15975
+ let kept = null;
15976
+ let saved = null;
15977
+ let texts = null;
15978
+ function pastes() {
15979
+ if (kept) return kept;
15980
+ kept = [];
15981
+ saved = kept;
15982
+ try {
15983
+ const raw = fs.readFileSync(PASTES_FILE, "utf8");
15984
+ kept = loadPastes(JSON.parse(raw), Date.now());
15985
+ saved = JSON.stringify(kept) + "\n" === raw ? kept : null;
15986
+ } catch {
15987
+ }
15988
+ return kept;
15989
+ }
15990
+ function keptTexts() {
15991
+ const list = pastes();
15992
+ if ((texts == null ? void 0 : texts.of) !== list) texts = { of: list, list: list.map((p) => p.text) };
15993
+ return texts.list;
15994
+ }
15995
+ let writing = Promise.resolve();
15996
+ function save() {
15997
+ writing = writing.then(async () => {
15998
+ const list = kept;
15999
+ if (!list || list === saved) return;
16000
+ const temp = `${PASTES_FILE}.${randomUUID$1()}.tmp`;
16001
+ try {
16002
+ await fs$1.mkdir(DATA_DIR, { recursive: true });
16003
+ await fs$1.writeFile(temp, JSON.stringify(list) + "\n", { mode: 384 });
16004
+ await fs$1.rename(temp, PASTES_FILE);
16005
+ saved = list;
16006
+ } catch {
16007
+ await fs$1.rm(temp, { force: true }).catch(() => {
16008
+ });
16009
+ }
16010
+ });
16011
+ }
16012
+ function keepPastes(text, ranges) {
16013
+ const next = addPastes(pastes(), pastesAt(text, ranges), Date.now());
16014
+ if (next === kept) return;
16015
+ kept = next;
16016
+ save();
16017
+ }
16018
+ function purgePastes(now = Date.now()) {
16019
+ const list = pastes();
16020
+ if (list.some((p) => !live(p, now))) kept = list.filter((p) => live(p, now));
16021
+ if (kept !== saved) save();
16022
+ }
16023
+ const chatPasted = (items) => withPasted(items, keptTexts());
16024
+ const queuedPasted = (q) => withQueuedPasted(q, keptTexts());
16025
+
15763
16026
  const DEFAULTS = { freeWaitMs: 2e3, quietMs: 400, showWaitMs: 4e3, submitWaitMs: 4e3, pollMs: 50 };
15764
16027
  const RECORD_MS = 600;
15765
16028
  const MAX_ATTEMPTS = 3;
@@ -16171,7 +16434,7 @@ async function createAgent(body) {
16171
16434
  created = await hx("worktree.create", params, 3e4);
16172
16435
  } catch (e) {
16173
16436
  if (e.code !== "not_git_worktree") throw e;
16174
- await sleep(1e3);
16437
+ await sleep$1(1e3);
16175
16438
  created = await hx("worktree.create", params, 3e4);
16176
16439
  }
16177
16440
  } else if (!target) {
@@ -16196,7 +16459,7 @@ async function createAgent(body) {
16196
16459
  const pathSetup = 'export PATH="$HOME/.local/bin:$HOME/.kimi-code/bin:$HOME/.bun/bin:/opt/homebrew/bin:/usr/local/bin:$PATH"';
16197
16460
  await herdr("pane.send_input", { pane_id: paneId, text: pathSetup });
16198
16461
  await herdr("pane.send_input", { pane_id: paneId, keys: ["enter"] });
16199
- await sleep(400);
16462
+ await sleep$1(400);
16200
16463
  let lastErr = null;
16201
16464
  for (let i = 0; i < 16; i++) {
16202
16465
  try {
@@ -16211,7 +16474,7 @@ async function createAgent(body) {
16211
16474
  break;
16212
16475
  }
16213
16476
  if (lastErr.code === "timeout" || lastErr.code === "unreachable" || /name/i.test(lastErr.code)) break;
16214
- await sleep(500);
16477
+ await sleep$1(500);
16215
16478
  }
16216
16479
  }
16217
16480
  if (lastErr) {
@@ -16297,7 +16560,7 @@ async function closePanel(paneId) {
16297
16560
  if (!panelOpen(r.read && r.read.text, p.agent)) return sent;
16298
16561
  await herdr("pane.send_input", { pane_id: paneId, keys: ["esc"] });
16299
16562
  sent = true;
16300
- await sleep(400);
16563
+ await sleep$1(400);
16301
16564
  }
16302
16565
  return sent;
16303
16566
  }
@@ -16336,10 +16599,184 @@ async function readUpload(name) {
16336
16599
  return { type: ext === "jpg" ? "image/jpeg" : `image/${ext}`, body: await fsp$2.readFile(path$1.join(UPLOAD_DIR, name)) };
16337
16600
  }
16338
16601
 
16602
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
16603
+ function createPaneSizes(deps) {
16604
+ const entries = /* @__PURE__ */ new Map();
16605
+ const log = deps.log || (() => {
16606
+ });
16607
+ function save() {
16608
+ if (!deps.save) return;
16609
+ const out = {};
16610
+ for (const [pane, e] of entries) if (e.original) out[pane] = e.original;
16611
+ deps.save(out);
16612
+ }
16613
+ function drop(pane, e) {
16614
+ if (entries.get(pane) !== e) return;
16615
+ entries.delete(pane);
16616
+ save();
16617
+ }
16618
+ function keepReason(e, now) {
16619
+ const o = e.original;
16620
+ if (!now) return "pane gone";
16621
+ if (o.terminal && now.terminal && o.terminal !== now.terminal) return "another terminal";
16622
+ if (now.rows === o.rows && now.cols === o.cols) return "already at its size";
16623
+ const set = e.set;
16624
+ if (set && (now.rows !== set.rows || now.exact && now.cols !== set.cols)) return `resized by another client to ${now.cols}x${now.rows}`;
16625
+ return null;
16626
+ }
16627
+ async function restore(pane, e) {
16628
+ var _a;
16629
+ const o = e.original;
16630
+ const why = keepReason(e, await deps.read(pane).catch(() => null));
16631
+ if (why) return log(`term ${pane} size kept (${why})`);
16632
+ for (let attempt = 0; attempt < 2; attempt++) {
16633
+ if (attempt) await sleep((_a = deps.retryMs) != null ? _a : 1500);
16634
+ if (e.clients > 0) return;
16635
+ if (await deps.resize(pane, o.cols, o.rows).catch(() => false)) return;
16636
+ }
16637
+ log(`term ${pane} could not get its ${o.cols}x${o.rows} back`);
16638
+ }
16639
+ function schedule(pane, e, ms) {
16640
+ clearTimeout(e.timer);
16641
+ e.timer = setTimeout(() => {
16642
+ e.timer = void 0;
16643
+ e.restoring = restore(pane, e).catch(() => {
16644
+ }).finally(() => {
16645
+ e.restoring = null;
16646
+ if (e.clients === 0 && !e.timer) drop(pane, e);
16647
+ });
16648
+ }, ms);
16649
+ }
16650
+ async function hold(pane) {
16651
+ let e = entries.get(pane);
16652
+ if (e) {
16653
+ clearTimeout(e.timer);
16654
+ e.timer = void 0;
16655
+ e.clients++;
16656
+ } else {
16657
+ const fresh = { original: null, ready: Promise.resolve(), clients: 1, set: null, restoring: null };
16658
+ fresh.ready = deps.read(pane).catch(() => null).then((size) => {
16659
+ fresh.original = size;
16660
+ if (size) save();
16661
+ });
16662
+ entries.set(pane, fresh);
16663
+ e = fresh;
16664
+ }
16665
+ const entry = e;
16666
+ await entry.ready;
16667
+ if (entry.restoring) await entry.restoring;
16668
+ let released = false;
16669
+ return {
16670
+ resized(cols, rows) {
16671
+ if (!released) entry.set = { cols, rows };
16672
+ },
16673
+ release(clean) {
16674
+ if (released) return;
16675
+ released = true;
16676
+ if (--entry.clients > 0) return;
16677
+ if (!entry.original) return drop(pane, entry);
16678
+ schedule(pane, entry, clean ? deps.closeMs : deps.graceMs);
16679
+ }
16680
+ };
16681
+ }
16682
+ function adopt(sizes) {
16683
+ for (const [pane, size] of Object.entries(sizes)) {
16684
+ if (entries.has(pane)) continue;
16685
+ const e = { original: size, ready: Promise.resolve(), clients: 0, set: null, restoring: null };
16686
+ entries.set(pane, e);
16687
+ schedule(pane, e, deps.graceMs);
16688
+ }
16689
+ }
16690
+ function stop() {
16691
+ for (const e of entries.values()) clearTimeout(e.timer);
16692
+ }
16693
+ return { hold, adopt, stop, held: () => [...entries.keys()] };
16694
+ }
16695
+ function parseSttySize(out) {
16696
+ const m = /^\s*(\d{1,4})\s+(\d{1,4})\s*$/.exec(out);
16697
+ if (!m) return null;
16698
+ const rows = Number(m[1]);
16699
+ const cols = Number(m[2]);
16700
+ return rows > 0 && cols > 0 ? { cols, rows } : null;
16701
+ }
16702
+ function cleanSavedSizes(raw, isPane) {
16703
+ const out = {};
16704
+ if (!raw || typeof raw !== "object") return out;
16705
+ for (const [pane, v] of Object.entries(raw)) {
16706
+ const s = v;
16707
+ if (!isPane(pane) || !s || !Number.isInteger(s.cols) || !Number.isInteger(s.rows)) continue;
16708
+ if (s.cols < 10 || s.cols > 400 || s.rows < 5 || s.rows > 200) continue;
16709
+ out[pane] = { cols: s.cols, rows: s.rows, exact: s.exact === true, ...typeof s.terminal === "string" ? { terminal: s.terminal } : {} };
16710
+ }
16711
+ return out;
16712
+ }
16713
+
16339
16714
  const clampInt = (v, lo, hi, dflt) => {
16340
16715
  const n = parseInt(String(v), 10);
16341
16716
  return Number.isFinite(n) ? Math.min(hi, Math.max(lo, n)) : dflt;
16342
16717
  };
16718
+ const TTY_SIZE_SCRIPT = 't=$(ps -o tty= -p "$1" 2>/dev/null | tr -d " "); [ -n "$t" ] && [ -c "/dev/$t" ] && stty size < "/dev/$t"';
16719
+ function ttySize(pane, pid) {
16720
+ const machine = machineOfPane(pane);
16721
+ if (!machine) return Promise.resolve("");
16722
+ if (machine.exec) return machine.exec(TTY_SIZE_SCRIPT, [String(pid)], { timeoutMs: 4e3 }).then((r) => r.code === 0 ? r.stdout.toString("utf8") : "", () => "");
16723
+ if (IN_DOCKER) return Promise.resolve("");
16724
+ return new Promise((resolve) => execFile("sh", ["-c", TTY_SIZE_SCRIPT, "sh", String(pid)], { timeout: 2e3 }, (err, stdout) => resolve(err ? "" : String(stdout))));
16725
+ }
16726
+ async function readPaneSize(pane) {
16727
+ var _a, _b, _c, _d, _e, _f, _g, _h;
16728
+ const p = (_a = await herdr("pane.get", { pane_id: pane }, 3e3).catch(() => null)) == null ? void 0 : _a.pane;
16729
+ if (!p) return null;
16730
+ const terminal = typeof p.terminal_id === "string" ? { terminal: p.terminal_id } : {};
16731
+ const rows = Number((_b = p.scroll) == null ? void 0 : _b.viewport_rows) || 0;
16732
+ const pid = Number((_d = (_c = await herdr("pane.process_info", { pane_id: pane }, 3e3).catch(() => null)) == null ? void 0 : _c.process_info) == null ? void 0 : _d.shell_pid);
16733
+ if (Number.isInteger(pid) && pid > 1) {
16734
+ const tty = parseSttySize(await ttySize(pane, pid));
16735
+ if (tty && (!rows || tty.rows === rows)) return { ...tty, exact: true, ...terminal };
16736
+ }
16737
+ const local = splitId(pane).local;
16738
+ const rect = (_h = (_g = (_f = (_e = await herdr("pane.layout", { pane_id: pane }, 3e3).catch(() => null)) == null ? void 0 : _e.layout) == null ? void 0 : _f.panes) == null ? void 0 : _g.find((x) => x.pane_id === local)) == null ? void 0 : _h.rect;
16739
+ const cols = Number(rect == null ? void 0 : rect.width) || 0;
16740
+ if (!rows || !cols) return null;
16741
+ return { cols, rows, exact: false, ...terminal };
16742
+ }
16743
+ const sizesFile = () => path$1.join(DATA_DIR, "term-sizes.json");
16744
+ const delay = (name, dflt) => {
16745
+ const n = Number(process.env[name]);
16746
+ return Number.isFinite(n) && n >= 0 ? n : dflt;
16747
+ };
16748
+ let sizes = null;
16749
+ const paneSizes = () => sizes || (sizes = createPaneSizes({
16750
+ read: readPaneSize,
16751
+ resize: (pane, cols, rows) => resizePane(pane, cols, rows),
16752
+ // Longer than the 1.5 s left to a closed control session to go away.
16753
+ closeMs: delay("WHERDR_TERM_CLOSE_MS", 2e3),
16754
+ graceMs: delay("WHERDR_TERM_GRACE_MS", 3e4),
16755
+ save(held) {
16756
+ const file = sizesFile();
16757
+ try {
16758
+ if (!Object.keys(held).length) return fs.rmSync(file, { force: true });
16759
+ fs.mkdirSync(DATA_DIR, { recursive: true });
16760
+ fs.writeFileSync(`${file}.tmp`, JSON.stringify(held) + "\n", { mode: 384 });
16761
+ fs.renameSync(`${file}.tmp`, file);
16762
+ } catch (e) {
16763
+ log(`term sizes: ${e.message}`);
16764
+ }
16765
+ },
16766
+ log
16767
+ }));
16768
+ function startPaneSizes() {
16769
+ let saved = null;
16770
+ try {
16771
+ saved = JSON.parse(fs.readFileSync(sizesFile(), "utf8"));
16772
+ } catch {
16773
+ return;
16774
+ }
16775
+ paneSizes().adopt(cleanSavedSizes(saved, (id) => PANE_RE.test(id)));
16776
+ }
16777
+ function stopPaneSizes() {
16778
+ sizes == null ? void 0 : sizes.stop();
16779
+ }
16343
16780
  function openTerm(ws, url) {
16344
16781
  const pane = url.searchParams.get("pane") || "";
16345
16782
  if (!PANE_RE.test(pane)) {
@@ -16356,30 +16793,53 @@ function openTerm(ws, url) {
16356
16793
  const rows = clampInt(url.searchParams.get("rows"), 5, 200, 24);
16357
16794
  const args = ["terminal", "session", "control", splitId(pane).local, "--cols", String(cols), "--rows", String(rows)];
16358
16795
  if (url.searchParams.get("takeover") === "1") args.push("--takeover");
16359
- const child = machine.spawnHerdr(args);
16360
16796
  const sess = { pane, visible: true };
16361
16797
  termSessions.add(sess);
16362
- log(`term ${pane} opened ${cols}x${rows}${args.includes("--takeover") ? " (takeover)" : ""}${machine.local ? "" : ` on ${machine.label}`}`);
16363
- readline.createInterface({ input: child.stdout }).on("line", (line) => {
16364
- if (ws.isOpen()) ws.send(line);
16365
- });
16366
- readline.createInterface({ input: child.stderr }).on("line", (line) => {
16367
- log(`term ${pane} stderr: ${line}`);
16368
- if (ws.isOpen()) ws.send(JSON.stringify({ type: "herdr.stderr", message: line }));
16369
- });
16370
- child.on("error", (e) => {
16371
- if (ws.isOpen()) ws.send(JSON.stringify({ type: "terminal.closed", reason: `herdr: ${e.message}` }));
16372
- ws.close(4500, "herdr error");
16373
- });
16374
- child.on("exit", (code) => {
16375
- termSessions.delete(sess);
16376
- if (ws.isOpen()) ws.close(4e3, `herdr exit ${code}`);
16377
- });
16378
- child.stdin.on("error", () => {
16379
- });
16798
+ let child = null;
16799
+ let hold = null;
16800
+ let closed = false;
16801
+ let size = { cols, rows };
16802
+ let attached = false;
16803
+ const early = [];
16380
16804
  const toChild = (obj) => {
16381
- if (child.stdin.writable) child.stdin.write(JSON.stringify(obj) + "\n");
16805
+ if (!child) {
16806
+ if (early.length < 64) early.push(obj);
16807
+ } else if (child.stdin.writable) {
16808
+ child.stdin.write(JSON.stringify(obj) + "\n");
16809
+ }
16382
16810
  };
16811
+ function start() {
16812
+ const c = machine.spawnHerdr(args);
16813
+ child = c;
16814
+ log(`term ${pane} opened ${cols}x${rows}${args.includes("--takeover") ? " (takeover)" : ""}${machine.local ? "" : ` on ${machine.label}`}`);
16815
+ readline.createInterface({ input: c.stdout }).on("line", (line) => {
16816
+ if (!attached && line.includes('"terminal.frame"')) {
16817
+ attached = true;
16818
+ hold == null ? void 0 : hold.resized(size.cols, size.rows);
16819
+ }
16820
+ if (ws.isOpen()) ws.send(line);
16821
+ });
16822
+ readline.createInterface({ input: c.stderr }).on("line", (line) => {
16823
+ log(`term ${pane} stderr: ${line}`);
16824
+ if (ws.isOpen()) ws.send(JSON.stringify({ type: "herdr.stderr", message: line }));
16825
+ });
16826
+ c.on("error", (e) => {
16827
+ if (ws.isOpen()) ws.send(JSON.stringify({ type: "terminal.closed", reason: `herdr: ${e.message}` }));
16828
+ ws.close(4500, "herdr error");
16829
+ });
16830
+ c.on("exit", (code) => {
16831
+ termSessions.delete(sess);
16832
+ if (ws.isOpen()) ws.close(4e3, `herdr exit ${code}`);
16833
+ });
16834
+ c.stdin.on("error", () => {
16835
+ });
16836
+ for (const m of early.splice(0)) toChild(m);
16837
+ }
16838
+ paneSizes().hold(pane).then((h) => {
16839
+ if (closed) return h.release(true);
16840
+ hold = h;
16841
+ start();
16842
+ });
16383
16843
  return {
16384
16844
  onMessage(raw) {
16385
16845
  let m;
@@ -16393,9 +16853,12 @@ function openTerm(ws, url) {
16393
16853
  if (typeof m.text === "string" && m.text.length <= 65536) toChild({ type: "terminal.input", text: m.text });
16394
16854
  else if (typeof m.bytes === "string" && m.bytes.length <= 9e4) toChild({ type: "terminal.input", bytes: m.bytes });
16395
16855
  break;
16396
- case "terminal.resize":
16397
- toChild({ type: "terminal.resize", cols: clampInt(m.cols, 10, 400, cols), rows: clampInt(m.rows, 5, 200, rows) });
16856
+ case "terminal.resize": {
16857
+ size = { cols: clampInt(m.cols, 10, 400, cols), rows: clampInt(m.rows, 5, 200, rows) };
16858
+ if (attached) hold == null ? void 0 : hold.resized(size.cols, size.rows);
16859
+ toChild({ type: "terminal.resize", ...size });
16398
16860
  break;
16861
+ }
16399
16862
  case "terminal.scroll":
16400
16863
  if (m.direction === "up" || m.direction === "down") {
16401
16864
  toChild({ type: "terminal.scroll", direction: m.direction, lines: clampInt(m.lines, 1, 500, 3) });
@@ -16419,13 +16882,18 @@ function openTerm(ws, url) {
16419
16882
  break;
16420
16883
  }
16421
16884
  },
16422
- onClose() {
16885
+ onClose(clean = false) {
16886
+ closed = true;
16423
16887
  termSessions.delete(sess);
16424
- toChild({ type: "terminal.release" });
16425
- child.stdin.end();
16426
- setTimeout(() => {
16427
- if (child.exitCode === null) child.kill("SIGTERM");
16428
- }, 1500);
16888
+ const c = child;
16889
+ if (c) {
16890
+ toChild({ type: "terminal.release" });
16891
+ c.stdin.end();
16892
+ setTimeout(() => {
16893
+ if (c.exitCode === null) c.kill("SIGTERM");
16894
+ }, 1500);
16895
+ }
16896
+ hold == null ? void 0 : hold.release(clean && sess.visible);
16429
16897
  }
16430
16898
  };
16431
16899
  }
@@ -16560,7 +17028,8 @@ const listKey = (paneId, agent) => {
16560
17028
  var _a;
16561
17029
  return `${((_a = machineOfPane(paneId)) == null ? void 0 : _a.key) || ""}|${agent}`;
16562
17030
  };
16563
- const busy = /* @__PURE__ */ new Set();
17031
+ const busy$1 = /* @__PURE__ */ new Set();
17032
+ const modelControlBusy = (paneId) => busy$1.has(paneId);
16564
17033
  async function withPane(paneId, fn) {
16565
17034
  const p = findPane(paneId);
16566
17035
  if (!p) throw new HerdrError("bad_pane", "Pane not found");
@@ -16568,12 +17037,12 @@ async function withPane(paneId, fn) {
16568
17037
  if (!READY.has(p.status || "")) {
16569
17038
  throw new HerdrError("busy", p.status === "blocked" ? "The agent is waiting for an answer \u2014 reply first." : "The agent is working \u2014 change the model once it\u2019s done.");
16570
17039
  }
16571
- if (busy.has(paneId)) throw new HerdrError("busy", "Model change already in progress");
16572
- busy.add(paneId);
17040
+ if (busy$1.has(paneId)) throw new HerdrError("busy", "Model change already in progress");
17041
+ busy$1.add(paneId);
16573
17042
  try {
16574
17043
  return await fn(p);
16575
17044
  } finally {
16576
- busy.delete(paneId);
17045
+ busy$1.delete(paneId);
16577
17046
  }
16578
17047
  }
16579
17048
  const screen = async (paneId) => {
@@ -16586,14 +17055,14 @@ async function waitMenu(paneId, ok, timeoutMs = 6e3) {
16586
17055
  const m = parseModelMenu(await screen(paneId));
16587
17056
  if (m && ok(m)) return m;
16588
17057
  if (Date.now() > until) return null;
16589
- await sleep(250);
17058
+ await sleep$1(250);
16590
17059
  }
16591
17060
  }
16592
17061
  async function closeMenu(paneId) {
16593
17062
  for (let i = 0; i < 3; i++) {
16594
17063
  if (!parseModelMenu(await screen(paneId))) return;
16595
17064
  await herdr("pane.send_input", { pane_id: paneId, keys: ["esc"] });
16596
- await sleep(400);
17065
+ await sleep$1(400);
16597
17066
  }
16598
17067
  }
16599
17068
  async function openMenu(p) {
@@ -16621,7 +17090,7 @@ async function readAllOptions(paneId, first) {
16621
17090
  for (let i = 0; i < 8 && m.scrollDown; i++) {
16622
17091
  const last = m.options[m.options.length - 1].n;
16623
17092
  await herdr("pane.send_input", { pane_id: paneId, keys: moveKeys(m.cursor, last + 1) });
16624
- await sleep(350);
17093
+ await sleep$1(350);
16625
17094
  const next = await waitMenu(paneId, (x) => x.kind === "model", 2e3);
16626
17095
  if (!next || next.cursor <= last) break;
16627
17096
  const before = all.size;
@@ -16703,7 +17172,7 @@ async function setModel(paneId, wanted) {
16703
17172
  const confirm = p.agent === "claude" ? switchConfirmKeys(text, confirmAs) : null;
16704
17173
  if (confirm) await herdr("pane.send_input", { pane_id: p.id, keys: confirm });
16705
17174
  clear = confirm || parseModelMenu(text) ? 0 : clear + 1;
16706
- await sleep(confirm ? 400 : 250);
17175
+ await sleep$1(confirm ? 400 : 250);
16707
17176
  }
16708
17177
  if (clear < 2) throw new HerdrError("stale", "The /model menu changed \u2014 try again.");
16709
17178
  const info = { id: null, label: confirmAs, effort, at: new Date(started).toISOString() };
@@ -16726,14 +17195,14 @@ async function waitOmpSelector(paneId, ok, timeoutMs = 6e3) {
16726
17195
  const s = await ompSelector(paneId).catch(() => null);
16727
17196
  if (s && ok(s)) return s;
16728
17197
  if (Date.now() > until) return null;
16729
- await sleep(250);
17198
+ await sleep$1(250);
16730
17199
  }
16731
17200
  }
16732
17201
  async function closeOmpSelector(paneId) {
16733
17202
  for (let i = 0; i < 3; i++) {
16734
17203
  if (!await ompSelector(paneId).catch(() => null)) return;
16735
17204
  await herdr("pane.send_input", { pane_id: paneId, keys: ["esc"] });
16736
- await sleep(400);
17205
+ await sleep$1(400);
16737
17206
  }
16738
17207
  }
16739
17208
  const OMP_SELECTOR_COLS = 160;
@@ -16773,7 +17242,7 @@ async function waitOmpModel(p, id, since) {
16773
17242
  const info = await transcripts.model(p).catch(() => null);
16774
17243
  if (info && info.id === id && info.at && Date.parse(info.at) >= since - 1500) return true;
16775
17244
  if (Date.now() > until) return false;
16776
- await sleep(300);
17245
+ await sleep$1(300);
16777
17246
  }
16778
17247
  }
16779
17248
  async function ompSetModel(paneId, wanted) {
@@ -16797,7 +17266,7 @@ async function ompSetModel(paneId, wanted) {
16797
17266
  }
16798
17267
  for (const ch of typed) {
16799
17268
  await herdr("pane.send_input", { pane_id: p.id, keys: [ch === " " ? "space" : ch] });
16800
- await sleep(80);
17269
+ await sleep$1(80);
16801
17270
  }
16802
17271
  s = await waitOmpSelector(p.id, (x) => x.search.length > 0, 3e3) || s;
16803
17272
  at = rank(s);
@@ -16808,7 +17277,7 @@ async function ompSetModel(paneId, wanted) {
16808
17277
  const delta = at - s.cursor;
16809
17278
  if (delta === 0) break;
16810
17279
  await herdr("pane.send_input", { pane_id: p.id, keys: [delta > 0 ? "down" : "up"] });
16811
- await sleep(120);
17280
+ await sleep$1(120);
16812
17281
  if (n === 79) throw new HerdrError("stale", "The model selector did not follow \u2014 nothing was changed.");
16813
17282
  }
16814
17283
  s = (_b = (_a = await waitOmpSelector(p.id, (x) => x.cursor === at && x.options[at].label === id, 3e3)) != null ? _a : await ompSelector(p.id)) != null ? _b : s;
@@ -16867,7 +17336,7 @@ async function waitSlider(paneId, ok, timeoutMs) {
16867
17336
  const s = parseClaudeEffortScreen(await screen(paneId).catch(() => ""));
16868
17337
  if (s && ok(s)) return s;
16869
17338
  if (Date.now() > until) return null;
16870
- await sleep(250);
17339
+ await sleep$1(250);
16871
17340
  }
16872
17341
  }
16873
17342
  async function sliderGone(paneId, timeoutMs = 3e3) {
@@ -16875,7 +17344,7 @@ async function sliderGone(paneId, timeoutMs = 3e3) {
16875
17344
  let clear = 0;
16876
17345
  while (clear < 2 && Date.now() < until) {
16877
17346
  clear = parseClaudeEffortScreen(await screen(paneId).catch(() => "")) ? 0 : clear + 1;
16878
- if (clear < 2) await sleep(250);
17347
+ if (clear < 2) await sleep$1(250);
16879
17348
  }
16880
17349
  return clear >= 2;
16881
17350
  }
@@ -16884,7 +17353,7 @@ async function closeEffortSlider(paneId) {
16884
17353
  if (!parseClaudeEffortScreen(await screen(paneId).catch(() => ""))) return;
16885
17354
  await herdr("pane.send_input", { pane_id: paneId, keys: ["esc"] }).catch(() => {
16886
17355
  });
16887
- await sleep(400);
17356
+ await sleep$1(400);
16888
17357
  }
16889
17358
  }
16890
17359
  async function openEffortSlider(p) {
@@ -16918,7 +17387,7 @@ async function waitOmpEffort(p, model, prev, shown, sinceAt, hasTranscript) {
16918
17387
  if (now && prev === "auto" && shown !== "auto" && Date.now() - pressed > OMP_REDRAW_MS) return { level: now, at: sinceAt, shown: now };
16919
17388
  }
16920
17389
  if (Date.now() - pressed > 3e3) return null;
16921
- await sleep(200);
17390
+ await sleep$1(200);
16922
17391
  }
16923
17392
  }
16924
17393
  async function ompCycleTo(p, before, level) {
@@ -17031,7 +17500,7 @@ async function setEffort(paneId, level) {
17031
17500
  let clear = 0;
17032
17501
  while (clear < 2 && Date.now() < until) {
17033
17502
  clear = parseModelMenu(await screen(p.id)) ? 0 : clear + 1;
17034
- await sleep(250);
17503
+ await sleep$1(250);
17035
17504
  }
17036
17505
  if (clear < 2) throw new HerdrError("stale", "The /model menu changed \u2014 try again.");
17037
17506
  } catch (e) {
@@ -17064,16 +17533,26 @@ function compareVersions(a, b) {
17064
17533
  for (let i = 0; i < 3; i++) if (x[i] !== y[i]) return x[i] - y[i];
17065
17534
  return 0;
17066
17535
  }
17536
+ const MODES = ["docker", "docker-build", "npm", "npm-global", "brew", "plugin"];
17067
17537
  function installMode(env) {
17068
- return env === "docker" || env === "docker-build" || env === "npm" || env === "brew" ? env : "native";
17538
+ var _a;
17539
+ return (_a = MODES.find((m) => m === env)) != null ? _a : "native";
17069
17540
  }
17070
- function updateCommand(mode) {
17541
+ function updateCommand(mode, repo) {
17071
17542
  if (mode === "docker") return "docker compose pull && docker compose up -d";
17072
17543
  if (mode === "docker-build") return "git pull && docker compose -f docker-compose.yml -f docker-compose.build.yml up -d --build";
17073
17544
  if (mode === "npm") return "npx wherdr@latest";
17074
- if (mode === "brew") return "brew upgrade wherdr";
17545
+ if (mode === "npm-global") return "npm install -g wherdr@latest && wherdr restart";
17546
+ if (mode === "brew") return "brew upgrade wherdr && brew services restart wherdr";
17547
+ if (mode === "plugin") return `herdr plugin install ${repo} --yes`;
17075
17548
  return "git pull && npm ci && npm run build";
17076
17549
  }
17550
+ function oneTapMode(mode) {
17551
+ return mode === "plugin" || mode === "npm-global" || mode === "brew";
17552
+ }
17553
+ function updateRunning(job, now) {
17554
+ return !!job && !["done", "rolled-back", "failed"].includes(job.state) && now - job.updatedAt < 10 * 6e4;
17555
+ }
17077
17556
 
17078
17557
  const CODEX_RELEASE_NOTES = "https://github.com/openai/codex/releases/latest";
17079
17558
  const CODEX_STANDALONE_COMMAND = `sh -c 'curl -fsSL https://chatgpt.com/codex/install.sh | CODEX_NON_INTERACTIVE=1 sh'`;
@@ -17860,6 +18339,494 @@ async function installClaudeStatusline(m) {
17860
18339
  return last(r.stdout.toString("utf8"));
17861
18340
  }
17862
18341
 
18342
+ const CLAUDE = {
18343
+ keep: {
18344
+ "--model": 1,
18345
+ "--effort": 1,
18346
+ "--permission-mode": 1,
18347
+ "--fallback-model": 1,
18348
+ "--agent": 1,
18349
+ "--agents": 1,
18350
+ "--settings": 1,
18351
+ "--setting-sources": 1,
18352
+ "--system-prompt": 1,
18353
+ "--append-system-prompt": 1,
18354
+ "--autocompact": 1,
18355
+ "-n": 1,
18356
+ "--name": 1,
18357
+ "--debug-file": 1,
18358
+ "--add-dir": "*",
18359
+ "--mcp-config": "*",
18360
+ "--plugin-dir": "*",
18361
+ "--allowedTools": "*",
18362
+ "--allowed-tools": "*",
18363
+ "--disallowedTools": "*",
18364
+ "--disallowed-tools": "*",
18365
+ "--tools": "*",
18366
+ "--betas": "*",
18367
+ "--dangerously-skip-permissions": 0,
18368
+ "--allow-dangerously-skip-permissions": 0,
18369
+ "--verbose": 0,
18370
+ "--ide": 0,
18371
+ "--chrome": 0,
18372
+ "--no-chrome": 0,
18373
+ "--strict-mcp-config": 0,
18374
+ "--bare": 0,
18375
+ "--brief": 0,
18376
+ "--disable-slash-commands": 0,
18377
+ "--safe-mode": 0,
18378
+ "--restricted": 0
18379
+ },
18380
+ // Conversation choice, one-off launch or already done (worktree created).
18381
+ drop: {
18382
+ "-c": 0,
18383
+ "--continue": 0,
18384
+ "-r": "?",
18385
+ "--resume": "?",
18386
+ "--session-id": 1,
18387
+ "--fork-session": 0,
18388
+ "-w": "?",
18389
+ "--worktree": "?",
18390
+ "--tmux": 0,
18391
+ "--from-pr": "?",
18392
+ "--teleport": "?",
18393
+ "-p": 0,
18394
+ "--print": 0,
18395
+ "--bg": 0,
18396
+ "--background": 0,
18397
+ "--remote-control": "?",
18398
+ "--cloud": "?",
18399
+ "--file": "*",
18400
+ "-d": "?",
18401
+ "--debug": "?"
18402
+ }
18403
+ };
18404
+ const CODEX = {
18405
+ keep: {
18406
+ "-c": 1,
18407
+ "--config": 1,
18408
+ "--enable": 1,
18409
+ "--disable": 1,
18410
+ "-m": 1,
18411
+ "--model": 1,
18412
+ "--local-provider": 1,
18413
+ "-p": 1,
18414
+ "--profile": 1,
18415
+ "-s": 1,
18416
+ "--sandbox": 1,
18417
+ "-C": 1,
18418
+ "--cd": 1,
18419
+ "--add-dir": 1,
18420
+ "-a": 1,
18421
+ "--ask-for-approval": 1,
18422
+ "--remote": 1,
18423
+ "--remote-auth-token-env": 1,
18424
+ "--oss": 0,
18425
+ "--search": 0,
18426
+ "--no-alt-screen": 0,
18427
+ "--no-daemon": 0,
18428
+ "--full-auto": 0,
18429
+ "--dangerously-bypass-approvals-and-sandbox": 0,
18430
+ "--yolo": 0,
18431
+ "--approve-for-me": 0,
18432
+ "--strict-config": 0,
18433
+ "--dangerously-bypass-hook-trust": 0
18434
+ },
18435
+ drop: { "--last": 0, "--all": 0, "--include-non-interactive": 0, "-i": "*", "--image": "*", "--worktree": 0 }
18436
+ };
18437
+ const OMP = {
18438
+ keep: {
18439
+ "--model": 1,
18440
+ "--smol": 1,
18441
+ "--slow": 1,
18442
+ "--plan": 1,
18443
+ "--prewalk-into": 1,
18444
+ "--plan-yolo-into": 1,
18445
+ "--provider": 1,
18446
+ "--system-prompt": 1,
18447
+ "--system-prompt-template": 1,
18448
+ "--append-system-prompt": 1,
18449
+ "--profile": 1,
18450
+ "--cwd": 1,
18451
+ "--config": 1,
18452
+ "--add-dir": 1,
18453
+ "--session-dir": 1,
18454
+ "--models": 1,
18455
+ "--tools": 1,
18456
+ "--thinking": 1,
18457
+ "--service-tier": 1,
18458
+ "--hook": 1,
18459
+ "-e": 1,
18460
+ "--extension": 1,
18461
+ "--skills": 1,
18462
+ "--approval-mode": 1,
18463
+ "--plugin-dir": 1,
18464
+ "--allow-home": 0,
18465
+ "--prewalk": 0,
18466
+ "--no-prewalk": 0,
18467
+ "--no-tools": 0,
18468
+ "--no-lsp": 0,
18469
+ "--no-pty": 0,
18470
+ "--hide-thinking": 0,
18471
+ "--advisor": 0,
18472
+ "--external-thinking": 0,
18473
+ "--no-extensions": 0,
18474
+ "--no-skills": 0,
18475
+ "--no-rules": 0,
18476
+ "--no-title": 0,
18477
+ "--auto-approve": 0
18478
+ },
18479
+ // Conversation choice, one-off launch; the API key is never replayed
18480
+ // (it would show in the confirmation).
18481
+ drop: {
18482
+ "-c": 0,
18483
+ "--continue": 0,
18484
+ "-r": "?",
18485
+ "--resume": "?",
18486
+ "-p": 0,
18487
+ "--print": 0,
18488
+ "--plan-yolo": 0,
18489
+ "--from-claude": 0,
18490
+ "--from-codex": 0,
18491
+ "--no-session": 0,
18492
+ "--export": 1,
18493
+ "--mode": 1,
18494
+ "--max-time": 1,
18495
+ "--alias": 1,
18496
+ "--api-key": 1,
18497
+ "--no-ui": 0,
18498
+ "--print-thoughts": 0
18499
+ }
18500
+ };
18501
+ const SILENT = /* @__PURE__ */ new Set(["-c", "--continue", "-r", "--resume", "--session-id", "--fork-session", "--last", "--all"]);
18502
+ const SPECS = { claude: CLAUDE, codex: CODEX, omp: OMP };
18503
+ const RESTARTABLE = new Set(Object.keys(SPECS));
18504
+ const base$1 = (s) => s.replace(/^.*\//, "").replace(/\.(c?js|mjs)$/, "");
18505
+ function launchOptions(kind, argv) {
18506
+ const spec = SPECS[kind];
18507
+ const kept = [];
18508
+ const dropped = [];
18509
+ if (!spec) return { kept, dropped };
18510
+ let i = argv.findIndex((a) => base$1(a) === kind);
18511
+ i = i < 0 ? 1 : i + 1;
18512
+ for (; i < argv.length; i++) {
18513
+ const a = argv[i];
18514
+ if (a === "--") break;
18515
+ if (!a.startsWith("-") || a === "-") continue;
18516
+ const eq = a.indexOf("=");
18517
+ const flag = eq > 0 ? a.slice(0, eq) : a;
18518
+ const keep = flag in spec.keep;
18519
+ const arity = keep ? spec.keep[flag] : spec.drop[flag];
18520
+ const out = [a];
18521
+ if (keep) kept.push(out);
18522
+ else if (!SILENT.has(flag)) dropped.push(out);
18523
+ if (arity === void 0) {
18524
+ if (eq < 0 && argv[i + 1] !== void 0 && !argv[i + 1].startsWith("-")) out.push(argv[++i]);
18525
+ continue;
18526
+ }
18527
+ if (eq > 0 || arity === 0) continue;
18528
+ if (arity === 1) {
18529
+ if (argv[i + 1] !== void 0) out.push(argv[++i]);
18530
+ continue;
18531
+ }
18532
+ while (argv[i + 1] !== void 0 && !argv[i + 1].startsWith("-")) {
18533
+ out.push(argv[++i]);
18534
+ if (arity === "?") break;
18535
+ }
18536
+ }
18537
+ return { kept, dropped };
18538
+ }
18539
+ const EFFORTS = /* @__PURE__ */ new Set(["low", "medium", "high", "xhigh", "max"]);
18540
+ function planRestart(o) {
18541
+ const opts = o.argv ? launchOptions(o.kind, o.argv) : { kept: [], dropped: [] };
18542
+ const mode = o.session ? "resume" : o.hadSession ? "fresh" : "continue";
18543
+ let groups = opts.kept;
18544
+ const without = (...flags) => {
18545
+ groups = groups.filter((g) => !flags.includes(g[0].split("=")[0]));
18546
+ };
18547
+ if (o.kind === "omp" && mode === "resume") without("--model", "--thinking");
18548
+ if (o.kind === "claude") {
18549
+ const cur = o.current || {};
18550
+ if (mode === "resume") without("--model");
18551
+ const effort = String(cur.effort || "").toLowerCase();
18552
+ if (EFFORTS.has(effort)) {
18553
+ without("--effort");
18554
+ groups.push(["--effort", effort]);
18555
+ }
18556
+ if (cur.permissionMode) {
18557
+ without("--permission-mode");
18558
+ if (cur.permissionMode !== "default") groups.push(["--permission-mode", cur.permissionMode]);
18559
+ }
18560
+ }
18561
+ const kept = groups.flat();
18562
+ const dropped = opts.dropped.flat();
18563
+ let args;
18564
+ if (o.kind === "codex") {
18565
+ args = mode === "fresh" ? kept : ["resume", ...kept, mode === "resume" ? o.session : "--last"];
18566
+ } else {
18567
+ args = mode === "resume" ? [...kept, "--resume", o.session] : mode === "continue" ? [...kept, "--continue"] : kept;
18568
+ }
18569
+ return { args, mode, kept, dropped, unknownArgs: !o.argv };
18570
+ }
18571
+ const FOOTER_MODES = [
18572
+ [/\bplan mode on\b/i, "plan"],
18573
+ [/\baccept edits on\b/i, "acceptEdits"],
18574
+ [/\bauto mode on\b/i, "auto"],
18575
+ [/\bbypass permissions on\b/i, "bypassPermissions"],
18576
+ [/\bdon'?t ask (?:mode )?on\b/i, "dontAsk"]
18577
+ ];
18578
+ function claudeFooterMode(text) {
18579
+ const lines = String(text || "").split("\n");
18580
+ let rule = -1;
18581
+ for (let i = lines.length - 1; i >= 0 && rule < 0; i--) if (/^\s*─{20,}\s*$/.test(lines[i])) rule = i;
18582
+ if (rule < 1 || !lines.slice(Math.max(0, rule - 6), rule).some((l) => /^\s*❯/.test(l))) return null;
18583
+ for (const l of lines.slice(rule + 1)) {
18584
+ for (const [re, mode] of FOOTER_MODES) if (re.test(l)) return mode;
18585
+ }
18586
+ return "default";
18587
+ }
18588
+
18589
+ function updateShellSignature(text) {
18590
+ const lines = text.replace(/\r/g, "").trimEnd().split("\n");
18591
+ const end = lines.findLastIndex((l) => /^\s*(?:🎉\s*)?Update ran successfully! Please restart Codex\.\s*$/.test(l));
18592
+ if (end < 0 || !lines.slice(Math.max(0, end - 4), end).some((l) => /^\s*Codex CLI \S+ installed successfully\.\s*$/.test(l))) return null;
18593
+ const tail = lines.slice(end + 1).filter((l) => l.trim());
18594
+ if (!tail.length || tail.length > 2) return null;
18595
+ const last = tail.at(-1);
18596
+ if (!/^\s*(?:[\w@./~:-]+(?: [\w@./~:-]+){0,2} )?[$%#❯]\s*$/.test(last)) return null;
18597
+ if (tail.length === 2 && (!/^\s*[\w./~:-]+ on [\w./:-]+\s*$/.test(tail[0]) || !/^\s*❯\s*$/.test(last))) return null;
18598
+ return tail.join("\n");
18599
+ }
18600
+ function updateChoices(launch) {
18601
+ return launch.conversation === "empty" && launch.argv && !launch.argv.some((a) => a === "resume" || a === "fork") ? ["restart"] : ["resume", "fresh"];
18602
+ }
18603
+ const UPDATE_LABELS = {
18604
+ restart: "Restart Codex",
18605
+ resume: "Resume conversation",
18606
+ fresh: "Start fresh"
18607
+ };
18608
+ function updatePrompt(launch) {
18609
+ return {
18610
+ kind: "self-update",
18611
+ question: "Codex updated itself and stopped",
18612
+ cursor: -1,
18613
+ options: updateChoices(launch).map((c) => ({ label: UPDATE_LABELS[c], hint: c === "resume" && !launch.session ? "Choose a conversation in Codex" : null }))
18614
+ };
18615
+ }
18616
+ function updateRestartPlan(launch, choice) {
18617
+ const kept = launch.argv ? launchOptions("codex", launch.argv).kept.flat() : [];
18618
+ let args = choice === "resume" ? ["resume", ...kept, ...launch.session ? [launch.session] : []] : kept;
18619
+ if (choice === "restart" && launch.argv) {
18620
+ const i = launch.argv.findIndex((a) => /(?:^|\/)codex(?:\.[cm]?js)?$/.test(a));
18621
+ if (i >= 0) args = launch.argv.slice(i + 1);
18622
+ }
18623
+ return { args, mode: choice === "resume" ? "resume" : "fresh", kept, dropped: [], unknownArgs: !launch.argv };
18624
+ }
18625
+
18626
+ const base = (s) => s.replace(/^.*\//, "").replace(/\.(c?js|mjs)$/, "");
18627
+ async function paneForeground(d, paneId, kind) {
18628
+ const r = await d.call("pane.process_info", { pane_id: paneId }, 4e3);
18629
+ const info = r && r.process_info || {};
18630
+ const procs = info.foreground_processes || [];
18631
+ const agent = procs.find((p) => p.name === kind || (p.argv || []).some((a) => base(a) === kind)) || null;
18632
+ const atShell = !agent && Boolean(info.shell_pid) && info.foreground_process_group_id === info.shell_pid;
18633
+ const sh = procs.find((p) => info.shell_pid && p.pid === info.shell_pid);
18634
+ const shell = { pid: Number(info.shell_pid) || null, name: sh ? String(sh.name || (sh.argv || [])[0] || "") || null : null };
18635
+ return { agent, atShell, shell, argv: agent && Array.isArray(agent.argv) ? agent.argv.map(String) : null };
18636
+ }
18637
+ async function waitFor(d, ms, test) {
18638
+ const end = d.now() + ms;
18639
+ while (d.now() < end) {
18640
+ await d.sleep(500);
18641
+ try {
18642
+ if (await test()) return true;
18643
+ } catch {
18644
+ }
18645
+ }
18646
+ return false;
18647
+ }
18648
+ const waitShell = (d, paneId, kind, ms) => waitFor(d, ms, async () => (await paneForeground(d, paneId, kind)).atShell);
18649
+ const busy = async (d, paneId) => {
18650
+ const r = await d.call("pane.get", { pane_id: paneId }, 4e3);
18651
+ const s = r && r.pane && r.pane.agent_status;
18652
+ return s === "working" || s === "blocked";
18653
+ };
18654
+ async function stopAgent(d, p) {
18655
+ const kind = p.agent;
18656
+ const key = (k) => d.call("pane.send_input", { pane_id: p.id, keys: [k] });
18657
+ if (p.status === "working" || p.status === "blocked") {
18658
+ for (let i = 0; i < 2; i++) {
18659
+ await key("esc");
18660
+ if (await waitFor(d, 3e3, async () => !await busy(d, p.id))) break;
18661
+ }
18662
+ }
18663
+ await d.call("pane.send_input", { pane_id: p.id, text: "/exit" });
18664
+ await d.sleep(300);
18665
+ await key("enter");
18666
+ if (await waitShell(d, p.id, kind, 8e3)) return "exit";
18667
+ await key("ctrl+c");
18668
+ await d.sleep(400);
18669
+ await key("ctrl+c");
18670
+ if (await waitShell(d, p.id, kind, 6e3)) return "ctrl-c";
18671
+ throw new HerdrError("restart_stop", "the agent did not stop");
18672
+ }
18673
+ async function startAgent(d, p, plan) {
18674
+ const name = p.name || `${p.agent}-${crypto.randomBytes(2).toString("hex")}`;
18675
+ let last = null;
18676
+ for (let i = 0; i < 6; i++) {
18677
+ try {
18678
+ await d.call("agent.start", { name, kind: p.agent, pane_id: p.id, timeout_ms: 6e4, ...plan.args.length ? { args: plan.args } : {} }, 75e3);
18679
+ return;
18680
+ } catch (e) {
18681
+ last = e;
18682
+ if (last.code === "agent_not_ready") return;
18683
+ if (last.code === "timeout" || last.code === "unreachable") break;
18684
+ await d.sleep(500);
18685
+ }
18686
+ }
18687
+ throw last || new HerdrError("restart_start", "the agent did not restart");
18688
+ }
18689
+
18690
+ const AT_SAVE_MS = 60 * 1e3;
18691
+ const OUTAGE_MS = 15e3;
18692
+ function createSelfUpdates(d, saved = []) {
18693
+ const records = new Map(saved);
18694
+ const restored = new Set(records.keys());
18695
+ const stable = () => JSON.stringify([...records].map(([id, r]) => [id, { ...r, at: 0 }]));
18696
+ const times = () => [...records.values()].map((r) => r.at).join();
18697
+ let written = stable();
18698
+ let writtenTimes = times();
18699
+ let writtenAt = d.now();
18700
+ const persist = () => {
18701
+ var _a;
18702
+ const s = stable();
18703
+ if (s === written && (d.now() - writtenAt < AT_SAVE_MS || times() === writtenTimes)) return;
18704
+ written = s;
18705
+ writtenTimes = times();
18706
+ writtenAt = d.now();
18707
+ (_a = d.save) == null ? void 0 : _a.call(d, [...records]);
18708
+ };
18709
+ const read = async (id) => {
18710
+ var _a, _b;
18711
+ return String(((_b = (_a = await d.call("pane.read", { pane_id: id, source: "recent_unwrapped", lines: 80 }, 4e3)) == null ? void 0 : _a.read) == null ? void 0 : _b.text) || "");
18712
+ };
18713
+ return {
18714
+ get: (id) => records.get(id),
18715
+ clear(id) {
18716
+ records.delete(id);
18717
+ persist();
18718
+ },
18719
+ // Service stopping: keep the exact time Codex was last seen alive, so a
18720
+ // quick restart still tells a short outage from a long one.
18721
+ flush() {
18722
+ var _a;
18723
+ if (records.size) (_a = d.save) == null ? void 0 : _a.call(d, [...records], true);
18724
+ },
18725
+ prune(ids, belongs) {
18726
+ let changed = false;
18727
+ for (const id of records.keys()) if (belongs(id) && !ids.has(id)) {
18728
+ records.delete(id);
18729
+ changed = true;
18730
+ }
18731
+ if (changed) persist();
18732
+ },
18733
+ async observe(p, owned) {
18734
+ var _a, _b, _c, _d;
18735
+ if (owned) return false;
18736
+ let last = records.get(p.id);
18737
+ if (p.agent === "codex") {
18738
+ const fg = await paneForeground(d, p.id, "codex");
18739
+ if (!fg.agent) return false;
18740
+ restored.delete(p.id);
18741
+ const pid = Number(fg.agent.pid);
18742
+ if (last && last.pid === pid && !last.gone && d.now() - last.at < 5e3 && p.agentSession === last.pane.agentSession && p.cwd === last.pane.cwd && p.bornAt === last.pane.bornAt) {
18743
+ last.argv = fg.argv || last.argv;
18744
+ persist();
18745
+ return false;
18746
+ }
18747
+ if (!last || last.pid !== pid || last.gone) {
18748
+ last = { pid, pane: { ...p }, at: d.now(), argv: fg.argv, session: null, conversation: "unknown" };
18749
+ records.set(p.id, last);
18750
+ }
18751
+ last.at = d.now();
18752
+ last.argv = fg.argv || last.argv;
18753
+ last.pane = { id: p.id, workspace: p.workspace, agent: p.agent, agentSession: p.agentSession, bornAt: p.bornAt, cwd: p.cwd, name: p.name, title: p.title };
18754
+ const screen = await read(p.id);
18755
+ const chat = await d.chat(p).catch(() => null);
18756
+ if (chat && (chat.guessed || chat.session && chat.session !== last.session)) {
18757
+ if (chat.session !== last.session) last.conversation = "unknown";
18758
+ last.session = null;
18759
+ }
18760
+ if ((chat == null ? void 0 : chat.available) && !chat.guessed && chat.session) {
18761
+ if (last.session && last.session !== chat.session) last.conversation = "unknown";
18762
+ last.session = chat.session;
18763
+ }
18764
+ if ((_a = chat == null ? void 0 : chat.items) == null ? void 0 : _a.some((i) => i.role === "user" || i.role === "assistant")) {
18765
+ last.conversation = "existing";
18766
+ if (!chat.guessed && chat.session) last.session = chat.session;
18767
+ } else if (chat && last.conversation !== "existing") {
18768
+ const startup = /(?:✨\s*)?Update available!|^Updating Codex via /m.test(screen);
18769
+ const witnessedBirth = Boolean(p.bornAt) && !((_b = fg.argv) == null ? void 0 : _b.some((a) => a === "resume" || a === "fork"));
18770
+ if (chat.available && !chat.guessed && !chat.start && !((_c = chat.items) == null ? void 0 : _c.length) || !p.agentSession && !chat.available && (startup || witnessedBirth)) last.conversation = "empty";
18771
+ }
18772
+ last.oldOutput = Boolean(updateShellSignature(screen));
18773
+ persist();
18774
+ return false;
18775
+ }
18776
+ if (!last) return false;
18777
+ if (p.agent) {
18778
+ records.delete(p.id);
18779
+ persist();
18780
+ return false;
18781
+ }
18782
+ if (!last.gone) {
18783
+ if (d.now() - last.at > OUTAGE_MS + (restored.has(p.id) ? AT_SAVE_MS : 0)) last.checked = true;
18784
+ last.gone = d.now();
18785
+ persist();
18786
+ }
18787
+ if (!last.checked) {
18788
+ if (d.now() - last.gone < 3e3) {
18789
+ p.leaving = true;
18790
+ return false;
18791
+ }
18792
+ last.checked = true;
18793
+ if (d.now() - last.gone < 15e3 && !last.oldOutput && (await paneForeground(d, p.id, "codex")).atShell) {
18794
+ last.signature = updateShellSignature(await read(p.id)) || void 0;
18795
+ if (last.signature) {
18796
+ const chat = await d.chat(last.pane).catch(() => null);
18797
+ if (!chat || chat.guessed || !chat.available && last.session) {
18798
+ if (last.conversation === "empty") last.conversation = "unknown";
18799
+ }
18800
+ if (chat && (chat.guessed || chat.session && chat.session !== last.session)) last.session = null;
18801
+ if ((chat == null ? void 0 : chat.available) && !chat.guessed && chat.session) last.session = chat.session;
18802
+ if ((_d = chat == null ? void 0 : chat.items) == null ? void 0 : _d.some((i) => i.role === "user" || i.role === "assistant")) {
18803
+ last.conversation = "existing";
18804
+ if (!chat.guessed && chat.session) last.session = chat.session;
18805
+ else if (chat.session !== last.session) last.session = null;
18806
+ }
18807
+ }
18808
+ }
18809
+ persist();
18810
+ }
18811
+ if (!last.signature) return false;
18812
+ p.agent = "codex";
18813
+ p.name = last.pane.name;
18814
+ p.title = last.pane.title;
18815
+ p.agentSession = last.session;
18816
+ p.bornAt = last.pane.bornAt;
18817
+ p.status = "blocked";
18818
+ p.stopped = { reason: "update" };
18819
+ p.prompt = updatePrompt(last);
18820
+ return true;
18821
+ },
18822
+ async check(id) {
18823
+ const last = records.get(id);
18824
+ if (!(last == null ? void 0 : last.signature) || !(await paneForeground(d, id, "codex")).atShell) return null;
18825
+ return updateShellSignature(await read(id)) === last.signature ? last : null;
18826
+ }
18827
+ };
18828
+ }
18829
+
17863
18830
  var _a, _b;
17864
18831
  const fsp$1 = fs.promises;
17865
18832
  function trFor(p) {
@@ -17909,17 +18876,22 @@ function watchScreen(paneId, ms = 6e4) {
17909
18876
  const choicesCache = /* @__PURE__ */ new Map();
17910
18877
  const askChecks = /* @__PURE__ */ new Map();
17911
18878
  async function choicesFor(p, rev, strict, watch = false) {
18879
+ if (modelControlBusy(p.id)) {
18880
+ choicesCache.delete(p.id);
18881
+ return { choices: null, screen: null, menu: null, interaction: "managed" };
18882
+ }
17912
18883
  const c = choicesCache.get(p.id);
17913
18884
  const watched = (screenWatch.get(p.id) || 0) > Date.now();
17914
18885
  if (!watched) screenWatch.delete(p.id);
17915
- if (c && keepReading(c, rev, strict, watch || watched, Date.now())) return c;
18886
+ if (c && !(p.status === "unknown" && c.interaction === "terminal") && keepReading(c, rev, strict, watch || watched, Date.now())) return c;
17916
18887
  let out;
17917
18888
  try {
17918
18889
  const r = await herdr("pane.read", { pane_id: p.id, source: "detection" }, 4e3);
17919
18890
  const text = r.read && r.read.text;
17920
18891
  const framed = String(text || "").split("\n").some((l) => TOP.test(l));
17921
- let choices = p.agent === "omp" ? parseOmpPrompt(text) : parseChoices(text, { strict });
17922
- const menu = p.agent !== "omp" && framed && (strict || !choices || !parseChoices(text, { strict: true })) ? await readMenu(p.id) : null;
18892
+ const usageLimit = p.status === "unknown" ? null : parseUsageLimit(String(text || ""), p.agent);
18893
+ let choices = usageLimit ? null : p.agent === "omp" ? parseOmpPrompt(text) : parseChoices(text, { strict });
18894
+ const menu = !usageLimit && p.agent !== "omp" && framed && (strict || !choices || !parseChoices(text, { strict: true })) ? await readMenu(p.id) : null;
17923
18895
  if (menu) choices = null;
17924
18896
  if (choices && p.agent === "omp" && !choices.detail) choices = await withOmpTab(p.id, completeOmpAsk(choices, await transcripts.pendingAsk(p).catch(() => [])));
17925
18897
  if (choices && p.agent === "claude") {
@@ -17928,7 +18900,9 @@ async function choicesFor(p, rev, strict, watch = false) {
17928
18900
  choices = completeClaudeAsk(choices, await transcripts.pendingClaudeQuestions(p).catch(() => []), checks);
17929
18901
  } else if (!choices) askChecks.delete(p.id);
17930
18902
  const settings = p.agent === "claude" && settingsTabs(text) !== null;
17931
- out = { choices, screen: menu || settings ? null : parseWaitScreen(text, { choices: Boolean(choices) }), menu };
18903
+ const screen = menu || settings ? null : parseWaitScreen(text, { choices: Boolean(choices) });
18904
+ const interaction = classifyScreen({ text: String(text || ""), agent: p.agent, choices, menu, screen, settings, status: p.status });
18905
+ out = interaction === "terminal" ? { choices: null, screen: null, menu: null, interaction } : { choices, screen: usageLimit ? null : screen, menu, interaction, ...usageLimit ? { usageLimit } : {} };
17932
18906
  noteScreen(p.id, p.agent, text);
17933
18907
  if (choices && (choices.detail || isPermissionQuestion(choices.question))) {
17934
18908
  const named = p.agent === "omp" && choices.detail && choices.detail.tool !== "tool" ? choices.detail.tool : null;
@@ -17938,7 +18912,7 @@ async function choicesFor(p, rev, strict, watch = false) {
17938
18912
  if (p.agent === "omp" && !choices.question && tr) choices.question = `Allow tool: ${tr.tool}`;
17939
18913
  }
17940
18914
  } catch {
17941
- out = { choices: c ? c.choices : null, screen: c ? c.screen : null, menu: c ? c.menu : null };
18915
+ out = { choices: c ? c.choices : null, screen: c ? c.screen : null, menu: c ? c.menu : null, interaction: (c == null ? void 0 : c.interaction) || "unknown", ...(c == null ? void 0 : c.usageLimit) ? { usageLimit: c.usageLimit } : {} };
17942
18916
  }
17943
18917
  choicesCache.set(p.id, { rev, strict, at: Date.now(), ...out });
17944
18918
  return out;
@@ -18089,7 +19063,7 @@ function addQueued(paneId, text, opts = {}) {
18089
19063
  };
18090
19064
  queued.set(paneId, [...list, e]);
18091
19065
  sent.set(paneId, addSent(sent.get(paneId) || [], e.text, e.at));
18092
- return publicEntry(e);
19066
+ return queuedPasted(publicEntry(e));
18093
19067
  }
18094
19068
  const sent = /* @__PURE__ */ new Map();
18095
19069
  const sentText = (paneId, entry, taken = []) => {
@@ -18112,7 +19086,11 @@ const restarting = (paneId) => {
18112
19086
  const GUARD_STATES = /* @__PURE__ */ new Set(["idle", "done", "working"]);
18113
19087
  function sendPrompt(p, text) {
18114
19088
  return withPaneLock(p.id, async () => {
18115
- if (p.agent !== "claude" || !GUARD_STATES.has(p.status || "") || isSlashCommand(text)) return agentPrompt(p.id, text);
19089
+ const now = findPane(p.id);
19090
+ if ((now == null ? void 0 : now.stopped) || (now == null ? void 0 : now.leaving)) {
19091
+ throw new HerdrError(isSlashCommand(text) ? "stale" : "no_input", "Restart Codex before sending a command.");
19092
+ }
19093
+ if (p.agent !== "claude" || !GUARD_STATES.has(p.status || "") || isSlashCommand(text) && !p.usageLimit) return agentPrompt(p.id, text);
18116
19094
  const keep = (texts) => {
18117
19095
  for (const t of texts || []) {
18118
19096
  addQueued(p.id, t, { held: true });
@@ -18131,7 +19109,7 @@ function sendPrompt(p, text) {
18131
19109
  keys: async (keys) => {
18132
19110
  await herdr("pane.send_input", { pane_id: p.id, keys });
18133
19111
  },
18134
- sleep,
19112
+ sleep: sleep$1,
18135
19113
  sentSince: async (since) => {
18136
19114
  const r2 = await transcripts.chat(p, {});
18137
19115
  return (r2.items || []).filter((i) => i.role === "user" && i.ts && Date.parse(i.ts) >= since - 1e3).map((i) => i.text);
@@ -18149,7 +19127,7 @@ const isBusyError = (e) => e instanceof HerdrError && (e.code === "input_busy" |
18149
19127
  const deliverBusy = /* @__PURE__ */ new Set();
18150
19128
  function deliverHeld(p) {
18151
19129
  const q = nextHeld(queued.get(p.id) || []);
18152
- if (!q || deliverBusy.has(p.id) || !p.agent || !INPUT_STATES.has(p.status || "") || restarting(p.id)) return;
19130
+ if (!q || deliverBusy.has(p.id) || !p.agent || p.stopped || !INPUT_STATES.has(p.status || "") || restarting(p.id)) return;
18153
19131
  deliverBusy.add(p.id);
18154
19132
  herdr("pane.read", { pane_id: p.id, source: "detection" }, 4e3).then(async (r) => {
18155
19133
  if (!inputVisible(r.read && r.read.text) || !q.held || q.failed) return;
@@ -18200,13 +19178,13 @@ function takeBackFromClaude(p) {
18200
19178
  await herdr("pane.send_input", { pane_id: p.id, keys });
18201
19179
  },
18202
19180
  chat: async () => (await transcripts.chat(p, { fresh: true })).items || [],
18203
- sleep,
19181
+ sleep: sleep$1,
18204
19182
  pending: () => (queued.get(p.id) || []).map((q) => q.text)
18205
19183
  }).catch(() => null);
18206
19184
  }
18207
19185
  async function cancelTurn(p, text) {
18208
19186
  if (!unansweredTurn((await transcripts.chat(p, { fresh: true })).items || [], text)) return null;
18209
- const r = await interruptAgent({ call: (m, params, t) => herdr(m, params, t), sleep, now: Date.now }, p);
19187
+ const r = await interruptAgent({ call: (m, params, t) => herdr(m, params, t), sleep: sleep$1, now: Date.now }, p);
18210
19188
  if (!r.stopped) return null;
18211
19189
  const item = await takeBackFromClaude(p);
18212
19190
  if (!item) return null;
@@ -18220,13 +19198,14 @@ function retryQueued(paneId, id) {
18220
19198
  q.failed = false;
18221
19199
  q.held = true;
18222
19200
  q.at = Date.now();
19201
+ delete q.back;
18223
19202
  delete q.busy;
18224
19203
  delete q.readySince;
18225
19204
  delete q.turnSeen;
18226
19205
  delete q.stuckSince;
18227
19206
  delete q.noInput;
18228
19207
  setTimeout(poll, 50);
18229
- return publicEntry(q);
19208
+ return queuedPasted(publicEntry(q));
18230
19209
  }
18231
19210
  const reconcileBusy = /* @__PURE__ */ new Set();
18232
19211
  function reconcileQueued(p) {
@@ -18242,11 +19221,11 @@ function reconcileQueued(p) {
18242
19221
  }
18243
19222
  const unqueueBusy = /* @__PURE__ */ new Set();
18244
19223
  async function deliveryDone(paneId) {
18245
- for (let i = 0; i < 60 && deliverBusy.has(paneId); i++) await sleep(100);
19224
+ for (let i = 0; i < 60 && deliverBusy.has(paneId); i++) await sleep$1(100);
18246
19225
  }
18247
19226
  async function cancelQueued(paneId, text, id) {
18248
19227
  const p = findPane(paneId);
18249
- if (!p || !p.agent) throw new HerdrError("not_found", "agent not found");
19228
+ if (!p) throw new HerdrError("not_found", "agent not found");
18250
19229
  let mine = findQueued(queued.get(p.id) || [], text, id);
18251
19230
  if (mine && (mine.held || mine.failed) && deliverBusy.has(p.id)) {
18252
19231
  await deliveryDone(p.id);
@@ -18262,6 +19241,7 @@ async function cancelQueued(paneId, text, id) {
18262
19241
  setTimeout(poll, 50);
18263
19242
  return { text: mine.text };
18264
19243
  }
19244
+ if (!p.agent) throw new HerdrError("not_found", "agent not found");
18265
19245
  const pend = pendingPrompts.get(p.id);
18266
19246
  if (pend && !pendingBusy.has(p.id) && sameMsg(msgText(pend.text), msgText(text))) {
18267
19247
  pendingPrompts.delete(p.id);
@@ -18288,7 +19268,7 @@ async function cancelQueued(paneId, text, id) {
18288
19268
  return { queue: r2.queue || [], items: r2.items || [] };
18289
19269
  },
18290
19270
  prompt: (t) => agentPrompt(p.id, t),
18291
- sleep,
19271
+ sleep: sleep$1,
18292
19272
  original: (e) => {
18293
19273
  var _a2, _b2;
18294
19274
  return !e.text && e.images ? ((_a2 = ownPhotos.shift()) == null ? void 0 : _a2.text) || fromHistory(e) : ((_b2 = own.find((q) => q !== mine && sameMsg(e.text, msgText(q.text)))) == null ? void 0 : _b2.text) || fromHistory(e) || e.text;
@@ -18320,6 +19300,41 @@ const pendingBusy = /* @__PURE__ */ new Set();
18320
19300
  const READY = /* @__PURE__ */ new Set(["done", "idle"]);
18321
19301
  const TURN = /* @__PURE__ */ new Set(["working", "blocked"]);
18322
19302
  const restarts = /* @__PURE__ */ new Map();
19303
+ const LAST_AGENTS_FILE = path$1.join(DATA_DIR, "last-agents.json");
19304
+ let lastAgentsWrite = Promise.resolve();
19305
+ const selfUpdates = createSelfUpdates({
19306
+ call: herdr,
19307
+ sleep: sleep$1,
19308
+ now: Date.now,
19309
+ chat: (p) => transcripts.chat(p, { fresh: true }),
19310
+ // Only called when a record changed (see agentSelfUpdate.ts).
19311
+ save: (records, sync) => {
19312
+ const s = JSON.stringify(records) + "\n";
19313
+ if (sync) {
19314
+ try {
19315
+ fs.mkdirSync(DATA_DIR, { recursive: true });
19316
+ fs.writeFileSync(LAST_AGENTS_FILE, s, { mode: 384 });
19317
+ } catch {
19318
+ }
19319
+ return;
19320
+ }
19321
+ lastAgentsWrite = lastAgentsWrite.then(async () => {
19322
+ await fsp$1.mkdir(DATA_DIR, { recursive: true });
19323
+ await fsp$1.writeFile(LAST_AGENTS_FILE, s, { mode: 384 });
19324
+ }).catch(() => {
19325
+ });
19326
+ }
19327
+ }, (() => {
19328
+ try {
19329
+ return JSON.parse(fs.readFileSync(LAST_AGENTS_FILE, "utf8"));
19330
+ } catch {
19331
+ return [];
19332
+ }
19333
+ })());
19334
+ function dismissStopped(paneId) {
19335
+ selfUpdates.clear(paneId);
19336
+ giveBackHeld(queued.get(paneId));
19337
+ }
18323
19338
  function flushPending(p) {
18324
19339
  const pend = pendingPrompts.get(p.id);
18325
19340
  if (!pend || pendingBusy.has(p.id)) return;
@@ -18421,6 +19436,7 @@ async function enrich(next, snap, machine) {
18421
19436
  const firstSnapshot = !seenMachines.has(machine);
18422
19437
  const prevStatus = new Map((((_a2 = mstates.get(machine)) == null ? void 0 : _a2.panes) || []).map((p) => [p.id, p.status || void 0]));
18423
19438
  const live = new Set(next.panes.map((p) => p.id));
19439
+ selfUpdates.prune(live, (id) => machineOf(id) === machine);
18424
19440
  for (const id of commands.keys()) if (machineOf(id) === machine && !live.has(id)) commands.delete(id);
18425
19441
  for (const p of next.panes) {
18426
19442
  const b = agentBorn.get(p.id);
@@ -18445,7 +19461,14 @@ async function enrich(next, snap, machine) {
18445
19461
  seenMachines.add(machine);
18446
19462
  (_b2 = getMachine(machine)) == null ? void 0 : _b2.transcripts.observe(next.panes);
18447
19463
  const revs = new Map((snap.panes || []).map((p) => [joinId(machine, p.pane_id), p.revision]));
18448
- for (const p of next.panes) {
19464
+ const exited = await Promise.all(next.panes.map((p) => selfUpdates.observe(p, restarts.has(p.id)).catch(() => false)));
19465
+ for (const [i, p] of next.panes.entries()) {
19466
+ if (exited[i]) {
19467
+ p.interaction = "managed";
19468
+ if (queued.has(p.id)) p.queued = queued.get(p.id).map((q) => queuedPasted(publicEntry(q)));
19469
+ continue;
19470
+ }
19471
+ if (leftForGood(p, findPane(p.id))) giveBackHeld(queued.get(p.id));
18449
19472
  if (pendingPrompts.has(p.id)) {
18450
19473
  p.pendingPrompt = true;
18451
19474
  flushPending(p);
@@ -18460,13 +19483,13 @@ async function enrich(next, snap, machine) {
18460
19483
  else queued.delete(p.id);
18461
19484
  }
18462
19485
  const list = queued.get(p.id);
18463
- const menu = Boolean(before && (before.menu || before.prompt || before.screen)) || restarting(p.id);
19486
+ const menu = Boolean(before && (before.menu || before.prompt || before.screen || before.interaction === "terminal")) || restarting(p.id);
18464
19487
  if (list && TURN.has(p.status || "")) {
18465
19488
  for (const q of list) if (!q.held && !q.failed) q.turnSeen = true;
18466
19489
  }
18467
19490
  if (list && checkQueue(list, (before == null ? void 0 : before.ompShell) ? "working" : p.status, Date.now(), menu)) log(`message not sent on ${p.id}`);
18468
19491
  if (list) deliverHeld(p);
18469
- if (list && list.length) p.queued = list.map(publicEntry);
19492
+ if (list && list.length) p.queued = list.map((q) => queuedPasted(publicEntry(q)));
18470
19493
  }
18471
19494
  const rs = restarts.get(p.id);
18472
19495
  if (rs && rs.phase === "failed" && (p.agent && rs.stopped || Date.now() - rs.at > 6e5)) restarts.delete(p.id);
@@ -18484,10 +19507,16 @@ async function enrich(next, snap, machine) {
18484
19507
  if (p.status !== "working") {
18485
19508
  const young = !p.agentSession || p.bornAt && Date.now() - p.bornAt < 12e4;
18486
19509
  const c = await choicesFor(p, revs.get(p.id), p.status !== "blocked", Boolean(young));
19510
+ p.interaction = c.interaction;
19511
+ if (c.usageLimit) {
19512
+ p.usageLimit = c.usageLimit;
19513
+ p.status = "idle";
19514
+ }
18487
19515
  if (c.choices) p.prompt = c.choices;
18488
19516
  if (c.screen) p.screen = c.screen;
18489
19517
  if (c.menu) p.menu = c.menu;
18490
19518
  } else {
19519
+ p.interaction = "working";
18491
19520
  choicesCache.delete(p.id);
18492
19521
  askChecks.delete(p.id);
18493
19522
  }
@@ -18672,14 +19701,17 @@ const localMachineOf = (ms) => ms.find((m) => m.local);
18672
19701
  function stopPolling() {
18673
19702
  stopped = true;
18674
19703
  if (pollTimer) clearTimeout(pollTimer);
19704
+ selfUpdates.flush();
18675
19705
  }
18676
19706
  const notifyTimers = /* @__PURE__ */ new Map();
18677
19707
  function watchTransitions(prev, next) {
19708
+ var _a2;
18678
19709
  const before = new Map(prev.panes.map((p) => [p.id, p.status]));
18679
19710
  for (const p of next.panes) {
18680
19711
  const was = before.get(p.id);
18681
- if (!p.agent || was === p.status) continue;
18682
- if (was === "working" && (p.status === "blocked" || READY.has(p.status || ""))) {
19712
+ const updated = p.stopped && !((_a2 = prev.panes.find((x) => x.id === p.id)) == null ? void 0 : _a2.stopped);
19713
+ if (!p.agent || was === p.status && !updated) continue;
19714
+ if (updated || was === "working" && (p.status === "blocked" || READY.has(p.status || ""))) {
18683
19715
  clearTimeout(notifyTimers.get(p.id));
18684
19716
  const target = p.status;
18685
19717
  notifyTimers.set(p.id, setTimeout(() => {
@@ -19170,1059 +20202,1598 @@ function stopNotices() {
19170
20202
  listeners.clear();
19171
20203
  }
19172
20204
 
19173
- const fsp = fs.promises;
19174
- const REFUSED = {
19175
- archive: "Archives cannot be read by the agent: extract the files first",
19176
- video: "Videos cannot be read by the agent",
19177
- audio: "Audio files cannot be read by the agent",
19178
- office: "Office documents cannot be read by the agent: export to PDF or text first",
19179
- executable: "Programs and binary files cannot be read by the agent",
19180
- binary: "Binary files cannot be read by the agent",
19181
- pdf: "This agent cannot read PDFs: only Claude Code can",
19182
- too_large: "File too large for the agent",
19183
- empty: "Empty file"
20205
+ // The phone address: wherdr published on the tailnet by `tailscale serve`
20206
+ // (private, only your devices). Reads Tailscale's state, publishes and
20207
+ // unpublishes wherdr's port, and checks that the address really answers.
20208
+ // Used by the server (Settings › Phone, server/utils/phone.ts) and by
20209
+ // `wherdr phone` / `wherdr panel`. No dependency on the rest of bin/lib nor
20210
+ // on packages: the server bundles this file.
20211
+
20212
+ const exec = promisify(execFile);
20213
+
20214
+ const LINKS = {
20215
+ download: 'https://tailscale.com/download',
20216
+ https: 'https://login.tailscale.com/admin/dns',
20217
+ operator: 'https://tailscale.com/kb/1242/tailscale-serve',
19184
20218
  };
19185
- async function saveAttachment(data, originalName, paneId) {
19186
- const pane = findPane(paneId);
19187
- const m = machineOfPane(paneId);
19188
- if (!pane || !m) throw new HerdrError("bad_pane", "Pane not found");
19189
- const check = checkAttachment(pane.agent, { name: originalName, type: "", size: data.length }, data.subarray(0, SNIFF_BYTES));
19190
- if (check.ok && check.kind === "image") throw new HerdrError("unsupported", "Images go through the photo upload");
19191
- if (!check.ok) throw new HerdrError(check.reason === "too_large" ? "too_large" : "unsupported", REFUSED[check.reason]);
19192
- const stored = storedAttachmentName(originalName, /* @__PURE__ */ new Date(), crypto.randomBytes(3).toString("hex"));
19193
- if (!isStoredAttachmentName(stored)) throw new HerdrError("bad_name", "Invalid file name");
19194
- let file;
19195
- if (m instanceof RemoteMachine) {
19196
- if (m.status !== "online") throw new HerdrError("unreachable", fmt("{machine} is unreachable", { machine: m.label }));
19197
- file = await m.putUpload(stored, data, "file");
19198
- } else {
19199
- file = path$1.join(ATTACH_DIR, stored);
19200
- if (path$1.dirname(file) !== path$1.resolve(ATTACH_DIR)) throw new HerdrError("bad_name", "Invalid file name");
19201
- await fsp.mkdir(ATTACH_DIR, { recursive: true, mode: 448 });
19202
- await fsp.writeFile(file, data, { mode: 384, flag: "wx" });
20219
+
20220
+ // The tailscale CLI: the macOS app's binary, Homebrew, system folders, then the PATH.
20221
+ function tailscaleBin(env = process.env, exists = existsSync) {
20222
+ const dirs = ['/Applications/Tailscale.app/Contents/MacOS/Tailscale', '/opt/homebrew/bin/tailscale', '/usr/local/bin/tailscale', '/usr/bin/tailscale'];
20223
+ for (const dir of (env.PATH || '').split(path$1.delimiter)) if (dir) dirs.push(path$1.join(dir, 'tailscale'));
20224
+ return dirs.find(p => exists(p)) || null
20225
+ }
20226
+
20227
+ // Tailscale's own socket on Linux. Its local API answers the same JSON as the
20228
+ // CLI: a Docker container has no `tailscale` command, but docker-compose.yml
20229
+ // mounts this folder, so wherdr reads the state there. Anyone may read; the
20230
+ // API also accepts changes from Tailscale's operator, but wherdr only ever
20231
+ // sends GET requests to it (localApi below).
20232
+ const TAILSCALED_SOCKET = '/var/run/tailscale/tailscaled.sock';
20233
+ function tailscaledSocket(env = process.env, exists = existsSync) {
20234
+ const file = env.WHERDR_TAILSCALED_SOCKET || TAILSCALED_SOCKET;
20235
+ return exists(file) ? file : null
20236
+ }
20237
+
20238
+ // GET on Tailscale's local API → the body, '' on any failure.
20239
+ function localApi(socketPath, route, timeoutMs = 4000) {
20240
+ const { promise, resolve } = Promise.withResolvers();
20241
+ const req = http.get({ socketPath, path: `/localapi/v0/${route}`, headers: { host: 'local-tailscaled.sock' }, timeout: timeoutMs }, (res) => {
20242
+ let text = '';
20243
+ res.setEncoding('utf8');
20244
+ res.on('data', (c) => { if (text.length < 1 << 20) text += c; });
20245
+ res.on('end', () => resolve(res.statusCode === 200 ? text : ''));
20246
+ res.on('error', () => resolve(''));
20247
+ });
20248
+ req.on('timeout', () => req.destroy());
20249
+ req.on('error', () => resolve(''));
20250
+ return promise
20251
+ }
20252
+
20253
+ // `tailscale status --json` → { name, connected, https }: this machine's
20254
+ // tailnet name (machine.tailnet.ts.net) or null, whether Tailscale is up,
20255
+ // and whether the tailnet issues HTTPS certificates.
20256
+ function tailnetStatus(statusJson) {
20257
+ let s = null;
20258
+ try { s = JSON.parse(statusJson); } catch {}
20259
+ const dns = s?.Self?.DNSName;
20260
+ return {
20261
+ name: typeof dns === 'string' && dns ? dns.replace(/\.$/, '') : null,
20262
+ connected: s?.BackendState === 'Running',
20263
+ https: Array.isArray(s?.CertDomains) && s.CertDomains.length > 0,
20264
+ }
20265
+ }
20266
+
20267
+ // The HTTPS entries of a serve configuration that proxy to wherdr's local
20268
+ // port on this machine's own name: [{ hostPort, httpsPort, funnel }], or null
20269
+ // when the configuration could not be read (Tailscale stopped, not JSON).
20270
+ // Background entries (`--bg`) and those of a `tailscale serve` still running
20271
+ // in a terminal (Foreground) both count. `funnel`: also open to the Internet.
20272
+ function wherdrEntries(name, port, serveJson) {
20273
+ let config = null;
20274
+ try { config = JSON.parse(serveJson); } catch {}
20275
+ if (!name || !config || typeof config !== 'object' || Array.isArray(config)) return null
20276
+ const target = new RegExp(`^(https?://)?(127\\.0\\.0\\.1|localhost):${port}/?$`);
20277
+ const out = [];
20278
+ for (const part of [config, ...Object.values(config.Foreground || {})]) {
20279
+ for (const [hostPort, entry] of Object.entries(part?.Web || {})) {
20280
+ const colon = hostPort.lastIndexOf(':');
20281
+ if (hostPort.slice(0, colon).toLowerCase() !== name.toLowerCase()) continue
20282
+ if (!Object.values(entry?.Handlers || {}).some(h => target.test(h?.Proxy || ''))) continue
20283
+ out.push({ hostPort, httpsPort: hostPort.slice(colon + 1), funnel: Boolean(part.AllowFunnel?.[hostPort]) });
20284
+ }
19203
20285
  }
19204
- log(`file received ${stored} (${Math.round(data.length / 1024)} KB, ${check.kind})${m instanceof RemoteMachine ? ` -> ${m.label}` : ""}`);
19205
- return { path: file, name: stored, kind: check.kind, size: data.length, ref: attachmentRef(pane.agent, check.kind, file) };
20286
+ return out
19206
20287
  }
19207
- async function cleanAttachments() {
20288
+
20289
+ const addressOf = (name, httpsPort) => `https://${name}${httpsPort === '443' ? '' : `:${httpsPort}`}/`;
20290
+
20291
+ // Phone address: the `tailscale serve` entry whose proxy targets wherdr's
20292
+ // local port (served on any HTTPS port), else the address publishing would
20293
+ // give (same port) with served: false. Null without a tailnet name.
20294
+ function phoneAddress(name, port, serveJson) {
20295
+ if (!name) return null
20296
+ const entries = wherdrEntries(name, port, serveJson) || [];
20297
+ const hit = entries.find(e => !e.funnel) || entries[0];
20298
+ if (hit) return { url: addressOf(name, hit.httpsPort), served: true, httpsPort: hit.httpsPort, ...(hit.funnel ? { funnel: true } : {}) }
20299
+ return { url: `https://${name}:${port}/`, served: false, httpsPort: String(port) }
20300
+ }
20301
+
20302
+ // The addresses wherdr adopts by itself (server/utils/phone.ts): what
20303
+ // `tailscale serve` publishes for its port on this machine's name, read from
20304
+ // Tailscale, which only the owner of the machine configures. null: unknown
20305
+ // (nothing is changed then); []: nothing is published. An address that
20306
+ // `tailscale funnel` also opens to the Internet is never adopted.
20307
+ function servedAddresses(name, port, serveJson) {
20308
+ const entries = wherdrEntries(name, port, serveJson);
20309
+ return entries && entries.filter(e => !e.funnel).map(e => addressOf(name, e.httpsPort))
20310
+ }
20311
+
20312
+ // HTTPS ports already published by `tailscale serve` (for anything).
20313
+ function servedPorts(serveJson) {
19208
20314
  try {
19209
- for (const n of await fsp.readdir(ATTACH_DIR)) {
19210
- const f = path$1.join(ATTACH_DIR, n);
19211
- const st = await fsp.lstat(f);
19212
- if (st.isFile() && Date.now() - st.mtimeMs > UPLOAD_TTL_MS) await fsp.unlink(f);
19213
- }
20315
+ const web = JSON.parse(serveJson || '{}')?.Web || {};
20316
+ return Object.keys(web).map(k => k.slice(k.lastIndexOf(':') + 1))
20317
+ } catch { return [] }
20318
+ }
20319
+
20320
+ // The command that publishes wherdr's port, as typed by hand (Docker mode).
20321
+ // `httpsPort`: the tailnet port, wherdr's own by default.
20322
+ const publishCommand = (port, httpsPort = port) => `tailscale serve --bg --https=${httpsPort} http://127.0.0.1:${port}`;
20323
+ const publishArgs = port => ['serve', '--bg', `--https=${port}`, `http://127.0.0.1:${port}`];
20324
+ const unpublishArgs = httpsPort => ['serve', `--https=${httpsPort}`, 'off'];
20325
+
20326
+ // A tailnet address typed by hand: https://machine.tailnet.ts.net[:port]/ → its
20327
+ // normalized form, or null.
20328
+ function tailnetUrl(text) {
20329
+ try {
20330
+ const u = new URL(String(text || '').trim());
20331
+ if (u.protocol !== 'https:' || !/^[a-z0-9-]+(\.[a-z0-9-]+)*\.ts\.net$/i.test(u.hostname) || u.username || u.password) return null
20332
+ return `https://${u.host.toLowerCase()}/`
20333
+ } catch { return null }
20334
+ }
20335
+
20336
+ // A failed `tailscale serve` → { code, link } for the known causes, else null.
20337
+ // code: 'operator' (Linux: the user may not change Tailscale's settings),
20338
+ // 'https' (no HTTPS certificates on the tailnet), 'offline' (not connected).
20339
+ function serveError(output) {
20340
+ const text = String(output || '');
20341
+ const link = /https:\/\/login\.tailscale\.com\/\S+/.exec(text)?.[0];
20342
+ if (/access denied|permission denied|--operator|not permitted/i.test(text)) return { code: 'operator', link: LINKS.operator }
20343
+ if (/https?\b.*(not enabled|disabled)|serve.*not enabled|enable https|certificates?.*(not enabled|disabled)|\/f\/serve/i.test(text)) return { code: 'https', link: link || LINKS.https }
20344
+ if (/needslogin|not logged in|logged out|tailscale is stopped|is tailscale running|not running|failed to connect to local tailscale/i.test(text)) return { code: 'offline', link: LINKS.download }
20345
+ return null
20346
+ }
20347
+
20348
+ // Tailscale's state for wherdr's port: { installed, bin, socket, connected,
20349
+ // https, name, phone, taken, served }. Read with the CLI, or without one
20350
+ // (Docker) from Tailscale's socket.
20351
+ async function inspect(port, bin = tailscaleBin(), socket = bin ? null : tailscaledSocket()) {
20352
+ if (!bin && !socket) return { installed: false }
20353
+ const run = args => exec(bin, args, { encoding: 'utf8', timeout: 8000 }).then(r => r.stdout, () => '');
20354
+ const status = tailnetStatus(bin ? await run(['status', '--json']) : await localApi(socket, 'status?peers=false'));
20355
+ const serve = !status.name ? '' : bin ? await run(['serve', 'status', '--json']) : await localApi(socket, 'serve-config');
20356
+ const phone = phoneAddress(status.name, port, serve);
20357
+ // Publishing on wherdr's port would replace whatever else is served there.
20358
+ const taken = Boolean(phone && !phone.served && servedPorts(serve).includes(String(port)));
20359
+ return { installed: true, bin, socket, ...status, phone, taken, served: servedAddresses(status.name, port, serve) }
20360
+ }
20361
+
20362
+ // The tailnet's DNS suffix (tailnet.ts.net) from /etc/resolv.conf's search
20363
+ // line: a Docker container copies it from the host when MagicDNS is on.
20364
+ function tailnetDomain(resolvConf) {
20365
+ for (const line of String(resolvConf || '').split('\n')) {
20366
+ const m = /^\s*search\s+(.+)$/.exec(line);
20367
+ const hit = m?.[1].split(/\s+/).find(d => /^[a-z0-9-]+\.ts\.net$/i.test(d));
20368
+ if (hit) return hit.toLowerCase()
20369
+ }
20370
+ return null
20371
+ }
20372
+
20373
+ // Tailscale's DNS (MagicDNS): a Docker container does not use it, but reaches it.
20374
+ const MAGIC_DNS = '100.100.100.100';
20375
+
20376
+ // The system's lookup, then MagicDNS for *.ts.net names.
20377
+ function lookup(host, opts, cb) {
20378
+ dns.lookup(host, opts, (err, address, family) => {
20379
+ if (!err || !/\.ts\.net$/i.test(host)) return cb(err, address, family)
20380
+ const resolver = new dns.Resolver({ timeout: 3000, tries: 1 });
20381
+ resolver.setServers([MAGIC_DNS]);
20382
+ resolver.resolve4(host, (e2, list) => {
20383
+ if (e2 || !list?.length) return cb(err)
20384
+ if (opts?.all) cb(null, list.map(a => ({ address: a, family: 4 })));
20385
+ else cb(null, list[0], 4);
20386
+ });
20387
+ });
20388
+ }
20389
+
20390
+ // Why a request failed: 'dns' (name not found), 'refused', 'timeout', 'cert'
20391
+ // (invalid certificate), 'tls' (no HTTPS on that port), or 'network'.
20392
+ function failureCause(err) {
20393
+ const code = String(err?.code || '');
20394
+ const msg = String(err?.message || '');
20395
+ if (code === 'ENOTFOUND' || code === 'EAI_AGAIN' || code === 'EAI_NONAME') return 'dns'
20396
+ if (code === 'ECONNREFUSED') return 'refused'
20397
+ if (code === 'ETIMEDOUT' || msg === 'timeout') return 'timeout'
20398
+ if (/CERT|SELF_SIGNED|UNABLE_TO_VERIFY|ALTNAME/.test(code)) return 'cert'
20399
+ if (code === 'EPROTO' || /wrong version number|ssl3_get_record|packet length/i.test(msg)) return 'tls'
20400
+ return 'network'
20401
+ }
20402
+
20403
+ // GET of `route` on the address → { status, text }, or { cause } when it failed.
20404
+ function fetchText(url, route, timeoutMs) {
20405
+ const { promise, resolve } = Promise.withResolvers();
20406
+ let u;
20407
+ try { u = new URL(route, url); } catch { return Promise.resolve({ cause: 'network' }) }
20408
+ // Accept JSON: a refused host then answers { code: 'host' }, not the HTML page.
20409
+ const req = https.get(u, { lookup, timeout: timeoutMs, headers: { accept: 'application/json' } }, (res) => {
20410
+ let text = '';
20411
+ res.setEncoding('utf8');
20412
+ res.on('data', (c) => { if (text.length < 65536) text += c; });
20413
+ res.on('end', () => resolve({ status: res.statusCode || 0, text }));
20414
+ res.on('error', e => resolve({ cause: failureCause(e) }));
20415
+ });
20416
+ req.on('timeout', () => req.destroy(new Error('timeout')));
20417
+ req.on('error', e => resolve({ cause: failureCause(e) }));
20418
+ return promise
20419
+ }
20420
+
20421
+ // What an answer of /api/health means: 'ok' (wherdr, and it accepts this
20422
+ // address), 'host' (wherdr's host check refused it), or null: not a wherdr
20423
+ // that has this route (versions up to 1.3.1, or another program).
20424
+ function healthReach(status, text) {
20425
+ if (status >= 200 && status < 300 && /"name"\s*:\s*"wherdr"/.test(text)) return 'ok'
20426
+ if (status === 403 && /"code"\s*:\s*"host"/.test(text)) return 'host'
20427
+ return null
20428
+ }
20429
+
20430
+ // reachable() with the details: { reach, cause?, status? }: `cause` when
20431
+ // unreachable (failureCause), `status` the HTTP code when something answered.
20432
+ // /api/health is asked first: it goes through wherdr's host check, which the
20433
+ // static manifest does not (it answers for any host name). The manifest only
20434
+ // recognizes an older wherdr without that route.
20435
+ async function probe(url, timeoutMs = 8000) {
20436
+ const health = await fetchText(url, 'api/health', timeoutMs);
20437
+ if (health.cause) return { reach: 'unreachable', cause: health.cause }
20438
+ const reach = healthReach(health.status, health.text);
20439
+ if (reach) return { reach, status: health.status }
20440
+ const manifest = await fetchText(url, 'manifest.webmanifest', timeoutMs);
20441
+ if (manifest.cause) return { reach: 'unreachable', cause: manifest.cause }
20442
+ const ok = manifest.status >= 200 && manifest.status < 300 && manifest.text.includes('wherdr');
20443
+ return { reach: ok ? 'ok' : 'other', status: ok ? manifest.status : health.status }
20444
+ }
20445
+
20446
+ // Runs `tailscale <args>` without a terminal → { code, output }. On a tailnet
20447
+ // without HTTPS, `tailscale serve` prints a link and waits: the timeout ends it.
20448
+ function runTailscale(bin, args, timeoutMs = 30_000) {
20449
+ return exec(bin, args, { encoding: 'utf8', timeout: timeoutMs }).then(
20450
+ r => ({ code: 0, output: `${r.stdout}${r.stderr}` }),
20451
+ e => ({ code: typeof e.code === 'number' ? e.code : -1, output: `${e.stdout || ''}${e.stderr || ''}${e.message || ''}` }),
20452
+ )
20453
+ }
20454
+
20455
+ const PHONE_GRACE_MS = 9e4;
20456
+ const TRANSIENT = { timeout: true, tls: true, refused: true, cert: true, network: true };
20457
+ function phoneReach(probe, since, now = Date.now()) {
20458
+ if (probe.reach !== "unreachable") return probe.reach;
20459
+ return probe.cause && TRANSIENT[probe.cause] && now - since < PHONE_GRACE_MS ? "pending" : "unreachable";
20460
+ }
20461
+
20462
+ function qrSvg(text) {
20463
+ const qr = new QRCode(-1, QRErrorCorrectLevel.M);
20464
+ qr.addData(text);
20465
+ qr.make();
20466
+ const n = qr.getModuleCount();
20467
+ let d = "";
20468
+ for (let r = 0; r < n; r++) {
20469
+ for (let col = 0; col < n; col++) if (qr.isDark(r, col)) d += `M${col + 2} ${r + 2}h1v1h-1z`;
20470
+ }
20471
+ return { size: n + 4, path: d };
20472
+ }
20473
+
20474
+ function hostname(value) {
20475
+ try {
20476
+ if (!value || /[\s/@?#]/.test(value)) return null;
20477
+ const url = new URL(`http://${value}`);
20478
+ if (url.username || url.password || url.pathname !== "/" || url.search || url.hash) return null;
20479
+ return url.hostname.replace(/^\[|\]$/g, "").toLowerCase().replace(/\.$/, "");
19214
20480
  } catch {
20481
+ return null;
19215
20482
  }
19216
20483
  }
19217
-
19218
- const _mZt23IdRC2vLuqDtWT6mb7m9BgHXq3U5QrnQ7Z5Hc4c = defineNitroPlugin((nitroApp) => {
19219
- initVapid();
19220
- startMachines();
19221
- startPolling();
19222
- startThreadLimits();
19223
- startNotices();
19224
- cleanUploads();
19225
- cleanAttachments();
19226
- const purge = setInterval(() => {
19227
- cleanUploads();
19228
- cleanAttachments();
19229
- }, 6 * 3600 * 1e3);
19230
- log(`herdr-web \u2014 socket ${HERDR_SOCK}${HERDR_SESSION ? ` (session ${HERDR_SESSION})` : ""}`);
19231
- nitroApp.hooks.hook("close", () => {
19232
- stopPolling();
19233
- stopNotices();
19234
- stopMachines();
19235
- clearInterval(purge);
20484
+ let servedHosts = [];
20485
+ function setServedHosts(urls) {
20486
+ servedHosts = urls.flatMap((url) => {
20487
+ try {
20488
+ return [new URL(url).hostname.toLowerCase()];
20489
+ } catch {
20490
+ return [];
20491
+ }
19236
20492
  });
19237
- });
20493
+ }
20494
+ function allowedHosts(appUrl2 = process.env.APP_URL || "", extra = process.env.HERDR_WEB_ALLOWED_HOSTS || "", interfaces = os.networkInterfaces(), served = servedHosts) {
20495
+ const names = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1", os.hostname().toLowerCase(), ...served]);
20496
+ try {
20497
+ if (appUrl2) names.add(new URL(appUrl2).hostname.toLowerCase().replace(/^\[|\]$/g, ""));
20498
+ } catch {
20499
+ }
20500
+ for (const addresses of Object.values(interfaces)) {
20501
+ for (const address of addresses || []) names.add(address.address.toLowerCase().replace(/%.+$/, ""));
20502
+ }
20503
+ for (const item of extra.split(",")) {
20504
+ const name = hostname(item.trim());
20505
+ if (name) names.add(name);
20506
+ }
20507
+ return names;
20508
+ }
20509
+ function hostAllowed(host, names = allowedHosts()) {
20510
+ const name = hostname(host || "");
20511
+ return Boolean(name && names.has(name));
20512
+ }
20513
+ function hostRefusalIsHtml(path, accept) {
20514
+ return !path.startsWith("/api/") && /\btext\/html\b/i.test(Array.isArray(accept) ? accept.join(",") : accept || "");
20515
+ }
20516
+ const CLI_DEFAULT_PORT = "7683";
20517
+ function phoneFixCommand(install = process.env.WHERDR_INSTALL || "", port = process.env.NITRO_PORT || process.env.PORT || "") {
20518
+ if (install === "docker" || install === "docker-build" || install === "plugin") return "curl -fsSL https://wherdr.dev/install | sh";
20519
+ const command = install === "brew" || install === "npm-global" ? "wherdr phone" : "npx wherdr phone";
20520
+ return /^\d+$/.test(port) && port !== CLI_DEFAULT_PORT ? `${command} --port ${port}` : command;
20521
+ }
20522
+ function tailnetName(host) {
20523
+ return /\.ts\.net$/.test(hostname(host || "") || "");
20524
+ }
20525
+ function hostRefusedPage(install, host, port) {
20526
+ var _a;
20527
+ const kind = (_a = process.env.WHERDR_INSTALL) != null ? _a : "";
20528
+ const command = phoneFixCommand(kind, port);
20529
+ const docker = kind === "docker" || kind === "docker-build";
20530
+ const where = docker ? "in the container's environment" : "in <code>~/wherdr/wherdr.env</code>";
20531
+ const whereFr = docker ? "dans l\u2019environnement du conteneur" : "dans <code>~/wherdr/wherdr.env</code>";
20532
+ const other = !tailnetName(host);
20533
+ const serve = other ? "If you reach wherdr through <code>tailscale serve</code>, wherdr enables that address by itself." : "wherdr enables by itself the address <code>tailscale serve</code> publishes for it.";
20534
+ const serveFr = other ? "Si tu passes par <code>tailscale serve</code>, wherdr active cette adresse tout seul." : "wherdr active tout seul l\u2019adresse que <code>tailscale serve</code> publie pour lui.";
20535
+ return `<!doctype html>
20536
+ <html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
20537
+ <meta name="robots" content="noindex"><title>wherdr \xB7 address not enabled</title>
20538
+ <style>
20539
+ :root{color-scheme:dark}*{box-sizing:border-box}
20540
+ body{margin:0;min-height:100vh;display:grid;place-items:center;background:#0d0f12;color:#e8eaed;font:15px/1.5 Inter,system-ui,-apple-system,sans-serif;padding:24px max(16px,env(safe-area-inset-right)) 24px max(16px,env(safe-area-inset-left))}
20541
+ main{max-width:480px;border:1px solid #3a3f47;padding:24px}
20542
+ .tag{font:11px/1 "JetBrains Mono",ui-monospace,monospace;letter-spacing:.08em;text-transform:uppercase;color:#9aa0a8;margin:0 0 16px}
20543
+ h1{font:600 20px/1.25 Archivo,system-ui,sans-serif;margin:0 0 8px}
20544
+ p{margin:0 0 12px}pre+p{margin-top:16px}p:last-child{margin-bottom:0}hr{border:0;border-top:1px solid #3a3f47;margin:20px 0}
20545
+ code{font:13px/1.45 "JetBrains Mono",ui-monospace,monospace;overflow-wrap:anywhere}
20546
+ pre{margin:0;padding:10px 12px;border:1px solid #3a3f47;background:#15181d;white-space:pre-wrap;overflow-wrap:anywhere;user-select:all;-webkit-user-select:all}
20547
+ </style></head><body><main>
20548
+ <p class="tag">wherdr \xB7 403</p>
20549
+ <h1>This address isn't enabled yet.</h1>
20550
+ <p>${serve} On the machine that runs wherdr, run this, then reload:</p>
20551
+ <pre><code>${command}</code></pre>${other ? `
20552
+ <p>For any other private name or IP address, add it to <code>HERDR_WEB_ALLOWED_HOSTS</code> (comma-separated) ${where}, then restart wherdr.</p>` : ""}
20553
+ <hr>
20554
+ <h1 lang="fr">Cette adresse n\u2019est pas encore activ\xE9e.</h1>
20555
+ <p lang="fr">${serveFr} Sur la machine qui fait tourner wherdr, lance ceci, puis recharge :</p>
20556
+ <pre><code>${command}</code></pre>${other ? `
20557
+ <p lang="fr">Pour tout autre nom priv\xE9 ou adresse IP, ajoute-le \xE0 <code>HERDR_WEB_ALLOWED_HOSTS</code> (s\xE9par\xE9s par des virgules) ${whereFr}, puis red\xE9marre wherdr.</p>` : ""}
20558
+ </main></body></html>
20559
+ `;
20560
+ }
20561
+ function crossSiteRequest(headers) {
20562
+ const site = headers["sec-fetch-site"];
20563
+ return typeof site === "string" && site !== "same-origin" && site !== "none";
20564
+ }
19238
20565
 
19239
- const plugins = [
19240
- _9as7ch5sSe9R7BrbrDE2GGZpuA6oP9uMAcL7iRtRgrw,
19241
- _mZt23IdRC2vLuqDtWT6mb7m9BgHXq3U5QrnQ7Z5Hc4c
19242
- ];
20566
+ const MAX_SESSION_DAYS = [1, 7, 30, 90, 365];
20567
+ const DEFAULT_MAX_SESSION_DAYS = 7;
20568
+ const DAY_MS = 24 * 3600 * 1e3;
20569
+ function isMaxSessionDays(v) {
20570
+ return MAX_SESSION_DAYS.includes(v);
20571
+ }
19243
20572
 
19244
- const assets = {
19245
- "/manifest-en.webmanifest": {
19246
- "type": "application/manifest+json",
19247
- "etag": "\"260-q1JnCK0/ecdKwgeTve6N4I6/mWI\"",
19248
- "mtime": "2026-10-08T11:44:03.930Z",
19249
- "size": 608,
19250
- "path": "../public/manifest-en.webmanifest"
19251
- },
19252
- "/manifest.webmanifest": {
19253
- "type": "application/manifest+json",
19254
- "etag": "\"26e-ykoYuj4V8la457yChW3oQTur8RU\"",
19255
- "mtime": "2026-10-08T11:44:03.930Z",
19256
- "size": 622,
19257
- "path": "../public/manifest.webmanifest"
19258
- },
19259
- "/sw.js": {
19260
- "type": "text/javascript; charset=utf-8",
19261
- "etag": "\"1b1d-uKe7we3azu1ltb6U6uThnVqkUn4\"",
19262
- "mtime": "2026-10-08T11:44:04.183Z",
19263
- "size": 6941,
19264
- "path": "../public/sw.js"
19265
- },
19266
- "/_nuxt/AgentView.C4YOwvZG.css": {
19267
- "type": "text/css; charset=utf-8",
19268
- "etag": "\"15c8-yoJUdtWy2mP7U+kxJ717a6Bzxgg\"",
19269
- "mtime": "2026-10-08T11:44:03.918Z",
19270
- "size": 5576,
19271
- "path": "../public/_nuxt/AgentView.C4YOwvZG.css"
19272
- },
19273
- "/_nuxt/B6qBWHWB.js": {
19274
- "type": "text/javascript; charset=utf-8",
19275
- "etag": "\"13f-jMtJt8dAqnEh2SAOR1babBORdK4\"",
19276
- "mtime": "2026-10-08T11:44:03.918Z",
19277
- "size": 319,
19278
- "path": "../public/_nuxt/B6qBWHWB.js"
19279
- },
19280
- "/_nuxt/BDNMzG2s.js": {
19281
- "type": "text/javascript; charset=utf-8",
19282
- "etag": "\"54-MasMfSk/A98C3Gn9uIOxtFxkWNw\"",
19283
- "mtime": "2026-10-08T11:44:03.918Z",
19284
- "size": 84,
19285
- "path": "../public/_nuxt/BDNMzG2s.js"
19286
- },
19287
- "/_nuxt/Bd17z0YL.js": {
19288
- "type": "text/javascript; charset=utf-8",
19289
- "etag": "\"1615-CPDn06/EO3pj6KudXz5uMZ0FkhI\"",
19290
- "mtime": "2026-10-08T11:44:03.918Z",
19291
- "size": 5653,
19292
- "path": "../public/_nuxt/Bd17z0YL.js"
19293
- },
19294
- "/_nuxt/BpQH8Ho1.js": {
19295
- "type": "text/javascript; charset=utf-8",
19296
- "etag": "\"e3-flLwB9P4XX6B82smMHA/xJ0MHtI\"",
19297
- "mtime": "2026-10-08T11:44:03.918Z",
19298
- "size": 227,
19299
- "path": "../public/_nuxt/BpQH8Ho1.js"
19300
- },
19301
- "/_nuxt/BMIe8p_3.js": {
19302
- "type": "text/javascript; charset=utf-8",
19303
- "etag": "\"1f34c-xbnpB4ou/Yo+mnIWUPmKT1dw6kg\"",
19304
- "mtime": "2026-10-08T11:44:03.918Z",
19305
- "size": 127820,
19306
- "path": "../public/_nuxt/BMIe8p_3.js"
19307
- },
19308
- "/_nuxt/C12eXl5z.js": {
19309
- "type": "text/javascript; charset=utf-8",
19310
- "etag": "\"3743-LHJuwpFjgipbDnhMaCyj/9CAzuY\"",
19311
- "mtime": "2026-10-08T11:44:03.918Z",
19312
- "size": 14147,
19313
- "path": "../public/_nuxt/C12eXl5z.js"
19314
- },
19315
- "/_nuxt/C2T9fQ4f.js": {
19316
- "type": "text/javascript; charset=utf-8",
19317
- "etag": "\"238-ULCQ+lAhpp4nPDbIDScqd3xGFLs\"",
19318
- "mtime": "2026-10-08T11:44:03.918Z",
19319
- "size": 568,
19320
- "path": "../public/_nuxt/C2T9fQ4f.js"
19321
- },
19322
- "/_nuxt/CWwyK2XK.js": {
19323
- "type": "text/javascript; charset=utf-8",
19324
- "etag": "\"7e5e-4BG2J9cvS3sx6WQ360LFqNaThyA\"",
19325
- "mtime": "2026-10-08T11:44:03.918Z",
19326
- "size": 32350,
19327
- "path": "../public/_nuxt/CWwyK2XK.js"
19328
- },
19329
- "/_nuxt/BrAeJIZR.js": {
19330
- "type": "text/javascript; charset=utf-8",
19331
- "etag": "\"46cdd-poAuGC5qlfraU64uRa+Yc2AkKhw\"",
19332
- "mtime": "2026-10-08T11:44:03.918Z",
19333
- "size": 290013,
19334
- "path": "../public/_nuxt/BrAeJIZR.js"
19335
- },
19336
- "/_nuxt/CXG-cOdh.js": {
19337
- "type": "text/javascript; charset=utf-8",
19338
- "etag": "\"d4e-hM6T4bjr/qfJVvSNRqFH3ePHBQY\"",
19339
- "mtime": "2026-10-08T11:44:03.918Z",
19340
- "size": 3406,
19341
- "path": "../public/_nuxt/CXG-cOdh.js"
19342
- },
19343
- "/_nuxt/CYxHnMNH.js": {
19344
- "type": "text/javascript; charset=utf-8",
19345
- "etag": "\"16ad4-A0W9NTd4vhWgkMxHSgPh07/MudQ\"",
19346
- "mtime": "2026-10-08T11:44:03.918Z",
19347
- "size": 92884,
19348
- "path": "../public/_nuxt/CYxHnMNH.js"
19349
- },
19350
- "/_nuxt/Ci8N4mWR.js": {
19351
- "type": "text/javascript; charset=utf-8",
19352
- "etag": "\"2426-fCrTYUyhtdHnTmu6ZJeEttvxVMo\"",
19353
- "mtime": "2026-10-08T11:44:03.918Z",
19354
- "size": 9254,
19355
- "path": "../public/_nuxt/Ci8N4mWR.js"
19356
- },
19357
- "/_nuxt/CctS2Xn6.js": {
19358
- "type": "text/javascript; charset=utf-8",
19359
- "etag": "\"16078-Cz/Wvlpwr6os4SjIEqnTr9/8LlU\"",
19360
- "mtime": "2026-10-08T11:44:03.918Z",
19361
- "size": 90232,
19362
- "path": "../public/_nuxt/CctS2Xn6.js"
19363
- },
19364
- "/_nuxt/DLLQgP0d.js": {
19365
- "type": "text/javascript; charset=utf-8",
19366
- "etag": "\"ed-o6vDEpQGmmIRAyMcrFDkG+VrA/Y\"",
19367
- "mtime": "2026-10-08T11:44:03.919Z",
19368
- "size": 237,
19369
- "path": "../public/_nuxt/DLLQgP0d.js"
19370
- },
19371
- "/_nuxt/CiYar6hg.js": {
19372
- "type": "text/javascript; charset=utf-8",
19373
- "etag": "\"16f07-We6JfDwPvSXrF5YZ4079o5Xc8i8\"",
19374
- "mtime": "2026-10-08T11:44:03.918Z",
19375
- "size": 93959,
19376
- "path": "../public/_nuxt/CiYar6hg.js"
19377
- },
19378
- "/_nuxt/SiL1FJ5s.js": {
19379
- "type": "text/javascript; charset=utf-8",
19380
- "etag": "\"176ad-/hlyeMkW67OV1A4G2nhIsYJX+vE\"",
19381
- "mtime": "2026-10-08T11:44:03.919Z",
19382
- "size": 95917,
19383
- "path": "../public/_nuxt/SiL1FJ5s.js"
19384
- },
19385
- "/_nuxt/Dwh-l2rQ.js": {
19386
- "type": "text/javascript; charset=utf-8",
19387
- "etag": "\"3919e-zK0jGmVhIk455555qPCvrLTDhXs\"",
19388
- "mtime": "2026-10-08T11:44:03.919Z",
19389
- "size": 233886,
19390
- "path": "../public/_nuxt/Dwh-l2rQ.js"
19391
- },
19392
- "/_nuxt/archivo-latin-ext-wght-normal.C4zznr8T.woff2": {
19393
- "type": "font/woff2",
19394
- "etag": "\"7f60-cwHN4xD2RhU0GVBfKzpzYZ7Al6U\"",
19395
- "mtime": "2026-10-08T11:44:03.919Z",
19396
- "size": 32608,
19397
- "path": "../public/_nuxt/archivo-latin-ext-wght-normal.C4zznr8T.woff2"
19398
- },
19399
- "/_nuxt/archivo-latin-wght-normal.E0tuGl4L.woff2": {
19400
- "type": "font/woff2",
19401
- "etag": "\"8870-ZUSfFgU3jYpWYbszVY1rxpynehc\"",
19402
- "mtime": "2026-10-08T11:44:03.919Z",
19403
- "size": 34928,
19404
- "path": "../public/_nuxt/archivo-latin-wght-normal.E0tuGl4L.woff2"
19405
- },
19406
- "/_nuxt/archivo-vietnamese-wght-normal.XAtsl5Q_.woff2": {
19407
- "type": "font/woff2",
19408
- "etag": "\"33b8-4zjQHgOy7y9EDRZs7TDAFDM4Ams\"",
19409
- "mtime": "2026-10-08T11:44:03.919Z",
19410
- "size": 13240,
19411
- "path": "../public/_nuxt/archivo-vietnamese-wght-normal.XAtsl5Q_.woff2"
19412
- },
19413
- "/_nuxt/WLQjK8uJ.js": {
19414
- "type": "text/javascript; charset=utf-8",
19415
- "etag": "\"9b3fe-Mhwqc5w8lJiuRM/0t8kIRoh6evE\"",
19416
- "mtime": "2026-10-08T11:44:03.919Z",
19417
- "size": 635902,
19418
- "path": "../public/_nuxt/WLQjK8uJ.js"
19419
- },
19420
- "/_nuxt/bLP7Jj-b.js": {
19421
- "type": "text/javascript; charset=utf-8",
19422
- "etag": "\"eab-MDKLGS0aoyNdipefSjbM7x6w6Y8\"",
19423
- "mtime": "2026-10-08T11:44:03.919Z",
19424
- "size": 3755,
19425
- "path": "../public/_nuxt/bLP7Jj-b.js"
19426
- },
19427
- "/_nuxt/error-404.Bb87HomL.css": {
19428
- "type": "text/css; charset=utf-8",
19429
- "etag": "\"97d-4h9VfBznldxjqagfMldh1hDncR0\"",
19430
- "mtime": "2026-10-08T11:44:03.919Z",
19431
- "size": 2429,
19432
- "path": "../public/_nuxt/error-404.Bb87HomL.css"
19433
- },
19434
- "/_nuxt/error-500.Bwd7zAaE.css": {
19435
- "type": "text/css; charset=utf-8",
19436
- "etag": "\"772-nqXXACYtFiPK+D42BNu4uEECARA\"",
19437
- "mtime": "2026-10-08T11:44:03.919Z",
19438
- "size": 1906,
19439
- "path": "../public/_nuxt/error-500.Bwd7zAaE.css"
19440
- },
19441
- "/_nuxt/inter-cyrillic-ext-wght-normal.BOeWTOD4.woff2": {
19442
- "type": "font/woff2",
19443
- "etag": "\"6568-cF1iUGbboMFZ8TfnP5HiMgl9II0\"",
19444
- "mtime": "2026-10-08T11:44:03.919Z",
19445
- "size": 25960,
19446
- "path": "../public/_nuxt/inter-cyrillic-ext-wght-normal.BOeWTOD4.woff2"
19447
- },
19448
- "/_nuxt/inter-cyrillic-wght-normal.DqGufNeO.woff2": {
19449
- "type": "font/woff2",
19450
- "etag": "\"493c-n3Oy9D6jvzfMjpClqox+Zo7ERQQ\"",
19451
- "mtime": "2026-10-08T11:44:03.919Z",
19452
- "size": 18748,
19453
- "path": "../public/_nuxt/inter-cyrillic-wght-normal.DqGufNeO.woff2"
19454
- },
19455
- "/_nuxt/inter-greek-ext-wght-normal.DlzME5K_.woff2": {
19456
- "type": "font/woff2",
19457
- "etag": "\"2be0-BP5iTzJeB8nLqYAgKpWNi5o1Zm8\"",
19458
- "mtime": "2026-10-08T11:44:03.919Z",
19459
- "size": 11232,
19460
- "path": "../public/_nuxt/inter-greek-ext-wght-normal.DlzME5K_.woff2"
19461
- },
19462
- "/_nuxt/inter-greek-wght-normal.CkhJZR-_.woff2": {
19463
- "type": "font/woff2",
19464
- "etag": "\"4a34-xor/hj4YNqI52zFecXnUbzQ4Xs4\"",
19465
- "mtime": "2026-10-08T11:44:03.920Z",
19466
- "size": 18996,
19467
- "path": "../public/_nuxt/inter-greek-wght-normal.CkhJZR-_.woff2"
19468
- },
19469
- "/_nuxt/inter-latin-ext-wght-normal.DO1Apj_S.woff2": {
19470
- "type": "font/woff2",
19471
- "etag": "\"14c4c-zz61D7IQFMB9QxHvTAOk/Vh4ibQ\"",
19472
- "mtime": "2026-10-08T11:44:03.920Z",
19473
- "size": 85068,
19474
- "path": "../public/_nuxt/inter-latin-ext-wght-normal.DO1Apj_S.woff2"
20573
+ var __defProp = Object.defineProperty;
20574
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
20575
+ var __publicField = (obj, key, value) => __defNormalProp(obj, key + "" , value);
20576
+ const SESSION_MS = 12 * 3600 * 1e3;
20577
+ const RENEW_MS = 10 * 60 * 1e3;
20578
+ const CHALLENGE_MS = 5 * 60 * 1e3;
20579
+ const AUTH_COOKIE = "hw_session";
20580
+ const CHALLENGE_COOKIE = "hw_challenge";
20581
+ class AuthError extends Error {
20582
+ constructor(code, message) {
20583
+ super(message);
20584
+ __publicField(this, "code");
20585
+ this.code = code;
20586
+ }
20587
+ }
20588
+ function createAuth({ dataDir, log: log2 = () => {
20589
+ }, passkeyUser = "herdr" }) {
20590
+ const FILE = path$1.join(dataDir, "auth.json");
20591
+ let db = { secret: null, generation: 0, credentials: [] };
20592
+ try {
20593
+ db = { ...db, ...JSON.parse(fs.readFileSync(FILE, "utf8")) };
20594
+ } catch {
20595
+ }
20596
+ const save = () => {
20597
+ fs.mkdirSync(dataDir, { recursive: true });
20598
+ fs.writeFileSync(FILE, JSON.stringify(db, null, 2) + "\n", { mode: 384 });
20599
+ };
20600
+ if (!db.secret) {
20601
+ db.secret = crypto.randomBytes(32).toString("hex");
20602
+ save();
20603
+ }
20604
+ const bootstrapToken = crypto.randomBytes(24).toString("base64url");
20605
+ if (!db.credentials.length) log2(`First passkey: bootstrap token ${bootstrapToken}`);
20606
+ const enabled = () => db.credentials.length > 0;
20607
+ const sign = (v) => crypto.createHmac("sha256", db.secret).update(v).digest("base64url");
20608
+ const safeEq = (a, b) => {
20609
+ const x = Buffer.from(String(a));
20610
+ const y = Buffer.from(String(b));
20611
+ return x.length === y.length && crypto.timingSafeEqual(x, y);
20612
+ };
20613
+ const readCookie = (req) => {
20614
+ const c = String(req.headers.cookie || "").split(/;\s*/).find((s) => s.startsWith(`${AUTH_COOKIE}=`));
20615
+ if (!c) return null;
20616
+ try {
20617
+ return decodeURIComponent(c.slice(AUTH_COOKIE.length + 1));
20618
+ } catch {
20619
+ return null;
20620
+ }
20621
+ };
20622
+ const challengeClient = (req) => {
20623
+ const c = String(req.headers.cookie || "").split(/;\s*/).find((s) => s.startsWith(`${CHALLENGE_COOKIE}=`));
20624
+ return (c == null ? void 0 : c.slice(CHALLENGE_COOKIE.length + 1)) || null;
20625
+ };
20626
+ const checkBootstrap = (body) => {
20627
+ if (!enabled() && !safeEq(String((body == null ? void 0 : body.bootstrapToken) || ""), bootstrapToken)) {
20628
+ throw new AuthError("bootstrap", "Invalid bootstrap token (see the server logs)");
20629
+ }
20630
+ };
20631
+ const maxSessionMs = () => {
20632
+ var _a;
20633
+ return ((_a = db.maxSessionDays) != null ? _a : DEFAULT_MAX_SESSION_DAYS) * DAY_MS;
20634
+ };
20635
+ function session(req, now = Date.now()) {
20636
+ const v = readCookie(req);
20637
+ if (!v) return null;
20638
+ const parts = v.split(".");
20639
+ if (parts.length !== 3 && parts.length !== 4) return null;
20640
+ const mac = parts.pop();
20641
+ if (!mac || parts[1] !== String(db.generation) || !safeEq(mac, sign(parts.join(".")))) return null;
20642
+ const exp = Number(parts[0]);
20643
+ const since = parts.length === 3 ? Number(parts[2]) : exp - SESSION_MS;
20644
+ if (!(exp >= now) || !Number.isFinite(since)) return null;
20645
+ if (now > since + maxSessionMs() + SESSION_MS) return null;
20646
+ return { exp, since };
20647
+ }
20648
+ function isUnlocked(req) {
20649
+ return !enabled() || session(req) !== null;
20650
+ }
20651
+ const secure = (req) => req.headers["x-forwarded-proto"] === "https" || String(req.headers.origin || "").startsWith("https://") ? "; Secure" : "";
20652
+ function sessionCookie(req, now = Date.now(), since = now) {
20653
+ const exp = now + SESSION_MS;
20654
+ const payload = `${exp}.${db.generation}.${since}`;
20655
+ return `${AUTH_COOKIE}=${payload}.${sign(payload)}; Path=/; HttpOnly; SameSite=Strict; Max-Age=${SESSION_MS / 1e3}${secure(req)}`;
20656
+ }
20657
+ const clearCookie = (req) => `${AUTH_COOKIE}=; Path=/; HttpOnly; SameSite=Strict; Max-Age=0${secure(req)}`;
20658
+ function renew(req, now = Date.now()) {
20659
+ if (!enabled()) return null;
20660
+ const s = session(req, now);
20661
+ if (s === null || s.exp - now > SESSION_MS - RENEW_MS) return null;
20662
+ return { cookie: sessionCookie(req, now, s.since), expiresAt: now + SESSION_MS };
20663
+ }
20664
+ function relying(req) {
20665
+ const origin = String(req.headers.origin || "");
20666
+ const { hostname } = new URL(origin);
20667
+ return { origin, rpID: hostname };
20668
+ }
20669
+ const challenges = /* @__PURE__ */ new Map();
20670
+ const challengeKey = (kind, client) => `${kind}:${client}`;
20671
+ const issueChallenge = (req, kind, value) => {
20672
+ const client = crypto.randomBytes(24).toString("base64url");
20673
+ for (const [key, challenge] of challenges) if (challenge.exp < Date.now()) challenges.delete(key);
20674
+ challenges.set(challengeKey(kind, client), { value, exp: Date.now() + CHALLENGE_MS });
20675
+ return `${CHALLENGE_COOKIE}=${client}; Path=/api/auth/; HttpOnly; SameSite=Strict; Max-Age=${CHALLENGE_MS / 1e3}${secure(req)}`;
20676
+ };
20677
+ const takeChallenge = (req, kind) => {
20678
+ const client = challengeClient(req);
20679
+ const key = challengeKey(kind, client || "");
20680
+ const c = challenges.get(key);
20681
+ challenges.delete(key);
20682
+ if (!c || c.exp < Date.now()) throw new AuthError("challenge", "Challenge expired, try again");
20683
+ return c.value;
20684
+ };
20685
+ async function registerOptions(req, body = {}) {
20686
+ if (enabled() && !isUnlocked(req)) throw new AuthError("locked", "Unlock first");
20687
+ checkBootstrap(body);
20688
+ const { rpID } = relying(req);
20689
+ const opts = await generateRegistrationOptions({
20690
+ rpName: "wherdr",
20691
+ rpID,
20692
+ userName: passkeyUser,
20693
+ userDisplayName: passkeyUser,
20694
+ attestationType: "none",
20695
+ excludeCredentials: db.credentials.map((c) => ({ id: c.id, transports: c.transports })),
20696
+ authenticatorSelection: { residentKey: "preferred", userVerification: "required" }
20697
+ });
20698
+ return { ...opts, __cookie: issueChallenge(req, "reg", opts.challenge) };
20699
+ }
20700
+ async function registerVerify(req, body) {
20701
+ if (enabled() && !isUnlocked(req)) throw new AuthError("locked", "Unlock first");
20702
+ checkBootstrap(body);
20703
+ const { origin, rpID } = relying(req);
20704
+ const v = await verifyRegistrationResponse({
20705
+ response: body.response,
20706
+ expectedChallenge: takeChallenge(req, "reg"),
20707
+ expectedOrigin: origin,
20708
+ expectedRPID: rpID,
20709
+ requireUserVerification: true
20710
+ });
20711
+ if (!v.verified || !v.registrationInfo) throw new AuthError("rejected", "Key rejected");
20712
+ const { credential } = v.registrationInfo;
20713
+ db.credentials.push({
20714
+ id: credential.id,
20715
+ publicKey: Buffer.from(credential.publicKey).toString("base64url"),
20716
+ counter: credential.counter,
20717
+ transports: credential.transports || [],
20718
+ name: String(body.name || "Device").slice(0, 40),
20719
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
20720
+ });
20721
+ save();
20722
+ log2(`lock: key registered (${db.credentials.length})`);
20723
+ return { ok: true, __cookie: sessionCookie(req) };
20724
+ }
20725
+ async function loginOptions(req) {
20726
+ const { rpID } = relying(req);
20727
+ const opts = await generateAuthenticationOptions({
20728
+ rpID,
20729
+ userVerification: "required",
20730
+ allowCredentials: db.credentials.map((c) => ({ id: c.id, transports: c.transports }))
20731
+ });
20732
+ return { ...opts, __cookie: issueChallenge(req, "auth", opts.challenge) };
20733
+ }
20734
+ async function loginVerify(req, body) {
20735
+ const { origin, rpID } = relying(req);
20736
+ const cred = db.credentials.find((c) => c.id === (body.response && body.response.id));
20737
+ if (!cred) throw new AuthError("unknown_key", "Key unknown on this server");
20738
+ const v = await verifyAuthenticationResponse({
20739
+ response: body.response,
20740
+ expectedChallenge: takeChallenge(req, "auth"),
20741
+ expectedOrigin: origin,
20742
+ expectedRPID: rpID,
20743
+ requireUserVerification: true,
20744
+ credential: { id: cred.id, publicKey: Buffer.from(cred.publicKey, "base64url"), counter: cred.counter, transports: cred.transports }
20745
+ });
20746
+ if (!v.verified) throw new AuthError("rejected", "Unlock rejected");
20747
+ cred.counter = v.authenticationInfo.newCounter;
20748
+ cred.lastUsed = (/* @__PURE__ */ new Date()).toISOString();
20749
+ save();
20750
+ return { ok: true, __cookie: sessionCookie(req) };
20751
+ }
20752
+ function disable(req) {
20753
+ if (!isUnlocked(req)) throw new AuthError("locked", "Unlock first");
20754
+ db.credentials = [];
20755
+ db.generation += 1;
20756
+ save();
20757
+ log2("lock disabled");
20758
+ return { ok: true, __cookie: clearCookie(req) };
20759
+ }
20760
+ function lockAll(req) {
20761
+ if (!enabled() || !isUnlocked(req)) throw new AuthError("locked", "Unlock first");
20762
+ db.generation += 1;
20763
+ save();
20764
+ log2("lock: all devices locked");
20765
+ return { ok: true, __cookie: clearCookie(req) };
20766
+ }
20767
+ function setMaxSession(req, days) {
20768
+ if (!enabled() || !isUnlocked(req)) throw new AuthError("locked", "Unlock first");
20769
+ if (!isMaxSessionDays(days)) throw new AuthError("invalid", "Invalid duration");
20770
+ db.maxSessionDays = days;
20771
+ save();
20772
+ return { ok: true, maxSessionDays: days };
20773
+ }
20774
+ function status(req, { resume = false, now = Date.now() } = {}) {
20775
+ var _a, _b;
20776
+ const devices = db.credentials.map((c) => ({ name: c.name, createdAt: c.createdAt, lastUsed: c.lastUsed || null }));
20777
+ if (!enabled()) return { enabled: false, unlocked: true, devices };
20778
+ const s = session(req, now);
20779
+ const deadline = s ? s.since + maxSessionMs() : 0;
20780
+ if (!s || resume && now >= deadline) {
20781
+ return { enabled: true, unlocked: false, expiresAt: null, now, devices, ...s ? { __cookie: clearCookie(req) } : {} };
20782
+ }
20783
+ const renewed = renew(req, now);
20784
+ return {
20785
+ enabled: true,
20786
+ unlocked: true,
20787
+ expiresAt: (_a = renewed == null ? void 0 : renewed.expiresAt) != null ? _a : s.exp,
20788
+ deadline,
20789
+ now,
20790
+ maxSessionDays: (_b = db.maxSessionDays) != null ? _b : DEFAULT_MAX_SESSION_DAYS,
20791
+ devices,
20792
+ ...renewed ? { __cookie: renewed.cookie } : {}
20793
+ };
20794
+ }
20795
+ return {
20796
+ isUnlocked,
20797
+ status,
20798
+ renew,
20799
+ registerOptions,
20800
+ registerVerify,
20801
+ loginOptions,
20802
+ loginVerify,
20803
+ disable,
20804
+ lockAll,
20805
+ setMaxSession,
20806
+ lock: (req) => ({ ok: true, __cookie: clearCookie(req) }),
20807
+ // For tests.
20808
+ _sessionCookie: sessionCookie,
20809
+ _db: () => db,
20810
+ _bootstrapToken: () => bootstrapToken,
20811
+ _takeChallenge: takeChallenge
20812
+ };
20813
+ }
20814
+
20815
+ const auth = createAuth({ dataDir: DATA_DIR, log, passkeyUser: PASSKEY_USER });
20816
+ const reqOf = (event) => ({ headers: getRequestHeaders(event) });
20817
+ function sameOrigin(headers) {
20818
+ const o = headers.origin;
20819
+ if (!o) return false;
20820
+ try {
20821
+ return new URL(o).host === headers.host;
20822
+ } catch {
20823
+ return false;
20824
+ }
20825
+ }
20826
+ const BODY_MAX = 256 * 1024;
20827
+ function sendError(event, status, body) {
20828
+ setResponseStatus(event, status);
20829
+ setResponseHeader(event, "cache-control", "no-store");
20830
+ return body;
20831
+ }
20832
+ function defineApi(fn, opts = {}) {
20833
+ return defineEventHandler(async (event) => {
20834
+ const url = getRequestURL(event);
20835
+ try {
20836
+ let body = {};
20837
+ if (event.method === "POST") {
20838
+ const headers = getRequestHeaders(event);
20839
+ const ctype = String(headers["content-type"] || "");
20840
+ const typeOk = opts.raw ? opts.raw.test(ctype) : ctype.startsWith("application/json");
20841
+ if (!sameOrigin(headers) || !typeOk) return sendError(event, 403, { error: "Origin refused" });
20842
+ const max = opts.raw ? opts.max || 20 * 1024 * 1024 : BODY_MAX;
20843
+ const tooLarge = opts.raw ? opts.tooLarge || "Image too large (20 MB max)" : "Request too large";
20844
+ if (Number(headers["content-length"] || 0) > max) throw new HerdrError("too_large", tooLarge);
20845
+ const raw = await readRawBody(event, false);
20846
+ if (raw && raw.length > max) throw new HerdrError("too_large", tooLarge);
20847
+ if (opts.raw) body = { data: raw || Buffer.alloc(0), ctype };
20848
+ else {
20849
+ try {
20850
+ body = raw && raw.length ? JSON.parse(raw.toString("utf8")) : {};
20851
+ } catch {
20852
+ throw new HerdrError("bad_json", "Invalid JSON");
20853
+ }
20854
+ }
20855
+ }
20856
+ const out = await fn(event, body);
20857
+ setResponseHeader(event, "cache-control", "no-store");
20858
+ if (out && typeof out === "object" && "__cookie" in out) {
20859
+ const o = out;
20860
+ setResponseHeader(event, "set-cookie", String(o.__cookie));
20861
+ delete o.__cookie;
20862
+ }
20863
+ return out;
20864
+ } catch (e) {
20865
+ const err = e;
20866
+ const known = e instanceof HerdrError || url.pathname.startsWith("/api/auth/") && Boolean(err.code);
20867
+ const code = known ? err.code : "internal";
20868
+ if (!known) log("error", event.method, url.pathname, e);
20869
+ const status = err.code === "unreachable" ? 503 : err.code === "locked" ? 401 : 400;
20870
+ return sendError(event, status, { error: err.message, code });
20871
+ }
20872
+ });
20873
+ }
20874
+ function sendImage(event, img) {
20875
+ setResponseHeader(event, "content-type", img.type);
20876
+ setResponseHeader(event, "cache-control", "private, max-age=86400");
20877
+ setResponseHeader(event, "x-content-type-options", "nosniff");
20878
+ setResponseHeader(event, "content-security-policy", "default-src 'none'; style-src 'unsafe-inline'; sandbox");
20879
+ return img.body;
20880
+ }
20881
+
20882
+ const PORT = String(process.env.NITRO_PORT || process.env.PORT || 3e3);
20883
+ const LOOPBACK = { "localhost": true, "127.0.0.1": true, "::1": true };
20884
+ function phoneAccess(event) {
20885
+ const req = reqOf(event);
20886
+ const lock = auth.status(req);
20887
+ if (lock.enabled && lock.unlocked) return true;
20888
+ const headers = getRequestHeaders(event);
20889
+ let host = "";
20890
+ try {
20891
+ host = new URL(`http://${headers.host || ""}`).hostname.replace(/^\[|\]$/g, "").toLowerCase();
20892
+ } catch {
20893
+ return false;
20894
+ }
20895
+ const proxied = Object.keys(headers).some((k) => k === "x-forwarded-for" || k === "forwarded" || k.startsWith("tailscale-"));
20896
+ const peer = (event.node.req.socket.remoteAddress || "").replace(/^::ffff:/, "");
20897
+ const localPeer = /^127\./.test(peer) || peer === "::1" || IN_DOCKER && peer === dockerGateway();
20898
+ return Boolean(LOOPBACK[host]) && !proxied && localPeer;
20899
+ }
20900
+ function dockerGateway() {
20901
+ try {
20902
+ const row = fs.readFileSync("/proc/net/route", "utf8").split("\n").map((l) => l.trim().split(/\s+/)).find((f) => f[1] === "00000000");
20903
+ const hex = row == null ? void 0 : row[2];
20904
+ return hex ? [3, 2, 1, 0].map((i) => String(Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16))).join(".") : "";
20905
+ } catch {
20906
+ return "";
20907
+ }
20908
+ }
20909
+ let adopted = (() => {
20910
+ var _a;
20911
+ try {
20912
+ return ((_a = JSON.parse(fs.readFileSync(APP_URL_FILE, "utf8"))) == null ? void 0 : _a.source) === "serve";
20913
+ } catch {
20914
+ return false;
20915
+ }
20916
+ })();
20917
+ function saveAppUrl(url, fromServe = false) {
20918
+ if (process.env.APP_URL === url && (adopted || !fromServe)) return;
20919
+ fs.mkdirSync(DATA_DIR, { recursive: true });
20920
+ fs.writeFileSync(APP_URL_FILE, `${JSON.stringify(fromServe ? { url, source: "serve" } : { url }, null, 2)}
20921
+ `);
20922
+ adopted = fromServe;
20923
+ if (process.env.APP_URL === url) return;
20924
+ process.env.APP_URL = url;
20925
+ initVapid();
20926
+ log(`APP_URL set to the phone address ${url}`);
20927
+ }
20928
+ function forgetAppUrl() {
20929
+ fs.rmSync(APP_URL_FILE, { force: true });
20930
+ const was = process.env.APP_URL;
20931
+ if (BOOT_APP_URL) process.env.APP_URL = BOOT_APP_URL;
20932
+ else delete process.env.APP_URL;
20933
+ initVapid();
20934
+ log(`phone address ${was} is no longer published by tailscale serve: forgotten`);
20935
+ }
20936
+ function applyServed(served) {
20937
+ if (!served) return;
20938
+ setServedHosts(served);
20939
+ if (ENV_APP_URL) return;
20940
+ const current = process.env.APP_URL || "";
20941
+ if (served.length) saveAppUrl(served.includes(current) ? current : served[0], true);
20942
+ else if (adopted) {
20943
+ if (current !== BOOT_APP_URL) forgetAppUrl();
20944
+ adopted = false;
20945
+ }
20946
+ }
20947
+ const nameListeners = [];
20948
+ function onTailnetName(f) {
20949
+ nameListeners.push(f);
20950
+ }
20951
+ function tellName(name) {
20952
+ if (name) for (const f of nameListeners) f(name);
20953
+ }
20954
+ let adoptedAt = 0;
20955
+ let adopting = null;
20956
+ function adoptServed(maxAgeMs = 0) {
20957
+ if (adopting) return adopting;
20958
+ if (maxAgeMs && Date.now() - adoptedAt < maxAgeMs) return Promise.resolve();
20959
+ const bin = tailscaleBin();
20960
+ adopting = inspect(PORT, bin, bin ? null : tailscaledSocket()).then((net) => {
20961
+ applyServed(net.served);
20962
+ tellName(net.name);
20963
+ }, (e) => {
20964
+ log(`tailscale serve could not be read: ${(e == null ? void 0 : e.message) || e}`);
20965
+ }).finally(() => {
20966
+ adoptedAt = Date.now();
20967
+ adopting = null;
20968
+ });
20969
+ return adopting;
20970
+ }
20971
+ let watch = null;
20972
+ function startTailnetWatch() {
20973
+ void adoptServed();
20974
+ watch = setInterval(() => {
20975
+ void adoptServed();
20976
+ }, TAILNET_REFRESH_MS);
20977
+ }
20978
+ function stopTailnetWatch() {
20979
+ if (watch) clearInterval(watch);
20980
+ watch = null;
20981
+ }
20982
+ let typed = "";
20983
+ const waitSince = /* @__PURE__ */ new Map();
20984
+ async function phoneStatus() {
20985
+ var _a, _b, _c, _d, _e, _f, _g;
20986
+ const bin = tailscaleBin();
20987
+ const socket = bin ? null : tailscaledSocket();
20988
+ const out = {
20989
+ mode: bin ? "native" : IN_DOCKER ? "docker" : "missing",
20990
+ platform: process.platform,
20991
+ port: PORT,
20992
+ connected: false,
20993
+ https: false,
20994
+ url: null,
20995
+ served: false,
20996
+ funnel: false,
20997
+ taken: false,
20998
+ suggested: null,
20999
+ command: publishCommand(PORT),
21000
+ reach: null,
21001
+ reachCause: null,
21002
+ reachStatus: null,
21003
+ checkedAt: null,
21004
+ appUrl: "",
21005
+ appUrlFromEnv: Boolean(ENV_APP_URL),
21006
+ qr: null
21007
+ };
21008
+ if (bin || socket) {
21009
+ const net = await inspect(PORT, bin, socket);
21010
+ applyServed(net.served);
21011
+ tellName(net.name);
21012
+ out.connected = Boolean(net.connected && net.name);
21013
+ out.https = Boolean(net.https);
21014
+ out.served = Boolean((_a = net.phone) == null ? void 0 : _a.served);
21015
+ out.funnel = Boolean((_b = net.phone) == null ? void 0 : _b.funnel);
21016
+ out.taken = Boolean(net.taken);
21017
+ out.suggested = (_d = (_c = net.phone) == null ? void 0 : _c.url) != null ? _d : null;
21018
+ out.url = out.served ? net.phone.url : null;
21019
+ }
21020
+ if (out.mode === "docker") {
21021
+ if (!out.suggested) {
21022
+ let resolv = "";
21023
+ try {
21024
+ resolv = fs.readFileSync("/etc/resolv.conf", "utf8");
21025
+ } catch {
21026
+ }
21027
+ const domain = tailnetDomain(resolv);
21028
+ const machine = (process.env.HOST_LABEL || "").trim().toLowerCase().replace(/[^a-z0-9-]/g, "-");
21029
+ out.suggested = domain && machine ? `https://${machine}.${domain}:${PORT}/` : null;
21030
+ }
21031
+ if (!out.url) out.url = typed || tailnetUrl(process.env.APP_URL);
21032
+ const httpsPort = httpsPortOf(out.url || out.suggested);
21033
+ if (httpsPort) out.command = publishCommand(PORT, httpsPort);
21034
+ }
21035
+ if (out.url) {
21036
+ let p = await probe(out.url, 5e3);
21037
+ if (!out.served && (p.reach === "ok" || p.reach === "host") && !ENV_APP_URL) {
21038
+ saveAppUrl(out.url);
21039
+ if (p.reach === "host") p = await probe(out.url, 5e3);
21040
+ }
21041
+ const since = (_e = waitSince.get(out.url)) != null ? _e : Date.now();
21042
+ if (p.reach === "ok") waitSince.delete(out.url);
21043
+ else waitSince.set(out.url, since);
21044
+ out.reach = phoneReach(p, since);
21045
+ out.reachCause = (_f = p.cause) != null ? _f : null;
21046
+ out.reachStatus = (_g = p.status) != null ? _g : null;
21047
+ out.checkedAt = Date.now();
21048
+ }
21049
+ out.appUrl = process.env.APP_URL || "";
21050
+ if (out.reach === "ok" && out.url) out.qr = qrSvg(out.url);
21051
+ return out;
21052
+ }
21053
+ function httpsPortOf(url) {
21054
+ try {
21055
+ return url ? new URL(url).port : "";
21056
+ } catch {
21057
+ return "";
21058
+ }
21059
+ }
21060
+ const fail = async (error, more = {}) => ({ ok: false, error, ...more, status: await phoneStatus() });
21061
+ async function phoneAction(body) {
21062
+ var _a;
21063
+ if (body.action === "address") {
21064
+ const url = tailnetUrl(body.url);
21065
+ if (!url) return fail("address");
21066
+ typed = url;
21067
+ return { ok: true, status: await phoneStatus() };
21068
+ }
21069
+ if (body.action !== "publish" && body.action !== "unpublish") return fail("failed", { detail: "Unknown action" });
21070
+ const bin = tailscaleBin();
21071
+ if (!bin) return fail("offline", { link: "https://tailscale.com/download" });
21072
+ const net = await inspect(PORT, bin);
21073
+ if (!net.connected || !net.phone) return fail("offline", { link: "https://tailscale.com/download" });
21074
+ let args;
21075
+ if (body.action === "publish") {
21076
+ if (net.phone.served) return { ok: true, status: await phoneStatus() };
21077
+ if (net.taken) return fail("taken");
21078
+ args = publishArgs(PORT);
21079
+ } else {
21080
+ if (!net.phone.served) return { ok: true, status: await phoneStatus() };
21081
+ args = unpublishArgs(net.phone.httpsPort);
21082
+ }
21083
+ const r = await runTailscale(bin, args);
21084
+ waitSince.clear();
21085
+ log(`tailscale ${args.join(" ")} \u2192 ${r.code}`);
21086
+ if (r.code !== 0) {
21087
+ const known = serveError(r.output);
21088
+ return fail((_a = known == null ? void 0 : known.code) != null ? _a : "failed", { link: known == null ? void 0 : known.link, detail: r.output.trim().slice(-600) });
21089
+ }
21090
+ return { ok: true, status: await phoneStatus() };
21091
+ }
21092
+
21093
+ const fsp = fs.promises;
21094
+ const REFUSED = {
21095
+ archive: "Archives cannot be read by the agent: extract the files first",
21096
+ video: "Videos cannot be read by the agent",
21097
+ audio: "Audio files cannot be read by the agent",
21098
+ office: "Office documents cannot be read by the agent: export to PDF or text first",
21099
+ executable: "Programs and binary files cannot be read by the agent",
21100
+ binary: "Binary files cannot be read by the agent",
21101
+ pdf: "This agent cannot read PDFs: only Claude Code can",
21102
+ too_large: "File too large for the agent",
21103
+ empty: "Empty file"
21104
+ };
21105
+ async function saveAttachment(data, originalName, paneId) {
21106
+ const pane = findPane(paneId);
21107
+ const m = machineOfPane(paneId);
21108
+ if (!pane || !m) throw new HerdrError("bad_pane", "Pane not found");
21109
+ const check = checkAttachment(pane.agent, { name: originalName, type: "", size: data.length }, data.subarray(0, SNIFF_BYTES));
21110
+ if (check.ok && check.kind === "image") throw new HerdrError("unsupported", "Images go through the photo upload");
21111
+ if (!check.ok) throw new HerdrError(check.reason === "too_large" ? "too_large" : "unsupported", REFUSED[check.reason]);
21112
+ const stored = storedAttachmentName(originalName, /* @__PURE__ */ new Date(), crypto.randomBytes(3).toString("hex"));
21113
+ if (!isStoredAttachmentName(stored)) throw new HerdrError("bad_name", "Invalid file name");
21114
+ let file;
21115
+ if (m instanceof RemoteMachine) {
21116
+ if (m.status !== "online") throw new HerdrError("unreachable", fmt("{machine} is unreachable", { machine: m.label }));
21117
+ file = await m.putUpload(stored, data, "file");
21118
+ } else {
21119
+ file = path$1.join(ATTACH_DIR, stored);
21120
+ if (path$1.dirname(file) !== path$1.resolve(ATTACH_DIR)) throw new HerdrError("bad_name", "Invalid file name");
21121
+ await fsp.mkdir(ATTACH_DIR, { recursive: true, mode: 448 });
21122
+ await fsp.writeFile(file, data, { mode: 384, flag: "wx" });
21123
+ }
21124
+ log(`file received ${stored} (${Math.round(data.length / 1024)} KB, ${check.kind})${m instanceof RemoteMachine ? ` -> ${m.label}` : ""}`);
21125
+ return { path: file, name: stored, kind: check.kind, size: data.length, ref: attachmentRef(pane.agent, check.kind, file) };
21126
+ }
21127
+ async function cleanAttachments() {
21128
+ try {
21129
+ for (const n of await fsp.readdir(ATTACH_DIR)) {
21130
+ const f = path$1.join(ATTACH_DIR, n);
21131
+ const st = await fsp.lstat(f);
21132
+ if (st.isFile() && Date.now() - st.mtimeMs > UPLOAD_TTL_MS) await fsp.unlink(f);
21133
+ }
21134
+ } catch {
21135
+ }
21136
+ }
21137
+
21138
+ const _mZt23IdRC2vLuqDtWT6mb7m9BgHXq3U5QrnQ7Z5Hc4c = defineNitroPlugin((nitroApp) => {
21139
+ initVapid();
21140
+ startMachines();
21141
+ startPolling();
21142
+ startThreadLimits();
21143
+ startNotices();
21144
+ startPaneSizes();
21145
+ onTailnetName(adoptTailnetName);
21146
+ startTailnetWatch();
21147
+ cleanUploads();
21148
+ cleanAttachments();
21149
+ purgePastes();
21150
+ const purge = setInterval(() => {
21151
+ cleanUploads();
21152
+ cleanAttachments();
21153
+ purgePastes();
21154
+ }, 6 * 3600 * 1e3);
21155
+ log(`herdr-web \u2014 socket ${HERDR_SOCK}${HERDR_SESSION ? ` (session ${HERDR_SESSION})` : ""}`);
21156
+ nitroApp.hooks.hook("close", () => {
21157
+ stopPolling();
21158
+ stopNotices();
21159
+ stopPaneSizes();
21160
+ stopTailnetWatch();
21161
+ stopMachines();
21162
+ clearInterval(purge);
21163
+ });
21164
+ });
21165
+
21166
+ const plugins = [
21167
+ _9as7ch5sSe9R7BrbrDE2GGZpuA6oP9uMAcL7iRtRgrw,
21168
+ _mZt23IdRC2vLuqDtWT6mb7m9BgHXq3U5QrnQ7Z5Hc4c
21169
+ ];
21170
+
21171
+ const assets = {
21172
+ "/manifest-en.webmanifest": {
21173
+ "type": "application/manifest+json",
21174
+ "etag": "\"260-q1JnCK0/ecdKwgeTve6N4I6/mWI\"",
21175
+ "mtime": "2026-10-12T02:43:06.055Z",
21176
+ "size": 608,
21177
+ "path": "../public/manifest-en.webmanifest"
19475
21178
  },
19476
- "/_nuxt/inter-latin-wght-normal.Dx4kXJAl.woff2": {
21179
+ "/manifest.webmanifest": {
21180
+ "type": "application/manifest+json",
21181
+ "etag": "\"26e-ykoYuj4V8la457yChW3oQTur8RU\"",
21182
+ "mtime": "2026-10-12T02:43:06.055Z",
21183
+ "size": 622,
21184
+ "path": "../public/manifest.webmanifest"
21185
+ },
21186
+ "/sw.js": {
21187
+ "type": "text/javascript; charset=utf-8",
21188
+ "etag": "\"1b75-QHSGlFHPf7wCrgMEz9tF99j/HQQ\"",
21189
+ "mtime": "2026-10-12T02:43:06.452Z",
21190
+ "size": 7029,
21191
+ "path": "../public/sw.js"
21192
+ },
21193
+ "/_nuxt/AgentView.DzFWBd2A.css": {
21194
+ "type": "text/css; charset=utf-8",
21195
+ "etag": "\"16c2-oz6EmjJDgIf6r38fC9pJHsZoJww\"",
21196
+ "mtime": "2026-10-12T02:43:06.045Z",
21197
+ "size": 5826,
21198
+ "path": "../public/_nuxt/AgentView.DzFWBd2A.css"
21199
+ },
21200
+ "/_nuxt/B4Yk2tB1.js": {
21201
+ "type": "text/javascript; charset=utf-8",
21202
+ "etag": "\"1176f-Op3w8R2G8EFcEwxudNsoKLq8Fl8\"",
21203
+ "mtime": "2026-10-12T02:43:06.045Z",
21204
+ "size": 71535,
21205
+ "path": "../public/_nuxt/B4Yk2tB1.js"
21206
+ },
21207
+ "/_nuxt/BBw_0a4M.js": {
21208
+ "type": "text/javascript; charset=utf-8",
21209
+ "etag": "\"17b75-3R2VJP1Oq7OKRMe/1iHRMGe6l3o\"",
21210
+ "mtime": "2026-10-12T02:43:06.045Z",
21211
+ "size": 97141,
21212
+ "path": "../public/_nuxt/BBw_0a4M.js"
21213
+ },
21214
+ "/_nuxt/BSy6OCIY.js": {
21215
+ "type": "text/javascript; charset=utf-8",
21216
+ "etag": "\"13f-1QDhbXhMYI+YPIbMXZLiVqG81SU\"",
21217
+ "mtime": "2026-10-12T02:43:06.045Z",
21218
+ "size": 319,
21219
+ "path": "../public/_nuxt/BSy6OCIY.js"
21220
+ },
21221
+ "/_nuxt/BX5PM2JQ.js": {
21222
+ "type": "text/javascript; charset=utf-8",
21223
+ "etag": "\"2905-uwamdHajAQRD2qCUB0gaMXs5+Hs\"",
21224
+ "mtime": "2026-10-12T02:43:06.045Z",
21225
+ "size": 10501,
21226
+ "path": "../public/_nuxt/BX5PM2JQ.js"
21227
+ },
21228
+ "/_nuxt/Bd17z0YL.js": {
21229
+ "type": "text/javascript; charset=utf-8",
21230
+ "etag": "\"1615-CPDn06/EO3pj6KudXz5uMZ0FkhI\"",
21231
+ "mtime": "2026-10-12T02:43:06.045Z",
21232
+ "size": 5653,
21233
+ "path": "../public/_nuxt/Bd17z0YL.js"
21234
+ },
21235
+ "/_nuxt/BgPDnf2S.js": {
21236
+ "type": "text/javascript; charset=utf-8",
21237
+ "etag": "\"14010-9tQrbfc74ogfocj+ZMU5WrK9qEM\"",
21238
+ "mtime": "2026-10-12T02:43:06.045Z",
21239
+ "size": 81936,
21240
+ "path": "../public/_nuxt/BgPDnf2S.js"
21241
+ },
21242
+ "/_nuxt/BkOxQzbS.js": {
21243
+ "type": "text/javascript; charset=utf-8",
21244
+ "etag": "\"91a1-Cc5iImaS93KRPJgLU4sZS6Cewjk\"",
21245
+ "mtime": "2026-10-12T02:43:06.045Z",
21246
+ "size": 37281,
21247
+ "path": "../public/_nuxt/BkOxQzbS.js"
21248
+ },
21249
+ "/_nuxt/CP8o925U.js": {
21250
+ "type": "text/javascript; charset=utf-8",
21251
+ "etag": "\"238-L5f/T9nwmMze/S7MlkAtOw2Agko\"",
21252
+ "mtime": "2026-10-12T02:43:06.045Z",
21253
+ "size": 568,
21254
+ "path": "../public/_nuxt/CP8o925U.js"
21255
+ },
21256
+ "/_nuxt/CWwyK2XK.js": {
21257
+ "type": "text/javascript; charset=utf-8",
21258
+ "etag": "\"7e5e-4BG2J9cvS3sx6WQ360LFqNaThyA\"",
21259
+ "mtime": "2026-10-12T02:43:06.045Z",
21260
+ "size": 32350,
21261
+ "path": "../public/_nuxt/CWwyK2XK.js"
21262
+ },
21263
+ "/_nuxt/CkbmecwS.js": {
21264
+ "type": "text/javascript; charset=utf-8",
21265
+ "etag": "\"f35-4fKoqAhIJbV/rJjyeg24lmHsFBg\"",
21266
+ "mtime": "2026-10-12T02:43:06.046Z",
21267
+ "size": 3893,
21268
+ "path": "../public/_nuxt/CkbmecwS.js"
21269
+ },
21270
+ "/_nuxt/Ci8N4mWR.js": {
21271
+ "type": "text/javascript; charset=utf-8",
21272
+ "etag": "\"2426-fCrTYUyhtdHnTmu6ZJeEttvxVMo\"",
21273
+ "mtime": "2026-10-12T02:43:06.045Z",
21274
+ "size": 9254,
21275
+ "path": "../public/_nuxt/Ci8N4mWR.js"
21276
+ },
21277
+ "/_nuxt/Cr3SrGky.js": {
21278
+ "type": "text/javascript; charset=utf-8",
21279
+ "etag": "\"e91-L57+rnkmURnrnbQ2ojMRXvZSac4\"",
21280
+ "mtime": "2026-10-12T02:43:06.046Z",
21281
+ "size": 3729,
21282
+ "path": "../public/_nuxt/Cr3SrGky.js"
21283
+ },
21284
+ "/_nuxt/DT8qZ0Wv.js": {
21285
+ "type": "text/javascript; charset=utf-8",
21286
+ "etag": "\"d34-pCkp/XrEPhSiS8s2YPYsI/DZ69I\"",
21287
+ "mtime": "2026-10-12T02:43:06.046Z",
21288
+ "size": 3380,
21289
+ "path": "../public/_nuxt/DT8qZ0Wv.js"
21290
+ },
21291
+ "/_nuxt/DAg9fiVh.js": {
21292
+ "type": "text/javascript; charset=utf-8",
21293
+ "etag": "\"1b244-sPLH9gYhaVJ89ISuFCDsYuNF9Vs\"",
21294
+ "mtime": "2026-10-12T02:43:06.046Z",
21295
+ "size": 111172,
21296
+ "path": "../public/_nuxt/DAg9fiVh.js"
21297
+ },
21298
+ "/_nuxt/DaaJIRB3.js": {
21299
+ "type": "text/javascript; charset=utf-8",
21300
+ "etag": "\"dd3-CbFoaT3rUHg6B+v1747rxTekQNQ\"",
21301
+ "mtime": "2026-10-12T02:43:06.046Z",
21302
+ "size": 3539,
21303
+ "path": "../public/_nuxt/DaaJIRB3.js"
21304
+ },
21305
+ "/_nuxt/DcJi16XA.js": {
21306
+ "type": "text/javascript; charset=utf-8",
21307
+ "etag": "\"10a-fvmuAmno7y+Lve3V9/nvXuQHotk\"",
21308
+ "mtime": "2026-10-12T02:43:06.046Z",
21309
+ "size": 266,
21310
+ "path": "../public/_nuxt/DcJi16XA.js"
21311
+ },
21312
+ "/_nuxt/SiL1FJ5s.js": {
21313
+ "type": "text/javascript; charset=utf-8",
21314
+ "etag": "\"176ad-/hlyeMkW67OV1A4G2nhIsYJX+vE\"",
21315
+ "mtime": "2026-10-12T02:43:06.046Z",
21316
+ "size": 95917,
21317
+ "path": "../public/_nuxt/SiL1FJ5s.js"
21318
+ },
21319
+ "/_nuxt/VpjCgSXu.js": {
21320
+ "type": "text/javascript; charset=utf-8",
21321
+ "etag": "\"5b489-LoKHy0L+KBvwlf0JAvODFbMFL2o\"",
21322
+ "mtime": "2026-10-12T02:43:06.046Z",
21323
+ "size": 373897,
21324
+ "path": "../public/_nuxt/VpjCgSXu.js"
21325
+ },
21326
+ "/_nuxt/archivo-latin-ext-wght-normal.C4zznr8T.woff2": {
19477
21327
  "type": "font/woff2",
19478
- "etag": "\"bc80-8R1ym7Ck2DUNLqPQ/AYs9u8tUpg\"",
19479
- "mtime": "2026-10-08T11:44:03.920Z",
19480
- "size": 48256,
19481
- "path": "../public/_nuxt/inter-latin-wght-normal.Dx4kXJAl.woff2"
21328
+ "etag": "\"7f60-cwHN4xD2RhU0GVBfKzpzYZ7Al6U\"",
21329
+ "mtime": "2026-10-12T02:43:06.046Z",
21330
+ "size": 32608,
21331
+ "path": "../public/_nuxt/archivo-latin-ext-wght-normal.C4zznr8T.woff2"
21332
+ },
21333
+ "/_nuxt/archivo-latin-wght-normal.E0tuGl4L.woff2": {
21334
+ "type": "font/woff2",
21335
+ "etag": "\"8870-ZUSfFgU3jYpWYbszVY1rxpynehc\"",
21336
+ "mtime": "2026-10-12T02:43:06.046Z",
21337
+ "size": 34928,
21338
+ "path": "../public/_nuxt/archivo-latin-wght-normal.E0tuGl4L.woff2"
21339
+ },
21340
+ "/_nuxt/CKhgznq4.js": {
21341
+ "type": "text/javascript; charset=utf-8",
21342
+ "etag": "\"a7330-Q9O7D5hd37aKtqvr2vkiACwyZzM\"",
21343
+ "mtime": "2026-10-12T02:43:06.046Z",
21344
+ "size": 684848,
21345
+ "path": "../public/_nuxt/CKhgznq4.js"
21346
+ },
21347
+ "/_nuxt/archivo-vietnamese-wght-normal.XAtsl5Q_.woff2": {
21348
+ "type": "font/woff2",
21349
+ "etag": "\"33b8-4zjQHgOy7y9EDRZs7TDAFDM4Ams\"",
21350
+ "mtime": "2026-10-12T02:43:06.046Z",
21351
+ "size": 13240,
21352
+ "path": "../public/_nuxt/archivo-vietnamese-wght-normal.XAtsl5Q_.woff2"
21353
+ },
21354
+ "/_nuxt/dPxB2L94.js": {
21355
+ "type": "text/javascript; charset=utf-8",
21356
+ "etag": "\"471ad-4Pu+hTmytPNTQQ7hdBgH0D/hZVE\"",
21357
+ "mtime": "2026-10-12T02:43:06.046Z",
21358
+ "size": 291245,
21359
+ "path": "../public/_nuxt/dPxB2L94.js"
21360
+ },
21361
+ "/_nuxt/error-500.Bwd7zAaE.css": {
21362
+ "type": "text/css; charset=utf-8",
21363
+ "etag": "\"772-nqXXACYtFiPK+D42BNu4uEECARA\"",
21364
+ "mtime": "2026-10-12T02:43:06.046Z",
21365
+ "size": 1906,
21366
+ "path": "../public/_nuxt/error-500.Bwd7zAaE.css"
21367
+ },
21368
+ "/_nuxt/error-404.Bb87HomL.css": {
21369
+ "type": "text/css; charset=utf-8",
21370
+ "etag": "\"97d-4h9VfBznldxjqagfMldh1hDncR0\"",
21371
+ "mtime": "2026-10-12T02:43:06.046Z",
21372
+ "size": 2429,
21373
+ "path": "../public/_nuxt/error-404.Bb87HomL.css"
21374
+ },
21375
+ "/_nuxt/hePW80VL.js": {
21376
+ "type": "text/javascript; charset=utf-8",
21377
+ "etag": "\"2cc-fA8td6k29UVF6JoPfhOPkceTK1M\"",
21378
+ "mtime": "2026-10-12T02:43:06.047Z",
21379
+ "size": 716,
21380
+ "path": "../public/_nuxt/hePW80VL.js"
21381
+ },
21382
+ "/_nuxt/inter-cyrillic-ext-wght-normal.BOeWTOD4.woff2": {
21383
+ "type": "font/woff2",
21384
+ "etag": "\"6568-cF1iUGbboMFZ8TfnP5HiMgl9II0\"",
21385
+ "mtime": "2026-10-12T02:43:06.047Z",
21386
+ "size": 25960,
21387
+ "path": "../public/_nuxt/inter-cyrillic-ext-wght-normal.BOeWTOD4.woff2"
21388
+ },
21389
+ "/_nuxt/inter-cyrillic-wght-normal.DqGufNeO.woff2": {
21390
+ "type": "font/woff2",
21391
+ "etag": "\"493c-n3Oy9D6jvzfMjpClqox+Zo7ERQQ\"",
21392
+ "mtime": "2026-10-12T02:43:06.047Z",
21393
+ "size": 18748,
21394
+ "path": "../public/_nuxt/inter-cyrillic-wght-normal.DqGufNeO.woff2"
21395
+ },
21396
+ "/_nuxt/inter-greek-ext-wght-normal.DlzME5K_.woff2": {
21397
+ "type": "font/woff2",
21398
+ "etag": "\"2be0-BP5iTzJeB8nLqYAgKpWNi5o1Zm8\"",
21399
+ "mtime": "2026-10-12T02:43:06.047Z",
21400
+ "size": 11232,
21401
+ "path": "../public/_nuxt/inter-greek-ext-wght-normal.DlzME5K_.woff2"
21402
+ },
21403
+ "/_nuxt/inter-greek-wght-normal.CkhJZR-_.woff2": {
21404
+ "type": "font/woff2",
21405
+ "etag": "\"4a34-xor/hj4YNqI52zFecXnUbzQ4Xs4\"",
21406
+ "mtime": "2026-10-12T02:43:06.047Z",
21407
+ "size": 18996,
21408
+ "path": "../public/_nuxt/inter-greek-wght-normal.CkhJZR-_.woff2"
21409
+ },
21410
+ "/_nuxt/inter-latin-ext-wght-normal.DO1Apj_S.woff2": {
21411
+ "type": "font/woff2",
21412
+ "etag": "\"14c4c-zz61D7IQFMB9QxHvTAOk/Vh4ibQ\"",
21413
+ "mtime": "2026-10-12T02:43:06.047Z",
21414
+ "size": 85068,
21415
+ "path": "../public/_nuxt/inter-latin-ext-wght-normal.DO1Apj_S.woff2"
19482
21416
  },
19483
21417
  "/_nuxt/inter-vietnamese-wght-normal.CBcvBZtf.woff2": {
19484
21418
  "type": "font/woff2",
19485
21419
  "etag": "\"280c-nBythjoDQ0+5wVAendJ6wU7Xz2M\"",
19486
- "mtime": "2026-10-08T11:44:03.920Z",
21420
+ "mtime": "2026-10-12T02:43:06.047Z",
19487
21421
  "size": 10252,
19488
21422
  "path": "../public/_nuxt/inter-vietnamese-wght-normal.CBcvBZtf.woff2"
19489
21423
  },
21424
+ "/_nuxt/inter-latin-wght-normal.Dx4kXJAl.woff2": {
21425
+ "type": "font/woff2",
21426
+ "etag": "\"bc80-8R1ym7Ck2DUNLqPQ/AYs9u8tUpg\"",
21427
+ "mtime": "2026-10-12T02:43:06.047Z",
21428
+ "size": 48256,
21429
+ "path": "../public/_nuxt/inter-latin-wght-normal.Dx4kXJAl.woff2"
21430
+ },
19490
21431
  "/_nuxt/jetbrains-mono-cyrillic-wght-normal.D73BlboJ.woff2": {
19491
21432
  "type": "font/woff2",
19492
21433
  "etag": "\"2f4c-WiAGfn140d4QND3ayQWaCHF8rbE\"",
19493
- "mtime": "2026-10-08T11:44:03.920Z",
21434
+ "mtime": "2026-10-12T02:43:06.047Z",
19494
21435
  "size": 12108,
19495
21436
  "path": "../public/_nuxt/jetbrains-mono-cyrillic-wght-normal.D73BlboJ.woff2"
19496
21437
  },
21438
+ "/_nuxt/entry.CGWKmYU3.css": {
21439
+ "type": "text/css; charset=utf-8",
21440
+ "etag": "\"68db2-q6wQXFOalVzn1yh9EoP09Ru8lNs\"",
21441
+ "mtime": "2026-10-12T02:43:06.046Z",
21442
+ "size": 429490,
21443
+ "path": "../public/_nuxt/entry.CGWKmYU3.css"
21444
+ },
19497
21445
  "/_nuxt/jetbrains-mono-greek-wght-normal.Bw9x6K1M.woff2": {
19498
21446
  "type": "font/woff2",
19499
21447
  "etag": "\"232c-Dnz9DhH4c266e6TziU1pxRkV6FY\"",
19500
- "mtime": "2026-10-08T11:44:03.920Z",
21448
+ "mtime": "2026-10-12T02:43:06.047Z",
19501
21449
  "size": 9004,
19502
21450
  "path": "../public/_nuxt/jetbrains-mono-greek-wght-normal.Bw9x6K1M.woff2"
19503
21451
  },
19504
21452
  "/_nuxt/jetbrains-mono-latin-ext-wght-normal.DBQx-q_a.woff2": {
19505
21453
  "type": "font/woff2",
19506
21454
  "etag": "\"3b5c-HLF7Wvs2Z1IA1cPRs6jnor8OUQ4\"",
19507
- "mtime": "2026-10-08T11:44:03.927Z",
21455
+ "mtime": "2026-10-12T02:43:06.047Z",
19508
21456
  "size": 15196,
19509
21457
  "path": "../public/_nuxt/jetbrains-mono-latin-ext-wght-normal.DBQx-q_a.woff2"
19510
21458
  },
19511
21459
  "/_nuxt/jetbrains-mono-latin-wght-normal.B9CIFXIH.woff2": {
19512
21460
  "type": "font/woff2",
19513
21461
  "etag": "\"9dd4-5yd+cUUhzrXxdMyYebUeD0qml1M\"",
19514
- "mtime": "2026-10-08T11:44:03.927Z",
21462
+ "mtime": "2026-10-12T02:43:06.047Z",
19515
21463
  "size": 40404,
19516
21464
  "path": "../public/_nuxt/jetbrains-mono-latin-wght-normal.B9CIFXIH.woff2"
19517
21465
  },
19518
21466
  "/_nuxt/jetbrains-mono-vietnamese-wght-normal.Bt-aOZkq.woff2": {
19519
21467
  "type": "font/woff2",
19520
21468
  "etag": "\"1d50-/Re0MyD6BV8h81wBPVijGZH5GBs\"",
19521
- "mtime": "2026-10-08T11:44:03.927Z",
21469
+ "mtime": "2026-10-12T02:43:06.047Z",
19522
21470
  "size": 7504,
19523
21471
  "path": "../public/_nuxt/jetbrains-mono-vietnamese-wght-normal.Bt-aOZkq.woff2"
19524
21472
  },
19525
- "/_nuxt/whFLy_Ne.js": {
19526
- "type": "text/javascript; charset=utf-8",
19527
- "etag": "\"10fef-Fe8e6U4rNb//7sdPdQau3Trl7fU\"",
19528
- "mtime": "2026-10-08T11:44:03.927Z",
19529
- "size": 69615,
19530
- "path": "../public/_nuxt/whFLy_Ne.js"
19531
- },
19532
- "/_nuxt/entry.cKR16VqG.css": {
19533
- "type": "text/css; charset=utf-8",
19534
- "etag": "\"5f50e-lGjGgrLDJlABD7dGL+/Cu4UfQi8\"",
19535
- "mtime": "2026-10-08T11:44:03.919Z",
19536
- "size": 390414,
19537
- "path": "../public/_nuxt/entry.cKR16VqG.css"
21473
+ "/_nuxt/wherdr-nerd-awesome.DQ3_iAfa.woff2": {
21474
+ "type": "font/woff2",
21475
+ "etag": "\"30178-02mpQKEDb74Ec8LtlhKceB1vHO8\"",
21476
+ "mtime": "2026-10-12T02:43:06.047Z",
21477
+ "size": 196984,
21478
+ "path": "../public/_nuxt/wherdr-nerd-awesome.DQ3_iAfa.woff2"
19538
21479
  },
19539
21480
  "/_nuxt/wherdr-nerd-codicons.CtEsfSAf.woff2": {
19540
21481
  "type": "font/woff2",
19541
21482
  "etag": "\"d9b0-kmO/x6KCTFRlVipOvl+5XcArGUw\"",
19542
- "mtime": "2026-10-08T11:44:03.927Z",
21483
+ "mtime": "2026-10-12T02:43:06.047Z",
19543
21484
  "size": 55728,
19544
21485
  "path": "../public/_nuxt/wherdr-nerd-codicons.CtEsfSAf.woff2"
19545
21486
  },
19546
- "/_nuxt/wherdr-nerd-awesome.DQ3_iAfa.woff2": {
19547
- "type": "font/woff2",
19548
- "etag": "\"30178-02mpQKEDb74Ec8LtlhKceB1vHO8\"",
19549
- "mtime": "2026-10-08T11:44:03.927Z",
19550
- "size": 196984,
19551
- "path": "../public/_nuxt/wherdr-nerd-awesome.DQ3_iAfa.woff2"
19552
- },
19553
21487
  "/_nuxt/wherdr-nerd-extension.Dai_ky39.woff2": {
19554
21488
  "type": "font/woff2",
19555
21489
  "etag": "\"f76c-CjERGtjtOrkpSXy7JyZNl1CAu0U\"",
19556
- "mtime": "2026-10-08T11:44:03.927Z",
21490
+ "mtime": "2026-10-12T02:43:06.047Z",
19557
21491
  "size": 63340,
19558
21492
  "path": "../public/_nuxt/wherdr-nerd-extension.Dai_ky39.woff2"
19559
21493
  },
19560
21494
  "/_nuxt/wherdr-nerd-devicons.ZMSFX1sW.woff2": {
19561
21495
  "type": "font/woff2",
19562
21496
  "etag": "\"30f78-2RuGTMLnbTBdhlI9ExApyiUeD8c\"",
19563
- "mtime": "2026-10-08T11:44:03.927Z",
21497
+ "mtime": "2026-10-12T02:43:06.047Z",
19564
21498
  "size": 200568,
19565
21499
  "path": "../public/_nuxt/wherdr-nerd-devicons.ZMSFX1sW.woff2"
19566
21500
  },
21501
+ "/_nuxt/wherdr-nerd-material-1.BdvqkCqW.woff2": {
21502
+ "type": "font/woff2",
21503
+ "etag": "\"1e130-1GKpbVoJoRf3AxaKTZS8FjxkOd4\"",
21504
+ "mtime": "2026-10-12T02:43:06.047Z",
21505
+ "size": 123184,
21506
+ "path": "../public/_nuxt/wherdr-nerd-material-1.BdvqkCqW.woff2"
21507
+ },
19567
21508
  "/_nuxt/wherdr-nerd-material-2.C_flbBWO.woff2": {
19568
21509
  "type": "font/woff2",
19569
21510
  "etag": "\"1f95c-q6dAP964z2dgEvWzAYjTAzBtzBc\"",
19570
- "mtime": "2026-10-08T11:44:03.928Z",
21511
+ "mtime": "2026-10-12T02:43:06.047Z",
19571
21512
  "size": 129372,
19572
21513
  "path": "../public/_nuxt/wherdr-nerd-material-2.C_flbBWO.woff2"
19573
21514
  },
19574
- "/_nuxt/wherdr-nerd-material-3.8xz7iX-Y.woff2": {
19575
- "type": "font/woff2",
19576
- "etag": "\"2248c-3GBJ1KjkT+xYPhza9V/DKaILa7Y\"",
19577
- "mtime": "2026-10-08T11:44:03.928Z",
19578
- "size": 140428,
19579
- "path": "../public/_nuxt/wherdr-nerd-material-3.8xz7iX-Y.woff2"
19580
- },
19581
- "/_nuxt/wherdr-nerd-material-1.BdvqkCqW.woff2": {
19582
- "type": "font/woff2",
19583
- "etag": "\"1e130-1GKpbVoJoRf3AxaKTZS8FjxkOd4\"",
19584
- "mtime": "2026-10-08T11:44:03.927Z",
19585
- "size": 123184,
19586
- "path": "../public/_nuxt/wherdr-nerd-material-1.BdvqkCqW.woff2"
21515
+ "/_nuxt/wherdr-nerd-material-3.8xz7iX-Y.woff2": {
21516
+ "type": "font/woff2",
21517
+ "etag": "\"2248c-3GBJ1KjkT+xYPhza9V/DKaILa7Y\"",
21518
+ "mtime": "2026-10-12T02:43:06.047Z",
21519
+ "size": 140428,
21520
+ "path": "../public/_nuxt/wherdr-nerd-material-3.8xz7iX-Y.woff2"
19587
21521
  },
19588
21522
  "/_nuxt/wherdr-nerd-material-4.CO_PudDR.woff2": {
19589
21523
  "type": "font/woff2",
19590
21524
  "etag": "\"1fa80-YcJvpFD/M3vTC4y6SMmp7s8a9gw\"",
19591
- "mtime": "2026-10-08T11:44:03.928Z",
21525
+ "mtime": "2026-10-12T02:43:06.047Z",
19592
21526
  "size": 129664,
19593
21527
  "path": "../public/_nuxt/wherdr-nerd-material-4.CO_PudDR.woff2"
19594
21528
  },
19595
- "/_nuxt/wherdr-nerd-powerline.B_kAMxXr.woff2": {
19596
- "type": "font/woff2",
19597
- "etag": "\"1bc0-iXm39wQwNbm2VjpVBOn72grfku0\"",
19598
- "mtime": "2026-10-08T11:44:03.928Z",
19599
- "size": 7104,
19600
- "path": "../public/_nuxt/wherdr-nerd-powerline.B_kAMxXr.woff2"
19601
- },
19602
21529
  "/_nuxt/wherdr-nerd-octicons.Hl4VEx8m.woff2": {
19603
21530
  "type": "font/woff2",
19604
21531
  "etag": "\"f890-FkAsW3iqENK+lU5UlSze0yqnE2s\"",
19605
- "mtime": "2026-10-08T11:44:03.928Z",
21532
+ "mtime": "2026-10-12T02:43:06.047Z",
19606
21533
  "size": 63632,
19607
21534
  "path": "../public/_nuxt/wherdr-nerd-octicons.Hl4VEx8m.woff2"
19608
21535
  },
21536
+ "/_nuxt/wherdr-nerd-powerline.B_kAMxXr.woff2": {
21537
+ "type": "font/woff2",
21538
+ "etag": "\"1bc0-iXm39wQwNbm2VjpVBOn72grfku0\"",
21539
+ "mtime": "2026-10-12T02:43:06.047Z",
21540
+ "size": 7104,
21541
+ "path": "../public/_nuxt/wherdr-nerd-powerline.B_kAMxXr.woff2"
21542
+ },
19609
21543
  "/_nuxt/wherdr-symbols.C1LsPKmg.woff2": {
19610
21544
  "type": "font/woff2",
19611
21545
  "etag": "\"1304-Lwyr1XiDl3FK38nmS/6Pk9wfiWc\"",
19612
- "mtime": "2026-10-08T11:44:03.928Z",
21546
+ "mtime": "2026-10-12T02:43:06.048Z",
19613
21547
  "size": 4868,
19614
21548
  "path": "../public/_nuxt/wherdr-symbols.C1LsPKmg.woff2"
19615
21549
  },
19616
21550
  "/_nuxt/yzABg5xl.js": {
19617
21551
  "type": "text/javascript; charset=utf-8",
19618
21552
  "etag": "\"1c49-W7I7waHyF8BfftITNIl1xCbNFQw\"",
19619
- "mtime": "2026-10-08T11:44:03.928Z",
21553
+ "mtime": "2026-10-12T02:43:06.048Z",
19620
21554
  "size": 7241,
19621
21555
  "path": "../public/_nuxt/yzABg5xl.js"
19622
21556
  },
19623
- "/_nuxt/builds/latest.json": {
19624
- "type": "application/json",
19625
- "etag": "\"47-InxxMr1b0NpxElCQDYMExsdTFyM\"",
19626
- "mtime": "2026-10-08T11:44:03.903Z",
19627
- "size": 71,
19628
- "path": "../public/_nuxt/builds/latest.json"
21557
+ "/_nuxt/xYX85SPI.js": {
21558
+ "type": "text/javascript; charset=utf-8",
21559
+ "etag": "\"374e-AaaIqO4sGKcceUeLatrSWJTAfAU\"",
21560
+ "mtime": "2026-10-12T02:43:06.048Z",
21561
+ "size": 14158,
21562
+ "path": "../public/_nuxt/xYX85SPI.js"
19629
21563
  },
19630
- "/_nuxt/builds/meta/6466b0ab-7935-460c-abdf-2ea632dae6d0.json": {
21564
+ "/_nuxt/builds/meta/c735b6c0-1bd0-47d7-a208-d310f0a06287.json": {
19631
21565
  "type": "application/json",
19632
- "etag": "\"58-DWte5uFWPeAlfWNx7Bc31+0PHIM\"",
19633
- "mtime": "2026-10-08T11:44:03.899Z",
21566
+ "etag": "\"58-d/XzFsF7fGOYu7cBbS83s+45DOk\"",
21567
+ "mtime": "2026-10-12T02:43:06.021Z",
19634
21568
  "size": 88,
19635
- "path": "../public/_nuxt/builds/meta/6466b0ab-7935-460c-abdf-2ea632dae6d0.json"
19636
- }
19637
- };
19638
-
19639
- const _DRIVE_LETTER_START_RE = /^[A-Za-z]:\//;
19640
- function normalizeWindowsPath(input = "") {
19641
- if (!input) {
19642
- return input;
19643
- }
19644
- return input.replace(/\\/g, "/").replace(_DRIVE_LETTER_START_RE, (r) => r.toUpperCase());
19645
- }
19646
- const _IS_ABSOLUTE_RE = /^[/\\](?![/\\])|^[/\\]{2}(?!\.)|^[A-Za-z]:[/\\]/;
19647
- const _DRIVE_LETTER_RE = /^[A-Za-z]:$/;
19648
- const _ROOT_FOLDER_RE = /^\/([A-Za-z]:)?$/;
19649
- function cwd() {
19650
- if (typeof process !== "undefined" && typeof process.cwd === "function") {
19651
- return process.cwd().replace(/\\/g, "/");
19652
- }
19653
- return "/";
19654
- }
19655
- const resolve = function(...arguments_) {
19656
- arguments_ = arguments_.map((argument) => normalizeWindowsPath(argument));
19657
- let resolvedPath = "";
19658
- let resolvedAbsolute = false;
19659
- for (let index = arguments_.length - 1; index >= -1 && !resolvedAbsolute; index--) {
19660
- const path = index >= 0 ? arguments_[index] : cwd();
19661
- if (!path || path.length === 0) {
19662
- continue;
19663
- }
19664
- resolvedPath = `${path}/${resolvedPath}`;
19665
- resolvedAbsolute = isAbsolute(path);
19666
- }
19667
- resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute);
19668
- if (resolvedAbsolute && !isAbsolute(resolvedPath)) {
19669
- return `/${resolvedPath}`;
19670
- }
19671
- return resolvedPath.length > 0 ? resolvedPath : ".";
19672
- };
19673
- function normalizeString(path, allowAboveRoot) {
19674
- let res = "";
19675
- let lastSegmentLength = 0;
19676
- let lastSlash = -1;
19677
- let dots = 0;
19678
- let char = null;
19679
- for (let index = 0; index <= path.length; ++index) {
19680
- if (index < path.length) {
19681
- char = path[index];
19682
- } else if (char === "/") {
19683
- break;
19684
- } else {
19685
- char = "/";
19686
- }
19687
- if (char === "/") {
19688
- if (lastSlash === index - 1 || dots === 1) ; else if (dots === 2) {
19689
- if (res.length < 2 || lastSegmentLength !== 2 || res[res.length - 1] !== "." || res[res.length - 2] !== ".") {
19690
- if (res.length > 2) {
19691
- const lastSlashIndex = res.lastIndexOf("/");
19692
- if (lastSlashIndex === -1) {
19693
- res = "";
19694
- lastSegmentLength = 0;
19695
- } else {
19696
- res = res.slice(0, lastSlashIndex);
19697
- lastSegmentLength = res.length - 1 - res.lastIndexOf("/");
19698
- }
19699
- lastSlash = index;
19700
- dots = 0;
19701
- continue;
19702
- } else if (res.length > 0) {
19703
- res = "";
19704
- lastSegmentLength = 0;
19705
- lastSlash = index;
19706
- dots = 0;
19707
- continue;
19708
- }
19709
- }
19710
- if (allowAboveRoot) {
19711
- res += res.length > 0 ? "/.." : "..";
19712
- lastSegmentLength = 2;
19713
- }
19714
- } else {
19715
- if (res.length > 0) {
19716
- res += `/${path.slice(lastSlash + 1, index)}`;
19717
- } else {
19718
- res = path.slice(lastSlash + 1, index);
19719
- }
19720
- lastSegmentLength = index - lastSlash - 1;
19721
- }
19722
- lastSlash = index;
19723
- dots = 0;
19724
- } else if (char === "." && dots !== -1) {
19725
- ++dots;
19726
- } else {
19727
- dots = -1;
19728
- }
19729
- }
19730
- return res;
19731
- }
19732
- const isAbsolute = function(p) {
19733
- return _IS_ABSOLUTE_RE.test(p);
19734
- };
19735
- const relative = function(from, to) {
19736
- const _from = resolve(from).replace(_ROOT_FOLDER_RE, "$1").split("/");
19737
- const _to = resolve(to).replace(_ROOT_FOLDER_RE, "$1").split("/");
19738
- if (_to[0][1] === ":" && _from[0][1] === ":" && _from[0] !== _to[0]) {
19739
- return _to.join("/");
19740
- }
19741
- const _fromCopy = [..._from];
19742
- for (const segment of _fromCopy) {
19743
- if (_to[0] !== segment) {
19744
- break;
19745
- }
19746
- _from.shift();
19747
- _to.shift();
19748
- }
19749
- return [..._from.map(() => ".."), ..._to].join("/");
19750
- };
19751
- const dirname = function(p) {
19752
- const segments = normalizeWindowsPath(p).replace(/\/$/, "").split("/").slice(0, -1);
19753
- if (segments.length === 1 && _DRIVE_LETTER_RE.test(segments[0])) {
19754
- segments[0] += "/";
19755
- }
19756
- return segments.join("/") || (isAbsolute(p) ? "/" : ".");
19757
- };
19758
-
19759
- function readAsset (id) {
19760
- const serverDir = dirname(fileURLToPath(globalThis._importMeta_.url));
19761
- return promises.readFile(resolve(serverDir, assets[id].path))
19762
- }
19763
-
19764
- const publicAssetBases = {"/_nuxt/builds/meta/":{"maxAge":31536000},"/_nuxt/builds/":{"maxAge":1},"/_nuxt/":{"maxAge":31536000}};
19765
-
19766
- function isPublicAssetURL(id = '') {
19767
- if (assets[id]) {
19768
- return true
19769
- }
19770
- for (const base in publicAssetBases) {
19771
- if (id.startsWith(base)) { return true }
19772
- }
19773
- return false
19774
- }
19775
-
19776
- function getAsset (id) {
19777
- return assets[id]
19778
- }
19779
-
19780
- const METHODS = /* @__PURE__ */ new Set(["HEAD", "GET"]);
19781
- const EncodingMap = { gzip: ".gz", br: ".br" };
19782
- const _uN03ln = eventHandler((event) => {
19783
- if (event.method && !METHODS.has(event.method)) {
19784
- return;
19785
- }
19786
- let id = decodePath(
19787
- withLeadingSlash(withoutTrailingSlash(parseURL(event.path).pathname))
19788
- );
19789
- let asset;
19790
- const encodingHeader = String(
19791
- getRequestHeader(event, "accept-encoding") || ""
19792
- );
19793
- const encodings = [
19794
- ...encodingHeader.split(",").map((e) => EncodingMap[e.trim()]).filter(Boolean).sort(),
19795
- ""
19796
- ];
19797
- for (const encoding of encodings) {
19798
- for (const _id of [id + encoding, joinURL(id, "index.html" + encoding)]) {
19799
- const _asset = getAsset(_id);
19800
- if (_asset) {
19801
- asset = _asset;
19802
- id = _id;
19803
- break;
19804
- }
19805
- }
19806
- }
19807
- if (!asset) {
19808
- if (isPublicAssetURL(id)) {
19809
- removeResponseHeader(event, "Cache-Control");
19810
- throw createError$1({ statusCode: 404 });
19811
- }
19812
- return;
19813
- }
19814
- if (asset.encoding !== void 0) {
19815
- appendResponseHeader(event, "Vary", "Accept-Encoding");
19816
- }
19817
- const ifNotMatch = getRequestHeader(event, "if-none-match") === asset.etag;
19818
- if (ifNotMatch) {
19819
- setResponseStatus(event, 304, "Not Modified");
19820
- return "";
19821
- }
19822
- const ifModifiedSinceH = getRequestHeader(event, "if-modified-since");
19823
- const mtimeDate = new Date(asset.mtime);
19824
- if (ifModifiedSinceH && asset.mtime && new Date(ifModifiedSinceH) >= mtimeDate) {
19825
- setResponseStatus(event, 304, "Not Modified");
19826
- return "";
19827
- }
19828
- if (asset.type && !getResponseHeader(event, "Content-Type")) {
19829
- setResponseHeader(event, "Content-Type", asset.type);
19830
- }
19831
- if (asset.etag && !getResponseHeader(event, "ETag")) {
19832
- setResponseHeader(event, "ETag", asset.etag);
19833
- }
19834
- if (asset.mtime && !getResponseHeader(event, "Last-Modified")) {
19835
- setResponseHeader(event, "Last-Modified", mtimeDate.toUTCString());
19836
- }
19837
- if (asset.encoding && !getResponseHeader(event, "Content-Encoding")) {
19838
- setResponseHeader(event, "Content-Encoding", asset.encoding);
19839
- }
19840
- if (asset.size > 0 && !getResponseHeader(event, "Content-Length")) {
19841
- setResponseHeader(event, "Content-Length", asset.size);
19842
- }
19843
- return readAsset(id);
19844
- });
19845
-
19846
- const MAX_SESSION_DAYS = [1, 7, 30, 90, 365];
19847
- const DEFAULT_MAX_SESSION_DAYS = 7;
19848
- const DAY_MS = 24 * 3600 * 1e3;
19849
- function isMaxSessionDays(v) {
19850
- return MAX_SESSION_DAYS.includes(v);
19851
- }
19852
-
19853
- var __defProp = Object.defineProperty;
19854
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
19855
- var __publicField = (obj, key, value) => __defNormalProp(obj, key + "" , value);
19856
- const SESSION_MS = 12 * 3600 * 1e3;
19857
- const RENEW_MS = 10 * 60 * 1e3;
19858
- const CHALLENGE_MS = 5 * 60 * 1e3;
19859
- const AUTH_COOKIE = "hw_session";
19860
- const CHALLENGE_COOKIE = "hw_challenge";
19861
- class AuthError extends Error {
19862
- constructor(code, message) {
19863
- super(message);
19864
- __publicField(this, "code");
19865
- this.code = code;
21569
+ "path": "../public/_nuxt/builds/meta/c735b6c0-1bd0-47d7-a208-d310f0a06287.json"
21570
+ },
21571
+ "/_nuxt/builds/latest.json": {
21572
+ "type": "application/json",
21573
+ "etag": "\"47-tXRY9adXBmru9/K9a4qadfDPmmc\"",
21574
+ "mtime": "2026-10-12T02:43:06.026Z",
21575
+ "size": 71,
21576
+ "path": "../public/_nuxt/builds/latest.json"
19866
21577
  }
19867
- }
19868
- function createAuth({ dataDir, log: log2 = () => {
19869
- }, passkeyUser = "herdr" }) {
19870
- const FILE = path$1.join(dataDir, "auth.json");
19871
- let db = { secret: null, generation: 0, credentials: [] };
19872
- try {
19873
- db = { ...db, ...JSON.parse(fs.readFileSync(FILE, "utf8")) };
19874
- } catch {
21578
+ };
21579
+
21580
+ const _DRIVE_LETTER_START_RE = /^[A-Za-z]:\//;
21581
+ function normalizeWindowsPath(input = "") {
21582
+ if (!input) {
21583
+ return input;
19875
21584
  }
19876
- const save = () => {
19877
- fs.mkdirSync(dataDir, { recursive: true });
19878
- fs.writeFileSync(FILE, JSON.stringify(db, null, 2) + "\n", { mode: 384 });
19879
- };
19880
- if (!db.secret) {
19881
- db.secret = crypto.randomBytes(32).toString("hex");
19882
- save();
21585
+ return input.replace(/\\/g, "/").replace(_DRIVE_LETTER_START_RE, (r) => r.toUpperCase());
21586
+ }
21587
+ const _IS_ABSOLUTE_RE = /^[/\\](?![/\\])|^[/\\]{2}(?!\.)|^[A-Za-z]:[/\\]/;
21588
+ const _DRIVE_LETTER_RE = /^[A-Za-z]:$/;
21589
+ const _ROOT_FOLDER_RE = /^\/([A-Za-z]:)?$/;
21590
+ function cwd() {
21591
+ if (typeof process !== "undefined" && typeof process.cwd === "function") {
21592
+ return process.cwd().replace(/\\/g, "/");
19883
21593
  }
19884
- const bootstrapToken = crypto.randomBytes(24).toString("base64url");
19885
- if (!db.credentials.length) log2(`First passkey: bootstrap token ${bootstrapToken}`);
19886
- const enabled = () => db.credentials.length > 0;
19887
- const sign = (v) => crypto.createHmac("sha256", db.secret).update(v).digest("base64url");
19888
- const safeEq = (a, b) => {
19889
- const x = Buffer.from(String(a));
19890
- const y = Buffer.from(String(b));
19891
- return x.length === y.length && crypto.timingSafeEqual(x, y);
19892
- };
19893
- const readCookie = (req) => {
19894
- const c = String(req.headers.cookie || "").split(/;\s*/).find((s) => s.startsWith(`${AUTH_COOKIE}=`));
19895
- if (!c) return null;
19896
- try {
19897
- return decodeURIComponent(c.slice(AUTH_COOKIE.length + 1));
19898
- } catch {
19899
- return null;
19900
- }
19901
- };
19902
- const challengeClient = (req) => {
19903
- const c = String(req.headers.cookie || "").split(/;\s*/).find((s) => s.startsWith(`${CHALLENGE_COOKIE}=`));
19904
- return (c == null ? void 0 : c.slice(CHALLENGE_COOKIE.length + 1)) || null;
19905
- };
19906
- const checkBootstrap = (body) => {
19907
- if (!enabled() && !safeEq(String((body == null ? void 0 : body.bootstrapToken) || ""), bootstrapToken)) {
19908
- throw new AuthError("bootstrap", "Invalid bootstrap token (see the server logs)");
21594
+ return "/";
21595
+ }
21596
+ const resolve = function(...arguments_) {
21597
+ arguments_ = arguments_.map((argument) => normalizeWindowsPath(argument));
21598
+ let resolvedPath = "";
21599
+ let resolvedAbsolute = false;
21600
+ for (let index = arguments_.length - 1; index >= -1 && !resolvedAbsolute; index--) {
21601
+ const path = index >= 0 ? arguments_[index] : cwd();
21602
+ if (!path || path.length === 0) {
21603
+ continue;
19909
21604
  }
19910
- };
19911
- const maxSessionMs = () => {
19912
- var _a;
19913
- return ((_a = db.maxSessionDays) != null ? _a : DEFAULT_MAX_SESSION_DAYS) * DAY_MS;
19914
- };
19915
- function session(req, now = Date.now()) {
19916
- const v = readCookie(req);
19917
- if (!v) return null;
19918
- const parts = v.split(".");
19919
- if (parts.length !== 3 && parts.length !== 4) return null;
19920
- const mac = parts.pop();
19921
- if (!mac || parts[1] !== String(db.generation) || !safeEq(mac, sign(parts.join(".")))) return null;
19922
- const exp = Number(parts[0]);
19923
- const since = parts.length === 3 ? Number(parts[2]) : exp - SESSION_MS;
19924
- if (!(exp >= now) || !Number.isFinite(since)) return null;
19925
- if (now > since + maxSessionMs() + SESSION_MS) return null;
19926
- return { exp, since };
19927
- }
19928
- function isUnlocked(req) {
19929
- return !enabled() || session(req) !== null;
19930
- }
19931
- const secure = (req) => req.headers["x-forwarded-proto"] === "https" || String(req.headers.origin || "").startsWith("https://") ? "; Secure" : "";
19932
- function sessionCookie(req, now = Date.now(), since = now) {
19933
- const exp = now + SESSION_MS;
19934
- const payload = `${exp}.${db.generation}.${since}`;
19935
- return `${AUTH_COOKIE}=${payload}.${sign(payload)}; Path=/; HttpOnly; SameSite=Strict; Max-Age=${SESSION_MS / 1e3}${secure(req)}`;
19936
- }
19937
- const clearCookie = (req) => `${AUTH_COOKIE}=; Path=/; HttpOnly; SameSite=Strict; Max-Age=0${secure(req)}`;
19938
- function renew(req, now = Date.now()) {
19939
- if (!enabled()) return null;
19940
- const s = session(req, now);
19941
- if (s === null || s.exp - now > SESSION_MS - RENEW_MS) return null;
19942
- return { cookie: sessionCookie(req, now, s.since), expiresAt: now + SESSION_MS };
19943
- }
19944
- function relying(req) {
19945
- const origin = String(req.headers.origin || "");
19946
- const { hostname } = new URL(origin);
19947
- return { origin, rpID: hostname };
19948
- }
19949
- const challenges = /* @__PURE__ */ new Map();
19950
- const challengeKey = (kind, client) => `${kind}:${client}`;
19951
- const issueChallenge = (req, kind, value) => {
19952
- const client = crypto.randomBytes(24).toString("base64url");
19953
- for (const [key, challenge] of challenges) if (challenge.exp < Date.now()) challenges.delete(key);
19954
- challenges.set(challengeKey(kind, client), { value, exp: Date.now() + CHALLENGE_MS });
19955
- return `${CHALLENGE_COOKIE}=${client}; Path=/api/auth/; HttpOnly; SameSite=Strict; Max-Age=${CHALLENGE_MS / 1e3}${secure(req)}`;
19956
- };
19957
- const takeChallenge = (req, kind) => {
19958
- const client = challengeClient(req);
19959
- const key = challengeKey(kind, client || "");
19960
- const c = challenges.get(key);
19961
- challenges.delete(key);
19962
- if (!c || c.exp < Date.now()) throw new AuthError("challenge", "Challenge expired, try again");
19963
- return c.value;
19964
- };
19965
- async function registerOptions(req, body = {}) {
19966
- if (enabled() && !isUnlocked(req)) throw new AuthError("locked", "Unlock first");
19967
- checkBootstrap(body);
19968
- const { rpID } = relying(req);
19969
- const opts = await generateRegistrationOptions({
19970
- rpName: "wherdr",
19971
- rpID,
19972
- userName: passkeyUser,
19973
- userDisplayName: passkeyUser,
19974
- attestationType: "none",
19975
- excludeCredentials: db.credentials.map((c) => ({ id: c.id, transports: c.transports })),
19976
- authenticatorSelection: { residentKey: "preferred", userVerification: "required" }
19977
- });
19978
- return { ...opts, __cookie: issueChallenge(req, "reg", opts.challenge) };
19979
- }
19980
- async function registerVerify(req, body) {
19981
- if (enabled() && !isUnlocked(req)) throw new AuthError("locked", "Unlock first");
19982
- checkBootstrap(body);
19983
- const { origin, rpID } = relying(req);
19984
- const v = await verifyRegistrationResponse({
19985
- response: body.response,
19986
- expectedChallenge: takeChallenge(req, "reg"),
19987
- expectedOrigin: origin,
19988
- expectedRPID: rpID,
19989
- requireUserVerification: true
19990
- });
19991
- if (!v.verified || !v.registrationInfo) throw new AuthError("rejected", "Key rejected");
19992
- const { credential } = v.registrationInfo;
19993
- db.credentials.push({
19994
- id: credential.id,
19995
- publicKey: Buffer.from(credential.publicKey).toString("base64url"),
19996
- counter: credential.counter,
19997
- transports: credential.transports || [],
19998
- name: String(body.name || "Device").slice(0, 40),
19999
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
20000
- });
20001
- save();
20002
- log2(`lock: key registered (${db.credentials.length})`);
20003
- return { ok: true, __cookie: sessionCookie(req) };
20004
- }
20005
- async function loginOptions(req) {
20006
- const { rpID } = relying(req);
20007
- const opts = await generateAuthenticationOptions({
20008
- rpID,
20009
- userVerification: "required",
20010
- allowCredentials: db.credentials.map((c) => ({ id: c.id, transports: c.transports }))
20011
- });
20012
- return { ...opts, __cookie: issueChallenge(req, "auth", opts.challenge) };
20013
- }
20014
- async function loginVerify(req, body) {
20015
- const { origin, rpID } = relying(req);
20016
- const cred = db.credentials.find((c) => c.id === (body.response && body.response.id));
20017
- if (!cred) throw new AuthError("unknown_key", "Key unknown on this server");
20018
- const v = await verifyAuthenticationResponse({
20019
- response: body.response,
20020
- expectedChallenge: takeChallenge(req, "auth"),
20021
- expectedOrigin: origin,
20022
- expectedRPID: rpID,
20023
- requireUserVerification: true,
20024
- credential: { id: cred.id, publicKey: Buffer.from(cred.publicKey, "base64url"), counter: cred.counter, transports: cred.transports }
20025
- });
20026
- if (!v.verified) throw new AuthError("rejected", "Unlock rejected");
20027
- cred.counter = v.authenticationInfo.newCounter;
20028
- cred.lastUsed = (/* @__PURE__ */ new Date()).toISOString();
20029
- save();
20030
- return { ok: true, __cookie: sessionCookie(req) };
21605
+ resolvedPath = `${path}/${resolvedPath}`;
21606
+ resolvedAbsolute = isAbsolute(path);
20031
21607
  }
20032
- function disable(req) {
20033
- if (!isUnlocked(req)) throw new AuthError("locked", "Unlock first");
20034
- db.credentials = [];
20035
- db.generation += 1;
20036
- save();
20037
- log2("lock disabled");
20038
- return { ok: true, __cookie: clearCookie(req) };
21608
+ resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute);
21609
+ if (resolvedAbsolute && !isAbsolute(resolvedPath)) {
21610
+ return `/${resolvedPath}`;
20039
21611
  }
20040
- function lockAll(req) {
20041
- if (!enabled() || !isUnlocked(req)) throw new AuthError("locked", "Unlock first");
20042
- db.generation += 1;
20043
- save();
20044
- log2("lock: all devices locked");
20045
- return { ok: true, __cookie: clearCookie(req) };
21612
+ return resolvedPath.length > 0 ? resolvedPath : ".";
21613
+ };
21614
+ function normalizeString(path, allowAboveRoot) {
21615
+ let res = "";
21616
+ let lastSegmentLength = 0;
21617
+ let lastSlash = -1;
21618
+ let dots = 0;
21619
+ let char = null;
21620
+ for (let index = 0; index <= path.length; ++index) {
21621
+ if (index < path.length) {
21622
+ char = path[index];
21623
+ } else if (char === "/") {
21624
+ break;
21625
+ } else {
21626
+ char = "/";
21627
+ }
21628
+ if (char === "/") {
21629
+ if (lastSlash === index - 1 || dots === 1) ; else if (dots === 2) {
21630
+ if (res.length < 2 || lastSegmentLength !== 2 || res[res.length - 1] !== "." || res[res.length - 2] !== ".") {
21631
+ if (res.length > 2) {
21632
+ const lastSlashIndex = res.lastIndexOf("/");
21633
+ if (lastSlashIndex === -1) {
21634
+ res = "";
21635
+ lastSegmentLength = 0;
21636
+ } else {
21637
+ res = res.slice(0, lastSlashIndex);
21638
+ lastSegmentLength = res.length - 1 - res.lastIndexOf("/");
21639
+ }
21640
+ lastSlash = index;
21641
+ dots = 0;
21642
+ continue;
21643
+ } else if (res.length > 0) {
21644
+ res = "";
21645
+ lastSegmentLength = 0;
21646
+ lastSlash = index;
21647
+ dots = 0;
21648
+ continue;
21649
+ }
21650
+ }
21651
+ if (allowAboveRoot) {
21652
+ res += res.length > 0 ? "/.." : "..";
21653
+ lastSegmentLength = 2;
21654
+ }
21655
+ } else {
21656
+ if (res.length > 0) {
21657
+ res += `/${path.slice(lastSlash + 1, index)}`;
21658
+ } else {
21659
+ res = path.slice(lastSlash + 1, index);
21660
+ }
21661
+ lastSegmentLength = index - lastSlash - 1;
21662
+ }
21663
+ lastSlash = index;
21664
+ dots = 0;
21665
+ } else if (char === "." && dots !== -1) {
21666
+ ++dots;
21667
+ } else {
21668
+ dots = -1;
21669
+ }
20046
21670
  }
20047
- function setMaxSession(req, days) {
20048
- if (!enabled() || !isUnlocked(req)) throw new AuthError("locked", "Unlock first");
20049
- if (!isMaxSessionDays(days)) throw new AuthError("invalid", "Invalid duration");
20050
- db.maxSessionDays = days;
20051
- save();
20052
- return { ok: true, maxSessionDays: days };
21671
+ return res;
21672
+ }
21673
+ const isAbsolute = function(p) {
21674
+ return _IS_ABSOLUTE_RE.test(p);
21675
+ };
21676
+ const relative = function(from, to) {
21677
+ const _from = resolve(from).replace(_ROOT_FOLDER_RE, "$1").split("/");
21678
+ const _to = resolve(to).replace(_ROOT_FOLDER_RE, "$1").split("/");
21679
+ if (_to[0][1] === ":" && _from[0][1] === ":" && _from[0] !== _to[0]) {
21680
+ return _to.join("/");
20053
21681
  }
20054
- function status(req, { resume = false, now = Date.now() } = {}) {
20055
- var _a, _b;
20056
- const devices = db.credentials.map((c) => ({ name: c.name, createdAt: c.createdAt, lastUsed: c.lastUsed || null }));
20057
- if (!enabled()) return { enabled: false, unlocked: true, devices };
20058
- const s = session(req, now);
20059
- const deadline = s ? s.since + maxSessionMs() : 0;
20060
- if (!s || resume && now >= deadline) {
20061
- return { enabled: true, unlocked: false, expiresAt: null, now, devices, ...s ? { __cookie: clearCookie(req) } : {} };
21682
+ const _fromCopy = [..._from];
21683
+ for (const segment of _fromCopy) {
21684
+ if (_to[0] !== segment) {
21685
+ break;
20062
21686
  }
20063
- const renewed = renew(req, now);
20064
- return {
20065
- enabled: true,
20066
- unlocked: true,
20067
- expiresAt: (_a = renewed == null ? void 0 : renewed.expiresAt) != null ? _a : s.exp,
20068
- deadline,
20069
- now,
20070
- maxSessionDays: (_b = db.maxSessionDays) != null ? _b : DEFAULT_MAX_SESSION_DAYS,
20071
- devices,
20072
- ...renewed ? { __cookie: renewed.cookie } : {}
20073
- };
21687
+ _from.shift();
21688
+ _to.shift();
20074
21689
  }
20075
- return {
20076
- isUnlocked,
20077
- status,
20078
- renew,
20079
- registerOptions,
20080
- registerVerify,
20081
- loginOptions,
20082
- loginVerify,
20083
- disable,
20084
- lockAll,
20085
- setMaxSession,
20086
- lock: (req) => ({ ok: true, __cookie: clearCookie(req) }),
20087
- // For tests.
20088
- _sessionCookie: sessionCookie,
20089
- _db: () => db,
20090
- _bootstrapToken: () => bootstrapToken,
20091
- _takeChallenge: takeChallenge
20092
- };
21690
+ return [..._from.map(() => ".."), ..._to].join("/");
21691
+ };
21692
+ const dirname = function(p) {
21693
+ const segments = normalizeWindowsPath(p).replace(/\/$/, "").split("/").slice(0, -1);
21694
+ if (segments.length === 1 && _DRIVE_LETTER_RE.test(segments[0])) {
21695
+ segments[0] += "/";
21696
+ }
21697
+ return segments.join("/") || (isAbsolute(p) ? "/" : ".");
21698
+ };
21699
+
21700
+ function readAsset (id) {
21701
+ const serverDir = dirname(fileURLToPath(globalThis._importMeta_.url));
21702
+ return promises.readFile(resolve(serverDir, assets[id].path))
20093
21703
  }
20094
21704
 
20095
- const auth = createAuth({ dataDir: DATA_DIR, log, passkeyUser: PASSKEY_USER });
20096
- const reqOf = (event) => ({ headers: getRequestHeaders(event) });
20097
- function sameOrigin(headers) {
20098
- const o = headers.origin;
20099
- if (!o) return false;
20100
- try {
20101
- return new URL(o).host === headers.host;
20102
- } catch {
20103
- return false;
21705
+ const publicAssetBases = {"/_nuxt/builds/meta/":{"maxAge":31536000},"/_nuxt/builds/":{"maxAge":1},"/_nuxt/":{"maxAge":31536000}};
21706
+
21707
+ function isPublicAssetURL(id = '') {
21708
+ if (assets[id]) {
21709
+ return true
20104
21710
  }
21711
+ for (const base in publicAssetBases) {
21712
+ if (id.startsWith(base)) { return true }
21713
+ }
21714
+ return false
20105
21715
  }
20106
- const BODY_MAX = 256 * 1024;
20107
- function sendError(event, status, body) {
20108
- setResponseStatus(event, status);
20109
- setResponseHeader(event, "cache-control", "no-store");
20110
- return body;
21716
+
21717
+ function getAsset (id) {
21718
+ return assets[id]
20111
21719
  }
20112
- function defineApi(fn, opts = {}) {
20113
- return defineEventHandler(async (event) => {
20114
- const url = getRequestURL(event);
20115
- try {
20116
- let body = {};
20117
- if (event.method === "POST") {
20118
- const headers = getRequestHeaders(event);
20119
- const ctype = String(headers["content-type"] || "");
20120
- const typeOk = opts.raw ? opts.raw.test(ctype) : ctype.startsWith("application/json");
20121
- if (!sameOrigin(headers) || !typeOk) return sendError(event, 403, { error: "Origin refused" });
20122
- const max = opts.raw ? opts.max || 20 * 1024 * 1024 : BODY_MAX;
20123
- const tooLarge = opts.raw ? opts.tooLarge || "Image too large (20 MB max)" : "Request too large";
20124
- if (Number(headers["content-length"] || 0) > max) throw new HerdrError("too_large", tooLarge);
20125
- const raw = await readRawBody(event, false);
20126
- if (raw && raw.length > max) throw new HerdrError("too_large", tooLarge);
20127
- if (opts.raw) body = { data: raw || Buffer.alloc(0), ctype };
20128
- else {
20129
- try {
20130
- body = raw && raw.length ? JSON.parse(raw.toString("utf8")) : {};
20131
- } catch {
20132
- throw new HerdrError("bad_json", "Invalid JSON");
20133
- }
20134
- }
20135
- }
20136
- const out = await fn(event, body);
20137
- setResponseHeader(event, "cache-control", "no-store");
20138
- if (out && typeof out === "object" && "__cookie" in out) {
20139
- const o = out;
20140
- setResponseHeader(event, "set-cookie", String(o.__cookie));
20141
- delete o.__cookie;
21720
+
21721
+ const METHODS = /* @__PURE__ */ new Set(["HEAD", "GET"]);
21722
+ const EncodingMap = { gzip: ".gz", br: ".br" };
21723
+ const _uN03ln = eventHandler((event) => {
21724
+ if (event.method && !METHODS.has(event.method)) {
21725
+ return;
21726
+ }
21727
+ let id = decodePath(
21728
+ withLeadingSlash(withoutTrailingSlash(parseURL(event.path).pathname))
21729
+ );
21730
+ let asset;
21731
+ const encodingHeader = String(
21732
+ getRequestHeader(event, "accept-encoding") || ""
21733
+ );
21734
+ const encodings = [
21735
+ ...encodingHeader.split(",").map((e) => EncodingMap[e.trim()]).filter(Boolean).sort(),
21736
+ ""
21737
+ ];
21738
+ for (const encoding of encodings) {
21739
+ for (const _id of [id + encoding, joinURL(id, "index.html" + encoding)]) {
21740
+ const _asset = getAsset(_id);
21741
+ if (_asset) {
21742
+ asset = _asset;
21743
+ id = _id;
21744
+ break;
20142
21745
  }
20143
- return out;
20144
- } catch (e) {
20145
- const err = e;
20146
- const known = e instanceof HerdrError || url.pathname.startsWith("/api/auth/") && Boolean(err.code);
20147
- const code = known ? err.code : "internal";
20148
- if (!known) log("error", event.method, url.pathname, e);
20149
- const status = err.code === "unreachable" ? 503 : err.code === "locked" ? 401 : 400;
20150
- return sendError(event, status, { error: err.message, code });
20151
21746
  }
20152
- });
20153
- }
20154
- function sendImage(event, img) {
20155
- setResponseHeader(event, "content-type", img.type);
20156
- setResponseHeader(event, "cache-control", "private, max-age=86400");
20157
- setResponseHeader(event, "x-content-type-options", "nosniff");
20158
- setResponseHeader(event, "content-security-policy", "default-src 'none'; style-src 'unsafe-inline'; sandbox");
20159
- return img.body;
20160
- }
20161
-
20162
- function hostname(value) {
20163
- try {
20164
- if (!value || /[\s/@?#]/.test(value)) return null;
20165
- const url = new URL(`http://${value}`);
20166
- if (url.username || url.password || url.pathname !== "/" || url.search || url.hash) return null;
20167
- return url.hostname.replace(/^\[|\]$/g, "").toLowerCase().replace(/\.$/, "");
20168
- } catch {
20169
- return null;
20170
21747
  }
20171
- }
20172
- function allowedHosts(appUrl2 = process.env.APP_URL || "", extra = process.env.HERDR_WEB_ALLOWED_HOSTS || "", interfaces = os.networkInterfaces()) {
20173
- const names = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1", os.hostname().toLowerCase()]);
20174
- try {
20175
- if (appUrl2) names.add(new URL(appUrl2).hostname.toLowerCase().replace(/^\[|\]$/g, ""));
20176
- } catch {
21748
+ if (!asset) {
21749
+ if (isPublicAssetURL(id)) {
21750
+ removeResponseHeader(event, "Cache-Control");
21751
+ throw createError$1({ statusCode: 404 });
21752
+ }
21753
+ return;
20177
21754
  }
20178
- for (const addresses of Object.values(interfaces)) {
20179
- for (const address of addresses || []) names.add(address.address.toLowerCase().replace(/%.+$/, ""));
21755
+ if (asset.encoding !== void 0) {
21756
+ appendResponseHeader(event, "Vary", "Accept-Encoding");
20180
21757
  }
20181
- for (const item of extra.split(",")) {
20182
- const name = hostname(item.trim());
20183
- if (name) names.add(name);
21758
+ const ifNotMatch = getRequestHeader(event, "if-none-match") === asset.etag;
21759
+ if (ifNotMatch) {
21760
+ setResponseStatus(event, 304, "Not Modified");
21761
+ return "";
20184
21762
  }
20185
- return names;
20186
- }
20187
- function hostAllowed(host, names = allowedHosts()) {
20188
- const name = hostname(host || "");
20189
- return Boolean(name && names.has(name));
20190
- }
20191
- function hostRefusalIsHtml(path, accept) {
20192
- return !path.startsWith("/api/") && /\btext\/html\b/i.test(Array.isArray(accept) ? accept.join(",") : accept || "");
20193
- }
20194
- const HOST_REFUSED_PAGE = `<!doctype html>
20195
- <html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
20196
- <meta name="robots" content="noindex"><title>wherdr \xB7 address not enabled</title>
20197
- <style>
20198
- :root{color-scheme:dark}*{box-sizing:border-box}
20199
- body{margin:0;min-height:100vh;display:grid;place-items:center;background:#0d0f12;color:#e8eaed;font:15px/1.5 Inter,system-ui,-apple-system,sans-serif;padding:24px max(16px,env(safe-area-inset-right)) 24px max(16px,env(safe-area-inset-left))}
20200
- main{max-width:440px;border:1px solid #3a3f47;padding:24px}
20201
- .tag{font:11px/1 "JetBrains Mono",ui-monospace,monospace;letter-spacing:.08em;text-transform:uppercase;color:#9aa0a8;margin:0 0 16px}
20202
- h1{font:600 20px/1.25 Archivo,system-ui,sans-serif;margin:0 0 8px}
20203
- p{margin:0 0 8px}hr{border:0;border-top:1px solid #3a3f47;margin:20px 0}
20204
- </style></head><body><main>
20205
- <p class="tag">wherdr \xB7 403</p>
20206
- <h1>This address isn't enabled yet.</h1>
20207
- <p>On the computer, open wherdr \u2192 Settings \u203A Phone (or press Check), then reload.</p>
20208
- <hr>
20209
- <h1 lang="fr">Cette adresse n\u2019est pas encore activ\xE9e.</h1>
20210
- <p lang="fr">Sur l\u2019ordinateur, ouvre wherdr \u2192 R\xE9glages \u203A T\xE9l\xE9phone (ou appuie sur V\xE9rifier), puis recharge.</p>
20211
- </main></body></html>
20212
- `;
20213
- function crossSiteRequest(headers) {
20214
- const site = headers["sec-fetch-site"];
20215
- return typeof site === "string" && site !== "same-origin" && site !== "none";
20216
- }
21763
+ const ifModifiedSinceH = getRequestHeader(event, "if-modified-since");
21764
+ const mtimeDate = new Date(asset.mtime);
21765
+ if (ifModifiedSinceH && asset.mtime && new Date(ifModifiedSinceH) >= mtimeDate) {
21766
+ setResponseStatus(event, 304, "Not Modified");
21767
+ return "";
21768
+ }
21769
+ if (asset.type && !getResponseHeader(event, "Content-Type")) {
21770
+ setResponseHeader(event, "Content-Type", asset.type);
21771
+ }
21772
+ if (asset.etag && !getResponseHeader(event, "ETag")) {
21773
+ setResponseHeader(event, "ETag", asset.etag);
21774
+ }
21775
+ if (asset.mtime && !getResponseHeader(event, "Last-Modified")) {
21776
+ setResponseHeader(event, "Last-Modified", mtimeDate.toUTCString());
21777
+ }
21778
+ if (asset.encoding && !getResponseHeader(event, "Content-Encoding")) {
21779
+ setResponseHeader(event, "Content-Encoding", asset.encoding);
21780
+ }
21781
+ if (asset.size > 0 && !getResponseHeader(event, "Content-Length")) {
21782
+ setResponseHeader(event, "Content-Length", asset.size);
21783
+ }
21784
+ return readAsset(id);
21785
+ });
20217
21786
 
20218
- const needsUnlock = (p) => p.startsWith("/api/") && !p.startsWith("/api/auth/") || p.startsWith("/uploads/");
20219
- const _M8hgLp = defineEventHandler((event) => {
21787
+ const HOST_RECHECK_MS = 3e3;
21788
+ const needsUnlock = (p) => p.startsWith("/api/") && !p.startsWith("/api/auth/") && p !== "/api/health" || p.startsWith("/uploads/");
21789
+ const _M8hgLp = defineEventHandler(async (event) => {
21790
+ if (!hostAllowed(event.node.req.headers.host)) await adoptServed(HOST_RECHECK_MS);
20220
21791
  if (!hostAllowed(event.node.req.headers.host)) {
20221
21792
  if (hostRefusalIsHtml(event.path.split("?")[0], event.node.req.headers.accept)) {
20222
21793
  setResponseStatus(event, 403);
20223
21794
  setResponseHeader(event, "content-type", "text/html; charset=utf-8");
20224
21795
  setResponseHeader(event, "cache-control", "no-store");
20225
- return HOST_REFUSED_PAGE;
21796
+ return hostRefusedPage(void 0, event.node.req.headers.host);
20226
21797
  }
20227
21798
  return sendError(event, 403, { error: "Host not allowed", code: "host" });
20228
21799
  }
@@ -20254,6 +21825,7 @@ const _lazy_4__oTm = () => import('../routes/api/auth/max-session.post.mjs');
20254
21825
  const _lazy_7O3TsZ = () => import('../routes/api/auth/register/options.post.mjs');
20255
21826
  const _lazy_fQByfS = () => import('../routes/api/auth/register/verify.post.mjs');
20256
21827
  const _lazy_jcfnWB = () => import('../routes/api/auth/status.get.mjs');
21828
+ const _lazy_DwN9dC = () => import('../routes/api/board.get.mjs');
20257
21829
  const _lazy_WhNb61 = () => import('../routes/api/changes.get.mjs');
20258
21830
  const _lazy_hdSKYJ = () => import('../routes/api/chat.get.mjs');
20259
21831
  const _lazy_WoSEa6 = () => import('../routes/api/chat/image.get.mjs');
@@ -20265,11 +21837,16 @@ const _lazy_GF2Rhb = () => import('../routes/api/codex-update.post.mjs');
20265
21837
  const _lazy_onTn85 = () => import('../routes/api/commands.get.mjs');
20266
21838
  const _lazy_XuUgLO = () => import('../routes/api/config.get.mjs');
20267
21839
  const _lazy_y_su18 = () => import('../routes/api/dirs.get.mjs');
21840
+ const _lazy_l7QdCS = () => import('../routes/api/dismiss-stopped.post.mjs');
20268
21841
  const _lazy_Z6oqjB = () => import('../routes/api/dismiss.post.mjs');
20269
21842
  const _lazy_2uy6Z9 = () => import('../routes/api/effort.post.mjs');
20270
21843
  const _lazy_7FhJOq = () => import('../routes/api/efforts.get.mjs');
20271
21844
  const _lazy_Wg_MPT = () => import('../routes/api/file.post.mjs');
21845
+ const _lazy_vy5jzJ = () => import('../routes/api/files.get.mjs');
21846
+ const _lazy_iD5EkU = () => import('../routes/api/files/download.get.mjs');
21847
+ const _lazy_TecKdl = () => import('../routes/api/files/read.get.mjs');
20272
21848
  const _lazy_kEWyNy = () => import('../routes/api/gitroot.get.mjs');
21849
+ const _lazy_1Vqvr9 = () => import('../routes/api/health.get.mjs');
20273
21850
  const _lazy_U4OEhz = () => import('../routes/api/input.post.mjs');
20274
21851
  const _lazy_PDyMr9 = () => import('../routes/api/interrupt.post.mjs');
20275
21852
  const _lazy_mtPGaX = () => import('../routes/api/isgit.get.mjs');
@@ -20293,8 +21870,15 @@ const _lazy_mhIdo4 = () => import('../routes/api/plugins/projects.get.mjs');
20293
21870
  const _lazy_zH9tge = () => import('../routes/api/plugins/projects.post.mjs');
20294
21871
  const _lazy_qE4HMW = () => import('../routes/api/plugins/thread-limits.get.mjs');
20295
21872
  const _lazy_ByJj04 = () => import('../routes/api/plugins/thread-limits.post.mjs');
21873
+ const _lazy_VZ7yeT = () => import('../routes/api/project/inbox.get.mjs');
21874
+ const _lazy_wq4Jn3 = () => import('../routes/api/project/inbox.post.mjs');
20296
21875
  const _lazy_WLg2Uu = () => import('../routes/api/index.get.mjs');
21876
+ const _lazy_LkLunk = () => import('../routes/api/project/memory.get.mjs');
20297
21877
  const _lazy_sQe2ZF = () => import('../routes/api/project/report.get.mjs');
21878
+ const _lazy_jP59VB = () => import('../routes/api/project/routines.get.mjs');
21879
+ const _lazy_do1380 = () => import('../routes/api/project/settings.get.mjs');
21880
+ const _lazy_6EuDYh = () => import('../routes/api/project/thread.get.mjs');
21881
+ const _lazy_qZGRKl = () => import('../routes/api/project/thread.post.mjs');
20298
21882
  const _lazy_wxPObX = () => import('../routes/api/prompt.post.mjs');
20299
21883
  const _lazy_gHqHgE = () => import('../routes/api/push/quiet.post.mjs');
20300
21884
  const _lazy_vXOoN7 = () => import('../routes/api/push/subscribe.post.mjs');
@@ -20316,6 +21900,8 @@ const _lazy_TqhtLZ = () => import('../routes/api/space.post.mjs');
20316
21900
  const _lazy_oeuOKk = () => import('../routes/api/state.get.mjs');
20317
21901
  const _lazy_ecbrXA = () => import('../routes/api/unqueue.post.mjs');
20318
21902
  const _lazy_AUmWqe = () => import('../routes/api/update.get.mjs');
21903
+ const _lazy_1DfWY6 = () => import('../routes/api/update.post.mjs');
21904
+ const _lazy_lxIhkr = () => import('../routes/api/update/status.get.mjs');
20319
21905
  const _lazy_5f22vB = () => import('../routes/api/upload.post.mjs');
20320
21906
  const _lazy_9iwHBL = () => import('../routes/api/worktrees.get.mjs');
20321
21907
  const _lazy_IA8rJi = () => import('../routes/api/worktrees/remove.post.mjs');
@@ -20324,7 +21910,6 @@ const _lazy_2a3aHZ = () => import('../routes/icons/_name_.get.mjs');
20324
21910
  const _lazy_MpH2Yl = () => import('../routes/uploads/_...name_.get.mjs');
20325
21911
  const _lazy_hSqSXW = () => import('../routes/ws/_..._.mjs');
20326
21912
  const _lazy_N9wZe_ = () => import('../routes/ws/events.mjs');
20327
- const _lazy_CXgyQE = () => import('../routes/ws/mirror.mjs');
20328
21913
  const _lazy_Stovcz = () => import('../routes/ws/term.mjs');
20329
21914
  const _lazy_9gHabX = () => import('../routes/renderer.mjs');
20330
21915
 
@@ -20344,6 +21929,7 @@ const handlers = [
20344
21929
  { route: '/api/auth/register/options', handler: _lazy_7O3TsZ, lazy: true, middleware: false, method: "post" },
20345
21930
  { route: '/api/auth/register/verify', handler: _lazy_fQByfS, lazy: true, middleware: false, method: "post" },
20346
21931
  { route: '/api/auth/status', handler: _lazy_jcfnWB, lazy: true, middleware: false, method: "get" },
21932
+ { route: '/api/board', handler: _lazy_DwN9dC, lazy: true, middleware: false, method: "get" },
20347
21933
  { route: '/api/changes', handler: _lazy_WhNb61, lazy: true, middleware: false, method: "get" },
20348
21934
  { route: '/api/chat', handler: _lazy_hdSKYJ, lazy: true, middleware: false, method: "get" },
20349
21935
  { route: '/api/chat/image', handler: _lazy_WoSEa6, lazy: true, middleware: false, method: "get" },
@@ -20355,11 +21941,16 @@ const handlers = [
20355
21941
  { route: '/api/commands', handler: _lazy_onTn85, lazy: true, middleware: false, method: "get" },
20356
21942
  { route: '/api/config', handler: _lazy_XuUgLO, lazy: true, middleware: false, method: "get" },
20357
21943
  { route: '/api/dirs', handler: _lazy_y_su18, lazy: true, middleware: false, method: "get" },
21944
+ { route: '/api/dismiss-stopped', handler: _lazy_l7QdCS, lazy: true, middleware: false, method: "post" },
20358
21945
  { route: '/api/dismiss', handler: _lazy_Z6oqjB, lazy: true, middleware: false, method: "post" },
20359
21946
  { route: '/api/effort', handler: _lazy_2uy6Z9, lazy: true, middleware: false, method: "post" },
20360
21947
  { route: '/api/efforts', handler: _lazy_7FhJOq, lazy: true, middleware: false, method: "get" },
20361
21948
  { route: '/api/file', handler: _lazy_Wg_MPT, lazy: true, middleware: false, method: "post" },
21949
+ { route: '/api/files', handler: _lazy_vy5jzJ, lazy: true, middleware: false, method: "get" },
21950
+ { route: '/api/files/download', handler: _lazy_iD5EkU, lazy: true, middleware: false, method: "get" },
21951
+ { route: '/api/files/read', handler: _lazy_TecKdl, lazy: true, middleware: false, method: "get" },
20362
21952
  { route: '/api/gitroot', handler: _lazy_kEWyNy, lazy: true, middleware: false, method: "get" },
21953
+ { route: '/api/health', handler: _lazy_1Vqvr9, lazy: true, middleware: false, method: "get" },
20363
21954
  { route: '/api/input', handler: _lazy_U4OEhz, lazy: true, middleware: false, method: "post" },
20364
21955
  { route: '/api/interrupt', handler: _lazy_PDyMr9, lazy: true, middleware: false, method: "post" },
20365
21956
  { route: '/api/isgit', handler: _lazy_mtPGaX, lazy: true, middleware: false, method: "get" },
@@ -20383,8 +21974,15 @@ const handlers = [
20383
21974
  { route: '/api/plugins/projects', handler: _lazy_zH9tge, lazy: true, middleware: false, method: "post" },
20384
21975
  { route: '/api/plugins/thread-limits', handler: _lazy_qE4HMW, lazy: true, middleware: false, method: "get" },
20385
21976
  { route: '/api/plugins/thread-limits', handler: _lazy_ByJj04, lazy: true, middleware: false, method: "post" },
21977
+ { route: '/api/project/inbox', handler: _lazy_VZ7yeT, lazy: true, middleware: false, method: "get" },
21978
+ { route: '/api/project/inbox', handler: _lazy_wq4Jn3, lazy: true, middleware: false, method: "post" },
20386
21979
  { route: '/api/project', handler: _lazy_WLg2Uu, lazy: true, middleware: false, method: "get" },
21980
+ { route: '/api/project/memory', handler: _lazy_LkLunk, lazy: true, middleware: false, method: "get" },
20387
21981
  { route: '/api/project/report', handler: _lazy_sQe2ZF, lazy: true, middleware: false, method: "get" },
21982
+ { route: '/api/project/routines', handler: _lazy_jP59VB, lazy: true, middleware: false, method: "get" },
21983
+ { route: '/api/project/settings', handler: _lazy_do1380, lazy: true, middleware: false, method: "get" },
21984
+ { route: '/api/project/thread', handler: _lazy_6EuDYh, lazy: true, middleware: false, method: "get" },
21985
+ { route: '/api/project/thread', handler: _lazy_qZGRKl, lazy: true, middleware: false, method: "post" },
20388
21986
  { route: '/api/prompt', handler: _lazy_wxPObX, lazy: true, middleware: false, method: "post" },
20389
21987
  { route: '/api/push/quiet', handler: _lazy_gHqHgE, lazy: true, middleware: false, method: "post" },
20390
21988
  { route: '/api/push/subscribe', handler: _lazy_vXOoN7, lazy: true, middleware: false, method: "post" },
@@ -20406,6 +22004,8 @@ const handlers = [
20406
22004
  { route: '/api/state', handler: _lazy_oeuOKk, lazy: true, middleware: false, method: "get" },
20407
22005
  { route: '/api/unqueue', handler: _lazy_ecbrXA, lazy: true, middleware: false, method: "post" },
20408
22006
  { route: '/api/update', handler: _lazy_AUmWqe, lazy: true, middleware: false, method: "get" },
22007
+ { route: '/api/update', handler: _lazy_1DfWY6, lazy: true, middleware: false, method: "post" },
22008
+ { route: '/api/update/status', handler: _lazy_lxIhkr, lazy: true, middleware: false, method: "get" },
20409
22009
  { route: '/api/upload', handler: _lazy_5f22vB, lazy: true, middleware: false, method: "post" },
20410
22010
  { route: '/api/worktrees', handler: _lazy_9iwHBL, lazy: true, middleware: false, method: "get" },
20411
22011
  { route: '/api/worktrees/remove', handler: _lazy_IA8rJi, lazy: true, middleware: false, method: "post" },
@@ -20414,7 +22014,6 @@ const handlers = [
20414
22014
  { route: '/uploads/**:name', handler: _lazy_MpH2Yl, lazy: true, middleware: false, method: "get" },
20415
22015
  { route: '/ws/**', handler: _lazy_hSqSXW, lazy: true, middleware: false, method: undefined },
20416
22016
  { route: '/ws/events', handler: _lazy_N9wZe_, lazy: true, middleware: false, method: undefined },
20417
- { route: '/ws/mirror', handler: _lazy_CXgyQE, lazy: true, middleware: false, method: undefined },
20418
22017
  { route: '/ws/term', handler: _lazy_Stovcz, lazy: true, middleware: false, method: undefined },
20419
22018
  { route: '/__nuxt_error', handler: _lazy_9gHabX, lazy: true, middleware: false, method: undefined },
20420
22019
  { route: '/__nuxt_island/**', handler: _SxA8c9, lazy: false, middleware: false, method: undefined },
@@ -20877,5 +22476,5 @@ setupGracefulShutdown(listener, nitroApp);
20877
22476
  }
20878
22477
  const nodeServer = {};
20879
22478
 
20880
- export { recentDirsWithKinds as $, ATTACH_MAX as A, keysFor as B, parseChoices as C, parseOmpField as D, sameQuestion as E, sleep as F, getMachine as G, HerdrError as H, installClaudeStatusline as I, codexStatus as J, codexMenuOpen as K, terminalUpdateLine as L, MACHINE_KEY_RE as M, terminalUpdateDone as N, restarts as O, PANE_RE as P, log as Q, withPaneLock as R, builtinCommands as S, slashCommands as T, UPLOAD_DIR as U, HOME as V, localMachine as W, readSubs as X, vapidPublicKey as Y, pushReady as Z, machineConfigs as _, defineApi as a, quietActive as a$, installedAgentKinds as a0, IN_DOCKER as a1, HOST_OPEN_TARGET as a2, HOST_OPEN_KEY as a3, PANE_RE$1 as a4, splitId as a5, HOST_OPEN_USER as a6, listDirs as a7, closePanel as a8, setEffort as a9, initVapid as aA, herdrOn as aB, getState as aC, HERDR_CHILD_ENV as aD, HERDR_BIN as aE, currentMachineSlots as aF, paneBaseMachine as aG, projectOf as aH, isProjectThread as aI, limitMachineKeys as aJ, validLimit as aK, readThreadLimits as aL, writeThreadLimits as aM, closeTrailingMention as aN, restarting as aO, hasHeld as aP, INPUT_STATES as aQ, inputVisible as aR, shouldHold as aS, sendPrompt as aT, agentPrompt as aU, isBusyError as aV, HOLD_AGENTS as aW, pendingPrompts as aX, parseQuiet as aY, writeGlobalQuiet as aZ, writeSubs as a_, refreshModel as aa, listEfforts as ab, saveUpload as ac, underHome as ad, isSlashCommand as ae, addQueued as af, interruptAgent as ag, takeBackFromClaude as ah, takeBack as ai, isGitRepo as aj, projectFolderKinds as ak, baseMachines as al, DATA_DIR as am, renameMachine as an, readMenu as ao, searchKeys as ap, findEntry as aq, stepToward as ar, clickMovesOnly as as, setModel as at, listModels as au, TOP as av, HOST_LABEL as aw, getRequestHeaders as ax, ENV_APP_URL as ay, APP_URL_FILE as az, screenChoices as b, readGlobalQuiet as b0, validPushSubscription as b1, replaceSubscription as b2, pushSend as b3, readQuotas as b4, retryQueued as b5, parseWaitScreen as b6, settingsTabs as b7, tabsOfRow as b8, hasTranscript as b9, encodePath as bA, joinRelativeURL as bB, defineRenderHandler as bC, createError$1 as bD, destr as bE, getRouteRules as bF, relative as bG, joinURL as bH, getResponseStatusText as bI, getResponseStatus as bJ, useNitroApp as bK, nodeServer as bL, SEARCH_DEADLINE_MS as ba, SEARCH_MAX_RESULTS as bb, spaceTitle as bc, markSeen as bd, listSessions as be, openSession as bf, PANE_DIRECTIONS as bg, cancelQueued as bh, installMode as bi, updateCommand as bj, compareVersions as bk, allMachines as bl, recentDirs as bm, joinId as bn, useStorage as bo, setResponseHeader as bp, getRouterParam as bq, readUpload as br, defineWebSocketHandler as bs, eventClients as bt, getStateJson as bu, hostAllowed as bv, sameOrigin as bw, mirrorSize as bx, openTerm as by, useRuntimeConfig as bz, createAgent as c, defineEventHandler as d, watchScreen as e, findPane as f, choicesCache as g, herdr as h, getQuery as i, saveAttachment as j, auth as k, localMachineLabel as l, machineFor as m, machineOfPane as n, fmt as o, poll as p, transcripts as q, reqOf as r, sendError as s, takenBackOf as t, uploadNames as u, withoutTakenBack as v, withOmpTab as w, withSentText as x, currentModel as y, sendImage as z };
22479
+ export { sleep$1 as $, ATTACH_MAX as A, isProjectThread as B, uploadNames as C, machineOfPane as D, fmt as E, takenBackOf as F, transcripts as G, HerdrError as H, chatPasted as I, withoutTakenBack as J, withSentText as K, currentModel as L, sendImage as M, keysFor as N, parseChoices as O, PANE_RE as P, parseOmpField as Q, withPaneLock as R, selfUpdates as S, restarting as T, UPLOAD_DIR as U, updateChoices as V, UPDATE_LABELS as W, updateRestartPlan as X, restarts as Y, usesTerminalException as Z, sameQuestion as _, defineApi as a, validLimit as a$, MACHINE_KEY_RE as a0, getMachine as a1, installClaudeStatusline as a2, codexStatus as a3, paneForeground as a4, codexMenuOpen as a5, stopAgent as a6, terminalUpdateLine as a7, waitFor as a8, terminalUpdateDone as a9, listEfforts as aA, saveUpload as aB, setResponseHeader as aC, underHome as aD, isSlashCommand as aE, addQueued as aF, interruptAgent as aG, takeBackFromClaude as aH, takeBack as aI, isGitRepo as aJ, projectFolderKinds as aK, baseMachines as aL, DATA_DIR as aM, renameMachine as aN, readMenu as aO, searchKeys as aP, findEntry as aQ, stepToward as aR, clickMovesOnly as aS, setModel as aT, listModels as aU, TOP as aV, HOST_LABEL as aW, phoneAccess as aX, phoneStatus as aY, phoneAction as aZ, limitMachineKeys as a_, startAgent as aa, log as ab, RESTARTABLE as ac, claudeFooterMode as ad, planRestart as ae, builtinCommands as af, slashCommands as ag, HOME as ah, localMachine as ai, readSubs as aj, vapidPublicKey as ak, pushReady as al, machineConfigs as am, recentDirsWithKinds as an, installedAgentKinds as ao, IN_DOCKER as ap, HOST_OPEN_TARGET as aq, HOST_OPEN_KEY as ar, PANE_RE$1 as as, splitId as at, HOST_OPEN_USER as au, listDirs as av, dismissStopped as aw, closePanel as ax, setEffort as ay, refreshModel as az, screenChoices as b, getResponseStatusText as b$, readThreadLimits as b0, writeThreadLimits as b1, closeTrailingMention as b2, keepPastes as b3, hasHeld as b4, INPUT_STATES as b5, inputVisible as b6, shouldHold as b7, sendPrompt as b8, agentPrompt as b9, cancelQueued as bA, installMode as bB, oneTapMode as bC, updateCommand as bD, compareVersions as bE, updateRunning as bF, allMachines as bG, recentDirs as bH, joinId as bI, useStorage as bJ, getRouterParam as bK, readUpload as bL, defineWebSocketHandler as bM, eventClients as bN, getStateJson as bO, hostAllowed as bP, sameOrigin as bQ, openTerm as bR, useRuntimeConfig as bS, encodePath as bT, joinRelativeURL as bU, defineRenderHandler as bV, createError$1 as bW, destr as bX, getRouteRules as bY, relative as bZ, joinURL as b_, isBusyError as ba, HOLD_AGENTS as bb, pendingPrompts as bc, parseQuiet as bd, writeGlobalQuiet as be, writeSubs as bf, quietActive as bg, readGlobalQuiet as bh, validPushSubscription as bi, replaceSubscription as bj, pushSend as bk, readQuotas as bl, retryQueued as bm, parseWaitScreen as bn, settingsTabs as bo, tabsOfRow as bp, classifyScreen as bq, parseMenu as br, hasTranscript as bs, SEARCH_DEADLINE_MS as bt, SEARCH_MAX_RESULTS as bu, spaceTitle as bv, markSeen as bw, listSessions as bx, openSession as by, PANE_DIRECTIONS as bz, createAgent as c, getResponseStatus as c0, useNitroApp as c1, nodeServer as c2, defineEventHandler as d, watchScreen as e, findPane as f, choicesCache as g, herdr as h, getQuery as i, saveAttachment as j, auth as k, localMachineLabel as l, getState as m, projectOf as n, isCoordinator as o, poll as p, paneBaseMachine as q, reqOf as r, sendError as s, pickCoordinator as t, currentMachineSlots as u, machineFor as v, withOmpTab as w, herdrOn as x, HERDR_CHILD_ENV as y, HERDR_BIN as z };
20881
22480
  //# sourceMappingURL=nitro.mjs.map