gsd-pi 2.46.0-dev.f45e6dc → 2.46.1-dev.44f59e2

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 (77) hide show
  1. package/README.md +47 -29
  2. package/dist/resources/extensions/claude-code-cli/index.js +25 -0
  3. package/dist/resources/extensions/claude-code-cli/models.js +40 -0
  4. package/dist/resources/extensions/claude-code-cli/package.json +11 -0
  5. package/dist/resources/extensions/claude-code-cli/partial-builder.js +223 -0
  6. package/dist/resources/extensions/claude-code-cli/readiness.js +26 -0
  7. package/dist/resources/extensions/claude-code-cli/sdk-types.js +8 -0
  8. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +293 -0
  9. package/dist/resources/extensions/gsd/auto-start.js +9 -8
  10. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  11. package/dist/resources/extensions/gsd/prompts/guided-plan-milestone.md +2 -2
  12. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +2 -2
  13. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  14. package/dist/resources/extensions/gsd/prompts/research-milestone.md +2 -2
  15. package/dist/resources/extensions/gsd/prompts/run-uat.md +2 -2
  16. package/dist/resources/extensions/gsd/repo-identity.js +5 -2
  17. package/dist/resources/extensions/gsd/state.js +29 -2
  18. package/dist/resources/extensions/gsd/workflow-events.js +1 -1
  19. package/dist/web/standalone/.next/BUILD_ID +1 -1
  20. package/dist/web/standalone/.next/app-path-routes-manifest.json +18 -18
  21. package/dist/web/standalone/.next/build-manifest.json +2 -2
  22. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  23. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  24. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  25. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  26. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  27. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  28. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  29. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  30. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  31. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  32. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  33. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  34. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  35. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  36. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  37. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  38. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  39. package/dist/web/standalone/.next/server/app/index.html +1 -1
  40. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  41. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  42. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  43. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  44. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  45. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  46. package/dist/web/standalone/.next/server/app-paths-manifest.json +18 -18
  47. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  48. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  49. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  50. package/package.json +3 -1
  51. package/packages/pi-coding-agent/dist/core/auth-storage.test.js +27 -2
  52. package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
  53. package/packages/pi-coding-agent/package.json +1 -1
  54. package/packages/pi-coding-agent/src/core/auth-storage.test.ts +27 -2
  55. package/pkg/package.json +1 -1
  56. package/src/resources/extensions/claude-code-cli/index.ts +28 -0
  57. package/src/resources/extensions/claude-code-cli/models.ts +42 -0
  58. package/src/resources/extensions/claude-code-cli/package.json +11 -0
  59. package/src/resources/extensions/claude-code-cli/partial-builder.ts +258 -0
  60. package/src/resources/extensions/claude-code-cli/readiness.ts +30 -0
  61. package/src/resources/extensions/claude-code-cli/sdk-types.ts +149 -0
  62. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +355 -0
  63. package/src/resources/extensions/gsd/auto-start.ts +8 -7
  64. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  65. package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +2 -2
  66. package/src/resources/extensions/gsd/prompts/plan-milestone.md +2 -2
  67. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  68. package/src/resources/extensions/gsd/prompts/research-milestone.md +2 -2
  69. package/src/resources/extensions/gsd/prompts/run-uat.md +2 -2
  70. package/src/resources/extensions/gsd/repo-identity.ts +5 -2
  71. package/src/resources/extensions/gsd/state.ts +33 -1
  72. package/src/resources/extensions/gsd/tests/inherited-repo-home-dir.test.ts +70 -0
  73. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +40 -0
  74. package/src/resources/extensions/gsd/tests/run-uat.test.ts +25 -0
  75. package/src/resources/extensions/gsd/workflow-events.ts +1 -1
  76. /package/dist/web/standalone/.next/static/{9CuEqE-DGAlva1uIjyfjF → sQCECerYL5daQCVxqF23f}/_buildManifest.js +0 -0
  77. /package/dist/web/standalone/.next/static/{9CuEqE-DGAlva1uIjyfjF → sQCECerYL5daQCVxqF23f}/_ssgManifest.js +0 -0
