@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,160 @@
1
+ /**
2
+ * Headless MCP server: runs the pyric sandbox IN this process (no browser, no
3
+ * serve) and exposes the SAME tool surface the bridge advertises, over stdio.
4
+ *
5
+ * This is the zero-setup half of the hybrid MCP server
6
+ * (design rationale): when no `pyric dev --bridge` is running to
7
+ * attach to, the MCP server hosts its own sandbox. The tool surface is identical
8
+ * to the served bridge by construction (`buildSandboxDispatcher` is the shared
9
+ * source pinned by `tool-parity.test.ts`), including the per-identity `as` arg.
10
+ *
11
+ * Persistence (Phase 1b) uses the v3 bundle codec the worker already uses for
12
+ * transfer/branches (`serializeToBuckets` + `bundleRecords`). It writes its OWN
13
+ * `.pyric/state/headless.json`, NOT serve's `state.json` (which currently uses a
14
+ * different on-disk envelope, and carries a controller-blob version that has
15
+ * drifted from pyric's snapshot SCHEMA_VERSION). Unifying the two formats so a
16
+ * headless session and a `pyric dev` session can share one file is a tracked
17
+ * design item, not done here.
18
+ */
19
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, renameSync } from 'node:fs';
20
+ import { join, dirname } from 'node:path';
21
+ import {
22
+ initializeSandbox,
23
+ serializeToBuckets,
24
+ bundleRecords,
25
+ parseBundle,
26
+ deserializeFromBuckets,
27
+ type LocalSandbox,
28
+ } from 'pyric/sandbox';
29
+ import { getFirestore } from 'pyric/firestore';
30
+ import { setRules } from 'pyric/sandbox/firestore';
31
+ import { buildMcpServer } from './mcp.js';
32
+ import { getDefaultMcpToolSurface } from './mcp-contract.js';
33
+ import { createLocalBridge, type LocalBridgeOptions } from './local-bridge.js';
34
+
35
+ /** Where the headless sandbox snapshot is persisted (relative to the project
36
+ * dir). Deliberately separate from serve's `state.json` (different format). */
37
+ export const HEADLESS_STATE_RELATIVE = join('.pyric', 'state', 'headless.json');
38
+
39
+ /**
40
+ * Build the headless MCP server around an in-process sandbox. Pure: no I/O and
41
+ * no transport, so callers (and tests) can drive it however they like. Mirrors
42
+ * the served bridge's construction (forwarded data-plane + in-process rules
43
+ * tools), with `dispatch` bound to the local sandbox instead of a ws peer.
44
+ */
45
+ export function buildHeadlessMcpServer(sandbox: LocalSandbox, opts?: LocalBridgeOptions) {
46
+ const bridge = createLocalBridge(sandbox, opts);
47
+ return buildMcpServer(bridge, getDefaultMcpToolSurface());
48
+ }
49
+
50
+ /**
51
+ * Load `<cwd>/firestore.rules` into the sandbox when present. Returns the path
52
+ * it loaded, or null if there was no rules file. Without rules, rules-enforcing
53
+ * (`as:{uid}`) ops fall back to the sandbox's default; admin ops are unaffected.
54
+ */
55
+ export function loadProjectRules(sandbox: LocalSandbox, cwd: string): string | null {
56
+ const rulesPath = join(cwd, 'firestore.rules');
57
+ if (!existsSync(rulesPath)) return null;
58
+ setRules(sandbox, readFileSync(rulesPath, 'utf8'));
59
+ return rulesPath;
60
+ }
61
+
62
+ /**
63
+ * Persist the sandbox to `<cwd>/.pyric/state/headless.json` using the v3 bundle
64
+ * codec (the same `serializeToBuckets` + `bundleRecords` the worker uses). Atomic
65
+ * tmp+rename so a crash mid-write never truncates the live file.
66
+ */
67
+ export function saveSandboxSnapshot(sandbox: LocalSandbox, cwd: string): void {
68
+ const snap = sandbox.snapshot();
69
+ const bundle = bundleRecords(serializeToBuckets(snap.firestore, snap.services, 0));
70
+ const path = join(cwd, HEADLESS_STATE_RELATIVE);
71
+ mkdirSync(dirname(path), { recursive: true });
72
+ const tmp = `${path}.tmp-${process.pid}`;
73
+ writeFileSync(tmp, bundle, 'utf8');
74
+ renameSync(tmp, path);
75
+ }
76
+
77
+ /**
78
+ * Restore the sandbox from the headless snapshot file if present (a clobber via
79
+ * `loadSnapshot`). Returns the restored doc count, or null when there is no file.
80
+ */
81
+ export function loadSandboxSnapshot(sandbox: LocalSandbox, cwd: string): number | null {
82
+ const path = join(cwd, HEADLESS_STATE_RELATIVE);
83
+ if (!existsSync(path)) return null;
84
+ const snap = deserializeFromBuckets(parseBundle(readFileSync(path, 'utf8')));
85
+ sandbox.loadSnapshot(snap);
86
+ return Object.keys(snap.firestore).length;
87
+ }
88
+
89
+ /**
90
+ * Run the headless MCP server over stdio. Loads `.pyric/state/headless.json` on
91
+ * start, debounces a save after each dispatch, and flushes on shutdown. Resolves
92
+ * with an exit code when the stdio transport closes (the editor disconnects).
93
+ */
94
+ export async function runHeadlessMcp(cwd: string = process.cwd()): Promise<number> {
95
+ const log = (m: string): void => {
96
+ process.stderr.write(`[pyric mcp headless] ${m}\n`);
97
+ };
98
+
99
+ const sandbox = initializeSandbox();
100
+ const rulesPath = loadProjectRules(sandbox, cwd);
101
+ log(rulesPath ? `rules loaded from ${rulesPath}` : `no firestore.rules found in ${cwd}`);
102
+
103
+ const restored = loadSandboxSnapshot(sandbox, cwd);
104
+ if (restored !== null) log(`restored ${restored} docs from ${join(cwd, HEADLESS_STATE_RELATIVE)}`);
105
+
106
+ // Debounced persistence: a burst of writes collapses to one flush; the final
107
+ // flush runs on shutdown so a clean disconnect never loses the tail.
108
+ let saveTimer: ReturnType<typeof setTimeout> | null = null;
109
+ const flush = (): void => {
110
+ if (saveTimer) {
111
+ clearTimeout(saveTimer);
112
+ saveTimer = null;
113
+ }
114
+ try {
115
+ saveSandboxSnapshot(sandbox, cwd);
116
+ } catch (e) {
117
+ log(`persist failed: ${e instanceof Error ? e.message : String(e)}`);
118
+ }
119
+ };
120
+ const scheduleSave = (): void => {
121
+ if (saveTimer) clearTimeout(saveTimer);
122
+ saveTimer = setTimeout(flush, 750);
123
+ };
124
+
125
+ const server = buildHeadlessMcpServer(sandbox, { onAfterDispatch: scheduleSave });
126
+ const { StdioServerTransport } = await import('@modelcontextprotocol/sdk/server/stdio.js');
127
+ const transport = new StdioServerTransport();
128
+
129
+ return await new Promise<number>((resolve) => {
130
+ let stopping = false;
131
+ const onStdinEnd = (): void => stop(0);
132
+ const stop = (code: number): void => {
133
+ if (stopping) return;
134
+ stopping = true;
135
+ process.stdin.off('end', onStdinEnd);
136
+ flush();
137
+ void server.close().then(
138
+ () => resolve(code),
139
+ (e) => {
140
+ log(`shutdown failed: ${e instanceof Error ? e.message : String(e)}`);
141
+ resolve(code === 0 ? 1 : code);
142
+ },
143
+ );
144
+ };
145
+ transport.onclose = () => stop(0);
146
+ // StdioServerTransport 1.29 no longer reports stdin EOF through onclose.
147
+ // Editors close the pipe to end an MCP session, so own that lifecycle
148
+ // signal explicitly and then close the server/transport above.
149
+ process.stdin.once('end', onStdinEnd);
150
+ process.once('SIGINT', () => stop(0));
151
+ process.once('SIGTERM', () => stop(0));
152
+ void server.connect(transport).then(
153
+ () => log(`headless sandbox MCP server ready (persisting to ${HEADLESS_STATE_RELATIVE})`),
154
+ (e) => {
155
+ log(`failed to start: ${e instanceof Error ? e.message : String(e)}`);
156
+ resolve(1);
157
+ },
158
+ );
159
+ });
160
+ }
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Tiny JSON-Schema → Zod shape converter. Handles only the shapes
3
+ * pyric's tool factories actually produce (string, number, boolean,
4
+ * object, array, enum, plus untyped catch-all). Anything outside this
5
+ * grammar becomes `z.any()` — better to under-validate than refuse to
6
+ * register a tool.
7
+ *
8
+ * The MCP SDK's `server.tool()` / `server.registerTool()` insist on
9
+ * Zod schemas for input validation. Pyric's tool definitions are
10
+ * plain JSON Schema (matching the inline shape in
11
+ * `packages/pyric/src/rules/tools.ts`), so the bridge converts
12
+ * just-in-time when registering each tool with the MCP server.
13
+ */
14
+
15
+ import { z, type ZodRawShape, type ZodTypeAny } from 'zod';
16
+
17
+ type JsonSchemaObject = {
18
+ type?: string | string[];
19
+ description?: string;
20
+ properties?: Record<string, JsonSchemaObject>;
21
+ items?: JsonSchemaObject;
22
+ required?: string[];
23
+ enum?: unknown[];
24
+ additionalProperties?: boolean | JsonSchemaObject;
25
+ [k: string]: unknown;
26
+ };
27
+
28
+ /**
29
+ * Convert a JSON-Schema-of-an-object into a Zod raw shape suitable
30
+ * for passing to `server.tool(name, description, shape, handler)`.
31
+ *
32
+ * Behaviour:
33
+ * - Empty / no `properties` returns `{}` (valid for parameterless tools).
34
+ * - Each property becomes a Zod schema chosen from its `type`.
35
+ * - Properties NOT in `required` are made `.optional()`.
36
+ * - Description metadata is attached via `.describe()` for MCP surfacing.
37
+ */
38
+ export function jsonSchemaToZodShape(schema: JsonSchemaObject): ZodRawShape {
39
+ const properties = schema.properties ?? {};
40
+ const required = new Set(schema.required ?? []);
41
+ const shape: ZodRawShape = {};
42
+ for (const [key, value] of Object.entries(properties)) {
43
+ let zodSchema = jsonSchemaToZod(value);
44
+ if (value.description) {
45
+ zodSchema = zodSchema.describe(value.description);
46
+ }
47
+ if (!required.has(key)) {
48
+ zodSchema = zodSchema.optional();
49
+ }
50
+ shape[key] = zodSchema;
51
+ }
52
+ return shape;
53
+ }
54
+
55
+ function jsonSchemaToZod(schema: JsonSchemaObject): ZodTypeAny {
56
+ // Enum (irrespective of type)
57
+ if (Array.isArray(schema.enum) && schema.enum.length > 0) {
58
+ const values = schema.enum.filter((v): v is string => typeof v === 'string');
59
+ if (values.length === schema.enum.length && values.length > 0) {
60
+ return z.enum(values as [string, ...string[]]);
61
+ }
62
+ return z.union(schema.enum.map((v) => z.literal(v as never)) as never);
63
+ }
64
+
65
+ // oneOf / anyOf — a union of sub-schemas (e.g. `as: 'admin' | { uid, … }`).
66
+ // Without this, a schema with no top-level `type` falls through to z.any(),
67
+ // silently dropping the contract the agent is supposed to see.
68
+ const variants = (schema.oneOf ?? schema.anyOf) as JsonSchemaObject[] | undefined;
69
+ if (Array.isArray(variants) && variants.length > 0) {
70
+ const members = variants.map((v) => jsonSchemaToZod(v));
71
+ return members.length === 1
72
+ ? members[0]
73
+ : z.union(members as [ZodTypeAny, ZodTypeAny, ...ZodTypeAny[]]);
74
+ }
75
+
76
+ const type = Array.isArray(schema.type) ? schema.type[0] : schema.type;
77
+ switch (type) {
78
+ case 'string':
79
+ return z.string();
80
+ case 'number':
81
+ case 'integer':
82
+ return z.number();
83
+ case 'boolean':
84
+ return z.boolean();
85
+ case 'null':
86
+ return z.null();
87
+ case 'array': {
88
+ const items = schema.items
89
+ ? jsonSchemaToZod(schema.items)
90
+ : z.any();
91
+ return z.array(items);
92
+ }
93
+ case 'object': {
94
+ if (!schema.properties || Object.keys(schema.properties).length === 0) {
95
+ // Open object — pyric tools commonly use `additionalProperties: true`
96
+ // here to mean "free-form payload" (e.g. `data` for a Firestore doc).
97
+ return z.record(z.any());
98
+ }
99
+ const inner = jsonSchemaToZodShape(schema);
100
+ return z.object(inner);
101
+ }
102
+ default:
103
+ // Untyped (e.g. the `auth` field that pyric tools accept as
104
+ // `null | { uid, token? }` without a `type` declaration) —
105
+ // accept anything.
106
+ return z.any();
107
+ }
108
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * In-process ("local") Bridge for the headless MCP server.
3
+ *
4
+ * The serve / standalone bridges forward data-plane tool calls over a WebSocket
5
+ * to the in-page sandbox. The headless server has no page: the sandbox runs IN
6
+ * this process, so the bridge's `dispatch` executes tools directly through
7
+ * `buildSandboxDispatcher`. Everything else the `Bridge` contract needs is
8
+ * trivial for a peerless bridge (always "connected", with the tool set from
9
+ * the dispatcher's `SANDBOX_TOOL_NAMES`).
10
+ *
11
+ * Because `buildSandboxDispatcher` is the SAME source the served bridge
12
+ * advertises (pinned by `tool-parity.test.ts`), the headless tool surface is
13
+ * identical to the served one, including the per-identity `as` arg.
14
+ *
15
+ * See design rationale (headless mode).
16
+ */
17
+ import type { LocalSandbox } from 'pyric/sandbox';
18
+ import { buildSandboxDispatcher, SANDBOX_TOOL_NAMES } from '../client/dispatch.js';
19
+ import { createBridge, type Bridge } from './bridge.js';
20
+ import { pyricVersion } from '../../serve/standalone-assets.js';
21
+
22
+ export interface LocalBridgeOptions {
23
+ /** Reported as the MCP server version. */
24
+ version?: string;
25
+ /** Project label surfaced in tool-result metadata. */
26
+ project?: string;
27
+ /** Called after each dispatch (success or failure). The headless runner uses
28
+ * this to schedule a debounced persistence flush so writes survive a restart. */
29
+ onAfterDispatch?: () => void;
30
+ }
31
+
32
+ /**
33
+ * A {@link Bridge} backed by an in-process {@link Sandbox}. `dispatch` runs tools
34
+ * locally; the peer-management members are no-ops (there is no remote peer).
35
+ */
36
+ export function createLocalBridge(sandbox: LocalSandbox, opts: LocalBridgeOptions = {}): Bridge {
37
+ const dispatcher = buildSandboxDispatcher(sandbox);
38
+ // Reuse the real bridge for the boilerplate the contract needs (startedAt,
39
+ // instanceId, health, recordToolEvent, the peer machinery), then override the
40
+ // three members that differ when the sandbox is in-process rather than a ws
41
+ // peer. The peer machinery stays idle because `dispatch` never touches it.
42
+ const base = createBridge({
43
+ project: opts.project ?? 'sandbox',
44
+ version: opts.version ?? pyricVersion(),
45
+ });
46
+ return {
47
+ ...base,
48
+ // The sandbox is in-process: always "connected", and the tool set is the
49
+ // dispatcher's, not a (nonexistent) peer's hello.
50
+ isSandboxConnected: () => true,
51
+ toolNames: () => [...SANDBOX_TOOL_NAMES],
52
+ // Execute tools locally instead of forwarding to a ws peer. The bridge
53
+ // contract RETURNS a result (ok:false on failure) rather than rejecting, and
54
+ // `buildSandboxDispatcher` throws on a tool error (e.g. a rules denial), so
55
+ // translate that here.
56
+ async dispatch(name, args) {
57
+ try {
58
+ return await dispatcher(name, args);
59
+ } catch (e) {
60
+ return { ok: false, summary: e instanceof Error ? e.message : String(e) };
61
+ } finally {
62
+ opts.onAfterDispatch?.();
63
+ }
64
+ },
65
+ };
66
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Tiny logger for bridge lifecycle events. The bridge core stays
3
+ * silent; consumers (standalone CLI, Vite plugin) wire a logger
4
+ * here when they want visibility.
5
+ *
6
+ * Premortem #U1 + #I7 + #U2 + #U3 all stemmed from "we never
7
+ * print what's happening." This module is the single observability
8
+ * surface so adding a log line at the bridge happens once, here.
9
+ *
10
+ * Levels: `info` for lifecycle (start, peer connect/disconnect),
11
+ * `verbose` for per-call (only emitted when verbose=true).
12
+ */
13
+
14
+ export interface BridgeLogger {
15
+ info(line: string): void;
16
+ verbose(line: string): void;
17
+ error(line: string): void;
18
+ }
19
+
20
+ /**
21
+ * Default logger that writes to stderr with a `[pyric]` prefix.
22
+ * Verbose lines are gated behind `PYRIC_VERBOSE=1` env var unless
23
+ * the option overrides it.
24
+ */
25
+ export function createConsoleLogger(options: { verbose?: boolean } = {}): BridgeLogger {
26
+ const verboseEnabled =
27
+ options.verbose ?? Boolean(process.env.PYRIC_VERBOSE);
28
+ return {
29
+ info(line) {
30
+ process.stderr.write(`[pyric] ${line}\n`);
31
+ },
32
+ verbose(line) {
33
+ if (verboseEnabled) {
34
+ process.stderr.write(`[pyric] ${line}\n`);
35
+ }
36
+ },
37
+ error(line) {
38
+ process.stderr.write(`[pyric] error: ${line}\n`);
39
+ },
40
+ };
41
+ }
42
+
43
+ /** Silent logger — useful in tests. */
44
+ export function createSilentLogger(): BridgeLogger {
45
+ return {
46
+ info() {},
47
+ verbose() {},
48
+ error() {},
49
+ };
50
+ }
@@ -0,0 +1,93 @@
1
+ import type { ToolHandler } from '@inbrowser/agent';
2
+ import {
3
+ getInProcessToolHandlers,
4
+ getSandboxToolMetadata,
5
+ type ToolMetadata,
6
+ } from './tool-metadata.js';
7
+
8
+ /**
9
+ * The exact tools a default local Pyric MCP server forwards to its sandbox.
10
+ * A tool addition or removal is a public contract change and must update this
11
+ * manifest deliberately.
12
+ */
13
+ export const DEFAULT_MCP_FORWARDED_TOOL_NAMES = [
14
+ 'firestore_simulator_create',
15
+ 'firestore_simulator_execute',
16
+ 'firestore_simulator_read',
17
+ 'firestore_simulator_batch',
18
+ 'firestore_create_with_auto_id',
19
+ 'firestore_simulator_undo',
20
+ 'firestore_simulator_redo',
21
+ 'firestore_simulator_events',
22
+ 'firestore_simulator_transaction',
23
+ 'firestore_get_document',
24
+ 'firestore_list_documents',
25
+ 'firestore_create_document',
26
+ 'firestore_add_document',
27
+ 'firestore_update_document',
28
+ 'firestore_delete_document',
29
+ 'firestore_batch_write',
30
+ 'firestore_query_where',
31
+ 'sandbox_inspect',
32
+ 'rtdb_simulate_access',
33
+ 'rtdb_crawl_structure',
34
+ ] as const;
35
+
36
+ /** Local rules tools that run in the MCP process without a browser peer. */
37
+ export const DEFAULT_MCP_IN_PROCESS_TOOL_NAMES = [
38
+ 'firestore_simulate_rules',
39
+ 'firestore_rules_stdlib_list',
40
+ 'firestore_rules_stdlib_get',
41
+ 'firestore_lint_rules',
42
+ 'firestore_resolve_modules',
43
+ 'pyric_can_i_use',
44
+ ] as const;
45
+
46
+ export const DEFAULT_MCP_TOOL_NAMES = [
47
+ ...DEFAULT_MCP_FORWARDED_TOOL_NAMES,
48
+ ...DEFAULT_MCP_IN_PROCESS_TOOL_NAMES,
49
+ ] as const;
50
+
51
+ export interface DefaultMcpToolSurface {
52
+ forwarded: ToolMetadata[];
53
+ inProcess: ToolHandler[];
54
+ }
55
+
56
+ function assertExactNames(
57
+ label: string,
58
+ actual: readonly string[],
59
+ expected: readonly string[],
60
+ ): void {
61
+ const actualSorted = [...actual].sort();
62
+ const expectedSorted = [...expected].sort();
63
+ if (
64
+ actualSorted.length !== expectedSorted.length ||
65
+ actualSorted.some((name, index) => name !== expectedSorted[index])
66
+ ) {
67
+ throw new Error(
68
+ `${label} drifted from the default MCP contract\n` +
69
+ `expected: ${expected.join(', ')}\n` +
70
+ `actual: ${actual.join(', ')}`,
71
+ );
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Assemble the default MCP surface and fail closed if a factory changed
77
+ * without a corresponding public-contract decision.
78
+ */
79
+ export function getDefaultMcpToolSurface(): DefaultMcpToolSurface {
80
+ const forwarded = getSandboxToolMetadata();
81
+ const inProcess = getInProcessToolHandlers();
82
+ assertExactNames(
83
+ 'forwarded sandbox tools',
84
+ forwarded.map((tool) => tool.name),
85
+ DEFAULT_MCP_FORWARDED_TOOL_NAMES,
86
+ );
87
+ assertExactNames(
88
+ 'in-process tools',
89
+ inProcess.map((tool) => tool.name),
90
+ DEFAULT_MCP_IN_PROCESS_TOOL_NAMES,
91
+ );
92
+ return { forwarded, inProcess };
93
+ }
@@ -0,0 +1,129 @@
1
+ /**
2
+ * MCP server assembly. Constructs an `@modelcontextprotocol/sdk`
3
+ * `McpServer` whose tools dispatch into the supplied bridge for
4
+ * forwarded calls plus in-process sandbox verification handlers.
5
+ *
6
+ * Each tool's MCP handler returns the bridge's `BridgeToolResult`
7
+ * serialised as a single MCP text content block — a stable shape so
8
+ * skill prompts work without adjustment.
9
+ */
10
+
11
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
12
+ import type { ToolHandler } from '@inbrowser/agent';
13
+ import type { Bridge, BridgeToolResult } from './bridge.js';
14
+ import type { ToolMetadata } from './tool-metadata.js';
15
+ import { jsonSchemaToZodShape } from './json-schema-to-zod.js';
16
+
17
+ export interface RegisterToolsOptions {
18
+ /** Metadata for tools whose dispatch goes to the bridge peer. */
19
+ forwarded: ToolMetadata[];
20
+ /** Live handlers for tools that execute in-process on the bridge. */
21
+ inProcess: ToolHandler[];
22
+ }
23
+
24
+ function toMcpResult(result: BridgeToolResult, project: string) {
25
+ // Wrap the bridge's tool result in MCP's content envelope. Include
26
+ // sandbox provenance + project label in a metadata block so the calling
27
+ // agent can always tell which target the tool hit.
28
+ const body = {
29
+ ...result,
30
+ _pyric: { mode: 'sandbox', project },
31
+ };
32
+ return {
33
+ content: [
34
+ {
35
+ type: 'text' as const,
36
+ text: JSON.stringify(body, null, 2),
37
+ },
38
+ ],
39
+ isError: !result.ok,
40
+ };
41
+ }
42
+
43
+ /**
44
+ * Build an MCP server seeded with the supplied tool surface. Caller
45
+ * connects the returned server to a transport (e.g.
46
+ * StreamableHTTPServerTransport).
47
+ */
48
+ export function buildMcpServer(
49
+ bridge: Bridge,
50
+ options: RegisterToolsOptions,
51
+ ): McpServer {
52
+ const server = new McpServer({
53
+ name: 'pyric',
54
+ version: bridge.version,
55
+ });
56
+
57
+ // Forwarded tools: bridge.dispatch handles the wire round-trip.
58
+ for (const meta of options.forwarded) {
59
+ const shape = jsonSchemaToZodShape(meta.parameters as never);
60
+ // The MCP SDK's `tool()` overloads have a deep generic tree
61
+ // that trips `Type instantiation is excessively deep` at the call
62
+ // site for the variadic shape argument. The cast collapses the
63
+ // inference; the runtime contract is the same.
64
+ (server.tool as unknown as Function)(
65
+ meta.name,
66
+ meta.description,
67
+ shape,
68
+ async (args: Record<string, unknown>) => {
69
+ const result = await bridge.dispatch(meta.name, args ?? {});
70
+ return toMcpResult(result, bridge.project);
71
+ },
72
+ );
73
+ }
74
+
75
+ // In-process tools are sandbox verification and rules helpers that do not
76
+ // touch real Firebase. Execute them directly and record the result.
77
+ for (const handler of options.inProcess) {
78
+ const shape = jsonSchemaToZodShape(handler.parameters as never);
79
+ (server.tool as unknown as Function)(
80
+ handler.name,
81
+ handler.description,
82
+ shape,
83
+ async (args: Record<string, unknown>) => {
84
+ const startedAtMs = Date.now();
85
+ try {
86
+ // The bridge supplies an AbortSignal-less ToolContext; tools
87
+ // that genuinely need cancellation should still respect a
88
+ // signal supplied via the MCP transport in the future.
89
+ const ctx = {
90
+ signal: new AbortController().signal,
91
+ } as never;
92
+ const result = await handler.execute(args ?? {}, ctx);
93
+ const normalised = {
94
+ ok: result.ok,
95
+ summary: result.summary,
96
+ data: result.data,
97
+ };
98
+ bridge.recordToolEvent({
99
+ timestamp: new Date(startedAtMs).toISOString(),
100
+ mode: 'sandbox',
101
+ project: bridge.project,
102
+ tool: handler.name,
103
+ args: args ?? {},
104
+ result: normalised,
105
+ durationMs: Date.now() - startedAtMs,
106
+ });
107
+ return toMcpResult(normalised, bridge.project);
108
+ } catch (err) {
109
+ const result = {
110
+ ok: false,
111
+ summary: err instanceof Error ? err.message : String(err),
112
+ };
113
+ bridge.recordToolEvent({
114
+ timestamp: new Date(startedAtMs).toISOString(),
115
+ mode: 'sandbox',
116
+ project: bridge.project,
117
+ tool: handler.name,
118
+ args: args ?? {},
119
+ result,
120
+ durationMs: Date.now() - startedAtMs,
121
+ });
122
+ return toMcpResult(result, bridge.project);
123
+ }
124
+ },
125
+ );
126
+ }
127
+
128
+ return server;
129
+ }