@flisk-sh/flisk 0.0.0-stage → 1.3.190
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 +492 -0
- package/LICENSE +21 -0
- package/README.md +479 -2
- package/Tiltfile +151 -0
- package/cli/bin/flisk.js +42 -0
- package/cli/dist/agent-runtimes/custom.js +335 -0
- package/cli/dist/agent-runtimes/registry.js +34 -0
- package/cli/dist/backend-frameworks/elysia.js +38 -0
- package/cli/dist/backend-frameworks/express.js +49 -0
- package/cli/dist/backend-frameworks/fastify.js +43 -0
- package/cli/dist/backend-frameworks/h3.js +37 -0
- package/cli/dist/backend-frameworks/koa.js +47 -0
- package/cli/dist/backend-frameworks/mcp.js +86 -0
- package/cli/dist/backend-frameworks/nestjs.js +55 -0
- package/cli/dist/backend-frameworks/registry.js +134 -0
- package/cli/dist/backend-frameworks/types.js +1 -0
- package/cli/dist/backend-languages/bun.js +76 -0
- package/cli/dist/backend-languages/go.js +125 -0
- package/cli/dist/backend-languages/node.js +122 -0
- package/cli/dist/backend-languages/python.js +94 -0
- package/cli/dist/backend-languages/registry.js +37 -0
- package/cli/dist/backend-languages/rust.js +82 -0
- package/cli/dist/backend-languages/types.js +1 -0
- package/cli/dist/cli.js +29 -0
- package/cli/dist/commands/__tests__/project-e2e-helpers.js +49 -0
- package/cli/dist/commands/__tests__/ui-loading-screen.test.js +16 -0
- package/cli/dist/commands/agent.js +182 -0
- package/cli/dist/commands/completion-candidates.js +116 -0
- package/cli/dist/commands/completion.js +843 -0
- package/cli/dist/commands/config.js +362 -0
- package/cli/dist/commands/doctor.js +1504 -0
- package/cli/dist/commands/down.js +114 -0
- package/cli/dist/commands/eject.js +108 -0
- package/cli/dist/commands/help.js +152 -0
- package/cli/dist/commands/import.js +164 -0
- package/cli/dist/commands/logs.js +119 -0
- package/cli/dist/commands/maintainers.js +102 -0
- package/cli/dist/commands/mcp.js +28 -0
- package/cli/dist/commands/networks.js +306 -0
- package/cli/dist/commands/project.js +447 -0
- package/cli/dist/commands/projects.js +103 -0
- package/cli/dist/commands/registry.js +53 -0
- package/cli/dist/commands/resource.js +992 -0
- package/cli/dist/commands/resources.js +127 -0
- package/cli/dist/commands/restart.js +89 -0
- package/cli/dist/commands/runtime.js +75 -0
- package/cli/dist/commands/stack.js +171 -0
- package/cli/dist/commands/stacks.js +71 -0
- package/cli/dist/commands/status.js +211 -0
- package/cli/dist/commands/ui.js +725 -0
- package/cli/dist/commands/up.js +609 -0
- package/cli/dist/commands/upgrade.js +840 -0
- package/cli/dist/commands/version.js +10 -0
- package/cli/dist/components/Accessible.js +11 -0
- package/cli/dist/components/BaseTooltip.js +30 -0
- package/cli/dist/components/DetailPanel.js +33 -0
- package/cli/dist/components/FileTree.js +51 -0
- package/cli/dist/components/ResourceSelectInput.js +42 -0
- package/cli/dist/components/ResourceTable.js +35 -0
- package/cli/dist/components/ServiceIssues.js +13 -0
- package/cli/dist/components/TUIHeader.js +27 -0
- package/cli/dist/components/TabBar.js +27 -0
- package/cli/dist/components/Tooltip.js +13 -0
- package/cli/dist/components/index.js +11 -0
- package/cli/dist/components/ui-theme.js +120 -0
- package/cli/dist/config/platform-standards.js +182 -0
- package/cli/dist/frontend-frameworks/lit.js +64 -0
- package/cli/dist/frontend-frameworks/preact.js +62 -0
- package/cli/dist/frontend-frameworks/qwik.js +61 -0
- package/cli/dist/frontend-frameworks/react.js +69 -0
- package/cli/dist/frontend-frameworks/registry.js +83 -0
- package/cli/dist/frontend-frameworks/solid.js +62 -0
- package/cli/dist/frontend-frameworks/svelte.js +67 -0
- package/cli/dist/frontend-frameworks/tanstack-router.js +101 -0
- package/cli/dist/frontend-frameworks/types.js +1 -0
- package/cli/dist/frontend-frameworks/vanilla.js +59 -0
- package/cli/dist/frontend-frameworks/vue.js +72 -0
- package/cli/dist/generator/__tests__/golden-helpers.js +43 -0
- package/cli/dist/generator/extension-fetch.js +291 -0
- package/cli/dist/generator/resource-templates.js +133 -0
- package/cli/dist/generator/template-engine.js +801 -0
- package/cli/dist/index.js +10 -0
- package/cli/dist/mcp/server.js +101 -0
- package/cli/dist/mcp/tools.js +351 -0
- package/cli/dist/program.js +37 -0
- package/cli/dist/types/index.js +38 -0
- package/cli/dist/utils/agent-host.js +36 -0
- package/cli/dist/utils/atomic-write.js +36 -0
- package/cli/dist/utils/bun-floor.js +9 -0
- package/cli/dist/utils/bun-requirement.js +10 -0
- package/cli/dist/utils/cache.js +63 -0
- package/cli/dist/utils/cold-preflight.js +257 -0
- package/cli/dist/utils/command-helpers.js +57 -0
- package/cli/dist/utils/constants.js +160 -0
- package/cli/dist/utils/discovery-context.js +71 -0
- package/cli/dist/utils/discovery-paths.js +93 -0
- package/cli/dist/utils/docker.js +34 -0
- package/cli/dist/utils/doctor-crashloop.js +161 -0
- package/cli/dist/utils/doctor-fix.js +391 -0
- package/cli/dist/utils/doctor-host.js +66 -0
- package/cli/dist/utils/doctor-report.js +78 -0
- package/cli/dist/utils/doctor-runtime.js +880 -0
- package/cli/dist/utils/doctor-starlark.js +132 -0
- package/cli/dist/utils/doctor-wiring.js +698 -0
- package/cli/dist/utils/down-cleanup.js +125 -0
- package/cli/dist/utils/env-validator.js +266 -0
- package/cli/dist/utils/errors.js +175 -0
- package/cli/dist/utils/exec-async.js +31 -0
- package/cli/dist/utils/file-helpers.js +104 -0
- package/cli/dist/utils/formatting.js +165 -0
- package/cli/dist/utils/generated-paths.js +51 -0
- package/cli/dist/utils/help-command.js +11 -0
- package/cli/dist/utils/host-port-config.js +140 -0
- package/cli/dist/utils/host-port-plan.js +100 -0
- package/cli/dist/utils/json-output.js +29 -0
- package/cli/dist/utils/machine-output.js +33 -0
- package/cli/dist/utils/paths.js +43 -0
- package/cli/dist/utils/platform.js +58 -0
- package/cli/dist/utils/port-assignment.js +75 -0
- package/cli/dist/utils/port-conflict-fix.js +25 -0
- package/cli/dist/utils/port-fallback-notice.js +17 -0
- package/cli/dist/utils/port-listener-probe.js +20 -0
- package/cli/dist/utils/project-config-defaults.js +17 -0
- package/cli/dist/utils/project-features.js +91 -0
- package/cli/dist/utils/project-templates.js +32 -0
- package/cli/dist/utils/prompt.js +97 -0
- package/cli/dist/utils/resource-features.js +184 -0
- package/cli/dist/utils/resource-kind.js +9 -0
- package/cli/dist/utils/schema-version-gate.js +45 -0
- package/cli/dist/utils/search-status.js +12 -0
- package/cli/dist/utils/secret-redaction.js +73 -0
- package/cli/dist/utils/service-config-checks.js +189 -0
- package/cli/dist/utils/service-manifest.js +254 -0
- package/cli/dist/utils/service-runtime-state.js +66 -0
- package/cli/dist/utils/service-urls.js +112 -0
- package/cli/dist/utils/services.js +360 -0
- package/cli/dist/utils/shared-infra.js +45 -0
- package/cli/dist/utils/shared-platform-postgres.js +232 -0
- package/cli/dist/utils/single-flight.js +21 -0
- package/cli/dist/utils/smoke.js +401 -0
- package/cli/dist/utils/spinner.js +40 -0
- package/cli/dist/utils/stack-features.js +15 -0
- package/cli/dist/utils/stack-status.js +14 -0
- package/cli/dist/utils/startup-report.js +78 -0
- package/cli/dist/utils/status-message.js +21 -0
- package/cli/dist/utils/status-ports.js +33 -0
- package/cli/dist/utils/suggestions.js +63 -0
- package/cli/dist/utils/tar.js +46 -0
- package/cli/dist/utils/tdk-version.js +51 -0
- package/cli/dist/utils/terminal-layout.js +19 -0
- package/cli/dist/utils/tilt-events.js +194 -0
- package/cli/dist/utils/tilt-logs.js +61 -0
- package/cli/dist/utils/tilt-port-owner.js +63 -0
- package/cli/dist/utils/tilt-process.js +54 -0
- package/cli/dist/utils/tilt-resource-state.js +32 -0
- package/cli/dist/utils/tilt-startup.js +49 -0
- package/cli/dist/utils/tilt-unreachable-message.js +9 -0
- package/cli/dist/utils/tilt.js +104 -0
- package/cli/dist/utils/ui-keymap.js +129 -0
- package/cli/dist/utils/ui-service-state.js +34 -0
- package/cli/dist/utils/up-only.js +28 -0
- package/cli/dist/utils/up-readiness.js +118 -0
- package/cli/dist/utils/url-actions.js +103 -0
- package/cli/dist/utils/validation.js +62 -0
- package/cli/dist/utils/which.js +56 -0
- package/cli/dist/utils/windows-doctor.js +176 -0
- package/cli/dist-bundle/chunk-6EA4DGCY.js +2 -0
- package/cli/dist-bundle/chunk-BZ4H7MM4.js +3 -0
- package/cli/dist-bundle/chunk-CLCUIDBH.js +510 -0
- package/cli/dist-bundle/cli.js +2017 -0
- package/cli/dist-bundle/cold-preflight-SY26O2IG.js +2 -0
- package/cli/dist-bundle/devtools-ZUHXW33W.js +5 -0
- package/cli/dist-bundle/extension-fetch-J4FFH7TJ.js +2 -0
- package/cli/package.json +83 -0
- package/cli/shared-platform-engineering/docker-templates/bun-hoisted-symlink-fix.sh +25 -0
- package/cli/shared-platform-engineering/docker-templates/infisical-entrypoint.sh +466 -0
- package/cli/shared-platform-engineering/docker-templates/install-deps.sh +154 -0
- package/cli/shared-platform-engineering/docker-templates/prisma-bun-client-link-fix.sh +14 -0
- package/cli/shared-platform-engineering/docker-templates/prisma-normalize-client.sh +58 -0
- package/cli/templates/.tiltignore.hbs +72 -0
- package/cli/templates/TILT_RESOURCE_DEFAULTS.star.hbs +220 -0
- package/cli/templates/TILT_TECH_STACK.star.hbs +149 -0
- package/cli/templates/Tiltfile.hbs +381 -0
- package/cli/templates/spec.master.hbs +192 -0
- package/discovery/AGENTS.md +362 -0
- package/discovery/TILT_DISCOVERY.star +429 -0
- package/discovery/Tiltfile +58 -0
- package/discovery/config.star +265 -0
- package/discovery/constants.star +167 -0
- package/discovery/deterministic.star +213 -0
- package/discovery/discovery_daemon.star +193 -0
- package/discovery/discovery_orchestrator.star +492 -0
- package/discovery/filtering.star +7 -0
- package/discovery/identity-management-backend/service.yaml +26 -0
- package/discovery/identity-management-frontend/service.yaml +29 -0
- package/discovery/identity-sdk/service.yaml +9 -0
- package/discovery/incremental_discovery.star +215 -0
- package/discovery/indexing.star +9 -0
- package/discovery/json_manifest_scanner.star +57 -0
- package/discovery/libraries.star +48 -0
- package/discovery/loading.star +25 -0
- package/discovery/manifest/constants.star +106 -0
- package/discovery/manifest/loading.star +418 -0
- package/discovery/manifest/normalize.star +289 -0
- package/discovery/manifest/validation.star +110 -0
- package/discovery/normalization.star +13 -0
- package/discovery/registry.star +562 -0
- package/discovery/resource_snapshot.py +384 -0
- package/discovery/resource_snapshot.star +145 -0
- package/discovery/schemas.star +3 -0
- package/discovery/services/platform/cdc/debezium/nats-connector/service.yaml +9 -0
- package/discovery/services/platform/cdc/nats-http-bridge/service.yaml +9 -0
- package/discovery/services/platform/orchestrator/orchestrator-glue-backend/service.yaml +26 -0
- package/discovery/services/platform/synthetic-monitor/service.yaml +9 -0
- package/discovery/test_resource_snapshot.py +253 -0
- package/discovery/validation.star +80 -0
- package/discovery/versioning.star +3 -0
- package/engine/AGENTS.md +706 -0
- package/engine/README.md +31 -0
- package/engine/REFACTORING_MANIFEST_SYSTEM.md +974 -0
- package/engine/Tiltfile +86 -0
- package/engine/assets/docker/golden-layers.Dockerfile +204 -0
- package/engine/assets/docker/wake-gateway/Dockerfile +28 -0
- package/engine/assets/docker/wake-gateway/decide.test.ts +177 -0
- package/engine/assets/docker/wake-gateway/decide.ts +135 -0
- package/engine/assets/docker/wake-gateway/server.ts +363 -0
- package/engine/assets/scripts/cleanup-completed-migrator-images.sh +39 -0
- package/engine/assets/scripts/cleanup-tilt-images.sh +122 -0
- package/engine/assets/scripts/mac-m1-resource-monitor.sh +123 -0
- package/engine/config.star +5 -0
- package/engine/entrypoint.star +101 -0
- package/engine/lifecycle/orchestrator.star +263 -0
- package/engine/paths.star +719 -0
- package/engine/schemas/project-schema.json +147 -0
- package/engine/schemas/service-schema.json +537 -0
- package/engine/scripts/README.md +89 -0
- package/engine/scripts/build-and-publish-lib.sh +207 -0
- package/engine/scripts/check-lib-publish.sh +87 -0
- package/engine/scripts/debug-container.sh +265 -0
- package/engine/scripts/generate-manifest.sh +200 -0
- package/engine/spec.master +702 -0
- package/engine/spec.master.example +341 -0
- package/engine/topologies/README.md +33 -0
- package/engine/topologies/platform/docker/AGENTS.md +59 -0
- package/engine/topologies/platform/docker/auth/auth.star +207 -0
- package/engine/topologies/platform/docker/build/frontend_build_plan.star +65 -0
- package/engine/topologies/platform/docker/build/golden_image.star +37 -0
- package/engine/topologies/platform/docker/build/golden_image_build.star +144 -0
- package/engine/topologies/platform/docker/build/golden_image_constants.star +100 -0
- package/engine/topologies/platform/docker/build/golden_image_dockerfile.star +263 -0
- package/engine/topologies/platform/docker/compose/compose.star +637 -0
- package/engine/topologies/platform/docker/compose/compose_helpers.star +54 -0
- package/engine/topologies/platform/docker/compose/traefik_standalone.star +171 -0
- package/engine/topologies/platform/docker/config/bunignore.star +144 -0
- package/engine/topologies/platform/docker/config/dockerignore.star +152 -0
- package/engine/topologies/platform/docker/config/healthcheck.star +58 -0
- package/engine/topologies/platform/docker/constants.star +211 -0
- package/engine/topologies/platform/docker/dockerfile/dockerfile.star +56 -0
- package/engine/topologies/platform/docker/dockerfile/language_dockerfile.star +264 -0
- package/engine/topologies/platform/docker/generators/golden_docker_generator_v2.star +76 -0
- package/engine/topologies/platform/docker/index.star +40 -0
- package/engine/topologies/platform/docker/layers/bun_helpers.star +25 -0
- package/engine/topologies/platform/docker/layers/infisical/index.star +42 -0
- package/engine/topologies/platform/docker/layers/infisical/infisical_docker.star +264 -0
- package/engine/topologies/platform/docker/layers/l1_base_layers.star +89 -0
- package/engine/topologies/platform/docker/layers/l2_dependency_layers.star +147 -0
- package/engine/topologies/platform/docker/layers/l3_builder_layers.star +229 -0
- package/engine/topologies/platform/docker/layers/l4_runtime_layers.star +282 -0
- package/engine/topologies/platform/docker/layers/prisma/constants.star +138 -0
- package/engine/topologies/platform/docker/layers/prisma/db_readiness.star +98 -0
- package/engine/topologies/platform/docker/layers/prisma/filesystem_setup.star +131 -0
- package/engine/topologies/platform/docker/layers/prisma/migrate_script.star +100 -0
- package/engine/topologies/platform/docker/layers/prisma/prisma_build.star +128 -0
- package/engine/topologies/platform/docker/layers/prisma/prisma_runtime.star +205 -0
- package/engine/topologies/platform/docker/layers/prisma/secret_resolution.star +121 -0
- package/engine/topologies/platform/docker/networking/api_path_constants.star +206 -0
- package/engine/topologies/platform/docker/networking/sablier_container_cycle.star +31 -0
- package/engine/topologies/platform/docker/networking/traefik.star +266 -0
- package/engine/topologies/platform/docker/networking/traefik_constants.star +148 -0
- package/engine/topologies/platform/docker/networking/traefik_helpers.star +162 -0
- package/engine/topologies/platform/docker/networking/traefik_static_routes.star +165 -0
- package/engine/topologies/platform/docker/secrets/env_secrets.star +74 -0
- package/engine/topologies/platform/docker/secrets/infisical.star +109 -0
- package/engine/topologies/platform/net/AGENTS.md +64 -0
- package/engine/topologies/platform/net/proxy.star +6 -0
- package/engine/topologies/platform/registries/local.star +6 -0
- package/engine/topologies/platform/registries/verdaccio_loader.star +20 -0
- package/engine/topologies/platform/security/AGENTS.md +49 -0
- package/engine/topologies/platform/security/secrets.star +241 -0
- package/engine/topologies/platform/state/AGENTS.md +64 -0
- package/engine/topologies/platform/state/relational.star +6 -0
- package/engine/topologies/tilt/common/AGENTS.md +59 -0
- package/engine/topologies/tilt/common/utils.star +409 -0
- package/engine/topologies/tilt/common/utils_debug.star +71 -0
- package/engine/topologies/tilt/common/utils_deterministic.star +170 -0
- package/engine/topologies/tilt/common/utils_docker_networks.star +54 -0
- package/engine/topologies/tilt/common/utils_env.star +92 -0
- package/engine/topologies/tilt/common/utils_output.star +99 -0
- package/engine/topologies/tilt/config/AGENTS.md +68 -0
- package/engine/topologies/tilt/config/defaults.star +5 -0
- package/engine/topologies/tilt/config/feature_flags.star +12 -0
- package/engine/topologies/tilt/config/global.star +57 -0
- package/engine/topologies/tilt/config/profiles.star +398 -0
- package/engine/topologies/tilt/generators/agents_md.star +64 -0
- package/engine/topologies/tilt/generators/api_gateway.star +147 -0
- package/engine/topologies/tilt/generators/index.star +126 -0
- package/engine/topologies/tilt/generators/infisical/README.md +424 -0
- package/engine/topologies/tilt/generators/infisical/examples.star +314 -0
- package/engine/topologies/tilt/generators/infisical/index.star +59 -0
- package/engine/topologies/tilt/generators/infisical/machine_identity.star +422 -0
- package/engine/topologies/tilt/generators/infisical/organization.star +536 -0
- package/engine/topologies/tilt/generators/infisical/path_manager.star +478 -0
- package/engine/topologies/tilt/generators/infisical/secrets_generator.star +422 -0
- package/engine/topologies/tilt/generators/nginx_conf.star +145 -0
- package/engine/topologies/tilt/generators/npmrc.star +132 -0
- package/engine/topologies/tilt/generators/tsconfig.star +25 -0
- package/engine/topologies/tilt/generators/typescript/backend_library_tsconfig.star +73 -0
- package/engine/topologies/tilt/generators/typescript/backend_tsconfig.star +137 -0
- package/engine/topologies/tilt/generators/typescript/common.star +9 -0
- package/engine/topologies/tilt/generators/typescript/frontend_library_tsconfig.star +78 -0
- package/engine/topologies/tilt/generators/typescript/frontend_tsconfig.star +140 -0
- package/engine/topologies/tilt/generators/typescript/include.star +25 -0
- package/engine/topologies/tilt/generators/typescript/playwright_config.star +42 -0
- package/engine/topologies/tilt/generators/typescript/prisma_config.star +51 -0
- package/engine/topologies/tilt/generators/validators.star +868 -0
- package/engine/topologies/tilt/generators/vite/backend.star +80 -0
- package/engine/topologies/tilt/generators/vite/frameworks/lit.star +92 -0
- package/engine/topologies/tilt/generators/vite/frameworks/preact.star +94 -0
- package/engine/topologies/tilt/generators/vite/frameworks/qwik.star +94 -0
- package/engine/topologies/tilt/generators/vite/frameworks/react.star +128 -0
- package/engine/topologies/tilt/generators/vite/frameworks/solid.star +94 -0
- package/engine/topologies/tilt/generators/vite/frameworks/svelte.star +94 -0
- package/engine/topologies/tilt/generators/vite/frameworks/vanilla.star +92 -0
- package/engine/topologies/tilt/generators/vite/frameworks/vue.star +94 -0
- package/engine/topologies/tilt/generators/vite/frontend.star +153 -0
- package/engine/topologies/tilt/generators/vite/generators.star +45 -0
- package/engine/topologies/tilt/generators/vite/helpers.star +256 -0
- package/engine/topologies/tilt/generators/vite/library.star +67 -0
- package/engine/topologies/tilt/generators/vite/sdk.star +75 -0
- package/engine/topologies/tilt/generators/vite/templates.star +460 -0
- package/engine/topologies/tilt/generators/vite_config.star +24 -0
- package/engine/topologies/tilt/manifest/AGENTS.md +61 -0
- package/engine/topologies/tilt/manifest/README.md +679 -0
- package/engine/topologies/tilt/manifest/__init__.star +220 -0
- package/engine/topologies/tilt/manifest/constants.star +262 -0
- package/engine/topologies/tilt/manifest/errors.star +268 -0
- package/engine/topologies/tilt/manifest/integration.star +348 -0
- package/engine/topologies/tilt/manifest/json_to_yaml.py +61 -0
- package/engine/topologies/tilt/manifest/loader.star +239 -0
- package/engine/topologies/tilt/manifest/mapper.star +229 -0
- package/engine/topologies/tilt/manifest/parser.star +490 -0
- package/engine/topologies/tilt/manifest/schema.star +502 -0
- package/engine/topologies/tilt/manifest/tests/test_integration.star +278 -0
- package/engine/topologies/tilt/manifest/tests/test_loader_parser.star +475 -0
- package/engine/topologies/tilt/manifest/tests/test_manifest.star +319 -0
- package/engine/topologies/tilt/manifest/tests/test_validator.star +472 -0
- package/engine/topologies/tilt/manifest/validator.star +744 -0
- package/engine/topologies/tilt/manifest/yaml_to_json.py +20 -0
- package/engine/topologies/tilt/resources/AGENTS.md +71 -0
- package/engine/topologies/tilt/resources/build_concurrency.star +56 -0
- package/engine/topologies/tilt/resources/conditions.star +5 -0
- package/engine/topologies/tilt/resources/databases.star +246 -0
- package/engine/topologies/tilt/resources/declaration.star +5 -0
- package/engine/topologies/tilt/resources/deps.star +5 -0
- package/engine/topologies/tilt/resources/infra-loader.star +504 -0
- package/engine/topologies/tilt/resources/libs.star +32 -0
- package/engine/topologies/tilt/resources/libs_discovery.star +104 -0
- package/engine/topologies/tilt/resources/libs_paths.star +81 -0
- package/engine/topologies/tilt/resources/libs_resource_definition.star +153 -0
- package/engine/topologies/tilt/resources/libs_setup.star +166 -0
- package/engine/topologies/tilt/resources/libs_tsconfig.star +39 -0
- package/engine/topologies/tilt/resources/orchestrator/apply.star +79 -0
- package/engine/topologies/tilt/resources/orchestrator/apply_compose_resource_registration.star +864 -0
- package/engine/topologies/tilt/resources/orchestrator/apply_manifest_orchestration.star +90 -0
- package/engine/topologies/tilt/resources/orchestrator/apply_migrator_orchestration.star +150 -0
- package/engine/topologies/tilt/resources/orchestrator/apply_resource_validation.star +38 -0
- package/engine/topologies/tilt/resources/orchestrator/apply_runtime_flags.star +46 -0
- package/engine/topologies/tilt/resources/orchestrator/builders/typescript.star +193 -0
- package/engine/topologies/tilt/resources/orchestrator/generators/dependencies/AGENTS.md +75 -0
- package/engine/topologies/tilt/resources/orchestrator/generators/dependencies/index.star +23 -0
- package/engine/topologies/tilt/resources/orchestrator/generators/dependencies/resolver.star +136 -0
- package/engine/topologies/tilt/resources/orchestrator/generators/env.star +219 -0
- package/engine/topologies/tilt/resources/orchestrator/generators/frontend.star +172 -0
- package/engine/topologies/tilt/resources/orchestrator/generators/manifest_resource.star +577 -0
- package/engine/topologies/tilt/resources/orchestrator/helpers.star +35 -0
- package/engine/topologies/tilt/resources/orchestrator.star +56 -0
- package/engine/topologies/tilt/resources/ordering.star +5 -0
- package/engine/topologies/tilt/resources/shared-platform-postgres.star +30 -0
- package/engine/topologies/tilt/resources/triggers.star +5 -0
- package/engine/topologies/tilt/resources/watchers.star +304 -0
- package/engine/where-things-live.star +719 -0
- package/examples/tdk-example/LICENSE +21 -0
- package/examples/tdk-example/README.md +23 -0
- package/examples/tdk-example/TILT_SERVICE_DEFAULTS.star +42 -0
- package/examples/tdk-example/TILT_TECH_STACK.star +67 -0
- package/examples/tdk-example/Tiltfile +25 -0
- package/examples/tdk-example/services/platform/messaging/docker-compose.yml +26 -0
- package/examples/tdk-example/services/shop/orders-api/package.json +20 -0
- package/examples/tdk-example/services/shop/orders-api/service.json +14 -0
- package/examples/tdk-example/services/shop/orders-api/src/index.ts +81 -0
- package/examples/tdk-example/services/shop/orders-app/index.html +12 -0
- package/examples/tdk-example/services/shop/orders-app/package.json +23 -0
- package/examples/tdk-example/services/shop/orders-app/public/.gitkeep +0 -0
- package/examples/tdk-example/services/shop/orders-app/service.json +9 -0
- package/examples/tdk-example/services/shop/orders-app/src/main.tsx +9 -0
- package/examples/tdk-example/services/shop/orders-app/src/orders.tsx +67 -0
- package/examples/tdk-example/services/shop/orders-app/src/style.css +15 -0
- package/examples/tdk-example/services/shop/orders-worker/package.json +18 -0
- package/examples/tdk-example/services/shop/orders-worker/service.json +13 -0
- package/examples/tdk-example/services/shop/orders-worker/src/index.ts +33 -0
- package/ext/Tiltfile +66 -0
- package/ext/ide-components/README.md +221 -0
- package/ext/ide-components/code_viewer/server.py +208 -0
- package/ext/ide-components/config_inspector/server.py +1052 -0
- package/ext/ide-components/file_browser/server.py +287 -0
- package/ext/ide-components/shared/config_service.py +471 -0
- package/ext/ide-components/shared/file_utils.py +300 -0
- package/ext/ide-components/shared/http_utils.py +234 -0
- package/ext/ide-components/shared/static/styles.css +1334 -0
- package/ext/ide-components/shared/syntax_highlighter.py +142 -0
- package/ext/ide-components/shared/template_engine.py +125 -0
- package/ext/ide-components/shared/templates/base.html +121 -0
- package/ext/ide-components/shared/test_config_service.py +328 -0
- package/ext/ide-components/shared/tilt_integration.py +160 -0
- package/ext/ui-enhancements/cron-status-server.sh +51 -0
- package/ext/ui-enhancements/cron-status-tracker.sh +148 -0
- package/ext/ui-enhancements/extension.yaml +36 -0
- package/ext/ui-enhancements/tiltfile.py +101 -0
- package/ext/ui-enhancements/ui/cron-jobs-tab.css +396 -0
- package/ext/ui-enhancements/ui/help-panel.css +279 -0
- package/ext/ui-enhancements/ui/icons.css +125 -0
- package/ext/ui-enhancements/ui/tooltips.css +114 -0
- package/package.json +94 -4
- package/shared-platform-engineering/docker-templates/bun-hoisted-symlink-fix.sh +25 -0
- package/shared-platform-engineering/docker-templates/infisical-entrypoint.sh +466 -0
- package/shared-platform-engineering/docker-templates/install-deps.sh +154 -0
- package/shared-platform-engineering/docker-templates/prisma-bun-client-link-fix.sh +14 -0
- package/shared-platform-engineering/docker-templates/prisma-normalize-client.sh +58 -0
- package/specs/specs/TILT_SERVICE_DEFAULTS.star +207 -0
- package/specs/specs/TILT_TECH_STACK.star +161 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// Copyright (c) 2026 Flisk Landscape contributors
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { dirname, resolve } from "node:path";
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
export function writeFilesWithProgress(basePath, tasks, onProgress) {
|
|
7
|
+
for (const [i, task] of tasks.entries()) {
|
|
8
|
+
console.log(chalk.blue(`${task.emoji} ${task.description}...`));
|
|
9
|
+
if (task.type === "json") {
|
|
10
|
+
writeJsonFileInDir(basePath, task.filename, task.content);
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
writeTextFileInDir(basePath, task.filename, String(task.content));
|
|
14
|
+
}
|
|
15
|
+
if (onProgress) {
|
|
16
|
+
onProgress(task, i, tasks.length);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Write JSON data to a file.
|
|
22
|
+
* @param filePath - Path to the file
|
|
23
|
+
* @param data - JSON-serializable data
|
|
24
|
+
* @param space - Number of spaces for indentation (default: 2)
|
|
25
|
+
*/
|
|
26
|
+
export function writeJsonFile(filePath, data, space = 2) {
|
|
27
|
+
const content = `${JSON.stringify(data, null, space)}\n`;
|
|
28
|
+
writeFileSync(filePath, content, "utf-8");
|
|
29
|
+
}
|
|
30
|
+
export function writeJsonFileInDir(dir, filename, data, space = 2) {
|
|
31
|
+
const filePath = resolve(dir, filename);
|
|
32
|
+
writeJsonFile(filePath, data, space);
|
|
33
|
+
}
|
|
34
|
+
export function writeTextFile(filePath, content) {
|
|
35
|
+
writeFileSync(filePath, content, "utf-8");
|
|
36
|
+
}
|
|
37
|
+
export function writeTextFileInDir(dir, filename, content) {
|
|
38
|
+
const filePath = resolve(dir, filename);
|
|
39
|
+
writeTextFile(filePath, content);
|
|
40
|
+
}
|
|
41
|
+
export function ensureDirectory(dirPath) {
|
|
42
|
+
if (!existsSync(dirPath)) {
|
|
43
|
+
mkdirSync(dirPath, { recursive: true });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** Generated files that must never be committed: .env holds a generated DB password. */
|
|
47
|
+
const GITIGNORE_ENTRIES = [
|
|
48
|
+
".env",
|
|
49
|
+
".tdk/.tdk-out/",
|
|
50
|
+
".tdk/.project-id",
|
|
51
|
+
".tdk/smoke/",
|
|
52
|
+
"node_modules/",
|
|
53
|
+
];
|
|
54
|
+
/**
|
|
55
|
+
* Appends the Flisk entries missing from the project's .gitignore (creating it
|
|
56
|
+
* if needed), so the first `git add .` doesn't commit .env or generated
|
|
57
|
+
* output. Returns the entries it added.
|
|
58
|
+
*/
|
|
59
|
+
export function ensureGitignore(projectRoot) {
|
|
60
|
+
const gitignorePath = resolve(projectRoot, ".gitignore");
|
|
61
|
+
const existing = existsSync(gitignorePath) ? readFileSync(gitignorePath, "utf-8") : "";
|
|
62
|
+
const lines = new Set(existing.split("\n").map((line) => line.trim().replace(/\/$/, "")));
|
|
63
|
+
const missing = GITIGNORE_ENTRIES.filter((entry) => !lines.has(entry.replace(/\/$/, "")));
|
|
64
|
+
if (missing.length === 0)
|
|
65
|
+
return [];
|
|
66
|
+
const prefix = existing && !existing.endsWith("\n") ? "\n" : "";
|
|
67
|
+
const header = existing
|
|
68
|
+
? "\n# Flisk (generated files and secrets)\n"
|
|
69
|
+
: "# Flisk (generated files and secrets)\n";
|
|
70
|
+
appendFileSync(gitignorePath, `${prefix}${header}${missing.join("\n")}\n`);
|
|
71
|
+
return missing;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* True when the path sits inside a git working tree: a `.git` directory in a clone, a `.git` file in a linked worktree.
|
|
75
|
+
* Only then is an ignore entry worth writing.
|
|
76
|
+
*/
|
|
77
|
+
export function isInsideGitWorkTree(startPath) {
|
|
78
|
+
let dir = resolve(startPath);
|
|
79
|
+
for (;;) {
|
|
80
|
+
if (existsSync(resolve(dir, ".git")))
|
|
81
|
+
return true;
|
|
82
|
+
const parent = dirname(dir);
|
|
83
|
+
if (parent === dir)
|
|
84
|
+
return false;
|
|
85
|
+
dir = parent;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Makes sure the smoke records cannot be committed by accident: the project .gitignore must ignore `.tdk/smoke/` before
|
|
90
|
+
* `flisk up` writes a record. Only meaningful inside a git working tree, so nothing is written outside one. Returns the
|
|
91
|
+
* warning to show when the entry could not be ensured, and undefined when it is.
|
|
92
|
+
*/
|
|
93
|
+
export function ensureSmokeRecordsIgnored(projectRoot) {
|
|
94
|
+
if (!isInsideGitWorkTree(projectRoot))
|
|
95
|
+
return undefined;
|
|
96
|
+
try {
|
|
97
|
+
ensureGitignore(projectRoot);
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
102
|
+
return `Could not add .tdk/smoke/ to ${resolve(projectRoot, ".gitignore")}: ${reason}. Git may track the smoke response records.`;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// Copyright (c) 2026 Flisk Landscape contributors
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
function pluralize(count, singular, plural) {
|
|
5
|
+
return count === 1 ? singular : plural || `${singular}s`;
|
|
6
|
+
}
|
|
7
|
+
export function formatCount(count, singular, plural) {
|
|
8
|
+
return `${count} ${pluralize(count, singular, plural)}`;
|
|
9
|
+
}
|
|
10
|
+
const DATE_FORMAT_OPTIONS = {
|
|
11
|
+
month: "short",
|
|
12
|
+
day: "numeric",
|
|
13
|
+
hour: "2-digit",
|
|
14
|
+
minute: "2-digit",
|
|
15
|
+
};
|
|
16
|
+
export function formatDate(timestamp) {
|
|
17
|
+
const date = new Date(timestamp);
|
|
18
|
+
return date.toLocaleString("en-US", DATE_FORMAT_OPTIONS);
|
|
19
|
+
}
|
|
20
|
+
export function formatShortDate(timestamp) {
|
|
21
|
+
const date = new Date(timestamp);
|
|
22
|
+
return date.toLocaleDateString("en-US", { month: "short", day: "numeric" });
|
|
23
|
+
}
|
|
24
|
+
const STATUS_CATEGORY_CONFIG = {
|
|
25
|
+
success: { color: "green", icon: "✓", chalkFn: chalk.green },
|
|
26
|
+
error: { color: "red", icon: "✗", chalkFn: chalk.red },
|
|
27
|
+
warning: { color: "yellow", icon: "○", chalkFn: chalk.yellow },
|
|
28
|
+
unknown: { color: "gray", icon: "?", chalkFn: chalk.gray },
|
|
29
|
+
};
|
|
30
|
+
function getStatusCategory(status) {
|
|
31
|
+
if (!status)
|
|
32
|
+
return "unknown";
|
|
33
|
+
const lowerStatus = status.toLowerCase();
|
|
34
|
+
if (lowerStatus === "ready" ||
|
|
35
|
+
lowerStatus === "healthy" ||
|
|
36
|
+
lowerStatus === "active" ||
|
|
37
|
+
lowerStatus === "running") {
|
|
38
|
+
return "success";
|
|
39
|
+
}
|
|
40
|
+
if (lowerStatus === "error" ||
|
|
41
|
+
lowerStatus === "failed" ||
|
|
42
|
+
lowerStatus === "critical" ||
|
|
43
|
+
lowerStatus === "stopped") {
|
|
44
|
+
return "error";
|
|
45
|
+
}
|
|
46
|
+
if (lowerStatus === "pending" ||
|
|
47
|
+
lowerStatus === "starting" ||
|
|
48
|
+
lowerStatus === "building" ||
|
|
49
|
+
lowerStatus === "degraded") {
|
|
50
|
+
return "warning";
|
|
51
|
+
}
|
|
52
|
+
return "unknown";
|
|
53
|
+
}
|
|
54
|
+
export function getStatusColor(status) {
|
|
55
|
+
return STATUS_CATEGORY_CONFIG[getStatusCategory(status)].color;
|
|
56
|
+
}
|
|
57
|
+
export function getStatusIcon(status) {
|
|
58
|
+
return STATUS_CATEGORY_CONFIG[getStatusCategory(status)].icon;
|
|
59
|
+
}
|
|
60
|
+
export function colorizeByStatus(text, status) {
|
|
61
|
+
return STATUS_CATEGORY_CONFIG[getStatusCategory(status)].chalkFn(text);
|
|
62
|
+
}
|
|
63
|
+
const EMPTY_STATE_CONFIG = {
|
|
64
|
+
resources: {
|
|
65
|
+
singular: "resource",
|
|
66
|
+
command: "flisk resource <name>",
|
|
67
|
+
context: "\nTo create a resource:",
|
|
68
|
+
},
|
|
69
|
+
stacks: {
|
|
70
|
+
singular: "stack",
|
|
71
|
+
command: "flisk stack <stack-name>",
|
|
72
|
+
context: "\nTo create a stack, use:",
|
|
73
|
+
},
|
|
74
|
+
services: {
|
|
75
|
+
singular: "service",
|
|
76
|
+
command: "flisk resource <name>",
|
|
77
|
+
context: "\nTo create a service:",
|
|
78
|
+
},
|
|
79
|
+
"stack-services": {
|
|
80
|
+
singular: "service",
|
|
81
|
+
command: "flisk resource <name> --stack <stack-name>",
|
|
82
|
+
context: "\nTo add services to this stack:",
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
export function showEmptyState(itemType, filterContext) {
|
|
86
|
+
const config = EMPTY_STATE_CONFIG[itemType];
|
|
87
|
+
if (filterContext) {
|
|
88
|
+
console.log(chalk.yellow(`No ${config.singular}s found${filterContext}.`));
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
console.log(chalk.yellow(`No ${config.singular}s found.`));
|
|
92
|
+
}
|
|
93
|
+
console.log(chalk.gray(config.context));
|
|
94
|
+
console.log(chalk.gray(` ${config.command}`));
|
|
95
|
+
if (itemType === "stacks") {
|
|
96
|
+
console.log(chalk.gray("\nOr create a new resource with a stack:"));
|
|
97
|
+
console.log(chalk.gray(" flisk resource <name> --stack <stack-name>"));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
export function showCancelled(message) {
|
|
101
|
+
console.log(chalk.yellow(message || "Cancelled."));
|
|
102
|
+
}
|
|
103
|
+
export function showCommandHeader(title) {
|
|
104
|
+
console.log(chalk.blue(`Flisk ${title}\n`));
|
|
105
|
+
}
|
|
106
|
+
export function showAllSatisfyCondition(items, condition) {
|
|
107
|
+
console.log(chalk.green(`All ${items} are ${condition}!`));
|
|
108
|
+
}
|
|
109
|
+
/** Default width for ASCII boxes */
|
|
110
|
+
export const DEFAULT_BOX_WIDTH = 62;
|
|
111
|
+
export function formatBoxLine(char = "─", width = DEFAULT_BOX_WIDTH) {
|
|
112
|
+
return char.repeat(width);
|
|
113
|
+
}
|
|
114
|
+
export function formatCentered(text, width = DEFAULT_BOX_WIDTH - 2) {
|
|
115
|
+
const padding = Math.max(0, width - text.length);
|
|
116
|
+
const left = Math.floor(padding / 2);
|
|
117
|
+
const right = padding - left;
|
|
118
|
+
return " ".repeat(left) + text + " ".repeat(right);
|
|
119
|
+
}
|
|
120
|
+
export function formatPadded(text, width) {
|
|
121
|
+
if (text.length > width) {
|
|
122
|
+
return `${text.slice(0, width - 1)}…`;
|
|
123
|
+
}
|
|
124
|
+
return text.padEnd(width);
|
|
125
|
+
}
|
|
126
|
+
export function truncate(str, maxLength) {
|
|
127
|
+
if (str.length <= maxLength)
|
|
128
|
+
return str;
|
|
129
|
+
return `${str.slice(0, maxLength - 3)}...`;
|
|
130
|
+
}
|
|
131
|
+
export function showSuccess(message) {
|
|
132
|
+
console.log(chalk.green(`✓ ${message}`));
|
|
133
|
+
}
|
|
134
|
+
export function showStep(message) {
|
|
135
|
+
console.log(chalk.blue(message));
|
|
136
|
+
}
|
|
137
|
+
export function showDetail(message, indent = 2) {
|
|
138
|
+
console.log(chalk.gray(`${" ".repeat(indent)}${message}`));
|
|
139
|
+
}
|
|
140
|
+
export function formatBytes(bytes) {
|
|
141
|
+
if (bytes < 1024)
|
|
142
|
+
return `${bytes} B`;
|
|
143
|
+
if (bytes < 1024 * 1024)
|
|
144
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
145
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Print a boxed header with title and optional subtitle.
|
|
149
|
+
* Consolidates common box formatting patterns from networks.ts and other commands.
|
|
150
|
+
* @param title - The main title to display
|
|
151
|
+
* @param subtitle - Optional subtitle (e.g., domain info)
|
|
152
|
+
* @param width - Box width (defaults to DEFAULT_BOX_WIDTH)
|
|
153
|
+
*/
|
|
154
|
+
export function printBoxedHeader(title, subtitle, width = DEFAULT_BOX_WIDTH) {
|
|
155
|
+
const innerWidth = width - 2;
|
|
156
|
+
const line = "─".repeat(innerWidth);
|
|
157
|
+
console.log();
|
|
158
|
+
console.log(chalk.cyan(`╭${line}╮`));
|
|
159
|
+
console.log(chalk.cyan("│") + chalk.bold.white(formatCentered(title, innerWidth)) + chalk.cyan("│"));
|
|
160
|
+
if (subtitle) {
|
|
161
|
+
console.log(chalk.cyan(`├${line}┤`));
|
|
162
|
+
console.log(chalk.cyan("│") + chalk.gray(formatCentered(subtitle, innerWidth)) + chalk.cyan("│"));
|
|
163
|
+
}
|
|
164
|
+
console.log(chalk.cyan(`╰${line}╯`));
|
|
165
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// Copyright (c) 2026 Flisk Landscape contributors
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
import { existsSync, lstatSync } from "node:fs";
|
|
4
|
+
import { isAbsolute, normalize, relative, sep } from "node:path";
|
|
5
|
+
import { AUTOGENERATED_DIR, PACKAGE_JSON, TILTIGNORE_FILE } from "./constants.js";
|
|
6
|
+
/** Reviewed Flisk-owned destinations written by `flisk config regenerate` or Tilt generation. */
|
|
7
|
+
export const TDK_GENERATED_PATH_ALLOWLIST = [
|
|
8
|
+
`${AUTOGENERATED_DIR}/**`,
|
|
9
|
+
".tdk/.tdk-out/**",
|
|
10
|
+
"Tiltfile",
|
|
11
|
+
"TILT_*.star",
|
|
12
|
+
TILTIGNORE_FILE,
|
|
13
|
+
`${PACKAGE_JSON} (create only)`,
|
|
14
|
+
"shared-platform-engineering/docker-templates/**",
|
|
15
|
+
"services/platform/database-management/docker-compose.yml",
|
|
16
|
+
"product_autogenerated_resource_manifests/**",
|
|
17
|
+
"product_autogenerated_services_list/**",
|
|
18
|
+
];
|
|
19
|
+
export function isTdkGeneratedPath(path) {
|
|
20
|
+
const normalized = normalize(path).split(sep).join("/");
|
|
21
|
+
if (isAbsolute(path) || normalized === ".." || normalized.startsWith("../"))
|
|
22
|
+
return false;
|
|
23
|
+
if (normalized.split("/").includes(AUTOGENERATED_DIR) ||
|
|
24
|
+
normalized.startsWith(".tdk/.tdk-out/") ||
|
|
25
|
+
normalized === "shared-platform-engineering/docker-templates" ||
|
|
26
|
+
normalized.startsWith("shared-platform-engineering/docker-templates/") ||
|
|
27
|
+
normalized.startsWith("product_autogenerated_resource_manifests/") ||
|
|
28
|
+
normalized.startsWith("product_autogenerated_services_list/")) {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
return (normalized === "Tiltfile" ||
|
|
32
|
+
/^TILT_[^/]+\.star$/.test(normalized) ||
|
|
33
|
+
normalized === TILTIGNORE_FILE ||
|
|
34
|
+
normalized === PACKAGE_JSON ||
|
|
35
|
+
normalized === "services/platform/database-management/docker-compose.yml");
|
|
36
|
+
}
|
|
37
|
+
export function assertTdkGeneratedPath(projectRoot, path) {
|
|
38
|
+
const absolute = isAbsolute(path) ? normalize(path) : normalize(`${projectRoot}/${path}`);
|
|
39
|
+
const projectRelative = relative(projectRoot, absolute).split(sep).join("/");
|
|
40
|
+
if (!isTdkGeneratedPath(projectRelative)) {
|
|
41
|
+
throw new Error(`Refusing to write outside Flisk's generated-file allowlist: ${projectRelative}`);
|
|
42
|
+
}
|
|
43
|
+
let cursor = projectRoot;
|
|
44
|
+
for (const segment of projectRelative.split("/")) {
|
|
45
|
+
cursor = normalize(`${cursor}/${segment}`);
|
|
46
|
+
if (existsSync(cursor) && lstatSync(cursor).isSymbolicLink()) {
|
|
47
|
+
throw new Error(`Refusing to write through a symbolic link in generated path: ${projectRelative}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return absolute;
|
|
51
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `flisk help <name>` should fail for a name that is not a command or alias, like `flisk <name>` does.
|
|
3
|
+
* Returns the unknown name, or undefined when the arguments are not `help <name>` or the name is known.
|
|
4
|
+
*/
|
|
5
|
+
export function unknownHelpTarget(args, commands) {
|
|
6
|
+
const [first, target] = args;
|
|
7
|
+
if (first !== "help" || !target || target.startsWith("-"))
|
|
8
|
+
return undefined;
|
|
9
|
+
const known = commands.some((command) => command.name() === target || command.aliases().includes(target));
|
|
10
|
+
return known ? undefined : target;
|
|
11
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// Copyright (c) 2026 Flisk Landscape contributors
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
import { execFileSync } from "node:child_process";
|
|
4
|
+
import { existsSync, mkdirSync, readFileSync, realpathSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { join, resolve, sep } from "node:path";
|
|
6
|
+
import { PROJECT_JSON } from "./constants.js";
|
|
7
|
+
import { createHostPortPlan, isHostPortAvailable, PREFERRED_HOST_PORTS, } from "./host-port-plan.js";
|
|
8
|
+
const CONFIG_PATH = join(".tdk", ".tdk-out", "host-ports.json");
|
|
9
|
+
export function isDockerPortOwnedByProject(dockerPs, projectPrefix, port) {
|
|
10
|
+
return dockerPs.split("\n").some((line) => {
|
|
11
|
+
const [name = "", ports = ""] = line.split("|");
|
|
12
|
+
const containerName = name.toLowerCase();
|
|
13
|
+
const prefix = projectPrefix.toLowerCase();
|
|
14
|
+
return ((containerName.startsWith(`${prefix}_`) || containerName.startsWith(`${prefix}-`)) &&
|
|
15
|
+
ports.includes(`:${port}->`));
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
export function readSavedHostPortPlan(projectRoot) {
|
|
19
|
+
const path = join(projectRoot, CONFIG_PATH);
|
|
20
|
+
if (!existsSync(path))
|
|
21
|
+
return null;
|
|
22
|
+
try {
|
|
23
|
+
const value = JSON.parse(readFileSync(path, "utf-8"));
|
|
24
|
+
if (!value || typeof value !== "object")
|
|
25
|
+
return null;
|
|
26
|
+
const plan = value;
|
|
27
|
+
if (!Number.isInteger(plan.ingressHttp) ||
|
|
28
|
+
!Number.isInteger(plan.ingressHttps) ||
|
|
29
|
+
!Number.isInteger(plan.postgres) ||
|
|
30
|
+
!plan.requested ||
|
|
31
|
+
!plan.explicit ||
|
|
32
|
+
!plan.reason)
|
|
33
|
+
return null;
|
|
34
|
+
return plan;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export function writeSavedHostPortPlan(projectRoot, plan) {
|
|
41
|
+
const path = join(projectRoot, CONFIG_PATH);
|
|
42
|
+
mkdirSync(join(projectRoot, ".tdk", ".tdk-out"), { recursive: true });
|
|
43
|
+
writeFileSync(path, `${JSON.stringify(plan, null, 2)}\n`, { encoding: "utf-8", mode: 0o600 });
|
|
44
|
+
}
|
|
45
|
+
export function exportHostPortPlan(plan) {
|
|
46
|
+
process.env.TDK_HTTP_PORT = String(plan.ingressHttp);
|
|
47
|
+
process.env.TDK_HTTPS_PORT = String(plan.ingressHttps);
|
|
48
|
+
process.env.TDK_POSTGRES_PORT = String(plan.postgres);
|
|
49
|
+
}
|
|
50
|
+
const COMPOSE_WORKING_DIR_LABEL = "com.docker.compose.project.working_dir";
|
|
51
|
+
/**
|
|
52
|
+
* True when a container whose Compose working directory is inside this project publishes the port. A container-name prefix is not
|
|
53
|
+
* enough: tdk_shop_ also matches the containers of a different project called tdk-shop-extra. Compose records the directory each
|
|
54
|
+
* project was started from, so that identifies the project exactly. Containers without the label never count.
|
|
55
|
+
*/
|
|
56
|
+
export function isPortHeldByProjectContainers(dockerPs, projectRoot, port) {
|
|
57
|
+
const roots = new Set([resolve(projectRoot)]);
|
|
58
|
+
try {
|
|
59
|
+
roots.add(realpathSync(projectRoot));
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// The project root is already known to exist; keep the unresolved path.
|
|
63
|
+
}
|
|
64
|
+
return dockerPs.split("\n").some((line) => {
|
|
65
|
+
const [, ports = "", workingDir = ""] = line.split("|");
|
|
66
|
+
if (!workingDir || !ports.includes(`:${port}->`))
|
|
67
|
+
return false;
|
|
68
|
+
return [...roots].some((root) => workingDir === root || workingDir.startsWith(`${root}${sep}`));
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The HTTP ingress port to probe while a stack is running. getHostPortPlan moves a saved fallback port (8080) back to the
|
|
73
|
+
* preferred one (80) as soon as that is free, which is right for the next `flisk up` but wrong for a running stack: Traefik is still
|
|
74
|
+
* bound to the saved port, so probing the planned one finds nothing and every runtime check is skipped. When this project's own
|
|
75
|
+
* containers hold the saved port, probe that one.
|
|
76
|
+
*/
|
|
77
|
+
export function resolveRuntimeIngressPort(projectRoot, plannedPort, readDockerPs = () => execFileSync("docker", ["ps", "--format", `{{.Names}}|{{.Ports}}|{{.Label "${COMPOSE_WORKING_DIR_LABEL}"}}`], { encoding: "utf-8", timeout: 5000 })) {
|
|
78
|
+
const saved = readSavedHostPortPlan(projectRoot);
|
|
79
|
+
if (!saved || saved.ingressHttp === plannedPort)
|
|
80
|
+
return plannedPort;
|
|
81
|
+
try {
|
|
82
|
+
if (isPortHeldByProjectContainers(readDockerPs(), projectRoot, saved.ingressHttp)) {
|
|
83
|
+
return saved.ingressHttp;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// Docker unavailable: keep the planned port.
|
|
88
|
+
}
|
|
89
|
+
return plannedPort;
|
|
90
|
+
}
|
|
91
|
+
/** Reuse this project's last selection when its ports are still free or held by its own containers. */
|
|
92
|
+
export async function getHostPortPlan(projectRoot, options = {}) {
|
|
93
|
+
const saved = readSavedHostPortPlan(projectRoot);
|
|
94
|
+
const hasOverride = ["TDK_HTTP_PORT", "TDK_HTTPS_PORT", "TDK_POSTGRES_PORT"].some((key) => process.env[key]);
|
|
95
|
+
// Dry-run must not call Docker. A saved plan is the best representation of
|
|
96
|
+
// the current landscape in that mode; explicit overrides are still planned.
|
|
97
|
+
if (options.inspectDocker === false && saved && !hasOverride)
|
|
98
|
+
return saved;
|
|
99
|
+
let prefix = "";
|
|
100
|
+
try {
|
|
101
|
+
const project = JSON.parse(readFileSync(join(projectRoot, ".tdk", PROJECT_JSON), "utf-8"));
|
|
102
|
+
prefix = String(project?.project?.name ?? "tdk-project")
|
|
103
|
+
.replace(/-/g, "_")
|
|
104
|
+
.toLowerCase();
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
prefix = "tdk_project";
|
|
108
|
+
}
|
|
109
|
+
let dockerPs = "";
|
|
110
|
+
if (options.inspectDocker !== false) {
|
|
111
|
+
try {
|
|
112
|
+
dockerPs = execFileSync("docker", ["ps", "--format", "{{.Names}}|{{.Ports}}"], {
|
|
113
|
+
encoding: "utf-8",
|
|
114
|
+
timeout: 5000,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
// If Docker is down, reuse only when host binds are still available.
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const ownedByProject = (port) => isDockerPortOwnedByProject(dockerPs, prefix, port);
|
|
122
|
+
const isPortFree = options.isPortFree ?? isHostPortAvailable;
|
|
123
|
+
const isAvailable = async (port) => (await isPortFree(port)) || ownedByProject(port);
|
|
124
|
+
if (saved && !hasOverride) {
|
|
125
|
+
for (const port of [saved.ingressHttp, saved.ingressHttps, saved.postgres]) {
|
|
126
|
+
if (!(await isAvailable(port)))
|
|
127
|
+
return createHostPortPlan({ isAvailable });
|
|
128
|
+
}
|
|
129
|
+
// A plan saved on a fallback port (8080) moves back to the preferred one (80)
|
|
130
|
+
// once that is free, so URLs lose the port without deleting host-ports.json.
|
|
131
|
+
for (const [key, preferred] of Object.entries(PREFERRED_HOST_PORTS)) {
|
|
132
|
+
const savedPort = saved[key];
|
|
133
|
+
if (preferred !== undefined && savedPort !== preferred && (await isAvailable(preferred))) {
|
|
134
|
+
return createHostPortPlan({ isAvailable });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return saved;
|
|
138
|
+
}
|
|
139
|
+
return createHostPortPlan({ isAvailable });
|
|
140
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// Copyright (c) 2026 Flisk Landscape contributors
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
import { createServer } from "node:net";
|
|
4
|
+
import { hasLocalListener } from "./port-listener-probe.js";
|
|
5
|
+
export const DEFAULT_HOST_PORT_RANGES = {
|
|
6
|
+
ingressHttp: { start: 8080, end: 8180 },
|
|
7
|
+
ingressHttps: { start: 8443, end: 8543 },
|
|
8
|
+
postgres: { start: 15432, end: 15532 },
|
|
9
|
+
};
|
|
10
|
+
const ENV_KEYS = {
|
|
11
|
+
ingressHttp: "TDK_HTTP_PORT",
|
|
12
|
+
ingressHttps: "TDK_HTTPS_PORT",
|
|
13
|
+
postgres: "TDK_POSTGRES_PORT",
|
|
14
|
+
};
|
|
15
|
+
const REQUESTED_PORTS = { ingressHttp: 80, ingressHttps: 443, postgres: 5432 };
|
|
16
|
+
// Tried before the fallback range so routed URLs need no port. Postgres keeps
|
|
17
|
+
// its range: a local Postgres on 5432 is common, and no URL carries that port.
|
|
18
|
+
export const PREFERRED_HOST_PORTS = {
|
|
19
|
+
ingressHttp: REQUESTED_PORTS.ingressHttp,
|
|
20
|
+
ingressHttps: REQUESTED_PORTS.ingressHttps,
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Whether a failed bind on 127.0.0.1 means the port is still free for Docker. Only macOS
|
|
24
|
+
* refuses a non-root bind below 1024 while Docker Desktop publishes it through a privileged
|
|
25
|
+
* helper. Callers check for a listener first, so a bind error here is never another process.
|
|
26
|
+
*/
|
|
27
|
+
export function isPrivilegedBindFree(errorCode, platform) {
|
|
28
|
+
return errorCode === "EACCES" && platform === "darwin";
|
|
29
|
+
}
|
|
30
|
+
export async function isHostPortAvailable(port) {
|
|
31
|
+
if (await hasLocalListener(port))
|
|
32
|
+
return false;
|
|
33
|
+
return new Promise((resolve) => {
|
|
34
|
+
const server = createServer();
|
|
35
|
+
server.once("error", (error) => {
|
|
36
|
+
resolve(isPrivilegedBindFree(error.code, process.platform));
|
|
37
|
+
});
|
|
38
|
+
// Docker Desktop and Windows publish these host ports through IPv4 loopback.
|
|
39
|
+
// Match that target instead of probing wildcard binds that may differ by OS.
|
|
40
|
+
server.listen(port, "127.0.0.1", () => server.close(() => resolve(true)));
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
function parsePort(name, value) {
|
|
44
|
+
const port = Number(value);
|
|
45
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
46
|
+
throw new Error(`${name} must be an integer between 1 and 65535 (received ${JSON.stringify(value)})`);
|
|
47
|
+
}
|
|
48
|
+
return port;
|
|
49
|
+
}
|
|
50
|
+
export async function createHostPortPlan(options = {}) {
|
|
51
|
+
const env = options.env ?? process.env;
|
|
52
|
+
const isAvailable = options.isAvailable ?? isHostPortAvailable;
|
|
53
|
+
const explicitFlags = { ingressHttp: false, ingressHttps: false, postgres: false };
|
|
54
|
+
const reasons = { ingressHttp: "", ingressHttps: "", postgres: "" };
|
|
55
|
+
const selected = { explicit: explicitFlags };
|
|
56
|
+
const reserved = new Set();
|
|
57
|
+
for (const key of ["ingressHttp", "ingressHttps", "postgres"]) {
|
|
58
|
+
const envValue = env[ENV_KEYS[key]];
|
|
59
|
+
const explicit = envValue !== undefined && envValue.trim() !== "";
|
|
60
|
+
const range = options.ranges?.[key] ?? DEFAULT_HOST_PORT_RANGES[key];
|
|
61
|
+
const preferred = PREFERRED_HOST_PORTS[key];
|
|
62
|
+
const candidates = explicit
|
|
63
|
+
? [parsePort(ENV_KEYS[key], envValue)]
|
|
64
|
+
: [
|
|
65
|
+
...(preferred === undefined ? [] : [preferred]),
|
|
66
|
+
...Array.from({ length: range.end - range.start + 1 }, (_, index) => range.start + index),
|
|
67
|
+
];
|
|
68
|
+
let chosen;
|
|
69
|
+
for (const port of candidates) {
|
|
70
|
+
if (reserved.has(port))
|
|
71
|
+
continue;
|
|
72
|
+
if (await isAvailable(port)) {
|
|
73
|
+
chosen = port;
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
if (explicit) {
|
|
77
|
+
throw new Error(`${ENV_KEYS[key]}=${port} is already in use. Choose a free host port and retry.`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (chosen === undefined) {
|
|
81
|
+
throw new Error(`No available host port in ${range.start}-${range.end} for ${key}. Set ${ENV_KEYS[key]} to a free port and retry.`);
|
|
82
|
+
}
|
|
83
|
+
reserved.add(chosen);
|
|
84
|
+
selected[key] = chosen;
|
|
85
|
+
explicitFlags[key] = explicit;
|
|
86
|
+
reasons[key] = explicit
|
|
87
|
+
? "explicit override"
|
|
88
|
+
: chosen === preferred
|
|
89
|
+
? "requested port is free"
|
|
90
|
+
: `selected from fallback range ${range.start}-${range.end}`;
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
...selected,
|
|
94
|
+
requested: { ...REQUESTED_PORTS },
|
|
95
|
+
reason: reasons,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
export function formatHostPortPlan(plan) {
|
|
99
|
+
return `Host ports: HTTP ${plan.ingressHttp}, HTTPS ${plan.ingressHttps}, Postgres ${plan.postgres}`;
|
|
100
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Copyright (c) 2026 Flisk Landscape contributors
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
import { writeSync } from "node:fs";
|
|
4
|
+
import { createMachineEnvelope } from "./machine-output.js";
|
|
5
|
+
/**
|
|
6
|
+
* Returns an emitter that writes at most one `{schemaVersion, data, errors}` object to stdout. If the process
|
|
7
|
+
* exits before anything was emitted (a preflight `process.exit`, Tilt ending early), a failure object is written
|
|
8
|
+
* so a JSON consumer never sees an empty stdout. Uses writeSync because `process.exit` does not flush async writes.
|
|
9
|
+
*/
|
|
10
|
+
export function createJsonEmitter(failureCode, command) {
|
|
11
|
+
let emitted = false;
|
|
12
|
+
const emit = (data, errors = []) => {
|
|
13
|
+
if (emitted)
|
|
14
|
+
return;
|
|
15
|
+
emitted = true;
|
|
16
|
+
writeSync(1, `${JSON.stringify(createMachineEnvelope(data, errors))}\n`);
|
|
17
|
+
};
|
|
18
|
+
process.once("exit", (code) => {
|
|
19
|
+
emit({ ok: false }, [
|
|
20
|
+
{
|
|
21
|
+
code: failureCode,
|
|
22
|
+
message: code === 0
|
|
23
|
+
? `${command} exited before reporting a result`
|
|
24
|
+
: `${command} exited with code ${code}`,
|
|
25
|
+
},
|
|
26
|
+
]);
|
|
27
|
+
});
|
|
28
|
+
return emit;
|
|
29
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Copyright (c) 2026 Flisk Landscape contributors
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
import { TdkError } from "./errors.js";
|
|
4
|
+
export const SCHEMA_VERSION = 1;
|
|
5
|
+
export function createMachineEnvelope(data, errors = []) {
|
|
6
|
+
return { schemaVersion: SCHEMA_VERSION, data, errors };
|
|
7
|
+
}
|
|
8
|
+
export function toMachineError(error) {
|
|
9
|
+
if (error instanceof TdkError) {
|
|
10
|
+
return {
|
|
11
|
+
error: {
|
|
12
|
+
code: "COMMAND_FAILED",
|
|
13
|
+
message: error.message,
|
|
14
|
+
...(error.suggestions.length > 0 ? { suggestions: error.suggestions } : {}),
|
|
15
|
+
},
|
|
16
|
+
exitCode: error.exitCode,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
error: {
|
|
21
|
+
code: "INTERNAL",
|
|
22
|
+
message: error instanceof Error ? error.message : String(error),
|
|
23
|
+
},
|
|
24
|
+
exitCode: 2,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/** Keep machine stdout parseable when a command fails before it has data to return. */
|
|
28
|
+
export function writeMachineError(error) {
|
|
29
|
+
const result = toMachineError(error);
|
|
30
|
+
console.log(JSON.stringify(createMachineEnvelope(null, [result.error])));
|
|
31
|
+
console.error(result.error.message);
|
|
32
|
+
process.exit(result.exitCode);
|
|
33
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Copyright (c) 2026 Flisk Landscape contributors
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import { dirname, join, resolve } from "node:path";
|
|
5
|
+
import { cwd } from "node:process";
|
|
6
|
+
import packageJson from "../../package.json" with { type: "json" };
|
|
7
|
+
import { PROJECT_JSON } from "./constants.js";
|
|
8
|
+
export function findProjectRoot(startDir = cwd()) {
|
|
9
|
+
let currentDir = resolve(startDir);
|
|
10
|
+
const root = resolve("/");
|
|
11
|
+
while (currentDir !== root) {
|
|
12
|
+
if (existsSync(join(currentDir, ".tdk", PROJECT_JSON))) {
|
|
13
|
+
return currentDir;
|
|
14
|
+
}
|
|
15
|
+
const parentDir = dirname(currentDir);
|
|
16
|
+
if (parentDir === currentDir) {
|
|
17
|
+
break;
|
|
18
|
+
}
|
|
19
|
+
currentDir = parentDir;
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
let packageCache = null;
|
|
24
|
+
function getPackageInfo() {
|
|
25
|
+
if (packageCache) {
|
|
26
|
+
return packageCache;
|
|
27
|
+
}
|
|
28
|
+
// Imported JSON is bundled by Bun compile, unlike a runtime fs lookup from $bunfs.
|
|
29
|
+
const parsed = packageJson;
|
|
30
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
31
|
+
throw new Error("Invalid package.json: expected object");
|
|
32
|
+
}
|
|
33
|
+
const packageInfo = parsed;
|
|
34
|
+
packageCache = {
|
|
35
|
+
name: String(packageInfo.name ?? "@flisk-sh/flisk"),
|
|
36
|
+
version: String(packageInfo.version ?? "0.0.0"),
|
|
37
|
+
fullPackage: packageInfo,
|
|
38
|
+
};
|
|
39
|
+
return packageCache;
|
|
40
|
+
}
|
|
41
|
+
export function getPackageVersion() {
|
|
42
|
+
return getPackageInfo().version;
|
|
43
|
+
}
|