@@ -1 +1 @@
1
- <!DOCTYPE html><!--9CuEqE_DGAlva1uIjyfjF--><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/><link rel="preload" href="/_next/static/media/4cf2300e9c8272f7-s.p.woff2" as="font" crossorigin="" type="font/woff2"/><link rel="preload" href="/_next/static/media/93f479601ee12b01-s.p.woff2" as="font" crossorigin="" type="font/woff2"/><link rel="stylesheet" href="/_next/static/css/de70bee13400563f.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/css/dd4ae3f58ac9b600.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-0a4cd455ec4197d2.js"/><script src="/_next/static/chunks/4bd1b696-e5d7c65570c947b7.js" async=""></script><script src="/_next/static/chunks/3794-337d1ca25ad99a89.js" async=""></script><script src="/_next/static/chunks/main-app-fdab67f7802d7832.js" async=""></script><script src="/_next/static/chunks/4986-c2fc8845ce785303.js" async=""></script><script src="/_next/static/chunks/app/layout-a16c7a7ecdf0c2cf.js" async=""></script><meta name="robots" content="noindex"/><meta name="next-size-adjust" content=""/><title>404: This page could not be found.</title><title>GSD</title><meta name="description" content="The evolution of Get Shit Done — now a real coding agent. One command. Walk away. Come back to a built project."/><meta name="application-name" content="GSD"/><link rel="icon" href="/icon-light-32x32.png" media="(prefers-color-scheme: light)"/><link rel="icon" href="/icon-dark-32x32.png" media="(prefers-color-scheme: dark)"/><link rel="icon" href="/icon.svg" type="image/svg+xml"/><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body class="__variable_188709 __variable_9a8899 font-sans antialiased"><div hidden=""><!--$--><!--/$--></div><script>((a,b,c,d,e,f,g,h)=>{let i=document.documentElement,j=["light","dark"];function k(b){var c;(Array.isArray(a)?a:[a]).forEach(a=>{let c="class"===a,d=c&&f?e.map(a=>f[a]||a):e;c?(i.classList.remove(...d),i.classList.add(f&&f[b]?f[b]:b)):i.setAttribute(a,b)}),c=b,h&&j.includes(c)&&(i.style.colorScheme=c)}if(d)k(d);else try{let a=localStorage.getItem(b)||c,d=g&&"system"===a?window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light":a;k(d)}catch(a){}})("class","theme","dark",null,["light","dark"],null,true,true)</script><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding:0 23px 0 0;font-size:24px;font-weight:500;vertical-align:top;line-height:49px">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:49px;margin:0">This page could not be found.</h2></div></div></div><!--$--><!--/$--><section aria-label="Notifications alt+T" tabindex="-1" aria-live="polite" aria-relevant="additions text" aria-atomic="false"></section><script src="/_next/static/chunks/webpack-0a4cd455ec4197d2.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[21942,[\"4986\",\"static/chunks/4986-c2fc8845ce785303.js\",\"7177\",\"static/chunks/app/layout-a16c7a7ecdf0c2cf.js\"],\"ThemeProvider\"]\n3:I[57121,[],\"\"]\n4:I[74581,[],\"\"]\n5:I[61549,[\"4986\",\"static/chunks/4986-c2fc8845ce785303.js\",\"7177\",\"static/chunks/app/layout-a16c7a7ecdf0c2cf.js\"],\"Toaster\"]\n6:I[90484,[],\"OutletBoundary\"]\n7:\"$Sreact.suspense\"\n9:I[90484,[],\"ViewportBoundary\"]\nb:I[90484,[],\"MetadataBoundary\"]\nd:I[27123,[],\"\"]\n:HL[\"/_next/static/media/4cf2300e9c8272f7-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n:HL[\"/_next/static/media/93f479601ee12b01-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n:HL[\"/_next/static/css/de70bee13400563f.css\",\"style\"]\n:HL[\"/_next/static/css/dd4ae3f58ac9b600.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"b\":\"9CuEqE-DGAlva1uIjyfjF\",\"c\":[\"\",\"_not-found\"],\"q\":\"\",\"i\":false,\"f\":[[[\"\",{\"children\":[\"_not-found\",{\"children\":[\"__PAGE__\",{}]}]},\"$undefined\",\"$undefined\",true],[[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/de70bee13400563f.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"link\",\"1\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/dd4ae3f58ac9b600.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"suppressHydrationWarning\":true,\"children\":[\"$\",\"body\",null,{\"className\":\"__variable_188709 __variable_9a8899 font-sans antialiased\",\"children\":[\"$\",\"$L2\",null,{\"attribute\":\"class\",\"defaultTheme\":\"dark\",\"children\":[[\"$\",\"$L3\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L4\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}],[\"$\",\"$L5\",null,{\"position\":\"bottom-right\"}]]}]}]}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L3\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L4\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":404}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],null,[\"$\",\"$L6\",null,{\"children\":[\"$\",\"$7\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@8\"}]}]]}],{},null,false,false]},null,false,false]},null,false,false],[\"$\",\"$1\",\"h\",{\"children\":[[\"$\",\"meta\",null,{\"name\":\"robots\",\"content\":\"noindex\"}],[\"$\",\"$L9\",null,{\"children\":\"$La\"}],[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$Lb\",null,{\"children\":[\"$\",\"$7\",null,{\"name\":\"Next.Metadata\",\"children\":\"$Lc\"}]}]}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$d\",[]],\"S\":true}\n"])</script><script>self.__next_f.push([1,"a:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no\"}]]\n"])</script><script>self.__next_f.push([1,"e:I[86869,[],\"IconMark\"]\n8:null\nc:[[\"$\",\"title\",\"0\",{\"children\":\"GSD\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"The evolution of Get Shit Done — now a real coding agent. One command. Walk away. Come back to a built project.\"}],[\"$\",\"meta\",\"2\",{\"name\":\"application-name\",\"content\":\"GSD\"}],[\"$\",\"link\",\"3\",{\"rel\":\"icon\",\"href\":\"/icon-light-32x32.png\",\"media\":\"(prefers-color-scheme: light)\"}],[\"$\",\"link\",\"4\",{\"rel\":\"icon\",\"href\":\"/icon-dark-32x32.png\",\"media\":\"(prefers-color-scheme: dark)\"}],[\"$\",\"link\",\"5\",{\"rel\":\"icon\",\"href\":\"/icon.svg\",\"type\":\"image/svg+xml\"}],[\"$\",\"$Le\",\"6\",{}]]\n"])</script></body></html>
1
+ <!DOCTYPE html><!--sQCECerYL5daQCVxqF23f--><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/><link rel="preload" href="/_next/static/media/4cf2300e9c8272f7-s.p.woff2" as="font" crossorigin="" type="font/woff2"/><link rel="preload" href="/_next/static/media/93f479601ee12b01-s.p.woff2" as="font" crossorigin="" type="font/woff2"/><link rel="stylesheet" href="/_next/static/css/de70bee13400563f.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/css/dd4ae3f58ac9b600.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-0a4cd455ec4197d2.js"/><script src="/_next/static/chunks/4bd1b696-e5d7c65570c947b7.js" async=""></script><script src="/_next/static/chunks/3794-337d1ca25ad99a89.js" async=""></script><script src="/_next/static/chunks/main-app-fdab67f7802d7832.js" async=""></script><script src="/_next/static/chunks/4986-c2fc8845ce785303.js" async=""></script><script src="/_next/static/chunks/app/layout-a16c7a7ecdf0c2cf.js" async=""></script><meta name="robots" content="noindex"/><meta name="next-size-adjust" content=""/><title>404: This page could not be found.</title><title>GSD</title><meta name="description" content="The evolution of Get Shit Done — now a real coding agent. One command. Walk away. Come back to a built project."/><meta name="application-name" content="GSD"/><link rel="icon" href="/icon-light-32x32.png" media="(prefers-color-scheme: light)"/><link rel="icon" href="/icon-dark-32x32.png" media="(prefers-color-scheme: dark)"/><link rel="icon" href="/icon.svg" type="image/svg+xml"/><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body class="__variable_188709 __variable_9a8899 font-sans antialiased"><div hidden=""><!--$--><!--/$--></div><script>((a,b,c,d,e,f,g,h)=>{let i=document.documentElement,j=["light","dark"];function k(b){var c;(Array.isArray(a)?a:[a]).forEach(a=>{let c="class"===a,d=c&&f?e.map(a=>f[a]||a):e;c?(i.classList.remove(...d),i.classList.add(f&&f[b]?f[b]:b)):i.setAttribute(a,b)}),c=b,h&&j.includes(c)&&(i.style.colorScheme=c)}if(d)k(d);else try{let a=localStorage.getItem(b)||c,d=g&&"system"===a?window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light":a;k(d)}catch(a){}})("class","theme","dark",null,["light","dark"],null,true,true)</script><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding:0 23px 0 0;font-size:24px;font-weight:500;vertical-align:top;line-height:49px">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:49px;margin:0">This page could not be found.</h2></div></div></div><!--$--><!--/$--><section aria-label="Notifications alt+T" tabindex="-1" aria-live="polite" aria-relevant="additions text" aria-atomic="false"></section><script src="/_next/static/chunks/webpack-0a4cd455ec4197d2.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[21942,[\"4986\",\"static/chunks/4986-c2fc8845ce785303.js\",\"7177\",\"static/chunks/app/layout-a16c7a7ecdf0c2cf.js\"],\"ThemeProvider\"]\n3:I[57121,[],\"\"]\n4:I[74581,[],\"\"]\n5:I[61549,[\"4986\",\"static/chunks/4986-c2fc8845ce785303.js\",\"7177\",\"static/chunks/app/layout-a16c7a7ecdf0c2cf.js\"],\"Toaster\"]\n6:I[90484,[],\"OutletBoundary\"]\n7:\"$Sreact.suspense\"\n9:I[90484,[],\"ViewportBoundary\"]\nb:I[90484,[],\"MetadataBoundary\"]\nd:I[27123,[],\"\"]\n:HL[\"/_next/static/media/4cf2300e9c8272f7-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n:HL[\"/_next/static/media/93f479601ee12b01-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n:HL[\"/_next/static/css/de70bee13400563f.css\",\"style\"]\n:HL[\"/_next/static/css/dd4ae3f58ac9b600.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"b\":\"sQCECerYL5daQCVxqF23f\",\"c\":[\"\",\"_not-found\"],\"q\":\"\",\"i\":false,\"f\":[[[\"\",{\"children\":[\"_not-found\",{\"children\":[\"__PAGE__\",{}]}]},\"$undefined\",\"$undefined\",true],[[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/de70bee13400563f.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"link\",\"1\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/dd4ae3f58ac9b600.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"suppressHydrationWarning\":true,\"children\":[\"$\",\"body\",null,{\"className\":\"__variable_188709 __variable_9a8899 font-sans antialiased\",\"children\":[\"$\",\"$L2\",null,{\"attribute\":\"class\",\"defaultTheme\":\"dark\",\"children\":[[\"$\",\"$L3\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L4\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}],[\"$\",\"$L5\",null,{\"position\":\"bottom-right\"}]]}]}]}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L3\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L4\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":404}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],null,[\"$\",\"$L6\",null,{\"children\":[\"$\",\"$7\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@8\"}]}]]}],{},null,false,false]},null,false,false]},null,false,false],[\"$\",\"$1\",\"h\",{\"children\":[[\"$\",\"meta\",null,{\"name\":\"robots\",\"content\":\"noindex\"}],[\"$\",\"$L9\",null,{\"children\":\"$La\"}],[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$Lb\",null,{\"children\":[\"$\",\"$7\",null,{\"name\":\"Next.Metadata\",\"children\":\"$Lc\"}]}]}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$d\",[]],\"S\":true}\n"])</script><script>self.__next_f.push([1,"a:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no\"}]]\n"])</script><script>self.__next_f.push([1,"e:I[86869,[],\"IconMark\"]\n8:null\nc:[[\"$\",\"title\",\"0\",{\"children\":\"GSD\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"The evolution of Get Shit Done — now a real coding agent. One command. Walk away. Come back to a built project.\"}],[\"$\",\"meta\",\"2\",{\"name\":\"application-name\",\"content\":\"GSD\"}],[\"$\",\"link\",\"3\",{\"rel\":\"icon\",\"href\":\"/icon-light-32x32.png\",\"media\":\"(prefers-color-scheme: light)\"}],[\"$\",\"link\",\"4\",{\"rel\":\"icon\",\"href\":\"/icon-dark-32x32.png\",\"media\":\"(prefers-color-scheme: dark)\"}],[\"$\",\"link\",\"5\",{\"rel\":\"icon\",\"href\":\"/icon.svg\",\"type\":\"image/svg+xml\"}],[\"$\",\"$Le\",\"6\",{}]]\n"])</script></body></html>
@@ -1,2 +1,2 @@
1
- <!DOCTYPE html><!--9CuEqE_DGAlva1uIjyfjF--><html id="__next_error__"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-0a4cd455ec4197d2.js"/><script src="/_next/static/chunks/4bd1b696-e5d7c65570c947b7.js" async=""></script><script src="/_next/static/chunks/3794-337d1ca25ad99a89.js" async=""></script><script src="/_next/static/chunks/main-app-fdab67f7802d7832.js" async=""></script><meta name="next-size-adjust" content=""/><title>500: Internal Server Error.</title><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body><div hidden=""><!--$--><!--/$--></div><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div style="line-height:48px"><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}
2
- @media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding-right:23px;font-size:24px;font-weight:500;vertical-align:top">500</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:28px">Internal Server Error.</h2></div></div></div><!--$--><!--/$--><script src="/_next/static/chunks/webpack-0a4cd455ec4197d2.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[57121,[],\"\"]\n3:I[74581,[],\"\"]\n4:I[90484,[],\"OutletBoundary\"]\n5:\"$Sreact.suspense\"\n7:I[90484,[],\"ViewportBoundary\"]\n9:I[90484,[],\"MetadataBoundary\"]\nb:I[27123,[],\"\"]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"b\":\"9CuEqE-DGAlva1uIjyfjF\",\"c\":[\"\",\"_global-error\"],\"q\":\"\",\"i\":false,\"f\":[[[\"\",{\"children\":[\"_global-error\",{\"children\":[\"__PAGE__\",{}]}]}],[[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L2\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L3\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L2\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L3\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[[\"$\",\"html\",null,{\"id\":\"__next_error__\",\"children\":[[\"$\",\"head\",null,{\"children\":[\"$\",\"title\",null,{\"children\":\"500: Internal Server Error.\"}]}],[\"$\",\"body\",null,{\"children\":[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"style\":{\"lineHeight\":\"48px\"},\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}\\n@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"paddingRight\":23,\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\"},\"children\":\"500\"}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"28px\"},\"children\":\"Internal Server Error.\"}]}]]}]}]}]]}],null,[\"$\",\"$L4\",null,{\"children\":[\"$\",\"$5\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@6\"}]}]]}],{},null,false,false]},null,false,false]},null,false,false],[\"$\",\"$1\",\"h\",{\"children\":[null,[\"$\",\"$L7\",null,{\"children\":\"$L8\"}],[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$L9\",null,{\"children\":[\"$\",\"$5\",null,{\"name\":\"Next.Metadata\",\"children\":\"$La\"}]}]}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$b\",[]],\"S\":true}\n"])</script><script>self.__next_f.push([1,"8:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n"])</script><script>self.__next_f.push([1,"6:null\na:[]\n"])</script></body></html>
1
+ <!DOCTYPE html><!--sQCECerYL5daQCVxqF23f--><html id="__next_error__"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-0a4cd455ec4197d2.js"/><script src="/_next/static/chunks/4bd1b696-e5d7c65570c947b7.js" async=""></script><script src="/_next/static/chunks/3794-337d1ca25ad99a89.js" async=""></script><script src="/_next/static/chunks/main-app-fdab67f7802d7832.js" async=""></script><meta name="next-size-adjust" content=""/><title>500: Internal Server Error.</title><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body><div hidden=""><!--$--><!--/$--></div><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div style="line-height:48px"><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}
2
+ @media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding-right:23px;font-size:24px;font-weight:500;vertical-align:top">500</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:28px">Internal Server Error.</h2></div></div></div><!--$--><!--/$--><script src="/_next/static/chunks/webpack-0a4cd455ec4197d2.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[57121,[],\"\"]\n3:I[74581,[],\"\"]\n4:I[90484,[],\"OutletBoundary\"]\n5:\"$Sreact.suspense\"\n7:I[90484,[],\"ViewportBoundary\"]\n9:I[90484,[],\"MetadataBoundary\"]\nb:I[27123,[],\"\"]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"b\":\"sQCECerYL5daQCVxqF23f\",\"c\":[\"\",\"_global-error\"],\"q\":\"\",\"i\":false,\"f\":[[[\"\",{\"children\":[\"_global-error\",{\"children\":[\"__PAGE__\",{}]}]}],[[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L2\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L3\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L2\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L3\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[[\"$\",\"html\",null,{\"id\":\"__next_error__\",\"children\":[[\"$\",\"head\",null,{\"children\":[\"$\",\"title\",null,{\"children\":\"500: Internal Server Error.\"}]}],[\"$\",\"body\",null,{\"children\":[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"style\":{\"lineHeight\":\"48px\"},\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}\\n@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"paddingRight\":23,\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\"},\"children\":\"500\"}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"28px\"},\"children\":\"Internal Server Error.\"}]}]]}]}]}]]}],null,[\"$\",\"$L4\",null,{\"children\":[\"$\",\"$5\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@6\"}]}]]}],{},null,false,false]},null,false,false]},null,false,false],[\"$\",\"$1\",\"h\",{\"children\":[null,[\"$\",\"$L7\",null,{\"children\":\"$L8\"}],[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$L9\",null,{\"children\":[\"$\",\"$5\",null,{\"name\":\"Next.Metadata\",\"children\":\"$La\"}]}]}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$b\",[]],\"S\":true}\n"])</script><script>self.__next_f.push([1,"8:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n"])</script><script>self.__next_f.push([1,"6:null\na:[]\n"])</script></body></html>
@@ -1 +1 @@
1
- {"node":{},"edge":{},"encryptionKey":"G6za9ZHDPidFPuz5+6gqkj4EFmPUl+r9fs1iT+fEKnc="}
1
+ {"node":{},"edge":{},"encryptionKey":"uJcNJizA4uCzsuuL7Qlkm5UEMqcrcMW/nhKRsBH4qZk="}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gsd-pi",
3
- "version": "2.46.0-dev.f45e6dc",
3
+ "version": "2.46.1-dev.44f59e2",
4
4
  "description": "GSD — Get Shit Done coding agent",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -54,6 +54,7 @@
