@pyric/cli 0.1.0-alpha.19 → 0.1.0-alpha.20
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.
- package/README.md +9 -9
- package/dist/cli/index.d.ts +2 -2
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +48 -42
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/mcp-proxy.js +3 -3
- package/dist/cli/mcp-proxy.js.map +1 -1
- package/dist/cli/parse-args.d.ts +1 -1
- package/dist/cli/parse-args.d.ts.map +1 -1
- package/dist/cli/parse-args.js +51 -15
- package/dist/cli/parse-args.js.map +1 -1
- package/dist/cli/pyric-config.d.ts +24 -0
- package/dist/cli/pyric-config.d.ts.map +1 -0
- package/dist/cli/pyric-config.js +28 -0
- package/dist/cli/pyric-config.js.map +1 -0
- package/dist/cli/{dev-runner.d.ts → sandbox-runner.d.ts} +33 -36
- package/dist/cli/sandbox-runner.d.ts.map +1 -0
- package/dist/cli/{dev-runner.js → sandbox-runner.js} +90 -55
- package/dist/cli/sandbox-runner.js.map +1 -0
- package/dist/cli/serve.d.ts +6 -4
- package/dist/cli/serve.d.ts.map +1 -1
- package/dist/cli/serve.js +99 -40
- package/dist/cli/serve.js.map +1 -1
- package/dist/cli/snapshot.js +5 -5
- package/dist/cli/snapshot.js.map +1 -1
- package/dist/cli/verify.js +1 -1
- package/dist/cli/verify.js.map +1 -1
- package/dist/functions-rtdb/development-runtime.js +1 -1
- package/dist/functions-rtdb/development-runtime.js.map +1 -1
- package/dist/functions-rtdb/project.js +8 -8
- package/dist/functions-rtdb/project.js.map +1 -1
- package/dist/next/guard.js +1 -1
- package/dist/next/guard.js.map +1 -1
- package/dist/remote/index.d.ts +1 -1
- package/dist/remote/index.js +9 -9
- package/dist/remote/index.js.map +1 -1
- package/dist/serve/bundler.js +10 -10
- package/dist/serve/bundler.js.map +1 -1
- package/dist/serve/entries/ai.d.ts.map +1 -1
- package/dist/serve/entries/ai.js +27 -1
- package/dist/serve/entries/ai.js.map +1 -1
- package/dist/serve/entries/auth-helper-dom.js +1 -1
- package/dist/serve/entries/auth-helper-dom.js.map +1 -1
- package/dist/serve/entries/auth-helper-runtime.js +1 -1
- package/dist/serve/entries/auth-helper-runtime.js.map +1 -1
- package/dist/serve/entries/firestore.js +3 -3
- package/dist/serve/entries/firestore.js.map +1 -1
- package/dist/serve/entries/keepalive.js +1 -1
- package/dist/serve/entries/keepalive.js.map +1 -1
- package/dist/serve/entries/runtime.js +21 -14
- package/dist/serve/entries/runtime.js.map +1 -1
- package/dist/serve/entries/storage.js +1 -1
- package/dist/serve/entries/storage.js.map +1 -1
- package/dist/serve/entries/worker-runtime.js +1 -1
- package/dist/serve/entries/worker-runtime.js.map +1 -1
- package/dist/serve/init-payload.d.ts +4 -0
- package/dist/serve/init-payload.d.ts.map +1 -1
- package/dist/serve/namespace.d.ts +6 -0
- package/dist/serve/namespace.d.ts.map +1 -1
- package/dist/serve/namespace.js +36 -5
- package/dist/serve/namespace.js.map +1 -1
- package/dist/serve/rules.d.ts.map +1 -1
- package/dist/serve/rules.js +44 -21
- package/dist/serve/rules.js.map +1 -1
- package/dist/serve/runtime/chip.d.ts.map +1 -1
- package/dist/serve/runtime/chip.js +32 -1
- package/dist/serve/runtime/chip.js.map +1 -1
- package/dist/serve/runtime/manifest.d.ts +1 -1
- package/dist/serve/runtime/manifest.d.ts.map +1 -1
- package/dist/serve/runtime/manifest.js +1 -1
- package/dist/serve/runtime/manifest.js.map +1 -1
- package/dist/serve/runtime/status.d.ts.map +1 -1
- package/dist/serve/runtime/status.js +23 -0
- package/dist/serve/runtime/status.js.map +1 -1
- package/dist/serve/sandbox-session.d.ts +1 -0
- package/dist/serve/sandbox-session.d.ts.map +1 -1
- package/dist/serve/sandbox-session.js +10 -4
- package/dist/serve/sandbox-session.js.map +1 -1
- package/dist/serve/server.d.ts +7 -0
- package/dist/serve/server.d.ts.map +1 -1
- package/dist/serve/server.js +15 -1
- package/dist/serve/server.js.map +1 -1
- package/dist/serve/site-ui/404.html +1 -1
- package/dist/serve/site-ui/_astro/{RulesCodeEditor.Bl-XMkhD.js → RulesCodeEditor.XtDmHOlN.js} +1 -1
- package/dist/serve/site-ui/_astro/_slug_.Da5VLhW1.css +1 -0
- package/dist/serve/site-ui/_astro/_studio_.BnfYCKoI.css +1 -0
- package/dist/serve/site-ui/_astro/app.B52xr-DK.js +1 -0
- package/dist/serve/site-ui/_astro/app.DWBraPUw.js +12 -0
- package/dist/serve/site-ui/_astro/app.pMSnN6Qw.css +1 -0
- package/dist/serve/site-ui/_astro/{chess-showcase.Dh5D8kqe.js → chess-showcase.B72XYeoh.js} +1 -1
- package/dist/serve/site-ui/_astro/{example-runtime.CuyAjjiG.js → example-runtime.C25_lx6Z.js} +1 -1
- package/dist/serve/site-ui/_astro/index.Tb-31efc.css +1 -0
- package/dist/serve/site-ui/_astro/index.UkCBr-_0.css +1 -0
- package/dist/serve/site-ui/_astro/{sandbox-controls.BP4M5f1M.js → sandbox-controls.DEpnKg-O.js} +11 -11
- package/dist/serve/site-ui/_astro/{seed.CSUkloMi.js → seed.Co0yUJY5.js} +1 -1
- package/dist/serve/site-ui/_astro/{writes.C57paAj2.js → writes.rpjrFzmu.js} +7 -4
- package/dist/serve/site-ui/auth/index.html +5 -3
- package/dist/serve/site-ui/david-east.jpg +0 -0
- package/dist/serve/site-ui/docs/_rhythm/index.html +6 -3
- package/dist/serve/site-ui/docs/_rhythm.md +1 -1
- package/dist/serve/site-ui/docs/agent/set-up-your-agent/index.html +18 -15
- package/dist/serve/site-ui/docs/agent/set-up-your-agent.md +16 -16
- package/dist/serve/site-ui/docs/agent/watch-and-review/index.html +6 -5
- package/dist/serve/site-ui/docs/agent/watch-and-review.md +1 -5
- package/dist/serve/site-ui/docs/agent/work-with-an-agent/index.html +8 -5
- package/dist/serve/site-ui/docs/agent/work-with-an-agent.md +4 -4
- package/dist/serve/site-ui/docs/ai-compat/index.html +5 -2
- package/dist/serve/site-ui/docs/api-reference/index.html +6 -2
- package/dist/serve/site-ui/docs/api-reference.md +1 -0
- package/dist/serve/site-ui/docs/app-compat/index.html +5 -2
- package/dist/serve/site-ui/docs/auth-compat/index.html +5 -2
- package/dist/serve/site-ui/docs/build/ai-logic/index.html +5 -2
- package/dist/serve/site-ui/docs/build/authentication/index.html +7 -4
- package/dist/serve/site-ui/docs/build/authentication.md +2 -2
- package/dist/serve/site-ui/docs/build/cloud-firestore/index.html +5 -2
- package/dist/serve/site-ui/docs/build/cloud-messaging/index.html +5 -2
- package/dist/serve/site-ui/docs/build/cloud-storage/index.html +6 -3
- package/dist/serve/site-ui/docs/build/cloud-storage.md +1 -1
- package/dist/serve/site-ui/docs/build/realtime-database/index.html +9 -6
- package/dist/serve/site-ui/docs/build/realtime-database.md +4 -4
- package/dist/serve/site-ui/docs/conformance-scores/index.html +5 -2
- package/dist/serve/site-ui/docs/create-pyric-reference-api/index.html +6 -3
- package/dist/serve/site-ui/docs/create-pyric-reference-api.md +1 -1
- package/dist/serve/site-ui/docs/database-compat/index.html +5 -2
- package/dist/serve/site-ui/docs/examples/chess/index.html +5 -2
- package/dist/serve/site-ui/docs/examples/index.html +5 -2
- package/dist/serve/site-ui/docs/firestore-compat/index.html +5 -2
- package/dist/serve/site-ui/docs/functions-rtdb-compat/index.html +5 -2
- package/dist/serve/site-ui/docs/get-started/how-the-swap-works/index.html +9 -6
- package/dist/serve/site-ui/docs/get-started/how-the-swap-works.md +5 -5
- package/dist/serve/site-ui/docs/get-started/nextjs/index.html +9 -6
- package/dist/serve/site-ui/docs/get-started/nextjs.md +4 -4
- package/dist/serve/site-ui/docs/get-started/start-building/index.html +14 -9
- package/dist/serve/site-ui/docs/get-started/start-building.md +16 -10
- package/dist/serve/site-ui/docs/get-started/vite/index.html +6 -3
- package/dist/serve/site-ui/docs/get-started/vite.md +1 -1
- package/dist/serve/site-ui/docs/index.json +180 -150
- package/dist/serve/site-ui/docs/messaging-compat/index.html +5 -2
- package/dist/serve/site-ui/docs/observe/resolve-runtime-status/index.html +5 -2
- package/dist/serve/site-ui/docs/observe/see-whats-happening/index.html +6 -4
- package/dist/serve/site-ui/docs/observe/see-whats-happening.md +2 -4
- package/dist/serve/site-ui/docs/observe/shape-your-data/index.html +9 -6
- package/dist/serve/site-ui/docs/observe/shape-your-data.md +4 -4
- package/dist/serve/site-ui/docs/overview/index.html +16 -10
- package/dist/serve/site-ui/docs/overview.md +19 -11
- package/dist/serve/site-ui/docs/pyric-admin-app-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-admin-auth-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-admin-database-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-admin-firestore-reference-api/index.html +8 -5
- package/dist/serve/site-ui/docs/pyric-admin-firestore-reference-api.md +3 -3
- package/dist/serve/site-ui/docs/pyric-admin-messaging-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-admin-reference-api/index.html +501 -0
- package/dist/serve/site-ui/docs/pyric-admin-reference-api.md +587 -0
- package/dist/serve/site-ui/docs/pyric-admin-storage-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ai-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ai-scripting-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-app-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-auth-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-cli-assurance-browser-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-cli-assurance-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-cli-bridge-client-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-cli-bridge-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-cli-conformance-browser-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-cli-conformance-docs-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-cli-conformance-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-cli-credentials-node-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-cli-discover-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-cli-next-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-cli-register-app-bridge-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-cli-register-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-cli-remote-reference-api/index.html +6 -3
- package/dist/serve/site-ui/docs/pyric-cli-remote-reference-api.md +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-serve-worker-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-cli-verify-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-cli-vite-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-database-reference-api/index.html +166 -95
- package/dist/serve/site-ui/docs/pyric-database-reference-api.md +107 -1
- package/dist/serve/site-ui/docs/pyric-firestore-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-messaging-reference-api/index.html +10 -4
- package/dist/serve/site-ui/docs/pyric-messaging-reference-api.md +5 -2
- package/dist/serve/site-ui/docs/pyric-messaging-sw-reference-api/index.html +10 -4
- package/dist/serve/site-ui/docs/pyric-messaging-sw-reference-api.md +5 -2
- package/dist/serve/site-ui/docs/pyric-rules-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-sandbox-database-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-sandbox-firestore-reference-api/index.html +6 -3
- package/dist/serve/site-ui/docs/pyric-sandbox-firestore-reference-api.md +2 -2
- package/dist/serve/site-ui/docs/pyric-sandbox-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-storage-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ui-agents-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ui-auth-hooks-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ui-auth-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ui-events-hooks-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ui-events-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ui-firestore-hooks-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ui-firestore-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ui-primitives-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ui-rtdb-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ui-rules-hooks-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ui-rules-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ui-storage-hooks-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ui-storage-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ui-traffic-hooks-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ui-traffic-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/reference/cli/index.html +153 -0
- package/dist/serve/site-ui/docs/reference/cli.md +114 -0
- package/dist/serve/site-ui/docs/rules-compat/index.html +5 -2
- package/dist/serve/site-ui/docs/secure/audit-your-rules/index.html +5 -2
- package/dist/serve/site-ui/docs/secure/firestore-rules-limits/index.html +5 -2
- package/dist/serve/site-ui/docs/secure/read-a-denial/index.html +6 -3
- package/dist/serve/site-ui/docs/secure/read-a-denial.md +1 -1
- package/dist/serve/site-ui/docs/secure/rtdb-rules-in-typescript/index.html +5 -2
- package/dist/serve/site-ui/docs/secure/rules-standard-library/index.html +5 -2
- package/dist/serve/site-ui/docs/secure/secure-it-with-rules/index.html +5 -2
- package/dist/serve/site-ui/docs/secure/simulate-and-lint/index.html +5 -2
- package/dist/serve/site-ui/docs/secure/write-a-rules-test-suite/index.html +5 -2
- package/dist/serve/site-ui/docs/ship/ship-to-production/index.html +7 -4
- package/dist/serve/site-ui/docs/ship/ship-to-production.md +2 -2
- package/dist/serve/site-ui/docs/ship/test-in-node/index.html +9 -6
- package/dist/serve/site-ui/docs/ship/test-in-node.md +4 -4
- package/dist/serve/site-ui/docs/storage-compat/index.html +5 -2
- package/dist/serve/site-ui/docs/trust/how-we-know-it-matches-firebase/index.html +5 -2
- package/dist/serve/site-ui/docs/trust/rules-standard-library/index.html +5 -2
- package/dist/serve/site-ui/docs/trust/versioning-and-compatibility/index.html +5 -2
- package/dist/serve/site-ui/docs/tutorial/index.html +10 -7
- package/dist/serve/site-ui/docs/tutorial.md +4 -4
- package/dist/serve/site-ui/examples/chess/index.html +4 -2
- package/dist/serve/site-ui/firestore/index.html +5 -3
- package/dist/serve/site-ui/hero-charcoal.jpg +0 -0
- package/dist/serve/site-ui/index.html +9 -7
- package/dist/serve/site-ui/llms.txt +4 -3
- package/dist/serve/site-ui/pyric-campfire-full.png +0 -0
- package/dist/serve/site-ui/pyric-campfire.png +0 -0
- package/dist/serve/site-ui/rtdb/index.html +5 -3
- package/dist/serve/site-ui/settings/index.html +5 -3
- package/dist/serve/site-ui/storage/index.html +5 -3
- package/dist/serve/site-ui/studio/index.html +13 -0
- package/dist/serve/site-ui/studio-routes.json +1 -1
- package/dist/serve/site-ui/traffic/index.html +5 -3
- package/dist/serve/studio/routes.d.ts +9 -0
- package/dist/serve/studio/routes.d.ts.map +1 -1
- package/dist/serve/studio/routes.js +47 -7
- package/dist/serve/studio/routes.js.map +1 -1
- package/dist/serve/vite-ai-config.d.ts.map +1 -1
- package/dist/serve/vite-ai-config.js +38 -8
- package/dist/serve/vite-ai-config.js.map +1 -1
- package/dist/serve/vite-module-swap.d.ts.map +1 -1
- package/dist/serve/vite-module-swap.js +0 -28
- package/dist/serve/vite-module-swap.js.map +1 -1
- package/dist/serve/vite-sandbox-generation.d.ts +1 -1
- package/dist/serve/vite-sandbox-generation.d.ts.map +1 -1
- package/dist/serve/vite-sandbox-generation.js +1 -1
- package/dist/serve/vite-sandbox-generation.js.map +1 -1
- package/dist/serve/worker/client/disconnect.js +1 -1
- package/dist/serve/worker/client/disconnect.js.map +1 -1
- package/dist/serve/worker/client/presence.d.ts.map +1 -1
- package/dist/serve/worker/client/presence.js +18 -5
- package/dist/serve/worker/client/presence.js.map +1 -1
- package/dist/serve/worker/host-ai.d.ts.map +1 -1
- package/dist/serve/worker/host-ai.js +15 -0
- package/dist/serve/worker/host-ai.js.map +1 -1
- package/dist/serve/worker/protocol.d.ts +4 -0
- package/dist/serve/worker/protocol.d.ts.map +1 -1
- package/dist/serve/worker/protocol.js.map +1 -1
- package/dist/serve/worker/serve-init.d.ts.map +1 -1
- package/dist/serve/worker/serve-init.js +7 -1
- package/dist/serve/worker/serve-init.js.map +1 -1
- package/package.json +5 -5
- package/src/cli/cli.test.ts +2 -2
- package/src/cli/index.ts +48 -42
- package/src/cli/mcp-proxy.ts +4 -4
- package/src/cli/parse-args.ts +61 -15
- package/src/cli/pyric-config.ts +45 -0
- package/src/cli/{dev-runner.ts → sandbox-runner.ts} +117 -72
- package/src/cli/serve.ts +115 -47
- package/src/cli/snapshot.ts +5 -5
- package/src/cli/verify.ts +1 -1
- package/src/functions-rtdb/development-runtime.ts +1 -1
- package/src/functions-rtdb/project.ts +8 -8
- package/src/next/guard.ts +1 -1
- package/src/remote/index.ts +9 -9
- package/src/serve/bundler.ts +10 -10
- package/src/serve/entries/ai.ts +27 -1
- package/src/serve/entries/auth-helper-dom.ts +1 -1
- package/src/serve/entries/auth-helper-runtime.ts +1 -1
- package/src/serve/entries/firestore.ts +3 -3
- package/src/serve/entries/keepalive.ts +1 -1
- package/src/serve/entries/runtime.ts +19 -14
- package/src/serve/entries/storage.ts +1 -1
- package/src/serve/entries/worker-runtime.ts +1 -1
- package/src/serve/init-payload.ts +4 -0
- package/src/serve/namespace.ts +44 -5
- package/src/serve/rules.ts +47 -22
- package/src/serve/runtime/chip.ts +38 -1
- package/src/serve/runtime/manifest.ts +1 -1
- package/src/serve/runtime/status.ts +24 -0
- package/src/serve/sandbox-session.ts +10 -4
- package/src/serve/server.ts +18 -1
- package/src/serve/studio/routes.ts +63 -7
- package/src/serve/vite-ai-config.ts +37 -7
- package/src/serve/vite-module-swap.ts +0 -28
- package/src/serve/vite-sandbox-generation.ts +1 -1
- package/src/serve/worker/client/disconnect.ts +1 -1
- package/src/serve/worker/client/presence.ts +25 -5
- package/src/serve/worker/host-ai.ts +15 -0
- package/src/serve/worker/protocol.ts +2 -1
- package/src/serve/worker/serve-init.ts +5 -1
- package/README.md.orig +0 -145
- package/dist/cli/dev-runner.d.ts.map +0 -1
- package/dist/cli/dev-runner.js.map +0 -1
- package/dist/serve/site-ui/_astro/_slug_.CXh9yTP1.css +0 -1
- package/dist/serve/site-ui/_astro/_slug_.CupVK4TM.css +0 -1
- package/dist/serve/site-ui/_astro/_studio_.CfxiQWkk.css +0 -1
- package/dist/serve/site-ui/_astro/app.BsFsMjn0.css +0 -1
- package/dist/serve/site-ui/_astro/app.Ce3MTLQm.js +0 -1
- package/dist/serve/site-ui/_astro/app.taqzyjD0.js +0 -11
- package/dist/serve/site-ui/docs/trust/storage-rules-stdlib-research/index.html +0 -582
- package/dist/serve/site-ui/docs/trust/storage-rules-stdlib-research.md +0 -448
package/src/cli/serve.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `pyric
|
|
2
|
+
* `pyric sandbox` is the local server with the Pyric sandbox standing in for
|
|
3
3
|
* Firebase. Feels like `firebase serve` (banner, labeled lines, port 3473,
|
|
4
4
|
* SIGINT shutdown) with the sandbox-flavored extras firebase can't do: the
|
|
5
5
|
* served page runs an in-browser backend, your `firestore.rules` deploy into
|
|
@@ -28,17 +28,16 @@ import { formatActivityWarning } from '../serve/activity-warning.js';
|
|
|
28
28
|
import { consoleServeLogger, startStaticServer, stderrServeLogger, type ServeHandle } from '../serve/server.js';
|
|
29
29
|
import { createBridgeMount } from '../serve/bridge-mount.js';
|
|
30
30
|
import { openBrowser, shouldAutoOpen } from '../serve/open-browser.js';
|
|
31
|
+
import { readPyricConfig, type PyricConfig } from './pyric-config.js';
|
|
31
32
|
import {
|
|
32
33
|
buildChildEnv,
|
|
33
|
-
detectPackageManager,
|
|
34
34
|
formatStartupEnvExport,
|
|
35
|
-
readDevScript,
|
|
36
35
|
registerModuleUrl,
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
resolveSandboxChild,
|
|
37
|
+
spawnSandboxChild,
|
|
39
38
|
waitForSandboxPeer,
|
|
40
|
-
type
|
|
41
|
-
} from './
|
|
39
|
+
type SandboxChildHandle,
|
|
40
|
+
} from './sandbox-runner.js';
|
|
42
41
|
import {
|
|
43
42
|
createFunctionsDevelopmentRuntime,
|
|
44
43
|
createHttpFunctionsPeerReadiness,
|
|
@@ -60,7 +59,7 @@ interface HostingConfig {
|
|
|
60
59
|
rewrites?: Array<{ source?: string; destination?: string }>;
|
|
61
60
|
}
|
|
62
61
|
|
|
63
|
-
/** Extract the single hosting config `pyric
|
|
62
|
+
/** Extract the single hosting config `pyric sandbox` supports. Arrays
|
|
64
63
|
* (multi-site) take the first entry with a warning — multi-site is out of
|
|
65
64
|
* scope (plan section 6). */
|
|
66
65
|
export function extractHosting(config: FirebaseJson | null): HostingConfig | null {
|
|
@@ -84,7 +83,7 @@ export interface ServeRuntime {
|
|
|
84
83
|
payload: () => InitPayload;
|
|
85
84
|
/** HTTP MCP endpoint when `--bridge` is on; null otherwise. */
|
|
86
85
|
mcpUrl: string | null;
|
|
87
|
-
/** The Studio
|
|
86
|
+
/** The Studio hub URL (`/__pyric/ui/studio`) when `--ui` is on and the build is
|
|
88
87
|
* present; null otherwise. */
|
|
89
88
|
uiUrl: string | null;
|
|
90
89
|
/** Persistence summary (null when `--persist` is off). */
|
|
@@ -147,6 +146,8 @@ export async function startServe(opts: {
|
|
|
147
146
|
* (the served project's file tree) + `/__pyric/projects` (single-project
|
|
148
147
|
* store over `cwd`). The Studio app's `local` mode talks to these. */
|
|
149
148
|
ui?: boolean;
|
|
149
|
+
/** Explicit opt-in to permissive mode (default-allow RTDB rules when unconfigured). */
|
|
150
|
+
permissive?: boolean;
|
|
150
151
|
logger?: Parameters<typeof startStaticServer>[0]['logger'];
|
|
151
152
|
}): Promise<ServeRuntime> {
|
|
152
153
|
const logger = opts.logger ?? consoleServeLogger();
|
|
@@ -157,7 +158,7 @@ export async function startServe(opts: {
|
|
|
157
158
|
// Fail fast instead of pretending to reset something.
|
|
158
159
|
if (opts.fresh && !opts.persist) {
|
|
159
160
|
throw new Error(
|
|
160
|
-
'pyric
|
|
161
|
+
'pyric sandbox: --fresh requires --persist (it discards .pyric/state/state.json). ' +
|
|
161
162
|
'Browser-stored data is cleared from Studio → Settings → Reset, or DevTools → Clear site data.',
|
|
162
163
|
);
|
|
163
164
|
}
|
|
@@ -172,9 +173,29 @@ export async function startServe(opts: {
|
|
|
172
173
|
logger.note(' ⚠ no firebase.json found — serving the current directory without hosting config');
|
|
173
174
|
}
|
|
174
175
|
|
|
176
|
+
const pyricConfig = await readPyricConfig(opts.cwd);
|
|
177
|
+
if (pyricConfig.rules) {
|
|
178
|
+
if (!config) config = {};
|
|
179
|
+
if (typeof pyricConfig.rules === 'string') {
|
|
180
|
+
config.firestore = { ...config.firestore, rules: pyricConfig.rules };
|
|
181
|
+
} else {
|
|
182
|
+
if (pyricConfig.rules.firestore) {
|
|
183
|
+
config.firestore = { ...config.firestore, rules: pyricConfig.rules.firestore };
|
|
184
|
+
}
|
|
185
|
+
if (pyricConfig.rules.database) {
|
|
186
|
+
config.database = { ...config.database, rules: pyricConfig.rules.database };
|
|
187
|
+
}
|
|
188
|
+
if (pyricConfig.rules.storage) {
|
|
189
|
+
const baseStorage =
|
|
190
|
+
typeof config.storage === 'object' && !Array.isArray(config.storage) ? config.storage : {};
|
|
191
|
+
config.storage = { ...baseStorage, rules: pyricConfig.rules.storage };
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
175
196
|
const hosting = extractHosting(config);
|
|
176
197
|
if (Array.isArray(config?.hosting) && (config.hosting as unknown[]).length > 1) {
|
|
177
|
-
logger.note(' ⚠ multiple hosting sites configured
|
|
198
|
+
logger.note(' ⚠ multiple hosting sites configured. pyric sandbox serves the first entry only');
|
|
178
199
|
}
|
|
179
200
|
const publicDir = resolve(opts.cwd, hosting?.public ?? '.');
|
|
180
201
|
if (!existsSync(publicDir)) {
|
|
@@ -184,16 +205,16 @@ export async function startServe(opts: {
|
|
|
184
205
|
const looksLikeBuildOutput = /(^|[\\/])(dist|build|out)$/.test(publicDir);
|
|
185
206
|
const hint = looksLikeBuildOutput
|
|
186
207
|
? `\n No build yet? Run \`bun run dev\` for the live sandbox dev server, ` +
|
|
187
|
-
`or \`bun run build\` then \`pyric
|
|
208
|
+
`or \`bun run build\` then \`pyric sandbox\` to preview the production build.`
|
|
188
209
|
: '';
|
|
189
|
-
throw new Error(`pyric
|
|
210
|
+
throw new Error(`pyric sandbox: hosting.public directory does not exist: ${publicDir}${hint}`);
|
|
190
211
|
}
|
|
191
212
|
|
|
192
213
|
// The import map can only remap BARE `firebase/*` specifiers. A plain bundler
|
|
193
214
|
// build (`vite build`) inlines the real SDK into the app chunk, leaving
|
|
194
215
|
// nothing to intercept — the page would then talk to REAL Firebase endpoints
|
|
195
216
|
// with the sandbox's fake credentials while the injected banner claims
|
|
196
|
-
// otherwise. That hole is structural, so `pyric
|
|
217
|
+
// otherwise. That hole is structural, so `pyric sandbox` refuses such a dist
|
|
197
218
|
// rather than serving it. A pyric SANDBOX build (`vite build --mode
|
|
198
219
|
// development`) carries the marker and bundles pyric's in-page adapters, so it
|
|
199
220
|
// is trusted and the scan is skipped (marker present → no real SDK to find).
|
|
@@ -201,14 +222,14 @@ export async function startServe(opts: {
|
|
|
201
222
|
const inlined = scanForInlinedFirebase(publicDir);
|
|
202
223
|
if (inlined.length > 0) {
|
|
203
224
|
throw new Error(
|
|
204
|
-
`pyric
|
|
225
|
+
`pyric sandbox: ${inlined[0]} bundles the real Firebase SDK, so this dist cannot be ` +
|
|
205
226
|
`sandboxed — its firebase/* calls would reach LIVE Google endpoints, not the ` +
|
|
206
227
|
`pyric sandbox. Two ways forward:\n` +
|
|
207
|
-
` (a)
|
|
228
|
+
` (a) \`pyric sandbox -- <command>\` runs the child server with the @pyric/cli/vite ` +
|
|
208
229
|
`plugin swaps firebase/* live) — use \`bun run dev\`;\n` +
|
|
209
230
|
` (b) rebuild as a self-contained sandbox bundle and serve THAT: ` +
|
|
210
231
|
`\`vite build --mode development\` (or pyric({ swapInBuild: true })) then ` +
|
|
211
|
-
`\`pyric
|
|
232
|
+
`\`pyric sandbox\`.\n` +
|
|
212
233
|
`A plain \`vite build\` is your production build — deploy it, don't sandbox it.`,
|
|
213
234
|
);
|
|
214
235
|
}
|
|
@@ -285,15 +306,16 @@ export async function startServe(opts: {
|
|
|
285
306
|
studio: opts.ui ? { siteUiDir } : false,
|
|
286
307
|
bridgeUrl: () => mount && origin.port > 0 ? mount.wsUrl(origin) : null,
|
|
287
308
|
activity: (incident) => logger.note(formatActivityWarning(incident)),
|
|
309
|
+
permissive: opts.permissive,
|
|
288
310
|
logger,
|
|
289
311
|
});
|
|
290
312
|
} catch (error) {
|
|
291
313
|
bundle.dispose?.();
|
|
292
314
|
if (error instanceof SandboxSeedError) {
|
|
293
315
|
if (error.kind === 'read') {
|
|
294
|
-
throw new Error(`pyric
|
|
316
|
+
throw new Error(`pyric sandbox: failed to read --seed ${error.path}: ${error.detail}`);
|
|
295
317
|
}
|
|
296
|
-
throw new Error(`pyric
|
|
318
|
+
throw new Error(`pyric sandbox: --seed must be a JSON object of "collection/doc" to fields, ${error.detail}`);
|
|
297
319
|
}
|
|
298
320
|
throw error;
|
|
299
321
|
}
|
|
@@ -376,7 +398,7 @@ export async function startServe(opts: {
|
|
|
376
398
|
};
|
|
377
399
|
if (bundle.dispose) handle.server.once('close', bundle.dispose);
|
|
378
400
|
handle.server.once('close', () => void closeOwnedResources());
|
|
379
|
-
const uiUrl = siteUiDir ? `${handle.url}/__pyric/ui
|
|
401
|
+
const uiUrl = siteUiDir ? `${handle.url}/__pyric/ui/studio` : null;
|
|
380
402
|
const docsUrl = siteUiDir ? `${handle.url}/__pyric/ui/docs/` : null;
|
|
381
403
|
|
|
382
404
|
// Hot-reload: the static adapter observes the filesystem; the session owns
|
|
@@ -547,7 +569,7 @@ export function bridgeEnabledFromFlags(flags: { get(key: string): unknown }): bo
|
|
|
547
569
|
* A dev child also implies the bridge: the runner injects
|
|
548
570
|
* `PYRIC_SANDBOX=remote:<url>` into the child, whose remote client dials the
|
|
549
571
|
* `/__pyric/sandbox` WS — without the bridge mount that upgrade 404s and
|
|
550
|
-
*
|
|
572
|
+
* `pyric sandbox -- <cmd>` breaks its own
|
|
551
573
|
* child. Pure + exported for unit coverage.
|
|
552
574
|
*/
|
|
553
575
|
export function bridgeEnabledFor(
|
|
@@ -560,7 +582,7 @@ export function bridgeEnabledFor(
|
|
|
560
582
|
|
|
561
583
|
/**
|
|
562
584
|
* Process-level last line of defense for the dev server: a single bad
|
|
563
|
-
* request/WS interaction must never take `pyric
|
|
585
|
+
* request/WS interaction must never take `pyric sandbox` down. Node kills the
|
|
564
586
|
* process on an unhandled rejection (default `--unhandled-rejections=throw`
|
|
565
587
|
* since v15) and on any uncaught exception — and the serve process hosts
|
|
566
588
|
* long-lived, browser-driven machinery (WS peers, SSE streams, file watchers,
|
|
@@ -578,13 +600,13 @@ export function installServeProcessGuard(
|
|
|
578
600
|
reason instanceof Error ? (reason.stack ?? reason.message) : String(reason);
|
|
579
601
|
const onRejection = (reason: unknown): void => {
|
|
580
602
|
log(
|
|
581
|
-
` ✖ pyric
|
|
603
|
+
` ✖ pyric sandbox: unhandled rejection. The server was kept alive. Please report this:\n` +
|
|
582
604
|
` ${describe(reason).split('\n').join('\n ')}`,
|
|
583
605
|
);
|
|
584
606
|
};
|
|
585
607
|
const onException = (err: unknown): void => {
|
|
586
608
|
log(
|
|
587
|
-
` ✖ pyric
|
|
609
|
+
` ✖ pyric sandbox: uncaught exception. The server was kept alive. Please report this:\n` +
|
|
588
610
|
` ${describe(err).split('\n').join('\n ')}`,
|
|
589
611
|
);
|
|
590
612
|
};
|
|
@@ -596,10 +618,49 @@ export function installServeProcessGuard(
|
|
|
596
618
|
};
|
|
597
619
|
}
|
|
598
620
|
|
|
621
|
+
function resolveServePort(flagPort: unknown, configPort?: number): number {
|
|
622
|
+
if (typeof flagPort === 'string') return Number(flagPort);
|
|
623
|
+
if (typeof flagPort === 'number') return flagPort;
|
|
624
|
+
if (typeof configPort === 'number') return configPort;
|
|
625
|
+
return 3473;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
function resolveProjectIdentifier(
|
|
629
|
+
flagProject?: unknown,
|
|
630
|
+
envProject?: string,
|
|
631
|
+
configProject?: string,
|
|
632
|
+
rcDefault?: string,
|
|
633
|
+
): string {
|
|
634
|
+
if (typeof flagProject === 'string' && flagProject.length > 0) return flagProject;
|
|
635
|
+
if (typeof envProject === 'string' && envProject.length > 0) return envProject;
|
|
636
|
+
if (typeof configProject === 'string' && configProject.length > 0) return configProject;
|
|
637
|
+
if (typeof rcDefault === 'string' && rcDefault.length > 0) return rcDefault;
|
|
638
|
+
return 'demo-project';
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
function resolveExplicitCommand(parsed: ParsedArgs): string[] | null {
|
|
642
|
+
if (parsed.passthrough && parsed.passthrough.length > 0) {
|
|
643
|
+
return parsed.passthrough;
|
|
644
|
+
}
|
|
645
|
+
if (parsed.positional.length > 0) {
|
|
646
|
+
return parsed.positional;
|
|
647
|
+
}
|
|
648
|
+
return null;
|
|
649
|
+
}
|
|
650
|
+
|
|
599
651
|
/** CLI entry. Resolves on SIGINT/SIGTERM after a clean stop. */
|
|
600
652
|
export async function runServe(parsed: ParsedArgs): Promise<number> {
|
|
653
|
+
const cwd = process.cwd();
|
|
654
|
+
let pyricConfig: PyricConfig = {};
|
|
655
|
+
try {
|
|
656
|
+
pyricConfig = await readPyricConfig(cwd);
|
|
657
|
+
} catch (error) {
|
|
658
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
659
|
+
return 2;
|
|
660
|
+
}
|
|
661
|
+
|
|
601
662
|
const flagPort = parsed.flags.get('port');
|
|
602
|
-
const port =
|
|
663
|
+
const port = resolveServePort(flagPort, pyricConfig.port);
|
|
603
664
|
if (!Number.isFinite(port) || port < 0 || port > 65535) {
|
|
604
665
|
process.stderr.write(`pyric: invalid --port '${flagPort}'.\n`);
|
|
605
666
|
return 1;
|
|
@@ -610,7 +671,7 @@ export async function runServe(parsed: ParsedArgs): Promise<number> {
|
|
|
610
671
|
const only = parsed.flags.get('only');
|
|
611
672
|
if (typeof only === 'string' && only !== 'hosting') {
|
|
612
673
|
process.stderr.write(
|
|
613
|
-
`pyric: --only '${only}' is not supported
|
|
674
|
+
`pyric: --only '${only}' is not supported. pyric sandbox serves hosting with the in-page sandbox standing in for Firestore and Auth.\n`,
|
|
614
675
|
);
|
|
615
676
|
return 1;
|
|
616
677
|
}
|
|
@@ -623,7 +684,6 @@ export async function runServe(parsed: ParsedArgs): Promise<number> {
|
|
|
623
684
|
// Resolve the child plan BEFORE the server starts: a planned child implies
|
|
624
685
|
// the bridge (see bridgeEnabledFor) — the child's injected PYRIC_SANDBOX
|
|
625
686
|
// is useless without the /__pyric/sandbox WS mount.
|
|
626
|
-
const cwd = process.cwd();
|
|
627
687
|
let functionsProject: FunctionsRtdbProject | null;
|
|
628
688
|
let functionsProjectId: string | null = null;
|
|
629
689
|
try {
|
|
@@ -631,22 +691,25 @@ export async function runServe(parsed: ParsedArgs): Promise<number> {
|
|
|
631
691
|
if (functionsProject) {
|
|
632
692
|
const flagProject = parsed.flags.get('project');
|
|
633
693
|
const rc = await readFirebaseRc(cwd);
|
|
634
|
-
functionsProjectId =
|
|
635
|
-
|
|
636
|
-
process.env.PYRIC_PROJECT
|
|
637
|
-
|
|
638
|
-
|
|
694
|
+
functionsProjectId = resolveProjectIdentifier(
|
|
695
|
+
flagProject,
|
|
696
|
+
process.env.PYRIC_PROJECT,
|
|
697
|
+
pyricConfig.project,
|
|
698
|
+
rc?.projects?.default,
|
|
699
|
+
);
|
|
639
700
|
}
|
|
640
701
|
} catch (error) {
|
|
641
702
|
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
642
703
|
return 2;
|
|
643
704
|
}
|
|
644
|
-
|
|
645
|
-
|
|
705
|
+
|
|
706
|
+
const explicitCommand = resolveExplicitCommand(parsed);
|
|
707
|
+
|
|
708
|
+
const plan = resolveSandboxChild({
|
|
709
|
+
explicitCommand,
|
|
710
|
+
configCommand: pyricConfig.command,
|
|
646
711
|
noRun: Boolean(parsed.flags.get('no-run')),
|
|
647
712
|
json,
|
|
648
|
-
devScript: readDevScript(cwd),
|
|
649
|
-
packageManager: detectPackageManager(cwd),
|
|
650
713
|
});
|
|
651
714
|
const bridgeOn = bridgeEnabledFor(parsed.flags, plan, functionsProject);
|
|
652
715
|
|
|
@@ -663,6 +726,7 @@ export async function runServe(parsed: ParsedArgs): Promise<number> {
|
|
|
663
726
|
watch: parsed.flags.get('no-watch') ? false : undefined,
|
|
664
727
|
persist: Boolean(parsed.flags.get('persist')),
|
|
665
728
|
fresh: Boolean(parsed.flags.get('fresh')),
|
|
729
|
+
permissive: Boolean(parsed.flags.get('permissive')),
|
|
666
730
|
// --ui mounts the Pyric Studio storage routes (workspace + projects).
|
|
667
731
|
ui: uiOn,
|
|
668
732
|
// --no-capture disables the default-on session capture. The pattern
|
|
@@ -675,7 +739,7 @@ export async function runServe(parsed: ParsedArgs): Promise<number> {
|
|
|
675
739
|
project:
|
|
676
740
|
typeof parsed.flags.get('project') === 'string'
|
|
677
741
|
? (parsed.flags.get('project') as string)
|
|
678
|
-
: process.env.PYRIC_PROJECT ?? functionsProjectId ?? undefined,
|
|
742
|
+
: process.env.PYRIC_PROJECT ?? pyricConfig.project ?? functionsProjectId ?? undefined,
|
|
679
743
|
});
|
|
680
744
|
} catch (e) {
|
|
681
745
|
process.stderr.write(`${e instanceof Error ? e.message : String(e)}\n`);
|
|
@@ -691,7 +755,7 @@ export async function runServe(parsed: ParsedArgs): Promise<number> {
|
|
|
691
755
|
// (the banner went to stderr). Readiness probe: GET /__pyric/init.json.
|
|
692
756
|
if (json) process.stdout.write(serveJsonLine(runtime) + '\n');
|
|
693
757
|
|
|
694
|
-
//
|
|
758
|
+
// A host-only sandbox mounts no MCP bridge unless one is needed.
|
|
695
759
|
// so a `pyric mcp-proxy` in an editor cannot attach. Nudge toward --bridge.
|
|
696
760
|
if (!bridgeOn && !json) {
|
|
697
761
|
process.stderr.write(
|
|
@@ -714,7 +778,7 @@ export async function runServe(parsed: ParsedArgs): Promise<number> {
|
|
|
714
778
|
void openBrowser(targetUrl);
|
|
715
779
|
}
|
|
716
780
|
|
|
717
|
-
let devChild:
|
|
781
|
+
let devChild: SandboxChildHandle | null = null;
|
|
718
782
|
let functionsRuntime: FunctionsDevelopmentRuntime | null = null;
|
|
719
783
|
let resolveFunctionsExit!: (code: number) => void;
|
|
720
784
|
const functionsExited = new Promise<number>((resolve) => { resolveFunctionsExit = resolve; });
|
|
@@ -793,7 +857,7 @@ export async function runServe(parsed: ParsedArgs): Promise<number> {
|
|
|
793
857
|
if (result.kind === 'no-peer') {
|
|
794
858
|
process.stderr.write(
|
|
795
859
|
` ⚠ functions not started — no browser tab connected after 30s. ` +
|
|
796
|
-
`Open ${runtime.handle.url} and restart pyric
|
|
860
|
+
`Open ${runtime.handle.url} and restart pyric sandbox.\n`,
|
|
797
861
|
);
|
|
798
862
|
} else if (result.kind === 'failed') {
|
|
799
863
|
process.stderr.write(`${result.error.message}\n`);
|
|
@@ -810,16 +874,15 @@ export async function runServe(parsed: ParsedArgs): Promise<number> {
|
|
|
810
874
|
for (const unsupported of ready.unsupportedTriggers) {
|
|
811
875
|
process.stderr.write(
|
|
812
876
|
` ⚠ functions export ${unsupported.exportName} uses unsupported trigger ` +
|
|
813
|
-
`${unsupported.eventType}; it will not run in pyric
|
|
877
|
+
`${unsupported.eventType}; it will not run in pyric sandbox.\n`,
|
|
814
878
|
);
|
|
815
879
|
}
|
|
816
880
|
}
|
|
817
881
|
}
|
|
818
882
|
|
|
819
|
-
//
|
|
820
|
-
//
|
|
821
|
-
//
|
|
822
|
-
// host-only; --json defaults to host-only (explicit `--` still wins).
|
|
883
|
+
// Run the explicit or configured command with the sandbox environment.
|
|
884
|
+
// `--no-run` forces host-only. `--json` skips the configured command, but an
|
|
885
|
+
// explicit command still runs.
|
|
823
886
|
// (`plan` was resolved above so it could imply the bridge mount.)
|
|
824
887
|
if (plan) {
|
|
825
888
|
const info = json ? process.stderr : process.stdout;
|
|
@@ -838,11 +901,16 @@ export async function runServe(parsed: ParsedArgs): Promise<number> {
|
|
|
838
901
|
` ⚠ no browser tab connected after 30s — starting your command anyway; sandbox ops will fail until ${runtime.handle.url} is open.\n`,
|
|
839
902
|
);
|
|
840
903
|
}
|
|
904
|
+
} else if (!json) {
|
|
905
|
+
info.write(
|
|
906
|
+
` ⓘ Auto-open is disabled (--no-open/CI). The pyric sandbox is browser-resident: ` +
|
|
907
|
+
`open ${runtime.handle.url} to connect if your command performs Firebase operations.\n`,
|
|
908
|
+
);
|
|
841
909
|
}
|
|
842
910
|
info.write(
|
|
843
911
|
`✔ run \`${plan.label}\` — firebase-admin/firebase imports are routed to the sandbox at ${runtime.handle.url}\n`,
|
|
844
912
|
);
|
|
845
|
-
devChild =
|
|
913
|
+
devChild = spawnSandboxChild(plan, {
|
|
846
914
|
cwd,
|
|
847
915
|
json,
|
|
848
916
|
env: buildChildEnv(process.env, {
|
|
@@ -895,7 +963,7 @@ export async function runServe(parsed: ParsedArgs): Promise<number> {
|
|
|
895
963
|
if (functionsRuntime) {
|
|
896
964
|
void functionsExited.then((code) => {
|
|
897
965
|
if (!settled && !shuttingDown) {
|
|
898
|
-
process.stderr.write(`pyric
|
|
966
|
+
process.stderr.write(`pyric sandbox: Functions child exited unexpectedly (code ${code}).\n`);
|
|
899
967
|
finish(code === 0 ? 1 : code);
|
|
900
968
|
}
|
|
901
969
|
});
|
package/src/cli/snapshot.ts
CHANGED
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
* pyric snapshot [--out FILE] [--port N] [--force] [--json]
|
|
7
7
|
*
|
|
8
8
|
* Source preference:
|
|
9
|
-
* 1.
|
|
9
|
+
* 1. Live state from a running `pyric sandbox --persist`
|
|
10
10
|
* (`GET /__pyric/state` — `--port`, else the 3473+ scan window),
|
|
11
11
|
* 2. else the on-disk `.pyric/state/state.json`,
|
|
12
12
|
* 3. else exit 2 with a clear message.
|
|
13
13
|
*
|
|
14
14
|
* The output is a `PyricStateFile` envelope — directly re-servable:
|
|
15
|
-
* `pyric
|
|
15
|
+
* `pyric sandbox --seed <out>` (the state-file shape is detected by its
|
|
16
16
|
* `version` key and seeds docs + users).
|
|
17
17
|
*/
|
|
18
18
|
import { existsSync, writeFileSync } from 'node:fs';
|
|
@@ -44,7 +44,7 @@ async function fetchLive(port: number): Promise<LiveState | null> {
|
|
|
44
44
|
if (!body || typeof body !== 'object' || !('version' in body)) return null;
|
|
45
45
|
return { envelope: body, projectDir: res.headers.get('x-pyric-project-dir') };
|
|
46
46
|
} catch {
|
|
47
|
-
return null; //
|
|
47
|
+
return null; // No Pyric sandbox is listening. Fall through.
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
|
|
@@ -110,7 +110,7 @@ export async function runSnapshot(parsed: ParsedArgs, deps: SnapshotDeps = {}):
|
|
|
110
110
|
}
|
|
111
111
|
if (!envelope) {
|
|
112
112
|
err.write(
|
|
113
|
-
'pyric snapshot: no state found
|
|
113
|
+
'pyric snapshot: no state found. No `pyric sandbox --persist` is running here and ' +
|
|
114
114
|
'no .pyric/state/state.json exists. Run with --persist (and use the app) first.\n',
|
|
115
115
|
);
|
|
116
116
|
return 2;
|
|
@@ -158,7 +158,7 @@ export async function runSnapshot(parsed: ParsedArgs, deps: SnapshotDeps = {}):
|
|
|
158
158
|
if (redactedCount > 0) {
|
|
159
159
|
report.write(` ⓘ redacted ${redactedCount} password(s) — re-run with --include-passwords to keep them\n`);
|
|
160
160
|
}
|
|
161
|
-
report.write(` Re-serve it: pyric
|
|
161
|
+
report.write(` Re-serve it: pyric sandbox --seed ${typeof outFlag === 'string' ? outFlag : 'pyric-state.json'}\n`);
|
|
162
162
|
if (json) out.write(JSON.stringify({ out: outPath, docs, users, source, redactedPasswords: redactedCount }) + '\n');
|
|
163
163
|
return 0;
|
|
164
164
|
}
|
package/src/cli/verify.ts
CHANGED
|
@@ -108,7 +108,7 @@ export async function runVerify(parsed: ParsedArgs, deps: VerifyCliDeps = {}): P
|
|
|
108
108
|
} else {
|
|
109
109
|
process.stderr.write(
|
|
110
110
|
`pyric verify: no captured session at ${SERVE_CAPTURE_PATH}.\n` +
|
|
111
|
-
' Run `pyric
|
|
111
|
+
' Run `pyric sandbox`, exercise your app, then `pyric verify`, or pass a fixture path.\n',
|
|
112
112
|
);
|
|
113
113
|
}
|
|
114
114
|
return 2;
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
type FunctionsRtdbChildReady,
|
|
7
7
|
type SpawnFunctionsRtdbChildOptions,
|
|
8
8
|
} from './child.js';
|
|
9
|
-
import { buildChildEnv, createLinePrefixer } from '../cli/
|
|
9
|
+
import { buildChildEnv, createLinePrefixer } from '../cli/sandbox-runner.js';
|
|
10
10
|
|
|
11
11
|
const INITIAL_PEER_TIMEOUT_MS = 30_000;
|
|
12
12
|
const RELOAD_PEER_TIMEOUT_MS = 5_000;
|
|
@@ -22,7 +22,7 @@ export function discoverFunctionsRtdbProject(cwd: string): FunctionsRtdbProject
|
|
|
22
22
|
config = JSON.parse(readFileSync(firebaseJsonPath, 'utf8')) as FirebaseJson;
|
|
23
23
|
} catch (error) {
|
|
24
24
|
throw new Error(
|
|
25
|
-
`pyric
|
|
25
|
+
`pyric sandbox: failed to parse firebase.json while discovering Functions: ` +
|
|
26
26
|
`${error instanceof Error ? error.message : String(error)}`,
|
|
27
27
|
);
|
|
28
28
|
}
|
|
@@ -33,23 +33,23 @@ export function discoverFunctionsRtdbProject(cwd: string): FunctionsRtdbProject
|
|
|
33
33
|
: [config.functions as FunctionsConfig];
|
|
34
34
|
if (declared.length !== 1) {
|
|
35
35
|
throw new Error(
|
|
36
|
-
'pyric
|
|
36
|
+
'pyric sandbox: multiple Functions codebases are not supported by the first ' +
|
|
37
37
|
'onValueCreated slice; configure one source for this development session.',
|
|
38
38
|
);
|
|
39
39
|
}
|
|
40
40
|
const functions = declared[0];
|
|
41
41
|
if (typeof functions !== 'object' || functions === null) {
|
|
42
|
-
throw new Error('pyric
|
|
42
|
+
throw new Error('pyric sandbox: firebase.json `functions` must be an object with a source.');
|
|
43
43
|
}
|
|
44
44
|
if (functions.source !== undefined && typeof functions.source !== 'string') {
|
|
45
|
-
throw new Error('pyric
|
|
45
|
+
throw new Error('pyric sandbox: firebase.json `functions.source` must be a string.');
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
const sourceDir = resolve(cwd, functions.source ?? 'functions');
|
|
49
49
|
const packageJsonPath = join(sourceDir, 'package.json');
|
|
50
50
|
if (!existsSync(packageJsonPath)) {
|
|
51
51
|
throw new Error(
|
|
52
|
-
`pyric
|
|
52
|
+
`pyric sandbox: Functions source is declared but ${packageJsonPath} does not exist.`,
|
|
53
53
|
);
|
|
54
54
|
}
|
|
55
55
|
|
|
@@ -58,17 +58,17 @@ export function discoverFunctionsRtdbProject(cwd: string): FunctionsRtdbProject
|
|
|
58
58
|
packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8')) as { main?: unknown };
|
|
59
59
|
} catch (error) {
|
|
60
60
|
throw new Error(
|
|
61
|
-
`pyric
|
|
61
|
+
`pyric sandbox: failed to parse ${packageJsonPath}: ` +
|
|
62
62
|
`${error instanceof Error ? error.message : String(error)}`,
|
|
63
63
|
);
|
|
64
64
|
}
|
|
65
65
|
if (packageJson.main !== undefined && typeof packageJson.main !== 'string') {
|
|
66
|
-
throw new Error(`pyric
|
|
66
|
+
throw new Error(`pyric sandbox: ${packageJsonPath} \`main\` must be a string.`);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
const entry = resolve(sourceDir, packageJson.main ?? 'index.js');
|
|
70
70
|
if (!existsSync(entry) || !statSync(entry).isFile()) {
|
|
71
|
-
throw new Error(`pyric
|
|
71
|
+
throw new Error(`pyric sandbox: Functions entry does not exist: ${entry}`);
|
|
72
72
|
}
|
|
73
73
|
return { sourceDir, entry };
|
|
74
74
|
}
|
package/src/next/guard.ts
CHANGED
|
@@ -54,7 +54,7 @@ export function enforceSandboxGuard(options?: PyricNextOptions): void {
|
|
|
54
54
|
const errorMessage =
|
|
55
55
|
'[Pyric] Next.js development server started without active Pyric sandbox environment variables.\n' +
|
|
56
56
|
'To prevent accidental connections to production Firebase databases, launch this server using:\n' +
|
|
57
|
-
' pyric
|
|
57
|
+
' pyric sandbox -- next dev\n' +
|
|
58
58
|
'or source environment keys via `pyric env`.\n' +
|
|
59
59
|
'To disable this guard, pass { guard: false } to withPyric(config, options).';
|
|
60
60
|
throw new Error(errorMessage);
|
package/src/remote/index.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Server-side Node code (ultimately `pyric-admin`'s remote dispatch arm)
|
|
6
6
|
* reaches the ONE sandbox the app + Studio + agent share:
|
|
7
7
|
*
|
|
8
|
-
* Node process
|
|
8
|
+
* Node process -> `pyric sandbox --bridge` -> browser tab
|
|
9
9
|
* (attach/consumer leg) (peer leg)
|
|
10
10
|
* ──MessagePort──> SharedWorker
|
|
11
11
|
*
|
|
@@ -320,7 +320,7 @@ export function createRemoteSandboxCore(
|
|
|
320
320
|
reject(
|
|
321
321
|
remoteError(
|
|
322
322
|
'deadline-exceeded',
|
|
323
|
-
`remote sandbox op timed out after ${opTimeoutMs}ms (op: ${payload.method})
|
|
323
|
+
`remote sandbox op timed out after ${opTimeoutMs}ms (op: ${payload.method}). Is pyric sandbox still running?` +
|
|
324
324
|
// A version-skewed old worker accepts frames it cannot
|
|
325
325
|
// handle and never responds — a timeout is its signature.
|
|
326
326
|
(versionSkewGuidance ? ` ${versionSkewGuidance}` : ''),
|
|
@@ -379,8 +379,8 @@ export function createRemoteSandboxCore(
|
|
|
379
379
|
msg.serveVersion !== cliVersion()
|
|
380
380
|
) {
|
|
381
381
|
versionSkewGuidance =
|
|
382
|
-
`pyric
|
|
383
|
-
`${cliVersion()}
|
|
382
|
+
`pyric sandbox is running version ${msg.serveVersion}, this client is ` +
|
|
383
|
+
`${cliVersion()}. Restart pyric sandbox and reload the browser tab.`;
|
|
384
384
|
process.stderr.write(`pyric: ${versionSkewGuidance}\n`);
|
|
385
385
|
}
|
|
386
386
|
if (msg.peerConnected) readyResolve();
|
|
@@ -405,7 +405,7 @@ export function createRemoteSandboxCore(
|
|
|
405
405
|
// Version skew: a live tab whose SharedWorker predates this op.
|
|
406
406
|
// Other open pages of this origin keep the old worker alive.
|
|
407
407
|
message +=
|
|
408
|
-
'
|
|
408
|
+
'. The running sandbox may predate this feature. Restart pyric sandbox ' +
|
|
409
409
|
'and close other open pages of this origin, then reload.';
|
|
410
410
|
}
|
|
411
411
|
call.reject(remoteError(code, message, msg.error?.denialContext, (msg.error as any)?.envelope));
|
|
@@ -721,7 +721,7 @@ export function createRemoteSandboxHandle(opts: {
|
|
|
721
721
|
enablePersistence: () =>
|
|
722
722
|
throwMember(
|
|
723
723
|
'enablePersistence',
|
|
724
|
-
'the browser worker owns persistence; it is already enabled by `pyric
|
|
724
|
+
'the browser worker owns persistence; it is already enabled by `pyric sandbox`',
|
|
725
725
|
),
|
|
726
726
|
flush: () =>
|
|
727
727
|
throwMember('flush()', 'the browser worker owns persistence and flushes itself'),
|
|
@@ -742,7 +742,7 @@ export function createRemoteSandboxHandle(opts: {
|
|
|
742
742
|
// ─── connect ───────────────────────────────────────────────────────────────
|
|
743
743
|
|
|
744
744
|
/**
|
|
745
|
-
* Discover the running `pyric
|
|
745
|
+
* Discover the running `pyric sandbox --bridge`, attach to its bridge WS as a
|
|
746
746
|
* worker-relay CONSUMER (never a peer — attaching cannot kick the browser
|
|
747
747
|
* tab out of last-connection-wins), and return the typed remote handle.
|
|
748
748
|
*
|
|
@@ -770,7 +770,7 @@ export async function connectRemoteSandbox(
|
|
|
770
770
|
if (!found) {
|
|
771
771
|
throw remoteError(
|
|
772
772
|
'not-found',
|
|
773
|
-
'no running `pyric
|
|
773
|
+
'no running `pyric sandbox --bridge` found (looked for .pyric/serve.json in ' +
|
|
774
774
|
`${cwd} and the default ports) — start your dev server with the bridge enabled and retry.`,
|
|
775
775
|
);
|
|
776
776
|
}
|
|
@@ -953,7 +953,7 @@ export function createLazyRemoteSandbox(
|
|
|
953
953
|
// Before discovery the URL is unknown; op-level errors always carry the
|
|
954
954
|
// real URL (they come from the inner connect), and `serveUrl` is patched
|
|
955
955
|
// to the discovered value as soon as the first connect succeeds.
|
|
956
|
-
serveUrl: options.url?.replace(/\/$/, '') ?? '<pyric
|
|
956
|
+
serveUrl: options.url?.replace(/\/$/, '') ?? '<pyric sandbox url (pending discovery)>',
|
|
957
957
|
close() {
|
|
958
958
|
if (closed) return;
|
|
959
959
|
closed = true;
|