@pyric/cli 0.1.0-alpha.11 → 0.1.0-alpha.13

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 (424) hide show
  1. package/dist/cli/serve.js +4 -4
  2. package/dist/cli/serve.js.map +1 -1
  3. package/dist/conformance/.generated/can-i-use-browser.d.ts.map +1 -1
  4. package/dist/conformance/.generated/can-i-use-browser.js +1 -1
  5. package/dist/conformance/.generated/can-i-use-browser.js.map +1 -1
  6. package/dist/conformance/.generated/can-i-use.d.ts.map +1 -1
  7. package/dist/conformance/.generated/can-i-use.js +2 -2
  8. package/dist/conformance/.generated/can-i-use.js.map +1 -1
  9. package/dist/serve/bundler.d.ts +13 -8
  10. package/dist/serve/bundler.d.ts.map +1 -1
  11. package/dist/serve/bundler.js +31 -36
  12. package/dist/serve/bundler.js.map +1 -1
  13. package/dist/serve/docs-ui/docs/_rhythm/index.html +1 -1
  14. package/dist/serve/docs-ui/docs/agent/set-up-your-agent/index.html +1 -1
  15. package/dist/serve/docs-ui/docs/agent/watch-and-review/index.html +1 -1
  16. package/dist/serve/docs-ui/docs/agent/work-with-an-agent/index.html +1 -1
  17. package/dist/serve/docs-ui/docs/ai-compat/index.html +1 -1
  18. package/dist/serve/docs-ui/docs/api-reference/index.html +2 -2
  19. package/dist/serve/docs-ui/docs/api-reference.md +1 -1
  20. package/dist/serve/docs-ui/docs/app-compat/index.html +1 -1
  21. package/dist/serve/docs-ui/docs/auth-compat/index.html +1 -1
  22. package/dist/serve/docs-ui/docs/build/ai-logic/index.html +44 -22
  23. package/dist/serve/docs-ui/docs/build/ai-logic.md +69 -21
  24. package/dist/serve/docs-ui/docs/build/authentication/index.html +1 -1
  25. package/dist/serve/docs-ui/docs/build/cloud-firestore/index.html +1 -1
  26. package/dist/serve/docs-ui/docs/build/cloud-messaging/index.html +1 -1
  27. package/dist/serve/docs-ui/docs/build/cloud-storage/index.html +1 -1
  28. package/dist/serve/docs-ui/docs/build/realtime-database/index.html +1 -1
  29. package/dist/serve/docs-ui/docs/conformance-scores/index.html +1 -1
  30. package/dist/serve/docs-ui/docs/create-pyric-reference-api/index.html +1 -1
  31. package/dist/serve/docs-ui/docs/database-compat/index.html +1 -1
  32. package/dist/serve/docs-ui/docs/firestore-compat/index.html +1 -1
  33. package/dist/serve/docs-ui/docs/functions-rtdb-compat/index.html +1 -1
  34. package/dist/serve/docs-ui/docs/get-started/how-the-swap-works/index.html +2 -1
  35. package/dist/serve/docs-ui/docs/get-started/how-the-swap-works.md +2 -0
  36. package/dist/serve/docs-ui/docs/get-started/start-building/index.html +3 -2
  37. package/dist/serve/docs-ui/docs/get-started/start-building.md +3 -1
  38. package/dist/serve/docs-ui/docs/index.json +349 -29
  39. package/dist/serve/docs-ui/docs/messaging-compat/index.html +1 -1
  40. package/dist/serve/docs-ui/docs/observe/resolve-runtime-status/index.html +28 -0
  41. package/dist/serve/docs-ui/docs/observe/resolve-runtime-status.md +47 -0
  42. package/dist/serve/docs-ui/docs/observe/see-whats-happening/index.html +1 -1
  43. package/dist/serve/docs-ui/docs/observe/shape-your-data/index.html +1 -1
  44. package/dist/serve/docs-ui/docs/overview/index.html +1 -1
  45. package/dist/serve/docs-ui/docs/pyric-admin-app-reference-api/index.html +1 -1
  46. package/dist/serve/docs-ui/docs/pyric-admin-auth-reference-api/index.html +1 -1
  47. package/dist/serve/docs-ui/docs/pyric-admin-database-reference-api/index.html +1 -1
  48. package/dist/serve/docs-ui/docs/pyric-admin-firestore-reference-api/index.html +1 -1
  49. package/dist/serve/docs-ui/docs/pyric-admin-messaging-reference-api/index.html +916 -0
  50. package/dist/serve/docs-ui/docs/pyric-admin-messaging-reference-api.md +706 -0
  51. package/dist/serve/docs-ui/docs/pyric-admin-storage-reference-api/index.html +1 -1
  52. package/dist/serve/docs-ui/docs/pyric-ai-reference-api/index.html +1 -1
  53. package/dist/serve/docs-ui/docs/pyric-ai-scripting-reference-api/index.html +1 -1
  54. package/dist/serve/docs-ui/docs/pyric-app-reference-api/index.html +1 -1
  55. package/dist/serve/docs-ui/docs/pyric-auth-reference-api/index.html +1 -1
  56. package/dist/serve/docs-ui/docs/pyric-cli-assurance-browser-reference-api/index.html +1 -1
  57. package/dist/serve/docs-ui/docs/pyric-cli-assurance-reference-api/index.html +1 -1
  58. package/dist/serve/docs-ui/docs/pyric-cli-bridge-client-reference-api/index.html +1 -1
  59. package/dist/serve/docs-ui/docs/pyric-cli-bridge-reference-api/index.html +1 -1
  60. package/dist/serve/docs-ui/docs/pyric-cli-conformance-browser-reference-api/index.html +1 -1
  61. package/dist/serve/docs-ui/docs/pyric-cli-conformance-docs-reference-api/index.html +1 -1
  62. package/dist/serve/docs-ui/docs/pyric-cli-conformance-reference-api/index.html +1 -1
  63. package/dist/serve/docs-ui/docs/pyric-cli-credentials-node-reference-api/index.html +1 -1
  64. package/dist/serve/docs-ui/docs/pyric-cli-discover-reference-api/index.html +1 -1
  65. package/dist/serve/docs-ui/docs/pyric-cli-register-reference-api/index.html +1 -1
  66. package/dist/serve/docs-ui/docs/pyric-cli-remote-reference-api/index.html +1 -1
  67. package/dist/serve/docs-ui/docs/pyric-cli-serve-worker-reference-api/index.html +168 -76
  68. package/dist/serve/docs-ui/docs/pyric-cli-serve-worker-reference-api.md +156 -73
  69. package/dist/serve/docs-ui/docs/pyric-cli-verify-reference-api/index.html +1 -1
  70. package/dist/serve/docs-ui/docs/pyric-cli-vite-reference-api/index.html +11 -8
  71. package/dist/serve/docs-ui/docs/pyric-cli-vite-reference-api.md +17 -4
  72. package/dist/serve/docs-ui/docs/pyric-database-reference-api/index.html +1 -1
  73. package/dist/serve/docs-ui/docs/pyric-firestore-reference-api/index.html +1 -1
  74. package/dist/serve/docs-ui/docs/pyric-messaging-reference-api/index.html +1 -1
  75. package/dist/serve/docs-ui/docs/pyric-messaging-sw-reference-api/index.html +1 -1
  76. package/dist/serve/docs-ui/docs/pyric-rules-reference-api/index.html +1 -1
  77. package/dist/serve/docs-ui/docs/pyric-sandbox-database-reference-api/index.html +1 -1
  78. package/dist/serve/docs-ui/docs/pyric-sandbox-firestore-reference-api/index.html +1 -1
  79. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-api/index.html +37 -14
  80. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-api.md +28 -8
  81. package/dist/serve/docs-ui/docs/pyric-storage-reference-api/index.html +1 -1
  82. package/dist/serve/docs-ui/docs/pyric-ui-agents-reference-api/index.html +1 -1
  83. package/dist/serve/docs-ui/docs/pyric-ui-auth-hooks-reference-api/index.html +1 -1
  84. package/dist/serve/docs-ui/docs/pyric-ui-auth-reference-api/index.html +1 -1
  85. package/dist/serve/docs-ui/docs/pyric-ui-events-hooks-reference-api/index.html +1 -1
  86. package/dist/serve/docs-ui/docs/pyric-ui-events-reference-api/index.html +1 -1
  87. package/dist/serve/docs-ui/docs/pyric-ui-firestore-hooks-reference-api/index.html +1 -1
  88. package/dist/serve/docs-ui/docs/pyric-ui-firestore-reference-api/index.html +1 -1
  89. package/dist/serve/docs-ui/docs/pyric-ui-primitives-reference-api/index.html +1 -1
  90. package/dist/serve/docs-ui/docs/pyric-ui-rtdb-reference-api/index.html +1 -1
  91. package/dist/serve/docs-ui/docs/pyric-ui-rules-hooks-reference-api/index.html +1 -1
  92. package/dist/serve/docs-ui/docs/pyric-ui-rules-reference-api/index.html +1 -1
  93. package/dist/serve/docs-ui/docs/pyric-ui-storage-hooks-reference-api/index.html +1 -1
  94. package/dist/serve/docs-ui/docs/pyric-ui-storage-reference-api/index.html +1 -1
  95. package/dist/serve/docs-ui/docs/pyric-ui-traffic-hooks-reference-api/index.html +1 -1
  96. package/dist/serve/docs-ui/docs/pyric-ui-traffic-reference-api/index.html +1 -1
  97. package/dist/serve/docs-ui/docs/rules-compat/index.html +1 -1
  98. package/dist/serve/docs-ui/docs/secure/audit-your-rules/index.html +1 -1
  99. package/dist/serve/docs-ui/docs/secure/firestore-rules-limits/index.html +1 -1
  100. package/dist/serve/docs-ui/docs/secure/read-a-denial/index.html +1 -1
  101. package/dist/serve/docs-ui/docs/secure/rtdb-rules-in-typescript/index.html +1 -1
  102. package/dist/serve/docs-ui/docs/secure/rules-standard-library/index.html +1 -1
  103. package/dist/serve/docs-ui/docs/secure/secure-it-with-rules/index.html +1 -1
  104. package/dist/serve/docs-ui/docs/secure/simulate-and-lint/index.html +1 -1
  105. package/dist/serve/docs-ui/docs/secure/write-a-rules-test-suite/index.html +1 -1
  106. package/dist/serve/docs-ui/docs/ship/ship-to-production/index.html +1 -1
  107. package/dist/serve/docs-ui/docs/ship/test-in-node/index.html +1 -1
  108. package/dist/serve/docs-ui/docs/storage-compat/index.html +1 -1
  109. package/dist/serve/docs-ui/docs/trust/how-we-know-it-matches-firebase/index.html +1 -1
  110. package/dist/serve/docs-ui/docs/trust/versioning-and-compatibility/index.html +1 -1
  111. package/dist/serve/docs-ui/docs/tutorial/index.html +429 -0
  112. package/dist/serve/docs-ui/docs/tutorial.md +701 -0
  113. package/dist/serve/docs-ui/llms.txt +3 -1
  114. package/dist/serve/entries/init.d.ts.map +1 -1
  115. package/dist/serve/entries/init.js +3 -0
  116. package/dist/serve/entries/init.js.map +1 -1
  117. package/dist/serve/entries/runtime.d.ts.map +1 -1
  118. package/dist/serve/entries/runtime.js +20 -5
  119. package/dist/serve/entries/runtime.js.map +1 -1
  120. package/dist/serve/entries/worker-runtime.d.ts +2 -2
  121. package/dist/serve/entries/worker-runtime.d.ts.map +1 -1
  122. package/dist/serve/entries/worker-runtime.js +45 -10
  123. package/dist/serve/entries/worker-runtime.js.map +1 -1
  124. package/dist/serve/runtime/chip-config.d.ts +19 -0
  125. package/dist/serve/runtime/chip-config.d.ts.map +1 -0
  126. package/dist/serve/runtime/chip-config.js +19 -0
  127. package/dist/serve/runtime/chip-config.js.map +1 -0
  128. package/dist/serve/runtime/chip-install.d.ts +10 -0
  129. package/dist/serve/runtime/chip-install.d.ts.map +1 -0
  130. package/dist/serve/runtime/chip-install.js +15 -0
  131. package/dist/serve/runtime/chip-install.js.map +1 -0
  132. package/dist/serve/runtime/chip.d.ts +17 -0
  133. package/dist/serve/runtime/chip.d.ts.map +1 -0
  134. package/dist/serve/runtime/chip.js +257 -0
  135. package/dist/serve/runtime/chip.js.map +1 -0
  136. package/dist/serve/runtime/manifest.d.ts +22 -0
  137. package/dist/serve/runtime/manifest.d.ts.map +1 -0
  138. package/dist/serve/runtime/manifest.js +20 -0
  139. package/dist/serve/runtime/manifest.js.map +1 -0
  140. package/dist/serve/runtime/status.d.ts +49 -0
  141. package/dist/serve/runtime/status.d.ts.map +1 -0
  142. package/dist/serve/runtime/status.js +164 -0
  143. package/dist/serve/runtime/status.js.map +1 -0
  144. package/dist/serve/runtime/worker-connection.d.ts +4 -0
  145. package/dist/serve/runtime/worker-connection.d.ts.map +1 -0
  146. package/dist/serve/runtime/worker-connection.js +11 -0
  147. package/dist/serve/runtime/worker-connection.js.map +1 -0
  148. package/dist/serve/runtime/worker-generation.d.ts +14 -0
  149. package/dist/serve/runtime/worker-generation.d.ts.map +1 -0
  150. package/dist/serve/runtime/worker-generation.js +56 -0
  151. package/dist/serve/runtime/worker-generation.js.map +1 -0
  152. package/dist/serve/runtime/worker-replacement.d.ts +18 -0
  153. package/dist/serve/runtime/worker-replacement.d.ts.map +1 -0
  154. package/dist/serve/runtime/worker-replacement.js +29 -0
  155. package/dist/serve/runtime/worker-replacement.js.map +1 -0
  156. package/dist/serve/standalone-assets.d.ts +2 -2
  157. package/dist/serve/standalone-assets.d.ts.map +1 -1
  158. package/dist/serve/standalone-assets.js +1 -1
  159. package/dist/serve/standalone-assets.js.map +1 -1
  160. package/dist/serve/studio-ui/assets/{RulesCodeEditor-CxVYcZXw.js → RulesCodeEditor-BDlH-5lp.js} +1 -1
  161. package/dist/serve/studio-ui/assets/{index-cpSfgQ7H.js → index-Bf9SSE8L.js} +5 -5
  162. package/dist/serve/studio-ui/assets/{seed-DeuFKi4J.js → seed-D_UjxfOU.js} +1 -1
  163. package/dist/serve/studio-ui/index.html +1 -1
  164. package/dist/serve/vite-ai-config.d.ts +25 -0
  165. package/dist/serve/vite-ai-config.d.ts.map +1 -0
  166. package/dist/serve/vite-ai-config.js +45 -0
  167. package/dist/serve/vite-ai-config.js.map +1 -0
  168. package/dist/serve/vite-plugin.d.ts +17 -36
  169. package/dist/serve/vite-plugin.d.ts.map +1 -1
  170. package/dist/serve/vite-plugin.js +38 -57
  171. package/dist/serve/vite-plugin.js.map +1 -1
  172. package/dist/serve/vite-rules-source.d.ts +8 -0
  173. package/dist/serve/vite-rules-source.d.ts.map +1 -0
  174. package/dist/serve/vite-rules-source.js +23 -0
  175. package/dist/serve/vite-rules-source.js.map +1 -0
  176. package/dist/serve/vite-worker-runtime.d.ts +20 -0
  177. package/dist/serve/vite-worker-runtime.d.ts.map +1 -0
  178. package/dist/serve/vite-worker-runtime.js +25 -0
  179. package/dist/serve/vite-worker-runtime.js.map +1 -0
  180. package/dist/serve/worker/client/connection.d.ts +7 -2
  181. package/dist/serve/worker/client/connection.d.ts.map +1 -1
  182. package/dist/serve/worker/client/connection.js +13 -16
  183. package/dist/serve/worker/client/connection.js.map +1 -1
  184. package/dist/serve/worker/client/core.d.ts +7 -0
  185. package/dist/serve/worker/client/core.d.ts.map +1 -1
  186. package/dist/serve/worker/client/core.js +28 -0
  187. package/dist/serve/worker/client/core.js.map +1 -1
  188. package/dist/serve/worker/client/runtime-control.d.ts +8 -0
  189. package/dist/serve/worker/client/runtime-control.d.ts.map +1 -0
  190. package/dist/serve/worker/client/runtime-control.js +11 -0
  191. package/dist/serve/worker/client/runtime-control.js.map +1 -0
  192. package/dist/serve/worker/entry.js +44 -2
  193. package/dist/serve/worker/entry.js.map +1 -1
  194. package/dist/serve/worker/host/core.js +1 -1
  195. package/dist/serve/worker/host/core.js.map +1 -1
  196. package/dist/serve/worker/host/firestore-writes.d.ts.map +1 -1
  197. package/dist/serve/worker/host/firestore-writes.js +3 -2
  198. package/dist/serve/worker/host/firestore-writes.js.map +1 -1
  199. package/dist/serve/worker/host/studio.js +1 -1
  200. package/dist/serve/worker/host/studio.js.map +1 -1
  201. package/dist/serve/worker/host-context.d.ts +1 -1
  202. package/dist/serve/worker/host-context.d.ts.map +1 -1
  203. package/dist/serve/worker/index.d.ts +2 -1
  204. package/dist/serve/worker/index.d.ts.map +1 -1
  205. package/dist/serve/worker/index.js +2 -1
  206. package/dist/serve/worker/index.js.map +1 -1
  207. package/dist/serve/worker/protocol.d.ts +15 -1
  208. package/dist/serve/worker/protocol.d.ts.map +1 -1
  209. package/dist/serve/worker/protocol.js +2 -2
  210. package/dist/serve/worker/protocol.js.map +1 -1
  211. package/dist/serve/worker/retirement.d.ts +21 -0
  212. package/dist/serve/worker/retirement.d.ts.map +1 -0
  213. package/dist/serve/worker/retirement.js +97 -0
  214. package/dist/serve/worker/retirement.js.map +1 -0
  215. package/dist/serve/worker/serve-init.d.ts.map +1 -1
  216. package/dist/serve/worker/serve-init.js +5 -6
  217. package/dist/serve/worker/serve-init.js.map +1 -1
  218. package/dist/vite.d.ts +1 -0
  219. package/dist/vite.d.ts.map +1 -1
  220. package/dist/vite.js.map +1 -1
  221. package/package.json +6 -4
  222. package/src/assurance/.generated/conformance-verdicts.ts +1090 -0
  223. package/src/assurance/attachment.ts +211 -0
  224. package/src/assurance/browser.ts +80 -0
  225. package/src/assurance/campaign.ts +496 -0
  226. package/src/assurance/capabilities.ts +446 -0
  227. package/src/assurance/cases.ts +43 -0
  228. package/src/assurance/index.ts +75 -0
  229. package/src/assurance/runner.ts +885 -0
  230. package/src/assurance/tool-names.ts +15 -0
  231. package/src/assurance/tools.ts +830 -0
  232. package/src/assurance/types.ts +352 -0
  233. package/src/assurance/validation.ts +434 -0
  234. package/src/bridge/client/bridge.ts +550 -0
  235. package/src/bridge/client/dispatch.ts +137 -0
  236. package/src/bridge/client.ts +45 -0
  237. package/src/bridge/protocol.ts +351 -0
  238. package/src/bridge/server/audit.ts +53 -0
  239. package/src/bridge/server/bridge.ts +597 -0
  240. package/src/bridge/server/headless.ts +160 -0
  241. package/src/bridge/server/json-schema-to-zod.ts +108 -0
  242. package/src/bridge/server/local-bridge.ts +66 -0
  243. package/src/bridge/server/logger.ts +50 -0
  244. package/src/bridge/server/mcp-contract.ts +93 -0
  245. package/src/bridge/server/mcp.ts +129 -0
  246. package/src/bridge/server/peer.ts +232 -0
  247. package/src/bridge/server/standalone.ts +347 -0
  248. package/src/bridge/server/tool-metadata.ts +99 -0
  249. package/src/bridge/server.ts +28 -0
  250. package/src/cli/can-i-use.ts +50 -0
  251. package/src/cli/cli.test.ts +493 -0
  252. package/src/cli/database-rules.ts +329 -0
  253. package/src/cli/dev-runner.ts +282 -0
  254. package/src/cli/firebase-json.ts +100 -0
  255. package/src/cli/firestore-indexes.ts +70 -0
  256. package/src/cli/index.ts +425 -0
  257. package/src/cli/init.ts +260 -0
  258. package/src/cli/mcp-proxy.ts +196 -0
  259. package/src/cli/parse-args.ts +80 -0
  260. package/src/cli/rules.ts +259 -0
  261. package/src/cli/scope.ts +67 -0
  262. package/src/cli/serve.ts +996 -0
  263. package/src/cli/service-commands.ts +73 -0
  264. package/src/cli/snapshot.ts +164 -0
  265. package/src/cli/storage-rules.ts +172 -0
  266. package/src/cli/verify.ts +416 -0
  267. package/src/conformance/.generated/can-i-use-browser.ts +113 -0
  268. package/src/conformance/.generated/can-i-use.ts +117 -0
  269. package/src/conformance/.generated/conformance-docs.ts +14 -0
  270. package/src/conformance/browser.ts +24 -0
  271. package/src/conformance/can-i-use-tool.ts +51 -0
  272. package/src/conformance/can-i-use.ts +18 -0
  273. package/src/conformance/docs.ts +8 -0
  274. package/src/conformance/index.ts +20 -0
  275. package/src/conformance/tools.ts +16 -0
  276. package/src/credentials/core/memoize-ttl.ts +142 -0
  277. package/src/credentials/core/types.ts +10 -0
  278. package/src/credentials/node/from-adc.ts +96 -0
  279. package/src/credentials/node/from-service-account.ts +133 -0
  280. package/src/credentials/node/index.ts +6 -0
  281. package/src/discover/concurrency.ts +127 -0
  282. package/src/discover/crawler-adapter.ts +142 -0
  283. package/src/discover/crawler.ts +1127 -0
  284. package/src/discover/credential-free.ts +27 -0
  285. package/src/discover/findCollectionGroup.ts +131 -0
  286. package/src/discover/firestore-source.ts +59 -0
  287. package/src/discover/index.ts +5 -0
  288. package/src/discover/merge.ts +523 -0
  289. package/src/discover/session.ts +402 -0
  290. package/src/discover/tools.ts +201 -0
  291. package/src/discover/types.ts +187 -0
  292. package/src/discover/wire.ts +324 -0
  293. package/src/functions-rtdb/child.ts +382 -0
  294. package/src/functions-rtdb/delivery.ts +7 -0
  295. package/src/functions-rtdb/discovery.ts +136 -0
  296. package/src/functions-rtdb/event.ts +64 -0
  297. package/src/functions-rtdb/execution.ts +111 -0
  298. package/src/functions-rtdb/in-memory-delivery.ts +40 -0
  299. package/src/functions-rtdb/project.ts +74 -0
  300. package/src/functions-rtdb/projection.ts +104 -0
  301. package/src/functions-rtdb/reference-pattern.ts +20 -0
  302. package/src/functions-rtdb/remote-delivery.ts +23 -0
  303. package/src/pkg-version.ts +49 -0
  304. package/src/register/esm-exports.ts +63 -0
  305. package/src/register/hooks.ts +30 -0
  306. package/src/register/index.ts +150 -0
  307. package/src/register/mapping.ts +33 -0
  308. package/src/remote/index.ts +1050 -0
  309. package/src/rtdb/crawl-snapshot.ts +118 -0
  310. package/src/rtdb/inspection.ts +137 -0
  311. package/src/rtdb/load-rules-document.ts +56 -0
  312. package/src/rtdb/rules-generation-tool.ts +38 -0
  313. package/src/rtdb/rules-json.ts +29 -0
  314. package/src/serve/activity-guard.ts +29 -0
  315. package/src/serve/activity-route.ts +164 -0
  316. package/src/serve/activity-warning.ts +26 -0
  317. package/src/serve/bridge-mount.ts +200 -0
  318. package/src/serve/bundler.ts +519 -0
  319. package/src/serve/capture-store.ts +65 -0
  320. package/src/serve/discovery.ts +199 -0
  321. package/src/serve/entries/ai.ts +292 -0
  322. package/src/serve/entries/app-backend.ts +4 -0
  323. package/src/serve/entries/app-client.ts +26 -0
  324. package/src/serve/entries/app-session-store.ts +47 -0
  325. package/src/serve/entries/app.ts +11 -0
  326. package/src/serve/entries/auth-helper-core.ts +181 -0
  327. package/src/serve/entries/auth-helper-dom.ts +134 -0
  328. package/src/serve/entries/auth-helper-runtime.ts +20 -0
  329. package/src/serve/entries/auth.ts +255 -0
  330. package/src/serve/entries/bridge-url.ts +31 -0
  331. package/src/serve/entries/database.ts +129 -0
  332. package/src/serve/entries/firestore.ts +232 -0
  333. package/src/serve/entries/init.ts +49 -0
  334. package/src/serve/entries/keepalive.ts +46 -0
  335. package/src/serve/entries/messaging-sw.ts +75 -0
  336. package/src/serve/entries/messaging.ts +96 -0
  337. package/src/serve/entries/runtime.ts +556 -0
  338. package/src/serve/entries/session-store.ts +97 -0
  339. package/src/serve/entries/storage.ts +92 -0
  340. package/src/serve/entries/tab-sync-wiring.ts +274 -0
  341. package/src/serve/entries/worker-runtime.ts +135 -0
  342. package/src/serve/init-payload.ts +42 -0
  343. package/src/serve/namespace.ts +675 -0
  344. package/src/serve/open-browser.ts +68 -0
  345. package/src/serve/rules.ts +272 -0
  346. package/src/serve/runtime/chip-config.ts +30 -0
  347. package/src/serve/runtime/chip-install.ts +27 -0
  348. package/src/serve/runtime/chip.ts +282 -0
  349. package/src/serve/runtime/manifest.ts +37 -0
  350. package/src/serve/runtime/status.ts +216 -0
  351. package/src/serve/runtime/worker-connection.ts +14 -0
  352. package/src/serve/runtime/worker-generation.ts +75 -0
  353. package/src/serve/runtime/worker-replacement.ts +42 -0
  354. package/src/serve/sandbox-marker.ts +31 -0
  355. package/src/serve/server.ts +428 -0
  356. package/src/serve/standalone-assets.ts +180 -0
  357. package/src/serve/state-store.ts +166 -0
  358. package/src/serve/studio/disk-project-store.ts +185 -0
  359. package/src/serve/studio/disk-workspace.ts +162 -0
  360. package/src/serve/studio/index.ts +20 -0
  361. package/src/serve/studio/routes.ts +237 -0
  362. package/src/serve/studio/store-types.ts +48 -0
  363. package/src/serve/studio/studio-storage.test.ts +289 -0
  364. package/src/serve/vite-ai-config.ts +77 -0
  365. package/src/serve/vite-plugin.ts +1075 -0
  366. package/src/serve/vite-rules-source.ts +28 -0
  367. package/src/serve/vite-worker-runtime.ts +51 -0
  368. package/src/serve/worker/activity-bootstrap.ts +24 -0
  369. package/src/serve/worker/client/admin-firestore.ts +38 -0
  370. package/src/serve/worker/client/ai.ts +124 -0
  371. package/src/serve/worker/client/auth.ts +489 -0
  372. package/src/serve/worker/client/connection.ts +227 -0
  373. package/src/serve/worker/client/core.ts +384 -0
  374. package/src/serve/worker/client/disconnect.ts +45 -0
  375. package/src/serve/worker/client/firestore-reads.ts +181 -0
  376. package/src/serve/worker/client/firestore-refs.ts +226 -0
  377. package/src/serve/worker/client/firestore-writes.ts +232 -0
  378. package/src/serve/worker/client/handles.ts +91 -0
  379. package/src/serve/worker/client/messaging.ts +119 -0
  380. package/src/serve/worker/client/presence.ts +174 -0
  381. package/src/serve/worker/client/rtdb.ts +311 -0
  382. package/src/serve/worker/client/rules.ts +63 -0
  383. package/src/serve/worker/client/runtime-control.ts +22 -0
  384. package/src/serve/worker/client/service-worker-connection.ts +59 -0
  385. package/src/serve/worker/client/snapshots.ts +86 -0
  386. package/src/serve/worker/client/storage.ts +195 -0
  387. package/src/serve/worker/client/studio.ts +88 -0
  388. package/src/serve/worker/client.ts +57 -0
  389. package/src/serve/worker/durable-persistence.ts +137 -0
  390. package/src/serve/worker/entry.ts +223 -0
  391. package/src/serve/worker/host/admin-firestore.ts +88 -0
  392. package/src/serve/worker/host/connection.ts +179 -0
  393. package/src/serve/worker/host/core.ts +387 -0
  394. package/src/serve/worker/host/dispatch.ts +316 -0
  395. package/src/serve/worker/host/firestore-reads.ts +117 -0
  396. package/src/serve/worker/host/firestore-writes.ts +456 -0
  397. package/src/serve/worker/host/presence.ts +312 -0
  398. package/src/serve/worker/host/rtdb.ts +136 -0
  399. package/src/serve/worker/host/rules.ts +128 -0
  400. package/src/serve/worker/host/storage.ts +312 -0
  401. package/src/serve/worker/host/studio.ts +75 -0
  402. package/src/serve/worker/host/subscriptions.ts +238 -0
  403. package/src/serve/worker/host-ai.ts +165 -0
  404. package/src/serve/worker/host-auth.ts +469 -0
  405. package/src/serve/worker/host-context.ts +273 -0
  406. package/src/serve/worker/host-events.ts +94 -0
  407. package/src/serve/worker/host-messaging.ts +240 -0
  408. package/src/serve/worker/host.ts +47 -0
  409. package/src/serve/worker/index.ts +196 -0
  410. package/src/serve/worker/presence-timing.ts +15 -0
  411. package/src/serve/worker/protocol.ts +1124 -0
  412. package/src/serve/worker/retirement.ts +125 -0
  413. package/src/serve/worker/serve-init.ts +605 -0
  414. package/src/serve/worker/service-worker-channel.ts +33 -0
  415. package/src/serve/worker/service-worker-relay.ts +86 -0
  416. package/src/serve/writer-lock.ts +53 -0
  417. package/src/verify/cases.ts +233 -0
  418. package/src/verify/fixture.ts +258 -0
  419. package/src/verify/index.ts +519 -0
  420. package/src/verify/tools.ts +108 -0
  421. package/src/version/compat-target.ts +17 -0
  422. package/src/vite.ts +18 -0
  423. package/dist/serve/docs-ui/docs/pyric-firestore-values-reference-api/index.html +0 -31
  424. package/dist/serve/docs-ui/docs/pyric-firestore-values-reference-api.md +0 -29