54
54
  "copy-themes": "node scripts/copy-themes.cjs",
55
55
  "copy-export-html": "node scripts/copy-export-html.cjs",
56
56
  "test:unit": "node --import ./src/resources/extensions/gsd/tests/resolve-ts.mjs --experimental-strip-types --experimental-test-isolation=process --test src/resources/extensions/gsd/tests/*.test.ts src/resources/extensions/gsd/tests/*.test.mjs src/tests/*.test.ts",
57
+ "test:packages": "node --test packages/pi-coding-agent/dist/core/*.test.js",
57
58
  "test:marketplace": "GSD_TEST_CLONE_MARKETPLACES=1 node --import ./src/resources/extensions/gsd/tests/resolve-ts.mjs --experimental-strip-types --test src/resources/extensions/gsd/tests/claude-import-tui.test.ts src/resources/extensions/gsd/tests/plugin-importer-live.test.ts src/tests/marketplace-discovery.test.ts",
58
59
  "test:coverage": "c8 --reporter=text --reporter=lcov --exclude='src/resources/extensions/gsd/tests/**' --exclude='src/tests/**' --exclude='scripts/**' --exclude='native/**' --exclude='node_modules/**' --check-coverage --statements=50 --lines=50 --branches=20 --functions=20 node --import ./src/resources/extensions/gsd/tests/resolve-ts.mjs --experimental-strip-types --experimental-test-isolation=process --test src/resources/extensions/gsd/tests/*.test.ts src/resources/extensions/gsd/tests/*.test.mjs src/tests/*.test.ts",
59
60
  "test:integration": "node --import ./src/resources/extensions/gsd/tests/resolve-ts.mjs --experimental-strip-types --experimental-test-isolation=process --test src/resources/extensions/gsd/tests/*integration*.test.ts src/tests/integration/*.test.ts",
@@ -139,6 +140,7 @@
139
140
  "typescript": "^5.4.0"
140
141
  },
