gsd-pi 2.41.0-dev.0acbce9 → 2.41.0-dev.3557dc4

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 (213) hide show
  1. package/dist/cli-web-branch.d.ts +6 -0
  2. package/dist/cli-web-branch.js +17 -0
  3. package/dist/onboarding.js +2 -1
  4. package/dist/resources/extensions/gsd/auto/loop.js +9 -1
  5. package/dist/resources/extensions/gsd/auto/phases.js +26 -8
  6. package/dist/resources/extensions/gsd/auto-dashboard.js +6 -2
  7. package/dist/resources/extensions/gsd/auto-dispatch.js +19 -2
  8. package/dist/resources/extensions/gsd/auto-post-unit.js +7 -0
  9. package/dist/resources/extensions/gsd/auto-recovery.js +12 -4
  10. package/dist/resources/extensions/gsd/auto-start.js +8 -3
  11. package/dist/resources/extensions/gsd/auto-worktree.js +147 -13
  12. package/dist/resources/extensions/gsd/auto.js +36 -1
  13. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +199 -164
  14. package/dist/resources/extensions/gsd/bootstrap/journal-tools.js +62 -0
  15. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +2 -0
  16. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +16 -0
  17. package/dist/resources/extensions/gsd/commands/catalog.js +8 -1
  18. package/dist/resources/extensions/gsd/commands/handlers/core.js +1 -0
  19. package/dist/resources/extensions/gsd/commands/handlers/ops.js +5 -0
  20. package/dist/resources/extensions/gsd/context-store.js +4 -3
  21. package/dist/resources/extensions/gsd/db-writer.js +5 -2
  22. package/dist/resources/extensions/gsd/detection.js +1 -1
  23. package/dist/resources/extensions/gsd/doctor.js +11 -1
  24. package/dist/resources/extensions/gsd/exit-command.js +12 -2
  25. package/dist/resources/extensions/gsd/export.js +9 -13
  26. package/dist/resources/extensions/gsd/extension-manifest.json +2 -2
  27. package/dist/resources/extensions/gsd/files.js +28 -11
  28. package/dist/resources/extensions/gsd/forensics.js +10 -3
  29. package/dist/resources/extensions/gsd/git-service.js +5 -1
  30. package/dist/resources/extensions/gsd/gsd-db.js +25 -8
  31. package/dist/resources/extensions/gsd/guided-flow-queue.js +1 -1
  32. package/dist/resources/extensions/gsd/guided-flow.js +7 -3
  33. package/dist/resources/extensions/gsd/journal.js +85 -0
  34. package/dist/resources/extensions/gsd/md-importer.js +5 -0
  35. package/dist/resources/extensions/gsd/milestone-ids.js +1 -1
  36. package/dist/resources/extensions/gsd/native-git-bridge.js +2 -2
  37. package/dist/resources/extensions/gsd/post-unit-hooks.js +24 -412
  38. package/dist/resources/extensions/gsd/preferences-types.js +1 -0
  39. package/dist/resources/extensions/gsd/preferences.js +1 -0
  40. package/dist/resources/extensions/gsd/prompt-loader.js +34 -4
  41. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +11 -10
  42. package/dist/resources/extensions/gsd/prompts/discuss-headless.md +2 -2
  43. package/dist/resources/extensions/gsd/prompts/discuss.md +1 -1
  44. package/dist/resources/extensions/gsd/prompts/queue.md +1 -1
  45. package/dist/resources/extensions/gsd/repo-identity.js +46 -2
  46. package/dist/resources/extensions/gsd/rule-registry.js +489 -0
  47. package/dist/resources/extensions/gsd/rule-types.js +6 -0
  48. package/dist/resources/extensions/gsd/service-tier.js +138 -0
  49. package/dist/resources/extensions/gsd/structured-data-formatter.js +2 -1
  50. package/dist/resources/extensions/gsd/templates/decisions.md +2 -2
  51. package/dist/resources/extensions/gsd/workflow-templates.js +13 -1
  52. package/dist/resources/extensions/gsd/worktree-manager.js +20 -6
  53. package/dist/resources/extensions/gsd/worktree-resolver.js +19 -2
  54. package/dist/resources/extensions/subagent/index.js +7 -3
  55. package/dist/resources/extensions/voice/index.js +4 -4
  56. package/dist/web/standalone/.next/BUILD_ID +1 -1
  57. package/dist/web/standalone/.next/app-path-routes-manifest.json +13 -13
  58. package/dist/web/standalone/.next/build-manifest.json +3 -3
  59. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  60. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  61. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  62. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  67. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  69. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  70. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  78. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  79. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  80. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  81. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  82. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  83. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  84. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  85. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  86. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  87. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  88. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
  89. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +1 -1
  90. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  91. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  92. package/dist/web/standalone/.next/server/app/index.html +1 -1
  93. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app-paths-manifest.json +13 -13
  100. package/dist/web/standalone/.next/server/chunks/229.js +3 -3
  101. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  102. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  103. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  104. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  105. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  106. package/dist/web/standalone/.next/static/chunks/4024.c195dc1fdd2adbea.js +9 -0
  107. package/dist/web/standalone/.next/static/chunks/{webpack-9afaaebf6042a1d7.js → webpack-fa307370fcf9fb2c.js} +1 -1
  108. package/dist/web-mode.d.ts +2 -0
  109. package/dist/web-mode.js +29 -7
  110. package/package.json +1 -1
  111. package/packages/native/src/__tests__/text.test.mjs +33 -0
  112. package/packages/pi-coding-agent/dist/core/discovery-cache.test.js +3 -1
  113. package/packages/pi-coding-agent/dist/core/discovery-cache.test.js.map +1 -1
  114. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  115. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +10 -7
  116. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js.map +1 -1
  117. package/packages/pi-coding-agent/src/core/discovery-cache.test.ts +4 -2
  118. package/packages/pi-coding-agent/src/modes/interactive/components/login-dialog.ts +11 -7
  119. package/src/resources/extensions/gsd/auto/loop-deps.ts +5 -1
  120. package/src/resources/extensions/gsd/auto/loop.ts +10 -1
  121. package/src/resources/extensions/gsd/auto/phases.ts +28 -8
  122. package/src/resources/extensions/gsd/auto/types.ts +4 -0
  123. package/src/resources/extensions/gsd/auto-dashboard.ts +7 -2
  124. package/src/resources/extensions/gsd/auto-dispatch.ts +25 -5
  125. package/src/resources/extensions/gsd/auto-post-unit.ts +8 -0
  126. package/src/resources/extensions/gsd/auto-recovery.ts +12 -4
  127. package/src/resources/extensions/gsd/auto-start.ts +8 -3
  128. package/src/resources/extensions/gsd/auto-worktree.ts +162 -18
  129. package/src/resources/extensions/gsd/auto.ts +40 -1
  130. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +209 -162
  131. package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +62 -0
  132. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +2 -0
  133. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +13 -0
  134. package/src/resources/extensions/gsd/commands/catalog.ts +8 -1
  135. package/src/resources/extensions/gsd/commands/handlers/core.ts +1 -0
  136. package/src/resources/extensions/gsd/commands/handlers/ops.ts +5 -0
  137. package/src/resources/extensions/gsd/context-store.ts +4 -3
  138. package/src/resources/extensions/gsd/db-writer.ts +6 -2
  139. package/src/resources/extensions/gsd/detection.ts +1 -1
  140. package/src/resources/extensions/gsd/doctor.ts +12 -1
  141. package/src/resources/extensions/gsd/exit-command.ts +14 -2
  142. package/src/resources/extensions/gsd/export.ts +8 -15
  143. package/src/resources/extensions/gsd/extension-manifest.json +2 -2
  144. package/src/resources/extensions/gsd/files.ts +29 -12
  145. package/src/resources/extensions/gsd/forensics.ts +9 -3
  146. package/src/resources/extensions/gsd/git-service.ts +5 -4
  147. package/src/resources/extensions/gsd/gsd-db.ts +37 -8
  148. package/src/resources/extensions/gsd/guided-flow-queue.ts +1 -1
  149. package/src/resources/extensions/gsd/guided-flow.ts +7 -3
  150. package/src/resources/extensions/gsd/journal.ts +134 -0
  151. package/src/resources/extensions/gsd/md-importer.ts +6 -0
  152. package/src/resources/extensions/gsd/milestone-ids.ts +1 -1
  153. package/src/resources/extensions/gsd/native-git-bridge.ts +2 -2
  154. package/src/resources/extensions/gsd/post-unit-hooks.ts +24 -462
  155. package/src/resources/extensions/gsd/preferences-types.ts +3 -0
  156. package/src/resources/extensions/gsd/preferences.ts +1 -0
  157. package/src/resources/extensions/gsd/prompt-loader.ts +35 -4
  158. package/src/resources/extensions/gsd/prompts/complete-milestone.md +11 -10
  159. package/src/resources/extensions/gsd/prompts/discuss-headless.md +2 -2
  160. package/src/resources/extensions/gsd/prompts/discuss.md +1 -1
  161. package/src/resources/extensions/gsd/prompts/queue.md +1 -1
  162. package/src/resources/extensions/gsd/repo-identity.ts +47 -2
  163. package/src/resources/extensions/gsd/rule-registry.ts +599 -0
  164. package/src/resources/extensions/gsd/rule-types.ts +68 -0
  165. package/src/resources/extensions/gsd/service-tier.ts +171 -0
  166. package/src/resources/extensions/gsd/structured-data-formatter.ts +3 -1
  167. package/src/resources/extensions/gsd/templates/decisions.md +2 -2
  168. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +3 -2
  169. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +85 -0
  170. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +202 -0
  171. package/src/resources/extensions/gsd/tests/context-store.test.ts +10 -5
  172. package/src/resources/extensions/gsd/tests/db-writer.test.ts +10 -0
  173. package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +15 -10
  174. package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +5 -4
  175. package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +167 -0
  176. package/src/resources/extensions/gsd/tests/doctor-task-done-missing-summary-slice-loop.test.ts +174 -0
  177. package/src/resources/extensions/gsd/tests/exit-command.test.ts +55 -0
  178. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +8 -1
  179. package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +7 -7
  180. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +513 -0
  181. package/src/resources/extensions/gsd/tests/journal-query-tool.test.ts +147 -0
  182. package/src/resources/extensions/gsd/tests/journal.test.ts +386 -0
  183. package/src/resources/extensions/gsd/tests/md-importer.test.ts +31 -1
  184. package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
  185. package/src/resources/extensions/gsd/tests/milestone-id-reservation.test.ts +1 -1
  186. package/src/resources/extensions/gsd/tests/parsers.test.ts +110 -0
  187. package/src/resources/extensions/gsd/tests/preferences.test.ts +47 -25
  188. package/src/resources/extensions/gsd/tests/prompt-db.test.ts +3 -1
  189. package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +61 -1
  190. package/src/resources/extensions/gsd/tests/routing-history.test.ts +11 -22
  191. package/src/resources/extensions/gsd/tests/rule-registry.test.ts +413 -0
  192. package/src/resources/extensions/gsd/tests/service-tier.test.ts +98 -0
  193. package/src/resources/extensions/gsd/tests/skill-lifecycle.test.ts +2 -2
  194. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +102 -0
  195. package/src/resources/extensions/gsd/tests/structured-data-formatter.test.ts +4 -3
  196. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +117 -0
  197. package/src/resources/extensions/gsd/tests/triage-dispatch.test.ts +6 -1
  198. package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +99 -0
  199. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +1 -0
  200. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +4 -0
  201. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +178 -0
  202. package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +78 -3
  203. package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +140 -0
  204. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +74 -0
  205. package/src/resources/extensions/gsd/types.ts +3 -0
  206. package/src/resources/extensions/gsd/workflow-templates.ts +12 -1
  207. package/src/resources/extensions/gsd/worktree-manager.ts +21 -6
  208. package/src/resources/extensions/gsd/worktree-resolver.ts +30 -9
  209. package/src/resources/extensions/subagent/index.ts +7 -3
  210. package/src/resources/extensions/voice/index.ts +4 -4
  211. package/dist/web/standalone/.next/static/chunks/4024.279c423e4661ece1.js +0 -9
  212. /package/dist/web/standalone/.next/static/{SwbKZ7JPNFlEmU4f8pKEv → JBSIr4fSfHXs5g5x2ZBSC}/_buildManifest.js +0 -0
  213. /package/dist/web/standalone/.next/static/{SwbKZ7JPNFlEmU4f8pKEv → JBSIr4fSfHXs5g5x2ZBSC}/_ssgManifest.js +0 -0
