@svenroth-ai/shipwright 0.25.0 → 0.27.0

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 (206) hide show
  1. package/client/dist/assets/{EmbeddedTerminal-5QvKe3_a.js → EmbeddedTerminal-CqCQUDtp.js} +1 -1
  2. package/client/dist/assets/{abnfDiagram-VRR7QNED-CXi9HegQ.js → abnfDiagram-VRR7QNED-DX2objTP.js} +1 -1
  3. package/client/dist/assets/{arc-DLh4GiqR.js → arc-DQmjbsFU.js} +1 -1
  4. package/client/dist/assets/{architectureDiagram-ZJ3FMSHR-DUypQ8dQ.js → architectureDiagram-ZJ3FMSHR-DAvCsPCW.js} +1 -1
  5. package/client/dist/assets/{blockDiagram-677ZJIJ3-D2cVMKJG.js → blockDiagram-677ZJIJ3-DC-dlyYI.js} +1 -1
  6. package/client/dist/assets/{c4Diagram-LMCZKHZV-DtruBGJw.js → c4Diagram-LMCZKHZV-DSlmXfhE.js} +1 -1
  7. package/client/dist/assets/channel-BfmIcrp7.js +1 -0
  8. package/client/dist/assets/{chunk-2Q5K7J3B-CAfQkFw2.js → chunk-2Q5K7J3B-C111EHL6.js} +1 -1
  9. package/client/dist/assets/{chunk-32BRIVSS-CPWXoLnQ.js → chunk-32BRIVSS-DrCYs2XG.js} +1 -1
  10. package/client/dist/assets/{chunk-5VM5RSS4-xVc3oppQ.js → chunk-5VM5RSS4-DIejoaXS.js} +1 -1
  11. package/client/dist/assets/{chunk-EX3LRPZG-CUpl6fRR.js → chunk-EX3LRPZG-CGWNeH2g.js} +1 -1
  12. package/client/dist/assets/{chunk-JWPE2WC7-NejUp56x.js → chunk-JWPE2WC7-CCH0SRFY.js} +1 -1
  13. package/client/dist/assets/{chunk-MOJQB5TN-C9-2lLg0.js → chunk-MOJQB5TN-B3OpZGRv.js} +1 -1
  14. package/client/dist/assets/{chunk-RYQCIY6F-BAPtwABy.js → chunk-RYQCIY6F-BKhdp_8v.js} +1 -1
  15. package/client/dist/assets/{chunk-V7JOEXUC-Dwzihw26.js → chunk-V7JOEXUC-B6Bu8x1y.js} +1 -1
  16. package/client/dist/assets/{chunk-VR4S4FIN-9-iArFJM.js → chunk-VR4S4FIN-OgFi2_iB.js} +1 -1
  17. package/client/dist/assets/{chunk-XXDRQBXY-DiWHOrqe.js → chunk-XXDRQBXY-xyjuuhxl.js} +1 -1
  18. package/client/dist/assets/classDiagram-OUVF2IWQ-HfUQZ6Uc.js +1 -0
  19. package/client/dist/assets/classDiagram-v2-EOCWNBFH-HfUQZ6Uc.js +1 -0
  20. package/client/dist/assets/{cose-bilkent-JH36ORCC-Cp04EOcH.js → cose-bilkent-JH36ORCC-BQIH3SCK.js} +1 -1
  21. package/client/dist/assets/{cynefin-VYW2F7L2-D--GN1v8.js → cynefin-VYW2F7L2-oyLAz8te.js} +1 -1
  22. package/client/dist/assets/{cynefinDiagram-TSTJHNR4-BGH0o_qp.js → cynefinDiagram-TSTJHNR4-DoLCsH0l.js} +1 -1
  23. package/client/dist/assets/{dagre-VKFMJZFB-DSKkIKlj.js → dagre-VKFMJZFB-D7djx1_Y.js} +1 -1
  24. package/client/dist/assets/{diagram-FQU43EPY-Cs_C4lb-.js → diagram-FQU43EPY-DFuYE0Vf.js} +1 -1
  25. package/client/dist/assets/{diagram-G47NLZAW-oDtAA8O_.js → diagram-G47NLZAW-CffCZVXk.js} +1 -1
  26. package/client/dist/assets/{diagram-NH7WQ7WH-BiFMjqSH.js → diagram-NH7WQ7WH-BhrHtUlt.js} +1 -1
  27. package/client/dist/assets/{diagram-OA4YK3LP-Ccs3thCj.js → diagram-OA4YK3LP-CwQPbOn0.js} +1 -1
  28. package/client/dist/assets/{diagram-WEI45ONY-CdZNqgO3.js → diagram-WEI45ONY-abtYnYqH.js} +1 -1
  29. package/client/dist/assets/{ebnfDiagram-CCIWWBDH-CapTA0VL.js → ebnfDiagram-CCIWWBDH-DfV7gT4m.js} +1 -1
  30. package/client/dist/assets/{erDiagram-Q63AITRT-BcRNEP8s.js → erDiagram-Q63AITRT-vtbCprgH.js} +1 -1
  31. package/client/dist/assets/{flowDiagram-23GEKE2U-CJ_zF7e_.js → flowDiagram-23GEKE2U-CVCgcyDF.js} +1 -1
  32. package/client/dist/assets/{ganttDiagram-NO4QXBWP-CnJdcg9A.js → ganttDiagram-NO4QXBWP-BclyOgMb.js} +1 -1
  33. package/client/dist/assets/{gitGraphDiagram-IHSO6WYX-YTUQ_ELk.js → gitGraphDiagram-IHSO6WYX-BXT4rIKj.js} +1 -1
  34. package/client/dist/assets/index-CKU9dY18.css +1 -0
  35. package/client/dist/assets/index-DOMl6atu.js +847 -0
  36. package/client/dist/assets/{infoDiagram-FWYZ7A6U-CLOP_ZWD.js → infoDiagram-FWYZ7A6U-Dr4mN24J.js} +1 -1
  37. package/client/dist/assets/{ishikawaDiagram-FXEZZL3T-aFTIzr7o.js → ishikawaDiagram-FXEZZL3T-BBUFWq5C.js} +1 -1
  38. package/client/dist/assets/{journeyDiagram-5HDEW3XC-CbPJB4xd.js → journeyDiagram-5HDEW3XC-CmclipcQ.js} +1 -1
  39. package/client/dist/assets/{kanban-definition-HUTT4EX6-BoyhytIS.js → kanban-definition-HUTT4EX6-eIzUeZ03.js} +1 -1
  40. package/client/dist/assets/{linear-1VXA-uBg.js → linear-DGiT7uKK.js} +1 -1
  41. package/client/dist/assets/{mermaid.core-D_lO2yK9.js → mermaid.core-BcLNGbxW.js} +4 -4
  42. package/client/dist/assets/{mindmap-definition-LN4V7U3C-iFK7LXMx.js → mindmap-definition-LN4V7U3C-C4ANnNiP.js} +1 -1
  43. package/client/dist/assets/{pegDiagram-2B236MQR-DNmcmfA-.js → pegDiagram-2B236MQR-DnMeh3Yh.js} +1 -1
  44. package/client/dist/assets/{pieDiagram-ENE6RG2P-ekx83O5b.js → pieDiagram-ENE6RG2P-CKV_rkhF.js} +1 -1
  45. package/client/dist/assets/{quadrantDiagram-ABIIQ3AL-DayyD1l8.js → quadrantDiagram-ABIIQ3AL-BuUsGwHh.js} +1 -1
  46. package/client/dist/assets/{railroadDiagram-RFXS5EU6-Bdgz7S6G.js → railroadDiagram-RFXS5EU6-BwwdKnFL.js} +1 -1
  47. package/client/dist/assets/{requirementDiagram-TGXJPOKE-szP3Muld.js → requirementDiagram-TGXJPOKE-CpQZouJV.js} +1 -1
  48. package/client/dist/assets/{sankeyDiagram-HTMAVEWB-Ct3rzgUj.js → sankeyDiagram-HTMAVEWB-4fnDUNVa.js} +1 -1
  49. package/client/dist/assets/{sequenceDiagram-DBY2YBRQ-_69EfmOU.js → sequenceDiagram-DBY2YBRQ-BOcaWlM0.js} +1 -1
  50. package/client/dist/assets/{sizeCapture-X5ZJPWSS-BSOU06J2.js → sizeCapture-X5ZJPWSS-_pv-9R0c.js} +1 -1
  51. package/client/dist/assets/{stateDiagram-2N3HPSRC-HmOmFwQm.js → stateDiagram-2N3HPSRC-CFwCOonY.js} +1 -1
  52. package/client/dist/assets/stateDiagram-v2-6OUMAXLB-srkiu3Ib.js +1 -0
  53. package/client/dist/assets/{swimlanes-5IMT3BWC-CVg_4oT7.js → swimlanes-5IMT3BWC-DFa2tzxu.js} +2 -2
  54. package/client/dist/assets/swimlanesDiagram-G3AALYLV-Dq-0K9Zm.js +8 -0
  55. package/client/dist/assets/{timeline-definition-FHXFAJF6-CBBaDKXm.js → timeline-definition-FHXFAJF6-rhGYElgn.js} +1 -1
  56. package/client/dist/assets/{vennDiagram-L72KCM5P-CBXiWPMc.js → vennDiagram-L72KCM5P-BoGjRxyA.js} +1 -1
  57. package/client/dist/assets/{wardleyDiagram-EHGQE667-uhBCZoW0.js → wardleyDiagram-EHGQE667-D-IGv5bH.js} +1 -1
  58. package/client/dist/assets/{xychartDiagram-FW5EYKEG-xxE_wY5O.js → xychartDiagram-FW5EYKEG-D9PFTCfm.js} +1 -1
  59. package/client/dist/index.html +27 -27
  60. package/lib/claude-cli.mjs +173 -173
  61. package/lib/preflight.mjs +252 -252
  62. package/lib/probe-path.mjs +84 -84
  63. package/lib/probes.mjs +10 -4
  64. package/lib/server.mjs +506 -369
  65. package/lib/util.mjs +157 -157
  66. package/lib/win32-spawn.mjs +253 -253
  67. package/package.json +68 -68
  68. package/scripts/deploy-procs.mjs +208 -208
  69. package/scripts/deploy-swap.mjs +276 -276
  70. package/scripts/kill-targets.js +140 -140
  71. package/scripts/repair-claude-json.mjs +295 -295
  72. package/server/dist/config/default-actions.json +90 -90
  73. package/server/dist/core/mission-context/association.d.ts +46 -0
  74. package/server/dist/core/mission-context/association.d.ts.map +1 -1
  75. package/server/dist/core/mission-context/association.js +69 -0
  76. package/server/dist/core/mission-context/association.js.map +1 -1
  77. package/server/dist/core/mission-context/resolver-iterate.d.ts +6 -0
  78. package/server/dist/core/mission-context/resolver-iterate.d.ts.map +1 -0
  79. package/server/dist/core/mission-context/resolver-iterate.js +202 -0
  80. package/server/dist/core/mission-context/resolver-iterate.js.map +1 -0
  81. package/server/dist/core/mission-context/resolver-parts.d.ts +20 -0
  82. package/server/dist/core/mission-context/resolver-parts.d.ts.map +1 -1
  83. package/server/dist/core/mission-context/resolver-parts.js +92 -0
  84. package/server/dist/core/mission-context/resolver-parts.js.map +1 -1
  85. package/server/dist/core/mission-context/resolver.d.ts +1 -1
  86. package/server/dist/core/mission-context/resolver.d.ts.map +1 -1
  87. package/server/dist/core/mission-context/resolver.js +17 -199
  88. package/server/dist/core/mission-context/resolver.js.map +1 -1
  89. package/server/dist/core/mission-context/run-id-recovery.d.ts +21 -5
  90. package/server/dist/core/mission-context/run-id-recovery.d.ts.map +1 -1
  91. package/server/dist/core/mission-context/run-id-recovery.js +32 -5
  92. package/server/dist/core/mission-context/run-id-recovery.js.map +1 -1
  93. package/server/dist/core/mission-context/scenario.d.ts +18 -11
  94. package/server/dist/core/mission-context/scenario.d.ts.map +1 -1
  95. package/server/dist/core/mission-context/scenario.js +40 -3
  96. package/server/dist/core/mission-context/scenario.js.map +1 -1
  97. package/server/dist/core/path-guard.d.ts +1 -1
  98. package/server/dist/core/path-guard.d.ts.map +1 -1
  99. package/server/dist/core/path-guard.js +9 -2
  100. package/server/dist/core/path-guard.js.map +1 -1
  101. package/server/dist/core/sdk-sessions-merge.d.ts +19 -0
  102. package/server/dist/core/sdk-sessions-merge.d.ts.map +1 -1
  103. package/server/dist/core/sdk-sessions-merge.js +19 -5
  104. package/server/dist/core/sdk-sessions-merge.js.map +1 -1
  105. package/server/dist/core/sdk-sessions-refresh.d.ts +50 -0
  106. package/server/dist/core/sdk-sessions-refresh.d.ts.map +1 -0
  107. package/server/dist/core/sdk-sessions-refresh.js +69 -0
  108. package/server/dist/core/sdk-sessions-refresh.js.map +1 -0
  109. package/server/dist/core/sdk-sessions-store.d.ts +2 -0
  110. package/server/dist/core/sdk-sessions-store.d.ts.map +1 -1
  111. package/server/dist/core/sdk-sessions-store.js +7 -9
  112. package/server/dist/core/sdk-sessions-store.js.map +1 -1
  113. package/server/dist/core/shipslog-docs-reader.d.ts +4 -0
  114. package/server/dist/core/shipslog-docs-reader.d.ts.map +1 -0
  115. package/server/dist/core/shipslog-docs-reader.js +181 -0
  116. package/server/dist/core/shipslog-docs-reader.js.map +1 -0
  117. package/server/dist/core/shipslog-docs-types.d.ts +15 -0
  118. package/server/dist/core/shipslog-docs-types.d.ts.map +1 -0
  119. package/server/dist/core/shipslog-docs-types.js +14 -0
  120. package/server/dist/core/shipslog-docs-types.js.map +1 -0
  121. package/server/dist/core/shipwright-plugin-version-reader.d.ts +15 -0
  122. package/server/dist/core/shipwright-plugin-version-reader.d.ts.map +1 -0
  123. package/server/dist/core/shipwright-plugin-version-reader.js +49 -0
  124. package/server/dist/core/shipwright-plugin-version-reader.js.map +1 -0
  125. package/server/dist/external/design-review/serve.js +27 -27
  126. package/server/dist/external/inbox/_derive.d.ts +2 -25
  127. package/server/dist/external/inbox/_derive.d.ts.map +1 -1
  128. package/server/dist/external/inbox/_derive.js.map +1 -1
  129. package/server/dist/external/inbox/_lead.d.ts +23 -0
  130. package/server/dist/external/inbox/_lead.d.ts.map +1 -0
  131. package/server/dist/external/inbox/_lead.js +83 -0
  132. package/server/dist/external/inbox/_lead.js.map +1 -0
  133. package/server/dist/external/inbox/_types.d.ts +32 -0
  134. package/server/dist/external/inbox/_types.d.ts.map +1 -0
  135. package/server/dist/external/inbox/_types.js +10 -0
  136. package/server/dist/external/inbox/_types.js.map +1 -0
  137. package/server/dist/external/inbox/routes.d.ts.map +1 -1
  138. package/server/dist/external/inbox/routes.js +29 -1
  139. package/server/dist/external/inbox/routes.js.map +1 -1
  140. package/server/dist/external/launch/claim-holder-gate.d.ts +42 -0
  141. package/server/dist/external/launch/claim-holder-gate.d.ts.map +1 -0
  142. package/server/dist/external/launch/claim-holder-gate.js +87 -0
  143. package/server/dist/external/launch/claim-holder-gate.js.map +1 -0
  144. package/server/dist/external/launch/routes.d.ts.map +1 -1
  145. package/server/dist/external/launch/routes.js +23 -19
  146. package/server/dist/external/launch/routes.js.map +1 -1
  147. package/server/dist/external/mission-context/association-write.d.ts +22 -0
  148. package/server/dist/external/mission-context/association-write.d.ts.map +1 -0
  149. package/server/dist/external/mission-context/association-write.js +90 -0
  150. package/server/dist/external/mission-context/association-write.js.map +1 -0
  151. package/server/dist/external/mission-context/routes.d.ts.map +1 -1
  152. package/server/dist/external/mission-context/routes.js +18 -31
  153. package/server/dist/external/mission-context/routes.js.map +1 -1
  154. package/server/dist/external/org/lead-question-threads.d.ts +46 -0
  155. package/server/dist/external/org/lead-question-threads.d.ts.map +1 -0
  156. package/server/dist/external/org/lead-question-threads.js +132 -0
  157. package/server/dist/external/org/lead-question-threads.js.map +1 -0
  158. package/server/dist/external/org/mount.d.ts +3 -0
  159. package/server/dist/external/org/mount.d.ts.map +1 -1
  160. package/server/dist/external/org/mount.js +2 -1
  161. package/server/dist/external/org/mount.js.map +1 -1
  162. package/server/dist/external/org/usage.d.ts.map +1 -1
  163. package/server/dist/external/org/usage.js +20 -2
  164. package/server/dist/external/org/usage.js.map +1 -1
  165. package/server/dist/external/routes.d.ts.map +1 -1
  166. package/server/dist/external/routes.js +10 -13
  167. package/server/dist/external/routes.js.map +1 -1
  168. package/server/dist/external/shipslog-docs/routes.d.ts +10 -0
  169. package/server/dist/external/shipslog-docs/routes.d.ts.map +1 -0
  170. package/server/dist/external/shipslog-docs/routes.js +41 -0
  171. package/server/dist/external/shipslog-docs/routes.js.map +1 -0
  172. package/server/dist/routes/diagnostics.d.ts +12 -0
  173. package/server/dist/routes/diagnostics.d.ts.map +1 -1
  174. package/server/dist/routes/diagnostics.js +4 -1
  175. package/server/dist/routes/diagnostics.js.map +1 -1
  176. package/server/dist/routes/org-threads-composite.d.ts +24 -0
  177. package/server/dist/routes/org-threads-composite.d.ts.map +1 -0
  178. package/server/dist/routes/org-threads-composite.js +51 -0
  179. package/server/dist/routes/org-threads-composite.js.map +1 -0
  180. package/server/dist/routes/org.d.ts +5 -0
  181. package/server/dist/routes/org.d.ts.map +1 -1
  182. package/server/dist/routes/org.js +13 -0
  183. package/server/dist/routes/org.js.map +1 -1
  184. package/server/dist/terminal/pty-manager.d.ts +23 -1
  185. package/server/dist/terminal/pty-manager.d.ts.map +1 -1
  186. package/server/dist/terminal/pty-manager.js +22 -3
  187. package/server/dist/terminal/pty-manager.js.map +1 -1
  188. package/server/dist/terminal/pty-write-chunker.d.ts +109 -0
  189. package/server/dist/terminal/pty-write-chunker.d.ts.map +1 -0
  190. package/server/dist/terminal/pty-write-chunker.js +234 -0
  191. package/server/dist/terminal/pty-write-chunker.js.map +1 -0
  192. package/server/dist/types/org.d.ts +19 -0
  193. package/server/dist/types/org.d.ts.map +1 -1
  194. package/server/package.json +1 -1
  195. package/server/profiles/PROFILE_SCHEMA_VERSION +1 -1
  196. package/server/profiles/README.md +27 -27
  197. package/server/profiles/python-plugin-monorepo.json +35 -35
  198. package/server/profiles/supabase-nextjs.json +253 -253
  199. package/server/profiles/vite-hono.json +92 -92
  200. package/client/dist/assets/channel-C_k-9jsa.js +0 -1
  201. package/client/dist/assets/classDiagram-OUVF2IWQ-CsEPq9Ac.js +0 -1
  202. package/client/dist/assets/classDiagram-v2-EOCWNBFH-CsEPq9Ac.js +0 -1
  203. package/client/dist/assets/index-6nZ1bt0a.css +0 -1
  204. package/client/dist/assets/index-irQhGNpn.js +0 -828
  205. package/client/dist/assets/stateDiagram-v2-6OUMAXLB-BW8H33dv.js +0 -1
  206. package/client/dist/assets/swimlanesDiagram-G3AALYLV-BaRlxcPD.js +0 -8