141
142
  "optionalDependencies": {
143
+ "@anthropic-ai/claude-agent-sdk": "^0.2.83",
142
144
  "@gsd-build/engine-darwin-arm64": ">=2.10.2",
143
145
  "@gsd-build/engine-darwin-x64": ">=2.10.2",
144
146
  "@gsd-build/engine-linux-arm64-gnu": ">=2.10.2",
@@ -215,7 +215,7 @@ describe("AuthStorage — areAllCredentialsBackedOff", () => {
215
215
  });
216
216
  // ─── mismatched oauth credential for non-OAuth provider (#2083) ───────────────
217
217
  describe("AuthStorage — oauth credential for non-OAuth provider (#2083)", () => {
218
- it("returns undefined when openrouter has type:oauth (no registered OAuth provider)", async () => {
218
+ it("returns undefined when openrouter has type:oauth (no registered OAuth provider)", async (t) => {
219
219
  // Simulates the bug: OpenRouter credential stored as type:"oauth"
220
220
  // but OpenRouter is not a registered OAuth provider.
221
221
  const storage = inMemory({
@@ -226,12 +226,25 @@ describe("AuthStorage — oauth credential for non-OAuth provider (#2083)", () =
226
226
  expires: Date.now() + 3_600_000,
227
227
  },
228
228
  });
229
+ // Isolate from any real OPENROUTER_API_KEY in the environment so the
230
+ // fall-through to env / fallback finds nothing and returns undefined.
231
+ const origEnv = process.env.OPENROUTER_API_KEY;
232
+ delete process.env.OPENROUTER_API_KEY;
233
+ t.after(() => {
234
+ if (origEnv === undefined) {
235
+ delete process.env.OPENROUTER_API_KEY;
236
+ }
237
+ else {
238
+ process.env.OPENROUTER_API_KEY = origEnv;
239
+ }
240
+ });
229
241
  // Before the fix, getApiKey returns undefined because
230
242
  // resolveCredentialApiKey calls getOAuthProvider("openrouter") → null → undefined.
231
243
  // The key in the oauth credential is never extracted.
232
244
  const key = await storage.getApiKey("openrouter");
233
245
  // After the fix, the oauth credential with an unrecognised provider
234
246
  // should be skipped, and getApiKey should fall through to env / fallback.
247
+ // With no env var and no fallback resolver configured, the result is undefined.
235
248
  assert.equal(key, undefined);
236
249
  });
237
250
  it("falls through to env var when openrouter has type:oauth credential", async (t) => {
@@ -257,7 +270,7 @@ describe("AuthStorage — oauth credential for non-OAuth provider (#2083)", () =
257
270
  const key = await storage.getApiKey("openrouter");
258
271
  assert.equal(key, "sk-or-v1-env-key");
259
272
  });
260
- it("falls through to fallback resolver when openrouter has type:oauth credential", async () => {
273
+ it("falls through to fallback resolver when openrouter has type:oauth credential", async (t) => {
261
274
  const storage = inMemory({
262
275
  openrouter: {
263
276
  type: "oauth",
@@ -266,6 +279,18 @@ describe("AuthStorage — oauth credential for non-OAuth provider (#2083)", () =
266
279
  expires: Date.now() + 3_600_000,
267
280
  },
268
281
  });
282
+ // Isolate from any real OPENROUTER_API_KEY so env fallback is skipped
283
+ // and the fallback resolver is reached.
284
+ const origEnv = process.env.OPENROUTER_API_KEY;
285
+ delete process.env.OPENROUTER_API_KEY;
286
+ t.after(() => {
287
+ if (origEnv === undefined) {
288
+ delete process.env.OPENROUTER_API_KEY;
289
+ }
290
+ else {
291
+ process.env.OPENROUTER_API_KEY = origEnv;
292
+ }
293
+ });
269
294
  storage.setFallbackResolver((provider) => provider === "openrouter" ? "sk-or-v1-fallback" : undefined);
270
295
  const key = await storage.getApiKey("openrouter");
271
296
  assert.equal(key, "sk-or-v1-fallback");
@@ -1 +1 @@
1
- {"version":3,"file":"auth-storage.test.js","sourceRoot":"","sources":["../../src/core/auth-storage.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, it } from \"node:test\";\nimport assert from \"node:assert/strict\";\nimport { AuthStorage } from \"./auth-storage.js\";\n\n// ─── helpers ──────────────────────────────────────────────────────────────────\n\nfunction makeKey(key: string) {\n\treturn { type: \"api_key\" as const, key };\n}\n\nfunction inMemory(data: Record<string, unknown> = {}) {\n\treturn AuthStorage.inMemory(data as any);\n}\n\n// ─── single credential (backward compat) ─────────────────────────────────────\n\ndescribe(\"AuthStorage — single credential (backward compat)\", () => {\n\tit(\"returns the api key for a provider with one key\", async () => {\n\t\tconst storage = inMemory({ anthropic: makeKey(\"sk-abc\") });\n\t\tconst key = await storage.getApiKey(\"anthropic\");\n\t\tassert.equal(key, \"sk-abc\");\n\t});\n\n\tit(\"returns undefined for unknown provider\", async () => {\n\t\tconst storage = inMemory({});\n\t\tconst key = await storage.getApiKey(\"unknown\");\n\t\tassert.equal(key, undefined);\n\t});\n\n\tit(\"runtime override takes precedence over stored key\", async () => {\n\t\tconst storage = inMemory({ anthropic: makeKey(\"sk-stored\") });\n\t\tstorage.setRuntimeApiKey(\"anthropic\", \"sk-runtime\");\n\t\tconst key = await storage.getApiKey(\"anthropic\");\n\t\tassert.equal(key, \"sk-runtime\");\n\t});\n});\n\n// ─── multiple credentials ─────────────────────────────────────────────────────\n\ndescribe(\"AuthStorage — multiple credentials\", () => {\n\tit(\"round-robins across multiple api keys without sessionId\", async () => {\n\t\tconst storage = inMemory({\n\t\t\tanthropic: [makeKey(\"sk-1\"), makeKey(\"sk-2\"), makeKey(\"sk-3\")],\n\t\t});\n\n\t\tconst keys = new Set<string>();\n\t\tfor (let i = 0; i < 6; i++) {\n\t\t\tconst k = await storage.getApiKey(\"anthropic\");\n\t\t\tassert.ok(k, `call ${i} should return a key`);\n\t\t\tkeys.add(k);\n\t\t}\n\t\t// All three keys should have been selected across 6 calls\n\t\tassert.deepEqual(keys, new Set([\"sk-1\", \"sk-2\", \"sk-3\"]));\n\t});\n\n\tit(\"session-sticky: same sessionId always picks the same key\", async () => {\n\t\tconst storage = inMemory({\n\t\t\tanthropic: [makeKey(\"sk-1\"), makeKey(\"sk-2\"), makeKey(\"sk-3\")],\n\t\t});\n\n\t\tconst sessionId = \"sess-abc\";\n\t\tconst first = await storage.getApiKey(\"anthropic\", sessionId);\n\t\tfor (let i = 0; i < 5; i++) {\n\t\t\tconst k = await storage.getApiKey(\"anthropic\", sessionId);\n\t\t\tassert.equal(k, first, `call ${i} should be sticky to first selection`);\n\t\t}\n\t});\n\n\tit(\"different sessionIds may select different keys\", async () => {\n\t\tconst storage = inMemory({\n\t\t\tanthropic: [makeKey(\"sk-1\"), makeKey(\"sk-2\"), makeKey(\"sk-3\")],\n\t\t});\n\n\t\tconst results = new Set<string>();\n\t\tfor (let i = 0; i < 20; i++) {\n\t\t\tconst k = await storage.getApiKey(\"anthropic\", `sess-${i}`);\n\t\t\tif (k) results.add(k);\n\t\t}\n\t\t// With 20 different sessions and 3 keys, we should see more than one key\n\t\tassert.ok(results.size > 1, \"multiple sessions should hash to different keys\");\n\t});\n});\n\n// ─── login accumulation ───────────────────────────────────────────────────────\n\ndescribe(\"AuthStorage — login accumulation\", () => {\n\tit(\"accumulates api keys on repeated set()\", () => {\n\t\tconst storage = inMemory({});\n\t\tstorage.set(\"anthropic\", makeKey(\"sk-1\"));\n\t\tstorage.set(\"anthropic\", makeKey(\"sk-2\"));\n\t\tconst creds = storage.getCredentialsForProvider(\"anthropic\");\n\t\tassert.equal(creds.length, 2);\n\t\tassert.deepEqual(\n\t\t\tcreds.map((c) => (c.type === \"api_key\" ? c.key : null)),\n\t\t\t[\"sk-1\", \"sk-2\"],\n\t\t);\n\t});\n\n\tit(\"deduplicates identical api keys\", () => {\n\t\tconst storage = inMemory({});\n\t\tstorage.set(\"anthropic\", makeKey(\"sk-1\"));\n\t\tstorage.set(\"anthropic\", makeKey(\"sk-1\"));\n\t\tconst creds = storage.getCredentialsForProvider(\"anthropic\");\n\t\tassert.equal(creds.length, 1);\n\t});\n});\n\n// ─── backoff / markUsageLimitReached ─────────────────────────────────────────\n\ndescribe(\"AuthStorage — rate-limit backoff\", () => {\n\tit(\"returns true when a backed-off credential has an alternate\", async () => {\n\t\tconst storage = inMemory({\n\t\t\tanthropic: [makeKey(\"sk-1\"), makeKey(\"sk-2\")],\n\t\t});\n\n\t\t// Use sk-1 via round-robin (first call, index 0)\n\t\tawait storage.getApiKey(\"anthropic\");\n\n\t\t// Mark it as rate-limited; sk-2 should still be available\n\t\tconst hasAlternate = storage.markUsageLimitReached(\"anthropic\");\n\t\tassert.equal(hasAlternate, true);\n\t});\n\n\tit(\"returns false when all credentials are backed off\", async () => {\n\t\tconst storage = inMemory({\n\t\t\tanthropic: [makeKey(\"sk-1\"), makeKey(\"sk-2\")],\n\t\t});\n\n\t\t// Back off both keys\n\t\tawait storage.getApiKey(\"anthropic\"); // uses index 0\n\t\tstorage.markUsageLimitReached(\"anthropic\"); // backs off index 0\n\t\tawait storage.getApiKey(\"anthropic\"); // uses index 1\n\t\tconst hasAlternate = storage.markUsageLimitReached(\"anthropic\"); // backs off index 1\n\t\tassert.equal(hasAlternate, false);\n\t});\n\n\tit(\"backed-off credential is skipped; next available key is returned\", async () => {\n\t\tconst storage = inMemory({\n\t\t\tanthropic: [makeKey(\"sk-1\"), makeKey(\"sk-2\")],\n\t\t});\n\n\t\t// First call → sk-1 (round-robin index 0)\n\t\tconst first = await storage.getApiKey(\"anthropic\");\n\t\tassert.equal(first, \"sk-1\");\n\n\t\t// Back off sk-1\n\t\tstorage.markUsageLimitReached(\"anthropic\");\n\n\t\t// Next call should skip backed-off sk-1 and return sk-2\n\t\tconst second = await storage.getApiKey(\"anthropic\");\n\t\tassert.equal(second, \"sk-2\");\n\t});\n\n\tit(\"single credential: markUsageLimitReached returns false\", async () => {\n\t\tconst storage = inMemory({ anthropic: makeKey(\"sk-only\") });\n\t\tawait storage.getApiKey(\"anthropic\");\n\t\tconst hasAlternate = storage.markUsageLimitReached(\"anthropic\");\n\t\tassert.equal(hasAlternate, false);\n\t});\n\n\tit(\"single credential: unknown error type skips backoff entirely\", async () => {\n\t\tconst storage = inMemory({ anthropic: makeKey(\"sk-only\") });\n\t\tawait storage.getApiKey(\"anthropic\");\n\n\t\t// Mark with unknown error type (transport failure)\n\t\tconst hasAlternate = storage.markUsageLimitReached(\"anthropic\", undefined, {\n\t\t\terrorType: \"unknown\",\n\t\t});\n\t\tassert.equal(hasAlternate, false);\n\n\t\t// Key should still be available — backoff was not applied\n\t\tconst key = await storage.getApiKey(\"anthropic\");\n\t\tassert.equal(key, \"sk-only\");\n\t});\n\n\tit(\"multiple credentials: unknown error type still backs off the used credential\", async () => {\n\t\tconst storage = inMemory({\n\t\t\tanthropic: [makeKey(\"sk-1\"), makeKey(\"sk-2\")],\n\t\t});\n\t\tawait storage.getApiKey(\"anthropic\"); // uses sk-1\n\n\t\t// Mark with unknown error type — should still back off when alternates exist\n\t\tconst hasAlternate = storage.markUsageLimitReached(\"anthropic\", undefined, {\n\t\t\terrorType: \"unknown\",\n\t\t});\n\t\tassert.equal(hasAlternate, true);\n\n\t\t// Next call should return sk-2\n\t\tconst key = await storage.getApiKey(\"anthropic\");\n\t\tassert.equal(key, \"sk-2\");\n\t});\n\n\tit(\"single credential: rate_limit error type still backs off\", async () => {\n\t\tconst storage = inMemory({ anthropic: makeKey(\"sk-only\") });\n\t\tawait storage.getApiKey(\"anthropic\");\n\n\t\t// rate_limit should still back off even single credentials\n\t\tconst hasAlternate = storage.markUsageLimitReached(\"anthropic\", undefined, {\n\t\t\terrorType: \"rate_limit\",\n\t\t});\n\t\tassert.equal(hasAlternate, false);\n\n\t\t// Key should be backed off\n\t\tconst key = await storage.getApiKey(\"anthropic\");\n\t\tassert.equal(key, undefined);\n\t});\n\n\tit(\"session-sticky: marks the correct credential as backed off\", async () => {\n\t\tconst storage = inMemory({\n\t\t\tanthropic: [makeKey(\"sk-1\"), makeKey(\"sk-2\")],\n\t\t});\n\n\t\tconst sessionId = \"sess-xyz\";\n\t\tconst chosen = await storage.getApiKey(\"anthropic\", sessionId);\n\t\tassert.ok(chosen);\n\n\t\t// Back off the chosen credential for this session\n\t\tconst hasAlternate = storage.markUsageLimitReached(\"anthropic\", sessionId);\n\t\tassert.equal(hasAlternate, true);\n\n\t\t// Next call with same session should return the other key\n\t\tconst next = await storage.getApiKey(\"anthropic\", sessionId);\n\t\tassert.ok(next);\n\t\tassert.notEqual(next, chosen);\n\t});\n});\n\n// ─── areAllCredentialsBackedOff ───────────────────────────────────────────────\n\ndescribe(\"AuthStorage — areAllCredentialsBackedOff\", () => {\n\tit(\"returns false when no credentials are configured\", () => {\n\t\tconst storage = inMemory({});\n\t\tassert.equal(storage.areAllCredentialsBackedOff(\"anthropic\"), false);\n\t});\n\n\tit(\"returns false when credentials exist and none are backed off\", async () => {\n\t\tconst storage = inMemory({ anthropic: makeKey(\"sk-abc\") });\n\t\tassert.equal(storage.areAllCredentialsBackedOff(\"anthropic\"), false);\n\t});\n\n\tit(\"returns true when the single credential is backed off\", async () => {\n\t\tconst storage = inMemory({ anthropic: makeKey(\"sk-only\") });\n\t\tawait storage.getApiKey(\"anthropic\");\n\t\tstorage.markUsageLimitReached(\"anthropic\");\n\t\tassert.equal(storage.areAllCredentialsBackedOff(\"anthropic\"), true);\n\t});\n\n\tit(\"returns false when at least one credential is still available\", async () => {\n\t\tconst storage = inMemory({ anthropic: [makeKey(\"sk-1\"), makeKey(\"sk-2\")] });\n\t\tawait storage.getApiKey(\"anthropic\"); // uses index 0\n\t\tstorage.markUsageLimitReached(\"anthropic\"); // backs off index 0\n\t\t// index 1 is still available\n\t\tassert.equal(storage.areAllCredentialsBackedOff(\"anthropic\"), false);\n\t});\n\n\tit(\"returns true when all credentials are backed off\", async () => {\n\t\tconst storage = inMemory({ anthropic: [makeKey(\"sk-1\"), makeKey(\"sk-2\")] });\n\t\tawait storage.getApiKey(\"anthropic\"); // uses index 0\n\t\tstorage.markUsageLimitReached(\"anthropic\"); // backs off index 0\n\t\tawait storage.getApiKey(\"anthropic\"); // uses index 1\n\t\tstorage.markUsageLimitReached(\"anthropic\"); // backs off index 1\n\t\tassert.equal(storage.areAllCredentialsBackedOff(\"anthropic\"), true);\n\t});\n});\n\n// ─── mismatched oauth credential for non-OAuth provider (#2083) ───────────────\n\ndescribe(\"AuthStorage — oauth credential for non-OAuth provider (#2083)\", () => {\n\tit(\"returns undefined when openrouter has type:oauth (no registered OAuth provider)\", async () => {\n\t\t// Simulates the bug: OpenRouter credential stored as type:\"oauth\"\n\t\t// but OpenRouter is not a registered OAuth provider.\n\t\tconst storage = inMemory({\n\t\t\topenrouter: {\n\t\t\t\ttype: \"oauth\",\n\t\t\t\taccess_token: \"sk-or-v1-fake\",\n\t\t\t\trefresh_token: \"rt-fake\",\n\t\t\t\texpires: Date.now() + 3_600_000,\n\t\t\t},\n\t\t});\n\n\t\t// Before the fix, getApiKey returns undefined because\n\t\t// resolveCredentialApiKey calls getOAuthProvider(\"openrouter\") → null → undefined.\n\t\t// The key in the oauth credential is never extracted.\n\t\tconst key = await storage.getApiKey(\"openrouter\");\n\t\t// After the fix, the oauth credential with an unrecognised provider\n\t\t// should be skipped, and getApiKey should fall through to env / fallback.\n\t\tassert.equal(key, undefined);\n\t});\n\n\tit(\"falls through to env var when openrouter has type:oauth credential\", async (t) => {\n\t\tconst storage = inMemory({\n\t\t\topenrouter: {\n\t\t\t\ttype: \"oauth\",\n\t\t\t\taccess_token: \"sk-or-v1-fake\",\n\t\t\t\trefresh_token: \"rt-fake\",\n\t\t\t\texpires: Date.now() + 3_600_000,\n\t\t\t},\n\t\t});\n\n\t\t// Simulate OPENROUTER_API_KEY being set via env\n\t\tconst origEnv = process.env.OPENROUTER_API_KEY;\n\t\tt.after(() => {\n\t\t\tif (origEnv === undefined) {\n\t\t\t\tdelete process.env.OPENROUTER_API_KEY;\n\t\t\t} else {\n\t\t\t\tprocess.env.OPENROUTER_API_KEY = origEnv;\n\t\t\t}\n\t\t});\n\n\t\tprocess.env.OPENROUTER_API_KEY = \"sk-or-v1-env-key\";\n\t\tconst key = await storage.getApiKey(\"openrouter\");\n\t\tassert.equal(key, \"sk-or-v1-env-key\");\n\t});\n\n\tit(\"falls through to fallback resolver when openrouter has type:oauth credential\", async () => {\n\t\tconst storage = inMemory({\n\t\t\topenrouter: {\n\t\t\t\ttype: \"oauth\",\n\t\t\t\taccess_token: \"sk-or-v1-fake\",\n\t\t\t\trefresh_token: \"rt-fake\",\n\t\t\t\texpires: Date.now() + 3_600_000,\n\t\t\t},\n\t\t});\n\n\t\tstorage.setFallbackResolver((provider) =>\n\t\t\tprovider === \"openrouter\" ? \"sk-or-v1-fallback\" : undefined,\n\t\t);\n\n\t\tconst key = await storage.getApiKey(\"openrouter\");\n\t\tassert.equal(key, \"sk-or-v1-fallback\");\n\t});\n});\n\n// ─── getAll truncation ────────────────────────────────────────────────────────\n\ndescribe(\"AuthStorage — getAll()\", () => {\n\tit(\"returns first credential only for providers with multiple keys\", () => {\n\t\tconst storage = inMemory({\n\t\t\tanthropic: [makeKey(\"sk-1\"), makeKey(\"sk-2\")],\n\t\t\topenai: makeKey(\"sk-openai\"),\n\t\t});\n\t\tconst all = storage.getAll();\n\t\tassert.ok(all[\"anthropic\"]?.type === \"api_key\");\n\t\tassert.equal((all[\"anthropic\"] as any).key, \"sk-1\");\n\t\tassert.equal((all[\"openai\"] as any).key, \"sk-openai\");\n\t});\n});\n"]}
1
+ {"version":3,"file":"auth-storage.test.js","sourceRoot":"","sources":["../../src/core/auth-storage.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, it } from \"node:test\";\nimport assert from \"node:assert/strict\";\nimport { AuthStorage } from \"./auth-storage.js\";\n\n// ─── helpers ──────────────────────────────────────────────────────────────────\n\nfunction makeKey(key: string) {\n\treturn { type: \"api_key\" as const, key };\n}\n\nfunction inMemory(data: Record<string, unknown> = {}) {\n\treturn AuthStorage.inMemory(data as any);\n}\n\n// ─── single credential (backward compat) ─────────────────────────────────────\n\ndescribe(\"AuthStorage — single credential (backward compat)\", () => {\n\tit(\"returns the api key for a provider with one key\", async () => {\n\t\tconst storage = inMemory({ anthropic: makeKey(\"sk-abc\") });\n\t\tconst key = await storage.getApiKey(\"anthropic\");\n\t\tassert.equal(key, \"sk-abc\");\n\t});\n\n\tit(\"returns undefined for unknown provider\", async () => {\n\t\tconst storage = inMemory({});\n\t\tconst key = await storage.getApiKey(\"unknown\");\n\t\tassert.equal(key, undefined);\n\t});\n\n\tit(\"runtime override takes precedence over stored key\", async () => {\n\t\tconst storage = inMemory({ anthropic: makeKey(\"sk-stored\") });\n\t\tstorage.setRuntimeApiKey(\"anthropic\", \"sk-runtime\");\n\t\tconst key = await storage.getApiKey(\"anthropic\");\n\t\tassert.equal(key, \"sk-runtime\");\n\t});\n});\n\n// ─── multiple credentials ─────────────────────────────────────────────────────\n\ndescribe(\"AuthStorage — multiple credentials\", () => {\n\tit(\"round-robins across multiple api keys without sessionId\", async () => {\n\t\tconst storage = inMemory({\n\t\t\tanthropic: [makeKey(\"sk-1\"), makeKey(\"sk-2\"), makeKey(\"sk-3\")],\n\t\t});\n\n\t\tconst keys = new Set<string>();\n\t\tfor (let i = 0; i < 6; i++) {\n\t\t\tconst k = await storage.getApiKey(\"anthropic\");\n\t\t\tassert.ok(k, `call ${i} should return a key`);\n\t\t\tkeys.add(k);\n\t\t}\n\t\t// All three keys should have been selected across 6 calls\n\t\tassert.deepEqual(keys, new Set([\"sk-1\", \"sk-2\", \"sk-3\"]));\n\t});\n\n\tit(\"session-sticky: same sessionId always picks the same key\", async () => {\n\t\tconst storage = inMemory({\n\t\t\tanthropic: [makeKey(\"sk-1\"), makeKey(\"sk-2\"), makeKey(\"sk-3\")],\n\t\t});\n\n\t\tconst sessionId = \"sess-abc\";\n\t\tconst first = await storage.getApiKey(\"anthropic\", sessionId);\n\t\tfor (let i = 0; i < 5; i++) {\n\t\t\tconst k = await storage.getApiKey(\"anthropic\", sessionId);\n\t\t\tassert.equal(k, first, `call ${i} should be sticky to first selection`);\n\t\t}\n\t});\n\n\tit(\"different sessionIds may select different keys\", async () => {\n\t\tconst storage = inMemory({\n\t\t\tanthropic: [makeKey(\"sk-1\"), makeKey(\"sk-2\"), makeKey(\"sk-3\")],\n\t\t});\n\n\t\tconst results = new Set<string>();\n\t\tfor (let i = 0; i < 20; i++) {\n\t\t\tconst k = await storage.getApiKey(\"anthropic\", `sess-${i}`);\n\t\t\tif (k) results.add(k);\n\t\t}\n\t\t// With 20 different sessions and 3 keys, we should see more than one key\n\t\tassert.ok(results.size > 1, \"multiple sessions should hash to different keys\");\n\t});\n});\n\n// ─── login accumulation ───────────────────────────────────────────────────────\n\ndescribe(\"AuthStorage — login accumulation\", () => {\n\tit(\"accumulates api keys on repeated set()\", () => {\n\t\tconst storage = inMemory({});\n\t\tstorage.set(\"anthropic\", makeKey(\"sk-1\"));\n\t\tstorage.set(\"anthropic\", makeKey(\"sk-2\"));\n\t\tconst creds = storage.getCredentialsForProvider(\"anthropic\");\n\t\tassert.equal(creds.length, 2);\n\t\tassert.deepEqual(\n\t\t\tcreds.map((c) => (c.type === \"api_key\" ? c.key : null)),\n\t\t\t[\"sk-1\", \"sk-2\"],\n\t\t);\n\t});\n\n\tit(\"deduplicates identical api keys\", () => {\n\t\tconst storage = inMemory({});\n\t\tstorage.set(\"anthropic\", makeKey(\"sk-1\"));\n\t\tstorage.set(\"anthropic\", makeKey(\"sk-1\"));\n\t\tconst creds = storage.getCredentialsForProvider(\"anthropic\");\n\t\tassert.equal(creds.length, 1);\n\t});\n});\n\n// ─── backoff / markUsageLimitReached ─────────────────────────────────────────\n\ndescribe(\"AuthStorage — rate-limit backoff\", () => {\n\tit(\"returns true when a backed-off credential has an alternate\", async () => {\n\t\tconst storage = inMemory({\n\t\t\tanthropic: [makeKey(\"sk-1\"), makeKey(\"sk-2\")],\n\t\t});\n\n\t\t// Use sk-1 via round-robin (first call, index 0)\n\t\tawait storage.getApiKey(\"anthropic\");\n\n\t\t// Mark it as rate-limited; sk-2 should still be available\n\t\tconst hasAlternate = storage.markUsageLimitReached(\"anthropic\");\n\t\tassert.equal(hasAlternate, true);\n\t});\n\n\tit(\"returns false when all credentials are backed off\", async () => {\n\t\tconst storage = inMemory({\n\t\t\tanthropic: [makeKey(\"sk-1\"), makeKey(\"sk-2\")],\n\t\t});\n\n\t\t// Back off both keys\n\t\tawait storage.getApiKey(\"anthropic\"); // uses index 0\n\t\tstorage.markUsageLimitReached(\"anthropic\"); // backs off index 0\n\t\tawait storage.getApiKey(\"anthropic\"); // uses index 1\n\t\tconst hasAlternate = storage.markUsageLimitReached(\"anthropic\"); // backs off index 1\n\t\tassert.equal(hasAlternate, false);\n\t});\n\n\tit(\"backed-off credential is skipped; next available key is returned\", async () => {\n\t\tconst storage = inMemory({\n\t\t\tanthropic: [makeKey(\"sk-1\"), makeKey(\"sk-2\")],\n\t\t});\n\n\t\t// First call → sk-1 (round-robin index 0)\n\t\tconst first = await storage.getApiKey(\"anthropic\");\n\t\tassert.equal(first, \"sk-1\");\n\n\t\t// Back off sk-1\n\t\tstorage.markUsageLimitReached(\"anthropic\");\n\n\t\t// Next call should skip backed-off sk-1 and return sk-2\n\t\tconst second = await storage.getApiKey(\"anthropic\");\n\t\tassert.equal(second, \"sk-2\");\n\t});\n\n\tit(\"single credential: markUsageLimitReached returns false\", async () => {\n\t\tconst storage = inMemory({ anthropic: makeKey(\"sk-only\") });\n\t\tawait storage.getApiKey(\"anthropic\");\n\t\tconst hasAlternate = storage.markUsageLimitReached(\"anthropic\");\n\t\tassert.equal(hasAlternate, false);\n\t});\n\n\tit(\"single credential: unknown error type skips backoff entirely\", async () => {\n\t\tconst storage = inMemory({ anthropic: makeKey(\"sk-only\") });\n\t\tawait storage.getApiKey(\"anthropic\");\n\n\t\t// Mark with unknown error type (transport failure)\n\t\tconst hasAlternate = storage.markUsageLimitReached(\"anthropic\", undefined, {\n\t\t\terrorType: \"unknown\",\n\t\t});\n\t\tassert.equal(hasAlternate, false);\n\n\t\t// Key should still be available — backoff was not applied\n\t\tconst key = await storage.getApiKey(\"anthropic\");\n\t\tassert.equal(key, \"sk-only\");\n\t});\n\n\tit(\"multiple credentials: unknown error type still backs off the used credential\", async () => {\n\t\tconst storage = inMemory({\n\t\t\tanthropic: [makeKey(\"sk-1\"), makeKey(\"sk-2\")],\n\t\t});\n\t\tawait storage.getApiKey(\"anthropic\"); // uses sk-1\n\n\t\t// Mark with unknown error type — should still back off when alternates exist\n\t\tconst hasAlternate = storage.markUsageLimitReached(\"anthropic\", undefined, {\n\t\t\terrorType: \"unknown\",\n\t\t});\n\t\tassert.equal(hasAlternate, true);\n\n\t\t// Next call should return sk-2\n\t\tconst key = await storage.getApiKey(\"anthropic\");\n\t\tassert.equal(key, \"sk-2\");\n\t});\n\n\tit(\"single credential: rate_limit error type still backs off\", async () => {\n\t\tconst storage = inMemory({ anthropic: makeKey(\"sk-only\") });\n\t\tawait storage.getApiKey(\"anthropic\");\n\n\t\t// rate_limit should still back off even single credentials\n\t\tconst hasAlternate = storage.markUsageLimitReached(\"anthropic\", undefined, {\n\t\t\terrorType: \"rate_limit\",\n\t\t});\n\t\tassert.equal(hasAlternate, false);\n\n\t\t// Key should be backed off\n\t\tconst key = await storage.getApiKey(\"anthropic\");\n\t\tassert.equal(key, undefined);\n\t});\n\n\tit(\"session-sticky: marks the correct credential as backed off\", async () => {\n\t\tconst storage = inMemory({\n\t\t\tanthropic: [makeKey(\"sk-1\"), makeKey(\"sk-2\")],\n\t\t});\n\n\t\tconst sessionId = \"sess-xyz\";\n\t\tconst chosen = await storage.getApiKey(\"anthropic\", sessionId);\n\t\tassert.ok(chosen);\n\n\t\t// Back off the chosen credential for this session\n\t\tconst hasAlternate = storage.markUsageLimitReached(\"anthropic\", sessionId);\n\t\tassert.equal(hasAlternate, true);\n\n\t\t// Next call with same session should return the other key\n\t\tconst next = await storage.getApiKey(\"anthropic\", sessionId);\n\t\tassert.ok(next);\n\t\tassert.notEqual(next, chosen);\n\t});\n});\n\n// ─── areAllCredentialsBackedOff ───────────────────────────────────────────────\n\ndescribe(\"AuthStorage — areAllCredentialsBackedOff\", () => {\n\tit(\"returns false when no credentials are configured\", () => {\n\t\tconst storage = inMemory({});\n\t\tassert.equal(storage.areAllCredentialsBackedOff(\"anthropic\"), false);\n\t});\n\n\tit(\"returns false when credentials exist and none are backed off\", async () => {\n\t\tconst storage = inMemory({ anthropic: makeKey(\"sk-abc\") });\n\t\tassert.equal(storage.areAllCredentialsBackedOff(\"anthropic\"), false);\n\t});\n\n\tit(\"returns true when the single credential is backed off\", async () => {\n\t\tconst storage = inMemory({ anthropic: makeKey(\"sk-only\") });\n\t\tawait storage.getApiKey(\"anthropic\");\n\t\tstorage.markUsageLimitReached(\"anthropic\");\n\t\tassert.equal(storage.areAllCredentialsBackedOff(\"anthropic\"), true);\n\t});\n\n\tit(\"returns false when at least one credential is still available\", async () => {\n\t\tconst storage = inMemory({ anthropic: [makeKey(\"sk-1\"), makeKey(\"sk-2\")] });\n\t\tawait storage.getApiKey(\"anthropic\"); // uses index 0\n\t\tstorage.markUsageLimitReached(\"anthropic\"); // backs off index 0\n\t\t// index 1 is still available\n\t\tassert.equal(storage.areAllCredentialsBackedOff(\"anthropic\"), false);\n\t});\n\n\tit(\"returns true when all credentials are backed off\", async () => {\n\t\tconst storage = inMemory({ anthropic: [makeKey(\"sk-1\"), makeKey(\"sk-2\")] });\n\t\tawait storage.getApiKey(\"anthropic\"); // uses index 0\n\t\tstorage.markUsageLimitReached(\"anthropic\"); // backs off index 0\n\t\tawait storage.getApiKey(\"anthropic\"); // uses index 1\n\t\tstorage.markUsageLimitReached(\"anthropic\"); // backs off index 1\n\t\tassert.equal(storage.areAllCredentialsBackedOff(\"anthropic\"), true);\n\t});\n});\n\n// ─── mismatched oauth credential for non-OAuth provider (#2083) ───────────────\n\ndescribe(\"AuthStorage — oauth credential for non-OAuth provider (#2083)\", () => {\n\tit(\"returns undefined when openrouter has type:oauth (no registered OAuth provider)\", async (t) => {\n\t\t// Simulates the bug: OpenRouter credential stored as type:\"oauth\"\n\t\t// but OpenRouter is not a registered OAuth provider.\n\t\tconst storage = inMemory({\n\t\t\topenrouter: {\n\t\t\t\ttype: \"oauth\",\n\t\t\t\taccess_token: \"sk-or-v1-fake\",\n\t\t\t\trefresh_token: \"rt-fake\",\n\t\t\t\texpires: Date.now() + 3_600_000,\n\t\t\t},\n\t\t});\n\n\t\t// Isolate from any real OPENROUTER_API_KEY in the environment so the\n\t\t// fall-through to env / fallback finds nothing and returns undefined.\n\t\tconst origEnv = process.env.OPENROUTER_API_KEY;\n\t\tdelete process.env.OPENROUTER_API_KEY;\n\t\tt.after(() => {\n\t\t\tif (origEnv === undefined) {\n\t\t\t\tdelete process.env.OPENROUTER_API_KEY;\n\t\t\t} else {\n\t\t\t\tprocess.env.OPENROUTER_API_KEY = origEnv;\n\t\t\t}\n\t\t});\n\n\t\t// Before the fix, getApiKey returns undefined because\n\t\t// resolveCredentialApiKey calls getOAuthProvider(\"openrouter\") → null → undefined.\n\t\t// The key in the oauth credential is never extracted.\n\t\tconst key = await storage.getApiKey(\"openrouter\");\n\t\t// After the fix, the oauth credential with an unrecognised provider\n\t\t// should be skipped, and getApiKey should fall through to env / fallback.\n\t\t// With no env var and no fallback resolver configured, the result is undefined.\n\t\tassert.equal(key, undefined);\n\t});\n\n\tit(\"falls through to env var when openrouter has type:oauth credential\", async (t) => {\n\t\tconst storage = inMemory({\n\t\t\topenrouter: {\n\t\t\t\ttype: \"oauth\",\n\t\t\t\taccess_token: \"sk-or-v1-fake\",\n\t\t\t\trefresh_token: \"rt-fake\",\n\t\t\t\texpires: Date.now() + 3_600_000,\n\t\t\t},\n\t\t});\n\n\t\t// Simulate OPENROUTER_API_KEY being set via env\n\t\tconst origEnv = process.env.OPENROUTER_API_KEY;\n\t\tt.after(() => {\n\t\t\tif (origEnv === undefined) {\n\t\t\t\tdelete process.env.OPENROUTER_API_KEY;\n\t\t\t} else {\n\t\t\t\tprocess.env.OPENROUTER_API_KEY = origEnv;\n\t\t\t}\n\t\t});\n\n\t\tprocess.env.OPENROUTER_API_KEY = \"sk-or-v1-env-key\";\n\t\tconst key = await storage.getApiKey(\"openrouter\");\n\t\tassert.equal(key, \"sk-or-v1-env-key\");\n\t});\n\n\tit(\"falls through to fallback resolver when openrouter has type:oauth credential\", async (t) => {\n\t\tconst storage = inMemory({\n\t\t\topenrouter: {\n\t\t\t\ttype: \"oauth\",\n\t\t\t\taccess_token: \"sk-or-v1-fake\",\n\t\t\t\trefresh_token: \"rt-fake\",\n\t\t\t\texpires: Date.now() + 3_600_000,\n\t\t\t},\n\t\t});\n\n\t\t// Isolate from any real OPENROUTER_API_KEY so env fallback is skipped\n\t\t// and the fallback resolver is reached.\n\t\tconst origEnv = process.env.OPENROUTER_API_KEY;\n\t\tdelete process.env.OPENROUTER_API_KEY;\n\t\tt.after(() => {\n\t\t\tif (origEnv === undefined) {\n\t\t\t\tdelete process.env.OPENROUTER_API_KEY;\n\t\t\t} else {\n\t\t\t\tprocess.env.OPENROUTER_API_KEY = origEnv;\n\t\t\t}\n\t\t});\n\n\t\tstorage.setFallbackResolver((provider) =>\n\t\t\tprovider === \"openrouter\" ? \"sk-or-v1-fallback\" : undefined,\n\t\t);\n\n\t\tconst key = await storage.getApiKey(\"openrouter\");\n\t\tassert.equal(key, \"sk-or-v1-fallback\");\n\t});\n});\n\n// ─── getAll truncation ────────────────────────────────────────────────────────\n\ndescribe(\"AuthStorage — getAll()\", () => {\n\tit(\"returns first credential only for providers with multiple keys\", () => {\n\t\tconst storage = inMemory({\n\t\t\tanthropic: [makeKey(\"sk-1\"), makeKey(\"sk-2\")],\n\t\t\topenai: makeKey(\"sk-openai\"),\n\t\t});\n\t\tconst all = storage.getAll();\n\t\tassert.ok(all[\"anthropic\"]?.type === \"api_key\");\n\t\tassert.equal((all[\"anthropic\"] as any).key, \"sk-1\");\n\t\tassert.equal((all[\"openai\"] as any).key, \"sk-openai\");\n\t});\n});\n"]}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gsd/pi-coding-agent",
3
- "version": "2.46.0",
3
+ "version": "2.46.1",
4
4
  "description": "Coding agent CLI (vendored from pi-mono)",
5
5
  "type": "module",
6
6
  "piConfig": {
@@ -266,7 +266,7 @@ describe("AuthStorage — areAllCredentialsBackedOff", () => {
266
266
  // ─── mismatched oauth credential for non-OAuth provider (#2083) ───────────────
267
267
 
268
268
  describe("AuthStorage — oauth credential for non-OAuth provider (#2083)", () => {
269
- it("returns undefined when openrouter has type:oauth (no registered OAuth provider)", async () => {
269
+ it("returns undefined when openrouter has type:oauth (no registered OAuth provider)", async (t) => {
270
270
  // Simulates the bug: OpenRouter credential stored as type:"oauth"
271
271
  // but OpenRouter is not a registered OAuth provider.
272
272
  const storage = inMemory({
@@ -278,12 +278,25 @@ describe("AuthStorage — oauth credential for non-OAuth provider (#2083)", () =
278
278
  },
279
279
  });
280
280
 
281
+ // Isolate from any real OPENROUTER_API_KEY in the environment so the
282
+ // fall-through to env / fallback finds nothing and returns undefined.
283
+ const origEnv = process.env.OPENROUTER_API_KEY;
284
+ delete process.env.OPENROUTER_API_KEY;
285
+ t.after(() => {
286
+ if (origEnv === undefined) {
287
+ delete process.env.OPENROUTER_API_KEY;
288
+ } else {
289
+ process.env.OPENROUTER_API_KEY = origEnv;
290
+ }
291
+ });
292
+
281
293
  // Before the fix, getApiKey returns undefined because
282
294
  // resolveCredentialApiKey calls getOAuthProvider("openrouter") → null → undefined.
283
295
  // The key in the oauth credential is never extracted.
284
296
  const key = await storage.getApiKey("openrouter");
285
297
  // After the fix, the oauth credential with an unrecognised provider
286
298
  // should be skipped, and getApiKey should fall through to env / fallback.
299
+ // With no env var and no fallback resolver configured, the result is undefined.
287
300
  assert.equal(key, undefined);
288
301
  });
289
302
 
@@ -312,7 +325,7 @@ describe("AuthStorage — oauth credential for non-OAuth provider (#2083)", () =
312
325
  assert.equal(key, "sk-or-v1-env-key");
313
326
  });
314
327
 
315
- it("falls through to fallback resolver when openrouter has type:oauth credential", async () => {
328
+ it("falls through to fallback resolver when openrouter has type:oauth credential", async (t) => {
316
329
  const storage = inMemory({
317
330
  openrouter: {
318
331
  type: "oauth",
@@ -322,6 +335,18 @@ describe("AuthStorage — oauth credential for non-OAuth provider (#2083)", () =
322
335
  },
323
336
  });
324
337
 
338
+ // Isolate from any real OPENROUTER_API_KEY so env fallback is skipped
339
+ // and the fallback resolver is reached.
340
+ const origEnv = process.env.OPENROUTER_API_KEY;
341
+ delete process.env.OPENROUTER_API_KEY;
342
+ t.after(() => {
343
+ if (origEnv === undefined) {
344
+ delete process.env.OPENROUTER_API_KEY;
345
+ } else {
346
+ process.env.OPENROUTER_API_KEY = origEnv;
347
+ }
348
+ });
349
+
325
350
  storage.setFallbackResolver((provider) =>
326
351
  provider === "openrouter" ? "sk-or-v1-fallback" : undefined,
327
352
  );
package/pkg/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glittercowboy/gsd",
3
- "version": "2.46.0",
3
+ "version": "2.46.1",
4
4
  "piConfig": {
5
5
  "name": "gsd",
6
6
  "configDir": ".gsd"
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Claude Code CLI Provider Extension
3
+ *
4
+ * Registers a model provider that delegates inference to the user's
5
+ * locally-installed Claude Code CLI via the official Agent SDK.
6
+ *
7
+ * Users with a Claude Code subscription (Pro/Max/Team) get access to
8
+ * subsidized inference through GSD's UI — no API key required.
9
+ *
10
+ * TOS-compliant: uses Anthropic's official `@anthropic-ai/claude-agent-sdk`,
11
+ * never touches credentials, never offers a login flow.
12
+ */
13
+
14
+ import type { ExtensionAPI } from "@gsd/pi-coding-agent";
15
+ import { CLAUDE_CODE_MODELS } from "./models.js";
16
+ import { isClaudeCodeReady } from "./readiness.js";
17
+ import { streamViaClaudeCode } from "./stream-adapter.js";
18
+
19
+ export default function claudeCodeCli(pi: ExtensionAPI) {
20
+ pi.registerProvider("claude-code", {
21
+ authMode: "externalCli",
22
+ api: "anthropic-messages",
23
+ baseUrl: "local://claude-code",
24
+ isReady: isClaudeCodeReady,
25
+ streamSimple: streamViaClaudeCode,
26
+ models: CLAUDE_CODE_MODELS,
27
+ });
28
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Model definitions for the Claude Code CLI provider.
3
+ *
4
+ * Costs are zero because inference is covered by the user's Claude Code
5
+ * subscription. The SDK's `result` message still provides token counts
6
+ * for display in the TUI.
7
+ *
8
+ * Context windows and max tokens match the Anthropic API definitions
9
+ * in models.generated.ts.
10
+ */
11
+
12
+ const ZERO_COST = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
13
+
14
+ export const CLAUDE_CODE_MODELS = [
15
+ {
16
+ id: "claude-opus-4-6",
17
+ name: "Claude Opus 4.6 (via Claude Code)",
18
+ reasoning: true,
19
+ input: ["text", "image"] as ("text" | "image")[],
20
+ cost: ZERO_COST,
21
+ contextWindow: 1_000_000,
22
+ maxTokens: 128_000,
23
+ },
24
+ {
25
+ id: "claude-sonnet-4-6",
26
+ name: "Claude Sonnet 4.6 (via Claude Code)",
27
+ reasoning: true,
28
+ input: ["text", "image"] as ("text" | "image")[],
29
+ cost: ZERO_COST,
30
+ contextWindow: 1_000_000,
31
+ maxTokens: 64_000,
32
+ },
33
+ {
34
+ id: "claude-haiku-4-5",
35
+ name: "Claude Haiku 4.5 (via Claude Code)",
36
+ reasoning: true,
37
+ input: ["text", "image"] as ("text" | "image")[],
38
+ cost: ZERO_COST,
39
+ contextWindow: 200_000,
40
+ maxTokens: 64_000,
41
+ },
42
+ ];
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "@gsd/claude-code-cli",
3
+ "private": true,
4
+ "version": "1.0.0",
5
+ "type": "module",
6
+ "pi": {
7
+ "extensions": [
8
+ "./index.ts"
9
+ ]
10
+ }
11
+ }