@starci/hfs 5.1.0 → 5.3.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/CHANGELOG.md +54 -0
- package/README.md +11 -2
- package/cache/hfs-family.d.mts +2 -3
- package/cache/hfs-family.mjs +4 -9
- package/cache/secret-records.d.mts +7 -0
- package/cache/secret-records.mjs +12 -0
- package/cache/session.mjs +4 -2
- package/codemod/account-doors.d.mts +36 -0
- package/codemod/account-doors.mjs +128 -0
- package/codemod/main.mjs +31 -1
- package/codemod/module-definition.d.mts +52 -0
- package/codemod/module-definition.mjs +242 -0
- package/codemod/quality-binding.d.mts +33 -0
- package/codemod/quality-binding.mjs +38 -0
- package/codemod/secrets-cache.d.mts +35 -0
- package/codemod/secrets-cache.mjs +70 -0
- package/codemod/sops-rules.d.mts +34 -0
- package/codemod/sops-rules.mjs +69 -0
- package/codemod/theme-tokens.mjs +48 -2
- package/db/postgres.mjs +9 -1
- package/lint/run.d.mts +6 -0
- package/lint/run.mjs +63 -10
- package/package.json +1 -1
- package/runtime/knowledge/hfs/canon-pins.yaml +65 -11
- package/runtime/knowledge/hfs/fe-stack.yaml +9 -9
- package/runtime/knowledge/hfs/fork-images.yaml +7 -0
- package/runtime/knowledge/hfs/rules.yaml +67 -15
- package/runtime/knowledge/hfs/slots.yaml +174 -30
- package/runtime/modules/kernel/failure-codes.yaml +67 -4
- package/runtime/modules/schemas/application-stacks.schema.yaml +6 -0
- package/runtime/scripts/hfs/architecture/config.d.mts +4 -0
- package/runtime/scripts/hfs/architecture/config.mjs +25 -1
- package/runtime/scripts/hfs/architecture/identity.mjs +18 -83
- package/runtime/scripts/hfs/architecture/module-per-transport.d.mts +1 -1
- package/runtime/scripts/hfs/architecture/module-per-transport.mjs +6 -1
- package/runtime/scripts/hfs/architecture/root-packages.d.mts +26 -0
- package/runtime/scripts/hfs/architecture/root-packages.mjs +78 -0
- package/runtime/scripts/hfs/architecture/transport-owner.d.mts +1 -2
- package/runtime/scripts/hfs/architecture/transport-owner.mjs +3 -6
- package/runtime/scripts/hfs/architecture/type-context.d.mts +4 -1
- package/runtime/scripts/hfs/architecture/type-context.mjs +30 -0
- package/runtime/scripts/hfs/architecture/typescript.d.mts +7 -1
- package/runtime/scripts/hfs/architecture/typescript.mjs +21 -1
- package/runtime/scripts/hfs/explicit-surface-rule.mjs +1 -1
- package/runtime/scripts/hfs/manifest-shape.d.mts +1 -0
- package/runtime/scripts/hfs/manifest-shape.mjs +3 -1
- package/runtime/scripts/hfs/rules/a15-findings.mjs +5 -0
- package/runtime/scripts/hfs/rules/database-access.mjs +39 -1
- package/runtime/scripts/hfs/rules/monorepo.d.mts +4 -0
- package/runtime/scripts/hfs/rules/monorepo.mjs +10 -5
- package/runtime/scripts/hfs/rules/secrets.mjs +11 -0
- package/runtime/scripts/hfs/rules/stacks-local.d.mts +7 -0
- package/runtime/scripts/hfs/rules/stacks-local.mjs +71 -0
- package/runtime/scripts/hfs/rules/stacks-runtime.mjs +6 -1
- package/runtime/scripts/hfs/rules/stacks-service-images.d.mts +12 -0
- package/runtime/scripts/hfs/rules/stacks-service-images.mjs +120 -0
- package/runtime/scripts/hfs/secret-dev-init.d.mts +11 -0
- package/runtime/scripts/hfs/secret-dev-init.mjs +79 -0
- package/runtime/scripts/hfs/secret-errors.d.mts +3 -0
- package/runtime/scripts/hfs/secret-errors.mjs +8 -0
- package/runtime/scripts/hfs/secret-init.d.mts +6 -0
- package/runtime/scripts/hfs/secret-init.mjs +23 -0
- package/runtime/scripts/hfs/secret-sops.mjs +9 -1
- package/runtime/scripts/hfs/secret-standard.d.mts +20 -0
- package/runtime/scripts/hfs/secret-standard.mjs +106 -0
- package/runtime/scripts/hfs/secret.mjs +15 -8
- package/runtime/scripts/hfs/slots.mjs +1 -1
- package/runtime/scripts/hfs/stack-local-config.d.mts +101 -0
- package/runtime/scripts/hfs/stack-local-config.mjs +320 -0
- package/runtime/scripts/hfs/stacks-gate.mjs +1 -1
- package/runtime/scripts/hfs/view.d.mts +23 -0
- package/runtime/scripts/hfs/view.mjs +68 -0
- package/runtime/scripts/lib/declaration-surface.d.mts +4 -0
- package/runtime/scripts/lib/declaration-surface.mjs +55 -1
- package/runtime/scripts/lib/secret-records.d.mts +13 -0
- package/runtime/scripts/lib/secret-records.mjs +83 -0
- package/runtime/scripts/lib/swarm-stack.d.mts +2 -1
- package/runtime/scripts/lib/swarm-stack.mjs +2 -2
- package/scaffold/add-inbox-lite.mjs +2 -2
- package/scaffold/add-module.d.mts +16 -0
- package/scaffold/add-module.mjs +232 -0
- package/scaffold/add-queue-lite.mjs +50 -3
- package/scaffold/add.mjs +5 -0
- package/scaffold/app.d.mts +2 -2
- package/scaffold/app.mjs +16 -18
- package/scaffold/lite-exports.mjs +1 -15
- package/scaffold/module-registry.d.mts +19 -0
- package/scaffold/module-registry.mjs +84 -0
- package/scripts/sync-bundles.d.mts +8 -0
- package/src/main.mjs +23 -2
- package/stack/cli.mjs +6 -1
- package/stack/context.d.mts +15 -0
- package/stack/context.mjs +1 -0
- package/stack/images/openclaw-runtime/Dockerfile +18 -0
- package/stack/images/openclaw-runtime/entrypoint.sh +23 -0
- package/stack/images/openclaw-runtime/openclaw-init.mjs +101 -0
- package/stack/images/openclaw-runtime/openclaw-relay.mjs +25 -0
- package/stack/local-cli.d.mts +25 -0
- package/stack/local-cli.mjs +89 -0
- package/stack/local-config.d.mts +17 -0
- package/stack/local-config.mjs +37 -0
- package/stack/local-custody.d.mts +24 -0
- package/stack/local-custody.mjs +109 -0
- package/stack/local-doctor.d.mts +6 -0
- package/stack/local-doctor.mjs +49 -0
- package/stack/local-down.d.mts +8 -0
- package/stack/local-down.mjs +20 -0
- package/stack/local-host.d.mts +42 -0
- package/stack/local-host.mjs +163 -0
- package/stack/local-logs.d.mts +8 -0
- package/stack/local-logs.mjs +32 -0
- package/stack/local-ports.d.mts +7 -0
- package/stack/local-ports.mjs +39 -0
- package/stack/local-prepare.d.mts +22 -0
- package/stack/local-prepare.mjs +62 -0
- package/stack/local-run.d.mts +71 -0
- package/stack/local-run.mjs +200 -0
- package/stack/local-state.d.mts +35 -0
- package/stack/local-state.mjs +29 -0
- package/stack/local-status.d.mts +6 -0
- package/stack/local-status.mjs +36 -0
- package/stack/local-up.d.mts +11 -0
- package/stack/local-up.mjs +169 -0
- package/stack/local-verify.d.mts +6 -0
- package/stack/local-verify.mjs +21 -0
- package/stack/runtime-layers.d.mts +21 -0
- package/stack/runtime-layers.mjs +67 -0
- package/sync/hygiene.mjs +3 -1
- package/sync/index.d.mts +15 -12
- package/sync/index.mjs +114 -28
- package/templates/app/auth-doors.sql.partial +115 -0
- package/templates/app/ci-workflows/github/workflows/ci.yml +1 -1
- package/templates/app/ci-workflows-lite/github/workflows/ci.yml +1 -1
- package/templates/app/module/jest.config.js.tpl +8 -0
- package/templates/app/module/src/index.ts.tpl +2 -0
- package/templates/app/module/src/manifest/declaration.spec.ts.tpl +11 -0
- package/templates/app/module/src/manifest/declaration.ts.tpl +13 -0
- package/templates/app/module/src/manifest/index.ts.tpl +1 -0
- package/templates/app/module/src/public/index.ts.tpl +2 -0
- package/templates/app/module/src/template/__name__.template.spec.ts.tpl +13 -0
- package/templates/app/module/src/template/__name__.template.ts.tpl +41 -0
- package/templates/app/module/src/template/index.ts.tpl +1 -0
- package/templates/app/module/src/workbench/components/__pascal__WorkbenchView.tsx.tpl +16 -0
- package/templates/app/module/src/workbench/index.ts.tpl +2 -0
- package/templates/app/package-scripts/core.json +1 -1
- package/templates/app/package-scripts/package.json +1 -1
- package/templates/app/package-scripts-lite/package.json +1 -1
- package/templates/app/skeleton-lite/.starcistacks/application-stacks.yaml +85 -70
- package/templates/app/skeleton-lite/.starcistacks/dev/README.md +24 -12
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/compose/compose.yaml +43 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/runtime/env/KEYS.md +21 -26
- package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/stack.yaml +6 -0
- package/templates/app/skeleton-lite/.starcistacks/production/runtime/env/KEYS.md +6 -1
- package/templates/app/skeleton-lite/db/migrations/__timestamp___baseline.sql +76 -13
- package/templates/be/auth-lite/src/features/api/auth/application/auth.contracts.ts +44 -0
- package/templates/be/auth-lite/src/features/api/auth/application/confirm.command.ts +11 -0
- package/templates/be/auth-lite/src/features/api/auth/application/confirm.handler.ts +22 -0
- package/templates/be/auth-lite/src/features/api/auth/application/forgot.command.ts +11 -0
- package/templates/be/auth-lite/src/features/api/auth/application/forgot.handler.ts +22 -0
- package/templates/be/auth-lite/src/features/api/auth/application/get-jwks.handler.ts +23 -0
- package/templates/be/auth-lite/src/features/api/auth/application/get-jwks.query.ts +12 -0
- package/templates/be/auth-lite/src/features/api/auth/application/google-begin.command.ts +11 -0
- package/templates/be/auth-lite/src/features/api/auth/application/google-begin.handler.ts +22 -0
- package/templates/be/auth-lite/src/features/api/auth/application/google-complete.command.ts +12 -0
- package/templates/be/auth-lite/src/features/api/auth/application/google-complete.handler.ts +23 -0
- package/templates/be/auth-lite/src/features/api/auth/application/login.command.ts +12 -0
- package/templates/be/auth-lite/src/features/api/auth/application/login.handler.ts +23 -0
- package/templates/be/auth-lite/src/features/api/auth/application/logout.command.ts +11 -0
- package/templates/be/auth-lite/src/features/api/auth/application/logout.handler.ts +22 -0
- package/templates/be/auth-lite/src/features/api/auth/application/refresh.command.ts +12 -0
- package/templates/be/auth-lite/src/features/api/auth/application/refresh.handler.ts +23 -0
- package/templates/be/auth-lite/src/features/api/auth/application/resend.command.ts +11 -0
- package/templates/be/auth-lite/src/features/api/auth/application/resend.handler.ts +22 -0
- package/templates/be/auth-lite/src/features/api/auth/application/reset.command.ts +11 -0
- package/templates/be/auth-lite/src/features/api/auth/application/reset.handler.ts +22 -0
- package/templates/be/auth-lite/src/features/api/auth/application/session.command.ts +12 -0
- package/templates/be/auth-lite/src/features/api/auth/application/session.handler.ts +23 -0
- package/templates/be/auth-lite/src/features/api/auth/application/signup.command.ts +11 -0
- package/templates/be/auth-lite/src/features/api/auth/application/signup.handler.ts +22 -0
- package/templates/be/auth-lite/src/features/api/auth/auth.module-definition.ts +6 -0
- package/templates/be/auth-lite/src/features/api/auth/auth.module.ts +21 -0
- package/templates/be/auth-lite/src/features/api/auth/auth.options.ts +3 -0
- package/templates/be/auth-lite/src/features/api/auth/index.ts +2 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/auth-http.module-definition.ts +6 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/auth-http.module.ts +9 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/auth-http.options.ts +3 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/auth.controller.ts +145 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/dto/forgot.request.ts +16 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/dto/google-start.request.ts +17 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/dto/login.request.ts +16 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/dto/resend.request.ts +16 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/dto/reset.request.ts +18 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/dto/signup.request.ts +22 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/dto/token.request.ts +11 -0
- package/templates/be/auth-lite/src/modules/domain/identity/account-directory.service.spec.ts +46 -0
- package/templates/be/auth-lite/src/modules/domain/identity/account-directory.service.ts +19 -0
- package/templates/be/auth-lite/src/modules/domain/identity/authenticator.service.spec.ts +209 -0
- package/templates/be/auth-lite/src/modules/domain/identity/authenticator.service.ts +84 -0
- package/templates/be/auth-lite/src/modules/domain/identity/identity-email.log-events.ts +6 -0
- package/templates/be/auth-lite/src/modules/domain/identity/identity-email.service.spec.ts +233 -0
- package/templates/be/auth-lite/src/modules/domain/identity/identity-email.service.ts +176 -0
- package/templates/be/auth-lite/src/modules/domain/identity/identity-sign-in.log-events.ts +6 -0
- package/templates/be/auth-lite/src/modules/domain/identity/persistence/account-doors.port.ts +98 -0
- package/templates/be/auth-lite/src/modules/domain/identity/persistence/entities/identity-attempt.entity.ts +18 -0
- package/templates/be/auth-lite/src/modules/domain/identity/persistence/entities/identity-email-token.entity.ts +34 -0
- package/templates/be/auth-lite/src/modules/domain/identity/persistence/entities/identity-refresh-token.entity.ts +34 -0
- package/templates/be/auth-lite/src/modules/domain/identity/persistence/entities/identity-session.entity.ts +30 -0
- package/templates/be/auth-lite/src/modules/domain/identity/persistence/entities/index.ts +10 -0
- package/templates/be/auth-lite/src/modules/domain/identity/rate-counter.service.spec.ts +86 -0
- package/templates/be/auth-lite/src/modules/domain/identity/rate-counter.service.ts +45 -0
- package/templates/be/auth-lite/src/modules/domain/identity/session-store.service.spec.ts +97 -0
- package/templates/be/auth-lite/src/modules/domain/identity/session-store.service.ts +81 -0
- package/templates/be/auth-lite/src/modules/domain/identity/sign-in.service.spec.ts +95 -0
- package/templates/be/auth-lite/src/modules/domain/identity/sign-in.service.ts +93 -0
- package/templates/be/auth-lite/src/tests/fixtures/builders/identity.builder.ts +40 -0
- package/templates/be/inbox/module-definition.ts.tpl +2 -9
- package/templates/be/inbox/platform/event-bus/event-bus.module-definition.ts.tpl +2 -11
- package/templates/be/inbox/platform/inbox/inbox.module-definition.ts.tpl +2 -9
- package/templates/be/patterns/api/feature.module-definition.ts.tpl +3 -9
- package/templates/be/patterns/api/graphql.module-definition.ts.tpl +3 -9
- package/templates/be/patterns/cli/group.module-definition.ts.tpl +2 -9
- package/templates/be/patterns/event-bus/platform/event-bus.module-definition.ts.tpl +3 -10
- package/templates/be/patterns/idempotency/platform/inbox/inbox.module-definition.ts.tpl +2 -9
- package/templates/be/patterns/jobs/job.module-definition.ts.tpl +2 -9
- package/templates/be/patterns/jobs/job.queue.module-definition.ts.tpl +3 -9
- package/templates/be/patterns/jobs/platform/jobs.module-definition.ts.tpl +3 -10
- package/templates/be/patterns/projections/projection.module-definition.ts.tpl +2 -9
- package/templates/be/patterns/queues/platform/queue.module-definition.ts.tpl +3 -10
- package/templates/be/patterns/queues/queue.module-definition.ts.tpl +2 -9
- package/templates/be/patterns/reactors/message.module-definition.ts.tpl +3 -9
- package/templates/be/patterns/reactors/reactor.module-definition.ts.tpl +2 -9
- package/templates/be/patterns/realtime/channel.graphql.module-definition.ts.tpl +3 -9
- package/templates/be/patterns/realtime/channel.sse.module-definition.ts.tpl +3 -9
- package/templates/be/patterns/realtime/channel.websocket.module-definition.ts.tpl +3 -9
- package/templates/be/patterns/saga/saga.message.module-definition.ts.tpl +3 -9
- package/templates/be/patterns/saga/saga.module-definition.ts.tpl +2 -9
- package/templates/be/patterns/webhooks/webhook.http.module-definition.ts.tpl +3 -9
- package/templates/be/platform/graphql/graphql.module-definition.ts +2 -9
- package/templates/be/skeleton/src/features/api/system-health/system-health.module-definition.ts +3 -9
- package/templates/be/skeleton/src/features/api/system-health/transport/http/system-health-http.module-definition.ts +3 -9
- package/templates/be/skeleton/src/features/cli/cli.module-definition.ts +2 -9
- package/templates/be/skeleton/src/features/cli/migrate/migrate.module-definition.ts +3 -9
- package/templates/be/skeleton/src/features/cli/seed/seed.module-definition.ts +2 -9
- package/templates/be/skeleton/src/modules/domain/identity/identity.module-definition.ts +2 -9
- package/templates/be/skeleton/src/modules/domain/liveness/liveness.module-definition.ts +2 -9
- package/templates/be/skeleton/src/modules/domain/note/note.module-definition.ts +2 -9
- package/templates/be/skeleton/src/modules/platform/clock/clock.module-definition.ts +2 -9
- package/templates/be/skeleton/src/modules/platform/cqrs/cqrs.contracts.ts +2 -0
- package/templates/be/skeleton/src/modules/platform/cqrs/cqrs.module-definition.ts +2 -9
- package/templates/be/skeleton/src/modules/platform/database/database.module-definition.ts +3 -13
- package/templates/be/skeleton/src/modules/platform/errors/errors.module-definition.ts +4 -11
- package/templates/be/skeleton/src/modules/platform/http/http.module-definition.ts +4 -9
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.module-definition.ts +5 -11
- package/templates/be/skeleton/src/modules/platform/i18n/i18n.module-definition.ts +4 -11
- package/templates/be/skeleton/src/modules/platform/ids/ids.module-definition.ts +2 -9
- package/templates/be/skeleton/src/modules/platform/logging/logging.module-definition.ts +2 -9
- package/templates/be/skeleton/src/modules/platform/retry/retry.module-definition.ts +2 -9
- package/templates/be/skeleton-lite/apps/api/src/api.env.ts +4 -0
- package/templates/be/skeleton-lite/apps/api/src/api.options.ts +3 -0
- package/templates/be/skeleton-lite/apps/api/src/app.module.ts +6 -5
- package/templates/be/skeleton-lite/apps/api/src/compose/api.ts +3 -2
- package/templates/be/skeleton-lite/apps/api/src/compose/integrations.ts +12 -5
- package/templates/be/skeleton-lite/src/modules/domain/identity/errors/identity.error.ts +40 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.decorators.ts +22 -1
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.module.ts +50 -2
- package/templates/be/skeleton-lite/src/modules/domain/identity/index.ts +3 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/messages/identity-mail.messages.ts +65 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/messages/identity.messages.ts +28 -0
- package/templates/be/skeleton-lite/src/modules/integrations/mail/index.ts +6 -0
- package/templates/be/skeleton-lite/src/modules/integrations/mail/mail.config.ts +33 -0
- package/templates/be/skeleton-lite/src/modules/integrations/mail/mail.decorators.ts +14 -0
- package/templates/be/skeleton-lite/src/modules/integrations/mail/mail.module-definition.ts +12 -0
- package/templates/be/skeleton-lite/src/modules/integrations/mail/mail.module.ts +27 -0
- package/templates/be/skeleton-lite/src/modules/integrations/mail/mail.options.ts +22 -0
- package/templates/be/skeleton-lite/src/modules/integrations/object-storage/index.ts +3 -3
- package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.config.ts +3 -1
- package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.decorators.ts +8 -4
- package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.module-definition.ts +5 -0
- package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.module.ts +29 -0
- package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.options.ts +6 -31
- package/templates/be/skeleton-lite/src/modules/integrations/passport/errors/passport.error.ts +18 -9
- package/templates/be/skeleton-lite/src/modules/integrations/passport/index.ts +35 -11
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.config.spec.ts +44 -8
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.config.ts +40 -18
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.decorators.ts +18 -6
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.kit.spec.ts +73 -0
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.kit.ts +26 -0
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.options.ts +24 -53
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.registration.ts +31 -0
- package/templates/be/skeleton-lite/src/modules/platform/cqrs/index.ts +1 -1
- package/templates/be/skeleton-lite/src/modules/platform/database/entities.ts +2 -1
- package/templates/be/skeleton-lite/src/modules/platform/database/index.ts +1 -0
- package/templates/be/skeleton-lite/src/modules/platform/http-security/index.ts +1 -1
- package/templates/be/skeleton-lite/src/modules/platform/http-security/rate-limit.guard.spec.ts +11 -0
- package/templates/be/skeleton-lite/src/tests/fixtures/builders/api.builder.ts +22 -4
- package/templates/be/table/api/feature.module-definition.ts.tpl +3 -9
- package/templates/be/table/api/graphql.module-definition.ts.tpl +3 -9
- package/templates/be/table/module-definition.ts.tpl +2 -9
- package/templates/be/test-world/e2e/serves-once-migrated.e2e-spec.ts.tpl +9 -9
- package/templates/be/test-world/fixtures/migrations.sql.ts.tpl +4 -4
- package/templates/fe/auth-lite/apps/web/src/modules/auth/client.ts +26 -0
- package/templates/fe/auth-lite/apps/web/src/modules/auth/email-doors.ts +16 -0
- package/templates/fe/auth-lite/apps/web/src/modules/auth/index.ts +5 -0
- package/templates/fe/common/site-shell.tsx.partial +3 -3
- package/templates/fe/skeleton/apps/__app__/src/components/providers/AppProviders/index.tsx +33 -10
- package/templates/fe/skeleton/apps/__app__/src/hooks/auth/index.ts +1 -0
- package/templates/fe/skeleton/apps/__app__/src/hooks/auth/useViewerId.ts +11 -0
- package/templates/fe/skeleton/apps/__app__/src/modules/toast/toast.tsx +15 -12
- package/templates/fe/skeleton/apps/__app__/src/modules/viewer-cache/index.ts +5 -0
- package/templates/fe/skeleton/apps/__app__/src/redux/slices/session.ts +1 -1
- package/templates/fe/skeleton/packages/__project__-i18n/src/index.ts +30 -67
- package/templates/fe/skeleton/packages/__project__-i18n/src/proxy.ts +6 -21
- package/templates/fe/skeleton/packages/__project__-i18n/src/routing.ts +10 -16
- package/templates/fe/skeleton/packages/__project__-ui/src/composites/NotFoundNotice/index.tsx +4 -7
- package/templates/fe/skeleton/packages/__project__-ui/src/frames/SiteShell/index.tsx +2 -3
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/auth/confirm/page.tsx +9 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/auth/forgot/page.tsx +9 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/auth/resend/page.tsx +9 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/auth/reset/page.tsx +9 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/auth/signup/page.tsx +9 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/layout.tsx +5 -9
- package/templates/fe/skeleton-lite/apps/web/src/components/blocks/EmailDoorForm/component.tsx +51 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/blocks/EmailDoorForm/index.tsx +84 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/composites/NotFoundNotice/index.tsx +4 -7
- package/templates/fe/skeleton-lite/apps/web/src/components/frames/SiteShell/index.tsx +1 -2
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppConfirmPage/index.tsx +25 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppForgotPage/index.tsx +21 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppHomePage/component.tsx +11 -3
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppHomePage/index.tsx +4 -1
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppResendPage/index.tsx +21 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppResetPage/index.tsx +25 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppSignupPage/index.tsx +21 -0
- package/templates/fe/skeleton-lite/apps/web/src/hooks/emailAuth/emailAuth.shared.ts +12 -0
- package/templates/fe/skeleton-lite/apps/web/src/hooks/emailAuth/index.ts +2 -0
- package/templates/fe/skeleton-lite/apps/web/src/hooks/emailAuth/useEmailDoor.ts +32 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/index.ts +2 -2
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/messages/en.json +50 -1
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/messages/vi.json +50 -1
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/request.ts +7 -30
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/routing.ts +13 -14
- package/templates/fe/skeleton-lite/apps/web/src/modules/routes/index.ts +5 -0
- package/templates/fe/skeleton-lite/apps/web/src/proxy.ts +4 -17
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/Caddyfile +0 -16
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/owner-actions.md +0 -11
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/recovery/restore-proof.md +0 -7
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/releases/current.yaml +0 -6
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/releases/images.lock.yaml +0 -8
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/releases/previous.yaml +0 -6
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/stack.yaml +0 -186
- package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.client.spec.ts +0 -22
- package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.client.ts +0 -25
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.cookie.spec.ts +0 -104
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.cookie.ts +0 -34
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.password.spec.ts +0 -28
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.password.ts +0 -11
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.tokens.spec.ts +0 -88
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.tokens.ts +0 -69
- package/templates/be/skeleton-lite/src/modules/integrations/passport/strategies/google.strategy.ts +0 -41
- package/templates/be/skeleton-lite/src/modules/integrations/passport/strategies/local.strategy.ts +0 -40
- package/templates/be/skeleton-lite/src/modules/integrations/passport/strategies/oauth-state.store.ts +0 -63
- package/templates/fe/skeleton/packages/__project__-ui/src/frames/SiteShell/classNames.ts +0 -4
- package/templates/fe/skeleton-lite/apps/__app__/src/modules/auth/auth-door.ts +0 -30
- package/templates/fe/skeleton-lite/apps/__app__/src/modules/auth/client.ts +0 -50
- package/templates/fe/skeleton-lite/apps/__app__/src/modules/auth/token-store.ts +0 -31
- package/templates/fe/skeleton-lite/apps/web/src/components/frames/SiteShell/classNames.ts +0 -4
|
@@ -79,13 +79,11 @@ const template = (relative) => read(path.join(TEMPLATES_DIR, ...relative.split('
|
|
|
79
79
|
const DATABASE_ERROR = 'be/src/modules/platform/database/errors/database.error.ts';
|
|
80
80
|
/** Skeleton files the lite starter leaves out and the queue capabilities use, each from its skeleton template. */
|
|
81
81
|
const SKELETON_FILES = Object.freeze([
|
|
82
|
-
['be/src/modules/platform/database/database.client.ts', 'skeleton-lite/src/modules/platform/database/database.client.ts'],
|
|
83
|
-
['be/src/modules/platform/database/database.client.spec.ts', 'skeleton-lite/src/modules/platform/database/database.client.spec.ts'],
|
|
84
82
|
['be/src/modules/platform/database/database.sql.ts', 'skeleton-lite/src/modules/platform/database/database.sql.ts'],
|
|
85
83
|
['be/src/modules/platform/primitives/sequence.policy.ts', 'skeleton/src/modules/platform/primitives/sequence.policy.ts'],
|
|
86
84
|
['be/src/modules/platform/primitives/sequence.policy.spec.ts', 'skeleton/src/modules/platform/primitives/sequence.policy.spec.ts'],
|
|
87
85
|
]);
|
|
88
|
-
/** The
|
|
86
|
+
/** The sql tag of the outbox and claim statements (with its identifier error) and the in-order relay the queue adapters walk their rows with. */
|
|
89
87
|
function skeletonFiles(repoRoot) {
|
|
90
88
|
const created = SKELETON_FILES.filter(([relative]) => !fs.existsSync(path.join(repoRoot, ...relative.split('/'))))
|
|
91
89
|
.map(([relative, source]) => ({ relative, text: render(template(`be/${source}`), {}) }));
|
|
@@ -300,6 +298,54 @@ function keysPatch(file) {
|
|
|
300
298
|
lines.splice(last + 1, 0, ...KEY_ROWS);
|
|
301
299
|
return { file, text: lines.join('\n') };
|
|
302
300
|
}
|
|
301
|
+
const DEV_REDIS_PORT = 56379;
|
|
302
|
+
/** The redis service a dev compose file gains: loopback only, append-only, on the host port the environment declares. */
|
|
303
|
+
const devRedisService = ` redis:
|
|
304
|
+
image: ${REDIS_IMAGE}
|
|
305
|
+
command: ["redis-server", "--appendonly", "yes"]
|
|
306
|
+
ports:
|
|
307
|
+
- "127.0.0.1:\${STACK_PORT_REDIS}:6379"
|
|
308
|
+
volumes:
|
|
309
|
+
- redis-data:/data
|
|
310
|
+
healthcheck:
|
|
311
|
+
test: ["CMD", "redis-cli", "ping"]
|
|
312
|
+
interval: 5s
|
|
313
|
+
timeout: 3s
|
|
314
|
+
retries: 20
|
|
315
|
+
restart: unless-stopped
|
|
316
|
+
`;
|
|
317
|
+
/**
|
|
318
|
+
* A docker-compose dev environment (runtime docker-compose, a `local` block) runs the declared Redis from its compose file, on its own loopback
|
|
319
|
+
* port, and the queue processes reach it there: the compose service, the port, the service entry and the api and worker processes' queue settings.
|
|
320
|
+
*/
|
|
321
|
+
function devQueuePatches(repoRoot, manifest, edit, consumers) {
|
|
322
|
+
const patches = [];
|
|
323
|
+
const dev = Object.entries(manifest?.environments ?? {}).filter(([, environment]) => environment?.runtime === 'docker-compose' && environment.local);
|
|
324
|
+
for (const [name, environment] of dev) {
|
|
325
|
+
const composeRelative = `.starcistacks/${name}/${environment.composeFiles?.[0] ?? 'infra/compose/compose.yaml'}`;
|
|
326
|
+
const composeFile = path.join(repoRoot, ...composeRelative.split('/'));
|
|
327
|
+
const compose = read(composeFile).replace(/\r\n/g, '\n');
|
|
328
|
+
if (/^ {2}redis:/m.test(compose))
|
|
329
|
+
continue;
|
|
330
|
+
const base = ['environments', name];
|
|
331
|
+
const value = edit.value.environments[name];
|
|
332
|
+
edit.append([...base, 'components'], `redis: {port: ${DEV_REDIS_PORT}}\n`);
|
|
333
|
+
value.components.redis = { port: DEV_REDIS_PORT };
|
|
334
|
+
edit.append([...base, 'local', 'ports'], `redis: ${DEV_REDIS_PORT}\n`);
|
|
335
|
+
value.local.ports.redis = DEV_REDIS_PORT;
|
|
336
|
+
edit.append([...base, 'local', 'services'], '- {name: redis, wait: healthy}\n');
|
|
337
|
+
value.local.services.push({ name: 'redis', wait: 'healthy' });
|
|
338
|
+
for (const run of value.local.processes ?? []) {
|
|
339
|
+
if (!consumers.includes(run.name))
|
|
340
|
+
continue;
|
|
341
|
+
edit.append([...base, 'local', 'processes', { item: 'name', is: run.name }, 'env'], 'QUEUE_ENABLED: "true"\nQUEUE_REDIS_HOST: 127.0.0.1\nQUEUE_REDIS_PORT: "${port.redis}"\n');
|
|
342
|
+
Object.assign(run.env, { QUEUE_ENABLED: 'true', QUEUE_REDIS_HOST: '127.0.0.1', QUEUE_REDIS_PORT: '${port.redis}' });
|
|
343
|
+
}
|
|
344
|
+
const head = compose.includes('\nvolumes:\n') ? compose.replace('\nvolumes:\n', `\n${devRedisService}volumes:\n`) : `${compose.replace(/\n*$/, '\n')}${devRedisService}volumes:\n`;
|
|
345
|
+
patches.push({ file: composeFile, text: `${head.replace(/\n*$/, '\n')} redis-data: {}\n` });
|
|
346
|
+
}
|
|
347
|
+
return patches;
|
|
348
|
+
}
|
|
303
349
|
/** Every stack file the declared Redis adds to or changes, planned before anything is written. */
|
|
304
350
|
function stackFiles(repoRoot, { project, consumers }) {
|
|
305
351
|
const manifestRelative = '.starcistacks/application-stacks.yaml';
|
|
@@ -330,6 +376,7 @@ function stackFiles(repoRoot, { project, consumers }) {
|
|
|
330
376
|
if (!fs.existsSync(path.join(repoRoot, ...recovery.split('/'))))
|
|
331
377
|
created.push({ relative: recovery, text: recoveryRecord });
|
|
332
378
|
}
|
|
379
|
+
patches.push(...devQueuePatches(repoRoot, manifest, edit, consumers));
|
|
333
380
|
patches.unshift({ file: manifestFile, text: edit.text() });
|
|
334
381
|
return { patches: patches.filter((patch) => patch !== null), created, environments: swarm.map(([name]) => name) };
|
|
335
382
|
}
|
package/scaffold/add.mjs
CHANGED
|
@@ -23,6 +23,7 @@ import { ScaffoldError, pascalOf } from './service.mjs';
|
|
|
23
23
|
import { addTable, graphqlPlatformFiles } from './add-table.mjs';
|
|
24
24
|
import { addModuleImport, compositionCode, readComposeGroup, writeComposeGroup } from './add-compose.mjs';
|
|
25
25
|
import { addApp } from './add-app.mjs';
|
|
26
|
+
import { addModule } from './add-module.mjs';
|
|
26
27
|
import { FE_UNIT_LAYERS, addFeUnit } from './add-fe-unit.mjs';
|
|
27
28
|
import { addFePage } from './add-fe-page.mjs';
|
|
28
29
|
import { addFeOverlay } from './add-fe-overlay.mjs';
|
|
@@ -57,6 +58,10 @@ export function addKind({ repoRoot, noun, name, options = {}, now = Date.now })
|
|
|
57
58
|
const result = addApp({ root: repoRoot, name });
|
|
58
59
|
return { ...result, registered: { patterns: [], kinds: [] } };
|
|
59
60
|
}
|
|
61
|
+
if (noun === 'module') {
|
|
62
|
+
const result = addModule({ root: repoRoot, name });
|
|
63
|
+
return { created: [...result.created, ...result.updated], registered: { patterns: [], kinds: [] } };
|
|
64
|
+
}
|
|
60
65
|
const feOwner = FE_OWNERS[noun];
|
|
61
66
|
if (feOwner !== undefined || Object.hasOwn(FE_UNIT_LAYERS, noun)) {
|
|
62
67
|
const result = feOwner === undefined ? addFeUnit({ repoRoot, noun, name, options }) : feOwner({ repoRoot, name, options });
|
package/scaffold/app.d.mts
CHANGED
|
@@ -160,8 +160,8 @@ export declare const PARTIAL_VARIABLES: any;
|
|
|
160
160
|
* The be files a generator writes when it first needs them, never the starter, so a fresh app carries no file and no export that
|
|
161
161
|
* nothing reads (R25). In both editions: the entity bases of platform/database (abstract.entity.ts, BE-PERSISTENCE-3), written by
|
|
162
162
|
* the first table whose entity extends them, since no starter table has both timestamps. In lite also: the cli's runners, the in-order
|
|
163
|
-
* relay, and the
|
|
164
|
-
*
|
|
163
|
+
* relay, and the sql tag of platform/database, which only the table, queue, jobs and webhook capabilities call; a fresh app reads and
|
|
164
|
+
* writes through entities and, for the account doors of its sign-in, the database-function call of database.client.ts (R334).
|
|
165
165
|
*/
|
|
166
166
|
export declare const ENTITY_BASE_FILE = "src/modules/platform/database/abstract.entity.ts";
|
|
167
167
|
/**
|
package/scaffold/app.mjs
CHANGED
|
@@ -18,7 +18,7 @@ import { spawnSync } from 'node:child_process';
|
|
|
18
18
|
import { loadSlotManifest, resolveAppDeclaration } from '../runtime/scripts/hfs/slots.mjs';
|
|
19
19
|
import { parseYaml } from '../runtime/engine/yaml.mjs';
|
|
20
20
|
import { HARNESS_PACKAGE, canonPins } from '../runtime/scripts/hfs/canon-pins.mjs';
|
|
21
|
-
import { TEMPLATES_DIR, appSource, imageFiles, render, renderTargets, writeTargets } from '../sync/index.mjs';
|
|
21
|
+
import { TEMPLATES_DIR, appSource, imageFiles, render, renderTargets, settleSource, writeTargets } from '../sync/index.mjs';
|
|
22
22
|
import { ScaffoldError } from './service.mjs';
|
|
23
23
|
import { testWorldFiles } from './test-world.mjs';
|
|
24
24
|
import { FE_APP_SCRIPTS, PACKAGE_MANAGER, WORKSPACES, feAppPackageName } from '../runtime/scripts/hfs/rules/monorepo.mjs';
|
|
@@ -139,7 +139,7 @@ const LITE_STARTER_SIDES = Object.freeze({
|
|
|
139
139
|
const STARTER_DEPENDENCIES = Object.freeze({
|
|
140
140
|
dependencies: {
|
|
141
141
|
'@apollo/server': null, '@nestjs/apollo': null, '@nestjs/common': null, '@nestjs/core': null, '@nestjs/cqrs': null, '@nestjs/graphql': null,
|
|
142
|
-
'@nestjs/platform-express': null, '@nestjs/typeorm': null, 'nest-commander': null, pg: null, graphql: null,
|
|
142
|
+
'@nestjs/platform-express': null, '@nestjs/typeorm': null, '@starci/nest-kit': null, 'nest-commander': null, pg: null, graphql: null,
|
|
143
143
|
'class-transformer': null, 'class-validator': null, 'reflect-metadata': null, rxjs: null, tslib: null, typeorm: null, undici: null, zod: null,
|
|
144
144
|
},
|
|
145
145
|
devDependencies: {
|
|
@@ -158,15 +158,14 @@ const STARTER_DEPENDENCIES = Object.freeze({
|
|
|
158
158
|
const LITE_STARTER_DEPENDENCIES = Object.freeze({
|
|
159
159
|
dependencies: {
|
|
160
160
|
'@apollo/server': null, '@nestjs/apollo': null, '@nestjs/common': null, '@nestjs/core': null, '@nestjs/cqrs': null, '@nestjs/graphql': null,
|
|
161
|
-
'@nestjs/platform-express': null, '@nestjs/typeorm': null, '@
|
|
162
|
-
'
|
|
163
|
-
'class-transformer': null, 'class-validator': null, graphql: null, jose: null, pg: null,
|
|
161
|
+
'@nestjs/platform-express': null, '@nestjs/typeorm': null, '@starci/auth': null, '@starci/mail': null, '@starci/nest-kit': null, '@starci/object-storage': null,
|
|
162
|
+
'class-transformer': null, 'class-validator': null, graphql: null, pg: null,
|
|
164
163
|
'reflect-metadata': null, rxjs: null, tslib: null, typeorm: null, undici: null, zod: null,
|
|
165
164
|
},
|
|
166
165
|
devDependencies: {
|
|
167
166
|
turbo: null, '@nestjs/testing': null, '@starci/eslint-canon-be': null, '@starci/eslint-canon-fe': null,
|
|
168
167
|
'@starci/jest-preset': null, '@starci/prettier-config': null, '@starci/stylelint-canon': null, '@starci/tsconfig': null, 'starci-harness': null, '@tailwindcss/postcss': null,
|
|
169
|
-
'@types/express': null, '@types/jest': null, '@types/node': null, '@types/
|
|
168
|
+
'@types/express': null, '@types/jest': null, '@types/node': null, '@types/react': null, '@types/react-dom': null,
|
|
170
169
|
eslint: null, 'eslint-plugin-react-hooks': null, husky: null, jest: null, 'postcss-value-parser': null, prettier: null,
|
|
171
170
|
stylelint: null, tailwindcss: null, 'ts-jest': null, 'ts-node-dev': null, 'tsc-alias': null,
|
|
172
171
|
'tsconfig-paths': null, typescript: null,
|
|
@@ -183,22 +182,22 @@ const FE_APP_DEPENDENCIES = Object.freeze({
|
|
|
183
182
|
'@heroui/react': null, '@heroui/styles': null, '@starci/grammar': null, '@xyflow/react': null, 'react-aria-components': null, next: null, 'next-intl': null, react: null, 'react-dom': null,
|
|
184
183
|
});
|
|
185
184
|
/**
|
|
186
|
-
* The lite fe workspace: the same base without the shared packages
|
|
187
|
-
* holds no identity SDK: modules/auth talks only to the app's own back-end auth doors.
|
|
185
|
+
* The lite fe workspace: the same base without the shared packages (its next-intl stack is @starci/next-i18n's, a core package of the
|
|
186
|
+
* fe stack). It holds no identity SDK: modules/auth talks only to the app's own back-end auth doors.
|
|
188
187
|
*/
|
|
189
188
|
const LITE_FE_APP_DEPENDENCIES = Object.freeze({
|
|
190
|
-
'@heroicons/react': null, '@heroui/react': null, '@heroui/styles': null, '@starci/grammar': null, '@starci/heroicons': null, '@xyflow/react': null, 'react-aria-components': null,
|
|
191
|
-
next: null, 'next-intl': null, react: null, 'react-dom': null,
|
|
189
|
+
'@heroicons/react': null, '@heroui/react': null, '@heroui/styles': null, '@starci/auth': null, '@starci/grammar': null, '@starci/heroicons': null, '@xyflow/react': null, 'react-aria-components': null,
|
|
190
|
+
next: null, 'next-intl': null, react: null, 'react-dom': null,
|
|
192
191
|
});
|
|
193
192
|
/**
|
|
194
193
|
* The shared fe workspace packages of the starter, fe/packages/<project>-<name> named @<project>/<name>: each one's exported
|
|
195
194
|
* subpaths (built to dist) and the runtime dependencies its own source imports. ui holds the drawings both apps mount; i18n the
|
|
196
|
-
* next-
|
|
197
|
-
* ./routing, client-safe: the default locale a client boundary reads).
|
|
195
|
+
* product's description of its languages over @starci/next-i18n (the entry, a server module: createAppI18n; ./proxy, the locale
|
|
196
|
+
* negotiation the apps' proxy.ts re-exports; ./routing, client-safe: the description and the default locale a client boundary reads).
|
|
198
197
|
*/
|
|
199
198
|
const FE_PACKAGES = Object.freeze({
|
|
200
199
|
ui: { exports: ['.'], dependencies: { '@heroui/react': null, '@starci/grammar': null, '@starci/heroicons': null, '@xyflow/react': null, 'react-aria-components': null, 'next-intl': null, react: null, 'react-dom': null } },
|
|
201
|
-
i18n: { exports: ['.', './proxy', './routing'], dependencies: { next: null,
|
|
200
|
+
i18n: { exports: ['.', './proxy', './routing'], dependencies: { '@starci/next-i18n': null, next: null, 'server-only': null } },
|
|
202
201
|
});
|
|
203
202
|
/** The scripts of every fe package workspace: tsc builds it to dist and type-checks it. */
|
|
204
203
|
const PACKAGE_WORKSPACE_SCRIPTS = Object.freeze({ build: 'tsc -p tsconfig.build.json', typecheck: 'tsc --noEmit -p tsconfig.json' });
|
|
@@ -334,15 +333,13 @@ const LITE_APP_REUSED = new Set(['.editorconfig', '.gitattributes', '.nvmrc']);
|
|
|
334
333
|
* The be files a generator writes when it first needs them, never the starter, so a fresh app carries no file and no export that
|
|
335
334
|
* nothing reads (R25). In both editions: the entity bases of platform/database (abstract.entity.ts, BE-PERSISTENCE-3), written by
|
|
336
335
|
* the first table whose entity extends them, since no starter table has both timestamps. In lite also: the cli's runners, the in-order
|
|
337
|
-
* relay, and the
|
|
338
|
-
*
|
|
336
|
+
* relay, and the sql tag of platform/database, which only the table, queue, jobs and webhook capabilities call; a fresh app reads and
|
|
337
|
+
* writes through entities and, for the account doors of its sign-in, the database-function call of database.client.ts (R334).
|
|
339
338
|
*/
|
|
340
339
|
export const ENTITY_BASE_FILE = 'src/modules/platform/database/abstract.entity.ts';
|
|
341
340
|
const DEFERRED_GENERATOR_FILES = new Set([ENTITY_BASE_FILE]);
|
|
342
341
|
const LITE_DEFERRED_GENERATOR_FILES = new Set([
|
|
343
342
|
...DEFERRED_GENERATOR_FILES,
|
|
344
|
-
'src/modules/platform/database/database.client.ts',
|
|
345
|
-
'src/modules/platform/database/database.client.spec.ts',
|
|
346
343
|
'src/modules/platform/database/database.sql.ts',
|
|
347
344
|
'src/modules/platform/database/migration-runner.service.ts',
|
|
348
345
|
'src/modules/platform/database/seed-runner.service.ts',
|
|
@@ -656,7 +653,8 @@ export function scaffoldApp({ name, into, presets, harness, edition = 'full', re
|
|
|
656
653
|
try {
|
|
657
654
|
for (const file of files)
|
|
658
655
|
writes.write(file.path, file.content);
|
|
659
|
-
const
|
|
656
|
+
const source = appSource(root);
|
|
657
|
+
const targets = renderTargets(declaration, presets, { manifest, source: settleSource(source, renderTargets(declaration, presets, { manifest, source })) });
|
|
660
658
|
for (const target of targets)
|
|
661
659
|
writes.claim(target.path);
|
|
662
660
|
writeTargets(root, targets);
|
|
@@ -59,20 +59,13 @@ const REGISTRATIONS = Object.freeze({
|
|
|
59
59
|
names: ["sql"],
|
|
60
60
|
beforeSource: "./errors/database.error",
|
|
61
61
|
},
|
|
62
|
-
{
|
|
63
|
-
// The outbox write and the relay call the queue functions of their migration (R334).
|
|
64
|
-
file: "be/src/modules/platform/database/index.ts",
|
|
65
|
-
source: "./database.client",
|
|
66
|
-
names: ["callDbFunction"],
|
|
67
|
-
beforeSource: "./primary.decorators",
|
|
68
|
-
},
|
|
69
62
|
],
|
|
70
63
|
// The jobs capability (add job, the fenced-job pattern): its tick takes a transaction-scoped lock before it claims the minute (R335).
|
|
71
64
|
jobs: [
|
|
72
65
|
{
|
|
73
66
|
file: "be/src/modules/platform/database/index.ts",
|
|
74
67
|
source: "./database.client",
|
|
75
|
-
names: ["advisoryXactLock"
|
|
68
|
+
names: ["advisoryXactLock"],
|
|
76
69
|
beforeSource: "./primary.decorators",
|
|
77
70
|
},
|
|
78
71
|
],
|
|
@@ -83,13 +76,6 @@ const REGISTRATIONS = Object.freeze({
|
|
|
83
76
|
names: ["BATCH_ROWS", "sql"],
|
|
84
77
|
beforeSource: "./errors/database.error",
|
|
85
78
|
},
|
|
86
|
-
{
|
|
87
|
-
// The inbox claim and the lite event bus call the functions of their migration (R334).
|
|
88
|
-
file: "be/src/modules/platform/database/index.ts",
|
|
89
|
-
source: "./database.client",
|
|
90
|
-
names: ["callDbFunction"],
|
|
91
|
-
beforeSource: "./primary.decorators",
|
|
92
|
-
},
|
|
93
79
|
{
|
|
94
80
|
// PRIMARY_ENTITY_MANAGER: the token the inbox service's unit spec provides its EntityManager double under.
|
|
95
81
|
file: "be/src/modules/platform/database/index.ts",
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** The folder name prefix of a module package. */
|
|
2
|
+
export declare const MODULE_PREFIX = "module-";
|
|
3
|
+
/** The package that lists every module package. */
|
|
4
|
+
export declare const REGISTRY_DIR = "packages/modules";
|
|
5
|
+
/** The product's agent SDK package folder every module builds on. */
|
|
6
|
+
export declare const SDK_DIR = "packages/agent-sdk";
|
|
7
|
+
/** A file of the registry package: its app-relative path and its content. */
|
|
8
|
+
export interface RegistryFile {
|
|
9
|
+
readonly path: string;
|
|
10
|
+
readonly content: string;
|
|
11
|
+
}
|
|
12
|
+
/** `loyalty-points` as the UPPER_SNAKE prefix of the constants a module exports. */
|
|
13
|
+
export declare const upperOf: (name: string) => string;
|
|
14
|
+
/** The module names under `packages/` of the app at `root` (a folder module-<name> holding a package.json), sorted. */
|
|
15
|
+
export declare function moduleNames(root: string): string[];
|
|
16
|
+
/** The files of the registry package for `names` (empty when no module exists: the package is then absent). */
|
|
17
|
+
export declare function registryFiles(project: string, names: readonly string[]): RegistryFile[];
|
|
18
|
+
/** The PascalCase label of a module, for the messages that name it. */
|
|
19
|
+
export declare const labelOf: (name: string) => string;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// module-registry.mts - the module packages of an app (packages/module-<name>) and the registry package (packages/modules) that lists them.
|
|
2
|
+
//
|
|
3
|
+
// The registry is managed: its manifests and its three source files are rendered from the module list, by `starci app add|remove module` and by
|
|
4
|
+
// `starci app sync` (the same function), so a hand edit is drift. A module package is written once by `add module` and is the product's from then on.
|
|
5
|
+
import fs from 'node:fs';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { byCodeUnit } from '../report/order.mjs';
|
|
8
|
+
import { pascalOf } from './service.mjs';
|
|
9
|
+
/** The folder name prefix of a module package. */
|
|
10
|
+
export const MODULE_PREFIX = 'module-';
|
|
11
|
+
/** The package that lists every module package. */
|
|
12
|
+
export const REGISTRY_DIR = 'packages/modules';
|
|
13
|
+
/** The product's agent SDK package folder every module builds on. */
|
|
14
|
+
export const SDK_DIR = 'packages/agent-sdk';
|
|
15
|
+
const HEADER = '// Generated by starci app sync (module registry). Do not edit: run "npx starci app sync --execute".';
|
|
16
|
+
const json = (value) => `${JSON.stringify(value, null, 2)}\n`;
|
|
17
|
+
/** `loyalty-points` as the UPPER_SNAKE prefix of the constants a module exports. */
|
|
18
|
+
export const upperOf = (name) => name.replace(/-/g, '_').toUpperCase();
|
|
19
|
+
/** The module names under `packages/` of the app at `root` (a folder module-<name> holding a package.json), sorted. */
|
|
20
|
+
export function moduleNames(root) {
|
|
21
|
+
const folder = path.join(root, 'packages');
|
|
22
|
+
if (!fs.existsSync(folder))
|
|
23
|
+
return [];
|
|
24
|
+
return fs.readdirSync(folder, { withFileTypes: true })
|
|
25
|
+
.filter((entry) => entry.isDirectory() && entry.name.startsWith(MODULE_PREFIX) && fs.existsSync(path.join(folder, entry.name, 'package.json')))
|
|
26
|
+
.map((entry) => entry.name.slice(MODULE_PREFIX.length))
|
|
27
|
+
.sort(byCodeUnit);
|
|
28
|
+
}
|
|
29
|
+
/** The exports map of the registry and of a module: the React-free halves and, for a module, its Grammar workbench. */
|
|
30
|
+
const registryExports = () => ({
|
|
31
|
+
'.': { types: './dist/index.d.ts', default: './dist/index.js' },
|
|
32
|
+
'./template': { types: './dist/template/index.d.ts', default: './dist/template/index.js' },
|
|
33
|
+
'./manifest': { types: './dist/manifest/index.d.ts', default: './dist/manifest/index.js' },
|
|
34
|
+
});
|
|
35
|
+
/** The files of the registry package for `names` (empty when no module exists: the package is then absent). */
|
|
36
|
+
export function registryFiles(project, names) {
|
|
37
|
+
if (names.length === 0)
|
|
38
|
+
return [];
|
|
39
|
+
const sorted = [...names].sort(byCodeUnit);
|
|
40
|
+
const sdk = `@${project}/agent-sdk`;
|
|
41
|
+
const module = (name) => `@${project}/${MODULE_PREFIX}${name}`;
|
|
42
|
+
const serverImports = sorted.map((name) => `import { ${upperOf(name)}_SERVER } from "${module(name)}/template"`).join('\n');
|
|
43
|
+
const workbenchImports = sorted.map((name) => `import { ${upperOf(name)}_WORKBENCH } from "${module(name)}/manifest"`).join('\n');
|
|
44
|
+
const dependencies = Object.fromEntries([[sdk, '*'], ...sorted.map((name) => [module(name), '*'])].sort(([a], [b]) => byCodeUnit(a, b)));
|
|
45
|
+
return [
|
|
46
|
+
{
|
|
47
|
+
path: `${REGISTRY_DIR}/package.json`,
|
|
48
|
+
content: json({
|
|
49
|
+
name: `@${project}/modules`,
|
|
50
|
+
version: '0.0.0',
|
|
51
|
+
private: true,
|
|
52
|
+
description: 'The one list of the product modules built on the agent SDK, all React-free: ./template (every module\'s server half, for the back end) and ./manifest (every workbench declaration and the spec registry they assemble).',
|
|
53
|
+
exports: registryExports(),
|
|
54
|
+
scripts: { build: 'tsc -p tsconfig.build.json', typecheck: 'tsc --noEmit --pretty false' },
|
|
55
|
+
dependencies,
|
|
56
|
+
}),
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
path: `${REGISTRY_DIR}/tsconfig.json`,
|
|
60
|
+
content: json({ extends: '@starci/tsconfig/next.json', compilerOptions: { incremental: false, types: [] }, include: ['src/**/*.ts'], exclude: ['node_modules', 'dist'] }),
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
path: `${REGISTRY_DIR}/tsconfig.build.json`,
|
|
64
|
+
content: json({
|
|
65
|
+
extends: './tsconfig.json',
|
|
66
|
+
compilerOptions: { rootDir: 'src', outDir: 'dist', noEmit: false, declaration: true, module: 'node16', moduleResolution: 'node16' },
|
|
67
|
+
}),
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
path: `${REGISTRY_DIR}/src/index.ts`,
|
|
71
|
+
content: `${HEADER}\nimport { MODULE_SERVERS } from "./template"\n\n/** The keys of every module this registry lists, for a host that checks its own wiring against it. */\nexport const REGISTERED_MODULE_KEYS: ReadonlyArray<string> = MODULE_SERVERS.map((module) => module.key)\n`,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
path: `${REGISTRY_DIR}/src/template/index.ts`,
|
|
75
|
+
content: `${HEADER}\nimport type { ModuleServer } from "${sdk}/module"\n${serverImports}\n\n/** The server half of every module built on the SDK; the back end reads nothing else of a module. */\nexport const MODULE_SERVERS: ReadonlyArray<ModuleServer> = [${sorted.map((name) => `${upperOf(name)}_SERVER`).join(', ')}]\n`,
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
path: `${REGISTRY_DIR}/src/manifest/index.ts`,
|
|
79
|
+
content: `${HEADER}\nimport { specRegistryFrom } from "${sdk}/workbench/contract"\nimport type { WorkbenchDeclaration, WorkbenchSpecRegistry } from "${sdk}/workbench/contract"\n${workbenchImports}\n\n/** The workbench declaration of every module built on the SDK; the front end pairs each with how it draws. */\nexport const MODULE_WORKBENCHES: ReadonlyArray<WorkbenchDeclaration> = [${sorted.map((name) => `${upperOf(name)}_WORKBENCH`).join(', ')}]\n\n/** Everything a stored spec may name: the SDK's blocks and each module's exposed blocks, reads and actions. */\nexport const SPEC_REGISTRY: WorkbenchSpecRegistry = specRegistryFrom(MODULE_WORKBENCHES)\n`,
|
|
80
|
+
},
|
|
81
|
+
];
|
|
82
|
+
}
|
|
83
|
+
/** The PascalCase label of a module, for the messages that name it. */
|
|
84
|
+
export const labelOf = (name) => pascalOf(name);
|
|
@@ -29,3 +29,11 @@ export declare function syncBundles(options: {
|
|
|
29
29
|
readonly check: boolean;
|
|
30
30
|
readonly packages?: readonly string[];
|
|
31
31
|
}): SyncResult;
|
|
32
|
+
/** The harness runtime layers whose recipe hfs ships for `starci app stack up` (stack/runtime-layers.mts): the folder name under images/ of the harness repository. */
|
|
33
|
+
export declare const layerRecipes: readonly ["openclaw-runtime"];
|
|
34
|
+
/** Syncs (or judges) the shipped copy stack/images/<layer>/ of each recipe against the owner, `images/<layer>/` of the harness repository. */
|
|
35
|
+
export declare function syncLayerRecipes(options: {
|
|
36
|
+
readonly imagesDir: string;
|
|
37
|
+
readonly shippedDir: string;
|
|
38
|
+
readonly check: boolean;
|
|
39
|
+
}): SyncResult;
|
package/src/main.mjs
CHANGED
|
@@ -101,6 +101,7 @@ import { emitContractsAsync } from '../emit/contracts.mjs';
|
|
|
101
101
|
import { ScaffoldError, newService, newSpec } from '../scaffold/service.mjs';
|
|
102
102
|
import { EditionRefusal } from '../scaffold/edition-gate.mjs';
|
|
103
103
|
import { addKind } from '../scaffold/add.mjs';
|
|
104
|
+
import { removeModule } from '../scaffold/add-module.mjs';
|
|
104
105
|
import { scaffoldApp } from '../scaffold/app.mjs';
|
|
105
106
|
import { newImages } from '../scaffold/image.mjs';
|
|
106
107
|
import { cachedLinters, eslintRepository, runLinter as nativeLinter } from '../lint/run.mjs';
|
|
@@ -140,7 +141,7 @@ const listFlag = (opts, flag) => { const given = opts[flag]; return Array.isArra
|
|
|
140
141
|
/** An argument the parser refuses (an unknown flag, a missing value, a form a verb does not take): the one error `main` follows with the usage. */
|
|
141
142
|
export class UsageError extends Error {
|
|
142
143
|
}
|
|
143
|
-
export const VERBS = Object.freeze(['scaffold', 'add', 'sync', 'check', 'upgrade', 'stack', 'world', 'release', 'slot', 'emit', 'secret', 'ci', 'db', 'coverage', 'codemod']);
|
|
144
|
+
export const VERBS = Object.freeze(['scaffold', 'add', 'remove', 'sync', 'check', 'upgrade', 'stack', 'world', 'release', 'slot', 'emit', 'secret', 'ci', 'db', 'coverage', 'codemod']);
|
|
144
145
|
const DATABASE_VERBS = new Map([['baseline', dbBaselineMain], ['restamp', dbRestampMain], ['migrate', dbMigrateMain], ['post-cutover', dbPostCutoverMain], ['dump', dbDumpMain], ['new', dbNewMain], ['drift', dbDriftMain]]);
|
|
145
146
|
/**
|
|
146
147
|
* The verbs parsed here, each with the only flags it takes ({ boolean, value, list }); a flag of another verb is refused, never ignored.
|
|
@@ -158,12 +159,13 @@ const PARSED = {
|
|
|
158
159
|
add: {
|
|
159
160
|
run: addMain, ...ADD_FLAGS, list: ['--inject'],
|
|
160
161
|
forms: [
|
|
161
|
-
{ operands: '<api|webhook|realtime|saga|job|reactor|queue|projection|cli|table|app|block|frame|composite|branch|leaf|page|overlay|layout> <name>', only: ADD_FLAGS },
|
|
162
|
+
{ operands: '<api|webhook|realtime|saga|job|reactor|queue|projection|cli|table|app|module|block|frame|composite|branch|leaf|page|overlay|layout> <name>', only: ADD_FLAGS },
|
|
162
163
|
{ operands: 'service <dir> <name>', only: { list: ['--inject'] } },
|
|
163
164
|
{ operands: 'spec <file>.service.ts', flags: [] },
|
|
164
165
|
{ operands: 'image', flags: [] },
|
|
165
166
|
],
|
|
166
167
|
},
|
|
168
|
+
remove: { run: removeMain, forms: [{ operands: 'module <name>', flags: [] }] },
|
|
167
169
|
check: { run: checkMain, value: ['--coverage-from', '--shard', '--side', '--out'], list: GATE_FLAGS, boolean: ['--json', '--staged', '--no-cache', '--fix'], edition: true, forms: [{ only: { boolean: ['--json', '--no-cache', '--fix'], list: GATE_FLAGS, value: ['--coverage-from', '--shard', '--side', '--out'] } }, { only: { boolean: ['--staged'] }, edition: false }] },
|
|
168
170
|
slot: { run: slotMain, boolean: ['--json'], forms: [{ operands: '<path>' }] },
|
|
169
171
|
emit: { run: emitMain, forms: [{}] },
|
|
@@ -204,6 +206,11 @@ const VERB_USAGE = {
|
|
|
204
206
|
world: ['starci app world <up|down|status> [--stack <dir>] [--services <list>] [--k3d] [--force] [--json] [--edition full|lite]'],
|
|
205
207
|
stack: [
|
|
206
208
|
...STACK_ACTIONS.map((action) => line(`starci app stack ${action}`, STACK_OPERANDS[action], STACK_READ_ONLY.includes(action) ? flagUsage('--env', { value: true }) : '--env <env>', STACK_FLAGS[action].map((flag) => flagUsage(flag, { value: STACK_VALUE.has(flag), repeat: STACK_REPEAT.has(flag) })), '[--json]')),
|
|
209
|
+
// The dev actions of a docker-compose environment (stack/local-cli.mts); prepare, status and verify above answer them too.
|
|
210
|
+
'starci app stack doctor --env <env> [--json]',
|
|
211
|
+
'starci app stack up --env <env> [--execute] [--timeout <seconds>] [--json]',
|
|
212
|
+
'starci app stack down --env <env> [--execute] [--json]',
|
|
213
|
+
'starci app stack logs --env <env> --service <name> [--lines <n>] [--json]',
|
|
207
214
|
],
|
|
208
215
|
release: Object.entries(RELEASE_FLAGS).map(([action, spec]) => line(`starci app release ${action}`, specUsage(spec))),
|
|
209
216
|
secret: SECRET_VERBS.map((verb) => line(`starci app secret ${verb}`, secretUsage(verb))),
|
|
@@ -680,6 +687,20 @@ function addMain(opts, { cwd: repoRoot, stdout: out }) {
|
|
|
680
687
|
out(`starci app add ${noun}: registered patterns ${registered.patterns.join(', ')}${kinds}\n`);
|
|
681
688
|
return 0;
|
|
682
689
|
}
|
|
690
|
+
/** `remove module <name>`: deletes the module package and its registrations and re-renders the registry. */
|
|
691
|
+
function removeMain(opts, { cwd: repoRoot, stdout: out }) {
|
|
692
|
+
const [noun, name, ...extra] = opts.positional;
|
|
693
|
+
if (noun !== 'module' || !name || extra.length)
|
|
694
|
+
throw new UsageError('starci app remove takes `module <name>`');
|
|
695
|
+
const { removed, updated, created } = removeModule({ root: repoRoot, name });
|
|
696
|
+
for (const file of removed)
|
|
697
|
+
out(`removed ${file}
|
|
698
|
+
`);
|
|
699
|
+
for (const file of [...updated, ...created])
|
|
700
|
+
out(`updated ${file}
|
|
701
|
+
`);
|
|
702
|
+
return 0;
|
|
703
|
+
}
|
|
683
704
|
async function scaffoldMain(opts, { cwd, harness, edition = 'full', presets, lock, stdout: out }) {
|
|
684
705
|
const [name, ...extra] = opts.positional;
|
|
685
706
|
const registryVisibility = valueFlag(opts, 'registry-visibility');
|
package/stack/cli.mjs
CHANGED
|
@@ -18,6 +18,7 @@ import { withDockerTransport } from './docker-transport.mjs';
|
|
|
18
18
|
import { StackError, EXIT, refusal } from './errors.mjs';
|
|
19
19
|
import { parseTargetBinding, bindStackTarget, nativeToolEnvironment } from './target-binding.mjs';
|
|
20
20
|
import { oneCommand, prepareUrgentRelease } from './urgent-release.mjs';
|
|
21
|
+
import { isLocalEnvironment, isLocalOnly, isLocalShared, runLocal } from './local-cli.mjs';
|
|
21
22
|
/** The actions that drive the docker CLI of the operator machine (pointed at the manager by docker-transport.mjs when the key is sealed). */
|
|
22
23
|
const DOCKER_ACTIONS = new Set(['deploy', 'accept', 'converge', 'verify', 'rollback', 'prune', 'status']);
|
|
23
24
|
export const ACTIONS = Object.freeze(['prepare', 'connect', 'disconnect', 'build-args', 'render', 'deploy', 'converge', 'verify', 'rollback', 'prune', 'backup', 'jobs', 'task', 'ingress', 'retire', 'build', 'accept', 'status', 'ci-key']);
|
|
@@ -185,8 +186,12 @@ export async function stackMain(argv, io = {}) {
|
|
|
185
186
|
const ctx = stackContext(io);
|
|
186
187
|
try {
|
|
187
188
|
const action = argv[0];
|
|
189
|
+
if (isLocalOnly(action))
|
|
190
|
+
return await runLocal(ctx, action, argv.slice(1), io.localDeps);
|
|
188
191
|
if (!isAction(action))
|
|
189
|
-
throw refusal('STACK_USAGE', `starci app stack takes ${[...ACTIONS, 'up', 'down', '
|
|
192
|
+
throw refusal('STACK_USAGE', `starci app stack takes ${[...ACTIONS, 'doctor', 'up', 'down', 'logs'].join(', ')}`);
|
|
193
|
+
if (isLocalShared(action) && argv.includes('--env') && isLocalEnvironment(ctx.cwd, String(argv[argv.indexOf('--env') + 1])))
|
|
194
|
+
return await runLocal(ctx, action, argv.slice(1), io.localDeps);
|
|
190
195
|
const { flags, positional } = parseFlags(action, argv.slice(1));
|
|
191
196
|
if (flags.env === undefined && !READ_ONLY.includes(action))
|
|
192
197
|
throw refusal('STACK_USAGE', `stack ${action} has an effect: name its environment with --env <env> (only ${READ_ONLY.join(', ')} default to production)`);
|
package/stack/context.d.mts
CHANGED
|
@@ -47,6 +47,16 @@ export interface PipeResult {
|
|
|
47
47
|
sink: number;
|
|
48
48
|
stderr: string;
|
|
49
49
|
}
|
|
50
|
+
/** One heavy command the dev verbs run (`npm ci`, a compose build or up): admitted by the harness's heavy-command governor when it hands one in. */
|
|
51
|
+
export interface HeavyRequest {
|
|
52
|
+
kind: 'install' | 'build';
|
|
53
|
+
label: string;
|
|
54
|
+
cwd: string;
|
|
55
|
+
argv: string[];
|
|
56
|
+
env: NodeJS.ProcessEnv;
|
|
57
|
+
}
|
|
58
|
+
/** The governed runner: waits for a slot of the host-wide semaphore, runs the argv without a shell and returns its result. */
|
|
59
|
+
export type HeavyRun = (request: HeavyRequest) => Promise<RunResult>;
|
|
50
60
|
/** The secret seam: decrypts the app's sops files. */
|
|
51
61
|
export interface Sops {
|
|
52
62
|
decrypt(file: string, format?: string): Record<string, string>;
|
|
@@ -82,6 +92,9 @@ export interface StackIo {
|
|
|
82
92
|
role?: string | undefined;
|
|
83
93
|
} | undefined;
|
|
84
94
|
importer?: ((file: string) => Promise<Record<string, unknown>>) | undefined;
|
|
95
|
+
heavy?: HeavyRun | undefined;
|
|
96
|
+
/** The seams of the dev verbs a spec replaces (stack/local-run.mjs LocalDeps). */
|
|
97
|
+
localDeps?: import('./local-run.mjs').LocalDeps | undefined;
|
|
85
98
|
}
|
|
86
99
|
/** The context of one stack invocation (see stackContext). */
|
|
87
100
|
export interface StackContext {
|
|
@@ -98,6 +111,8 @@ export interface StackContext {
|
|
|
98
111
|
readonly fetch: typeof fetch;
|
|
99
112
|
readonly pipe: (source: PipeEnd, sink: PipeEnd) => Promise<PipeResult>;
|
|
100
113
|
readonly sops: Sops;
|
|
114
|
+
/** The governed runner of heavy commands (the harness CLI hands it in); absent, a dev verb runs them directly. */
|
|
115
|
+
readonly heavy?: HeavyRun | undefined;
|
|
101
116
|
/** The command-owned SSH connection, set by the Docker transport (docker-transport.mjs). */
|
|
102
117
|
readonly stackSsh?: SshConnection | undefined;
|
|
103
118
|
}
|
package/stack/context.mjs
CHANGED
|
@@ -74,6 +74,7 @@ export function stackContext(io = {}) {
|
|
|
74
74
|
stderr: io.stderr ?? ((text) => process.stderr.write(text)),
|
|
75
75
|
fetch: io.fetch ?? globalThis.fetch,
|
|
76
76
|
pipe: io.pipe ?? realPipe,
|
|
77
|
+
heavy: io.heavy,
|
|
77
78
|
/** The secret seam: the app's own sops/age tools, created on first use so verbs that open no custody never need them. */
|
|
78
79
|
get sops() {
|
|
79
80
|
const opened = sops ?? io.openSops?.() ?? defaultSops(env, cwd, io.platform ?? process.platform, undefined, io.hostIdentity ?? null);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# StarCi OpenClaw runtime layer: the harness-pinned source-built fork (images/openclaw, sources.json) plus the generic gateway wrapper.
|
|
2
|
+
# An app image does `FROM ghcr.io/starci183/openclaw-runtime@sha256:...` and only COPYs its own plugins into /opt/starci/plugins/<name>/;
|
|
3
|
+
# it carries no wrapper code (images/README.md "The OpenClaw runtime layer"). The base below is the recorded `published.reference` of the
|
|
4
|
+
# `openclaw` entry of sources.json; a new fork build raises it here in the same change.
|
|
5
|
+
FROM ghcr.io/starci183/openclaw:2026.10.10-beta.5-starci.1@sha256:0770c1da11ec4a46607ec16957ac3cff68d05db4a287f59f0e1d0e76de980ff8
|
|
6
|
+
|
|
7
|
+
USER root
|
|
8
|
+
# The wrapper is root-owned and not writable by the runtime user; the plugin directory is root-owned and world-readable, so a plugin an
|
|
9
|
+
# app copies in cannot be rewritten by the gateway it runs inside.
|
|
10
|
+
COPY --chmod=0555 entrypoint.sh /opt/starci/entrypoint.sh
|
|
11
|
+
COPY --chmod=0444 openclaw-init.mjs openclaw-relay.mjs /opt/starci/
|
|
12
|
+
RUN mkdir -p /opt/starci/plugins \
|
|
13
|
+
&& chown -R root:root /opt/starci \
|
|
14
|
+
&& chmod 0755 /opt/starci /opt/starci/plugins
|
|
15
|
+
|
|
16
|
+
USER node
|
|
17
|
+
ENV STARCI_PLUGINS_DIR=/opt/starci/plugins
|
|
18
|
+
ENTRYPOINT ["tini", "-s", "--", "sh", "/opt/starci/entrypoint.sh"]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Entrypoint of the StarCi OpenClaw runtime layer (/opt/starci/entrypoint.sh).
|
|
3
|
+
#
|
|
4
|
+
# 1. The OpenClaw image has no `<NAME>_FILE` support, so every secret named in STARCI_SECRETS (space separated; OPENCLAW_GATEWAY_TOKEN is always
|
|
5
|
+
# read) is exported from /run/secrets/<NAME> into THIS process tree only: no env file on the host, no value in the service specification.
|
|
6
|
+
# 2. openclaw-init.mjs merges the gateway settings into openclaw.json (idempotent; other keys, agents and plugins are kept) and enables the plugins
|
|
7
|
+
# found under STARCI_PLUGINS_DIR.
|
|
8
|
+
# 3. openclaw-relay.mjs listens on the overlay address and forwards to the loopback-only gateway: the gateway honours the scopes a client
|
|
9
|
+
# declares only for a loopback peer using the shared secret, so clients connect to the relay (OPENCLAW_RELAY_PORT, default 18790).
|
|
10
|
+
set -eu
|
|
11
|
+
|
|
12
|
+
for name in OPENCLAW_GATEWAY_TOKEN ${STARCI_SECRETS:-}; do
|
|
13
|
+
file="/run/secrets/$name"
|
|
14
|
+
if [ -r "$file" ]; then
|
|
15
|
+
value="$(cat "$file")"
|
|
16
|
+
export "$name=$value"
|
|
17
|
+
fi
|
|
18
|
+
done
|
|
19
|
+
: "${OPENCLAW_GATEWAY_TOKEN:?the secret OPENCLAW_GATEWAY_TOKEN is not granted to this service}"
|
|
20
|
+
|
|
21
|
+
OPENCLAW_GATEWAY_TOKEN_FILE=/run/secrets/OPENCLAW_GATEWAY_TOKEN node /opt/starci/openclaw-init.mjs
|
|
22
|
+
node /opt/starci/openclaw-relay.mjs &
|
|
23
|
+
exec node openclaw.mjs gateway
|