@@ -1,140 +1,140 @@
1
- /* eslint-disable @typescript-eslint/no-require-imports */
2
- /**
3
- * Port-selection helper for dev-restart.js.
4
- *
5
- * Pure function: no subprocess calls, no filesystem. The kill-scope is
6
- * exactly the two configured ports (Hono + Vite), each with a default.
7
- *
8
- * Worktree-parallel contract: if the operator sets PORT and VITE_PORT for
9
- * their worktree, this function returns ONLY those ports — nothing
10
- * hardcoded, nothing leaked from a different worktree's config. The
11
- * historic VITE_ALT_PORT=5177 hardcode was removed in v0.3.2.
12
- */
13
-
14
- const DEFAULT_HONO_PORT = 3847;
15
- const DEFAULT_VITE_PORT = 5173;
16
-
17
- /**
18
- * @param {Record<string,string|undefined>} env
19
- * @param {string} _platform // reserved for future platform-specific overrides
20
- * @returns {number[]} deduped, finite, positive kill targets
21
- */
22
- function computeKillTargets(env, _platform) {
23
- const honoPort = parsePort(env.PORT, DEFAULT_HONO_PORT);
24
- const vitePort = parsePort(env.VITE_PORT, DEFAULT_VITE_PORT);
25
- const seen = new Set();
26
- const out = [];
27
- for (const p of [honoPort, vitePort]) {
28
- if (!Number.isFinite(p) || p <= 0) continue;
29
- if (seen.has(p)) continue;
30
- seen.add(p);
31
- out.push(p);
32
- }
33
- return out;
34
- }
35
-
36
- function parsePort(raw, fallback) {
37
- if (raw === undefined || raw === null || raw === '') return fallback;
38
- const n = parseInt(String(raw), 10);
39
- if (!Number.isFinite(n) || n <= 0) return fallback;
40
- return n;
41
- }
42
-
43
- /**
44
- * Extract the numeric port from a netstat "Local Address" token.
45
- *
46
- * Handles IPv4 (`0.0.0.0:5173`, `127.0.0.1:5173`) and IPv6
47
- * (`[::]:5173`, `[::1]:5173`) — the port is always the run of digits after
48
- * the LAST colon. Returns null when no numeric port is present.
49
- *
50
- * @param {string} addr
51
- * @returns {number|null}
52
- */
53
- function localAddressPort(addr) {
54
- if (typeof addr !== 'string') return null;
55
- const idx = addr.lastIndexOf(':');
56
- if (idx === -1) return null;
57
- const portStr = addr.slice(idx + 1);
58
- if (!/^\d+$/.test(portStr)) return null;
59
- return Number(portStr);
60
- }
61
-
62
- /**
63
- * Structurally parse Windows `netstat -ano -p TCP` output and return the PIDs
64
- * of processes LISTENING on one of the given ports.
65
- *
66
- * Fixes F16 (D11): the prior `netstat … | findstr :<port>` did plain substring
67
- * matching over the whole line with no state filter, so it also caught
68
- * (1) ESTABLISHED browser sockets whose FOREIGN address ended `:<port>`, and
69
- * (2) port-prefix collisions (`:5173` is a substring of `:51730`),
70
- * and taskkill /F /T'd those unrelated PIDs. This parser requires
71
- * state === LISTENING AND an EXACT local-address port match.
72
- *
73
- * Handles IPv4 AND IPv6: plain `netstat -ano` output labels IPv6 rows proto
74
- * `TCP` (distinguished by the `[::]` address), and some Windows builds use
75
- * `TCPv6` — the proto check is `startsWith('TCP')` to cover both. UDP rows
76
- * (`*:*` foreign, no State column, 4 tokens) fall out via the < 5 / state
77
- * checks. Feed it `netstat -ano` (NOT `-p TCP`, which drops IPv6 listeners).
78
- *
79
- * @param {string} netstatOutput raw stdout of `netstat -ano`
80
- * @param {number[]} ports kill-target ports (already validated)
81
- * @returns {string[]} deduped PID strings, in first-seen order
82
- */
83
- function parseWindowsListenerPids(netstatOutput, ports) {
84
- const wanted = new Set(ports.map(Number));
85
- const seen = new Set();
86
- const pids = [];
87
- for (const rawLine of String(netstatOutput ?? '').split(/\r?\n/)) {
88
- const cols = rawLine.trim().split(/\s+/);
89
- // A TCP row is: Proto LocalAddr ForeignAddr State PID
90
- if (cols.length < 5) continue;
91
- if (!cols[0].toUpperCase().startsWith('TCP')) continue;
92
- const state = cols[cols.length - 2].toUpperCase();
93
- if (state !== 'LISTENING') continue;
94
- const port = localAddressPort(cols[1]);
95
- if (port === null || !wanted.has(port)) continue;
96
- const pid = cols[cols.length - 1];
97
- if (!/^\d+$/.test(pid)) continue;
98
- if (seen.has(pid)) continue;
99
- seen.add(pid);
100
- pids.push(pid);
101
- }
102
- return pids;
103
- }
104
-
105
- /**
106
- * Build the POSIX lsof command that lists only LISTENING PIDs on the given
107
- * ports. The `-sTCP:LISTEN` state filter is the POSIX half of the F16 fix —
108
- * without it, `lsof -ti tcp:<ports>` returns every socket (incl. the
109
- * ESTABLISHED browser connections) bound to those ports.
110
- *
111
- * Argument ORDER is load-bearing: the address (`tcp:<ports>`) MUST stay bound
112
- * to `-i`, with `-sTCP:LISTEN` APPENDED AFTER it. If `-sTCP:LISTEN` sat between
113
- * `-i` and its address, `-i` would be addressless (its argument starts with
114
- * `-`) → it selects EVERY internet file and `-sTCP:LISTEN` then narrows to ALL
115
- * listeners machine-wide, killing every listener — the inverse of this fix.
116
- *
117
- * The command is run via `execSync` (shell), so — although callers pass ports
118
- * already validated by computeKillTargets — every port is coerced to a positive
119
- * integer and non-integers are dropped here too. That keeps the exported helper
120
- * injection-proof regardless of caller (defense in depth at the boundary). An
121
- * empty filtered port list returns '' (kill nothing) so a portless
122
- * `lsof -ti tcp:` (which would select ALL TCP files) can never be built.
123
- *
124
- * @param {number[]} ports
125
- * @returns {string} shell command string for execSync ('' when no valid ports)
126
- */
127
- function buildLsofCommand(ports) {
128
- const portList = ports
129
- .map(Number)
130
- .filter((p) => Number.isInteger(p) && p > 0)
131
- .join(',');
132
- if (portList === '') return '';
133
- return `lsof -ti tcp:${portList} -sTCP:LISTEN`;
134
- }
135
-
136
- module.exports = {
137
- computeKillTargets,
138
- parseWindowsListenerPids,
139
- buildLsofCommand,
140
- };
1
+ /* eslint-disable @typescript-eslint/no-require-imports */
2
+ /**
3
+ * Port-selection helper for dev-restart.js.
4
+ *
5
+ * Pure function: no subprocess calls, no filesystem. The kill-scope is
6
+ * exactly the two configured ports (Hono + Vite), each with a default.
7
+ *
8
+ * Worktree-parallel contract: if the operator sets PORT and VITE_PORT for
9
+ * their worktree, this function returns ONLY those ports — nothing
10
+ * hardcoded, nothing leaked from a different worktree's config. The
11
+ * historic VITE_ALT_PORT=5177 hardcode was removed in v0.3.2.
12
+ */
13
+
14
+ const DEFAULT_HONO_PORT = 3847;
15
+ const DEFAULT_VITE_PORT = 5173;
16
+
17
+ /**
18
+ * @param {Record<string,string|undefined>} env
19
+ * @param {string} _platform // reserved for future platform-specific overrides
20
+ * @returns {number[]} deduped, finite, positive kill targets
21
+ */
22
+ function computeKillTargets(env, _platform) {
23
+ const honoPort = parsePort(env.PORT, DEFAULT_HONO_PORT);
24
+ const vitePort = parsePort(env.VITE_PORT, DEFAULT_VITE_PORT);
25
+ const seen = new Set();
26
+ const out = [];
27
+ for (const p of [honoPort, vitePort]) {
28
+ if (!Number.isFinite(p) || p <= 0) continue;
29
+ if (seen.has(p)) continue;
30
+ seen.add(p);
31
+ out.push(p);
32
+ }
33
+ return out;
34
+ }
35
+
36
+ function parsePort(raw, fallback) {
37
+ if (raw === undefined || raw === null || raw === '') return fallback;
38
+ const n = parseInt(String(raw), 10);
39
+ if (!Number.isFinite(n) || n <= 0) return fallback;
40
+ return n;
41
+ }
42
+
43
+ /**
44
+ * Extract the numeric port from a netstat "Local Address" token.
45
+ *
46
+ * Handles IPv4 (`0.0.0.0:5173`, `127.0.0.1:5173`) and IPv6
47
+ * (`[::]:5173`, `[::1]:5173`) — the port is always the run of digits after
48
+ * the LAST colon. Returns null when no numeric port is present.
49
+ *
50
+ * @param {string} addr
51
+ * @returns {number|null}
52
+ */
53
+ function localAddressPort(addr) {
54
+ if (typeof addr !== 'string') return null;
55
+ const idx = addr.lastIndexOf(':');
56
+ if (idx === -1) return null;
57
+ const portStr = addr.slice(idx + 1);
58
+ if (!/^\d+$/.test(portStr)) return null;
59
+ return Number(portStr);
60
+ }
61
+
62
+ /**
63
+ * Structurally parse Windows `netstat -ano -p TCP` output and return the PIDs
64
+ * of processes LISTENING on one of the given ports.
65
+ *
66
+ * Fixes F16 (D11): the prior `netstat … | findstr :<port>` did plain substring
67
+ * matching over the whole line with no state filter, so it also caught
68
+ * (1) ESTABLISHED browser sockets whose FOREIGN address ended `:<port>`, and
69
+ * (2) port-prefix collisions (`:5173` is a substring of `:51730`),
70
+ * and taskkill /F /T'd those unrelated PIDs. This parser requires
71
+ * state === LISTENING AND an EXACT local-address port match.
72
+ *
73
+ * Handles IPv4 AND IPv6: plain `netstat -ano` output labels IPv6 rows proto
74
+ * `TCP` (distinguished by the `[::]` address), and some Windows builds use
75
+ * `TCPv6` — the proto check is `startsWith('TCP')` to cover both. UDP rows
76
+ * (`*:*` foreign, no State column, 4 tokens) fall out via the < 5 / state
77
+ * checks. Feed it `netstat -ano` (NOT `-p TCP`, which drops IPv6 listeners).
78
+ *
79
+ * @param {string} netstatOutput raw stdout of `netstat -ano`
80
+ * @param {number[]} ports kill-target ports (already validated)
81
+ * @returns {string[]} deduped PID strings, in first-seen order
82
+ */
83
+ function parseWindowsListenerPids(netstatOutput, ports) {
84
+ const wanted = new Set(ports.map(Number));
85
+ const seen = new Set();
86
+ const pids = [];
87
+ for (const rawLine of String(netstatOutput ?? '').split(/\r?\n/)) {
88
+ const cols = rawLine.trim().split(/\s+/);
89
+ // A TCP row is: Proto LocalAddr ForeignAddr State PID
90
+ if (cols.length < 5) continue;
91
+ if (!cols[0].toUpperCase().startsWith('TCP')) continue;
92
+ const state = cols[cols.length - 2].toUpperCase();
93
+ if (state !== 'LISTENING') continue;
94
+ const port = localAddressPort(cols[1]);
95
+ if (port === null || !wanted.has(port)) continue;
96
+ const pid = cols[cols.length - 1];
97
+ if (!/^\d+$/.test(pid)) continue;
98
+ if (seen.has(pid)) continue;
99
+ seen.add(pid);
100
+ pids.push(pid);
101
+ }
102
+ return pids;
103
+ }
104
+
105
+ /**
106
+ * Build the POSIX lsof command that lists only LISTENING PIDs on the given
107
+ * ports. The `-sTCP:LISTEN` state filter is the POSIX half of the F16 fix —
108
+ * without it, `lsof -ti tcp:<ports>` returns every socket (incl. the
109
+ * ESTABLISHED browser connections) bound to those ports.
110
+ *
111
+ * Argument ORDER is load-bearing: the address (`tcp:<ports>`) MUST stay bound
112
+ * to `-i`, with `-sTCP:LISTEN` APPENDED AFTER it. If `-sTCP:LISTEN` sat between
113
+ * `-i` and its address, `-i` would be addressless (its argument starts with
114
+ * `-`) → it selects EVERY internet file and `-sTCP:LISTEN` then narrows to ALL
115
+ * listeners machine-wide, killing every listener — the inverse of this fix.
116
+ *
117
+ * The command is run via `execSync` (shell), so — although callers pass ports
118
+ * already validated by computeKillTargets — every port is coerced to a positive
119
+ * integer and non-integers are dropped here too. That keeps the exported helper
120
+ * injection-proof regardless of caller (defense in depth at the boundary). An
121
+ * empty filtered port list returns '' (kill nothing) so a portless
122
+ * `lsof -ti tcp:` (which would select ALL TCP files) can never be built.
123
+ *
124
+ * @param {number[]} ports
125
+ * @returns {string} shell command string for execSync ('' when no valid ports)
126
+ */
127
+ function buildLsofCommand(ports) {
128
+ const portList = ports
129
+ .map(Number)
130
+ .filter((p) => Number.isInteger(p) && p > 0)
131
+ .join(',');
132
+ if (portList === '') return '';
133
+ return `lsof -ti tcp:${portList} -sTCP:LISTEN`;
134
+ }
135
+
136
+ module.exports = {
137
+ computeKillTargets,
138
+ parseWindowsListenerPids,
139
+ buildLsofCommand,
140
+ };