@@ -0,0 +1,260 @@
1
+ /**
2
+ * `pyric init` — scaffold a pyric project (v2, agent-first).
3
+ *
4
+ * pyric init [dir] [--name N] [--template web|node|static|chat] [--force] [--json]
5
+ * [--deps vendor|npm] [--pyric-version X]
6
+ *
7
+ * Scaffold templates and file writing live in `create-pyric`. This module
8
+ * adds standalone-binary vendor mode (`--deps vendor`) and `pyric vendor`.
9
+ */
10
+
11
+ import { writeFile, readFile, mkdir, access } from 'node:fs/promises';
12
+ import { join, basename, resolve } from 'node:path';
13
+ import type { ParsedArgs } from './parse-args.js';
14
+ import {
15
+ TEMPLATES,
16
+ TEMPLATE_NAMES,
17
+ applyDepsMode,
18
+ isTemplateName,
19
+ mergeIntoExistingPackageJson,
20
+ packageJsonFor,
21
+ normalizeBoolFlags as normalizeScaffoldBoolFlags,
22
+ runScaffold,
23
+ type ScaffoldTemplate,
24
+ type DepsMode,
25
+ type PackageJsonMerge,
26
+ type TemplateName,
27
+ } from 'create-pyric';
28
+ import {
29
+ isStandalone,
30
+ hasEmbeddedTarballs,
31
+ embeddedVersion,
32
+ materializeVendorTarballs,
33
+ } from '../serve/standalone-assets.js';
34
+
35
+ export type { DepsMode, ScaffoldTemplate };
36
+ export { TEMPLATES, applyDepsMode, mergeIntoExistingPackageJson };
37
+
38
+ export function resolveDepsMode(
39
+ parsed: ParsedArgs,
40
+ env: Record<string, string | undefined> = process.env,
41
+ ): DepsMode {
42
+ const flag = parsed.flags.get('deps');
43
+ if (flag === 'vendor' || flag === 'npm') return flag;
44
+ const fromEnv = env.PYRIC_INIT_DEPS;
45
+ if (fromEnv === 'vendor' || fromEnv === 'npm') return fromEnv;
46
+ return isStandalone() && hasEmbeddedTarballs() ? 'vendor' : 'npm';
47
+ }
48
+
49
+ export interface InitResult {
50
+ template: TemplateName;
51
+ dir: string;
52
+ depsMode: DepsMode;
53
+ created: string[];
54
+ merged: string[];
55
+ skipped: string[];
56
+ conflicts: Array<{ key: string; existing: unknown; wanted: unknown }>;
57
+ nextSteps: string[];
58
+ }
59
+
60
+ export interface InitDeps {
61
+ writeFile?: typeof writeFile;
62
+ readFile?: typeof readFile;
63
+ mkdir?: typeof mkdir;
64
+ exists?: (path: string) => Promise<boolean>;
65
+ cwd?: string;
66
+ stdout?: { write(s: string): void };
67
+ stderr?: { write(s: string): void };
68
+ }
69
+
70
+ async function defaultExists(path: string): Promise<boolean> {
71
+ try {
72
+ await access(path);
73
+ return true;
74
+ } catch {
75
+ return false;
76
+ }
77
+ }
78
+
79
+ function normalizeBoolFlags(parsed: ParsedArgs): void {
80
+ normalizeScaffoldBoolFlags(parsed.flags, parsed.positional);
81
+ }
82
+
83
+ export async function runInit(parsed: ParsedArgs, deps: InitDeps = {}): Promise<number> {
84
+ const err = deps.stderr ?? process.stderr;
85
+
86
+ normalizeBoolFlags(parsed);
87
+
88
+ const templateFlag = parsed.flags.get('template');
89
+ const templateName = typeof templateFlag === 'string' ? templateFlag : 'web';
90
+ if (!isTemplateName(templateName)) {
91
+ err.write(
92
+ `pyric init: unknown template '${templateName}' (expected ${TEMPLATE_NAMES.join('|')})\n`,
93
+ );
94
+ return 1;
95
+ }
96
+
97
+ const depsFlag = parsed.flags.get('deps');
98
+ if (depsFlag !== undefined && depsFlag !== 'vendor' && depsFlag !== 'npm') {
99
+ err.write(`pyric init: --deps must be 'vendor' or 'npm' (got '${String(depsFlag)}')\n`);
100
+ return 1;
101
+ }
102
+ const depsMode = resolveDepsMode(parsed);
103
+ if (depsMode === 'vendor' && !(isStandalone() && hasEmbeddedTarballs())) {
104
+ err.write(
105
+ `pyric init: --deps vendor needs the standalone binary (with embedded packages). Use --deps npm.\n`,
106
+ );
107
+ return 1;
108
+ }
109
+
110
+ const versionFlag = parsed.flags.get('pyric-version');
111
+ const pinVersion =
112
+ typeof versionFlag === 'string' && versionFlag.length > 0
113
+ ? versionFlag
114
+ : isStandalone()
115
+ ? embeddedVersion()
116
+ : null;
117
+
118
+ const cwd = deps.cwd ?? process.cwd();
119
+ const dir = parsed.positional[0] ? resolve(cwd, parsed.positional[0]) : cwd;
120
+ const nameFlag = parsed.flags.get('name');
121
+ const projectName =
122
+ typeof nameFlag === 'string' && nameFlag.length > 0 ? nameFlag : basename(dir);
123
+
124
+ const template = TEMPLATES[templateName];
125
+ let effective = template;
126
+ const preCreated: string[] = [];
127
+
128
+ if (depsMode === 'vendor') {
129
+ try {
130
+ const vendorSpecs = await materializeVendorTarballs(dir);
131
+ for (const spec of Object.values(vendorSpecs)) preCreated.push(spec.replace(/^file:/, ''));
132
+ effective = applyDepsMode(template, 'vendor', { vendorSpecs });
133
+ } catch (e) {
134
+ err.write(
135
+ `pyric init: failed to vendor packages: ${e instanceof Error ? e.message : String(e)}\n`,
136
+ );
137
+ return 2;
138
+ }
139
+ } else {
140
+ effective = applyDepsMode(template, 'npm', { version: pinVersion });
141
+ }
142
+
143
+ return runScaffold(
144
+ {
145
+ dir,
146
+ template: templateName,
147
+ name: projectName,
148
+ force: parsed.flags.get('force') === true,
149
+ json: parsed.flags.get('json') === true,
150
+ depsMode,
151
+ effectiveTemplate: effective,
152
+ pinVersion,
153
+ commandLabel: 'pyric init',
154
+ preCreated,
155
+ },
156
+ deps,
157
+ );
158
+ }
159
+
160
+ export const VENDOR_TEMPLATE: ScaffoldTemplate = {
161
+ scripts: {},
162
+ dependencies: {},
163
+ devDependencies: { '@pyric/cli': '*' },
164
+ dirs: [],
165
+ files: () => [],
166
+ nextSteps: [],
167
+ };
168
+
169
+ /**
170
+ * `pyric vendor [dir]` — lay vendored tarballs into an existing project.
171
+ * Standalone-binary only.
172
+ */
173
+ export async function runVendor(parsed: ParsedArgs, deps: InitDeps = {}): Promise<number> {
174
+ const out = deps.stdout ?? process.stdout;
175
+ const err = deps.stderr ?? process.stderr;
176
+ const cwd = deps.cwd ?? process.cwd();
177
+ const write = deps.writeFile ?? writeFile;
178
+ const read = deps.readFile ?? readFile;
179
+ const mk = deps.mkdir ?? mkdir;
180
+ const exists = deps.exists ?? defaultExists;
181
+
182
+ normalizeBoolFlags(parsed);
183
+ const json = parsed.flags.get('json') === true;
184
+ const report = json ? err : out;
185
+
186
+ if (!(isStandalone() && hasEmbeddedTarballs())) {
187
+ err.write(
188
+ 'pyric vendor: needs the standalone binary (it carries the embedded pyric / ' +
189
+ '@pyric/cli tarballs). A monorepo/npm checkout has nothing to vendor; depend ' +
190
+ 'on the packages directly instead.\n',
191
+ );
192
+ return 1;
193
+ }
194
+
195
+ const dir = parsed.positional[0] ? resolve(cwd, parsed.positional[0]) : cwd;
196
+ const projectName = basename(dir);
197
+
198
+ let vendorSpecs: Record<string, string>;
199
+ try {
200
+ await mk(dir, { recursive: true });
201
+ vendorSpecs = await materializeVendorTarballs(dir);
202
+ } catch (e) {
203
+ err.write(
204
+ `pyric vendor: failed to lay down vendor tarballs: ${e instanceof Error ? e.message : String(e)}\n`,
205
+ );
206
+ return 2;
207
+ }
208
+
209
+ const effective = applyDepsMode(VENDOR_TEMPLATE, 'vendor', { vendorSpecs });
210
+
211
+ const pkgPath = join(dir, 'package.json');
212
+ let added: string[];
213
+ let conflicts: PackageJsonMerge['conflicts'] = [];
214
+ let created = false;
215
+ try {
216
+ if (await exists(pkgPath)) {
217
+ const raw = String(await read(pkgPath, 'utf-8'));
218
+ const merge = mergeIntoExistingPackageJson(raw, projectName, effective);
219
+ conflicts = merge.conflicts;
220
+ added = merge.added;
221
+ if (!merge.unchanged) await write(pkgPath, merge.contents, 'utf-8');
222
+ } else {
223
+ await write(pkgPath, packageJsonFor(projectName, effective), 'utf-8');
224
+ added = [];
225
+ created = true;
226
+ }
227
+ } catch (e) {
228
+ err.write(
229
+ `pyric vendor: failed to update package.json: ${e instanceof Error ? e.message : String(e)}\n`,
230
+ );
231
+ return 2;
232
+ }
233
+
234
+ report.write(
235
+ `pyric vendor: vendored ${Object.keys(vendorSpecs).join(', ')} into ${join(dir, 'vendor')}\n`,
236
+ );
237
+ for (const spec of Object.values(vendorSpecs)) {
238
+ report.write(` + ${spec.replace(/^file:/, '')}\n`);
239
+ }
240
+ if (created) {
241
+ report.write(' + package.json (created)\n');
242
+ } else if (added.length === 0) {
243
+ report.write(' · package.json already had the vendored deps\n');
244
+ } else {
245
+ report.write(` ~ package.json (added: ${added.join(', ')})\n`);
246
+ }
247
+ for (const c of conflicts) {
248
+ report.write(
249
+ ` · ${c.key}: kept ${JSON.stringify(c.existing)} (pyric would set ${JSON.stringify(c.wanted)})\n`,
250
+ );
251
+ }
252
+ report.write('\n next: run `bun install` (or npm/pnpm install) to install the vendored packages.\n');
253
+
254
+ if (json) {
255
+ out.write(
256
+ JSON.stringify({ dir, vendored: Object.keys(vendorSpecs), added, conflicts, created }) + '\n',
257
+ );
258
+ }
259
+ return 0;
260
+ }
@@ -0,0 +1,196 @@
1
+ /**
2
+ * `pyric mcp-proxy` — a stdio MCP server that relays to a RUNNING
3
+ * `pyric dev --bridge`'s HTTP MCP endpoint.
4
+ *
5
+ * Why this exists: a Claude Code plugin can only declare a STATIC MCP URL in
6
+ * `.mcp.json`, but serve's endpoint is `http://localhost:<PORT>/__pyric/mcp`
7
+ * with a runtime port (scan-forward / AirPlay). So the plugin declares a
8
+ * STDIO server that runs this proxy; the proxy discovers the live serve and
9
+ * forwards the protocol. No manual `claude mcp add`, no fixed port.
10
+ * (design rationale)
11
+ *
12
+ * Relay is at the TRANSPORT level via the MCP SDK's own transports — we do
13
+ * NOT re-implement JSON-RPC/SSE/session framing. `StdioServerTransport`
14
+ * talks to Claude Code; `StreamableHTTPClientTransport` talks to serve; each
15
+ * transport's `onmessage` is piped to the other's `send`.
16
+ *
17
+ * Two hardening layers sit on top of that pipe:
18
+ *
19
+ * IDENTITY — the discovery pointer records the bridge's `instanceId`; the
20
+ * proxy accepts a server only if its `/health` reports the SAME id. Two
21
+ * sandboxes can collide on one port across loopback families (IPv4 `*:P`
22
+ * + IPv6 `[::1]:P`); without this, the proxy locks onto whichever family
23
+ * answers first while the browser is on the other — split-brain.
24
+ * TIMEOUT — every relayed request is failed with a JSON-RPC error after
25
+ * REQUEST_TIMEOUT_MS instead of hanging Claude Code forever (a killed
26
+ * server can leave a half-open keep-alive socket that never FINs). A
27
+ * `settled` set swallows a late real response so the client never sees a
28
+ * duplicate after the timeout already failed the call.
29
+ *
30
+ * (Auto-reconnect on serve restart is deliberately NOT here — it needs a
31
+ * live-restart test harness; the timeout makes a stale connection fail fast,
32
+ * and the user restarts the MCP connection.)
33
+ *
34
+ * Discovery preference: the `.pyric/serve.json` pointer serve writes in the
35
+ * project cwd (exact + project-correct), then a health probe across the scan
36
+ * window as a fallback. Degrades LEGIBLY: if no serve is found, or the pointed
37
+ * server's identity can't be matched, we report it — never a silent hang.
38
+ */
39
+ import type { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js';
40
+ import type { ParsedArgs } from './parse-args.js';
41
+ import { discoverServe, SCAN_PORTS } from '../serve/discovery.js';
42
+
43
+ // Discovery (pointer + identity-pinned health probing) lives in
44
+ // `serve/discovery.ts` — shared with the Node remote-sandbox client
45
+ // (`remote/index.ts`). Re-exported here for existing consumers.
46
+ export { discoverServe } from '../serve/discovery.js';
47
+
48
+ /** A relayed request with no response within this window is failed with a
49
+ * JSON-RPC error rather than hanging forever. Sits just above the bridge's
50
+ * own 30s `callTimeoutMs` so a legitimately-slow tool still completes. */
51
+ const REQUEST_TIMEOUT_MS = 35_000;
52
+ /** JSON-RPC error code for proxy-synthesized failures (server-defined range). */
53
+ const PROXY_ERROR_CODE = -32001;
54
+
55
+ // ── JSON-RPC shape helpers (relay-level; no SDK runtime import) ──────────────
56
+ function msgId(m: JSONRPCMessage): string | number | null {
57
+ return 'id' in m && (m as { id?: unknown }).id != null ? (m as { id: string | number }).id : null;
58
+ }
59
+ /** A request expects a response (has both `id` and `method`). */
60
+ function isRequest(m: JSONRPCMessage): boolean {
61
+ return 'method' in m && msgId(m) != null;
62
+ }
63
+ /** A response/error answers a request (has `id`, no `method`). */
64
+ function isResponse(m: JSONRPCMessage): boolean {
65
+ return !('method' in m) && msgId(m) != null;
66
+ }
67
+
68
+ /** Injectable seams for testing the attach-vs-headless selection. */
69
+ export interface McpProxyDeps {
70
+ discover?: typeof discoverServe;
71
+ headless?: (cwd: string) => Promise<number>;
72
+ }
73
+
74
+ export async function runMcpProxy(
75
+ _parsed: ParsedArgs,
76
+ cwd: string = process.cwd(),
77
+ deps: McpProxyDeps = {},
78
+ ): Promise<number> {
79
+ // NB: stdout is the MCP stdio channel — diagnostics go to stderr ONLY.
80
+ const log = (m: string): void => {
81
+ process.stderr.write(`[pyric mcp-proxy] ${m}\n`);
82
+ };
83
+
84
+ const found = await (deps.discover ?? discoverServe)(cwd, log);
85
+ if (!found) {
86
+ // Hybrid mode (design rationale): no dev server to attach to, so host the
87
+ // sandbox IN this process. Zero setup, no browser tab required. A running
88
+ // `pyric dev --bridge` upgrades to the shared-live experience on reconnect.
89
+ log(
90
+ 'no running `pyric dev --bridge` found (looked for .pyric/serve.json and ports ' +
91
+ `${SCAN_PORTS.join(', ')}); starting a headless in-process sandbox (zero-setup).\n` +
92
+ ' Data persists to .pyric/state/headless.json. For a shared-live Studio\n' +
93
+ ' session, start `pyric dev --bridge` BEFORE connecting the agent (a dev server\n' +
94
+ ' started mid-session does not yet adopt this headless data).',
95
+ );
96
+ const headless =
97
+ deps.headless ??
98
+ ((c: string) => import('../bridge/server/headless.js').then((m) => m.runHeadlessMcp(c)));
99
+ return await headless(cwd);
100
+ }
101
+ log(`relaying stdio ↔ ${found.mcpUrl} (via ${found.source}; attached to a running serve)`);
102
+
103
+ // Late, dynamic imports: the SDK is heavy and only needed here.
104
+ const { StdioServerTransport } = await import('@modelcontextprotocol/sdk/server/stdio.js');
105
+ const { StreamableHTTPClientTransport } = await import(
106
+ '@modelcontextprotocol/sdk/client/streamableHttp.js'
107
+ );
108
+
109
+ const http = new StreamableHTTPClientTransport(new URL(found.mcpUrl));
110
+ const stdio = new StdioServerTransport();
111
+
112
+ return await new Promise<number>((resolveExit) => {
113
+ let closing = false;
114
+ /** In-flight relayed requests → their timeout timers. */
115
+ const pending = new Map<string | number, ReturnType<typeof setTimeout>>();
116
+ /** Ids already failed by a timeout — used to SWALLOW a late real response so
117
+ * Claude Code never sees a duplicate (error frame, then result frame). */
118
+ const settled = new Set<string | number>();
119
+
120
+ const sendStdio = (m: JSONRPCMessage): void => {
121
+ void stdio.send(m).catch((e) => log(`→client send failed: ${e}`));
122
+ };
123
+ const failRequest = (id: string | number, message: string): void => {
124
+ const timer = pending.get(id);
125
+ if (timer) clearTimeout(timer);
126
+ pending.delete(id);
127
+ settled.add(id);
128
+ sendStdio({ jsonrpc: '2.0', id, error: { code: PROXY_ERROR_CODE, message } } as JSONRPCMessage);
129
+ };
130
+
131
+ const shutdown = (code: number): void => {
132
+ if (closing) return; // re-entrancy guard: close() fires onclose → shutdown → …
133
+ closing = true;
134
+ process.stdin.off('end', onStdinEnd);
135
+ for (const t of pending.values()) clearTimeout(t);
136
+ pending.clear();
137
+ void Promise.allSettled([stdio.close(), http.close()]).then(() => resolveExit(code));
138
+ };
139
+
140
+ // ── relay: stdio (Claude Code) → http (serve) ──
141
+ stdio.onmessage = (msg) => {
142
+ if (isRequest(msg)) {
143
+ const id = msgId(msg)!;
144
+ const timer = setTimeout(() => {
145
+ failRequest(
146
+ id,
147
+ `pyric mcp-proxy: no response from serve within ${REQUEST_TIMEOUT_MS / 1000}s ` +
148
+ `(the server may have stopped, or no browser tab is connected). ` +
149
+ `Reopen the dev URL and retry.`,
150
+ );
151
+ }, REQUEST_TIMEOUT_MS);
152
+ pending.set(id, timer);
153
+ }
154
+ void http.send(msg).catch((e) => {
155
+ log(`→serve send failed: ${e}`);
156
+ const id = msgId(msg);
157
+ if (id != null && pending.has(id)) failRequest(id, 'pyric mcp-proxy: failed to reach serve — retry.');
158
+ });
159
+ };
160
+
161
+ // ── relay: http (serve) → stdio (Claude Code) ──
162
+ http.onmessage = (msg) => {
163
+ if (isResponse(msg)) {
164
+ const id = msgId(msg)!;
165
+ const timer = pending.get(id);
166
+ if (timer) {
167
+ clearTimeout(timer);
168
+ pending.delete(id);
169
+ } else if (settled.has(id)) {
170
+ // A response that lost the race to its timeout — already failed to the
171
+ // client. Swallow it so the client never sees a duplicate frame.
172
+ settled.delete(id);
173
+ return;
174
+ }
175
+ }
176
+ sendStdio(msg);
177
+ };
178
+
179
+ http.onerror = (e) => log(`serve transport error: ${e instanceof Error ? e.message : String(e)}`);
180
+ http.onclose = () => {
181
+ log('serve closed the connection (did serve stop?)');
182
+ shutdown(0);
183
+ };
184
+ stdio.onclose = () => shutdown(0); // Claude Code disconnected
185
+ const onStdinEnd = (): void => shutdown(0);
186
+ process.stdin.once('end', onStdinEnd);
187
+
188
+ process.once('SIGINT', () => shutdown(0));
189
+ process.once('SIGTERM', () => shutdown(0));
190
+
191
+ Promise.all([http.start(), stdio.start()]).catch((e) => {
192
+ log(`failed to start relay: ${e instanceof Error ? e.message : String(e)}`);
193
+ shutdown(1);
194
+ });
195
+ });
196
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Minimal CLI argument parser shared across subcommands.
3
+ *
4
+ * Intentionally tiny — no `commander` / `yargs` dependency. The parser
5
+ * is biased toward Firebase-style flag syntax (`--flag value` and
6
+ * `--flag=value`); short flags are single-char booleans. Anything
7
+ * beyond a "command + positionals + flags" model belongs in a
8
+ * dedicated subcommand handler, not here.
9
+ */
10
+
11
+ export interface ParsedArgs {
12
+ subcommand: string | null;
13
+ flags: Map<string, FlagValue>;
14
+ positional: string[];
15
+ /** Everything after a bare `--`, verbatim (e.g. `pyric dev -- npm start`).
16
+ * Optional so hand-built ParsedArgs literals stay valid. */
17
+ passthrough?: string[];
18
+ }
19
+
20
+ export type FlagValue = string | boolean | Array<string | boolean>;
21
+
22
+ // Flags whose command contract is boolean must not consume a following
23
+ // positional. Value-bearing flags retain the Firebase-style `--flag value`
24
+ // form, while explicit boolean values remain available as `--json=true`.
25
+ const BOOLEAN_FLAGS = new Set(['json']);
26
+
27
+ export function parseArgs(argv: string[]): ParsedArgs {
28
+ const flags = new Map<string, FlagValue>();
29
+ const positional: string[] = [];
30
+ const passthrough: string[] = [];
31
+ let subcommand: string | null = null;
32
+ let i = 0;
33
+ while (i < argv.length) {
34
+ const arg = argv[i];
35
+ if (arg === undefined) {
36
+ i += 1;
37
+ continue;
38
+ }
39
+ if (arg === '--') {
40
+ // Everything after a bare `--` belongs to the child command, verbatim
41
+ // — never parsed as flags (`pyric dev -- npm start --port 3000`).
42
+ passthrough.push(...argv.slice(i + 1).filter((a): a is string => a !== undefined));
43
+ break;
44
+ }
45
+ if (arg.startsWith('--')) {
46
+ const eq = arg.indexOf('=');
47
+ if (eq !== -1) {
48
+ setFlag(flags, arg.slice(2, eq), arg.slice(eq + 1));
49
+ } else {
50
+ const key = arg.slice(2);
51
+ const next = argv[i + 1];
52
+ if (!BOOLEAN_FLAGS.has(key) && next && !next.startsWith('-')) {
53
+ setFlag(flags, key, next);
54
+ i += 1;
55
+ } else {
56
+ setFlag(flags, key, true);
57
+ }
58
+ }
59
+ } else if (arg.startsWith('-')) {
60
+ setFlag(flags, arg.slice(1), true);
61
+ } else if (subcommand === null) {
62
+ subcommand = arg;
63
+ } else {
64
+ positional.push(arg);
65
+ }
66
+ i += 1;
67
+ }
68
+ return { subcommand, flags, positional, passthrough };
69
+ }
70
+
71
+ function setFlag(flags: Map<string, FlagValue>, key: string, value: string | boolean): void {
72
+ const current = flags.get(key);
73
+ if (current === undefined) {
74
+ flags.set(key, value);
75
+ } else if (Array.isArray(current)) {
76
+ current.push(value);
77
+ } else {
78
+ flags.set(key, [current, value]);
79
+ }
80
+ }