@@ -1 +1 @@
1
- (()=>{"use strict";var e,a,f,c,d,b,t,r,o,n={},i={};function l(e){var a=i[e];if(void 0!==a)return a.exports;var f=i[e]={exports:{}},c=!0;try{n[e].call(f.exports,f,f.exports,l),c=!1}finally{c&&delete i[e]}return f.exports}l.m=n,e=[],l.O=(a,f,c,d)=>{if(f){d=d||0;for(var b=e.length;b>0&&e[b-1][2]>d;b--)e[b]=e[b-1];e[b]=[f,c,d];return}for(var t=1/0,b=0;b<e.length;b++){for(var[f,c,d]=e[b],r=!0,o=0;o<f.length;o++)(!1&d||t>=d)&&Object.keys(l.O).every(e=>l.O[e](f[o]))?f.splice(o--,1):(r=!1,d<t&&(t=d));if(r){e.splice(b--,1);var n=c();void 0!==n&&(a=n)}}return a},l.n=e=>{var a=e&&e.__esModule?()=>e.default:()=>e;return l.d(a,{a:a}),a},f=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,l.t=function(e,c){if(1&c&&(e=this(e)),8&c||"object"==typeof e&&e&&(4&c&&e.__esModule||16&c&&"function"==typeof e.then))return e;var d=Object.create(null);l.r(d);var b={};a=a||[null,f({}),f([]),f(f)];for(var t=2&c&&e;"object"==typeof t&&!~a.indexOf(t);t=f(t))Object.getOwnPropertyNames(t).forEach(a=>b[a]=()=>e[a]);return b.default=()=>e,l.d(d,b),d},l.d=(e,a)=>{for(var f in a)l.o(a,f)&&!l.o(e,f)&&Object.defineProperty(e,f,{enumerable:!0,get:a[f]})},l.f={},l.e=e=>Promise.all(Object.keys(l.f).reduce((a,f)=>(l.f[f](e,a),a),[])),l.u=e=>4986===e?"static/chunks/4986-c2fc8845ce785303.js":"static/chunks/"+(({535:"4ca0cff5",1290:"cee15710",1630:"53c1bd3f",2577:"d1c28714",3879:"cfdf2ac7",3888:"e868780c",4763:"4402d2ac",4814:"799ebd4e",6079:"363642f4",6666:"570e4624",6814:"7d29de82",7442:"92e53eb0",8835:"ce16f5a9",9312:"1cd6e1d3"})[e]||e)+"."+({10:"6cf001c181ce1098",34:"ce581867cf95e24b",36:"b4a553bf2106f6d3",194:"76dbdc07cdc2311d",214:"f6bba63dfa159e01",252:"a9e22657cbf67b42",327:"8ecab0b86d52b597",376:"9e742ff6919b2b54",392:"3cad93691f1b7360",400:"59979e0d3ae126e4",425:"9a3434a28926566a",535:"07efe2bc8f09d72e",541:"cfa15b606745131a",579:"33fc87fb95163c6a",583:"b3efc73cb21097e2",624:"2dd909fbfce98c5e",640:"5bf1e25d0bddf875",698:"e0dda03fd303b96b",794:"06b1ae4fc9cc10a9",816:"f909c891b91aa79f",830:"e49b226c07876df4",840:"a9c535f8868cc4bd",862:"fe3b86ecfb9c0716",893:"c469257bbbbca4f5",966:"57e4cd93c2368d34",968:"237f19aca13543fe",1078:"02294b2934d5bf21",1142:"8248bfb76c695146",1161:"06b33878b495da50",1184:"e80a999422621ed9",1188:"1d4ce94ddc297119",1198:"2a5c215f01a8b74d",1215:"88ed89521e915107",1219:"e27b0bd10b029e40",1247:"64e212daa5bb3925",1248:"36af3903f037c1f5",1274:"ce6906b1fe2e04b8",1280:"1651bb5167170457",1290:"869cef5f741d40c2",1335:"716cc5924662708e",1365:"d7391145ca7f8791",1379:"03e1e0f31bdaa2cc",1410:"77acea37535dbbef",1413:"117c5958c59cc695",1453:"3c24c777a3deca2d",1456:"217c3832b3e76b7f",1501:"549af2c91a889a08",1534:"918a25b2e3daab22",1556:"96abc9ff91b0c383",1583:"03a06dbdde85ac56",1595:"ee61519fde230f60",1630:"2911e2bac119d910",1642:"8c00543baa1aef54",1656:"a9efc78185a5a85f",1664:"6d5b5e3112b66906",1898:"e07b2b1c5e789b06",1932:"b0aea9b52cabe5e8",1977:"a06c8e8d2198f765",2013:"598558c40e51e107",2031:"3877441c49bff5cd",2041:"00f4fa0ab26c6ba5",2062:"4a0ab3400fea0e34",2107:"c627112b02b9e1c8",2130:"441ae0f68863efeb",2160:"e38d781fd5c4cde0",2179:"1bf5fe315e6e132c",2215:"9d2d6fbe90ffcc80",2278:"0b14de1cb2041bfd",2349:"ca15d465c4260b4b",2374:"7560cc5c4b8ed6b0",2397:"0e4dce0a557ac7cf",2403:"3dda71b6482dce3b",2485:"3188870375e9fbee",2489:"fcc70bd67329a70e",2501:"4e65b56602faa956",2503:"bb9d3528c3c7ccae",2504:"159b28843e61aae9",2577:"f52cffdd449a2d14",2614:"8349e4b407470a30",2710:"fe0656d4ebae4d30",2728:"5fed9ac13dfcd1d1",2735:"5a3e65a580bd5af9",2747:"1d85d388314f2904",2765:"2c6d9772af50feed",2783:"e8bbf2c794ef9750",2812:"dcd8179a6b1d76c9",2839:"c9a657d5b4031a19",2895:"0339e755e79ee794",2937:"7e724516d5e6ce42",2954:"7d8daeae6410d4a4",2957:"2dd93db1a16e96b0",2964:"1a0e15f7327680a2",3072:"b6da96c10080c967",3074:"911584369786c26c",3077:"bf82c0c332cc9fec",3082:"159e4ab8f0572597",3095:"8cd478c42cee505a",3111:"1cf85db2faeb20cc",3175:"538ce7aec634bddb",3186:"3665756f8aae3eb7",3194:"5a331fb5e3dc34e9",3229:"5e91f7d0b135266b",3274:"858f7f9216c8b074",3286:"196c508356bb0b66",3288:"f23c8a5183e6b910",3290:"b5ba2646ad49c9fd",3304:"16305e84f4ff6598",3313:"471138e5504646a9",3324:"85e21e3313d0901c",3392:"460f5a753531f501",3419:"78b520dab7e2f96d",3437:"0ad1ecc8c5b8a679",3476:"246c30b47bded3b7",3483:"fc2a0b1185dbaa5d",3492:"ca55498822ad7796",3526:"24826331ab265b39",3606:"e5c833cdda239583",3633:"2526b2d018636ff0",3721:"bf31263de6d5fa46",3782:"8d7b66d0e37dbe76",3787:"78ebe68a043f50ce",3860:"be5b243bf7d6618c",3873:"72a9a21509f713f7",3879:"f50abf4e93e943ea",3888:"f7831045ac345aaa",3942:"085da8d58242eeab",3979:"cea8623f6e9f83ea",4024:"279c423e4661ece1",4053:"90d71fcbe4ffd852",4080:"08a21dae9e93c547",4163:"c6ebefdd9aa5c358",4174:"b6ff75dd91a2a32e",4223:"c10556cd92fc8018",4242:"3a66c58375424a5b",4275:"8f2e531757c2a813",4295:"6c0f4838b0c9c69d",4309:"a887a10e84c44f9e",4350:"b9d80827e8eee01a",4357:"ac4f251cdac34fbe",4358:"f89b14cf7a988731",4370:"ac13b62c811f7ede",4417:"0b5ee948c3518db3",4447:"f86b52aea068b189",4474:"053807fa34066566",4539:"559c223da16b0282",4540:"03df20c499bca840",4553:"ce3eab65a260353e",4567:"7dc81a6e1c7df565",4583:"2a5df6eb3a6ea30b",4593:"676ac488744e1af5",4608:"1ff246d6b4c771f8",4634:"0332a56fdb8e26c0",4650:"61aec912b756287b",4659:"0b14de1cb2041bfd",4678:"867dfae26694be02",4763:"d0d1b1777a45e0b2",4801:"17596dbc1818c262",4814:"ccb85c56e9216a38",4823:"907850963b2bcb70",4829:"402ec60139d36417",4846:"c563765de65a34c7",4900:"b723a916eafdf743",4903:"004f583a12223dac",4963:"0f6678151a159143",4969:"1cb10af9489ea295",4976:"8870319ffbd1c31f",5005:"73745d7fcae6989e",5025:"b597364f0cf8b85a",5061:"025d30c3123eeaf6",5136:"ac897c2a56ee392f",5183:"2e5c895a2be76262",5269:"2e771096652dc113",5320:"00f00028b9163a43",5326:"adc4bf49a22f4e67",5331:"4d5d42e5253dbee2",5347:"3192e0021174aaa8",5375:"ebc026e6140b52f1",5424:"4d62f7ee887cd8b9",5482:"0907421e6bf5eeec",5491:"5cc285c4b84dac30",5498:"117ef4b6035a90c5",5518:"6c3dc9afe81a566b",5520:"eab85d7487e77510",5553:"75df19e3cc1246d6",5575:"3d90c643343a1329",5645:"0b86853ae26d9ee3",5670:"2a1b27a6fdb7aa6b",5690:"d30877807cf5c7eb",5712:"574fca2d5f06483a",5728:"8ef773d61765855e",5750:"aa57d25b74640d23",5755:"00015c284ed6fb39",5920:"9db223329ee9e11a",5922:"ce56021f8cc6ebbc",5937:"0827b5b3eb831ff0",5968:"63d5869783fad06f",5997:"226c07d9e6c496e0",6046:"00d36aea7fd5b147",6079:"f9053a63f1b97508",6089:"2dd351186ec85cc3",6099:"43e6595fcf24fc33",6110:"5ce3f75fced27aaf",6144:"f967a8228bcdf663",6155:"19719cff570d6559",6166:"ecd82a9dcb484b97",6290:"e14cf4fc52dd4539",6293:"5914fa73a9f7e777",6311:"5b9c1f74df9a8f67",6331:"758f2f66b92cada9",6339:"bc9301cc22898be0",6360:"ff0a9063773daeeb",6402:"a0070d7688ed986b",6412:"089ee787a294d10b",6455:"12b78a2660d609eb",6563:"5d618b608750f4dc",6572:"ca2463823b9b01a0",6580:"75d53830b434451f",6588:"0fbca957af7d9e6a",6647:"584c5d5cd8dfdb5a",6666:"a82467c542429379",6686:"796c782c33ab8ca9",6701:"bff0efb979f71a17",6704:"f280f0ad0f5d8644",6729:"77a5381ac8f5c9ae",6741:"06fdaf122961074e",6814:"30ff86a90cbfd921",6846:"91283f709c40802c",6859:"bacad166ee995fe0",6887:"6c81234d73740fb9",6927:"aa3914b5fb0d14d4",6931:"3c90e9fe0f7f1e66",6938:"0ccf683bd8aa8410",6948:"2719ed8731963209",6959:"5a803faf27f88e9f",7021:"b71240b1d5f6fbe4",7040:"0b14de1cb2041bfd",7041:"174bf29bd837f06d",7082:"fc816f91214026d7",7113:"39704408fef3d26e",7130:"8d24694735826c22",7142:"dbd2cf8141eece5e",7152:"c7b7f1f5e9622f21",7227:"a6b2f01487bb3d9d",7258:"6cbe24c3bcb63d1d",7425:"8b47c8e398a12dd8",7442:"097194be4a0e7d55",7455:"71d54397710b923c",7465:"867e8ff655666413",7524:"9528295a19a5837a",7525:"f433aaf1fe40c90a",7557:"bdc743175125b3d8",7610:"b1439345dbcf285d",7625:"ab66bd50d2fb61a4",7635:"166d83de1d29e758",7636:"bf91320a9ff695af",7785:"1402a4110c300764",7799:"40b047a634f96a24",7819:"3c56f48c0dc5ad5d",7844:"2bb29faa262f313e",7846:"c9705c044ab8f87a",7884:"390ae2efbbf230c4",7889:"af3e581842f34c28",7921:"c361f794cc208e06",7977:"80e4daa080a79c39",8113:"907baefc7227cfb8",8132:"74491162f08af899",8192:"5918d83ac6a0e2dc",8243:"1450514f8390977e",8257:"dd8f651f1a43fee2",8288:"9bd1d26060cb00a1",8318:"81c7ac586fcbb728",8406:"955ccfbb4b70b8c3",8430:"b1f802dcffa41626",8432:"bd66ac287c569996",8457:"b61d96d8d30215fc",8496:"046efd6301dd804f",8545:"be0c9eea6aae5971",8555:"ab3ee6358f1d64da",8656:"2ceb4e5ff5df2fca",8670:"90ede75068758e82",8704:"a1003918da6654da",8773:"614f1f4f0f845c5e",8835:"5c7821b201f35593",8848:"7c6e47cca9f0059a",8921:"fb24a4d4d4d3265b",8947:"31d3d834d8281fd9",8977:"01b9c429327004e9",9005:"33cf91e617920d4f",9006:"11537833945ec4b8",9015:"c4e40321a6503a1b",9136:"191be42a2fee842c",9178:"48a90097ee3f0b0c",9186:"3af6fcd13ab752fa",9189:"3e9d29bf275ec462",9220:"bc767ebb5adb9f49",9224:"401f9320ee58570b",9240:"d38ddbe94e0bf128",9267:"fbfd65ea31e7448c",9294:"a20485f3282321b3",9302:"726b065f2df0323a",9312:"8121a74b2042bff3",9377:"f9db706e76f04afe",9382:"bc0e11ecc88dd62c",9421:"a5c1dbcfbfdd8f52",9448:"2668b3cd408e86de",9466:"c9d8f45d14235087",9521:"e1240eb862eb32dc",9547:"7729788fff21926f",9565:"8de0671f99d12abe",9569:"2f36b2b1c2f838d7",9605:"dfac539f89cea7a2",9615:"3b62c13af671f714",9648:"8235b6fea7087b3a",9666:"a515fa6d89a71f1c",9680:"d5ceac8967e0b6a8",9690:"7773c72f0eff6dec",9841:"422321143547591a",9842:"3b71b65b30d8ef2a",9996:"18b7bca2f0ab222e"})[e]+".js",l.miniCssF=e=>"static/css/659eccb5db697b76.css",l.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||Function("return this")()}catch(e){if("object"==typeof window)return window}}(),l.o=(e,a)=>Object.prototype.hasOwnProperty.call(e,a),c={},l.l=(e,a,f,d)=>{if(c[e])return void c[e].push(a);if(void 0!==f)for(var b,t,r=document.getElementsByTagName("script"),o=0;o<r.length;o++){var n=r[o];if(n.getAttribute("src")==e||n.getAttribute("data-webpack")=="_N_E:"+f){b=n;break}}b||(t=!0,(b=document.createElement("script")).charset="utf-8",b.timeout=120,l.nc&&b.setAttribute("nonce",l.nc),b.setAttribute("data-webpack","_N_E:"+f),b.src=l.tu(e)),c[e]=[a];var i=(a,f)=>{b.onerror=b.onload=null,clearTimeout(u);var d=c[e];if(delete c[e],b.parentNode&&b.parentNode.removeChild(b),d&&d.forEach(e=>e(f)),a)return a(f)},u=setTimeout(i.bind(null,void 0,{type:"timeout",target:b}),12e4);b.onerror=i.bind(null,b.onerror),b.onload=i.bind(null,b.onload),t&&document.head.appendChild(b)},l.r=e=>{"u">typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.tt=()=>(void 0===d&&(d={createScriptURL:e=>e},"u">typeof trustedTypes&&trustedTypes.createPolicy&&(d=trustedTypes.createPolicy("nextjs#bundler",d))),d),l.tu=e=>l.tt().createScriptURL(e),l.p="/_next/",b={8068:0},l.f.miniCss=(e,a)=>{if(b[e])a.push(b[e]);else 0!==b[e]&&({1838:1})[e]&&a.push(b[e]=new Promise((a,f)=>{var c,d=l.miniCssF(e),b=l.p+d;if(((e,a)=>{for(var f=document.getElementsByTagName("link"),c=0;c<f.length;c++){var d=f[c],b=d.getAttribute("data-href")||d.getAttribute("href");if("stylesheet"===d.rel&&(b===e||b===a))return d}for(var t=document.getElementsByTagName("style"),c=0;c<t.length;c++){var d=t[c],b=d.getAttribute("data-href");if(b===e||b===a)return d}})(d,b))return a();(c=document.createElement("link")).rel="stylesheet",c.type="text/css",c.onerror=c.onload=d=>{if(c.onerror=c.onload=null,"load"===d.type)a();else{var t=d&&("load"===d.type?"missing":d.type),r=d&&d.target&&d.target.href||b,o=Error("Loading CSS chunk "+e+" failed.\n("+r+")");o.code="CSS_CHUNK_LOAD_FAILED",o.type=t,o.request=r,c.parentNode.removeChild(c),f(o)}},c.href=b,function(e){if("function"==typeof _N_E_STYLE_LOAD){let{href:a,onload:f,onerror:c}=e;_N_E_STYLE_LOAD(0===a.indexOf(window.location.origin)?new URL(a).pathname:a).then(()=>null==f?void 0:f.call(e,{type:"load"}),()=>null==c?void 0:c.call(e,{}))}else document.head.appendChild(e)}(c)}).then(()=>{b[e]=0},a=>{throw delete b[e],a}))},t={8068:0,2557:0,7513:0},l.f.j=(e,a)=>{var f=l.o(t,e)?t[e]:void 0;if(0!==f)if(f)a.push(f[2]);else if(/^(1838|2557|7513|8068)$/.test(e))t[e]=0;else{var c=new Promise((a,c)=>f=t[e]=[a,c]);a.push(f[2]=c);var d=l.p+l.u(e),b=Error();l.l(d,a=>{if(l.o(t,e)&&(0!==(f=t[e])&&(t[e]=void 0),f)){var c=a&&("load"===a.type?"missing":a.type),d=a&&a.target&&a.target.src;b.message="Loading chunk "+e+" failed.\n("+c+": "+d+")",b.name="ChunkLoadError",b.type=c,b.request=d,f[1](b)}},"chunk-"+e,e)}},l.O.j=e=>0===t[e],r=(e,a)=>{var f,c,[d,b,r]=a,o=0;if(d.some(e=>0!==t[e])){for(f in b)l.o(b,f)&&(l.m[f]=b[f]);if(r)var n=r(l)}for(e&&e(a);o<d.length;o++)c=d[o],l.o(t,c)&&t[c]&&t[c][0](),t[c]=0;return l.O(n)},(o=self.webpackChunk_N_E=self.webpackChunk_N_E||[]).forEach(r.bind(null,0)),o.push=r.bind(null,o.push.bind(o)),l.nc=void 0})();
1
+ (()=>{"use strict";var e,a,f,c,d,b,t,r,o,n={},i={};function l(e){var a=i[e];if(void 0!==a)return a.exports;var f=i[e]={exports:{}},c=!0;try{n[e].call(f.exports,f,f.exports,l),c=!1}finally{c&&delete i[e]}return f.exports}l.m=n,e=[],l.O=(a,f,c,d)=>{if(f){d=d||0;for(var b=e.length;b>0&&e[b-1][2]>d;b--)e[b]=e[b-1];e[b]=[f,c,d];return}for(var t=1/0,b=0;b<e.length;b++){for(var[f,c,d]=e[b],r=!0,o=0;o<f.length;o++)(!1&d||t>=d)&&Object.keys(l.O).every(e=>l.O[e](f[o]))?f.splice(o--,1):(r=!1,d<t&&(t=d));if(r){e.splice(b--,1);var n=c();void 0!==n&&(a=n)}}return a},l.n=e=>{var a=e&&e.__esModule?()=>e.default:()=>e;return l.d(a,{a:a}),a},f=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,l.t=function(e,c){if(1&c&&(e=this(e)),8&c||"object"==typeof e&&e&&(4&c&&e.__esModule||16&c&&"function"==typeof e.then))return e;var d=Object.create(null);l.r(d);var b={};a=a||[null,f({}),f([]),f(f)];for(var t=2&c&&e;"object"==typeof t&&!~a.indexOf(t);t=f(t))Object.getOwnPropertyNames(t).forEach(a=>b[a]=()=>e[a]);return b.default=()=>e,l.d(d,b),d},l.d=(e,a)=>{for(var f in a)l.o(a,f)&&!l.o(e,f)&&Object.defineProperty(e,f,{enumerable:!0,get:a[f]})},l.f={},l.e=e=>Promise.all(Object.keys(l.f).reduce((a,f)=>(l.f[f](e,a),a),[])),l.u=e=>4986===e?"static/chunks/4986-c2fc8845ce785303.js":"static/chunks/"+(({535:"4ca0cff5",1290:"cee15710",1630:"53c1bd3f",2577:"d1c28714",3879:"cfdf2ac7",3888:"e868780c",4763:"4402d2ac",4814:"799ebd4e",6079:"363642f4",6666:"570e4624",6814:"7d29de82",7442:"92e53eb0",8835:"ce16f5a9",9312:"1cd6e1d3"})[e]||e)+"."+({10:"6cf001c181ce1098",34:"ce581867cf95e24b",36:"b4a553bf2106f6d3",194:"76dbdc07cdc2311d",214:"f6bba63dfa159e01",252:"a9e22657cbf67b42",327:"8ecab0b86d52b597",376:"9e742ff6919b2b54",392:"3cad93691f1b7360",400:"59979e0d3ae126e4",425:"9a3434a28926566a",535:"07efe2bc8f09d72e",541:"cfa15b606745131a",579:"33fc87fb95163c6a",583:"b3efc73cb21097e2",624:"2dd909fbfce98c5e",640:"5bf1e25d0bddf875",698:"e0dda03fd303b96b",794:"06b1ae4fc9cc10a9",816:"f909c891b91aa79f",830:"e49b226c07876df4",840:"a9c535f8868cc4bd",862:"fe3b86ecfb9c0716",893:"c469257bbbbca4f5",966:"57e4cd93c2368d34",968:"237f19aca13543fe",1078:"02294b2934d5bf21",1142:"8248bfb76c695146",1161:"06b33878b495da50",1184:"e80a999422621ed9",1188:"1d4ce94ddc297119",1198:"2a5c215f01a8b74d",1215:"88ed89521e915107",1219:"e27b0bd10b029e40",1247:"64e212daa5bb3925",1248:"36af3903f037c1f5",1274:"ce6906b1fe2e04b8",1280:"1651bb5167170457",1290:"869cef5f741d40c2",1335:"716cc5924662708e",1365:"d7391145ca7f8791",1379:"03e1e0f31bdaa2cc",1410:"77acea37535dbbef",1413:"117c5958c59cc695",1453:"3c24c777a3deca2d",1456:"217c3832b3e76b7f",1501:"549af2c91a889a08",1534:"918a25b2e3daab22",1556:"96abc9ff91b0c383",1583:"03a06dbdde85ac56",1595:"ee61519fde230f60",1630:"2911e2bac119d910",1642:"8c00543baa1aef54",1656:"a9efc78185a5a85f",1664:"6d5b5e3112b66906",1898:"e07b2b1c5e789b06",1932:"b0aea9b52cabe5e8",1977:"a06c8e8d2198f765",2013:"598558c40e51e107",2031:"3877441c49bff5cd",2041:"00f4fa0ab26c6ba5",2062:"4a0ab3400fea0e34",2107:"c627112b02b9e1c8",2130:"441ae0f68863efeb",2160:"e38d781fd5c4cde0",2179:"1bf5fe315e6e132c",2215:"9d2d6fbe90ffcc80",2278:"0b14de1cb2041bfd",2349:"ca15d465c4260b4b",2374:"7560cc5c4b8ed6b0",2397:"0e4dce0a557ac7cf",2403:"3dda71b6482dce3b",2485:"3188870375e9fbee",2489:"fcc70bd67329a70e",2501:"4e65b56602faa956",2503:"bb9d3528c3c7ccae",2504:"159b28843e61aae9",2577:"f52cffdd449a2d14",2614:"8349e4b407470a30",2710:"fe0656d4ebae4d30",2728:"5fed9ac13dfcd1d1",2735:"5a3e65a580bd5af9",2747:"1d85d388314f2904",2765:"2c6d9772af50feed",2783:"e8bbf2c794ef9750",2812:"dcd8179a6b1d76c9",2839:"c9a657d5b4031a19",2895:"0339e755e79ee794",2937:"7e724516d5e6ce42",2954:"7d8daeae6410d4a4",2957:"2dd93db1a16e96b0",2964:"1a0e15f7327680a2",3072:"b6da96c10080c967",3074:"911584369786c26c",3077:"bf82c0c332cc9fec",3082:"159e4ab8f0572597",3095:"8cd478c42cee505a",3111:"1cf85db2faeb20cc",3175:"538ce7aec634bddb",3186:"3665756f8aae3eb7",3194:"5a331fb5e3dc34e9",3229:"5e91f7d0b135266b",3274:"858f7f9216c8b074",3286:"196c508356bb0b66",3288:"f23c8a5183e6b910",3290:"b5ba2646ad49c9fd",3304:"16305e84f4ff6598",3313:"471138e5504646a9",3324:"85e21e3313d0901c",3392:"460f5a753531f501",3419:"78b520dab7e2f96d",3437:"0ad1ecc8c5b8a679",3476:"246c30b47bded3b7",3483:"fc2a0b1185dbaa5d",3492:"ca55498822ad7796",3526:"24826331ab265b39",3606:"e5c833cdda239583",3633:"2526b2d018636ff0",3721:"bf31263de6d5fa46",3782:"8d7b66d0e37dbe76",3787:"78ebe68a043f50ce",3860:"be5b243bf7d6618c",3873:"72a9a21509f713f7",3879:"f50abf4e93e943ea",3888:"f7831045ac345aaa",3942:"085da8d58242eeab",3979:"cea8623f6e9f83ea",4024:"c195dc1fdd2adbea",4053:"90d71fcbe4ffd852",4080:"08a21dae9e93c547",4163:"c6ebefdd9aa5c358",4174:"b6ff75dd91a2a32e",4223:"c10556cd92fc8018",4242:"3a66c58375424a5b",4275:"8f2e531757c2a813",4295:"6c0f4838b0c9c69d",4309:"a887a10e84c44f9e",4350:"b9d80827e8eee01a",4357:"ac4f251cdac34fbe",4358:"f89b14cf7a988731",4370:"ac13b62c811f7ede",4417:"0b5ee948c3518db3",4447:"f86b52aea068b189",4474:"053807fa34066566",4539:"559c223da16b0282",4540:"03df20c499bca840",4553:"ce3eab65a260353e",4567:"7dc81a6e1c7df565",4583:"2a5df6eb3a6ea30b",4593:"676ac488744e1af5",4608:"1ff246d6b4c771f8",4634:"0332a56fdb8e26c0",4650:"61aec912b756287b",4659:"0b14de1cb2041bfd",4678:"867dfae26694be02",4763:"d0d1b1777a45e0b2",4801:"17596dbc1818c262",4814:"ccb85c56e9216a38",4823:"907850963b2bcb70",4829:"402ec60139d36417",4846:"c563765de65a34c7",4900:"b723a916eafdf743",4903:"004f583a12223dac",4963:"0f6678151a159143",4969:"1cb10af9489ea295",4976:"8870319ffbd1c31f",5005:"73745d7fcae6989e",5025:"b597364f0cf8b85a",5061:"025d30c3123eeaf6",5136:"ac897c2a56ee392f",5183:"2e5c895a2be76262",5269:"2e771096652dc113",5320:"00f00028b9163a43",5326:"adc4bf49a22f4e67",5331:"4d5d42e5253dbee2",5347:"3192e0021174aaa8",5375:"ebc026e6140b52f1",5424:"4d62f7ee887cd8b9",5482:"0907421e6bf5eeec",5491:"5cc285c4b84dac30",5498:"117ef4b6035a90c5",5518:"6c3dc9afe81a566b",5520:"eab85d7487e77510",5553:"75df19e3cc1246d6",5575:"3d90c643343a1329",5645:"0b86853ae26d9ee3",5670:"2a1b27a6fdb7aa6b",5690:"d30877807cf5c7eb",5712:"574fca2d5f06483a",5728:"8ef773d61765855e",5750:"aa57d25b74640d23",5755:"00015c284ed6fb39",5920:"9db223329ee9e11a",5922:"ce56021f8cc6ebbc",5937:"0827b5b3eb831ff0",5968:"63d5869783fad06f",5997:"226c07d9e6c496e0",6046:"00d36aea7fd5b147",6079:"f9053a63f1b97508",6089:"2dd351186ec85cc3",6099:"43e6595fcf24fc33",6110:"5ce3f75fced27aaf",6144:"f967a8228bcdf663",6155:"19719cff570d6559",6166:"ecd82a9dcb484b97",6290:"e14cf4fc52dd4539",6293:"5914fa73a9f7e777",6311:"5b9c1f74df9a8f67",6331:"758f2f66b92cada9",6339:"bc9301cc22898be0",6360:"ff0a9063773daeeb",6402:"a0070d7688ed986b",6412:"089ee787a294d10b",6455:"12b78a2660d609eb",6563:"5d618b608750f4dc",6572:"ca2463823b9b01a0",6580:"75d53830b434451f",6588:"0fbca957af7d9e6a",6647:"584c5d5cd8dfdb5a",6666:"a82467c542429379",6686:"796c782c33ab8ca9",6701:"bff0efb979f71a17",6704:"f280f0ad0f5d8644",6729:"77a5381ac8f5c9ae",6741:"06fdaf122961074e",6814:"30ff86a90cbfd921",6846:"91283f709c40802c",6859:"bacad166ee995fe0",6887:"6c81234d73740fb9",6927:"aa3914b5fb0d14d4",6931:"3c90e9fe0f7f1e66",6938:"0ccf683bd8aa8410",6948:"2719ed8731963209",6959:"5a803faf27f88e9f",7021:"b71240b1d5f6fbe4",7040:"0b14de1cb2041bfd",7041:"174bf29bd837f06d",7082:"fc816f91214026d7",7113:"39704408fef3d26e",7130:"8d24694735826c22",7142:"dbd2cf8141eece5e",7152:"c7b7f1f5e9622f21",7227:"a6b2f01487bb3d9d",7258:"6cbe24c3bcb63d1d",7425:"8b47c8e398a12dd8",7442:"097194be4a0e7d55",7455:"71d54397710b923c",7465:"867e8ff655666413",7524:"9528295a19a5837a",7525:"f433aaf1fe40c90a",7557:"bdc743175125b3d8",7610:"b1439345dbcf285d",7625:"ab66bd50d2fb61a4",7635:"166d83de1d29e758",7636:"bf91320a9ff695af",7785:"1402a4110c300764",7799:"40b047a634f96a24",7819:"3c56f48c0dc5ad5d",7844:"2bb29faa262f313e",7846:"c9705c044ab8f87a",7884:"390ae2efbbf230c4",7889:"af3e581842f34c28",7921:"c361f794cc208e06",7977:"80e4daa080a79c39",8113:"907baefc7227cfb8",8132:"74491162f08af899",8192:"5918d83ac6a0e2dc",8243:"1450514f8390977e",8257:"dd8f651f1a43fee2",8288:"9bd1d26060cb00a1",8318:"81c7ac586fcbb728",8406:"955ccfbb4b70b8c3",8430:"b1f802dcffa41626",8432:"bd66ac287c569996",8457:"b61d96d8d30215fc",8496:"046efd6301dd804f",8545:"be0c9eea6aae5971",8555:"ab3ee6358f1d64da",8656:"2ceb4e5ff5df2fca",8670:"90ede75068758e82",8704:"a1003918da6654da",8773:"614f1f4f0f845c5e",8835:"5c7821b201f35593",8848:"7c6e47cca9f0059a",8921:"fb24a4d4d4d3265b",8947:"31d3d834d8281fd9",8977:"01b9c429327004e9",9005:"33cf91e617920d4f",9006:"11537833945ec4b8",9015:"c4e40321a6503a1b",9136:"191be42a2fee842c",9178:"48a90097ee3f0b0c",9186:"3af6fcd13ab752fa",9189:"3e9d29bf275ec462",9220:"bc767ebb5adb9f49",9224:"401f9320ee58570b",9240:"d38ddbe94e0bf128",9267:"fbfd65ea31e7448c",9294:"a20485f3282321b3",9302:"726b065f2df0323a",9312:"8121a74b2042bff3",9377:"f9db706e76f04afe",9382:"bc0e11ecc88dd62c",9421:"a5c1dbcfbfdd8f52",9448:"2668b3cd408e86de",9466:"c9d8f45d14235087",9521:"e1240eb862eb32dc",9547:"7729788fff21926f",9565:"8de0671f99d12abe",9569:"2f36b2b1c2f838d7",9605:"dfac539f89cea7a2",9615:"3b62c13af671f714",9648:"8235b6fea7087b3a",9666:"a515fa6d89a71f1c",9680:"d5ceac8967e0b6a8",9690:"7773c72f0eff6dec",9841:"422321143547591a",9842:"3b71b65b30d8ef2a",9996:"18b7bca2f0ab222e"})[e]+".js",l.miniCssF=e=>"static/css/659eccb5db697b76.css",l.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||Function("return this")()}catch(e){if("object"==typeof window)return window}}(),l.o=(e,a)=>Object.prototype.hasOwnProperty.call(e,a),c={},l.l=(e,a,f,d)=>{if(c[e])return void c[e].push(a);if(void 0!==f)for(var b,t,r=document.getElementsByTagName("script"),o=0;o<r.length;o++){var n=r[o];if(n.getAttribute("src")==e||n.getAttribute("data-webpack")=="_N_E:"+f){b=n;break}}b||(t=!0,(b=document.createElement("script")).charset="utf-8",b.timeout=120,l.nc&&b.setAttribute("nonce",l.nc),b.setAttribute("data-webpack","_N_E:"+f),b.src=l.tu(e)),c[e]=[a];var i=(a,f)=>{b.onerror=b.onload=null,clearTimeout(u);var d=c[e];if(delete c[e],b.parentNode&&b.parentNode.removeChild(b),d&&d.forEach(e=>e(f)),a)return a(f)},u=setTimeout(i.bind(null,void 0,{type:"timeout",target:b}),12e4);b.onerror=i.bind(null,b.onerror),b.onload=i.bind(null,b.onload),t&&document.head.appendChild(b)},l.r=e=>{"u">typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.tt=()=>(void 0===d&&(d={createScriptURL:e=>e},"u">typeof trustedTypes&&trustedTypes.createPolicy&&(d=trustedTypes.createPolicy("nextjs#bundler",d))),d),l.tu=e=>l.tt().createScriptURL(e),l.p="/_next/",b={8068:0},l.f.miniCss=(e,a)=>{if(b[e])a.push(b[e]);else 0!==b[e]&&({1838:1})[e]&&a.push(b[e]=new Promise((a,f)=>{var c,d=l.miniCssF(e),b=l.p+d;if(((e,a)=>{for(var f=document.getElementsByTagName("link"),c=0;c<f.length;c++){var d=f[c],b=d.getAttribute("data-href")||d.getAttribute("href");if("stylesheet"===d.rel&&(b===e||b===a))return d}for(var t=document.getElementsByTagName("style"),c=0;c<t.length;c++){var d=t[c],b=d.getAttribute("data-href");if(b===e||b===a)return d}})(d,b))return a();(c=document.createElement("link")).rel="stylesheet",c.type="text/css",c.onerror=c.onload=d=>{if(c.onerror=c.onload=null,"load"===d.type)a();else{var t=d&&("load"===d.type?"missing":d.type),r=d&&d.target&&d.target.href||b,o=Error("Loading CSS chunk "+e+" failed.\n("+r+")");o.code="CSS_CHUNK_LOAD_FAILED",o.type=t,o.request=r,c.parentNode.removeChild(c),f(o)}},c.href=b,function(e){if("function"==typeof _N_E_STYLE_LOAD){let{href:a,onload:f,onerror:c}=e;_N_E_STYLE_LOAD(0===a.indexOf(window.location.origin)?new URL(a).pathname:a).then(()=>null==f?void 0:f.call(e,{type:"load"}),()=>null==c?void 0:c.call(e,{}))}else document.head.appendChild(e)}(c)}).then(()=>{b[e]=0},a=>{throw delete b[e],a}))},t={8068:0,2557:0,7513:0},l.f.j=(e,a)=>{var f=l.o(t,e)?t[e]:void 0;if(0!==f)if(f)a.push(f[2]);else if(/^(1838|2557|7513|8068)$/.test(e))t[e]=0;else{var c=new Promise((a,c)=>f=t[e]=[a,c]);a.push(f[2]=c);var d=l.p+l.u(e),b=Error();l.l(d,a=>{if(l.o(t,e)&&(0!==(f=t[e])&&(t[e]=void 0),f)){var c=a&&("load"===a.type?"missing":a.type),d=a&&a.target&&a.target.src;b.message="Loading chunk "+e+" failed.\n("+c+": "+d+")",b.name="ChunkLoadError",b.type=c,b.request=d,f[1](b)}},"chunk-"+e,e)}},l.O.j=e=>0===t[e],r=(e,a)=>{var f,c,[d,b,r]=a,o=0;if(d.some(e=>0!==t[e])){for(f in b)l.o(b,f)&&(l.m[f]=b[f]);if(r)var n=r(l)}for(e&&e(a);o<d.length;o++)c=d[o],l.o(t,c)&&t[c]&&t[c][0](),t[c]=0;return l.O(n)},(o=self.webpackChunk_N_E=self.webpackChunk_N_E||[]).forEach(r.bind(null,0)),o.push=r.bind(null,o.push.bind(o)),l.nc=void 0})();
@@ -8,6 +8,8 @@ export interface WebModeLaunchOptions {
8
8
  packageRoot?: string;
9
9
  host?: string;
10
10
  port?: number;
11
+ /** Additional allowed origins for CORS (forwarded as GSD_WEB_ALLOWED_ORIGINS). */
12
+ allowedOrigins?: string[];
11
13
  }
