@runeditrun/rer 0.0.0-stage → 0.1.0
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 +154 -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 +177 -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 +155 -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 +129 -0
- package/global/launch/facts.mjs +93 -0
- package/global/launch/failure.mjs +5 -0
- package/global/launch/local-defaults.mjs +4 -0
- package/global/launch/prepare.mjs +61 -0
- package/global/launch/render.mjs +44 -0
- package/global/launch/state.mjs +97 -0
- package/global/launch/steps.mjs +81 -0
- package/global/launch/worker.mjs +43 -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,33 @@
|
|
|
1
|
+
import {localDependencyLinks} from './dependency-doctor.mjs';
|
|
2
|
+
import { contained } from './project.mjs';
|
|
3
|
+
import { missing, refuse } from './errors.mjs';
|
|
4
|
+
import { locateExport, locateImport } from './archive-path.mjs';
|
|
5
|
+
export async function transfer(project, options, signal, custody) {
|
|
6
|
+
const direction = options.verb, file = direction === 'export' ? options.output : options.input;
|
|
7
|
+
if (!file) throw missing(`Provide --${direction === 'export' ? 'output' : 'input'} FILE.`);
|
|
8
|
+
if (direction === 'import' && !options.yes) throw refuse('Import requires explicit --yes confirmation.');
|
|
9
|
+
const target = options.target ?? 'local', api = project.adapter.portability;
|
|
10
|
+
if(direction==='import' && options.legacyV1) {
|
|
11
|
+
if(!options.acknowledgeUnverifiedMetadata)throw refuse('Legacy v1 recovery requires --acknowledge-unverified-metadata: source identity, erasure metadata and other manifest metadata cannot be authenticated. See modules/portability/README.md#backup-only-v1-recovery.');
|
|
12
|
+
if(!options.output)throw missing('Legacy v1 recovery requires --output FILE for the new v2 backup.');
|
|
13
|
+
if(target!=='local'||options.platform==='container'||api?.legacyRecovery!==true)throw refuse('Legacy v1 recovery is supported only by the local Workers portability adapter in an empty isolated installation.');
|
|
14
|
+
}else if(direction==='import' && (options.acknowledgeUnverifiedMetadata||options.output))throw refuse('--acknowledge-unverified-metadata and import --output require --legacy-v1.');
|
|
15
|
+
if (!api || typeof api[direction] !== 'function' || (target === 'remote' && api.remote !== true)) throw refuse(`Complete ${target} ${direction} is unsupported: the reviewed portability API and binding policy must be adopted first.`);
|
|
16
|
+
const located = direction === 'export' ? await locateExport(project.root, file) : await locateImport(project.root, file), path = located.path;
|
|
17
|
+
const legacyOutput = options.legacyV1 ? await locateExport(project.root, options.output) : undefined;
|
|
18
|
+
const legacyRecovery=legacyOutput?{output:legacyOutput.path,acknowledged:options.acknowledgeUnverifiedMetadata}:undefined;
|
|
19
|
+
const sessionFile = options.sessionFile ? await contained(project.root, options.sessionFile, { privateFile: true }) : undefined;
|
|
20
|
+
if(options.journal && legacyRecovery)throw refuse('--journal requires normal v2 import; legacy v1 metadata cannot authenticate a later-journal recovery.');
|
|
21
|
+
if (options.journal && (direction !== 'import' || target !== 'local')) throw refuse('--journal applies to a local import only.');
|
|
22
|
+
// A later archive of the same installation carries its latest erasure journal.
|
|
23
|
+
const journal = options.journal ? await contained(project.root, options.journal, { privateFile: true }) : undefined;
|
|
24
|
+
await project.adapter.prepareData?.(project.root, custody.store, signal);
|
|
25
|
+
// Only now can the seed's Vite configuration load in a checkout that never built.
|
|
26
|
+
await located.unserved(); await legacyOutput?.unserved();
|
|
27
|
+
const result=await api[direction]({ root: project.root, target, path, journal, origin: options.origin, sessionFile, signal, platform:options.platform, legacyRecovery, ...custody });
|
|
28
|
+
if(direction==='import' && target==='local' && (await localDependencyLinks(project.root)).some(link=>link.needs_relink)) {
|
|
29
|
+
custody.store.report('Imported provider links need fresh approval. Run rer setup with RER_LINK_TOKEN_FILE to re-link this installation.');
|
|
30
|
+
return {...result,needsRelink:true,nextCommand:'rer setup'};
|
|
31
|
+
}
|
|
32
|
+
return result;
|
|
33
|
+
}
|
package/cli/version.mjs
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// The rer CLI version: one source (global/package.json in Base) for the npm
|
|
2
|
+
// package, `rer --version` and the version a vendored copy records. A commit
|
|
3
|
+
// tagged rer-v<version> is that version; any other commit stamps
|
|
4
|
+
// <version>-dev.g<sha8>, which sorts below the release.
|
|
5
|
+
import { execFileSync } from 'node:child_process';
|
|
6
|
+
import { readFileSync } from 'node:fs';
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
|
|
10
|
+
const packageName = '@runeditrun/rer';
|
|
11
|
+
const readJson = path => { try { return JSON.parse(readFileSync(path, 'utf8')); } catch (error) { if (error.code === 'ENOENT') return undefined; throw error; } };
|
|
12
|
+
|
|
13
|
+
export function stampedVersion(base, version, revision = 'HEAD') {
|
|
14
|
+
const git = args => execFileSync('git', ['-C', base, ...args], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trim();
|
|
15
|
+
if (git(['tag', '--points-at', revision]).split('\n').includes(`rer-v${version}`)) return version;
|
|
16
|
+
return `${version}-dev.g${git(['rev-parse', '--short=8', revision])}`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// The version of the rer source at `root`: a packed @runeditrun/rer carries it
|
|
20
|
+
// in its package.json; a Base checkout derives it from its version source and
|
|
21
|
+
// Git. undefined for a source tree without the version source.
|
|
22
|
+
export function cliVersion(root) {
|
|
23
|
+
const packed = readJson(join(root, 'package.json'));
|
|
24
|
+
if (packed?.name === packageName) return packed.version;
|
|
25
|
+
const source = readJson(join(root, 'global/package.json'));
|
|
26
|
+
return source ? stampedVersion(root, source.version) : undefined;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// The version a vendored copy recorded (scripts/base-cli/inventory.json), else
|
|
30
|
+
// the version of the rer source this file belongs to. undefined for a vendored
|
|
31
|
+
// copy too old to record one.
|
|
32
|
+
export function runningVersion() {
|
|
33
|
+
const vendored = readJson(new URL('../inventory.json', import.meta.url));
|
|
34
|
+
if (vendored) return vendored.cliVersion;
|
|
35
|
+
return cliVersion(fileURLToPath(new URL('..', import.meta.url)));
|
|
36
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
export const sha256 = (bytes) =>
|
|
4
|
+
createHash("sha256").update(bytes).digest("hex");
|
|
5
|
+
export function git(repo, args) {
|
|
6
|
+
return execFileSync("git", ["--no-replace-objects", "-C", repo, ...args], {
|
|
7
|
+
maxBuffer: 128 * 1024 * 1024,
|
|
8
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
export function commit(repo, ref = "HEAD") {
|
|
12
|
+
if (typeof ref !== "string" || ref.startsWith("-"))
|
|
13
|
+
throw new Error("Invalid Git ref");
|
|
14
|
+
const sha = git(repo, ["rev-parse", "--verify", `${ref}^{commit}`])
|
|
15
|
+
.toString()
|
|
16
|
+
.trim();
|
|
17
|
+
if (!/^[a-f0-9]{40}$/.test(sha)) throw new Error("Expected SHA-1 Git commit");
|
|
18
|
+
return sha;
|
|
19
|
+
}
|
|
20
|
+
export function safePath(path) {
|
|
21
|
+
if (
|
|
22
|
+
typeof path !== "string" ||
|
|
23
|
+
!path ||
|
|
24
|
+
path.includes("\\") ||
|
|
25
|
+
/[\x00-\x1f\x7f]/.test(path) ||
|
|
26
|
+
path.split("/").some((p) => !p || p === "." || p === "..")
|
|
27
|
+
)
|
|
28
|
+
throw new Error(`Unsafe source or target: ${path}`);
|
|
29
|
+
if (
|
|
30
|
+
path
|
|
31
|
+
.split("/")
|
|
32
|
+
.some(
|
|
33
|
+
(p) =>
|
|
34
|
+
[
|
|
35
|
+
".git",
|
|
36
|
+
".seed",
|
|
37
|
+
".worktrees",
|
|
38
|
+
"node_modules",
|
|
39
|
+
"dist",
|
|
40
|
+
".output",
|
|
41
|
+
".wrangler",
|
|
42
|
+
".tanstack",
|
|
43
|
+
"test-results",
|
|
44
|
+
"playwright-report",
|
|
45
|
+
"coverage",
|
|
46
|
+
".DS_Store",
|
|
47
|
+
].includes(p) ||
|
|
48
|
+
/^\.env|^\.dev\.vars|\.(pem|key|p12|pfx|tsbuildinfo)$/.test(p),
|
|
49
|
+
) ||
|
|
50
|
+
/(^|\/)(wrangler\.toml|wrangler\.local\.jsonc?|composition\.lock\.json)$/.test(
|
|
51
|
+
path,
|
|
52
|
+
)
|
|
53
|
+
)
|
|
54
|
+
throw new Error(`State or secret is not source: ${path}`);
|
|
55
|
+
return path;
|
|
56
|
+
}
|
|
57
|
+
export function tree(repo, sha) {
|
|
58
|
+
return git(repo, ["ls-tree", "-rz", "--full-tree", sha])
|
|
59
|
+
.toString()
|
|
60
|
+
.split("\0")
|
|
61
|
+
.filter(Boolean)
|
|
62
|
+
.map((line) => {
|
|
63
|
+
const tab = line.indexOf("\t");
|
|
64
|
+
const header = line.slice(0, tab),
|
|
65
|
+
path = line.slice(tab + 1);
|
|
66
|
+
const [mode, type, oid] = header.split(" ");
|
|
67
|
+
return { path, mode, type, oid };
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
export function blob(repo, sha, path) {
|
|
71
|
+
safePath(path);
|
|
72
|
+
return git(repo, ["show", `${sha}:${path}`]);
|
|
73
|
+
}
|
|
74
|
+
export function json(repo, sha, path) {
|
|
75
|
+
return JSON.parse(blob(repo, sha, path));
|
|
76
|
+
}
|
|
77
|
+
// Base's verification belongs in Base, rather than in each adopting seed.
|
|
78
|
+
export function testSource(path) {
|
|
79
|
+
return path.split('/').some(part => ['test', 'tests', 'fixtures'].includes(part)) ||
|
|
80
|
+
/(?:\.test\.[^/]+|(?:^|\/)test-manifest-support\.mjs$|(?:^|\/)(?:test-fixture|[^/]+-fixture)\.(?:mjs|cjs)$|(?:^|\/)(?:tests|test_[^/]+|[^/]*_tests)\.py|(?:^|\/)browser-(?:check|output)\.mjs)$/.test(path);
|
|
81
|
+
}
|
|
82
|
+
export function select(repo, sha, source, { includeTests = true } = {}) {
|
|
83
|
+
safePath(source);
|
|
84
|
+
const entries = tree(repo, sha).filter(
|
|
85
|
+
(e) => (e.path === source || e.path.startsWith(`${source}/`)) &&
|
|
86
|
+
(includeTests || !testSource(e.path)),
|
|
87
|
+
);
|
|
88
|
+
if (!entries.length) throw new Error(`Missing source: ${source}`);
|
|
89
|
+
return entries.map((e) => {
|
|
90
|
+
safePath(e.path);
|
|
91
|
+
if (e.type !== "blob" || !["100644", "100755"].includes(e.mode))
|
|
92
|
+
throw new Error(`Unsafe source mode: ${e.path}`);
|
|
93
|
+
return {
|
|
94
|
+
path: e.path,
|
|
95
|
+
relative: e.path === source ? "" : e.path.slice(source.length + 1),
|
|
96
|
+
bytes: git(repo, ["cat-file", "blob", e.oid]),
|
|
97
|
+
mode: e.mode === "100755" ? 0o755 : 0o644,
|
|
98
|
+
};
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
export function familyManifest(repo, sha) {
|
|
102
|
+
const data = json(repo, sha, "family.json");
|
|
103
|
+
// Bootstrap validates only what is needed to locate immutable tooling.
|
|
104
|
+
// The pinned exporter owns the complete schema and compatibility rules.
|
|
105
|
+
if (
|
|
106
|
+
typeof data?.base?.commit !== "string" ||
|
|
107
|
+
!/^[a-f0-9]{40}$/.test(data.base.commit)
|
|
108
|
+
)
|
|
109
|
+
throw new Error("family.json needs an immutable base commit");
|
|
110
|
+
return data;
|
|
111
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import crypto from 'node:crypto';
|
|
4
|
+
import {execFileSync} from 'node:child_process';
|
|
5
|
+
export const hash = value => crypto.createHash('sha256').update(value).digest('hex');
|
|
6
|
+
export const json = value => JSON.stringify(value, null, 2) + '\n';
|
|
7
|
+
const environment = {...process.env, GIT_NO_REPLACE_OBJECTS: '1'};
|
|
8
|
+
export function git(root, ...args) {
|
|
9
|
+
return execFileSync('git', ['-C', root, ...args], {env: environment, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe']}).trim();
|
|
10
|
+
}
|
|
11
|
+
// Raw Git output, for committed source bytes. The limit fails explicitly rather than truncating.
|
|
12
|
+
function output(root, args, input) {
|
|
13
|
+
try {
|
|
14
|
+
return execFileSync('git', ['-C', root, ...args], {env: environment, input, maxBuffer: 64 * 1024 * 1024, stdio: [input === undefined ? 'ignore' : 'pipe', 'pipe', 'pipe']});
|
|
15
|
+
} catch (error) {
|
|
16
|
+
if (error.code === 'ENOBUFS') throw Error('Git source output exceeds the 64 MiB acquisition limit');
|
|
17
|
+
throw error;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export const raw = (root, ...args) => output(root, args);
|
|
21
|
+
export const batch = (root, input, ...args) => output(root, args, input);
|
|
22
|
+
export function safe(name) {
|
|
23
|
+
if (typeof name !== 'string' || !name || name.includes('\\') || /[\x00-\x1f\x7f]/.test(name) || path.posix.isAbsolute(name) || name.split('/').some(p => !p || p === '.' || p === '..' || p === '.git')) {
|
|
24
|
+
throw Error(`Unsafe path: ${name}`);
|
|
25
|
+
}
|
|
26
|
+
return name;
|
|
27
|
+
}
|
|
28
|
+
export function noLinks(root, relative) {
|
|
29
|
+
let current = root;
|
|
30
|
+
for (const part of relative.split('/')) {
|
|
31
|
+
current = path.join(current, part);
|
|
32
|
+
let stat;
|
|
33
|
+
try { stat = fs.lstatSync(current); }
|
|
34
|
+
catch (error) { if (error.code !== 'ENOENT') throw error; }
|
|
35
|
+
if (stat?.isSymbolicLink()) throw Error('Symlink in destination path');
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export function tracked(root, allowMissing = false) {
|
|
39
|
+
const result = Object.create(null);
|
|
40
|
+
for (const name of git(root, 'ls-files', '-z').split('\0').filter(Boolean)) {
|
|
41
|
+
safe(name);
|
|
42
|
+
if (name === 'sources.lock.json' || name.startsWith('.edition/')) continue;
|
|
43
|
+
noLinks(root, name);
|
|
44
|
+
if (allowMissing && !fs.existsSync(path.join(root, name))) continue;
|
|
45
|
+
const stat = fs.statSync(path.join(root, name));
|
|
46
|
+
if (!stat.isFile()) throw Error(`Unsupported tracked source: ${name}`);
|
|
47
|
+
result[name] = {data: fs.readFileSync(path.join(root, name)).toString('base64'), executable: !!(stat.mode & 0o111)};
|
|
48
|
+
}
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
export const digest = files => hash(json(files));
|
|
52
|
+
// Base's test, fixture and browser-check sources, which portable copies leave out.
|
|
53
|
+
// Composition pins its tooling alone, so composition/git-source.mjs keeps its own copy.
|
|
54
|
+
export function testSource(path) {
|
|
55
|
+
return path.split('/').some(part => ['test', 'tests', 'fixtures'].includes(part)) ||
|
|
56
|
+
/(?:\.test\.[^/]+|(?:^|\/)test-manifest-support\.mjs$|(?:^|\/)(?:test-fixture|[^/]+-fixture)\.(?:mjs|cjs)$|(?:^|\/)(?:tests|test_[^/]+|[^/]*_tests)\.py|(?:^|\/)browser-(?:check|output)\.mjs)$/.test(path);
|
|
57
|
+
}
|
|
58
|
+
export function writeTree(root, files) {
|
|
59
|
+
// Validate the complete destination set before writing any selected source.
|
|
60
|
+
for (const name of Object.keys(files)) {
|
|
61
|
+
safe(name); noLinks(root, name);
|
|
62
|
+
if (fs.existsSync(path.join(root, name))) throw Error(`Source destination already exists: ${name}`);
|
|
63
|
+
}
|
|
64
|
+
for (const [name, value] of Object.entries(files)) {
|
|
65
|
+
const target = path.join(root, name);
|
|
66
|
+
fs.mkdirSync(path.dirname(target), {recursive: true});
|
|
67
|
+
fs.writeFileSync(target, Buffer.from(value.data, 'base64'), {mode: value.executable ? 0o755 : 0o644, flag: 'wx'});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
export function fresh(destination, root) {
|
|
71
|
+
let absolute = path.resolve(destination);
|
|
72
|
+
let parent = path.dirname(absolute);
|
|
73
|
+
let tail = path.basename(absolute);
|
|
74
|
+
while (!fs.existsSync(parent)) {
|
|
75
|
+
tail = path.join(path.basename(parent), tail);
|
|
76
|
+
parent = path.dirname(parent);
|
|
77
|
+
}
|
|
78
|
+
absolute = path.join(fs.realpathSync(parent), tail);
|
|
79
|
+
if (absolute === root || absolute.startsWith(root + path.sep) || fs.existsSync(absolute)) throw Error('Output must be absent and outside edition');
|
|
80
|
+
return absolute;
|
|
81
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import {safe} from './files.mjs';
|
|
4
|
+
export const VERSION = '1.1.0';
|
|
5
|
+
const identity = /^[a-zA-Z0-9][a-zA-Z0-9._-]*\/[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
|
|
6
|
+
export function origin(value) {
|
|
7
|
+
if (!value || typeof value.repository !== 'string' || !value.repository || value.repository.startsWith('-') || (!value.projection && !/^[a-f0-9]{40}$/.test(value.revision))) {
|
|
8
|
+
throw Error('Source requires repository and immutable full commit');
|
|
9
|
+
}
|
|
10
|
+
if (value.projection) {
|
|
11
|
+
const p = value.projection;
|
|
12
|
+
if (value.revision !== undefined || p.schemaVersion !== 1 || !/^[a-f0-9]{64}$/.test(p.originalSha256) || !/^[a-f0-9]{64}$/.test(p.recipeSha256) || Object.keys(p).sort().join(',') !== 'originalSha256,recipeSha256,schemaVersion') throw Error('Invalid public projection identity');
|
|
13
|
+
}
|
|
14
|
+
if (value.paths) {
|
|
15
|
+
if (!Array.isArray(value.paths) || !value.paths.length) throw Error('Origin paths must be nonempty');
|
|
16
|
+
value.paths.forEach(safe);
|
|
17
|
+
}
|
|
18
|
+
if (value.path !== undefined && value.path !== '.') safe(value.path);
|
|
19
|
+
}
|
|
20
|
+
export function manifest(root) {
|
|
21
|
+
const recipe = JSON.parse(fs.readFileSync(path.join(root, 'edition.json'), 'utf8'));
|
|
22
|
+
if (recipe.schemaVersion !== 1 || !identity.test(recipe.id)) throw Error('Invalid edition identity/schema');
|
|
23
|
+
if (recipe.toolchain?.id !== 'runeditrun/edition-authoring' || recipe.toolchain.version !== VERSION) throw Error('Unsupported independently pinned toolchain');
|
|
24
|
+
if (recipe.parent) origin(recipe.parent);
|
|
25
|
+
if (!Array.isArray(recipe.components)) throw Error('Components required');
|
|
26
|
+
const ids = new Set();
|
|
27
|
+
const paths = [];
|
|
28
|
+
let foundations = 0;
|
|
29
|
+
for (const component of recipe.components) {
|
|
30
|
+
if (!identity.test(component.id) || ids.has(component.id)) throw Error('Duplicate/invalid publisher-qualified component');
|
|
31
|
+
ids.add(component.id);
|
|
32
|
+
if (!['edition', 'module', 'family', 'foundation'].includes(component.role)) throw Error('Invalid component role');
|
|
33
|
+
if (component.role === 'foundation') foundations++;
|
|
34
|
+
const selected = component.source;
|
|
35
|
+
if (selected?.kind === 'local') {
|
|
36
|
+
if (!Array.isArray(selected.paths) || !selected.paths.length) throw Error('Local source paths required');
|
|
37
|
+
selected.paths.forEach(p => paths.push(safe(p)));
|
|
38
|
+
if (selected.origin) origin(selected.origin);
|
|
39
|
+
for (const mapping of selected.mappings || []) {
|
|
40
|
+
safe(mapping.path); safe(mapping.originPath);
|
|
41
|
+
if (!selected.origin || !selected.paths.some(p => mapping.path === p || mapping.path.startsWith(p + '/'))) {
|
|
42
|
+
throw Error('Mapping outside local source or missing origin');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
} else if (selected?.kind === 'git') {
|
|
46
|
+
origin(selected);
|
|
47
|
+
paths.push(safe(selected.destination));
|
|
48
|
+
if (selected.workspace && !path.isAbsolute(selected.workspace)) throw Error('Workspace must be an explicit absolute checkout');
|
|
49
|
+
} else throw Error('Unknown source kind');
|
|
50
|
+
}
|
|
51
|
+
for (const component of recipe.components) {
|
|
52
|
+
for (const required of component.requires || []) if (!ids.has(required)) throw Error(`Missing selected dependency: ${required}`);
|
|
53
|
+
for (const conflict of component.conflicts || []) if (ids.has(conflict)) throw Error(`Conflicting selected component: ${conflict}`);
|
|
54
|
+
}
|
|
55
|
+
if (foundations > 1) throw Error('Select one runtime foundation');
|
|
56
|
+
for (let i = 0; i < paths.length; i++) {
|
|
57
|
+
if (paths[i] === 'edition.json' || paths[i] === 'sources.lock.json' || paths[i].startsWith('.edition')) throw Error('Reserved source destination');
|
|
58
|
+
for (let j = 0; j < i; j++) {
|
|
59
|
+
if (paths[i] === paths[j] || paths[i].startsWith(paths[j] + '/') || paths[j].startsWith(paths[i] + '/')) {
|
|
60
|
+
throw Error(`Colliding source paths: ${paths[i]} and ${paths[j]}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return recipe;
|
|
65
|
+
}
|
package/global/README.md
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# rer
|
|
2
|
+
|
|
3
|
+
`rer` is the Run Edit Run command. Install it once, then run `rer <verb>` in any
|
|
4
|
+
Run Edit Run checkout: every verb runs that checkout's own pinned copy, so a
|
|
5
|
+
checkout always gets the behaviour it was built and tested with.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
Any one of these leaves `rer` on your PATH:
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
npx -y @runeditrun/rer cli install # user install in ~/.local/share/rer, launcher ~/.local/bin/rer
|
|
13
|
+
curl -fsSL https://runeditrun.com/install.sh | sh # the same user install, without npm
|
|
14
|
+
npm install -g @runeditrun/rer # npm's own global layout
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
`install.sh` is the file at the root of this package. It reads the version, the
|
|
18
|
+
tarball and its sha512 integrity from the npm registry (`npm_config_registry`,
|
|
19
|
+
default `https://registry.npmjs.org`; `RER_VERSION=X` pins an exact version,
|
|
20
|
+
otherwise the latest), checks the download's integrity, file count and size,
|
|
21
|
+
refuses archives with absolute paths, `..` entries, links or devices, then runs
|
|
22
|
+
the package's own `rer cli install`. A failed or refused download leaves the
|
|
23
|
+
version you had selected.
|
|
24
|
+
|
|
25
|
+
No installer edits shell profiles. When `~/.local/bin` is not on your PATH, the
|
|
26
|
+
install prints the exact line to add for your shell. The launcher runs `node` from
|
|
27
|
+
PATH, so a Node upgrade does not break it. A re-run with the same build changes
|
|
28
|
+
nothing; a different build is installed beside the current one, the previous one
|
|
29
|
+
is kept and older ones are removed. It refuses to replace a `~/.local/bin/rer`
|
|
30
|
+
that it did not write.
|
|
31
|
+
|
|
32
|
+
`rer --version` prints this rer's version; inside a checkout a second line
|
|
33
|
+
names the version the checkout pins, or says that its vendored copy is
|
|
34
|
+
unversioned (exported before copies recorded one).
|
|
35
|
+
|
|
36
|
+
`rer cli status` shows the version, the digest of the installed contents, the
|
|
37
|
+
install channel (`npx`, `script` or `npm-global`), the launcher, the install root
|
|
38
|
+
and every other `rer` on PATH. `rer cli uninstall` removes the launcher and the
|
|
39
|
+
install root it runs only; `~/.rer` (servers, jobs, Node runtimes) is untouched.
|
|
40
|
+
An npm global `rer` prints its `npm uninstall -g` command and leaves a user
|
|
41
|
+
install in place.
|
|
42
|
+
|
|
43
|
+
### Requirements
|
|
44
|
+
|
|
45
|
+
- macOS or Linux, on x64 or arm64. WSL2 counts as Linux.
|
|
46
|
+
- Node 22 or later on PATH; `rer` stops with one line naming the minimum on an
|
|
47
|
+
older Node.
|
|
48
|
+
- For `install.sh`: `curl` and `tar`.
|
|
49
|
+
- For `rer dev` in a checkout: the checkout's own package manager (pnpm for
|
|
50
|
+
Run Edit Run seeds).
|
|
51
|
+
|
|
52
|
+
Not supported: native Windows (`install.sh` and `rer` say to use WSL2), and
|
|
53
|
+
`OPERATIONS.json` version 1 projects.
|
|
54
|
+
|
|
55
|
+
## Update
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
rer update # the latest published version
|
|
59
|
+
rer update --version X # exactly X
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`rer update` follows the way `rer` was installed. A user install (`npx` or
|
|
63
|
+
`install.sh`) downloads the version from the registry, checks it as `install.sh`
|
|
64
|
+
does and switches to it atomically, keeping the previous version and the same
|
|
65
|
+
channel; a damaged or mismatched download leaves the current version selected.
|
|
66
|
+
It says "already current" when the registry's version has the same contents. A
|
|
67
|
+
development build (`<version>-dev.g<sha8>`) moves to the release once that
|
|
68
|
+
version is published, and a different build of the same version counts as an
|
|
69
|
+
update. An npm global install runs `npm install -g @runeditrun/rer@X` for its own
|
|
70
|
+
prefix; when npm fails (for example, a prefix only root can write), `rer update`
|
|
71
|
+
exits non-zero with npm's message and the exact command to run.
|
|
72
|
+
|
|
73
|
+
In a terminal, `rer` checks the registry at most once a day and prints one line
|
|
74
|
+
when a newer version is published. It never prints in `--json` output or when
|
|
75
|
+
output is not a terminal, never delays a command by more than about 1.5 seconds,
|
|
76
|
+
and `RER_NO_UPDATE_CHECK=1` turns it off.
|
|
77
|
+
|
|
78
|
+
## Launch a checkout
|
|
79
|
+
|
|
80
|
+
```sh
|
|
81
|
+
rer dev
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
In a first-party checkout this is the whole launch. It installs dependencies when
|
|
85
|
+
`node_modules` is missing, incomplete (no `node_modules/.modules.yaml`, or an
|
|
86
|
+
install a launch started did not finish) or older than the lockfile, sets up a local owner
|
|
87
|
+
(`owner@<checkout-name>.test`) on the first free port from 8787 when the checkout
|
|
88
|
+
has never been set up (`--port N` picks the port), fills any setup answer the
|
|
89
|
+
seed declares a `localDefault` for in `rer-setup.json` (a literal, a generated
|
|
90
|
+
secret kept only in the checkout's private settings, or the chosen local
|
|
91
|
+
origin), starts the app and prints:
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
RER_READY http://127.0.0.1:8787
|
|
95
|
+
RER_SIGNED_IN owner@cradle.test
|
|
96
|
+
RER_OPEN http://127.0.0.1:8787/__dev/key#...
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
or `RER_SIGN_IN <outbox>` for a seed that signs in through its development outbox.
|
|
100
|
+
It never prompts and never re-runs setup on a checkout that is already set up. A
|
|
101
|
+
setup a launch started that was stopped or failed is run again by the next
|
|
102
|
+
`rer dev`, keeping the owner and origin it saved, unless a setup has finished
|
|
103
|
+
there since (for example your own `rer setup`). A checkout whose pin reports it is
|
|
104
|
+
not set up (`rer describe`'s `local.prepared`) but that already holds an owner
|
|
105
|
+
(`local.ownerSaved`, for example when a newer manifest requires another setting)
|
|
106
|
+
finishes setup keeping that owner and origin. A required setup answer with no value and no
|
|
107
|
+
`localDefault` stops the launch with exit 3 and setup's message naming it.
|
|
108
|
+
|
|
109
|
+
Inside a checkout `rer` acts on the nearest `OPERATIONS.json` at or above the
|
|
110
|
+
working directory; `--cwd DIR` names the project root itself, exactly as
|
|
111
|
+
`node scripts/rer.mjs` reads it.
|
|
112
|
+
|
|
113
|
+
Without a terminal (an agent's shell tool) it returns as soon as the app is ready
|
|
114
|
+
and leaves it running, printing the server's private log path; in a terminal it
|
|
115
|
+
stays in the foreground and Ctrl-C stops it. `--detach` and `--foreground`
|
|
116
|
+
override the choice, and `--json` prints `progress` events and the ready event. A
|
|
117
|
+
first launch can take minutes: it prints a progress line at least every 30
|
|
118
|
+
seconds, and if the calling command is killed the launch carries on; run
|
|
119
|
+
`rer dev` again to reattach. A second `rer dev` reprints the ready lines.
|
|
120
|
+
`rer dev --stop` stops the server.
|
|
121
|
+
|
|
122
|
+
Servers started through `rer` (`rer dev`, `rer preview`) get a 10-server limit and a 12-hour idle timeout
|
|
123
|
+
unless you set `RER_SERVER_LIMIT`, `RER_SERVER_IDLE_SECONDS` or `--idle-timeout`.
|
|
124
|
+
`rer` supplies them only to checkouts whose pinned copy applies lower defaults
|
|
125
|
+
or does not report its own (`rer describe` reports `defaults`), so a current
|
|
126
|
+
checkout never records a limit you did not set.
|
|
127
|
+
A start is never refused for machine load alone unless `RER_SERVERS_LOAD_THRESHOLD`
|
|
128
|
+
or `RER_JOBS_LOAD_THRESHOLD` is set; memory and disk guards stay.
|
|
129
|
+
|
|
130
|
+
## Find what to launch
|
|
131
|
+
|
|
132
|
+
```sh
|
|
133
|
+
rer list [DIR] [--json]
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
lists the checkouts in a folder, newest change first, with their profile
|
|
137
|
+
(`first-party`, `independent` or `unsupported`), whether `rer dev` can launch
|
|
138
|
+
them, whether they are set up, and their running URL.
|
|
139
|
+
|
|
140
|
+
Independent projects (an `OPERATIONS.json` version 2 without a seed contract) run
|
|
141
|
+
their declared commands with `rer <verb> [-- args]`; zero-setup launching does not
|
|
142
|
+
apply to them. `OPERATIONS.json` version 1 is not supported.
|
|
143
|
+
|
|
144
|
+
## Verbs
|
|
145
|
+
|
|
146
|
+
In a checkout, every verb runs that checkout's own pinned `rer`; `rer VERB --help`
|
|
147
|
+
there lists its flags:
|
|
148
|
+
|
|
149
|
+
`install`, `setup`, `dev`, `preview`, `build`, `verify`, `reset --yes`, `deploy`,
|
|
150
|
+
`export`, `import`, `outbox`, `doctor`, `describe`, `check`, `release`.
|
|
151
|
+
|
|
152
|
+
Anywhere: `rer list [DIR]`, `rer cli status|install|uninstall`, `rer update`,
|
|
153
|
+
`rer --version` and `rer --help`.
|
|
154
|
+
|
|
155
|
+
Licence: MIT.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// Find the project an invocation acts on: the nearest OPERATIONS.json at or
|
|
2
|
+
// above the working directory, or exactly --cwd DIR. Then pick whose code runs it.
|
|
3
|
+
import { existsSync, readFileSync, realpathSync, statSync } from 'node:fs';
|
|
4
|
+
import { dirname, join, resolve } from 'node:path';
|
|
5
|
+
import { ownRoot, packageName } from './package-info.mjs';
|
|
6
|
+
|
|
7
|
+
// The value of --cwd among the wrapper flags (never after a -- separator).
|
|
8
|
+
function cwdFlag(args) {
|
|
9
|
+
for (let i = 0; i < args.length; i++) {
|
|
10
|
+
if (args[i] === '--') return undefined;
|
|
11
|
+
if (args[i] === '--cwd') return { index: i + 1, value: args[i + 1] };
|
|
12
|
+
}
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function descriptorProfile(file) {
|
|
17
|
+
let descriptor;
|
|
18
|
+
try { descriptor = JSON.parse(readFileSync(file, 'utf8')); } catch { return { profile: 'unreadable' }; }
|
|
19
|
+
if (descriptor?.version === 2) return { profile: descriptor.seedContract ? 'first-party' : 'independent' };
|
|
20
|
+
return { profile: 'unsupported', version: descriptor?.version };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const projectRoot = directory => { try { return statSync(join(directory, 'OPERATIONS.json')).isFile() ? realpathSync(directory) : undefined; } catch { return undefined; } };
|
|
24
|
+
|
|
25
|
+
// --cwd names the project root itself, as the pinned dispatcher reads it: no
|
|
26
|
+
// upward search. When it names no project, the dispatcher of the checkout
|
|
27
|
+
// around the working directory (else this package's own) receives it
|
|
28
|
+
// unchanged, as an absolute path, and refuses it with its own message.
|
|
29
|
+
export function locate(argv) {
|
|
30
|
+
const flag = cwdFlag(argv.slice(1)), args = [...argv];
|
|
31
|
+
if (flag?.value !== undefined) {
|
|
32
|
+
// A value the dispatcher's parser rejects (empty, or another flag) reaches it as given.
|
|
33
|
+
const usable = flag.value.trim() && !flag.value.startsWith('--'), named = resolve(process.cwd(), flag.value), root = usable ? projectRoot(named) : undefined;
|
|
34
|
+
if (root) { args[flag.index + 1] = root; return { root, args, ...descriptorProfile(join(root, 'OPERATIONS.json')) }; }
|
|
35
|
+
if (usable) args[flag.index + 1] = named;
|
|
36
|
+
const near = around(process.cwd());
|
|
37
|
+
return { root: near && ['first-party', 'independent'].includes(descriptorProfile(join(near, 'OPERATIONS.json')).profile) ? near : undefined, args, profile: 'refused' };
|
|
38
|
+
}
|
|
39
|
+
const root = around(process.cwd());
|
|
40
|
+
return { root, args, ...(root ? descriptorProfile(join(root, 'OPERATIONS.json')) : { profile: 'none' }) };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// The nearest project root at or above a directory.
|
|
44
|
+
function around(start) {
|
|
45
|
+
for (let directory = start; ; directory = dirname(directory)) {
|
|
46
|
+
const root = projectRoot(directory);
|
|
47
|
+
if (root) return root;
|
|
48
|
+
if (dirname(directory) === directory) return undefined;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function unsupportedMessage(root, version) {
|
|
53
|
+
return `${join(root, 'OPERATIONS.json')} is an OPERATIONS version ${version ?? '(none)'} descriptor, which rer does not run. Use the commands that project documents, or move it to OPERATIONS version 2.`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function declaresPackage(root) {
|
|
57
|
+
try {
|
|
58
|
+
const manifest = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'));
|
|
59
|
+
return Boolean(manifest.dependencies?.[packageName] ?? manifest.devDependencies?.[packageName]);
|
|
60
|
+
} catch { return false; }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// 1. the checkout's own pinned package, 2. its vendored scripts/rer.mjs,
|
|
64
|
+
// 3. this package's bundled dispatcher.
|
|
65
|
+
export function dispatcher(root) {
|
|
66
|
+
if (root && declaresPackage(root)) {
|
|
67
|
+
const pinned = join(root, 'node_modules', packageName);
|
|
68
|
+
if (existsSync(join(pinned, 'global/rer.mjs')) && realpathSync(pinned) !== ownRoot) return { mode: 'package', entry: join(realpathSync(pinned), 'global/rer.mjs') };
|
|
69
|
+
}
|
|
70
|
+
if (root && existsSync(join(root, 'scripts/rer.mjs'))) return { mode: 'vendored', entry: join(root, 'scripts/rer.mjs') };
|
|
71
|
+
return { mode: 'bundled', entry: join(ownRoot, 'bin/rer.mjs') };
|
|
72
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// rer cli status|install|uninstall
|
|
2
|
+
const usage = 'Usage: rer cli status [--json] | rer cli install | rer cli uninstall\n';
|
|
3
|
+
|
|
4
|
+
export async function cliCommand(args) {
|
|
5
|
+
const [action, ...flags] = args;
|
|
6
|
+
if (!action || ['--help', '-h'].includes(action)) { process.stdout.write(usage); return 0; }
|
|
7
|
+
const allowed = action === 'status' ? ['--json'] : [];
|
|
8
|
+
const unknown = flags.find(flag => !allowed.includes(flag));
|
|
9
|
+
if (!['status', 'install', 'uninstall'].includes(action) || unknown !== undefined) { process.stderr.write(`Unsupported: rer cli ${args.join(' ')}\n${usage}`); return 2; }
|
|
10
|
+
if (action === 'status') return (await import('./status.mjs')).status({ json: flags.includes('--json') });
|
|
11
|
+
if (action === 'install') return (await import('./install.mjs')).install();
|
|
12
|
+
return (await import('./uninstall.mjs')).uninstall();
|
|
13
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// rer cli install: copy the running package's own bytes into an immutable
|
|
2
|
+
// version directory, switch `current` atomically, keep the previous version,
|
|
3
|
+
// prune older ones and write the launcher. Never edits shell profiles.
|
|
4
|
+
import { randomUUID } from 'node:crypto';
|
|
5
|
+
import { chmod, cp, mkdir, readdir, readlink, rename, rm, symlink, writeFile } from 'node:fs/promises';
|
|
6
|
+
import { basename, join, resolve } from 'node:path';
|
|
7
|
+
import { ownManifest, ownRoot, packageDigest, packageFileList } from '../package-info.mjs';
|
|
8
|
+
import { installRecord, launcherOwner, launcherScript, layout } from './layout.mjs';
|
|
9
|
+
|
|
10
|
+
async function readLink(path) { try { return await readlink(path); } catch (error) { if (['ENOENT', 'EINVAL'].includes(error.code)) return undefined; throw error; } }
|
|
11
|
+
|
|
12
|
+
// Files read-only (immutable by intent); directories stay removable for pruning.
|
|
13
|
+
async function freeze(root) {
|
|
14
|
+
for (const path of await packageFileList(root)) await chmod(join(root, path), path.endsWith('global/rer.mjs') || path === 'bin/rer.mjs' ? 0o555 : 0o444);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function copyVersion(paths, name) {
|
|
18
|
+
const staging = join(paths.versions, `.staging-${randomUUID()}`);
|
|
19
|
+
await mkdir(paths.versions, { recursive: true });
|
|
20
|
+
try {
|
|
21
|
+
await cp(ownRoot, staging, { recursive: true });
|
|
22
|
+
await freeze(staging);
|
|
23
|
+
await rename(staging, join(paths.versions, name));
|
|
24
|
+
} finally { await rm(staging, { recursive: true, force: true }); }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function switchCurrent(paths, name) {
|
|
28
|
+
const temporary = `${paths.current}.${randomUUID()}`;
|
|
29
|
+
await symlink(join('versions', name), temporary);
|
|
30
|
+
await rename(temporary, paths.current);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function writeLauncher(paths) {
|
|
34
|
+
await mkdir(paths.bin, { recursive: true });
|
|
35
|
+
const temporary = `${paths.launcher}.${randomUUID()}`;
|
|
36
|
+
await writeFile(temporary, launcherScript(paths.data), { mode: 0o755 });
|
|
37
|
+
await rename(temporary, paths.launcher);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function pathAdvice(paths, env = process.env) {
|
|
41
|
+
const entries = (env.PATH ?? '').split(':').filter(Boolean).map(entry => resolve(entry));
|
|
42
|
+
if (entries.includes(resolve(paths.bin))) return undefined;
|
|
43
|
+
const shell = basename(env.SHELL ?? '');
|
|
44
|
+
if (shell === 'fish') return `${paths.bin} is not on your PATH. Run this once in fish, then open a new terminal:\n fish_add_path $HOME/.local/bin`;
|
|
45
|
+
const profile = shell === 'zsh' ? '~/.zshrc' : shell === 'bash' ? (process.platform === 'darwin' ? '~/.bash_profile' : '~/.bashrc') : '~/.profile';
|
|
46
|
+
return `${paths.bin} is not on your PATH. Add this line to ${profile}, then open a new terminal:\n export PATH="$HOME/.local/bin:$PATH"`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function install() {
|
|
50
|
+
const paths = layout(), manifest = ownManifest(), digest = await packageDigest(ownRoot);
|
|
51
|
+
const name = `${manifest.version}-${digest.slice(0, 8)}`, channel = process.env.RER_INSTALL_CHANNEL === 'script' ? 'script' : 'npx';
|
|
52
|
+
const launcher = await launcherOwner(paths.launcher);
|
|
53
|
+
if (launcher.state === 'foreign') {
|
|
54
|
+
process.stderr.write(`rer cli install: ${paths.launcher} already exists and was not written by rer cli install (it may be the unrelated npm package "rer"). Nothing was changed. Remove or rename it, then run the install again.\n`);
|
|
55
|
+
return 4;
|
|
56
|
+
}
|
|
57
|
+
const record = await installRecord(paths), current = await readLink(paths.current);
|
|
58
|
+
const script = launcherScript(paths.data);
|
|
59
|
+
let present = false;
|
|
60
|
+
try { present = await packageDigest(join(paths.versions, name)) === digest; } catch (error) { if (error.code !== 'ENOENT') throw error; }
|
|
61
|
+
const advice = pathAdvice(paths);
|
|
62
|
+
if (present && current === join('versions', name) && launcher.text === script && record?.channel === channel) {
|
|
63
|
+
process.stdout.write(`rer ${manifest.version} is already current (${join(paths.versions, name)}). Nothing changed.\n${advice ? advice + '\n' : ''}`);
|
|
64
|
+
return 0;
|
|
65
|
+
}
|
|
66
|
+
if (!present) { await rm(join(paths.versions, name), { recursive: true, force: true }); await copyVersion(paths, name); }
|
|
67
|
+
const previousName = current?.startsWith('versions/') && current !== join('versions', name) ? current.slice('versions/'.length) : record?.previous;
|
|
68
|
+
if (current !== join('versions', name)) await switchCurrent(paths, name);
|
|
69
|
+
const keep = new Set([name, previousName].filter(Boolean));
|
|
70
|
+
for (const entry of await readdir(paths.versions)) if (!keep.has(entry) && !entry.startsWith('.staging-')) await rm(join(paths.versions, entry), { recursive: true, force: true });
|
|
71
|
+
await writeFile(paths.record, JSON.stringify({ channel, previous: previousName && keep.has(previousName) ? previousName : null }, null, 2) + '\n', { mode: 0o644 });
|
|
72
|
+
if (launcher.text !== script) await writeLauncher(paths);
|
|
73
|
+
process.stdout.write(`Installed rer ${manifest.version} in ${join(paths.versions, name)}.\n${paths.launcher} runs it.${previousName ? ` The previous version (${previousName}) is kept.` : ''}\n${advice ? advice + '\n' : ''}`);
|
|
74
|
+
return 0;
|
|
75
|
+
}
|