@pyric/cli 0.1.0-alpha.18 → 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 +14 -9
- package/dist/bridge/server/mcp-contract.d.ts +2 -2
- package/dist/bridge/server/mcp-contract.d.ts.map +1 -1
- package/dist/bridge/server/mcp-contract.js +3 -0
- package/dist/bridge/server/mcp-contract.js.map +1 -1
- package/dist/cli/index.d.ts +3 -2
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +51 -42
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/mcp-proxy.d.ts.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/rules.d.ts +1 -0
- package/dist/cli/rules.d.ts.map +1 -1
- package/dist/cli/rules.js +18 -4
- package/dist/cli/rules.js.map +1 -1
- 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/service-command-records/storage-rules-resolve.d.ts +4 -0
- package/dist/cli/service-command-records/storage-rules-resolve.d.ts.map +1 -0
- package/dist/cli/service-command-records/storage-rules-resolve.js +3 -0
- package/dist/cli/service-command-records/storage-rules-resolve.js.map +1 -0
- package/dist/cli/service-commands.generated.d.ts +3 -0
- package/dist/cli/service-commands.generated.d.ts.map +1 -1
- package/dist/cli/service-commands.generated.js +2 -0
- package/dist/cli/service-commands.generated.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 +3 -3
- package/dist/serve/rules.d.ts.map +1 -1
- package/dist/serve/rules.js +56 -25
- 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.CzwHdCi9.js → RulesCodeEditor.XtDmHOlN.js} +1 -1
- package/dist/serve/site-ui/_astro/_example_.B9DG9HLJ.css +1 -0
- 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.B72XYeoh.js +354 -0
- package/dist/serve/site-ui/_astro/{example-runtime.B0byNQOl.js → example-runtime.C25_lx6Z.js} +2 -26
- 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.DEpnKg-O.js +1169 -0
- package/dist/serve/site-ui/_astro/seed.Co0yUJY5.js +20 -0
- package/dist/serve/site-ui/_astro/writes.rpjrFzmu.js +102 -0
- 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 +45 -17
- package/dist/serve/site-ui/docs/agent/set-up-your-agent.md +20 -17
- 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 +34 -53
- package/dist/serve/site-ui/docs/build/cloud-firestore.md +40 -36
- 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 +649 -250
- package/dist/serve/site-ui/docs/examples/chess.md +45 -16
- package/dist/serve/site-ui/docs/examples/index.html +6 -3
- 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 +41 -10
- package/dist/serve/site-ui/docs/get-started/start-building.md +21 -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 +203 -168
- 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-rules-source.d.ts.map +1 -1
- package/dist/serve/vite-rules-source.js +28 -7
- package/dist/serve/vite-rules-source.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/bridge/server/mcp-contract.ts +3 -0
- package/src/cli/cli.test.ts +2 -2
- package/src/cli/index.ts +51 -42
- package/src/cli/mcp-proxy.ts +10 -11
- package/src/cli/parse-args.ts +61 -15
- package/src/cli/pyric-config.ts +45 -0
- package/src/cli/rules.ts +31 -4
- package/src/cli/{dev-runner.ts → sandbox-runner.ts} +117 -72
- package/src/cli/serve.ts +115 -47
- package/src/cli/service-command-records/storage-rules-resolve.ts +4 -0
- package/src/cli/service-commands.generated.ts +2 -0
- 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 +61 -26
- 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-rules-source.ts +29 -7
- 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 -141
- 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/_example_.jevZ7OdV.css +0 -1
- package/dist/serve/site-ui/_astro/_slug_.C6QfVOxJ.css +0 -1
- package/dist/serve/site-ui/_astro/_slug_.CXh9yTP1.css +0 -1
- package/dist/serve/site-ui/_astro/_studio_.B6b_G1ev.css +0 -1
- package/dist/serve/site-ui/_astro/app.B7pBzz5N.js +0 -11
- package/dist/serve/site-ui/_astro/app.BsFsMjn0.css +0 -1
- package/dist/serve/site-ui/_astro/app.boex5SnQ.js +0 -1
- package/dist/serve/site-ui/_astro/chess-showcase.DDqE-QGe.js +0 -1
- package/dist/serve/site-ui/_astro/chess-source-explorer.C5k9c5yK.js +0 -1
- package/dist/serve/site-ui/_astro/chess-v2.CFHmd3UL.js +0 -354
- package/dist/serve/site-ui/_astro/index.uwPvjH_1.js +0 -1
- package/dist/serve/site-ui/_astro/jsx-runtime.D_zvdyIk.js +0 -9
- package/dist/serve/site-ui/_astro/reads.Dh3z0fJy.js +0 -1
- package/dist/serve/site-ui/_astro/sandbox-controls.C2IqtoSw.js +0 -1252
- package/dist/serve/site-ui/_astro/seed.CCmj00su.js +0 -20
- 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/dist/serve/site-ui/examples/firestore-first-write/index.html +0 -2
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The `pyric
|
|
2
|
+
* The `pyric sandbox` child runner starts the configured application command
|
|
3
|
+
* after the host is ready:
|
|
3
4
|
* after the host is up, run the user's OWN dev command with the environment
|
|
4
5
|
* activated so their unchanged firebase-admin/firebase imports resolve to the
|
|
5
6
|
* pyric sandbox:
|
|
@@ -7,23 +8,20 @@
|
|
|
7
8
|
* PYRIC_SANDBOX=remote:<serve url> (the activator)
|
|
8
9
|
* NODE_OPTIONS += --import @pyric/cli/register (the substitution seam)
|
|
9
10
|
*
|
|
10
|
-
* Child-command precedence:
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* defaults to host-only (an explicit `--` still wins — agents can opt in).
|
|
11
|
+
* Child-command precedence: `--no-run`, an explicit command, `--json`, the
|
|
12
|
+
* `pyric.json` command, then host-only. An explicit command still runs with
|
|
13
|
+
* `--json`.
|
|
14
14
|
*
|
|
15
15
|
* Everything decision-shaped here is pure and exported for the unit suite;
|
|
16
16
|
* only `spawnDevChild` touches the process table.
|
|
17
17
|
*/
|
|
18
18
|
import { spawn, type ChildProcess } from 'node:child_process';
|
|
19
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
20
|
-
import { join } from 'node:path';
|
|
21
19
|
|
|
22
20
|
// ─── First-run race guard ───────────────────────────────────────────────────
|
|
23
21
|
|
|
24
22
|
/**
|
|
25
23
|
* Wait (bounded) for a browser tab to register as the bridge's sandbox peer
|
|
26
|
-
* before the child spawns. `pyric
|
|
24
|
+
* before the child spawns. `pyric sandbox` opens the tab and spawns the child in
|
|
27
25
|
* the same breath; a child whose first line is a sandbox op otherwise races
|
|
28
26
|
* the tab's boot and dies on the no-tab fail-fast — the exact first-run of
|
|
29
27
|
* the two-command story. Polls `/__pyric/health` (`sandboxConnected`).
|
|
@@ -63,65 +61,74 @@ export async function waitForSandboxPeer(
|
|
|
63
61
|
|
|
64
62
|
// ─── Child-command resolution (pure) ───────────────────────────────────────
|
|
65
63
|
|
|
66
|
-
export
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if (existsSync(join(cwd, 'pnpm-lock.yaml'))) return 'pnpm';
|
|
72
|
-
if (existsSync(join(cwd, 'yarn.lock'))) return 'yarn';
|
|
73
|
-
return 'npm';
|
|
64
|
+
export interface SandboxChildPlan {
|
|
65
|
+
/** argv to spawn — argv[0] is the executable. */
|
|
66
|
+
argv: string[];
|
|
67
|
+
/** Human-readable command for the pre-spawn line. */
|
|
68
|
+
label: string;
|
|
74
69
|
}
|
|
75
70
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
71
|
+
export type DevChildPlan = SandboxChildPlan;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Tokenize a command string into an argv array, preserving quoted strings.
|
|
75
|
+
*/
|
|
76
|
+
export function parseCommandString(cmd: string): string[] {
|
|
77
|
+
const parts: string[] = [];
|
|
78
|
+
const regex = /[^\s"']+|"([^"]*)"|'([^']*)'/g;
|
|
79
|
+
let match: RegExpExecArray | null;
|
|
80
|
+
while ((match = regex.exec(cmd)) !== null) {
|
|
81
|
+
if (match[1] !== undefined) {
|
|
82
|
+
parts.push(match[1]);
|
|
83
|
+
} else if (match[2] !== undefined) {
|
|
84
|
+
parts.push(match[2]);
|
|
85
|
+
} else {
|
|
86
|
+
parts.push(match[0]);
|
|
87
|
+
}
|
|
86
88
|
}
|
|
89
|
+
return parts;
|
|
87
90
|
}
|
|
88
91
|
|
|
89
|
-
export interface
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
export interface ResolveSandboxChildOptions {
|
|
93
|
+
explicitCommand?: string[] | null;
|
|
94
|
+
passthrough?: string[];
|
|
95
|
+
configCommand?: string | null;
|
|
96
|
+
noRun?: boolean;
|
|
97
|
+
json?: boolean;
|
|
94
98
|
}
|
|
95
99
|
|
|
96
100
|
/**
|
|
97
101
|
* Decide what (if anything) to run after the host is up.
|
|
98
102
|
*
|
|
99
|
-
*
|
|
100
|
-
* --no-run
|
|
101
|
-
*
|
|
102
|
-
* --json (no
|
|
103
|
-
*
|
|
104
|
-
* none of the above
|
|
105
|
-
*
|
|
106
|
-
* A `dev` script that itself invokes `pyric dev` is treated as absent —
|
|
107
|
-
* running it would recurse forever (pyric dev → npm run dev → pyric dev …).
|
|
103
|
+
* Precedence:
|
|
104
|
+
* --no-run → null, always
|
|
105
|
+
* explicit CLI command (args or `--`) → that command (wins over everything else)
|
|
106
|
+
* --json (with no explicit command) → null (machine mode defaults to host-only)
|
|
107
|
+
* pyric.json `command` → parsed command argv
|
|
108
|
+
* none of the above → null (host-only)
|
|
108
109
|
*/
|
|
109
|
-
export function
|
|
110
|
-
passthrough: string[];
|
|
111
|
-
noRun: boolean;
|
|
112
|
-
json: boolean;
|
|
113
|
-
devScript: string | null;
|
|
114
|
-
packageManager: PackageManager;
|
|
115
|
-
}): DevChildPlan | null {
|
|
110
|
+
export function resolveSandboxChild(opts: ResolveSandboxChildOptions): SandboxChildPlan | null {
|
|
116
111
|
if (opts.noRun) return null;
|
|
117
|
-
|
|
118
|
-
|
|
112
|
+
|
|
113
|
+
let activeCommand: string[] | null = null;
|
|
114
|
+
if (opts.explicitCommand && opts.explicitCommand.length > 0) {
|
|
115
|
+
activeCommand = opts.explicitCommand;
|
|
116
|
+
} else if (opts.passthrough && opts.passthrough.length > 0) {
|
|
117
|
+
activeCommand = opts.passthrough;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (activeCommand !== null) {
|
|
121
|
+
return { argv: [...activeCommand], label: activeCommand.join(' ') };
|
|
119
122
|
}
|
|
123
|
+
|
|
120
124
|
if (opts.json) return null;
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
125
|
+
|
|
126
|
+
if (opts.configCommand && opts.configCommand.trim().length > 0) {
|
|
127
|
+
const trimmed = opts.configCommand.trim();
|
|
128
|
+
const argv = parseCommandString(trimmed);
|
|
129
|
+
return { argv, label: trimmed };
|
|
124
130
|
}
|
|
131
|
+
|
|
125
132
|
return null;
|
|
126
133
|
}
|
|
127
134
|
|
|
@@ -215,37 +222,73 @@ export function createLinePrefixer(
|
|
|
215
222
|
|
|
216
223
|
// ─── Spawn + lifecycle ─────────────────────────────────────────────────────
|
|
217
224
|
|
|
218
|
-
|
|
219
|
-
|
|
225
|
+
/** How long a signalled child may linger before SIGKILL. */
|
|
226
|
+
const FORCE_KILL_AFTER_MS = 2_000;
|
|
227
|
+
|
|
228
|
+
const SHELL_OPERATORS = new Set(['&&', '||', '|', ';', '&']);
|
|
229
|
+
|
|
230
|
+
export interface SandboxChildHandle {
|
|
220
231
|
exited: Promise<number>;
|
|
221
|
-
/** Forward a signal (Ctrl-C / SIGTERM). Marks the exit as user-initiated
|
|
222
|
-
* so the propagated code is 0, and SIGKILLs a child that lingers. */
|
|
223
232
|
signal(sig: NodeJS.Signals): void;
|
|
224
233
|
child: ChildProcess;
|
|
225
234
|
}
|
|
226
235
|
|
|
227
|
-
|
|
228
|
-
const FORCE_KILL_AFTER_MS = 2_000;
|
|
236
|
+
export type DevChildHandle = SandboxChildHandle;
|
|
229
237
|
|
|
230
238
|
/**
|
|
231
239
|
* Spawn the child with inherited stdin and `[dev]`-prefixed stdout/stderr.
|
|
232
240
|
* In `--json` mode ALL child output goes to stderr — stdout carries exactly
|
|
233
241
|
* the one machine line (the serve --json contract).
|
|
234
242
|
*/
|
|
235
|
-
export function
|
|
236
|
-
plan:
|
|
243
|
+
export function spawnSandboxChild(
|
|
244
|
+
plan: SandboxChildPlan,
|
|
237
245
|
opts: { cwd: string; env: NodeJS.ProcessEnv; json: boolean },
|
|
238
|
-
):
|
|
239
|
-
const
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
246
|
+
): SandboxChildHandle {
|
|
247
|
+
const parts = [...plan.argv];
|
|
248
|
+
const env: NodeJS.ProcessEnv = { ...opts.env };
|
|
249
|
+
|
|
250
|
+
// 1. Extract leading KEY=VAL assignments (e.g. PORT=8080 node server.js)
|
|
251
|
+
while (parts.length > 0 && /^[A-Za-z_][A-Za-z0-9_]*=/.test(parts[0]!)) {
|
|
252
|
+
const token = parts.shift()!;
|
|
253
|
+
const eq = token.indexOf('=');
|
|
254
|
+
const key = token.slice(0, eq);
|
|
255
|
+
const val = token.slice(eq + 1);
|
|
256
|
+
env[key] = val;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// 2. Check for discrete shell operators (&&, ||, |, ;) among top-level tokens
|
|
260
|
+
const hasShellOperators = parts.some((token) => SHELL_OPERATORS.has(token));
|
|
261
|
+
|
|
262
|
+
// 3. Warn if runtime is bun or deno
|
|
263
|
+
if (parts.length > 0 && (parts[0] === 'bun' || parts[0] === 'deno')) {
|
|
264
|
+
const runner = parts[0];
|
|
265
|
+
const target = opts.json ? process.stderr : process.stdout;
|
|
266
|
+
target.write(
|
|
267
|
+
` ⚠ ${runner} detected: pyric intercepts Firebase imports via Node.js module loader hooks (NODE_OPTIONS="--import ..."). ` +
|
|
268
|
+
`${runner} does not evaluate Node loader hooks — SDK calls in this process will not reach the pyric sandbox. ` +
|
|
269
|
+
`Use \`node\` (or \`npx tsx\`) to run with the sandbox swap.\n`,
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const [command, ...args] = parts;
|
|
274
|
+
let child: ChildProcess;
|
|
275
|
+
if (hasShellOperators) {
|
|
276
|
+
child = spawn(plan.label, [], {
|
|
277
|
+
cwd: opts.cwd,
|
|
278
|
+
env,
|
|
279
|
+
stdio: ['inherit', 'pipe', 'pipe'],
|
|
280
|
+
shell: true,
|
|
281
|
+
detached: process.platform !== 'win32',
|
|
282
|
+
});
|
|
283
|
+
} else {
|
|
284
|
+
child = spawn(command!, args, {
|
|
285
|
+
cwd: opts.cwd,
|
|
286
|
+
env,
|
|
287
|
+
stdio: ['inherit', 'pipe', 'pipe'],
|
|
288
|
+
shell: process.platform === 'win32',
|
|
289
|
+
detached: process.platform !== 'win32',
|
|
290
|
+
});
|
|
291
|
+
}
|
|
249
292
|
|
|
250
293
|
const outTarget = opts.json ? process.stderr : process.stdout;
|
|
251
294
|
const out = createLinePrefixer('[dev] ', (line) => outTarget.write(line));
|
|
@@ -267,7 +310,7 @@ export function spawnDevChild(
|
|
|
267
310
|
|
|
268
311
|
const exited = new Promise<number>((resolve) => {
|
|
269
312
|
child.once('error', (e) => {
|
|
270
|
-
process.stderr.write(`pyric
|
|
313
|
+
process.stderr.write(`pyric sandbox: failed to run \`${plan.label}\`: ${e.message}\n`);
|
|
271
314
|
resolve(1);
|
|
272
315
|
});
|
|
273
316
|
child.once('exit', (code, signal) => {
|
|
@@ -301,3 +344,5 @@ export function spawnDevChild(
|
|
|
301
344
|
},
|
|
302
345
|
};
|
|
303
346
|
}
|
|
347
|
+
|
|
348
|
+
export const spawnDevChild = spawnSandboxChild;
|
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
|
});
|