12
14
  export interface ResolvedWebHostBootstrap {
13
15
  ok: true;
package/dist/web-mode.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { randomBytes } from 'node:crypto';
2
- import { exec, spawn } from 'node:child_process';
2
+ import { execFile, spawn } from 'node:child_process';
3
3
  import { existsSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
4
4
  import { request as httpRequest } from 'node:http';
5
5
  import { createServer } from 'node:net';
@@ -10,12 +10,14 @@ const DEFAULT_HOST = '127.0.0.1';
10
10
  const DEFAULT_PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
11
11
  /** Open a URL in the user's default browser. */
12
12
  function openBrowser(url) {
13
- const cmd = process.platform === 'darwin' ? 'open' :
14
- process.platform === 'win32' ? 'start' :
15
- 'xdg-open';
16
- exec(`${cmd} "${url}"`, () => {
17
- // Ignore errors — user can manually open the URL
18
- });
13
+ if (process.platform === 'win32') {
14
+ // PowerShell's Start-Process handles URLs with '&' safely; cmd /c start does not.
15
+ execFile('powershell', ['-c', `Start-Process '${url.replace(/'/g, "''")}'`], () => { });
16
+ }
17
+ else {
18
+ const cmd = process.platform === 'darwin' ? 'open' : 'xdg-open';
19
+ execFile(cmd, [url], () => { });
20
+ }
19
21
  }
20
22
  const WEB_INSTANCES_PATH = join(appRoot, 'web-instances.json');
21
23
  export function readInstanceRegistry(registryPath = WEB_INSTANCES_PATH) {
@@ -301,7 +303,10 @@ async function waitForBootReady(url, timeoutMs = 180_000, stderr, authToken) {
301
303
  const deadline = Date.now() + timeoutMs;
302
304
  const startedAt = Date.now();
303
305
  let lastError = null;
306
+ let lastBody = null;
304
307
  let hostUp = false;
308
+ let consecutive5xx = 0;
309
+ const MAX_CONSECUTIVE_5XX = 3;
305
310
  // Print a progress dot every N ms while waiting so the terminal isn't silent
306
311
  const TICKER_INTERVAL_MS = 5_000;
307
312
  let lastTickAt = startedAt;
@@ -315,14 +320,30 @@ async function waitForBootReady(url, timeoutMs = 180_000, stderr, authToken) {
315
320
  hostUp = true;
316
321
  stderr?.write(`[gsd] Web host ready.\n`);
317
322
  }
323
+ consecutive5xx = 0;
318
324
  // Host responded successfully — it's ready for the browser
319
325
  return;
320
326
  }
327
+ else if (response.statusCode >= 500) {
328
+ consecutive5xx++;
329
+ lastError = `http ${response.statusCode}`;
330
+ lastBody = response.body || null;
331
+ if (consecutive5xx >= MAX_CONSECUTIVE_5XX) {
332
+ const detail = lastBody ? `: ${lastBody.slice(0, 500)}` : '';
333
+ throw new Error(`boot route returned ${MAX_CONSECUTIVE_5XX} consecutive 5xx responses (last: ${response.statusCode})${detail}`);
334
+ }
335
+ }
321
336
  else {
337
+ consecutive5xx = 0;
322
338
  lastError = `http ${response.statusCode}`;
323
339
  }
324
340
  }
325
341
  catch (error) {
342
+ if (error instanceof Error && error.message.startsWith('boot route returned')) {
343
+ throw error;
344
+ }
345
+ // Connection refused, timeout, etc. — transient during cold start
346
+ consecutive5xx = 0;
326
347
  lastError = error instanceof Error ? error.message : String(error);
327
348
  }
328
349
  // Emit a heartbeat line every TICKER_INTERVAL_MS to show we're alive
@@ -381,6 +402,7 @@ export async function launchWebMode(options, deps = {}) {
381
402
  GSD_WEB_PACKAGE_ROOT: resolution.packageRoot,
382
403
  GSD_WEB_HOST_KIND: resolution.kind,
383
404
  ...(resolution.kind === 'source-dev' ? { NEXT_PUBLIC_GSD_DEV: '1' } : {}),
405
+ ...(options.allowedOrigins?.length ? { GSD_WEB_ALLOWED_ORIGINS: options.allowedOrigins.join(',') } : {}),
384
406
  };
385
407
  try {
386
408
  stderr.write(`[gsd] Initialising resources…\n`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gsd-pi",
3
- "version": "2.41.0-dev.0acbce9",
3
+ "version": "2.41.0-dev.3557dc4",
4
4
  "description": "GSD — Get Shit Done coding agent",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -130,6 +130,39 @@ describe("wrapTextWithAnsi", () => {
130
130
  assert.equal(lines[0], "abcde");
131
131
  assert.equal(lines[1], "fghij");
132
132
  });
133
+
134
+ test("carries OSC 8 hyperlink across word-boundary wrap", () => {
135
+ const url = "https://example.com";
136
+ const open = `\x1b]8;;${url}\x07`;
137
+ const close = `\x1b]8;;\x07`;
138
+ const text = `${open}click here please${close}`;
139
+ const lines = native.wrapTextWithAnsi(text, 10);
140
+ assert.ok(lines.length >= 2, `Expected wrapping, got ${lines.length} lines`);
141
+
142
+ // First line should open the hyperlink and close it at the end
143
+ assert.ok(lines[0].startsWith(open), `First line should start with OSC 8 open: ${JSON.stringify(lines[0])}`);
144
+ assert.ok(lines[0].endsWith(close), `First line should end with OSC 8 close: ${JSON.stringify(lines[0])}`);
145
+
146
+ // Second line should re-open the hyperlink
147
+ assert.ok(lines[1].startsWith(open), `Second line should re-open OSC 8: ${JSON.stringify(lines[1])}`);
148
+ });
149
+
150
+ test("carries OSC 8 hyperlink across long-word break", () => {
151
+ const url = "https://accounts.google.com/o/oauth2/v2/auth?client_id=abc&redirect_uri=http://localhost:9004&scope=email&state=xyz";
152
+ const open = `\x1b]8;;${url}\x07`;
153
+ const close = `\x1b]8;;\x07`;
154
+ const text = `${open}${url}${close}`;
155
+ const lines = native.wrapTextWithAnsi(text, 40);
156
+ assert.ok(lines.length >= 2, `Expected wrapping, got ${lines.length} lines`);
157
+
158
+ // Every line except the last should end with close and re-open on next
159
+ for (let i = 0; i < lines.length - 1; i++) {
160
+ assert.ok(lines[i].includes(open), `Line ${i} should contain OSC 8 open`);
161
+ assert.ok(lines[i].endsWith(close), `Line ${i} should end with OSC 8 close`);
162
+ }
163
+ // Last line should contain close
164
+ assert.ok(lines[lines.length - 1].includes(close), `Last line should contain OSC 8 close`);
165
+ });
133
166
  });
134
167
 
135
168
  // ── truncateToWidth ────────────────────────────────────────────────────
@@ -49,7 +49,9 @@ describe("ModelDiscoveryCache — basic operations", () => {
49
49
  cache.set("google", [{ id: "gemini-pro" }]);
50
50
  cache.clear("openai");
51
51
  assert.equal(cache.get("openai"), undefined);
52
- assert.ok(cache.get("google"));
52
+ const googleEntry = cache.get("google");
53
+ assert.ok(googleEntry);
54
+ assert.equal(googleEntry.models[0].id, "gemini-pro");
53
55
  });
54
56
  it("clear without provider removes all entries", () => {
55
57
  const cache = new ModelDiscoveryCache(cachePath);
@@ -1 +1 @@
1
- {"version":3,"file":"discovery-cache.test.js","sourceRoot":"","sources":["../../src/core/discovery-cache.test.ts"],"names":[],"mappings":"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","sourcesContent":["import assert from \"node:assert/strict\";\nimport { existsSync, mkdirSync, rmSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { afterEach, beforeEach, describe, it } from \"node:test\";\nimport { ModelDiscoveryCache } from \"./discovery-cache.js\";\n\nlet testDir: string;\nlet cachePath: string;\n\nbeforeEach(() => {\n\ttestDir = join(tmpdir(), `discovery-cache-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);\n\tmkdirSync(testDir, { recursive: true });\n\tcachePath = join(testDir, \"discovery-cache.json\");\n});\n\nafterEach(() => {\n\ttry {\n\t\trmSync(testDir, { recursive: true, force: true });\n\t} catch {\n\t\t// Cleanup best-effort\n\t}\n});\n\n// ─── basic operations ────────────────────────────────────────────────────────\n\ndescribe(\"ModelDiscoveryCache — basic operations\", () => {\n\tit(\"starts with no entries\", () => {\n\t\tconst cache = new ModelDiscoveryCache(cachePath);\n\t\tassert.equal(cache.get(\"openai\"), undefined);\n\t});\n\n\tit(\"stores and retrieves models\", () => {\n\t\tconst cache = new ModelDiscoveryCache(cachePath);\n\t\tconst models = [{ id: \"gpt-4o\", name: \"GPT-4o\" }];\n\t\tcache.set(\"openai\", models);\n\n\t\tconst entry = cache.get(\"openai\");\n\t\tassert.ok(entry);\n\t\tassert.deepEqual(entry.models, models);\n\t\tassert.ok(entry.fetchedAt > 0);\n\t\tassert.ok(entry.ttlMs > 0);\n\t});\n\n\tit(\"persists to disk and reloads\", () => {\n\t\tconst cache1 = new ModelDiscoveryCache(cachePath);\n\t\tcache1.set(\"openai\", [{ id: \"gpt-4o\" }]);\n\n\t\tconst cache2 = new ModelDiscoveryCache(cachePath);\n\t\tconst entry = cache2.get(\"openai\");\n\t\tassert.ok(entry);\n\t\tassert.equal(entry.models[0].id, \"gpt-4o\");\n\t});\n\n\tit(\"clear removes a specific provider\", () => {\n\t\tconst cache = new ModelDiscoveryCache(cachePath);\n\t\tcache.set(\"openai\", [{ id: \"gpt-4o\" }]);\n\t\tcache.set(\"google\", [{ id: \"gemini-pro\" }]);\n\n\t\tcache.clear(\"openai\");\n\t\tassert.equal(cache.get(\"openai\"), undefined);\n\t\tassert.ok(cache.get(\"google\"));\n\t});\n\n\tit(\"clear without provider removes all entries\", () => {\n\t\tconst cache = new ModelDiscoveryCache(cachePath);\n\t\tcache.set(\"openai\", [{ id: \"gpt-4o\" }]);\n\t\tcache.set(\"google\", [{ id: \"gemini-pro\" }]);\n\n\t\tcache.clear();\n\t\tassert.equal(cache.get(\"openai\"), undefined);\n\t\tassert.equal(cache.get(\"google\"), undefined);\n\t});\n});\n\n// ─── staleness ───────────────────────────────────────────────────────────────\n\ndescribe(\"ModelDiscoveryCache — staleness\", () => {\n\tit(\"newly set entries are not stale\", () => {\n\t\tconst cache = new ModelDiscoveryCache(cachePath);\n\t\tcache.set(\"openai\", [{ id: \"gpt-4o\" }]);\n\t\tassert.equal(cache.isStale(\"openai\"), false);\n\t});\n\n\tit(\"missing providers are stale\", () => {\n\t\tconst cache = new ModelDiscoveryCache(cachePath);\n\t\tassert.equal(cache.isStale(\"unknown\"), true);\n\t});\n\n\tit(\"entries with expired TTL are stale\", () => {\n\t\tconst cache = new ModelDiscoveryCache(cachePath);\n\t\tcache.set(\"openai\", [{ id: \"gpt-4o\" }], 1); // 1ms TTL\n\n\t\t// Wait for TTL to expire\n\t\tconst start = Date.now();\n\t\twhile (Date.now() - start < 5) {\n\t\t\t// busy wait\n\t\t}\n\n\t\tassert.equal(cache.isStale(\"openai\"), true);\n\t});\n});\n\n// ─── getAll ──────────────────────────────────────────────────────────────────\n\ndescribe(\"ModelDiscoveryCache — getAll\", () => {\n\tit(\"returns non-stale entries by default\", () => {\n\t\tconst cache = new ModelDiscoveryCache(cachePath);\n\t\tcache.set(\"openai\", [{ id: \"gpt-4o\" }]);\n\t\tcache.set(\"stale\", [{ id: \"old\" }], 1);\n\n\t\t// Wait for stale TTL\n\t\tconst start = Date.now();\n\t\twhile (Date.now() - start < 5) {\n\t\t\t// busy wait\n\t\t}\n\n\t\tconst all = cache.getAll();\n\t\tassert.ok(all.has(\"openai\"));\n\t\tassert.ok(!all.has(\"stale\"));\n\t});\n\n\tit(\"returns all entries when includeStale is true\", () => {\n\t\tconst cache = new ModelDiscoveryCache(cachePath);\n\t\tcache.set(\"openai\", [{ id: \"gpt-4o\" }]);\n\t\tcache.set(\"stale\", [{ id: \"old\" }], 1);\n\n\t\t// Wait for stale TTL\n\t\tconst start = Date.now();\n\t\twhile (Date.now() - start < 5) {\n\t\t\t// busy wait\n\t\t}\n\n\t\tconst all = cache.getAll(true);\n\t\tassert.ok(all.has(\"openai\"));\n\t\tassert.ok(all.has(\"stale\"));\n\t});\n});\n\n// ─── edge cases ──────────────────────────────────────────────────────────────\n\ndescribe(\"ModelDiscoveryCache — edge cases\", () => {\n\tit(\"handles corrupted cache file gracefully\", () => {\n\t\twriteFileSync(cachePath, \"not valid json\", \"utf-8\");\n\t\tconst cache = new ModelDiscoveryCache(cachePath);\n\t\tassert.equal(cache.get(\"openai\"), undefined);\n\t});\n\n\tit(\"handles wrong version gracefully\", () => {\n\t\twriteFileSync(cachePath, JSON.stringify({ version: 99, entries: {} }), \"utf-8\");\n\t\tconst cache = new ModelDiscoveryCache(cachePath);\n\t\tassert.equal(cache.get(\"openai\"), undefined);\n\t});\n\n\tit(\"handles missing cache file\", () => {\n\t\tconst cache = new ModelDiscoveryCache(join(testDir, \"nonexistent\", \"cache.json\"));\n\t\tassert.equal(cache.get(\"openai\"), undefined);\n\t});\n\n\tit(\"overwrites existing entry for same provider\", () => {\n\t\tconst cache = new ModelDiscoveryCache(cachePath);\n\t\tcache.set(\"openai\", [{ id: \"gpt-4o\" }]);\n\t\tcache.set(\"openai\", [{ id: \"gpt-4o-mini\" }]);\n\n\t\tconst entry = cache.get(\"openai\");\n\t\tassert.ok(entry);\n\t\tassert.equal(entry.models.length, 1);\n\t\tassert.equal(entry.models[0].id, \"gpt-4o-mini\");\n\t});\n});\n"]}
1
+ {"version":3,"file":"discovery-cache.test.js","sourceRoot":"","sources":["../../src/core/discovery-cache.test.ts"],"names":[],"mappings":"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","sourcesContent":["import assert from \"node:assert/strict\";\nimport { mkdirSync, rmSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { afterEach, beforeEach, describe, it } from \"node:test\";\nimport { ModelDiscoveryCache } from \"./discovery-cache.js\";\n\nlet testDir: string;\nlet cachePath: string;\n\nbeforeEach(() => {\n\ttestDir = join(tmpdir(), `discovery-cache-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);\n\tmkdirSync(testDir, { recursive: true });\n\tcachePath = join(testDir, \"discovery-cache.json\");\n});\n\nafterEach(() => {\n\ttry {\n\t\trmSync(testDir, { recursive: true, force: true });\n\t} catch {\n\t\t// Cleanup best-effort\n\t}\n});\n\n// ─── basic operations ────────────────────────────────────────────────────────\n\ndescribe(\"ModelDiscoveryCache — basic operations\", () => {\n\tit(\"starts with no entries\", () => {\n\t\tconst cache = new ModelDiscoveryCache(cachePath);\n\t\tassert.equal(cache.get(\"openai\"), undefined);\n\t});\n\n\tit(\"stores and retrieves models\", () => {\n\t\tconst cache = new ModelDiscoveryCache(cachePath);\n\t\tconst models = [{ id: \"gpt-4o\", name: \"GPT-4o\" }];\n\t\tcache.set(\"openai\", models);\n\n\t\tconst entry = cache.get(\"openai\");\n\t\tassert.ok(entry);\n\t\tassert.deepEqual(entry.models, models);\n\t\tassert.ok(entry.fetchedAt > 0);\n\t\tassert.ok(entry.ttlMs > 0);\n\t});\n\n\tit(\"persists to disk and reloads\", () => {\n\t\tconst cache1 = new ModelDiscoveryCache(cachePath);\n\t\tcache1.set(\"openai\", [{ id: \"gpt-4o\" }]);\n\n\t\tconst cache2 = new ModelDiscoveryCache(cachePath);\n\t\tconst entry = cache2.get(\"openai\");\n\t\tassert.ok(entry);\n\t\tassert.equal(entry.models[0].id, \"gpt-4o\");\n\t});\n\n\tit(\"clear removes a specific provider\", () => {\n\t\tconst cache = new ModelDiscoveryCache(cachePath);\n\t\tcache.set(\"openai\", [{ id: \"gpt-4o\" }]);\n\t\tcache.set(\"google\", [{ id: \"gemini-pro\" }]);\n\n\t\tcache.clear(\"openai\");\n\t\tassert.equal(cache.get(\"openai\"), undefined);\n\t\tconst googleEntry = cache.get(\"google\");\n\t\tassert.ok(googleEntry);\n\t\tassert.equal(googleEntry.models[0].id, \"gemini-pro\");\n\t});\n\n\tit(\"clear without provider removes all entries\", () => {\n\t\tconst cache = new ModelDiscoveryCache(cachePath);\n\t\tcache.set(\"openai\", [{ id: \"gpt-4o\" }]);\n\t\tcache.set(\"google\", [{ id: \"gemini-pro\" }]);\n\n\t\tcache.clear();\n\t\tassert.equal(cache.get(\"openai\"), undefined);\n\t\tassert.equal(cache.get(\"google\"), undefined);\n\t});\n});\n\n// ─── staleness ───────────────────────────────────────────────────────────────\n\ndescribe(\"ModelDiscoveryCache — staleness\", () => {\n\tit(\"newly set entries are not stale\", () => {\n\t\tconst cache = new ModelDiscoveryCache(cachePath);\n\t\tcache.set(\"openai\", [{ id: \"gpt-4o\" }]);\n\t\tassert.equal(cache.isStale(\"openai\"), false);\n\t});\n\n\tit(\"missing providers are stale\", () => {\n\t\tconst cache = new ModelDiscoveryCache(cachePath);\n\t\tassert.equal(cache.isStale(\"unknown\"), true);\n\t});\n\n\tit(\"entries with expired TTL are stale\", () => {\n\t\tconst cache = new ModelDiscoveryCache(cachePath);\n\t\tcache.set(\"openai\", [{ id: \"gpt-4o\" }], 1); // 1ms TTL\n\n\t\t// Wait for TTL to expire\n\t\tconst start = Date.now();\n\t\twhile (Date.now() - start < 5) {\n\t\t\t// busy wait\n\t\t}\n\n\t\tassert.equal(cache.isStale(\"openai\"), true);\n\t});\n});\n\n// ─── getAll ──────────────────────────────────────────────────────────────────\n\ndescribe(\"ModelDiscoveryCache — getAll\", () => {\n\tit(\"returns non-stale entries by default\", () => {\n\t\tconst cache = new ModelDiscoveryCache(cachePath);\n\t\tcache.set(\"openai\", [{ id: \"gpt-4o\" }]);\n\t\tcache.set(\"stale\", [{ id: \"old\" }], 1);\n\n\t\t// Wait for stale TTL\n\t\tconst start = Date.now();\n\t\twhile (Date.now() - start < 5) {\n\t\t\t// busy wait\n\t\t}\n\n\t\tconst all = cache.getAll();\n\t\tassert.ok(all.has(\"openai\"));\n\t\tassert.ok(!all.has(\"stale\"));\n\t});\n\n\tit(\"returns all entries when includeStale is true\", () => {\n\t\tconst cache = new ModelDiscoveryCache(cachePath);\n\t\tcache.set(\"openai\", [{ id: \"gpt-4o\" }]);\n\t\tcache.set(\"stale\", [{ id: \"old\" }], 1);\n\n\t\t// Wait for stale TTL\n\t\tconst start = Date.now();\n\t\twhile (Date.now() - start < 5) {\n\t\t\t// busy wait\n\t\t}\n\n\t\tconst all = cache.getAll(true);\n\t\tassert.ok(all.has(\"openai\"));\n\t\tassert.ok(all.has(\"stale\"));\n\t});\n});\n\n// ─── edge cases ──────────────────────────────────────────────────────────────\n\ndescribe(\"ModelDiscoveryCache — edge cases\", () => {\n\tit(\"handles corrupted cache file gracefully\", () => {\n\t\twriteFileSync(cachePath, \"not valid json\", \"utf-8\");\n\t\tconst cache = new ModelDiscoveryCache(cachePath);\n\t\tassert.equal(cache.get(\"openai\"), undefined);\n\t});\n\n\tit(\"handles wrong version gracefully\", () => {\n\t\twriteFileSync(cachePath, JSON.stringify({ version: 99, entries: {} }), \"utf-8\");\n\t\tconst cache = new ModelDiscoveryCache(cachePath);\n\t\tassert.equal(cache.get(\"openai\"), undefined);\n\t});\n\n\tit(\"handles missing cache file\", () => {\n\t\tconst cache = new ModelDiscoveryCache(join(testDir, \"nonexistent\", \"cache.json\"));\n\t\tassert.equal(cache.get(\"openai\"), undefined);\n\t});\n\n\tit(\"overwrites existing entry for same provider\", () => {\n\t\tconst cache = new ModelDiscoveryCache(cachePath);\n\t\tcache.set(\"openai\", [{ id: \"gpt-4o\" }]);\n\t\tcache.set(\"openai\", [{ id: \"gpt-4o-mini\" }]);\n\n\t\tconst entry = cache.get(\"openai\");\n\t\tassert.ok(entry);\n\t\tassert.equal(entry.models.length, 1);\n\t\tassert.equal(entry.models[0].id, \"gpt-4o-mini\");\n\t});\n});\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"login-dialog.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/login-dialog.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,KAAK,SAAS,EAA6C,KAAK,GAAG,EAAE,MAAM,aAAa,CAAC;AAM7G;;;;;;;GAOG;AACH,qBAAa,oBAAqB,SAAQ,SAAU,YAAW,SAAS;IAsBtE,OAAO,CAAC,UAAU;IArBnB,OAAO,CAAC,gBAAgB,CAAY;IACpC,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,eAAe,CAAyB;IAChD,OAAO,CAAC,aAAa,CAAC,CAA0B;IAChD,OAAO,CAAC,aAAa,CAAC,CAAyB;IAC/C,OAAO,CAAC,QAAQ,CAAS;IAGzB,OAAO,CAAC,QAAQ,CAAS;IACzB,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAGzB;gBAGA,GAAG,EAAE,GAAG,EACR,UAAU,EAAE,MAAM,EACV,UAAU,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI;IAwCjE,IAAI,MAAM,IAAI,WAAW,CAExB;IAED;;;OAGG;IACH,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,MAAM;IASd;;;;OAIG;IACH,OAAO,IAAI,IAAI;IAOf;;OAEG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI;IA0BlD;;OAEG;IACH,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAgBhD;;;OAGG;IACH,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAuBlE;;OAEG;IACH,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAOlC;;OAEG;IACH,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAKnC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;CAa/B"}
1
+ {"version":3,"file":"login-dialog.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/login-dialog.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,KAAK,SAAS,EAA8D,KAAK,GAAG,EAAE,MAAM,aAAa,CAAC;AAM9H;;;;;;;GAOG;AACH,qBAAa,oBAAqB,SAAQ,SAAU,YAAW,SAAS;IAsBtE,OAAO,CAAC,UAAU;IArBnB,OAAO,CAAC,gBAAgB,CAAY;IACpC,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,eAAe,CAAyB;IAChD,OAAO,CAAC,aAAa,CAAC,CAA0B;IAChD,OAAO,CAAC,aAAa,CAAC,CAAyB;IAC/C,OAAO,CAAC,QAAQ,CAAS;IAGzB,OAAO,CAAC,QAAQ,CAAS;IACzB,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAGzB;gBAGA,GAAG,EAAE,GAAG,EACR,UAAU,EAAE,MAAM,EACV,UAAU,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI;IAwCjE,IAAI,MAAM,IAAI,WAAW,CAExB;IAED;;;OAGG;IACH,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,MAAM;IASd;;;;OAIG;IACH,OAAO,IAAI,IAAI;IAOf;;OAEG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI;IA8BlD;;OAEG;IACH,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAgBhD;;;OAGG;IACH,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAuBlE;;OAEG;IACH,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAOlC;;OAEG;IACH,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAKnC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;CAa/B"}
@@ -1,7 +1,7 @@
1
1
  // GSD Login Dialog Component — OAuth login flow UI
2
2
  // Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
3
3
  import { getOAuthProviders } from "@gsd/pi-ai/oauth";
4
- import { Container, getEditorKeybindings, Input, Spacer, Text } from "@gsd/pi-tui";
4
+ import { Container, getEditorKeybindings, Input, Spacer, Text, truncateToWidth } from "@gsd/pi-tui";
5
5
  import { execFile } from "child_process";
6
6
  import { theme } from "../theme/theme.js";
7
7
  import { DynamicBorder } from "./dynamic-border.js";
@@ -100,18 +100,21 @@ export class LoginDialogComponent extends Container {
100
100
  showAuth(url, instructions) {
101
101
  this.contentContainer.clear();
102
102
  this.contentContainer.addChild(new Spacer(1));
103
- this.contentContainer.addChild(new Text(theme.fg("accent", url), 1, 0));
103
+ // Truncate the visible URL text so it never wraps (which would break
104
+ // the OSC 8 hyperlink). The full URL is still the link target.
105
+ const maxUrlWidth = Math.max(20, this.tui.terminal.columns - 4);
106
+ const displayUrl = truncateToWidth(url, maxUrlWidth);
107
+ const urlLink = `\x1b]8;;${url}\x07${theme.fg("accent", displayUrl)}\x1b]8;;\x07`;
108
+ this.contentContainer.addChild(new Text(urlLink, 1, 0));
104
109
  const clickHint = process.platform === "darwin" ? "Cmd+click to open" : "Ctrl+click to open";
105
- const hyperlink = `\x1b]8;;${url}\x07${clickHint}\x1b]8;;\x07`;
106
- this.contentContainer.addChild(new Text(theme.fg("dim", hyperlink), 1, 0));
110
+ this.contentContainer.addChild(new Text(theme.fg("dim", clickHint), 1, 0));
107
111
  if (instructions) {
108
112
  this.contentContainer.addChild(new Spacer(1));
109
113
  this.contentContainer.addChild(new Text(theme.fg("warning", instructions), 1, 0));
110
114
  }
111
- // Try to open browser on Windows, `start` needs an empty title arg
112
- // so it treats the URL as a target, not a window title
115
+ // PowerShell's Start-Process handles URLs with '&' safely; cmd /c start does not.
113
116
  if (process.platform === "win32") {
114
- execFile("cmd", ["/c", "start", "", url], () => { });
117
+ execFile("powershell", ["-c", `Start-Process '${url.replace(/'/g, "''")}'`], () => { });
115
118
  }
116
119
  else {
117
120
  const openCmd = process.platform === "darwin" ? "open" : "xdg-open";
@@ -1 +1 @@
1
- {"version":3,"file":"login-dialog.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/login-dialog.ts"],"names":[],"mappings":"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","sourcesContent":["// GSD Login Dialog Component — OAuth login flow UI\n// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>\nimport { getOAuthProviders } from \"@gsd/pi-ai/oauth\";\nimport { Container, type Focusable, getEditorKeybindings, Input, Spacer, Text, type TUI } from \"@gsd/pi-tui\";\nimport { execFile } from \"child_process\";\nimport { theme } from \"../theme/theme.js\";\nimport { DynamicBorder } from \"./dynamic-border.js\";\nimport { keyHint } from \"./keybinding-hints.js\";\n\n/**\n * Login dialog component - replaces editor during OAuth login flow.\n *\n * Guards against stuck UI by:\n * - Rejecting any outstanding promise before creating a new one\n * - Listening on the internal AbortSignal so external cancellation cleans up\n * - Exposing a public dispose() method so the caller can force-cleanup\n */\nexport class LoginDialogComponent extends Container implements Focusable {\n\tprivate contentContainer: Container;\n\tprivate input: Input;\n\tprivate tui: TUI;\n\tprivate abortController = new AbortController();\n\tprivate inputResolver?: (value: string) => void;\n\tprivate inputRejecter?: (error: Error) => void;\n\tprivate disposed = false;\n\n\t// Focusable implementation - propagate to input for IME cursor positioning\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.input.focused = value;\n\t}\n\n\tconstructor(\n\t\ttui: TUI,\n\t\tproviderId: string,\n\t\tprivate onComplete: (success: boolean, message?: string) => void,\n\t) {\n\t\tsuper();\n\t\tthis.tui = tui;\n\n\t\tconst providerInfo = getOAuthProviders().find((p) => p.id === providerId);\n\t\tconst providerName = providerInfo?.name || providerId;\n\n\t\t// Top border\n\t\tthis.addChild(new DynamicBorder());\n\n\t\t// Title\n\t\tthis.addChild(new Text(theme.fg(\"warning\", `Login to ${providerName}`), 1, 0));\n\n\t\t// Dynamic content area\n\t\tthis.contentContainer = new Container();\n\t\tthis.addChild(this.contentContainer);\n\n\t\t// Input (always present, used when needed)\n\t\tthis.input = new Input();\n\t\tthis.input.onSubmit = () => {\n\t\t\tif (this.inputResolver) {\n\t\t\t\tthis.inputResolver(this.input.getValue());\n\t\t\t\tthis.inputResolver = undefined;\n\t\t\t\tthis.inputRejecter = undefined;\n\t\t\t}\n\t\t};\n\t\tthis.input.onEscape = () => {\n\t\t\tthis.cancel();\n\t\t};\n\n\t\t// Bottom border\n\t\tthis.addChild(new DynamicBorder());\n\n\t\t// Wire abort signal so external cancellation rejects pending promises\n\t\tthis.abortController.signal.addEventListener(\"abort\", () => {\n\t\t\tthis.rejectPending(\"Login cancelled\");\n\t\t});\n\t}\n\n\tget signal(): AbortSignal {\n\t\treturn this.abortController.signal;\n\t}\n\n\t/**\n\t * Reject any outstanding input promise without triggering a full cancel.\n\t * Safe to call multiple times.\n\t */\n\tprivate rejectPending(reason: string): void {\n\t\tif (this.inputRejecter) {\n\t\t\tconst rejecter = this.inputRejecter;\n\t\t\tthis.inputResolver = undefined;\n\t\t\tthis.inputRejecter = undefined;\n\t\t\trejecter(new Error(reason));\n\t\t}\n\t}\n\n\tprivate cancel(): void {\n\t\tif (this.disposed) return;\n\t\tthis.abortController.abort();\n\t\t// rejectPending is also called by the abort listener, but guard with\n\t\t// disposed flag and nulling to avoid double-reject\n\t\tthis.rejectPending(\"Login cancelled\");\n\t\tthis.onComplete(false, \"Login cancelled\");\n\t}\n\n\t/**\n\t * Force-dispose the dialog, rejecting any pending promises.\n\t * Called by the parent when restoring the editor, as a safety net\n\t * to ensure no promises are left dangling.\n\t */\n\tdispose(): void {\n\t\tif (this.disposed) return;\n\t\tthis.disposed = true;\n\t\tthis.abortController.abort();\n\t\tthis.rejectPending(\"Login dialog disposed\");\n\t}\n\n\t/**\n\t * Called by onAuth callback - show URL and optional instructions\n\t */\n\tshowAuth(url: string, instructions?: string): void {\n\t\tthis.contentContainer.clear();\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"accent\", url), 1, 0));\n\n\t\tconst clickHint = process.platform === \"darwin\" ? \"Cmd+click to open\" : \"Ctrl+click to open\";\n\t\tconst hyperlink = `\\x1b]8;;${url}\\x07${clickHint}\\x1b]8;;\\x07`;\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"dim\", hyperlink), 1, 0));\n\n\t\tif (instructions) {\n\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\tthis.contentContainer.addChild(new Text(theme.fg(\"warning\", instructions), 1, 0));\n\t\t}\n\n\t\t// Try to open browser — on Windows, `start` needs an empty title arg\n\t\t// so it treats the URL as a target, not a window title\n\t\tif (process.platform === \"win32\") {\n\t\t\texecFile(\"cmd\", [\"/c\", \"start\", \"\", url], () => {});\n\t\t} else {\n\t\t\tconst openCmd = process.platform === \"darwin\" ? \"open\" : \"xdg-open\";\n\t\t\texecFile(openCmd, [url], () => {});\n\t\t}\n\n\t\tthis.tui.requestRender();\n\t}\n\n\t/**\n\t * Show input for manual code/URL entry (for callback server providers)\n\t */\n\tshowManualInput(prompt: string): Promise<string> {\n\t\t// Reject any previous pending promise before creating a new one\n\t\tthis.rejectPending(\"Superseded by new input prompt\");\n\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"dim\", prompt), 1, 0));\n\t\tthis.contentContainer.addChild(this.input);\n\t\tthis.contentContainer.addChild(new Text(`(${keyHint(\"selectCancel\", \"to cancel\")})`, 1, 0));\n\t\tthis.tui.requestRender();\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tthis.inputResolver = resolve;\n\t\t\tthis.inputRejecter = reject;\n\t\t});\n\t}\n\n\t/**\n\t * Called by onPrompt callback - show prompt and wait for input\n\t * Note: Does NOT clear content, appends to existing (preserves URL from showAuth)\n\t */\n\tshowPrompt(message: string, placeholder?: string): Promise<string> {\n\t\t// Reject any previous pending promise before creating a new one\n\t\tthis.rejectPending(\"Superseded by new input prompt\");\n\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"text\", message), 1, 0));\n\t\tif (placeholder) {\n\t\t\tthis.contentContainer.addChild(new Text(theme.fg(\"dim\", `e.g., ${placeholder}`), 1, 0));\n\t\t}\n\t\tthis.contentContainer.addChild(this.input);\n\t\tthis.contentContainer.addChild(\n\t\t\tnew Text(`(${keyHint(\"selectCancel\", \"to cancel,\")} ${keyHint(\"selectConfirm\", \"to submit\")})`, 1, 0),\n\t\t);\n\n\t\tthis.input.setValue(\"\");\n\t\tthis.tui.requestRender();\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tthis.inputResolver = resolve;\n\t\t\tthis.inputRejecter = reject;\n\t\t});\n\t}\n\n\t/**\n\t * Show waiting message (for polling flows like GitHub Copilot)\n\t */\n\tshowWaiting(message: string): void {\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"dim\", message), 1, 0));\n\t\tthis.contentContainer.addChild(new Text(`(${keyHint(\"selectCancel\", \"to cancel\")})`, 1, 0));\n\t\tthis.tui.requestRender();\n\t}\n\n\t/**\n\t * Called by onProgress callback\n\t */\n\tshowProgress(message: string): void {\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"dim\", message), 1, 0));\n\t\tthis.tui.requestRender();\n\t}\n\n\thandleInput(data: string): void {\n\t\tif (this.disposed) return;\n\n\t\tconst kb = getEditorKeybindings();\n\n\t\tif (kb.matches(data, \"selectCancel\")) {\n\t\t\tthis.cancel();\n\t\t\treturn;\n\t\t}\n\n\t\t// Pass to input\n\t\tthis.input.handleInput(data);\n\t}\n}\n"]}
1
+ {"version":3,"file":"login-dialog.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/login-dialog.ts"],"names":[],"mappings":"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","sourcesContent":["// GSD Login Dialog Component — OAuth login flow UI\n// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>\nimport { getOAuthProviders } from \"@gsd/pi-ai/oauth\";\nimport { Container, type Focusable, getEditorKeybindings, Input, Spacer, Text, truncateToWidth, type TUI } from \"@gsd/pi-tui\";\nimport { execFile } from \"child_process\";\nimport { theme } from \"../theme/theme.js\";\nimport { DynamicBorder } from \"./dynamic-border.js\";\nimport { keyHint } from \"./keybinding-hints.js\";\n\n/**\n * Login dialog component - replaces editor during OAuth login flow.\n *\n * Guards against stuck UI by:\n * - Rejecting any outstanding promise before creating a new one\n * - Listening on the internal AbortSignal so external cancellation cleans up\n * - Exposing a public dispose() method so the caller can force-cleanup\n */\nexport class LoginDialogComponent extends Container implements Focusable {\n\tprivate contentContainer: Container;\n\tprivate input: Input;\n\tprivate tui: TUI;\n\tprivate abortController = new AbortController();\n\tprivate inputResolver?: (value: string) => void;\n\tprivate inputRejecter?: (error: Error) => void;\n\tprivate disposed = false;\n\n\t// Focusable implementation - propagate to input for IME cursor positioning\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.input.focused = value;\n\t}\n\n\tconstructor(\n\t\ttui: TUI,\n\t\tproviderId: string,\n\t\tprivate onComplete: (success: boolean, message?: string) => void,\n\t) {\n\t\tsuper();\n\t\tthis.tui = tui;\n\n\t\tconst providerInfo = getOAuthProviders().find((p) => p.id === providerId);\n\t\tconst providerName = providerInfo?.name || providerId;\n\n\t\t// Top border\n\t\tthis.addChild(new DynamicBorder());\n\n\t\t// Title\n\t\tthis.addChild(new Text(theme.fg(\"warning\", `Login to ${providerName}`), 1, 0));\n\n\t\t// Dynamic content area\n\t\tthis.contentContainer = new Container();\n\t\tthis.addChild(this.contentContainer);\n\n\t\t// Input (always present, used when needed)\n\t\tthis.input = new Input();\n\t\tthis.input.onSubmit = () => {\n\t\t\tif (this.inputResolver) {\n\t\t\t\tthis.inputResolver(this.input.getValue());\n\t\t\t\tthis.inputResolver = undefined;\n\t\t\t\tthis.inputRejecter = undefined;\n\t\t\t}\n\t\t};\n\t\tthis.input.onEscape = () => {\n\t\t\tthis.cancel();\n\t\t};\n\n\t\t// Bottom border\n\t\tthis.addChild(new DynamicBorder());\n\n\t\t// Wire abort signal so external cancellation rejects pending promises\n\t\tthis.abortController.signal.addEventListener(\"abort\", () => {\n\t\t\tthis.rejectPending(\"Login cancelled\");\n\t\t});\n\t}\n\n\tget signal(): AbortSignal {\n\t\treturn this.abortController.signal;\n\t}\n\n\t/**\n\t * Reject any outstanding input promise without triggering a full cancel.\n\t * Safe to call multiple times.\n\t */\n\tprivate rejectPending(reason: string): void {\n\t\tif (this.inputRejecter) {\n\t\t\tconst rejecter = this.inputRejecter;\n\t\t\tthis.inputResolver = undefined;\n\t\t\tthis.inputRejecter = undefined;\n\t\t\trejecter(new Error(reason));\n\t\t}\n\t}\n\n\tprivate cancel(): void {\n\t\tif (this.disposed) return;\n\t\tthis.abortController.abort();\n\t\t// rejectPending is also called by the abort listener, but guard with\n\t\t// disposed flag and nulling to avoid double-reject\n\t\tthis.rejectPending(\"Login cancelled\");\n\t\tthis.onComplete(false, \"Login cancelled\");\n\t}\n\n\t/**\n\t * Force-dispose the dialog, rejecting any pending promises.\n\t * Called by the parent when restoring the editor, as a safety net\n\t * to ensure no promises are left dangling.\n\t */\n\tdispose(): void {\n\t\tif (this.disposed) return;\n\t\tthis.disposed = true;\n\t\tthis.abortController.abort();\n\t\tthis.rejectPending(\"Login dialog disposed\");\n\t}\n\n\t/**\n\t * Called by onAuth callback - show URL and optional instructions\n\t */\n\tshowAuth(url: string, instructions?: string): void {\n\t\tthis.contentContainer.clear();\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\n\t\t// Truncate the visible URL text so it never wraps (which would break\n\t\t// the OSC 8 hyperlink). The full URL is still the link target.\n\t\tconst maxUrlWidth = Math.max(20, this.tui.terminal.columns - 4);\n\t\tconst displayUrl = truncateToWidth(url, maxUrlWidth);\n\t\tconst urlLink = `\\x1b]8;;${url}\\x07${theme.fg(\"accent\", displayUrl)}\\x1b]8;;\\x07`;\n\t\tthis.contentContainer.addChild(new Text(urlLink, 1, 0));\n\n\t\tconst clickHint = process.platform === \"darwin\" ? \"Cmd+click to open\" : \"Ctrl+click to open\";\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"dim\", clickHint), 1, 0));\n\n\t\tif (instructions) {\n\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\tthis.contentContainer.addChild(new Text(theme.fg(\"warning\", instructions), 1, 0));\n\t\t}\n\n\t\t// PowerShell's Start-Process handles URLs with '&' safely; cmd /c start does not.\n\t\tif (process.platform === \"win32\") {\n\t\t\texecFile(\"powershell\", [\"-c\", `Start-Process '${url.replace(/'/g, \"''\")}'`], () => {});\n\t\t} else {\n\t\t\tconst openCmd = process.platform === \"darwin\" ? \"open\" : \"xdg-open\";\n\t\t\texecFile(openCmd, [url], () => {});\n\t\t}\n\n\t\tthis.tui.requestRender();\n\t}\n\n\t/**\n\t * Show input for manual code/URL entry (for callback server providers)\n\t */\n\tshowManualInput(prompt: string): Promise<string> {\n\t\t// Reject any previous pending promise before creating a new one\n\t\tthis.rejectPending(\"Superseded by new input prompt\");\n\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"dim\", prompt), 1, 0));\n\t\tthis.contentContainer.addChild(this.input);\n\t\tthis.contentContainer.addChild(new Text(`(${keyHint(\"selectCancel\", \"to cancel\")})`, 1, 0));\n\t\tthis.tui.requestRender();\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tthis.inputResolver = resolve;\n\t\t\tthis.inputRejecter = reject;\n\t\t});\n\t}\n\n\t/**\n\t * Called by onPrompt callback - show prompt and wait for input\n\t * Note: Does NOT clear content, appends to existing (preserves URL from showAuth)\n\t */\n\tshowPrompt(message: string, placeholder?: string): Promise<string> {\n\t\t// Reject any previous pending promise before creating a new one\n\t\tthis.rejectPending(\"Superseded by new input prompt\");\n\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"text\", message), 1, 0));\n\t\tif (placeholder) {\n\t\t\tthis.contentContainer.addChild(new Text(theme.fg(\"dim\", `e.g., ${placeholder}`), 1, 0));\n\t\t}\n\t\tthis.contentContainer.addChild(this.input);\n\t\tthis.contentContainer.addChild(\n\t\t\tnew Text(`(${keyHint(\"selectCancel\", \"to cancel,\")} ${keyHint(\"selectConfirm\", \"to submit\")})`, 1, 0),\n\t\t);\n\n\t\tthis.input.setValue(\"\");\n\t\tthis.tui.requestRender();\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tthis.inputResolver = resolve;\n\t\t\tthis.inputRejecter = reject;\n\t\t});\n\t}\n\n\t/**\n\t * Show waiting message (for polling flows like GitHub Copilot)\n\t */\n\tshowWaiting(message: string): void {\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"dim\", message), 1, 0));\n\t\tthis.contentContainer.addChild(new Text(`(${keyHint(\"selectCancel\", \"to cancel\")})`, 1, 0));\n\t\tthis.tui.requestRender();\n\t}\n\n\t/**\n\t * Called by onProgress callback\n\t */\n\tshowProgress(message: string): void {\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"dim\", message), 1, 0));\n\t\tthis.tui.requestRender();\n\t}\n\n\thandleInput(data: string): void {\n\t\tif (this.disposed) return;\n\n\t\tconst kb = getEditorKeybindings();\n\n\t\tif (kb.matches(data, \"selectCancel\")) {\n\t\t\tthis.cancel();\n\t\t\treturn;\n\t\t}\n\n\t\t// Pass to input\n\t\tthis.input.handleInput(data);\n\t}\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import assert from "node:assert/strict";
2
- import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
2
+ import { mkdirSync, rmSync, writeFileSync } from "node:fs";
3
3
  import { tmpdir } from "node:os";
4
4
  import { join } from "node:path";
5
5
  import { afterEach, beforeEach, describe, it } from "node:test";
@@ -59,7 +59,9 @@ describe("ModelDiscoveryCache — basic operations", () => {
59
59
 
60
60
  cache.clear("openai");
61
61
  assert.equal(cache.get("openai"), undefined);
62
- assert.ok(cache.get("google"));
62
+ const googleEntry = cache.get("google");
63
+ assert.ok(googleEntry);
64
+ assert.equal(googleEntry.models[0].id, "gemini-pro");
63
65
  });
64
66
 
65
67
  it("clear without provider removes all entries", () => {
@@ -1,7 +1,7 @@
1
1
  // GSD Login Dialog Component — OAuth login flow UI
2
2
  // Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
3
3
  import { getOAuthProviders } from "@gsd/pi-ai/oauth";
4
- import { Container, type Focusable, getEditorKeybindings, Input, Spacer, Text, type TUI } from "@gsd/pi-tui";
4
+ import { Container, type Focusable, getEditorKeybindings, Input, Spacer, Text, truncateToWidth, type TUI } from "@gsd/pi-tui";
5
5
  import { execFile } from "child_process";
6
6
  import { theme } from "../theme/theme.js";
7
7
  import { DynamicBorder } from "./dynamic-border.js";
@@ -121,21 +121,25 @@ export class LoginDialogComponent extends Container implements Focusable {
121
121
  showAuth(url: string, instructions?: string): void {
122
122
  this.contentContainer.clear();
123
123
  this.contentContainer.addChild(new Spacer(1));
124
- this.contentContainer.addChild(new Text(theme.fg("accent", url), 1, 0));
124
+
125
+ // Truncate the visible URL text so it never wraps (which would break
126
+ // the OSC 8 hyperlink). The full URL is still the link target.
127
+ const maxUrlWidth = Math.max(20, this.tui.terminal.columns - 4);
128
+ const displayUrl = truncateToWidth(url, maxUrlWidth);
129
+ const urlLink = `\x1b]8;;${url}\x07${theme.fg("accent", displayUrl)}\x1b]8;;\x07`;
130
+ this.contentContainer.addChild(new Text(urlLink, 1, 0));
125
131
 
126
132
  const clickHint = process.platform === "darwin" ? "Cmd+click to open" : "Ctrl+click to open";
127
- const hyperlink = `\x1b]8;;${url}\x07${clickHint}\x1b]8;;\x07`;
128
- this.contentContainer.addChild(new Text(theme.fg("dim", hyperlink), 1, 0));
133
+ this.contentContainer.addChild(new Text(theme.fg("dim", clickHint), 1, 0));
129
134
 
130
135
  if (instructions) {
131
136
  this.contentContainer.addChild(new Spacer(1));
132
137
  this.contentContainer.addChild(new Text(theme.fg("warning", instructions), 1, 0));
133
138
  }
134
139
 
135
- // Try to open browser on Windows, `start` needs an empty title arg
136
- // so it treats the URL as a target, not a window title
140
+ // PowerShell's Start-Process handles URLs with '&' safely; cmd /c start does not.
137
141
  if (process.platform === "win32") {
138
- execFile("cmd", ["/c", "start", "", url], () => {});
142
+ execFile("powershell", ["-c", `Start-Process '${url.replace(/'/g, "''")}'`], () => {});
139
143
  } else {
140
144
  const openCmd = process.platform === "darwin" ? "open" : "xdg-open";
141
145
  execFile(openCmd, [url], () => {});
@@ -19,6 +19,7 @@ import type {
19
19
  import type { DispatchAction } from "../auto-dispatch.js";
20
20
  import type { WorktreeResolver } from "../worktree-resolver.js";
21
21
  import type { CmuxLogLevel } from "../../cmux/index.js";
22
+ import type { JournalEntry } from "../journal.js";
22
23
 
23
24
  /**
24
25
  * Dependencies injected by the caller (auto.ts startAuto) so autoLoop
@@ -102,7 +103,7 @@ export interface LoopDeps {
102
103
  basePath: string,
103
104
  milestoneId: string,
104
105
  roadmapContent: string,
105
- ) => { pushed: boolean };
106
+ ) => { pushed: boolean; codeFilesChanged: boolean };
106
107
  teardownAutoWorktree: (basePath: string, milestoneId: string) => void;
107
108
  createAutoWorktree: (basePath: string, milestoneId: string) => string;
108
109
  captureIntegrationBranch: (
@@ -285,4 +286,7 @@ export interface LoopDeps {
285
286
 
286
287
  // Session manager
287
288
  getSessionFile: (ctx: ExtensionContext) => string;
289
+
290
+ // Journal
291
+ emitJournalEvent: (entry: JournalEntry) => void;
288
292
  }
@@ -9,6 +9,7 @@
9
9
 
10
10
  import type { ExtensionAPI, ExtensionContext } from "@gsd/pi-coding-agent";
11
11
 
12
+ import { randomUUID } from "node:crypto";
12
13
  import type { AutoSession, SidecarItem } from "./session.js";
13
14
  import type { LoopDeps } from "./loop-deps.js";
14
15
  import {
@@ -51,6 +52,11 @@ export async function autoLoop(
51
52
  iteration++;
52
53
  debugLog("autoLoop", { phase: "loop-top", iteration });
53
54
 
55
+ // ── Journal: per-iteration flow grouping ──
56
+ const flowId = randomUUID();
57
+ let seqCounter = 0;
58
+ const nextSeq = () => ++seqCounter;
59
+
54
60
  if (iteration > MAX_LOOP_ITERATIONS) {
55
61
  debugLog("autoLoop", {
56
62
  phase: "exit",
@@ -84,6 +90,7 @@ export async function autoLoop(
84
90
  unitType: sidecarItem.unitType,
85
91
  unitId: sidecarItem.unitId,
86
92
  });
93
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "sidecar-dequeue", data: { kind: sidecarItem.kind, unitType: sidecarItem.unitType, unitId: sidecarItem.unitId } });
87
94
  }
88
95
 
89
96
  const sessionLockBase = deps.lockBase();
@@ -106,7 +113,8 @@ export async function autoLoop(
106
113
  }
107
114
  }
108
115
 
109
- const ic: IterationContext = { ctx, pi, s, deps, prefs, iteration };
116
+ const ic: IterationContext = { ctx, pi, s, deps, prefs, iteration, flowId, nextSeq };
117
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "iteration-start", data: { iteration } });
110
118
  let iterData: IterationData;
111
119
 
112
120
  if (!sidecarItem) {
@@ -153,6 +161,7 @@ export async function autoLoop(
153
161
  if (finalizeResult.action === "continue") continue;
154
162
 
155
163
  consecutiveErrors = 0; // Iteration completed successfully
164
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "iteration-end", data: { iteration } });
156
165
  debugLog("autoLoop", { phase: "iteration-complete", iteration });
157
166
  } catch (loopErr) {
158
167
  // ── Blanket catch: absorb unexpected exceptions, apply graduated recovery ──