@runeditrun/rer 0.0.0-stage → 0.1.1
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/LICENSE +21 -0
- package/README.md +157 -2
- package/bin/rer.mjs +3 -0
- package/cli/README.md +243 -0
- package/cli/adapter.mjs +10 -0
- package/cli/ai-local.mjs +65 -0
- package/cli/archive-path.mjs +52 -0
- package/cli/archive-vite.mjs +55 -0
- package/cli/archive.mjs +8 -0
- package/cli/auth-copy.mjs +80 -0
- package/cli/bounded.mjs +70 -0
- package/cli/cache-doctor.mjs +63 -0
- package/cli/container-transfer.mjs +35 -0
- package/cli/container.mjs +62 -0
- package/cli/contract.mjs +25 -0
- package/cli/dependencies.mjs +45 -0
- package/cli/dependency-approval.mjs +33 -0
- package/cli/dependency-doctor.mjs +34 -0
- package/cli/dependency-input.mjs +43 -0
- package/cli/deploy-config.mjs +118 -0
- package/cli/deploy.mjs +37 -0
- package/cli/dev-identity.mjs +80 -0
- package/cli/errors.mjs +14 -0
- package/cli/export.mjs +42 -0
- package/cli/font-cache.mjs +29 -0
- package/cli/hosted-output.mjs +110 -0
- package/cli/independent.mjs +29 -0
- package/cli/inspect.mjs +174 -0
- package/cli/jsonc.mjs +111 -0
- package/cli/lifecycle.mjs +148 -0
- package/cli/local.mjs +100 -0
- package/cli/main.mjs +57 -0
- package/cli/managed-server.mjs +143 -0
- package/cli/node-runtime-cache.mjs +68 -0
- package/cli/node-runtime-discovery.mjs +56 -0
- package/cli/node-runtime-download.mjs +59 -0
- package/cli/node-runtime.mjs +50 -0
- package/cli/output.mjs +22 -0
- package/cli/parse.mjs +77 -0
- package/cli/port.mjs +52 -0
- package/cli/portability-worker.mjs +56 -0
- package/cli/portability.mjs +66 -0
- package/cli/preview-cache.mjs +99 -0
- package/cli/project.mjs +48 -0
- package/cli/release.mjs +24 -0
- package/cli/runtime-front-door.mjs +46 -0
- package/cli/scheduled.mjs +81 -0
- package/cli/server-activity.mjs +12 -0
- package/cli/server-proxy.mjs +93 -0
- package/cli/server-registry.mjs +182 -0
- package/cli/server-resources.mjs +22 -0
- package/cli/settings.mjs +15 -0
- package/cli/setup-answers.mjs +29 -0
- package/cli/setup-input.mjs +113 -0
- package/cli/shared.mjs +7 -0
- package/cli/transfers.mjs +33 -0
- package/cli/version.mjs +36 -0
- package/composition/git-source.mjs +111 -0
- package/edition-authoring/files.mjs +81 -0
- package/edition-authoring/manifest.mjs +65 -0
- package/global/README.md +158 -0
- package/global/checkout.mjs +72 -0
- package/global/cli/command.mjs +13 -0
- package/global/cli/install.mjs +75 -0
- package/global/cli/layout.mjs +45 -0
- package/global/cli/status.mjs +54 -0
- package/global/cli/uninstall.mjs +36 -0
- package/global/delegate.mjs +13 -0
- package/global/launch/capacity.mjs +72 -0
- package/global/launch/dev.mjs +117 -0
- package/global/launch/facts.mjs +94 -0
- package/global/launch/failure.mjs +5 -0
- package/global/launch/local-defaults.mjs +4 -0
- package/global/launch/prepare.mjs +63 -0
- package/global/launch/render.mjs +44 -0
- package/global/launch/state.mjs +106 -0
- package/global/launch/steps.mjs +90 -0
- package/global/launch/stop.mjs +89 -0
- package/global/launch/worker.mjs +62 -0
- package/global/list.mjs +59 -0
- package/global/main.mjs +53 -0
- package/global/package-info.mjs +38 -0
- package/global/registry.mjs +24 -0
- package/global/rer.mjs +29 -0
- package/global/update/archive.mjs +89 -0
- package/global/update/command.mjs +90 -0
- package/global/update/latest.mjs +6 -0
- package/global/update/notice.mjs +58 -0
- package/global/update/registry.mjs +47 -0
- package/global/version.mjs +29 -0
- package/install.sh +144 -0
- package/modules/ai/LICENSE +21 -0
- package/modules/ai/README.md +90 -0
- package/modules/ai/client.mjs +71 -0
- package/modules/ai/config.mjs +39 -0
- package/modules/ai/dev/index.d.mts +6 -0
- package/modules/ai/dev/index.mjs +64 -0
- package/modules/ai/dev/outbox.mjs +33 -0
- package/modules/ai/errors.mjs +28 -0
- package/modules/ai/fake/index.d.mts +3 -0
- package/modules/ai/fake/index.mjs +82 -0
- package/modules/ai/index.d.mts +10 -0
- package/modules/ai/index.mjs +3 -0
- package/modules/ai/package.json +6 -0
- package/modules/container/README.md +156 -0
- package/modules/container/adapters/d1.mjs +34 -0
- package/modules/container/adapters/egress.mjs +40 -0
- package/modules/container/adapters/jobs.mjs +47 -0
- package/modules/container/adapters/objects.mjs +84 -0
- package/modules/container/adapters/retained.mjs +14 -0
- package/modules/container/adapters/sqlite-error.mjs +7 -0
- package/modules/container/adapters/sqlite-worker.mjs +26 -0
- package/modules/container/assets.mjs +14 -0
- package/modules/container/bindings.mjs +1 -0
- package/modules/container/boundary.mjs +38 -0
- package/modules/container/build.mjs +65 -0
- package/modules/container/deploy-input.mjs +17 -0
- package/modules/container/entrypoint.sh +5 -0
- package/modules/container/host.mjs +43 -0
- package/modules/container/ingress.mjs +23 -0
- package/modules/container/lock.mjs +11 -0
- package/modules/container/migrations.mjs +16 -0
- package/modules/container/node-engine.mjs +39 -0
- package/modules/container/portability-schema.mjs +14 -0
- package/modules/container/remote/deploy.py +76 -0
- package/modules/container/remote/files.py +275 -0
- package/modules/container/remote/resources.py +157 -0
- package/modules/container/runner.mjs +10 -0
- package/modules/container/ssh-plan.mjs +44 -0
- package/modules/container/ssh.mjs +63 -0
- package/modules/container/transfer.mjs +27 -0
- package/modules/container/worker-engine.mjs +32 -0
- package/modules/dependencies/README.md +211 -0
- package/modules/dependencies/approval.mjs +51 -0
- package/modules/dependencies/auth.mjs +40 -0
- package/modules/dependencies/authority.mjs +14 -0
- package/modules/dependencies/crypto.mjs +13 -0
- package/modules/dependencies/index.d.mts +23 -0
- package/modules/dependencies/index.mjs +113 -0
- package/modules/dependencies/logout.mjs +42 -0
- package/modules/dependencies/native.mjs +9 -0
- package/modules/dependencies/protocol.mjs +23 -0
- package/modules/dependencies/schema/approval-attempt.sql +3 -0
- package/modules/dependencies/schema/dependencies.sql +15 -0
- package/modules/dependencies/store.mjs +19 -0
- package/modules/deploy-kit/README.md +308 -0
- package/modules/deploy-kit/artifact.mjs +47 -0
- package/modules/deploy-kit/build-modules.mjs +21 -0
- package/modules/deploy-kit/check.mjs +13 -0
- package/modules/deploy-kit/cli.mjs +50 -0
- package/modules/deploy-kit/container-config.mjs +8 -0
- package/modules/deploy-kit/default-stage.mjs +14 -0
- package/modules/deploy-kit/deploy.mjs +80 -0
- package/modules/deploy-kit/health-request.mjs +86 -0
- package/modules/deploy-kit/health.mjs +99 -0
- package/modules/deploy-kit/index.mjs +12 -0
- package/modules/deploy-kit/local-only.mjs +10 -0
- package/modules/deploy-kit/migrations.mjs +48 -0
- package/modules/deploy-kit/owner-cli.mjs +63 -0
- package/modules/deploy-kit/owner.mjs +297 -0
- package/modules/deploy-kit/prepare.mjs +62 -0
- package/modules/deploy-kit/provider.mjs +99 -0
- package/modules/deploy-kit/queue-consumer.mjs +10 -0
- package/modules/deploy-kit/receipt.mjs +16 -0
- package/modules/deploy-kit/render.mjs +47 -0
- package/modules/deploy-kit/resources.mjs +106 -0
- package/modules/deploy-kit/service-grants.mjs +25 -0
- package/modules/deploy-kit/sql-safety.mjs +94 -0
- package/modules/deploy-kit/sql-tokens.mjs +49 -0
- package/modules/deploy-kit/teardown.mjs +100 -0
- package/modules/deploy-kit/templates.mjs +31 -0
- package/modules/deploy-kit/validation.mjs +58 -0
- package/modules/deploy-kit/worker.mjs +105 -0
- package/modules/erasure/CONTRACT.md +90 -0
- package/modules/erasure/README.md +221 -0
- package/modules/erasure/audit.mjs +23 -0
- package/modules/erasure/blobs.mjs +45 -0
- package/modules/erasure/cascade.mjs +91 -0
- package/modules/erasure/completion.mjs +62 -0
- package/modules/erasure/declaration.mjs +51 -0
- package/modules/erasure/fence.mjs +19 -0
- package/modules/erasure/index.d.mts +62 -0
- package/modules/erasure/index.mjs +85 -0
- package/modules/erasure/order.mjs +56 -0
- package/modules/erasure/plan.mjs +121 -0
- package/modules/erasure/rate.mjs +57 -0
- package/modules/erasure/receipt.mjs +32 -0
- package/modules/erasure/root.mjs +21 -0
- package/modules/erasure/schema/erasure-rates.sql +5 -0
- package/modules/erasure/schema/erasure.sql +27 -0
- package/modules/erasure/scope.mjs +29 -0
- package/modules/erasure/sql.mjs +23 -0
- package/modules/erasure/verification.mjs +69 -0
- package/modules/integrations/core/CONTRACT.md +50 -0
- package/modules/integrations/core/README.md +27 -0
- package/modules/integrations/core/crypto.ts +19 -0
- package/modules/integrations/core/database.ts +21 -0
- package/modules/integrations/core/delivery.ts +97 -0
- package/modules/integrations/core/destination.ts +13 -0
- package/modules/integrations/core/index.ts +7 -0
- package/modules/integrations/core/node-transport.ts +72 -0
- package/modules/integrations/core/public-address.ts +57 -0
- package/modules/integrations/core/repository.ts +74 -0
- package/modules/integrations/core/schema.sql +36 -0
- package/modules/integrations/core/sqlite.ts +20 -0
- package/modules/integrations/core/timestamped-body-signature.ts +38 -0
- package/modules/integrations/core/tokens.ts +71 -0
- package/modules/integrations/core/types.ts +52 -0
- package/modules/integrations/core/upgrade-token-usage-context.sql +5 -0
- package/modules/integrations/core/webhook.ts +73 -0
- package/modules/job-queue/README.md +173 -0
- package/modules/job-queue/cli.mjs +64 -0
- package/modules/job-queue/command.mjs +64 -0
- package/modules/job-queue/config.mjs +48 -0
- package/modules/job-queue/identity.mjs +48 -0
- package/modules/job-queue/index.mjs +135 -0
- package/modules/job-queue/membership.mjs +30 -0
- package/modules/job-queue/module.json +10 -0
- package/modules/job-queue/policy.mjs +29 -0
- package/modules/job-queue/processes.mjs +80 -0
- package/modules/job-queue/resources.mjs +37 -0
- package/modules/job-queue/storage.mjs +64 -0
- package/modules/job-queue/supervisor.mjs +30 -0
- package/modules/job-queue/waiting.mjs +16 -0
- package/modules/mail/LICENSE +21 -0
- package/modules/mail/README.md +336 -0
- package/modules/mail/doctor.mjs +81 -0
- package/modules/mail/index.mjs +8 -0
- package/modules/mail/live-proof/README.md +113 -0
- package/modules/mail/live-proof/run.mjs +55 -0
- package/modules/mail/package.json +19 -0
- package/modules/mail/pnpm-lock.yaml +537 -0
- package/modules/mail/src/adapters/cloudflare/README.md +84 -0
- package/modules/mail/src/adapters/cloudflare/headers.mjs +63 -0
- package/modules/mail/src/adapters/cloudflare/index.mjs +47 -0
- package/modules/mail/src/adapters/cloudflare/response.mjs +99 -0
- package/modules/mail/src/adapters/messaging/README.md +92 -0
- package/modules/mail/src/adapters/messaging/contract.mjs +66 -0
- package/modules/mail/src/adapters/messaging/event-schema.mjs +28 -0
- package/modules/mail/src/adapters/messaging/index.mjs +61 -0
- package/modules/mail/src/adapters/messaging/jws.mjs +68 -0
- package/modules/mail/src/adapters/messaging/request.mjs +52 -0
- package/modules/mail/src/adapters/messaging/serialize.mjs +21 -0
- package/modules/mail/src/adapters/resend/README.md +120 -0
- package/modules/mail/src/adapters/resend/index.mjs +22 -0
- package/modules/mail/src/adapters/resend/response.mjs +54 -0
- package/modules/mail/src/adapters/resend/serialize.mjs +46 -0
- package/modules/mail/src/adapters/resend/size.mjs +99 -0
- package/modules/mail/src/adapters/sendgrid/README.md +75 -0
- package/modules/mail/src/adapters/sendgrid/index.mjs +35 -0
- package/modules/mail/src/adapters/sendgrid/serialize.mjs +104 -0
- package/modules/mail/src/core/errors.mjs +9 -0
- package/modules/mail/src/core/transport.mjs +33 -0
- package/modules/mail/src/core/validation.mjs +172 -0
- package/modules/mail/src/development/index.d.mts +37 -0
- package/modules/mail/src/development/index.mjs +35 -0
- package/modules/mail/src/development/outbox.mjs +78 -0
- package/modules/mail/src/development/schema.sql +9 -0
- package/modules/mail/src/development/viewer-html.mjs +42 -0
- package/modules/mail/src/development/viewer.mjs +65 -0
- package/modules/mail/src/doctor/README.md +107 -0
- package/modules/mail/src/doctor/common.mjs +64 -0
- package/modules/mail/src/doctor/dns.mjs +102 -0
- package/modules/mail/src/doctor/index.mjs +28 -0
- package/modules/mail/src/doctor/probe.mjs +66 -0
- package/modules/mail/src/doctor/receive.mjs +81 -0
- package/modules/mail/src/doctor/records.mjs +45 -0
- package/modules/mail/src/events/README.md +268 -0
- package/modules/mail/src/events/d1-schema.sql +25 -0
- package/modules/mail/src/events/d1-storage.mjs +76 -0
- package/modules/mail/src/events/index.mjs +3 -0
- package/modules/mail/src/events/intake.mjs +201 -0
- package/modules/mail/src/events/storage.mjs +52 -0
- package/modules/mail/src/events/verify.mjs +92 -0
- package/modules/mail/src/inbound/README.md +137 -0
- package/modules/mail/src/inbound/bytes.mjs +91 -0
- package/modules/mail/src/inbound/headers.mjs +143 -0
- package/modules/mail/src/inbound/index.mjs +29 -0
- package/modules/mail/src/inbound/mime.mjs +36 -0
- package/modules/mail/src/inbound/multipart.mjs +27 -0
- package/modules/mail/src/inbound/resend-attachment.mjs +43 -0
- package/modules/mail/src/inbound/resend.mjs +54 -0
- package/modules/mail/src/inbound/sendgrid.mjs +60 -0
- package/modules/mail/src/sender.mjs +33 -0
- package/modules/mail/src/sendgrid-email/index.d.mts +33 -0
- package/modules/mail/src/sendgrid-email/index.mjs +86 -0
- package/modules/mail/src/unsubscribe/index.mjs +142 -0
- package/modules/operator-auth/README.md +599 -0
- package/modules/operator-auth/accounts/adapter.mjs +41 -0
- package/modules/operator-auth/accounts/admin.mjs +159 -0
- package/modules/operator-auth/accounts/credentials.mjs +27 -0
- package/modules/operator-auth/accounts/editor-owner.mjs +13 -0
- package/modules/operator-auth/accounts/index.d.mts +9 -0
- package/modules/operator-auth/accounts/index.mjs +9 -0
- package/modules/operator-auth/accounts/native.d.mts +38 -0
- package/modules/operator-auth/accounts/native.mjs +171 -0
- package/modules/operator-auth/accounts/oa.mjs +90 -0
- package/modules/operator-auth/accounts/provider-control.mjs +50 -0
- package/modules/operator-auth/accounts/provider-pin.mjs +14 -0
- package/modules/operator-auth/accounts/roles.mjs +26 -0
- package/modules/operator-auth/accounts/schema/dependency-setup.sql +17 -0
- package/modules/operator-auth/accounts/schema/dev-sessions.sql +22 -0
- package/modules/operator-auth/accounts/schema/domain-rules-step-up.sql +20 -0
- package/modules/operator-auth/accounts/schema/invitation-cancellation.sql +17 -0
- package/modules/operator-auth/accounts/schema/operator-auth.sql +142 -0
- package/modules/operator-auth/accounts/schema/provider-control.sql +30 -0
- package/modules/operator-auth/accounts/schema/provider-pins.sql +6 -0
- package/modules/operator-auth/accounts/settings-page.mjs +4 -0
- package/modules/operator-auth/accounts/store.mjs +246 -0
- package/modules/operator-auth/branding.mjs +66 -0
- package/modules/operator-auth/config.mjs +35 -0
- package/modules/operator-auth/conformance/README.md +81 -0
- package/modules/operator-auth/conformance/index.d.mts +43 -0
- package/modules/operator-auth/conformance/index.mjs +300 -0
- package/modules/operator-auth/conformance/presentation.mjs +57 -0
- package/modules/operator-auth/crypto.mjs +17 -0
- package/modules/operator-auth/dev/badge.mjs +62 -0
- package/modules/operator-auth/dev/index.d.mts +14 -0
- package/modules/operator-auth/dev/index.mjs +137 -0
- package/modules/operator-auth/dev/personas.mjs +17 -0
- package/modules/operator-auth/dev/sign-in-as.d.mts +3 -0
- package/modules/operator-auth/dev/sign-in-as.mjs +25 -0
- package/modules/operator-auth/dev-admission.d.mts +4 -0
- package/modules/operator-auth/dev-admission.mjs +10 -0
- package/modules/operator-auth/dev-gate.mjs +86 -0
- package/modules/operator-auth/docs/README.md +78 -0
- package/modules/operator-auth/docs/native-accounts.md +313 -0
- package/modules/operator-auth/docs/tanstack-start.md +218 -0
- package/modules/operator-auth/docs/vite-spa.md +144 -0
- package/modules/operator-auth/errors.mjs +9 -0
- package/modules/operator-auth/http.mjs +42 -0
- package/modules/operator-auth/identity/admission.mjs +105 -0
- package/modules/operator-auth/identity/after-confirm.mjs +18 -0
- package/modules/operator-auth/identity/email-link/confirmation.mjs +14 -0
- package/modules/operator-auth/identity/email-link/index.d.mts +48 -0
- package/modules/operator-auth/identity/email-link/index.mjs +128 -0
- package/modules/operator-auth/identity/email-link/message.mjs +16 -0
- package/modules/operator-auth/identity/index.d.mts +7 -0
- package/modules/operator-auth/identity/index.mjs +5 -0
- package/modules/operator-auth/identity/oidc/README.md +171 -0
- package/modules/operator-auth/identity/oidc/client-auth.mjs +16 -0
- package/modules/operator-auth/identity/oidc/config.mjs +102 -0
- package/modules/operator-auth/identity/oidc/crypto.mjs +29 -0
- package/modules/operator-auth/identity/oidc/errors.mjs +5 -0
- package/modules/operator-auth/identity/oidc/index.d.mts +33 -0
- package/modules/operator-auth/identity/oidc/index.mjs +287 -0
- package/modules/operator-auth/identity/oidc/jwt.mjs +102 -0
- package/modules/operator-auth/identity/oidc/network.mjs +93 -0
- package/modules/operator-auth/identity/oidc/presets.mjs +26 -0
- package/modules/operator-auth/identity/oidc/schema/oidc.sql +50 -0
- package/modules/operator-auth/identity/oidc/store.mjs +68 -0
- package/modules/operator-auth/identity/principal.mjs +28 -0
- package/modules/operator-auth/index.d.mts +223 -0
- package/modules/operator-auth/index.mjs +11 -0
- package/modules/operator-auth/input.mjs +55 -0
- package/modules/operator-auth/local-cli.mjs +16 -0
- package/modules/operator-auth/page.mjs +34 -0
- package/modules/operator-auth/policy.mjs +20 -0
- package/modules/operator-auth/routes.mjs +91 -0
- package/modules/operator-auth/service.mjs +152 -0
- package/modules/operator-auth/signin/index.mjs +67 -0
- package/modules/owner-claim/README.md +137 -0
- package/modules/owner-claim/idp.mjs +40 -0
- package/modules/owner-claim/index.d.mts +27 -0
- package/modules/owner-claim/index.mjs +39 -0
- package/modules/owner-claim/page.mjs +11 -0
- package/modules/owner-claim/schema/idp.sql +4 -0
- package/modules/owner-claim/schema/owner-claim.sql +18 -0
- package/modules/owner-claim/transaction.mjs +87 -0
- package/modules/owner-claim/verify.mjs +80 -0
- package/modules/portability/README.md +444 -0
- package/modules/portability/bridge.mjs +102 -0
- package/modules/portability/catalogue.mjs +118 -0
- package/modules/portability/cli-options.mjs +38 -0
- package/modules/portability/cli.mjs +56 -0
- package/modules/portability/copies.mjs +68 -0
- package/modules/portability/dev-data.mjs +32 -0
- package/modules/portability/diff.mjs +33 -0
- package/modules/portability/export.mjs +129 -0
- package/modules/portability/files.mjs +65 -0
- package/modules/portability/format.mjs +122 -0
- package/modules/portability/generated.mjs +60 -0
- package/modules/portability/hash.mjs +79 -0
- package/modules/portability/http.mjs +42 -0
- package/modules/portability/import.mjs +185 -0
- package/modules/portability/index.d.mts +58 -0
- package/modules/portability/index.mjs +22 -0
- package/modules/portability/journal.mjs +49 -0
- package/modules/portability/legacy-files.mjs +43 -0
- package/modules/portability/legacy-safety.mjs +27 -0
- package/modules/portability/lines.mjs +36 -0
- package/modules/portability/migration-history.mjs +18 -0
- package/modules/portability/migration-statements.mjs +23 -0
- package/modules/portability/payloads.mjs +52 -0
- package/modules/portability/provider-data.mjs +25 -0
- package/modules/portability/restore-safety.mjs +30 -0
- package/modules/portability/schema/portability.sql +25 -0
- package/modules/portability/storage.mjs +49 -0
- package/modules/release/README.md +212 -0
- package/modules/release/catalogue.mjs +79 -0
- package/modules/release/export.mjs +51 -0
- package/modules/release/produce.mjs +136 -0
- package/modules/release/public-child-transport-state.cjs +29 -0
- package/modules/release/public-child.mjs +71 -0
- package/modules/release/public-git.mjs +21 -0
- package/modules/release/publication-scan.mjs +22 -0
- package/modules/release/publish-handoff.mjs +37 -0
- package/modules/release/source.mjs +71 -0
- package/modules/release/test-summary.mjs +50 -0
- package/onboarding/README.md +163 -0
- package/onboarding/activity.mjs +31 -0
- package/onboarding/adapter-auth.mjs +88 -0
- package/onboarding/adapter-hosting.mjs +35 -0
- package/onboarding/adapter-labels.mjs +13 -0
- package/onboarding/adapter-settings.mjs +129 -0
- package/onboarding/adapter.mjs +238 -0
- package/onboarding/agent-handoff.mjs +9 -0
- package/onboarding/app-processes.mjs +21 -0
- package/onboarding/archive-output.d.mts +3 -0
- package/onboarding/archive-output.mjs +54 -0
- package/onboarding/command-custody.mjs +24 -0
- package/onboarding/command-recovery.mjs +8 -0
- package/onboarding/commands.mjs +166 -0
- package/onboarding/dependency-approval.mjs +36 -0
- package/onboarding/dev-identity.mjs +23 -0
- package/onboarding/email.mjs +24 -0
- package/onboarding/failures.mjs +42 -0
- package/onboarding/input-limits.mjs +7 -0
- package/onboarding/installer-inputs.mjs +24 -0
- package/onboarding/launcher.mjs +34 -0
- package/onboarding/local/build-files.mjs +64 -0
- package/onboarding/local/build.mjs +49 -0
- package/onboarding/local/environment.mjs +30 -0
- package/onboarding/local/files.mjs +35 -0
- package/onboarding/local/operation.mjs +166 -0
- package/onboarding/local/owner.mjs +34 -0
- package/onboarding/local/process-table.mjs +114 -0
- package/onboarding/local/recovery.mjs +79 -0
- package/onboarding/local/state.mjs +31 -0
- package/onboarding/metadata.mjs +7 -0
- package/onboarding/operator-install.mjs +45 -0
- package/onboarding/operator-installer.mjs +39 -0
- package/onboarding/operator-prerequisites.mjs +27 -0
- package/onboarding/package.json +6 -0
- package/onboarding/prefill.mjs +16 -0
- package/onboarding/private-files.mjs +97 -0
- package/onboarding/private.mjs +35 -0
- package/onboarding/process-cleanup.mjs +134 -0
- package/onboarding/process-diagnostics.mjs +91 -0
- package/onboarding/process-scope.mjs +118 -0
- package/onboarding/process-table.mjs +118 -0
- package/onboarding/process-tree.mjs +36 -0
- package/onboarding/production-modules.mjs +22 -0
- package/onboarding/production-options.mjs +40 -0
- package/onboarding/production-plan.mjs +8 -0
- package/onboarding/production-runtime.mjs +30 -0
- package/onboarding/project-vite.mjs +14 -0
- package/onboarding/providers.mjs +289 -0
- package/onboarding/queue.mjs +53 -0
- package/onboarding/queued-entry.mjs +34 -0
- package/onboarding/readiness.mjs +59 -0
- package/onboarding/redaction-jwt.mjs +32 -0
- package/onboarding/redaction.mjs +298 -0
- package/onboarding/schema/SETUP.schema.json +514 -0
- package/onboarding/schema/dependencies.mjs +35 -0
- package/onboarding/schema/json-schema.mjs +202 -0
- package/onboarding/schema/setup-validation.mjs +325 -0
- package/onboarding/secrets.mjs +21 -0
- package/onboarding/server.mjs +188 -0
- package/onboarding/setup-manifest.mjs +116 -0
- package/onboarding/state.mjs +46 -0
- package/onboarding/theme-files.mjs +37 -0
- package/onboarding/theme.mjs +118 -0
- package/onboarding/ui/activity.js +98 -0
- package/onboarding/ui/address-client.mjs +33 -0
- package/onboarding/ui/app.js +377 -0
- package/onboarding/ui/index.html +1 -0
- package/onboarding/ui/model-core.mjs +127 -0
- package/onboarding/ui/model.mjs +5 -0
- package/onboarding/ui/style.css +63 -0
- package/onboarding/ui/theme.css +37 -0
- package/onboarding/ui/workspace-art.js +30 -0
- package/onboarding/wrangler-auth.mjs +43 -0
- package/operations/README.md +246 -0
- package/operations/operations.mjs +168 -0
- package/operations/project-operations.mjs +157 -0
- package/package.json +11 -4
- package/scripts/publish-check/README.md +72 -0
- package/scripts/publish-check/checksum_maps.py +52 -0
- package/scripts/publish-check/cli.py +34 -0
- package/scripts/publish-check/command_arguments.py +51 -0
- package/scripts/publish-check/command_shell_quotes.py +41 -0
- package/scripts/publish-check/command_source_quotes.py +120 -0
- package/scripts/publish-check/content_identities.py +250 -0
- package/scripts/publish-check/copy_identities.py +41 -0
- package/scripts/publish-check/der.py +61 -0
- package/scripts/publish-check/destination.py +117 -0
- package/scripts/publish-check/env_values.py +74 -0
- package/scripts/publish-check/font_licence.py +23 -0
- package/scripts/publish-check/git_history.py +115 -0
- package/scripts/publish-check/github_refs.py +217 -0
- package/scripts/publish-check/gitleaks.py +72 -0
- package/scripts/publish-check/gitleaks.toml +40 -0
- package/scripts/publish-check/history_scan.py +176 -0
- package/scripts/publish-check/hit_location.py +18 -0
- package/scripts/publish-check/hygiene_config.py +42 -0
- package/scripts/publish-check/hygiene_text.py +11 -0
- package/scripts/publish-check/identity-recipe.mjs +54 -0
- package/scripts/publish-check/json_identity.py +79 -0
- package/scripts/publish-check/license-exceptions.json +11 -0
- package/scripts/publish-check/licenses.py +49 -0
- package/scripts/publish-check/policy.py +18 -0
- package/scripts/publish-check/project_identity.py +89 -0
- package/scripts/publish-check/provenance.py +91 -0
- package/scripts/publish-check/provenance_pins.py +33 -0
- package/scripts/publish-check/public_base.py +63 -0
- package/scripts/publish-check/public_hygiene.py +80 -0
- package/scripts/publish-check/publication-config.json +12 -0
- package/scripts/publish-check/publication_config.py +34 -0
- package/scripts/publish-check/report.py +52 -0
- package/scripts/publish-check/repository_commands.py +75 -0
- package/scripts/publish-check/repository_intent.py +52 -0
- package/scripts/publish-check/reviewed.py +41 -0
- package/scripts/publish-check/source_hashes.mjs +6 -0
- package/scripts/publish-check/tree_scan.py +88 -0
- package/scripts/publish-check/trust_bundle.py +87 -0
- package/scripts/publish-check.mjs +58 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Where a user install lives. Data: ${XDG_DATA_HOME:-$HOME/.local/share}/rer,
|
|
2
|
+
// as immutable versions/<version>-<digest8> directories and a `current` link.
|
|
3
|
+
// Launcher: $HOME/.local/bin/rer, a POSIX sh script that runs node from PATH.
|
|
4
|
+
import { readFile, lstat } from 'node:fs/promises';
|
|
5
|
+
import { homedir } from 'node:os';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
|
|
8
|
+
export function layout(env = process.env) {
|
|
9
|
+
const home = env.HOME || homedir();
|
|
10
|
+
const data = join(env.XDG_DATA_HOME || join(home, '.local/share'), 'rer');
|
|
11
|
+
const bin = join(home, '.local/bin');
|
|
12
|
+
return { home, data, versions: join(data, 'versions'), current: join(data, 'current'), record: join(data, 'install.json'), bin, launcher: join(bin, 'rer') };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const marker = '# rer launcher, written by `rer cli install` (@runeditrun/rer). Do not edit.';
|
|
16
|
+
const quote = value => `'${value.replaceAll("'", `'\\''`)}'`;
|
|
17
|
+
|
|
18
|
+
// No absolute Node path is baked in, so a Node upgrade never breaks it.
|
|
19
|
+
export function launcherScript(data) {
|
|
20
|
+
return `#!/bin/sh
|
|
21
|
+
${marker}
|
|
22
|
+
# It runs the installed version with the node on PATH.
|
|
23
|
+
root=${quote(data)}
|
|
24
|
+
if ! command -v node >/dev/null 2>&1; then
|
|
25
|
+
echo "rer: node is not on PATH. Install Node 22 or later, then run rer again." >&2
|
|
26
|
+
exit 127
|
|
27
|
+
fi
|
|
28
|
+
exec node "$root/current/global/rer.mjs" "$@"
|
|
29
|
+
`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// 'absent', 'ours' (with the install root it runs), or 'foreign'.
|
|
33
|
+
export async function launcherOwner(path) {
|
|
34
|
+
let info;
|
|
35
|
+
try { info = await lstat(path); } catch (error) { if (error.code === 'ENOENT') return { state: 'absent' }; throw error; }
|
|
36
|
+
if (!info.isFile()) return { state: 'foreign' };
|
|
37
|
+
const text = await readFile(path, 'utf8');
|
|
38
|
+
if (text.split('\n')[1] !== marker) return { state: 'foreign' };
|
|
39
|
+
const root = /^root='((?:[^']|'\\'')*)'$/m.exec(text)?.[1]?.replaceAll(`'\\''`, "'");
|
|
40
|
+
return { state: 'ours', root, text };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function installRecord(paths) {
|
|
44
|
+
try { return JSON.parse(await readFile(paths.record, 'utf8')); } catch (error) { if (error.code === 'ENOENT') return undefined; throw error; }
|
|
45
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// rer cli status: what this rer is and every other rer on PATH.
|
|
2
|
+
import { access, constants, realpath } from 'node:fs/promises';
|
|
3
|
+
import { join, resolve, sep } from 'node:path';
|
|
4
|
+
import { ownManifest, ownRoot, packageDigest } from '../package-info.mjs';
|
|
5
|
+
import { installRecord, launcherOwner, layout } from './layout.mjs';
|
|
6
|
+
|
|
7
|
+
const inside = (path, root) => path === root || path.startsWith(root + sep);
|
|
8
|
+
async function real(path) { try { return await realpath(path); } catch { return undefined; } }
|
|
9
|
+
|
|
10
|
+
export async function channelOf(paths) {
|
|
11
|
+
const data = await real(paths.data);
|
|
12
|
+
if (data && inside(ownRoot, join(data, 'versions'))) return { channel: (await installRecord(paths))?.channel ?? 'npx', root: data };
|
|
13
|
+
if (ownRoot.split(sep).includes('_npx')) return { channel: 'npx', root: ownRoot };
|
|
14
|
+
if (ownRoot.endsWith(join('node_modules', '@runeditrun', 'rer'))) return { channel: 'npm-global', root: ownRoot };
|
|
15
|
+
return { channel: 'source', root: ownRoot };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Every executable `rer` on PATH except the ones that run this very install.
|
|
19
|
+
export async function pathCommands(installRoot, env = process.env) {
|
|
20
|
+
const seen = new Set(), self = [], others = [];
|
|
21
|
+
for (const directory of (env.PATH ?? '').split(':').filter(Boolean)) {
|
|
22
|
+
const candidate = resolve(directory, 'rer');
|
|
23
|
+
if (seen.has(candidate)) continue;
|
|
24
|
+
seen.add(candidate);
|
|
25
|
+
try { await access(candidate, constants.X_OK); } catch { continue; }
|
|
26
|
+
const target = await real(candidate);
|
|
27
|
+
const launcher = await launcherOwner(candidate);
|
|
28
|
+
if (target && inside(target, ownRoot) || launcher.state === 'ours' && launcher.root && await real(launcher.root) === installRoot) self.push(candidate);
|
|
29
|
+
else others.push(candidate);
|
|
30
|
+
}
|
|
31
|
+
return { self, others };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function status({ json }) {
|
|
35
|
+
const paths = layout(), manifest = ownManifest(), { channel, root } = await channelOf(paths);
|
|
36
|
+
const launcher = await launcherOwner(paths.launcher), commands = await pathCommands(root);
|
|
37
|
+
const result = {
|
|
38
|
+
version: manifest.version,
|
|
39
|
+
digest: await packageDigest(ownRoot),
|
|
40
|
+
channel,
|
|
41
|
+
launcher: channel === 'npm-global' ? commands.self[0] ?? null : launcher.state === 'ours' ? paths.launcher : null,
|
|
42
|
+
root,
|
|
43
|
+
others: commands.others,
|
|
44
|
+
};
|
|
45
|
+
if (json) process.stdout.write(JSON.stringify(result) + '\n');
|
|
46
|
+
else process.stdout.write([
|
|
47
|
+
`rer ${result.version} (${result.channel})`,
|
|
48
|
+
`digest: ${result.digest}`,
|
|
49
|
+
`root: ${result.root}`,
|
|
50
|
+
`launcher: ${result.launcher ?? '(none)'}`,
|
|
51
|
+
`others on PATH: ${result.others.length ? '\n ' + result.others.join('\n ') : '(none)'}`,
|
|
52
|
+
].join('\n') + '\n');
|
|
53
|
+
return 0;
|
|
54
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// rer cli uninstall: remove the user install (the launcher `rer cli install`
|
|
2
|
+
// wrote and the install root it runs), and nothing else. ~/.rer (servers, jobs,
|
|
3
|
+
// Node runtimes) is untouched, and so is any folder that is not a rer install
|
|
4
|
+
// root. An npm global rer leaves other channels' installs alone.
|
|
5
|
+
import { lstat, readlink, rm } from 'node:fs/promises';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
import { launcherOwner, layout } from './layout.mjs';
|
|
8
|
+
import { channelOf } from './status.mjs';
|
|
9
|
+
|
|
10
|
+
// A folder `rer cli install` made: a versions/ directory and a `current` link into it.
|
|
11
|
+
async function installRoot(path) {
|
|
12
|
+
try {
|
|
13
|
+
if (!(await lstat(join(path, 'versions'))).isDirectory()) return false;
|
|
14
|
+
return /^versions\/[^/]+$/.test(await readlink(join(path, 'current')));
|
|
15
|
+
} catch { return false; }
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function uninstall() {
|
|
19
|
+
const paths = layout(), { channel } = await channelOf(paths), launcher = await launcherOwner(paths.launcher), lines = [];
|
|
20
|
+
// The root the launcher runs; with no launcher, the layout's own root.
|
|
21
|
+
const root = launcher.state === 'ours' && launcher.root ? launcher.root : paths.data;
|
|
22
|
+
const user = launcher.state === 'ours' || await installRoot(root);
|
|
23
|
+
if (channel === 'npm-global') {
|
|
24
|
+
lines.push('This rer came from npm; remove it with: npm uninstall -g @runeditrun/rer');
|
|
25
|
+
if (user) lines.push(`A user install is also present${launcher.state === 'ours' ? ` (${paths.launcher})` : ` (${root})`}; remove it with: ${launcher.state === 'ours' ? paths.launcher : 'npx -y @runeditrun/rer'} cli uninstall`);
|
|
26
|
+
process.stdout.write(lines.join('\n') + '\n');
|
|
27
|
+
return 0;
|
|
28
|
+
}
|
|
29
|
+
if (launcher.state === 'ours') { await rm(paths.launcher); lines.push(`Removed ${paths.launcher}.`); }
|
|
30
|
+
else if (launcher.state === 'foreign') lines.push(`Left ${paths.launcher} in place: rer cli install did not write it.`);
|
|
31
|
+
if (await installRoot(root)) { await rm(root, { recursive: true, force: true }); lines.push(`Removed ${root}.`); }
|
|
32
|
+
else if (launcher.state === 'ours') lines.push(`Left ${root} in place: it is not a rer install root.`);
|
|
33
|
+
if (!lines.length) lines.push('No user install of rer was found; nothing changed.');
|
|
34
|
+
process.stdout.write(lines.join('\n') + '\n');
|
|
35
|
+
return 0;
|
|
36
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Run an invocation with the checkout's own dispatcher, in this process, exactly
|
|
2
|
+
// as `node scripts/rer.mjs ...` would from the project root: process.argv[1] is
|
|
3
|
+
// that file, so its Node-pin re-exec and queued child spawns behave as they do
|
|
4
|
+
// when it is run directly. The dispatcher sets process.exitCode itself.
|
|
5
|
+
import { pathToFileURL } from 'node:url';
|
|
6
|
+
import { dispatcher } from './checkout.mjs';
|
|
7
|
+
|
|
8
|
+
export async function delegate(target, args) {
|
|
9
|
+
const { entry } = dispatcher(target.root);
|
|
10
|
+
if (target.root) process.chdir(target.root);
|
|
11
|
+
process.argv = [process.argv[0], entry, ...args];
|
|
12
|
+
await import(pathToFileURL(entry).href);
|
|
13
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// Person defaults and the server-limit check, applied by the launcher on every
|
|
2
|
+
// pin. A pin from before Base's person defaults (2 servers, 1800 s, and a cap
|
|
3
|
+
// set by the lowest limit any running server recorded) gets 10 servers and 12
|
|
4
|
+
// hours unless the person chose otherwise. The 1-minute-load guard belongs to
|
|
5
|
+
// the fleet's own environment: a person's start never waits or is refused for
|
|
6
|
+
// load alone, so an unset threshold becomes an unbounded one for old pins.
|
|
7
|
+
import { existsSync } from 'node:fs';
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
import { pathToFileURL } from 'node:url';
|
|
10
|
+
import { liveServers, stopCommand } from '../registry.mjs';
|
|
11
|
+
import { LaunchFailure } from './failure.mjs';
|
|
12
|
+
|
|
13
|
+
export const personDefaults = { limit: 10, idleSeconds: 43200 };
|
|
14
|
+
const unboundedLoad = '1000000';
|
|
15
|
+
// The verbs that start a server or enter the job queue on every pin.
|
|
16
|
+
export const personVerbs = new Set(['dev', 'preview', 'install', 'setup', 'build', 'verify', 'deploy', 'export', 'import']);
|
|
17
|
+
|
|
18
|
+
// How the pinned dispatcher counts servers, and which person defaults it lacks.
|
|
19
|
+
// describe's `defaults` (from rer-cli-2) is authoritative: the launcher supplies
|
|
20
|
+
// a person default only where the pin's own is lower, so a current pin never
|
|
21
|
+
// records a limit nobody set. Before it, a pin whose registry exports its
|
|
22
|
+
// default limit already has the person defaults and binds only explicit limits;
|
|
23
|
+
// older pins have neither.
|
|
24
|
+
export async function pinPolicy(root, mode, described) {
|
|
25
|
+
const own = described?.defaults;
|
|
26
|
+
if (own) return { supply: { limit: !(own.serverLimit >= personDefaults.limit), idle: !(own.idleSeconds >= personDefaults.idleSeconds) }, explicitOnly: true, limit: own.serverLimit ?? personDefaults.limit };
|
|
27
|
+
if (mode !== 'vendored') return { supply: { limit: false, idle: false }, explicitOnly: true, limit: personDefaults.limit };
|
|
28
|
+
const registry = join(root, 'scripts/base-cli/cli/server-registry.mjs');
|
|
29
|
+
const exported = existsSync(registry) ? await import(pathToFileURL(registry).href) : {};
|
|
30
|
+
return 'defaultServerLimit' in exported
|
|
31
|
+
? { supply: { limit: false, idle: false }, explicitOnly: true, limit: exported.defaultServerLimit }
|
|
32
|
+
: { supply: { limit: true, idle: true }, explicitOnly: false, limit: 2 };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function personEnvironment(env, args, pin) {
|
|
36
|
+
const added = {};
|
|
37
|
+
if (pin.supply.limit && env.RER_SERVER_LIMIT === undefined) added.RER_SERVER_LIMIT = String(personDefaults.limit);
|
|
38
|
+
if (pin.supply.idle && env.RER_SERVER_IDLE_SECONDS === undefined && !args.includes('--idle-timeout')) added.RER_SERVER_IDLE_SECONDS = String(personDefaults.idleSeconds);
|
|
39
|
+
if (env.RER_SERVERS_LOAD_THRESHOLD === undefined) added.RER_SERVERS_LOAD_THRESHOLD = unboundedLoad;
|
|
40
|
+
if (env.RER_JOBS_LOAD_THRESHOLD === undefined) added.RER_JOBS_LOAD_THRESHOLD = unboundedLoad;
|
|
41
|
+
return added;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const listing = record => ` ${record.mode} on port ${record.port ?? '(starting)'} in ${record.checkout} (PID ${record.owner?.pid})\n stop it with: ${stopCommand(record)}`;
|
|
45
|
+
|
|
46
|
+
// Refuse in seconds, before any install or setup, when the pinned dispatcher
|
|
47
|
+
// would refuse the start for the server limit. Returns this checkout's own
|
|
48
|
+
// running server when there is one.
|
|
49
|
+
export async function checkCapacity(checkout, env, pin) {
|
|
50
|
+
const running = await liveServers(env);
|
|
51
|
+
const own = running.find(record => record.checkout === checkout);
|
|
52
|
+
if (own) return { own };
|
|
53
|
+
if (env.RER_SERVER_JOURNEY === '1') return {};
|
|
54
|
+
const counted = running.filter(record => !record.journey);
|
|
55
|
+
const limit = Number(env.RER_SERVER_LIMIT ?? pin.limit);
|
|
56
|
+
const binding = pin.explicitOnly ? counted.filter(record => record.explicit) : counted;
|
|
57
|
+
const cap = Math.min(limit, ...binding.map(record => record.limit));
|
|
58
|
+
if (counted.length < cap) return {};
|
|
59
|
+
const holders = cap < limit ? binding.filter(record => record.limit === cap) : [];
|
|
60
|
+
if (holders.length) throw new LaunchFailure(4, [
|
|
61
|
+
`Not started: ${holders.length === 1 ? 'a server' : 'servers'} started with a limit of ${cap} hold${holders.length === 1 ? 's' : ''} this checkout's rer copy to ${cap} running servers, and ${counted.length} ${counted.length === 1 ? 'is' : 'are'} running:`,
|
|
62
|
+
...holders.map(record => ` ${record.mode} for ${record.checkout} (PID ${record.owner?.pid})`),
|
|
63
|
+
`Restart ${holders.length === 1 ? 'it' : 'each'} under rer, which records ${personDefaults.limit} servers:`,
|
|
64
|
+
...holders.flatMap(record => [` rer ${record.mode} --stop --cwd ${JSON.stringify(record.checkout)}`, ` rer ${record.mode} --cwd ${JSON.stringify(record.checkout)}`]),
|
|
65
|
+
'rer never stops another checkout\'s server itself.',
|
|
66
|
+
].join('\n'));
|
|
67
|
+
throw new LaunchFailure(4, [
|
|
68
|
+
`Not started: ${counted.length} dev or preview ${counted.length === 1 ? 'server is' : 'servers are'} running on this computer and the limit is ${cap}.`,
|
|
69
|
+
'Running now:', ...counted.map(listing),
|
|
70
|
+
`To make room, stop one with its command above, or start this one with a higher limit, for example RER_SERVER_LIMIT=${cap * 2} rer dev.`,
|
|
71
|
+
].join('\n'));
|
|
72
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// Zero-setup `rer dev` for a first-party checkout. With stdout not a terminal
|
|
2
|
+
// (an agent's shell tool) the preparation and start run in a detached worker
|
|
3
|
+
// and this command returns once the app is ready, leaving it running; in a
|
|
4
|
+
// terminal it stays in the foreground as the dispatcher's own dev does.
|
|
5
|
+
// --detach and --foreground override the choice.
|
|
6
|
+
import { spawn } from 'node:child_process';
|
|
7
|
+
import { randomUUID } from 'node:crypto';
|
|
8
|
+
import { createWriteStream, existsSync } from 'node:fs';
|
|
9
|
+
import { open, realpath, stat } from 'node:fs/promises';
|
|
10
|
+
import { join } from 'node:path';
|
|
11
|
+
import { setTimeout as delay } from 'node:timers/promises';
|
|
12
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
13
|
+
import { dispatcher } from '../checkout.mjs';
|
|
14
|
+
import { delegate } from '../delegate.mjs';
|
|
15
|
+
import { ownRoot } from '../package-info.mjs';
|
|
16
|
+
import { LaunchFailure } from './failure.mjs';
|
|
17
|
+
import { prepare } from './prepare.mjs';
|
|
18
|
+
import { printer } from './render.mjs';
|
|
19
|
+
import { stopLaunch } from './stop.mjs';
|
|
20
|
+
import { launchDirectory, liveWorker } from './state.mjs';
|
|
21
|
+
|
|
22
|
+
const workerPath = fileURLToPath(new URL('./worker.mjs', import.meta.url));
|
|
23
|
+
|
|
24
|
+
export async function launchDev(target) {
|
|
25
|
+
const args = target.args.slice(1);
|
|
26
|
+
if (args.includes('--detach') && args.includes('--foreground')) { process.stderr.write('Choose one of --detach and --foreground.\n'); return 2; }
|
|
27
|
+
const devArgs = args.filter(arg => arg !== '--detach' && arg !== '--foreground');
|
|
28
|
+
// An invocation the pinned dispatcher refuses goes straight to it, so the
|
|
29
|
+
// refusal is its own, with nothing prepared first.
|
|
30
|
+
if (!await accepted(target.root, devArgs)) return delegate(target, ['dev', ...devArgs]);
|
|
31
|
+
const checkout = await realpath(target.root), directory = await launchDirectory(checkout);
|
|
32
|
+
if (devArgs.includes('--stop')) {
|
|
33
|
+
try { return await stopLaunch(target, devArgs, { checkout, directory }); }
|
|
34
|
+
catch (failure) { if (!(failure instanceof LaunchFailure)) throw failure; process.stderr.write(failure.message + '\n'); return failure.exitCode; }
|
|
35
|
+
}
|
|
36
|
+
const here = await realpath(process.cwd()).catch(() => undefined);
|
|
37
|
+
const out = printer({ json: devArgs.includes('--json'), login: devArgs.includes('--login'), stopHint: `rer dev --stop${here === checkout ? '' : ` --cwd ${JSON.stringify(checkout)}`}` });
|
|
38
|
+
const detach = args.includes('--detach') || (!args.includes('--foreground') && !process.stdout.isTTY);
|
|
39
|
+
return detach ? detached(target, devArgs, directory, out) : foreground(target, devArgs, directory, out);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function accepted(root, devArgs) {
|
|
43
|
+
const parser = dispatcher(root).mode === 'vendored' ? join(root, 'scripts/base-cli/cli/parse.mjs') : join(ownRoot, 'cli/parse.mjs');
|
|
44
|
+
if (!existsSync(parser)) return true;
|
|
45
|
+
try { (await import(pathToFileURL(parser).href)).parse(['dev', ...devArgs], process.cwd()); return true; } catch { return false; }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function foreground(target, devArgs, directory, out) {
|
|
49
|
+
const holder = await liveWorker(directory);
|
|
50
|
+
if (holder) {
|
|
51
|
+
out.attaching(holder.startedAt);
|
|
52
|
+
const code = await follow(directory, holder.token, out, { quiet: true });
|
|
53
|
+
if (code !== 0) return code;
|
|
54
|
+
}
|
|
55
|
+
const log = createWriteStream(join(directory, 'launch.log'), { flags: 'a', mode: 0o600 });
|
|
56
|
+
let prepared;
|
|
57
|
+
// The same progress lines as a detached launch while install or setup runs.
|
|
58
|
+
const heartbeat = setInterval(() => out.heartbeat(), 1000);
|
|
59
|
+
try { prepared = await prepare({ root: target.root, args: devArgs, env: process.env, report: fields => out.phase(fields), directory, log: text => log.write(text + '\n'), children: new Set() }); }
|
|
60
|
+
catch (failure) { if (!(failure instanceof LaunchFailure)) throw failure; out.failure(failure); return failure.exitCode; }
|
|
61
|
+
finally { clearInterval(heartbeat); log.end(); }
|
|
62
|
+
Object.assign(process.env, prepared.added);
|
|
63
|
+
return delegate(target, ['dev', ...devArgs]);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function detached(target, devArgs, directory, out) {
|
|
67
|
+
let token, worker;
|
|
68
|
+
const holder = await liveWorker(directory);
|
|
69
|
+
if (holder) { token = holder.token; out.attaching(holder.startedAt); }
|
|
70
|
+
else {
|
|
71
|
+
token = randomUUID();
|
|
72
|
+
// The worker ends itself for a stop that ran after this command started.
|
|
73
|
+
worker = spawn(process.execPath, [workerPath, target.root, token, JSON.stringify(devArgs.filter(arg => arg !== '--json')), String(performance.timeOrigin)], { cwd: target.root, env: process.env, detached: true, stdio: 'ignore' });
|
|
74
|
+
worker.unref();
|
|
75
|
+
}
|
|
76
|
+
return follow(directory, token, out, { worker });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Print a worker's events until it is ready or has failed. A redirect means
|
|
80
|
+
// another worker already held this checkout's launch; follow that one.
|
|
81
|
+
async function follow(directory, token, out, { worker, quiet = false }) {
|
|
82
|
+
const path = join(directory, `events-${token}.jsonl`);
|
|
83
|
+
let offset = 0, rest = '', exited = false, missingSince, polls = 0;
|
|
84
|
+
worker?.once('exit', () => { exited = true; });
|
|
85
|
+
for (;;) {
|
|
86
|
+
let size = 0;
|
|
87
|
+
try { size = (await stat(path)).size; } catch (error) { if (error.code !== 'ENOENT') throw error; }
|
|
88
|
+
if (size > offset) {
|
|
89
|
+
const handle = await open(path, 'r');
|
|
90
|
+
try { const { buffer, bytesRead } = await handle.read({ buffer: Buffer.alloc(size - offset), position: offset }); offset += bytesRead; rest += buffer.subarray(0, bytesRead).toString('utf8'); }
|
|
91
|
+
finally { await handle.close(); }
|
|
92
|
+
const lines = rest.split('\n'); rest = lines.pop();
|
|
93
|
+
for (const line of lines) {
|
|
94
|
+
const event = JSON.parse(line);
|
|
95
|
+
if (event.event === 'phase') out.phase(event);
|
|
96
|
+
else if (event.event === 'redirect') return event.token ? follow(directory, event.token, out, { quiet }) : missing(directory);
|
|
97
|
+
else if (event.event === 'ready') { if (!quiet) out.ready(event.ready, event); return 0; }
|
|
98
|
+
else if (event.event === 'failed') { out.failure(new LaunchFailure(event.exitCode, event.message, event.error)); return event.exitCode; }
|
|
99
|
+
}
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
// No terminal event: is the worker still there? (checked about once a second)
|
|
103
|
+
const gone = worker ? exited : (polls++ % 4 === 0 ? (await liveWorker(directory))?.token !== token : false);
|
|
104
|
+
if (gone || missingSince) {
|
|
105
|
+
missingSince ??= Date.now();
|
|
106
|
+
// Let a just-finished worker's last write land before calling it lost.
|
|
107
|
+
if (Date.now() - missingSince > 1000) return missing(directory);
|
|
108
|
+
}
|
|
109
|
+
out.heartbeat();
|
|
110
|
+
await delay(250);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function missing(directory) {
|
|
115
|
+
process.stderr.write(`rer dev: the launch stopped before it finished. Run rer dev again; its log is ${join(directory, 'launch.log')}.\n`);
|
|
116
|
+
return 1;
|
|
117
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// What a launch needs to know about a checkout before it changes anything.
|
|
2
|
+
import { execFile } from 'node:child_process';
|
|
3
|
+
import { stat } from 'node:fs/promises';
|
|
4
|
+
import { createServer } from 'node:net';
|
|
5
|
+
import { basename, join } from 'node:path';
|
|
6
|
+
import { promisify } from 'node:util';
|
|
7
|
+
import { listeningPorts } from '../../onboarding/process-diagnostics.mjs';
|
|
8
|
+
import { LaunchFailure } from './failure.mjs';
|
|
9
|
+
|
|
10
|
+
const run = promisify(execFile);
|
|
11
|
+
async function mtime(path) { try { return (await stat(path)).mtimeMs; } catch (error) { if (error.code === 'ENOENT') return undefined; throw error; } }
|
|
12
|
+
|
|
13
|
+
// The pinned dispatcher's own read-only description.
|
|
14
|
+
export async function describeCheckout(root, entry, env, signal) {
|
|
15
|
+
try {
|
|
16
|
+
const { stdout } = await run(process.execPath, [entry, 'describe', '--json'], { cwd: root, env, maxBuffer: 16 * 1024 * 1024, signal });
|
|
17
|
+
return JSON.parse(stdout);
|
|
18
|
+
} catch (error) {
|
|
19
|
+
if (signal?.aborted) throw signal.reason;
|
|
20
|
+
throw new LaunchFailure(error.code ?? 1, (error.stderr?.trim() || `rer describe failed in ${root}.`));
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Install when the last install did not finish or the lockfile changed since.
|
|
25
|
+
// pnpm writes node_modules/.modules.yaml only once dependencies are linked and
|
|
26
|
+
// built, so a stopped install leaves it absent. An install this launcher ran
|
|
27
|
+
// is finished only when its record says so: that also covers the project's
|
|
28
|
+
// own install hooks, which pnpm runs after writing the manifest.
|
|
29
|
+
export async function needsInstall(root, last) {
|
|
30
|
+
if (last && last.state !== 'done') return true;
|
|
31
|
+
const manifest = await mtime(join(root, 'node_modules/.modules.yaml'));
|
|
32
|
+
if (manifest === undefined) return true;
|
|
33
|
+
const lock = await mtime(join(root, 'pnpm-lock.yaml'));
|
|
34
|
+
return lock !== undefined && Math.max(manifest, last?.at ?? 0) < lock;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// A checkout is set up when its pin's describe says so (`local.prepared`, from
|
|
38
|
+
// rer-cli-2); for older pins, when its primary private settings file holds
|
|
39
|
+
// anything. The pin's describe names it (`local.settings`, where the pin has it).
|
|
40
|
+
export async function hasSettings(root, described) {
|
|
41
|
+
if (typeof described?.local?.prepared === 'boolean') return described.local.prepared;
|
|
42
|
+
const primary = described?.local?.settings?.[0] ?? '.dev.vars';
|
|
43
|
+
try { return (await stat(join(root, primary))).size > 0; } catch (error) { if (error.code === 'ENOENT') return false; throw error; }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Every pin's setup saves its answers file only once it has finished, so a
|
|
47
|
+
// file newer than this launcher's record of its own attempt (its start for
|
|
48
|
+
// 'running', its end for 'failed') is a setup that finished after it.
|
|
49
|
+
async function finishedSince(root, described, at) {
|
|
50
|
+
const saved = await mtime(join(root, described?.local?.installerState ?? '.seed/installer', 'setup-answers.json'));
|
|
51
|
+
return saved !== undefined && typeof at === 'number' && saved > at;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Setup runs on a checkout with no saved settings ('fresh'). When a setup this
|
|
55
|
+
// launcher started did not finish ('running' after a kill, or 'failed') and no
|
|
56
|
+
// setup has finished since, it runs again ('resume') and keeps what the earlier
|
|
57
|
+
// attempt saved. A checkout set up any other way is never set up again.
|
|
58
|
+
// A checkout that is not prepared but already holds an owner (a setting a newer
|
|
59
|
+
// manifest added is missing) resumes too, so the saved owner and origin are
|
|
60
|
+
// kept; one without a saved owner gets the local owner ('fresh').
|
|
61
|
+
export async function setupNeeded(root, described, last) {
|
|
62
|
+
if (!await hasSettings(root, described)) return described?.local?.ownerSaved === true ? 'resume' : 'fresh';
|
|
63
|
+
if (!last || last.state === 'done') return undefined;
|
|
64
|
+
return await finishedSince(root, described, last.at) ? undefined : 'resume';
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function localOwner(root) {
|
|
68
|
+
const label = basename(root).toLowerCase().replace(/[^a-z0-9-]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 63) || 'checkout';
|
|
69
|
+
return `owner@${label}.test`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function bindable(host, port) {
|
|
73
|
+
const socket = createServer();
|
|
74
|
+
try { await new Promise((resolve, reject) => { socket.once('error', reject); socket.listen({ port, host, exclusive: true }, resolve); }); return true; }
|
|
75
|
+
catch (error) { return host === '::1' && ['EADDRNOTAVAIL', 'EAFNOSUPPORT'].includes(error.code); }
|
|
76
|
+
finally { if (socket.listening) await new Promise(resolve => socket.close(resolve)); }
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// The first port from 8787 (RER_DEV_PORT_FROM moves the start) that no
|
|
80
|
+
// registered server holds, nothing listens on, binds on both 127.0.0.1 and ::1
|
|
81
|
+
// (a browser that tries ::1 first never reaches another server) and that
|
|
82
|
+
// `claim` secures against another launch choosing it at the same time.
|
|
83
|
+
export async function freePort(servers, claim, env = process.env) {
|
|
84
|
+
const from = Number(env.RER_DEV_PORT_FROM ?? 8787);
|
|
85
|
+
if (!Number.isInteger(from) || from < 1 || from > 65535) throw new LaunchFailure(2, 'RER_DEV_PORT_FROM must be a port number.');
|
|
86
|
+
const held = new Set(servers.map(record => record.port).filter(Boolean)), busy = await listeningPorts();
|
|
87
|
+
for (let port = from; port <= 65535; port++) {
|
|
88
|
+
if (held.has(port) || busy.has(port)) continue;
|
|
89
|
+
if (!await bindable('127.0.0.1', port) || !await bindable('::1', port)) continue;
|
|
90
|
+
const claimed = await claim(port);
|
|
91
|
+
if (claimed) return { port, ...claimed };
|
|
92
|
+
}
|
|
93
|
+
throw new LaunchFailure(4, `No free port from ${from} upward. Pass one with rer dev --port PORT.`);
|
|
94
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// A launch that stops with the dispatcher's own message (or the launcher's)
|
|
2
|
+
// and exit code. `event` keeps the dispatcher's JSON error event when it sent one.
|
|
3
|
+
export class LaunchFailure extends Error {
|
|
4
|
+
constructor(exitCode, message, event) { super(message); this.exitCode = exitCode; this.event = event; }
|
|
5
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// Zero-setup preparation, composed only of the pinned dispatcher's public
|
|
2
|
+
// verbs so it works on every pin: limit check, install when needed, setup with
|
|
3
|
+
// local answers when the checkout is not set up. It never prompts and never
|
|
4
|
+
// runs setup on a checkout that is set up, so a saved owner, origin or private
|
|
5
|
+
// settings are never changed. An install or setup this launcher started and
|
|
6
|
+
// that did not finish runs again on the next launch.
|
|
7
|
+
import { realpath } from 'node:fs/promises';
|
|
8
|
+
import { dispatcher } from '../checkout.mjs';
|
|
9
|
+
import { liveServers } from '../registry.mjs';
|
|
10
|
+
import { checkCapacity, personEnvironment, pinPolicy } from './capacity.mjs';
|
|
11
|
+
import { describeCheckout, freePort, localOwner, needsInstall, setupNeeded } from './facts.mjs';
|
|
12
|
+
import { claimPort, installState, setupState } from './state.mjs';
|
|
13
|
+
import { runVerb } from './steps.mjs';
|
|
14
|
+
import { localDefaults } from './local-defaults.mjs';
|
|
15
|
+
|
|
16
|
+
// The value given for a flag in the user's dev arguments.
|
|
17
|
+
export const flagValue = (args, flag) => { const index = args.indexOf(flag); return index < 0 ? undefined : args[index + 1]; };
|
|
18
|
+
|
|
19
|
+
// A worker passes the signal a stop aborts: no step starts after it, and the
|
|
20
|
+
// step running then is signalled through `children`.
|
|
21
|
+
export async function prepare({ root, args, env, report, directory, log, children, signal }) {
|
|
22
|
+
const checkout = await realpath(root), { mode, entry } = dispatcher(root);
|
|
23
|
+
const described = await describeCheckout(root, entry, env, signal);
|
|
24
|
+
const pin = await pinPolicy(root, mode, described);
|
|
25
|
+
const added = personEnvironment(env, args, pin), childEnv = { ...env, ...added };
|
|
26
|
+
report({ phase: 'check', message: 'checking the server limit' });
|
|
27
|
+
const capacity = await checkCapacity(checkout, childEnv, pin);
|
|
28
|
+
if (capacity.own) return { entry, added, own: capacity.own };
|
|
29
|
+
const note = message => report({ phase: 'note', message });
|
|
30
|
+
const step = (record, args, fields, env = childEnv) => recorded(directory, record, () => runVerb(entry, args, { root, env, note, log, children, signal }), fields);
|
|
31
|
+
// Only this setup fills the seed's declared local defaults.
|
|
32
|
+
const setupEnv = { ...childEnv, [localDefaults]: '1' };
|
|
33
|
+
if (await needsInstall(root, await installState.read(directory))) {
|
|
34
|
+
report({ phase: 'install', message: 'installing dependencies' });
|
|
35
|
+
await step(installState, ['install']);
|
|
36
|
+
}
|
|
37
|
+
const last = await setupState.read(directory), setup = await setupNeeded(root, described, last);
|
|
38
|
+
const host = flagValue(args, '--host'), hostArgs = host ? ['--host', host] : [];
|
|
39
|
+
if (setup === 'resume') {
|
|
40
|
+
// Saved settings hold an owner and origin; setup keeps them.
|
|
41
|
+
report({ phase: 'setup', message: 'finishing this checkout\'s setup, keeping its saved owner and port' });
|
|
42
|
+
await step(setupState, ['setup', ...hostArgs], {}, setupEnv);
|
|
43
|
+
} else if (setup === 'fresh') {
|
|
44
|
+
// An interrupted setup resumes on the port it chose while that port is free.
|
|
45
|
+
const owner = localOwner(checkout), explicit = flagValue(args, '--port');
|
|
46
|
+
const { port, release } = explicit ? { port: Number(explicit) } : await freePort(await liveServers(childEnv), port => claimPort(port, childEnv), last?.port ? { ...childEnv, RER_DEV_PORT_FROM: String(last.port) } : childEnv);
|
|
47
|
+
report({ phase: 'setup', message: `setting up a local owner (${owner}) on port ${port}` });
|
|
48
|
+
try { await step(setupState, ['setup', '--owner-email', owner, '--port', String(port), ...hostArgs], { port, owner }, setupEnv); }
|
|
49
|
+
catch (failure) { await release?.(); throw failure; }
|
|
50
|
+
// The claim lasts until the server registers the port: the caller releases it.
|
|
51
|
+
return { entry, added, release };
|
|
52
|
+
}
|
|
53
|
+
return { entry, added };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Run one step with its record: 'running' while it runs (left so by a killed
|
|
57
|
+
// process), then 'done' or 'failed'.
|
|
58
|
+
async function recorded(directory, record, run, fields = {}) {
|
|
59
|
+
await record.write(directory, { state: 'running', ...fields });
|
|
60
|
+
let state = 'failed';
|
|
61
|
+
try { await run(); state = 'done'; }
|
|
62
|
+
finally { await record.write(directory, { state, ...fields }); }
|
|
63
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// What `rer dev` prints while it launches: a progress line at least every 30
|
|
2
|
+
// seconds (a `progress` event with --json), then the dispatcher's ready lines.
|
|
3
|
+
const elapsed = ms => { const s = Math.round(ms / 1000); return s < 60 ? `${s}s` : `${Math.floor(s / 60)}m ${s % 60}s`; };
|
|
4
|
+
const quiet = 20000;
|
|
5
|
+
|
|
6
|
+
export function printer({ json, login, stopHint }) {
|
|
7
|
+
const started = Date.now();
|
|
8
|
+
let current = 'preparing', last = Date.now();
|
|
9
|
+
const event = (name, fields) => process.stdout.write(JSON.stringify({ version: 1, verb: 'dev', event: name, ...fields }) + '\n');
|
|
10
|
+
const line = text => process.stderr.write(`rer dev: ${text}\n`);
|
|
11
|
+
const progress = (phase, message) => {
|
|
12
|
+
last = Date.now();
|
|
13
|
+
if (json) event('progress', { phase, message, elapsedSeconds: Math.round((last - started) / 1000) });
|
|
14
|
+
else line(message);
|
|
15
|
+
};
|
|
16
|
+
return {
|
|
17
|
+
phase({ phase, message }) { if (phase !== 'note') current = message; progress(phase, message); },
|
|
18
|
+
attaching(startedAt) { progress('attach', `attaching to the launch already preparing this checkout (started ${elapsed(Date.now() - startedAt)} ago)`); },
|
|
19
|
+
heartbeat() { if (Date.now() - last >= quiet) progress('waiting', `still ${current} (${elapsed(Date.now() - started)}). Allow up to 10 minutes; if this command is stopped, run rer dev again to reattach.`); },
|
|
20
|
+
// The dispatcher's ready event, as its own human ready lines.
|
|
21
|
+
ready(ready, { log, pid }) {
|
|
22
|
+
if (json) { event('server', { pid, log }); process.stdout.write(JSON.stringify(ready) + '\n'); return; }
|
|
23
|
+
const out = [`RER_READY ${ready.url}`];
|
|
24
|
+
if (ready.openUrl && !login) {
|
|
25
|
+
if (ready.signedInAs) out.push(`RER_SIGNED_IN ${ready.signedInAs}`);
|
|
26
|
+
out.push(`RER_OPEN ${ready.openUrl}`, 'Open that link on this computer. Its fragment holds this checkout\'s dev key, which the page keeps only as a cookie; never share the link.');
|
|
27
|
+
} else if (ready.signInUrl) {
|
|
28
|
+
out.push(`RER_SIGN_IN ${ready.signInUrl}`);
|
|
29
|
+
if (ready.openUrl) out.push(`RER_OPEN ${ready.openUrl}`);
|
|
30
|
+
out.push(`Open ${ready.openUrl ? 'the last link on this computer (it carries the dev key; never share it)' : 'the outbox'}, choose the newest sign-in message and follow its link. No email was sent.`);
|
|
31
|
+
}
|
|
32
|
+
out.push(`The server keeps running in the background${pid ? ` (PID ${pid})` : ''}. Stop it with: ${stopHint}`);
|
|
33
|
+
if (log) out.push(`Server log (private): ${log}`);
|
|
34
|
+
process.stdout.write(out.join('\n') + '\n');
|
|
35
|
+
},
|
|
36
|
+
failure(failure) {
|
|
37
|
+
if (json) {
|
|
38
|
+
if (failure.event) process.stdout.write(JSON.stringify(failure.event) + '\n');
|
|
39
|
+
else event('error', { code: 'launch_refused', message: failure.message, exitCode: failure.exitCode });
|
|
40
|
+
event('result', { ok: false, exitCode: failure.exitCode });
|
|
41
|
+
} else process.stderr.write(failure.message + '\n');
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|