@interf/compiler 0.22.0 → 0.33.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/README.md +268 -0
- package/dist/bin-mcp.d.ts +2 -0
- package/dist/bin-mcp.js +63 -0
- package/dist/bin-runtime.d.ts +2 -0
- package/dist/bin-runtime.js +111 -0
- package/dist/cli/commands/agents.js +4 -4
- package/dist/cli/commands/auth.d.ts +20 -0
- package/dist/cli/commands/auth.js +161 -0
- package/dist/cli/commands/benchmark.d.ts +10 -0
- package/dist/cli/commands/benchmark.js +88 -0
- package/dist/cli/commands/build-plan.js +95 -103
- package/dist/cli/commands/build.d.ts +1 -1
- package/dist/cli/commands/build.js +16 -15
- package/dist/cli/commands/doctor.js +3 -3
- package/dist/cli/commands/graphs.d.ts +2 -0
- package/dist/cli/commands/graphs.js +199 -0
- package/dist/cli/commands/login.js +4 -6
- package/dist/cli/commands/logout.js +1 -1
- package/dist/cli/commands/mcp.d.ts +3 -2
- package/dist/cli/commands/mcp.js +721 -204
- package/dist/cli/commands/project.d.ts +2 -0
- package/dist/cli/commands/project.js +202 -0
- package/dist/cli/commands/reset.d.ts +1 -1
- package/dist/cli/commands/reset.js +10 -10
- package/dist/cli/commands/runs.js +52 -26
- package/dist/cli/commands/runtime.d.ts +24 -0
- package/dist/cli/commands/runtime.js +373 -0
- package/dist/cli/commands/status.d.ts +1 -0
- package/dist/cli/commands/status.js +33 -22
- package/dist/cli/commands/traces.d.ts +2 -0
- package/dist/cli/commands/traces.js +125 -0
- package/dist/cli/commands/wizard.js +155 -150
- package/dist/cli/index.d.ts +7 -4
- package/dist/cli/index.js +13 -7
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/packages/build-plans/authoring/brief.d.ts +517 -0
- package/dist/packages/build-plans/authoring/brief.js +89 -0
- package/dist/packages/build-plans/authoring/build-plan-authoring.d.ts +10 -10
- package/dist/packages/build-plans/authoring/build-plan-authoring.js +66 -26
- package/dist/packages/build-plans/authoring/build-plan-edit-session.d.ts +1 -1
- package/dist/packages/build-plans/authoring/build-plan-edit-session.js +8 -2
- package/dist/packages/build-plans/authoring/build-plan-improvement.d.ts +9 -6
- package/dist/packages/build-plans/authoring/build-plan-improvement.js +39 -42
- package/dist/packages/build-plans/authoring/lib/build-plan-edit-utils.js +7 -7
- package/dist/packages/build-plans/build-plan-resolution.d.ts +1 -1
- package/dist/packages/build-plans/build-plan-resolution.js +3 -3
- package/dist/packages/build-plans/index.d.ts +1 -1
- package/dist/packages/build-plans/index.js +1 -1
- package/dist/packages/build-plans/package/build-plan-definitions.d.ts +14 -12
- package/dist/packages/build-plans/package/build-plan-definitions.js +40 -39
- package/dist/packages/build-plans/package/build-plan-helpers.d.ts +3 -2
- package/dist/packages/build-plans/package/build-plan-helpers.js +27 -13
- package/dist/packages/build-plans/package/build-plan-review-paths.d.ts +5 -5
- package/dist/packages/build-plans/package/build-plan-review-paths.js +15 -15
- package/dist/packages/build-plans/package/build-plan-stage-runner.d.ts +4 -4
- package/dist/packages/build-plans/package/build-plan-stage-runner.js +22 -11
- package/dist/packages/build-plans/package/builtin-build-plan.d.ts +5 -6
- package/dist/packages/build-plans/package/builtin-build-plan.js +7 -8
- package/dist/packages/build-plans/package/context-interface.d.ts +11 -9
- package/dist/packages/build-plans/package/context-interface.js +14 -33
- package/dist/packages/build-plans/package/interf-build-plan-package.d.ts +6 -17
- package/dist/packages/build-plans/package/interf-build-plan-package.js +56 -52
- package/dist/packages/build-plans/package/local-build-plans.d.ts +12 -10
- package/dist/packages/build-plans/package/local-build-plans.js +56 -31
- package/dist/packages/build-plans/package/user-build-plans.js +1 -1
- package/dist/packages/contracts/index.d.ts +4 -2
- package/dist/packages/contracts/index.js +2 -1
- package/dist/packages/contracts/lib/project-paths.d.ts +137 -0
- package/dist/packages/contracts/lib/project-paths.js +211 -0
- package/dist/packages/contracts/lib/project-schema.d.ts +160 -0
- package/dist/packages/contracts/lib/project-schema.js +113 -0
- package/dist/packages/contracts/lib/schema.d.ts +739 -80
- package/dist/packages/contracts/lib/schema.js +410 -75
- package/dist/packages/contracts/utils/parse.js +67 -0
- package/dist/packages/projects/index.d.ts +6 -0
- package/dist/packages/{project → projects}/index.js +0 -3
- package/dist/packages/{project → projects}/interf-detect.d.ts +12 -12
- package/dist/packages/{project → projects}/interf-detect.js +56 -50
- package/dist/packages/projects/interf.d.ts +2 -0
- package/dist/packages/projects/interf.js +1 -0
- package/dist/packages/projects/lib/schema.d.ts +79 -0
- package/dist/packages/projects/lib/schema.js +89 -0
- package/dist/packages/projects/source-config.d.ts +58 -0
- package/dist/packages/projects/source-config.js +352 -0
- package/dist/packages/projects/source-folders.d.ts +11 -0
- package/dist/packages/{project → projects}/source-folders.js +26 -26
- package/dist/packages/{engine → runtime}/action-planner.d.ts +1 -1
- package/dist/packages/{engine → runtime}/action-planner.js +20 -22
- package/dist/packages/runtime/action-values.d.ts +1 -0
- package/dist/packages/runtime/action-values.js +1 -0
- package/dist/packages/runtime/actions/errors.d.ts +2 -0
- package/dist/packages/runtime/actions/errors.js +12 -0
- package/dist/packages/runtime/actions/fields.d.ts +82 -0
- package/dist/packages/runtime/actions/form-builders.d.ts +14 -0
- package/dist/packages/runtime/actions/form-builders.js +619 -0
- package/dist/packages/runtime/actions/form-validators.d.ts +8 -0
- package/dist/packages/runtime/actions/form-validators.js +128 -0
- package/dist/packages/runtime/actions/helpers.d.ts +11 -0
- package/dist/packages/runtime/actions/helpers.js +80 -0
- package/dist/packages/runtime/actions/index.d.ts +8 -0
- package/dist/packages/runtime/actions/index.js +11 -0
- package/dist/packages/runtime/actions/registry.d.ts +64 -0
- package/dist/packages/runtime/actions/registry.js +62 -0
- package/dist/packages/runtime/actions/requests.d.ts +45 -0
- package/dist/packages/runtime/actions/requests.js +158 -0
- package/dist/packages/runtime/actions/schemas.d.ts +154 -0
- package/dist/packages/runtime/actions/schemas.js +36 -0
- package/dist/packages/runtime/agent-handoff.d.ts +11 -0
- package/dist/packages/runtime/agent-handoff.js +101 -0
- package/dist/packages/{engine → runtime}/agents/index.d.ts +1 -2
- package/dist/packages/{engine → runtime}/agents/index.js +1 -2
- package/dist/packages/runtime/agents/lib/args.d.ts +14 -0
- package/dist/packages/runtime/agents/lib/args.js +24 -0
- package/dist/packages/{engine → runtime}/agents/lib/constants.d.ts +4 -1
- package/dist/packages/runtime/agents/lib/constants.js +13 -0
- package/dist/packages/runtime/agents/lib/context-graph-bootstrap.d.ts +3 -0
- package/dist/packages/{engine/agents/lib/verifiable-context-bootstrap.js → runtime/agents/lib/context-graph-bootstrap.js} +5 -6
- package/dist/packages/{engine → runtime}/agents/lib/detection.d.ts +5 -0
- package/dist/packages/{engine → runtime}/agents/lib/detection.js +16 -7
- package/dist/packages/{engine → runtime}/agents/lib/execution-profile.js +8 -14
- package/dist/packages/{engine → runtime}/agents/lib/execution.js +14 -4
- package/dist/packages/{engine → runtime}/agents/lib/preflight.js +15 -13
- package/dist/packages/{engine → runtime}/agents/lib/render.js +4 -4
- package/dist/packages/runtime/agents/lib/shell-fs.d.ts +18 -0
- package/dist/packages/runtime/agents/lib/shell-fs.js +190 -0
- package/dist/packages/runtime/agents/lib/shell-paths.d.ts +16 -0
- package/dist/packages/runtime/agents/lib/shell-paths.js +63 -0
- package/dist/packages/runtime/agents/lib/shell-projection.d.ts +25 -0
- package/dist/packages/runtime/agents/lib/shell-projection.js +314 -0
- package/dist/packages/runtime/agents/lib/shell-templates.d.ts +30 -0
- package/dist/packages/runtime/agents/lib/shell-templates.js +452 -0
- package/dist/packages/runtime/agents/lib/shell-workspace.d.ts +17 -0
- package/dist/packages/runtime/agents/lib/shell-workspace.js +70 -0
- package/dist/packages/runtime/agents/lib/shells.d.ts +63 -0
- package/dist/packages/runtime/agents/lib/shells.js +383 -0
- package/dist/packages/{engine → runtime}/agents/lib/user-config.d.ts +8 -2
- package/dist/packages/{engine → runtime}/agents/lib/user-config.js +8 -2
- package/dist/packages/runtime/agents/providers/claude-code.d.ts +13 -0
- package/dist/packages/runtime/agents/providers/claude-code.js +45 -0
- package/dist/packages/runtime/agents/providers/codex.d.ts +17 -0
- package/dist/packages/runtime/agents/providers/codex.js +64 -0
- package/dist/packages/runtime/agents/providers/cursor.d.ts +9 -0
- package/dist/packages/runtime/agents/providers/cursor.js +24 -0
- package/dist/packages/runtime/agents/providers/index.d.ts +9 -0
- package/dist/packages/runtime/agents/providers/index.js +31 -0
- package/dist/packages/runtime/agents/providers/types.d.ts +50 -0
- package/dist/packages/{engine → runtime}/agents/registry.d.ts +13 -2
- package/dist/packages/{engine → runtime}/agents/registry.js +48 -10
- package/dist/packages/{engine → runtime}/agents/role-executors.d.ts +1 -1
- package/dist/packages/{engine → runtime}/agents/role-executors.js +7 -6
- package/dist/packages/{engine → runtime}/agents/role-router.js +7 -5
- package/dist/packages/runtime/auth/account-context.d.ts +52 -0
- package/dist/packages/runtime/auth/account-context.js +68 -0
- package/dist/packages/runtime/auth/auth-flow.d.ts +73 -0
- package/dist/packages/runtime/auth/auth-flow.js +189 -0
- package/dist/packages/runtime/auth/jwt-validator.d.ts +58 -0
- package/dist/packages/runtime/auth/jwt-validator.js +86 -0
- package/dist/packages/runtime/auth/keychain.d.ts +35 -0
- package/dist/packages/runtime/auth/keychain.js +85 -0
- package/dist/packages/runtime/auth/session-store.d.ts +38 -0
- package/dist/packages/runtime/auth/session-store.js +88 -0
- package/dist/packages/runtime/auth/workos-client.d.ts +58 -0
- package/dist/packages/runtime/auth/workos-client.js +87 -0
- package/dist/packages/runtime/benchmark-question-draft.d.ts +20 -0
- package/dist/packages/runtime/benchmark-question-draft.js +124 -0
- package/dist/packages/runtime/build/artifact-counts.d.ts +1 -0
- package/dist/packages/{engine → runtime}/build/artifact-counts.js +5 -9
- package/dist/packages/{engine → runtime}/build/artifact-status.d.ts +5 -5
- package/dist/packages/{engine → runtime}/build/artifact-status.js +25 -23
- package/dist/packages/runtime/build/atomic-fs.d.ts +3 -0
- package/dist/packages/runtime/build/atomic-fs.js +95 -0
- package/dist/packages/runtime/build/billing-events.d.ts +78 -0
- package/dist/packages/{engine → runtime}/build/billing-events.js +17 -19
- package/dist/packages/runtime/build/build-evidence.d.ts +15 -0
- package/dist/packages/runtime/build/build-evidence.js +173 -0
- package/dist/packages/{engine → runtime}/build/build-pipeline.d.ts +12 -8
- package/dist/packages/runtime/build/build-pipeline.js +304 -0
- package/dist/packages/{engine → runtime}/build/build-plan-primitives.d.ts +1 -1
- package/dist/packages/{engine → runtime}/build/build-plan-primitives.js +0 -1
- package/dist/packages/runtime/build/build-plan-runs.d.ts +14 -0
- package/dist/packages/runtime/build/build-plan-runs.js +31 -0
- package/dist/packages/runtime/build/build-stage-plan.d.ts +16 -0
- package/dist/packages/{engine → runtime}/build/build-stage-plan.js +28 -29
- package/dist/packages/{engine → runtime}/build/build-stage-runner.d.ts +2 -1
- package/dist/packages/runtime/build/build-stage-runner.js +165 -0
- package/dist/packages/{engine → runtime}/build/build-target.d.ts +4 -4
- package/dist/packages/runtime/build/build-target.js +16 -0
- package/dist/packages/{engine → runtime}/build/check-evaluator.d.ts +14 -16
- package/dist/packages/runtime/build/check-evaluator.js +575 -0
- package/dist/packages/runtime/build/context-graph-paths.d.ts +51 -0
- package/dist/packages/runtime/build/context-graph-paths.js +133 -0
- package/dist/packages/runtime/build/context-graph-schema.d.ts +19 -0
- package/dist/packages/runtime/build/context-graph-schema.js +39 -0
- package/dist/packages/{engine → runtime}/build/discovery.d.ts +2 -2
- package/dist/packages/{engine → runtime}/build/discovery.js +4 -4
- package/dist/packages/{engine → runtime}/build/index.d.ts +5 -3
- package/dist/packages/{engine → runtime}/build/index.js +5 -3
- package/dist/packages/{engine → runtime}/build/lib/schema.d.ts +221 -88
- package/dist/packages/{engine → runtime}/build/lib/schema.js +61 -41
- package/dist/packages/runtime/build/reset.d.ts +2 -0
- package/dist/packages/runtime/build/reset.js +62 -0
- package/dist/packages/{engine → runtime}/build/runtime-contracts.js +5 -5
- package/dist/packages/runtime/build/runtime-inventory.d.ts +7 -0
- package/dist/packages/{engine → runtime}/build/runtime-inventory.js +3 -3
- package/dist/packages/{engine → runtime}/build/runtime-paths.js +6 -6
- package/dist/packages/{engine → runtime}/build/runtime-prompt.js +7 -6
- package/dist/packages/{engine → runtime}/build/runtime-reconcile.d.ts +1 -1
- package/dist/packages/{engine → runtime}/build/runtime-reconcile.js +25 -21
- package/dist/packages/runtime/build/runtime-runs.d.ts +10 -0
- package/dist/packages/runtime/build/runtime-runs.js +265 -0
- package/dist/packages/{engine → runtime}/build/runtime-types.d.ts +6 -6
- package/dist/packages/runtime/build/runtime-types.js +1 -0
- package/dist/packages/runtime/build/runtime.d.ts +6 -0
- package/dist/packages/runtime/build/runtime.js +5 -0
- package/dist/packages/runtime/build/source-files.d.ts +58 -0
- package/dist/packages/runtime/build/source-files.js +184 -0
- package/dist/packages/runtime/build/source-inventory.d.ts +27 -0
- package/dist/packages/runtime/build/source-inventory.js +273 -0
- package/dist/packages/runtime/build/source-manifest.d.ts +52 -0
- package/dist/packages/runtime/build/source-manifest.js +192 -0
- package/dist/packages/runtime/build/stage-evidence.d.ts +22 -0
- package/dist/packages/runtime/build/stage-evidence.js +317 -0
- package/dist/packages/runtime/build/stage-reuse.d.ts +11 -0
- package/dist/packages/runtime/build/stage-reuse.js +142 -0
- package/dist/packages/runtime/build/state-artifacts.d.ts +9 -0
- package/dist/packages/runtime/build/state-artifacts.js +14 -0
- package/dist/packages/runtime/build/state-health.d.ts +4 -0
- package/dist/packages/{engine → runtime}/build/state-health.js +21 -26
- package/dist/packages/runtime/build/state-io.d.ts +12 -0
- package/dist/packages/runtime/build/state-io.js +119 -0
- package/dist/packages/{engine → runtime}/build/state-paths.d.ts +3 -1
- package/dist/packages/runtime/build/state-paths.js +22 -0
- package/dist/packages/runtime/build/state-view.d.ts +5 -0
- package/dist/packages/{engine → runtime}/build/state-view.js +60 -33
- package/dist/packages/runtime/build/state.d.ts +7 -0
- package/dist/packages/runtime/build/state.js +12 -0
- package/dist/packages/runtime/build/traces.d.ts +30 -0
- package/dist/packages/runtime/build/traces.js +133 -0
- package/dist/packages/{engine/build/validate-verifiable-context.d.ts → runtime/build/validate-context-graph.d.ts} +6 -6
- package/dist/packages/{engine/build/validate-verifiable-context.js → runtime/build/validate-context-graph.js} +48 -35
- package/dist/packages/{engine → runtime}/build/validate.d.ts +5 -5
- package/dist/packages/{engine → runtime}/build/validate.js +26 -26
- package/dist/packages/{engine → runtime}/client.d.ts +15 -15
- package/dist/packages/{engine → runtime}/client.js +41 -24
- package/dist/packages/{engine → runtime}/connection-config.d.ts +3 -2
- package/dist/packages/{engine → runtime}/connection-config.js +9 -8
- package/dist/packages/runtime/context-checks.d.ts +10 -0
- package/dist/packages/runtime/context-checks.js +114 -0
- package/dist/packages/runtime/context-graph-scaffold.d.ts +9 -0
- package/dist/packages/runtime/context-graph-scaffold.js +134 -0
- package/dist/packages/runtime/entitlement-guard.d.ts +43 -0
- package/dist/packages/runtime/entitlement-guard.js +70 -0
- package/dist/packages/{engine → runtime}/execution/index.d.ts +2 -2
- package/dist/packages/{engine → runtime}/execution/index.js +1 -1
- package/dist/packages/{engine → runtime}/execution/lib/schema.d.ts +270 -192
- package/dist/packages/{engine → runtime}/execution/lib/schema.js +35 -32
- package/dist/packages/runtime/index.d.ts +29 -0
- package/dist/packages/runtime/index.js +21 -0
- package/dist/packages/runtime/instance-paths.d.ts +30 -0
- package/dist/packages/runtime/instance-paths.js +29 -0
- package/dist/packages/{engine → runtime}/native-run-handlers.d.ts +4 -4
- package/dist/packages/{engine → runtime}/native-run-handlers.js +184 -152
- package/dist/packages/runtime/plan-artifact-contract.d.ts +17 -0
- package/dist/packages/runtime/plan-artifact-contract.js +42 -0
- package/dist/packages/runtime/project-entries.d.ts +11 -0
- package/dist/packages/runtime/project-entries.js +49 -0
- package/dist/packages/runtime/project-source-state.d.ts +26 -0
- package/dist/packages/runtime/project-source-state.js +53 -0
- package/dist/packages/runtime/project-store.d.ts +85 -0
- package/dist/packages/runtime/project-store.js +168 -0
- package/dist/packages/runtime/requested-artifacts.d.ts +7 -0
- package/dist/packages/{engine → runtime}/requested-artifacts.js +23 -1
- package/dist/packages/{engine → runtime}/run-observability.d.ts +2 -1
- package/dist/packages/{engine → runtime}/run-observability.js +166 -84
- package/dist/packages/runtime/runtime-action-proposals.d.ts +7 -0
- package/dist/packages/runtime/runtime-action-proposals.js +542 -0
- package/dist/packages/runtime/runtime-build-plans.d.ts +5 -0
- package/dist/packages/runtime/runtime-build-plans.js +131 -0
- package/dist/packages/runtime/runtime-build-runs.d.ts +47 -0
- package/dist/packages/runtime/runtime-build-runs.js +562 -0
- package/dist/packages/runtime/runtime-caches.d.ts +91 -0
- package/dist/packages/{engine → runtime}/runtime-caches.js +77 -49
- package/dist/packages/{engine → runtime}/runtime-event-applier.d.ts +3 -1
- package/dist/packages/{engine → runtime}/runtime-event-applier.js +53 -17
- package/dist/packages/runtime/runtime-executor.d.ts +22 -0
- package/dist/packages/runtime/runtime-executor.js +131 -0
- package/dist/packages/runtime/runtime-jobs.d.ts +13 -0
- package/dist/packages/runtime/runtime-jobs.js +463 -0
- package/dist/packages/runtime/runtime-observability.d.ts +11 -0
- package/dist/packages/runtime/runtime-observability.js +39 -0
- package/dist/packages/{engine → runtime}/runtime-persistence.d.ts +9 -18
- package/dist/packages/{engine → runtime}/runtime-persistence.js +25 -25
- package/dist/packages/runtime/runtime-project-mutations.d.ts +7 -0
- package/dist/packages/runtime/runtime-project-mutations.js +64 -0
- package/dist/packages/runtime/runtime-project-reads.d.ts +15 -0
- package/dist/packages/runtime/runtime-project-reads.js +381 -0
- package/dist/packages/runtime/runtime-proposal-helpers.d.ts +22 -0
- package/dist/packages/runtime/runtime-proposal-helpers.js +223 -0
- package/dist/packages/{engine → runtime}/runtime-resource-builders.d.ts +21 -16
- package/dist/packages/{engine → runtime}/runtime-resource-builders.js +48 -38
- package/dist/packages/runtime/runtime-verify-runs.d.ts +84 -0
- package/dist/packages/runtime/runtime-verify-runs.js +295 -0
- package/dist/packages/runtime/runtime.d.ts +1165 -0
- package/dist/packages/runtime/runtime.js +417 -0
- package/dist/packages/runtime/schemas/actions.d.ts +1182 -0
- package/dist/packages/runtime/schemas/actions.js +117 -0
- package/dist/packages/runtime/schemas/agents.d.ts +76 -0
- package/dist/packages/runtime/schemas/agents.js +41 -0
- package/dist/packages/runtime/schemas/build-plans.d.ts +959 -0
- package/dist/packages/runtime/schemas/build-plans.js +107 -0
- package/dist/packages/runtime/schemas/files.d.ts +567 -0
- package/dist/packages/runtime/schemas/files.js +51 -0
- package/dist/packages/runtime/schemas/index.d.ts +8 -0
- package/dist/packages/runtime/schemas/index.js +12 -0
- package/dist/packages/runtime/schemas/instance.d.ts +141 -0
- package/dist/packages/runtime/schemas/instance.js +143 -0
- package/dist/packages/runtime/schemas/jobs.d.ts +339 -0
- package/dist/packages/runtime/schemas/jobs.js +103 -0
- package/dist/packages/runtime/schemas/projects.d.ts +339 -0
- package/dist/packages/runtime/schemas/projects.js +136 -0
- package/dist/packages/runtime/schemas/runs.d.ts +2676 -0
- package/dist/packages/runtime/schemas/runs.js +98 -0
- package/dist/packages/runtime/service/index.d.ts +3 -0
- package/dist/packages/runtime/service/index.js +3 -0
- package/dist/packages/runtime/service/openapi.d.ts +7 -0
- package/dist/packages/runtime/service/openapi.js +117 -0
- package/dist/packages/runtime/service/operations.d.ts +1490 -0
- package/dist/packages/runtime/service/operations.js +245 -0
- package/dist/packages/runtime/service/routes.d.ts +106 -0
- package/dist/packages/runtime/service/routes.js +120 -0
- package/dist/packages/runtime/service/server-api-files.d.ts +10 -0
- package/dist/packages/runtime/service/server-api-files.js +85 -0
- package/dist/packages/runtime/service/server-app-boot.d.ts +4 -0
- package/dist/packages/runtime/service/server-app-boot.js +46 -0
- package/dist/packages/runtime/service/server-guards.d.ts +63 -0
- package/dist/packages/runtime/service/server-guards.js +181 -0
- package/dist/packages/runtime/service/server-helpers.d.ts +27 -0
- package/dist/packages/runtime/service/server-helpers.js +89 -0
- package/dist/packages/runtime/service/server-instance-helpers.d.ts +30 -0
- package/dist/packages/runtime/service/server-instance-helpers.js +114 -0
- package/dist/packages/runtime/service/server-routes-action-proposals.d.ts +3 -0
- package/dist/packages/runtime/service/server-routes-action-proposals.js +43 -0
- package/dist/packages/runtime/service/server-routes-agents.d.ts +4 -0
- package/dist/packages/runtime/service/server-routes-agents.js +198 -0
- package/dist/packages/runtime/service/server-routes-auth.d.ts +33 -0
- package/dist/packages/runtime/service/server-routes-auth.js +138 -0
- package/dist/packages/runtime/service/server-routes-build-plans.d.ts +3 -0
- package/dist/packages/runtime/service/server-routes-build-plans.js +83 -0
- package/dist/packages/runtime/service/server-routes-discovery.d.ts +4 -0
- package/dist/packages/runtime/service/server-routes-discovery.js +196 -0
- package/dist/packages/runtime/service/server-routes-events.d.ts +5 -0
- package/dist/packages/runtime/service/server-routes-events.js +99 -0
- package/dist/packages/runtime/service/server-routes-project-context.d.ts +9 -0
- package/dist/packages/runtime/service/server-routes-project-context.js +192 -0
- package/dist/packages/runtime/service/server-routes-project-jobs.d.ts +9 -0
- package/dist/packages/runtime/service/server-routes-project-jobs.js +130 -0
- package/dist/packages/runtime/service/server-routes-project-runs.d.ts +14 -0
- package/dist/packages/runtime/service/server-routes-project-runs.js +85 -0
- package/dist/packages/runtime/service/server-routes-projects.d.ts +4 -0
- package/dist/packages/runtime/service/server-routes-projects.js +92 -0
- package/dist/packages/runtime/service/server-routes-runs.d.ts +3 -0
- package/dist/packages/runtime/service/server-routes-runs.js +112 -0
- package/dist/packages/runtime/service/server.d.ts +37 -0
- package/dist/packages/runtime/service/server.js +300 -0
- package/dist/packages/{engine → runtime/service}/service-registry.d.ts +5 -5
- package/dist/packages/{engine → runtime/service}/service-registry.js +7 -7
- package/dist/packages/runtime/verify/benchmark-run.d.ts +81 -0
- package/dist/packages/runtime/verify/benchmark-run.js +303 -0
- package/dist/packages/{engine → runtime}/verify/index.d.ts +2 -2
- package/dist/packages/{engine → runtime}/verify/index.js +1 -1
- package/dist/packages/{engine → runtime}/verify/lib/schema.d.ts +83 -16
- package/dist/packages/{engine → runtime}/verify/lib/schema.js +26 -18
- package/dist/packages/{engine → runtime}/verify/verify-execution.js +11 -11
- package/dist/packages/{engine → runtime}/verify/verify-paths.d.ts +4 -4
- package/dist/packages/runtime/verify/verify-paths.js +61 -0
- package/dist/packages/{engine → runtime}/verify/verify-sandbox.d.ts +1 -1
- package/dist/packages/runtime/verify/verify-sandbox.js +88 -0
- package/dist/packages/{engine → runtime}/verify/verify-specs.d.ts +2 -0
- package/dist/packages/{engine → runtime}/verify/verify-specs.js +5 -3
- package/dist/packages/runtime/verify/verify-targets.d.ts +5 -0
- package/dist/packages/{engine → runtime}/verify/verify-targets.js +12 -12
- package/dist/packages/runtime/verify/verify-types.js +1 -0
- package/dist/packages/{engine → runtime}/verify/verify.d.ts +1 -1
- package/dist/packages/{engine → runtime}/verify/verify.js +1 -1
- package/dist/packages/runtime/wire-schemas.d.ts +18 -0
- package/dist/packages/runtime/wire-schemas.js +27 -0
- package/package.json +40 -30
- package/public-repo/CONTRIBUTING.md +7 -16
- package/public-repo/README.md +175 -234
- package/public-repo/SECURITY.md +3 -4
- package/public-repo/build-plans/interf-default/README.md +19 -14
- package/public-repo/build-plans/interf-default/build/stages/shape/SKILL.md +18 -11
- package/public-repo/build-plans/interf-default/build/stages/structure/SKILL.md +12 -5
- package/public-repo/build-plans/interf-default/build/stages/summarize/SKILL.md +16 -4
- package/public-repo/build-plans/interf-default/build-plan.json +34 -28
- package/public-repo/build-plans/interf-default/build-plan.schema.json +54 -32
- package/public-repo/build-plans/interf-default/improve/SKILL.md +3 -3
- package/public-repo/build-plans/interf-default/use/query/SKILL.md +17 -11
- package/public-repo/openapi/local-service.openapi.json +6803 -0
- package/public-repo/skills/interf/SKILL.md +460 -179
- package/dist/cli/commands/prep.d.ts +0 -2
- package/dist/cli/commands/prep.js +0 -240
- package/dist/cli/commands/test.d.ts +0 -10
- package/dist/cli/commands/test.js +0 -85
- package/dist/cli/commands/web.d.ts +0 -2
- package/dist/cli/commands/web.js +0 -286
- package/dist/interf-ui/404.html +0 -1
- package/dist/interf-ui/__next.__PAGE__.txt +0 -10
- package/dist/interf-ui/__next._full.txt +0 -20
- package/dist/interf-ui/__next._head.txt +0 -5
- package/dist/interf-ui/__next._index.txt +0 -5
- package/dist/interf-ui/__next._tree.txt +0 -5
- package/dist/interf-ui/_next/static/_qJKnA0dammQ306MG_zMq/_buildManifest.js +0 -11
- package/dist/interf-ui/_next/static/_qJKnA0dammQ306MG_zMq/_clientMiddlewareManifest.js +0 -1
- package/dist/interf-ui/_next/static/_qJKnA0dammQ306MG_zMq/_ssgManifest.js +0 -1
- package/dist/interf-ui/_next/static/chunks/0.tjb6f4golw..css +0 -3
- package/dist/interf-ui/_next/static/chunks/03~yq9q893hmn.js +0 -1
- package/dist/interf-ui/_next/static/chunks/085-n_jv2ng_q.css +0 -1
- package/dist/interf-ui/_next/static/chunks/0dn41fa_zvgsl.js +0 -1
- package/dist/interf-ui/_next/static/chunks/0g-ea0zj5d-0k.js +0 -1
- package/dist/interf-ui/_next/static/chunks/0gwqglc4iz583.js +0 -1
- package/dist/interf-ui/_next/static/chunks/0haldgm65ve6l.js +0 -1
- package/dist/interf-ui/_next/static/chunks/0nv3am99vjzn4.js +0 -1
- package/dist/interf-ui/_next/static/chunks/0s77gt_o4jwtx.js +0 -1
- package/dist/interf-ui/_next/static/chunks/0y5z3t-z1c8ks.js.map +0 -5
- package/dist/interf-ui/_next/static/chunks/0~a36ujuzpaz..js +0 -116
- package/dist/interf-ui/_next/static/chunks/10jeodxe4nkgj.js +0 -31
- package/dist/interf-ui/_next/static/chunks/119h2rouych2t.js +0 -1
- package/dist/interf-ui/_next/static/chunks/13c8b~m8knjsf.js +0 -1
- package/dist/interf-ui/_next/static/chunks/14dznb2qpt-ho.js +0 -91
- package/dist/interf-ui/_next/static/chunks/15z_en80lrq-3.js +0 -5
- package/dist/interf-ui/_next/static/chunks/turbopack-0p.pvcjrtq-jh.js +0 -1
- package/dist/interf-ui/_next/static/chunks/turbopack-0usj_75.8frlw.js +0 -1
- package/dist/interf-ui/_next/static/chunks/turbopack-worker-0sjn--fhq~1cg.js +0 -1
- package/dist/interf-ui/_next/static/media/GeistMono_Variable.p.17jn9btb_52pq.woff2 +0 -0
- package/dist/interf-ui/_next/static/media/Geist_Variable-s.p.0-te~ja_gpvcf.woff2 +0 -0
- package/dist/interf-ui/_next/static/media/worker.102zas1s52_pf.js +0 -109
- package/dist/interf-ui/_not-found/__next._full.txt +0 -15
- package/dist/interf-ui/_not-found/__next._head.txt +0 -5
- package/dist/interf-ui/_not-found/__next._index.txt +0 -5
- package/dist/interf-ui/_not-found/__next._not-found.__PAGE__.txt +0 -5
- package/dist/interf-ui/_not-found/__next._not-found.txt +0 -5
- package/dist/interf-ui/_not-found/__next._tree.txt +0 -2
- package/dist/interf-ui/_not-found.html +0 -1
- package/dist/interf-ui/_not-found.txt +0 -15
- package/dist/interf-ui/index.html +0 -1
- package/dist/interf-ui/index.txt +0 -20
- package/dist/packages/contracts/lib/preparation-paths.d.ts +0 -117
- package/dist/packages/contracts/lib/preparation-paths.js +0 -177
- package/dist/packages/engine/action-definitions.d.ts +0 -407
- package/dist/packages/engine/action-definitions.js +0 -1158
- package/dist/packages/engine/action-values.d.ts +0 -1
- package/dist/packages/engine/action-values.js +0 -1
- package/dist/packages/engine/agents/lib/args.d.ts +0 -4
- package/dist/packages/engine/agents/lib/args.js +0 -52
- package/dist/packages/engine/agents/lib/chart-guidance.d.ts +0 -1
- package/dist/packages/engine/agents/lib/chart-guidance.js +0 -8
- package/dist/packages/engine/agents/lib/constants.js +0 -28
- package/dist/packages/engine/agents/lib/schema.d.ts +0 -8
- package/dist/packages/engine/agents/lib/schema.js +0 -7
- package/dist/packages/engine/agents/lib/shells.d.ts +0 -74
- package/dist/packages/engine/agents/lib/shells.js +0 -1052
- package/dist/packages/engine/agents/lib/verifiable-context-bootstrap.d.ts +0 -3
- package/dist/packages/engine/build/artifact-counts.d.ts +0 -1
- package/dist/packages/engine/build/billing-events.d.ts +0 -89
- package/dist/packages/engine/build/build-pipeline.js +0 -175
- package/dist/packages/engine/build/build-plan-runs.d.ts +0 -14
- package/dist/packages/engine/build/build-plan-runs.js +0 -31
- package/dist/packages/engine/build/build-stage-plan.d.ts +0 -16
- package/dist/packages/engine/build/build-stage-runner.js +0 -94
- package/dist/packages/engine/build/build-target.js +0 -16
- package/dist/packages/engine/build/check-evaluator.js +0 -298
- package/dist/packages/engine/build/reset.d.ts +0 -2
- package/dist/packages/engine/build/reset.js +0 -74
- package/dist/packages/engine/build/runtime-inventory.d.ts +0 -7
- package/dist/packages/engine/build/runtime-runs.d.ts +0 -10
- package/dist/packages/engine/build/runtime-runs.js +0 -224
- package/dist/packages/engine/build/runtime.d.ts +0 -5
- package/dist/packages/engine/build/runtime.js +0 -4
- package/dist/packages/engine/build/source-files.d.ts +0 -46
- package/dist/packages/engine/build/source-files.js +0 -149
- package/dist/packages/engine/build/state-artifacts.d.ts +0 -9
- package/dist/packages/engine/build/state-artifacts.js +0 -14
- package/dist/packages/engine/build/state-health.d.ts +0 -4
- package/dist/packages/engine/build/state-io.d.ts +0 -11
- package/dist/packages/engine/build/state-io.js +0 -82
- package/dist/packages/engine/build/state-paths.js +0 -16
- package/dist/packages/engine/build/state-view.d.ts +0 -5
- package/dist/packages/engine/build/state.d.ts +0 -7
- package/dist/packages/engine/build/state.js +0 -12
- package/dist/packages/engine/build/validate-helpers.d.ts +0 -12
- package/dist/packages/engine/build/validate-helpers.js +0 -41
- package/dist/packages/engine/build/verifiable-context-paths.d.ts +0 -47
- package/dist/packages/engine/build/verifiable-context-paths.js +0 -121
- package/dist/packages/engine/build/verifiable-context-schema.d.ts +0 -21
- package/dist/packages/engine/build/verifiable-context-schema.js +0 -126
- package/dist/packages/engine/cloud-seams.d.ts +0 -115
- package/dist/packages/engine/cloud-seams.js +0 -84
- package/dist/packages/engine/index.d.ts +0 -22
- package/dist/packages/engine/index.js +0 -15
- package/dist/packages/engine/instance-paths.d.ts +0 -106
- package/dist/packages/engine/instance-paths.js +0 -171
- package/dist/packages/engine/lib/schema.d.ts +0 -6304
- package/dist/packages/engine/lib/schema.js +0 -730
- package/dist/packages/engine/preparation-store.d.ts +0 -105
- package/dist/packages/engine/preparation-store.js +0 -213
- package/dist/packages/engine/readiness-check-draft.d.ts +0 -20
- package/dist/packages/engine/readiness-check-draft.js +0 -111
- package/dist/packages/engine/requested-artifacts.d.ts +0 -5
- package/dist/packages/engine/routes.d.ts +0 -85
- package/dist/packages/engine/routes.js +0 -99
- package/dist/packages/engine/runtime-caches.d.ts +0 -76
- package/dist/packages/engine/runtime-proposal-helpers.d.ts +0 -35
- package/dist/packages/engine/runtime-proposal-helpers.js +0 -247
- package/dist/packages/engine/runtime.d.ts +0 -371
- package/dist/packages/engine/runtime.js +0 -2463
- package/dist/packages/engine/server.d.ts +0 -58
- package/dist/packages/engine/server.js +0 -1399
- package/dist/packages/engine/verify/readiness-check-run.d.ts +0 -82
- package/dist/packages/engine/verify/readiness-check-run.js +0 -265
- package/dist/packages/engine/verify/verify-paths.js +0 -61
- package/dist/packages/engine/verify/verify-sandbox.js +0 -88
- package/dist/packages/engine/verify/verify-targets.d.ts +0 -5
- package/dist/packages/engine/wire-schemas.d.ts +0 -547
- package/dist/packages/engine/wire-schemas.js +0 -59
- package/dist/packages/project/index.d.ts +0 -9
- package/dist/packages/project/interf-bootstrap.d.ts +0 -1
- package/dist/packages/project/interf-bootstrap.js +0 -1
- package/dist/packages/project/interf-scaffold.d.ts +0 -3
- package/dist/packages/project/interf-scaffold.js +0 -136
- package/dist/packages/project/interf.d.ts +0 -4
- package/dist/packages/project/interf.js +0 -3
- package/dist/packages/project/lib/schema.d.ts +0 -328
- package/dist/packages/project/lib/schema.js +0 -136
- package/dist/packages/project/preparation-entries.d.ts +0 -11
- package/dist/packages/project/preparation-entries.js +0 -49
- package/dist/packages/project/source-config.d.ts +0 -46
- package/dist/packages/project/source-config.js +0 -394
- package/dist/packages/project/source-folders.d.ts +0 -11
- package/public-repo/plugins/README.md +0 -9
- package/public-repo/plugins/interf/.claude-plugin/plugin.json +0 -21
- package/public-repo/plugins/interf/.mcp.json +0 -12
- package/public-repo/plugins/interf/README.md +0 -32
- package/public-repo/plugins/interf/skills/interf/SKILL.md +0 -376
- /package/dist/packages/{engine/agents/lib/types.js → runtime/actions/fields.js} +0 -0
- /package/dist/packages/{engine → runtime}/agents/lib/agents.d.ts +0 -0
- /package/dist/packages/{engine → runtime}/agents/lib/agents.js +0 -0
- /package/dist/packages/{engine → runtime}/agents/lib/execution-profile.d.ts +0 -0
- /package/dist/packages/{engine → runtime}/agents/lib/execution.d.ts +0 -0
- /package/dist/packages/{engine → runtime}/agents/lib/executors.d.ts +0 -0
- /package/dist/packages/{engine → runtime}/agents/lib/executors.js +0 -0
- /package/dist/packages/{engine → runtime}/agents/lib/logs.d.ts +0 -0
- /package/dist/packages/{engine → runtime}/agents/lib/logs.js +0 -0
- /package/dist/packages/{engine → runtime}/agents/lib/preflight.d.ts +0 -0
- /package/dist/packages/{engine → runtime}/agents/lib/render.d.ts +0 -0
- /package/dist/packages/{engine → runtime}/agents/lib/status.d.ts +0 -0
- /package/dist/packages/{engine → runtime}/agents/lib/status.js +0 -0
- /package/dist/packages/{engine → runtime}/agents/lib/types.d.ts +0 -0
- /package/dist/packages/{engine/build/runtime-types.js → runtime/agents/lib/types.js} +0 -0
- /package/dist/packages/{engine/verify/verify-types.js → runtime/agents/providers/types.js} +0 -0
- /package/dist/packages/{engine → runtime}/agents/role-router.d.ts +0 -0
- /package/dist/packages/{engine → runtime}/build/build-execution.d.ts +0 -0
- /package/dist/packages/{engine → runtime}/build/build-execution.js +0 -0
- /package/dist/packages/{engine → runtime}/build/runtime-contracts.d.ts +0 -0
- /package/dist/packages/{engine → runtime}/build/runtime-paths.d.ts +0 -0
- /package/dist/packages/{engine → runtime}/build/runtime-prompt.d.ts +0 -0
- /package/dist/packages/{engine → runtime}/execution/adapters.d.ts +0 -0
- /package/dist/packages/{engine → runtime}/execution/adapters.js +0 -0
- /package/dist/packages/{engine → runtime}/execution/events.d.ts +0 -0
- /package/dist/packages/{engine → runtime}/execution/events.js +0 -0
- /package/dist/packages/{engine → runtime}/verify/verify-execution.d.ts +0 -0
- /package/dist/packages/{engine → runtime}/verify/verify-profile-presets.d.ts +0 -0
- /package/dist/packages/{engine → runtime}/verify/verify-profile-presets.js +0 -0
- /package/dist/packages/{engine → runtime}/verify/verify-types.d.ts +0 -0
|
@@ -1,1399 +0,0 @@
|
|
|
1
|
-
import { createServer } from "node:http";
|
|
2
|
-
import { spawn } from "node:child_process";
|
|
3
|
-
import { randomBytes } from "node:crypto";
|
|
4
|
-
import { existsSync, statSync, readFileSync } from "node:fs";
|
|
5
|
-
import { dirname, extname, join, normalize, resolve, sep } from "node:path";
|
|
6
|
-
import { fileURLToPath } from "node:url";
|
|
7
|
-
import { LOCAL_SERVICE_LOOPBACK_HOSTS, LocalServiceConfigSchema, LocalServiceDiscoverySchema, LocalServiceErrorSchema, OpenPathRequestSchema, PreparationCreateRequestSchema, PreparationUpdateRequestSchema, ServiceRegistryEntrySchema, BuildPlanSaveRequestSchema, } from "./lib/schema.js";
|
|
8
|
-
import { assertPathWithinRoot, } from "../contracts/utils/path-guards.js";
|
|
9
|
-
import { createLocalServiceRuntime, } from "./runtime.js";
|
|
10
|
-
import { buildLocalServiceUrl, LOCAL_SERVICE_DEFAULT_HOST, LOCAL_SERVICE_DEFAULT_PORT, LOCAL_SERVICE_ROUTES, PREPARATION_SUBRESOURCES, } from "./routes.js";
|
|
11
|
-
import { registerServiceLocally, unregisterService, } from "./service-registry.js";
|
|
12
|
-
import { createStoredPreparation, deleteStoredPreparation, getStoredPreparation, listStoredPreparations, preparationWireShape, rehydratePreparations, updateStoredPreparation, } from "./preparation-store.js";
|
|
13
|
-
import { clearConnection, readActiveConnection, writeConnection, } from "./connection-config.js";
|
|
14
|
-
import { userBuildPlansRoot } from "./instance-paths.js";
|
|
15
|
-
import { builtinBuildPlanPackagePath, buildPlanDefinitionPath, } from "../build-plans/package/local-build-plans.js";
|
|
16
|
-
import { installUserBuildPlan, userBuildPlanPath, userBuildPlanExists } from "../build-plans/package/user-build-plans.js";
|
|
17
|
-
/** HTTP methods that require an authenticated bearer token + Origin guard. */
|
|
18
|
-
const MUTATING_METHODS = new Set(["POST", "PUT", "PATCH", "DELETE"]);
|
|
19
|
-
/** Generate a fresh per-instance bearer token. */
|
|
20
|
-
export function createLocalServiceAuthToken() {
|
|
21
|
-
return randomBytes(32).toString("hex");
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Build the per-instance CORS / Origin allowlist. Only the loopback
|
|
25
|
-
* hostnames bound to the active port count as same-origin. `null` and
|
|
26
|
-
* absent Origin are also accepted (Electron / file:// / CLI tools).
|
|
27
|
-
*/
|
|
28
|
-
function buildAllowedOrigins(host, port) {
|
|
29
|
-
const hostnames = Array.from(new Set([host, ...LOCAL_SERVICE_LOOPBACK_HOSTS]));
|
|
30
|
-
return {
|
|
31
|
-
hostnames,
|
|
32
|
-
ports: [port],
|
|
33
|
-
hostUrls: hostnames.map((hostname) => {
|
|
34
|
-
const wrapped = hostname.includes(":") ? `[${hostname}]` : hostname;
|
|
35
|
-
return `http://${wrapped}:${port}`;
|
|
36
|
-
}),
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
function originHeaderValue(req) {
|
|
40
|
-
const raw = req.headers.origin;
|
|
41
|
-
if (raw === undefined)
|
|
42
|
-
return null;
|
|
43
|
-
const value = Array.isArray(raw) ? raw[0] : raw;
|
|
44
|
-
if (typeof value !== "string")
|
|
45
|
-
return null;
|
|
46
|
-
const trimmed = value.trim();
|
|
47
|
-
return trimmed.length > 0 ? trimmed : null;
|
|
48
|
-
}
|
|
49
|
-
function authorizationHeaderValue(req) {
|
|
50
|
-
const raw = req.headers.authorization;
|
|
51
|
-
if (!raw)
|
|
52
|
-
return null;
|
|
53
|
-
const value = Array.isArray(raw) ? raw[0] : raw;
|
|
54
|
-
if (typeof value !== "string")
|
|
55
|
-
return null;
|
|
56
|
-
const trimmed = value.trim();
|
|
57
|
-
return trimmed.length > 0 ? trimmed : null;
|
|
58
|
-
}
|
|
59
|
-
function isOriginAllowed(origin, allowed) {
|
|
60
|
-
if (origin === null)
|
|
61
|
-
return true; // CLI / Node fetch / native app — no Origin sent.
|
|
62
|
-
if (origin === "null")
|
|
63
|
-
return true; // Electron / sandboxed / file:// page.
|
|
64
|
-
let parsed;
|
|
65
|
-
try {
|
|
66
|
-
parsed = new URL(origin);
|
|
67
|
-
}
|
|
68
|
-
catch {
|
|
69
|
-
return false;
|
|
70
|
-
}
|
|
71
|
-
if (parsed.protocol !== "http:" && parsed.protocol !== "https:")
|
|
72
|
-
return false;
|
|
73
|
-
const hostname = parsed.hostname.replace(/^\[|\]$/g, "");
|
|
74
|
-
if (!allowed.hostnames.includes(hostname))
|
|
75
|
-
return false;
|
|
76
|
-
// Default ports (no explicit port in URL) cannot be the local service.
|
|
77
|
-
if (!parsed.port)
|
|
78
|
-
return false;
|
|
79
|
-
const portNumber = Number.parseInt(parsed.port, 10);
|
|
80
|
-
if (!Number.isInteger(portNumber))
|
|
81
|
-
return false;
|
|
82
|
-
return allowed.ports.includes(portNumber);
|
|
83
|
-
}
|
|
84
|
-
function corsHeadersFor(origin, allowed) {
|
|
85
|
-
if (!isOriginAllowed(origin, allowed))
|
|
86
|
-
return { vary: "origin" };
|
|
87
|
-
return {
|
|
88
|
-
"access-control-allow-origin": origin ?? allowed.hostUrls[0] ?? "",
|
|
89
|
-
"access-control-allow-credentials": "false",
|
|
90
|
-
"access-control-allow-methods": "GET,POST,PATCH,DELETE,OPTIONS",
|
|
91
|
-
"access-control-allow-headers": "content-type, authorization, x-interf-idempotency-key",
|
|
92
|
-
vary: "origin",
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
function isMutatingMethod(method) {
|
|
96
|
-
return method !== undefined && MUTATING_METHODS.has(method.toUpperCase());
|
|
97
|
-
}
|
|
98
|
-
/**
|
|
99
|
-
* Validate the bearer token on a mutating request. Returns true when:
|
|
100
|
-
* - the runtime has no token (test harness mode), or
|
|
101
|
-
* - the request includes `Authorization: Bearer <token>` matching the runtime token.
|
|
102
|
-
*/
|
|
103
|
-
function isAuthorizedMutation(req, runtime) {
|
|
104
|
-
if (!runtime.authToken)
|
|
105
|
-
return true;
|
|
106
|
-
const authorization = authorizationHeaderValue(req);
|
|
107
|
-
if (!authorization)
|
|
108
|
-
return false;
|
|
109
|
-
const match = /^Bearer\s+([A-Za-z0-9._\-]+)$/.exec(authorization);
|
|
110
|
-
if (!match)
|
|
111
|
-
return false;
|
|
112
|
-
const presented = match[1];
|
|
113
|
-
if (!presented || presented.length !== runtime.authToken.length)
|
|
114
|
-
return false;
|
|
115
|
-
// Constant-time compare to avoid timing oracles.
|
|
116
|
-
let mismatch = 0;
|
|
117
|
-
for (let index = 0; index < runtime.authToken.length; index += 1) {
|
|
118
|
-
mismatch |= presented.charCodeAt(index) ^ runtime.authToken.charCodeAt(index);
|
|
119
|
-
}
|
|
120
|
-
return mismatch === 0;
|
|
121
|
-
}
|
|
122
|
-
function packageRoot() {
|
|
123
|
-
return resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "..");
|
|
124
|
-
}
|
|
125
|
-
export function resolveInterfUiStaticRoot(rootPath = packageRoot()) {
|
|
126
|
-
const distRoot = join(rootPath, "dist", "interf-ui");
|
|
127
|
-
const sourceExportRoot = join(rootPath, "src", "apps", "interf-ui", "out");
|
|
128
|
-
if (existsSync(join(distRoot, "index.html")))
|
|
129
|
-
return distRoot;
|
|
130
|
-
if (existsSync(join(sourceExportRoot, "index.html")))
|
|
131
|
-
return sourceExportRoot;
|
|
132
|
-
return distRoot;
|
|
133
|
-
}
|
|
134
|
-
function interfUiStaticRoot() {
|
|
135
|
-
const explicit = process.env.INTERF_UI_STATIC_ROOT?.trim();
|
|
136
|
-
if (explicit)
|
|
137
|
-
return resolve(explicit);
|
|
138
|
-
return resolveInterfUiStaticRoot();
|
|
139
|
-
}
|
|
140
|
-
function contentType(filePath) {
|
|
141
|
-
switch (extname(filePath)) {
|
|
142
|
-
case ".html":
|
|
143
|
-
return "text/html; charset=utf-8";
|
|
144
|
-
case ".js":
|
|
145
|
-
return "text/javascript; charset=utf-8";
|
|
146
|
-
case ".css":
|
|
147
|
-
return "text/css; charset=utf-8";
|
|
148
|
-
case ".json":
|
|
149
|
-
return "application/json; charset=utf-8";
|
|
150
|
-
case ".svg":
|
|
151
|
-
return "image/svg+xml";
|
|
152
|
-
case ".png":
|
|
153
|
-
return "image/png";
|
|
154
|
-
case ".ico":
|
|
155
|
-
return "image/x-icon";
|
|
156
|
-
case ".txt":
|
|
157
|
-
return "text/plain; charset=utf-8";
|
|
158
|
-
case ".md":
|
|
159
|
-
return "text/markdown; charset=utf-8";
|
|
160
|
-
case ".yml":
|
|
161
|
-
case ".yaml":
|
|
162
|
-
return "application/yaml; charset=utf-8";
|
|
163
|
-
case ".map":
|
|
164
|
-
return "application/json; charset=utf-8";
|
|
165
|
-
case ".webmanifest":
|
|
166
|
-
return "application/manifest+json; charset=utf-8";
|
|
167
|
-
case ".woff2":
|
|
168
|
-
return "font/woff2";
|
|
169
|
-
default:
|
|
170
|
-
return "application/octet-stream";
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
const NO_ORIGIN_RESPONSE_CONTEXT = { cors: { vary: "origin" } };
|
|
174
|
-
/** Stash slot on the response object that holds the per-request CORS headers. */
|
|
175
|
-
const RESPONSE_CONTEXT_KEY = Symbol.for("interf.localService.responseContext");
|
|
176
|
-
function attachResponseContext(res, ctx) {
|
|
177
|
-
res[RESPONSE_CONTEXT_KEY] = ctx;
|
|
178
|
-
}
|
|
179
|
-
function activeResponseContext(res) {
|
|
180
|
-
const ctx = res[RESPONSE_CONTEXT_KEY];
|
|
181
|
-
return ctx ?? NO_ORIGIN_RESPONSE_CONTEXT;
|
|
182
|
-
}
|
|
183
|
-
function writeHeaders(res, statusCode, contextOrHeaders, headers = {}) {
|
|
184
|
-
// Compatibility shim while call sites migrate. Older callers pass a
|
|
185
|
-
// plain headers record as the third argument; new callers thread a
|
|
186
|
-
// `ResponseContext`. When neither is supplied (or only headers are),
|
|
187
|
-
// we read the per-request context the entry-point handler stashed on
|
|
188
|
-
// the response.
|
|
189
|
-
const isContext = contextOrHeaders !== undefined && "cors" in contextOrHeaders;
|
|
190
|
-
const context = isContext ? contextOrHeaders : activeResponseContext(res);
|
|
191
|
-
const extraHeaders = isContext ? headers : (contextOrHeaders ?? {});
|
|
192
|
-
res.writeHead(statusCode, {
|
|
193
|
-
...context.cors,
|
|
194
|
-
...extraHeaders,
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
function sendJson(res, statusCode, contextOrValue, maybeValue) {
|
|
198
|
-
const usingContext = contextOrValue !== null
|
|
199
|
-
&& typeof contextOrValue === "object"
|
|
200
|
-
&& "cors" in contextOrValue
|
|
201
|
-
&& maybeValue !== undefined;
|
|
202
|
-
const context = usingContext ? contextOrValue : activeResponseContext(res);
|
|
203
|
-
const value = usingContext ? maybeValue : contextOrValue;
|
|
204
|
-
writeHeaders(res, statusCode, context, {
|
|
205
|
-
"content-type": "application/json; charset=utf-8",
|
|
206
|
-
});
|
|
207
|
-
res.end(`${JSON.stringify(value, null, 2)}\n`);
|
|
208
|
-
}
|
|
209
|
-
function sendText(res, statusCode, contextOrValue, maybeValue) {
|
|
210
|
-
const usingContext = typeof contextOrValue === "object" && contextOrValue !== null && "cors" in contextOrValue;
|
|
211
|
-
const context = usingContext ? contextOrValue : activeResponseContext(res);
|
|
212
|
-
const value = usingContext ? (maybeValue ?? "") : contextOrValue;
|
|
213
|
-
writeHeaders(res, statusCode, context, {
|
|
214
|
-
"content-type": "text/plain; charset=utf-8",
|
|
215
|
-
});
|
|
216
|
-
res.end(value);
|
|
217
|
-
}
|
|
218
|
-
function sendError(res, statusCode, contextOrMessage, maybeMessage) {
|
|
219
|
-
const usingContext = typeof contextOrMessage === "object" && contextOrMessage !== null && "cors" in contextOrMessage;
|
|
220
|
-
const context = usingContext ? contextOrMessage : activeResponseContext(res);
|
|
221
|
-
const message = usingContext ? (maybeMessage ?? "") : contextOrMessage;
|
|
222
|
-
sendJson(res, statusCode, context, LocalServiceErrorSchema.parse({
|
|
223
|
-
error: {
|
|
224
|
-
message,
|
|
225
|
-
},
|
|
226
|
-
}));
|
|
227
|
-
}
|
|
228
|
-
function resolveOpenPath(allowedRoots, pathValue) {
|
|
229
|
-
const roots = allowedRoots.map((root) => resolve(root));
|
|
230
|
-
const targetPath = resolve(pathValue);
|
|
231
|
-
for (const root of roots) {
|
|
232
|
-
try {
|
|
233
|
-
return assertPathWithinRoot(root, targetPath, "Open path");
|
|
234
|
-
}
|
|
235
|
-
catch {
|
|
236
|
-
continue;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
throw new Error(`Open path must stay inside ${roots.join(" or ")}: ${targetPath}`);
|
|
240
|
-
}
|
|
241
|
-
async function openLocalPath(allowedRoots, pathValue) {
|
|
242
|
-
const targetPath = resolveOpenPath(allowedRoots, pathValue);
|
|
243
|
-
if (!existsSync(targetPath)) {
|
|
244
|
-
throw new Error(`Path does not exist: ${targetPath}`);
|
|
245
|
-
}
|
|
246
|
-
if (process.env.INTERF_OPEN_PATH_DRY_RUN === "1") {
|
|
247
|
-
return targetPath;
|
|
248
|
-
}
|
|
249
|
-
const isDirectory = statSync(targetPath).isDirectory();
|
|
250
|
-
const command = process.platform === "darwin"
|
|
251
|
-
? "open"
|
|
252
|
-
: process.platform === "win32"
|
|
253
|
-
? "explorer.exe"
|
|
254
|
-
: "xdg-open";
|
|
255
|
-
const args = process.platform === "darwin"
|
|
256
|
-
? (isDirectory ? [targetPath] : ["-R", targetPath])
|
|
257
|
-
: process.platform === "win32"
|
|
258
|
-
? (isDirectory ? [targetPath] : ["/select,", targetPath])
|
|
259
|
-
: [isDirectory ? targetPath : dirname(targetPath)];
|
|
260
|
-
await new Promise((resolveOpen, rejectOpen) => {
|
|
261
|
-
const child = spawn(command, args, {
|
|
262
|
-
detached: true,
|
|
263
|
-
stdio: "ignore",
|
|
264
|
-
});
|
|
265
|
-
child.once("error", rejectOpen);
|
|
266
|
-
child.once("spawn", () => {
|
|
267
|
-
child.unref();
|
|
268
|
-
resolveOpen();
|
|
269
|
-
});
|
|
270
|
-
});
|
|
271
|
-
return targetPath;
|
|
272
|
-
}
|
|
273
|
-
function parseRequestUrl(req) {
|
|
274
|
-
return new URL(req.url ?? "/", "http://127.0.0.1");
|
|
275
|
-
}
|
|
276
|
-
function safeStaticPath(root, relativePath) {
|
|
277
|
-
const normalized = normalize(relativePath).replace(/^(\.\.(?:\/|\\|$))+/, "");
|
|
278
|
-
const absolute = resolve(root, normalized);
|
|
279
|
-
const resolvedRoot = resolve(root);
|
|
280
|
-
if (absolute !== resolvedRoot && !absolute.startsWith(`${resolvedRoot}${sep}`)) {
|
|
281
|
-
return null;
|
|
282
|
-
}
|
|
283
|
-
return absolute;
|
|
284
|
-
}
|
|
285
|
-
/**
|
|
286
|
-
* Resolve the on-disk root path for a Build Plan id. Used by the
|
|
287
|
-
* `/v1/build-plans/<id>/files/<relpath>` route. Resolution order matches
|
|
288
|
-
* the runtime: preparation-draft (if a prep id is supplied) → user
|
|
289
|
-
* library → built-in. Returns `null` when the Build Plan id resolves
|
|
290
|
-
* nowhere.
|
|
291
|
-
*/
|
|
292
|
-
function resolveBuildPlanPackageRoot(buildPlanId, prepDataDir) {
|
|
293
|
-
if (prepDataDir) {
|
|
294
|
-
const localPath = buildPlanDefinitionPath(prepDataDir, buildPlanId);
|
|
295
|
-
if (existsSync(join(localPath, "build-plan.json")))
|
|
296
|
-
return localPath;
|
|
297
|
-
}
|
|
298
|
-
if (userBuildPlanExists(buildPlanId)) {
|
|
299
|
-
return userBuildPlanPath(buildPlanId);
|
|
300
|
-
}
|
|
301
|
-
const builtinPath = builtinBuildPlanPackagePath(buildPlanId);
|
|
302
|
-
if (existsSync(join(builtinPath, "build-plan.json")))
|
|
303
|
-
return builtinPath;
|
|
304
|
-
return null;
|
|
305
|
-
}
|
|
306
|
-
function sendFile(res, filePath) {
|
|
307
|
-
if (!existsSync(filePath))
|
|
308
|
-
return false;
|
|
309
|
-
try {
|
|
310
|
-
if (!statSync(filePath).isFile())
|
|
311
|
-
return false;
|
|
312
|
-
const type = contentType(filePath);
|
|
313
|
-
const headers = { "content-type": type };
|
|
314
|
-
// 0.17 — never cache HTML responses. UI deploys ship a fresh
|
|
315
|
-
// dist via `npm run web:build` and a restart; without `no-store`,
|
|
316
|
-
// a browser sitting on a stale `index.html` would keep loading
|
|
317
|
-
// hashed asset paths that no longer exist. Hashed Next.js bundles
|
|
318
|
-
// under `_next/static/<hash>/` keep the default long-cache
|
|
319
|
-
// because the filename itself changes when content changes.
|
|
320
|
-
if (type.startsWith("text/html")) {
|
|
321
|
-
headers["cache-control"] = "no-store";
|
|
322
|
-
}
|
|
323
|
-
writeHeaders(res, 200, headers);
|
|
324
|
-
res.end(readFileSync(filePath));
|
|
325
|
-
return true;
|
|
326
|
-
}
|
|
327
|
-
catch {
|
|
328
|
-
return false;
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
function isUsableInterfUiIndexHtml(filePath) {
|
|
332
|
-
if (!existsSync(filePath))
|
|
333
|
-
return false;
|
|
334
|
-
const html = readFileSync(filePath, "utf8");
|
|
335
|
-
return html.includes("</html>")
|
|
336
|
-
&& html.includes("</script>")
|
|
337
|
-
&& html.includes("_next/static/");
|
|
338
|
-
}
|
|
339
|
-
/**
|
|
340
|
-
* Reject any path that contains `..` or absolute segments — used by the
|
|
341
|
-
* 0.16 file-serving endpoints (`/v1/build-plans/<id>/files/...`,
|
|
342
|
-
* `/v1/preparations/<id>/files/...`). The `safeStaticPath` helper used
|
|
343
|
-
* by the static-asset path silently strips traversal segments; for the
|
|
344
|
-
* API endpoints we want a hard reject so the response is unambiguous.
|
|
345
|
-
*/
|
|
346
|
-
function isTraversalRelativePath(relPath) {
|
|
347
|
-
if (relPath.length === 0)
|
|
348
|
-
return true;
|
|
349
|
-
const segments = relPath.split(/[\\/]+/);
|
|
350
|
-
for (const segment of segments) {
|
|
351
|
-
if (segment === ".." || segment === "")
|
|
352
|
-
return true;
|
|
353
|
-
}
|
|
354
|
-
if (relPath.startsWith("/") || /^[A-Za-z]:/.test(relPath))
|
|
355
|
-
return true;
|
|
356
|
-
return false;
|
|
357
|
-
}
|
|
358
|
-
function sendInterfUiAsset(req, res, _runtime) {
|
|
359
|
-
const staticRoot = interfUiStaticRoot();
|
|
360
|
-
const url = parseRequestUrl(req);
|
|
361
|
-
if (url.pathname === "/") {
|
|
362
|
-
const indexPath = join(staticRoot, "index.html");
|
|
363
|
-
if (!isUsableInterfUiIndexHtml(indexPath)) {
|
|
364
|
-
sendText(res, 503, "Interf UI assets are invalid. Run `npm run web:build`, then restart `interf web`.\n");
|
|
365
|
-
return true;
|
|
366
|
-
}
|
|
367
|
-
if (sendFile(res, indexPath))
|
|
368
|
-
return true;
|
|
369
|
-
sendText(res, 503, "Interf UI assets are missing. Run `npm run web:build`, then restart `interf web`.\n");
|
|
370
|
-
return true;
|
|
371
|
-
}
|
|
372
|
-
if (url.pathname.startsWith("/_next/static/")) {
|
|
373
|
-
const relativePath = url.pathname.slice("/_next/static/".length);
|
|
374
|
-
const assetPath = safeStaticPath(join(staticRoot, "_next", "static"), relativePath);
|
|
375
|
-
if (assetPath && sendFile(res, assetPath))
|
|
376
|
-
return true;
|
|
377
|
-
}
|
|
378
|
-
const assetPath = safeStaticPath(staticRoot, url.pathname.slice(1));
|
|
379
|
-
if (assetPath && sendFile(res, assetPath))
|
|
380
|
-
return true;
|
|
381
|
-
return false;
|
|
382
|
-
}
|
|
383
|
-
async function readJsonBody(req) {
|
|
384
|
-
const chunks = [];
|
|
385
|
-
for await (const chunk of req) {
|
|
386
|
-
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
387
|
-
}
|
|
388
|
-
const raw = Buffer.concat(chunks).toString("utf8").trim();
|
|
389
|
-
if (!raw)
|
|
390
|
-
return {};
|
|
391
|
-
return JSON.parse(raw);
|
|
392
|
-
}
|
|
393
|
-
async function routeApi(req, res, runtime) {
|
|
394
|
-
const url = parseRequestUrl(req);
|
|
395
|
-
const path = url.pathname;
|
|
396
|
-
const method = req.method ?? "GET";
|
|
397
|
-
const origin = originHeaderValue(req);
|
|
398
|
-
const allowed = buildAllowedOrigins(runtime.host, runtime.port);
|
|
399
|
-
const prepDataDirsForInstance = () => {
|
|
400
|
-
const dirs = listStoredPreparations().map((prep) => prep.prepDataDir);
|
|
401
|
-
return dirs.length > 0 ? dirs : [runtime.rootPath];
|
|
402
|
-
};
|
|
403
|
-
const listInstanceRuns = () => {
|
|
404
|
-
return prepDataDirsForInstance()
|
|
405
|
-
.flatMap((prepDataDir) => runtime.listRunObservability(prepDataDir))
|
|
406
|
-
.sort((left, right) => {
|
|
407
|
-
const leftMs = Date.parse(left.started_at ?? left.created_at ?? left.finished_at ?? "");
|
|
408
|
-
const rightMs = Date.parse(right.started_at ?? right.created_at ?? right.finished_at ?? "");
|
|
409
|
-
return (Number.isFinite(rightMs) ? rightMs : 0) - (Number.isFinite(leftMs) ? leftMs : 0);
|
|
410
|
-
});
|
|
411
|
-
};
|
|
412
|
-
const findInstanceRun = (runId) => {
|
|
413
|
-
for (const prepDataDir of prepDataDirsForInstance()) {
|
|
414
|
-
const run = runtime.getRunObservability(prepDataDir, runId);
|
|
415
|
-
if (run)
|
|
416
|
-
return run;
|
|
417
|
-
}
|
|
418
|
-
return null;
|
|
419
|
-
};
|
|
420
|
-
const findInstanceJob = (runId) => {
|
|
421
|
-
for (const prepDataDir of prepDataDirsForInstance()) {
|
|
422
|
-
const job = runtime.getJob(prepDataDir, runId);
|
|
423
|
-
if (job)
|
|
424
|
-
return { job, prepDataDir };
|
|
425
|
-
}
|
|
426
|
-
return null;
|
|
427
|
-
};
|
|
428
|
-
const listInstanceBuildPlans = () => {
|
|
429
|
-
const byId = new Map();
|
|
430
|
-
for (const prepDataDir of prepDataDirsForInstance()) {
|
|
431
|
-
for (const buildPlanResource of runtime.listBuildPlans(prepDataDir)) {
|
|
432
|
-
const existing = byId.get(buildPlanResource.id);
|
|
433
|
-
if (!existing) {
|
|
434
|
-
byId.set(buildPlanResource.id, buildPlanResource);
|
|
435
|
-
continue;
|
|
436
|
-
}
|
|
437
|
-
const preferred = existing.built_in && !buildPlanResource.built_in
|
|
438
|
-
? buildPlanResource
|
|
439
|
-
: existing;
|
|
440
|
-
byId.set(buildPlanResource.id, {
|
|
441
|
-
...preferred,
|
|
442
|
-
active_for_preparations: Array.from(new Set([
|
|
443
|
-
...existing.active_for_preparations,
|
|
444
|
-
...buildPlanResource.active_for_preparations,
|
|
445
|
-
])).sort(),
|
|
446
|
-
});
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
return [...byId.values()].sort((left, right) => left.id.localeCompare(right.id));
|
|
450
|
-
};
|
|
451
|
-
const findInstanceBuildPlan = (buildPlanId) => {
|
|
452
|
-
return listInstanceBuildPlans().find((buildPlanResource) => buildPlanResource.id === buildPlanId) ?? null;
|
|
453
|
-
};
|
|
454
|
-
const listInstanceBuildPlanRuns = (buildPlanId) => {
|
|
455
|
-
return prepDataDirsForInstance()
|
|
456
|
-
.flatMap((prepDataDir) => runtime.listBuildPlanRuns(prepDataDir, buildPlanId))
|
|
457
|
-
.sort((left, right) => {
|
|
458
|
-
const leftMs = Date.parse(left.started_at ?? left.created_at ?? left.finished_at ?? "");
|
|
459
|
-
const rightMs = Date.parse(right.started_at ?? right.created_at ?? right.finished_at ?? "");
|
|
460
|
-
return (Number.isFinite(rightMs) ? rightMs : 0) - (Number.isFinite(leftMs) ? leftMs : 0);
|
|
461
|
-
});
|
|
462
|
-
};
|
|
463
|
-
const resolveInstanceBuildPlanPackageRoot = (buildPlanId) => {
|
|
464
|
-
for (const prepDataDir of prepDataDirsForInstance()) {
|
|
465
|
-
const localPath = buildPlanDefinitionPath(prepDataDir, buildPlanId);
|
|
466
|
-
if (existsSync(join(localPath, "build-plan.json")))
|
|
467
|
-
return localPath;
|
|
468
|
-
}
|
|
469
|
-
return resolveBuildPlanPackageRoot(buildPlanId);
|
|
470
|
-
};
|
|
471
|
-
// CORS preflight — answered for allowed origins, refused otherwise.
|
|
472
|
-
if (method === "OPTIONS") {
|
|
473
|
-
if (!isOriginAllowed(origin, allowed)) {
|
|
474
|
-
attachResponseContext(res, NO_ORIGIN_RESPONSE_CONTEXT);
|
|
475
|
-
sendError(res, 403, "Origin not allowed.");
|
|
476
|
-
return true;
|
|
477
|
-
}
|
|
478
|
-
attachResponseContext(res, { cors: corsHeadersFor(origin, allowed) });
|
|
479
|
-
writeHeaders(res, 204);
|
|
480
|
-
res.end();
|
|
481
|
-
return true;
|
|
482
|
-
}
|
|
483
|
-
// For non-OPTIONS, attach the CORS context once.
|
|
484
|
-
attachResponseContext(res, { cors: corsHeadersFor(origin, allowed) });
|
|
485
|
-
// Mutating requests must:
|
|
486
|
-
// 1. Have an Origin that's local OR no Origin at all (CLI),
|
|
487
|
-
// 2. Carry a valid Authorization: Bearer <token> if the runtime issued one.
|
|
488
|
-
// Read-only GETs stay open so dev tooling can still inspect state.
|
|
489
|
-
if (isMutatingMethod(method)) {
|
|
490
|
-
if (!isOriginAllowed(origin, allowed)) {
|
|
491
|
-
sendError(res, 403, "Origin not allowed.");
|
|
492
|
-
return true;
|
|
493
|
-
}
|
|
494
|
-
if (!isAuthorizedMutation(req, runtime)) {
|
|
495
|
-
writeHeaders(res, 401, { "www-authenticate": "Bearer realm=\"interf-local-service\"" });
|
|
496
|
-
res.end(`${JSON.stringify(LocalServiceErrorSchema.parse({
|
|
497
|
-
error: { message: "Missing or invalid bearer token." },
|
|
498
|
-
}), null, 2)}\n`);
|
|
499
|
-
return true;
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
503
|
-
// Top-level engine routes — instance metadata, health, discovery.
|
|
504
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
505
|
-
// GET /health — liveness probe used by the CLI bootstrap.
|
|
506
|
-
if (method === "GET" && path === LOCAL_SERVICE_ROUTES.health) {
|
|
507
|
-
sendJson(res, 200, runtime.health());
|
|
508
|
-
return true;
|
|
509
|
-
}
|
|
510
|
-
// GET /v1 — discovery list of available resources.
|
|
511
|
-
if (method === "GET" && path === LOCAL_SERVICE_ROUTES.api) {
|
|
512
|
-
sendJson(res, 200, LocalServiceDiscoverySchema.parse({
|
|
513
|
-
kind: "interf-local-service-discovery",
|
|
514
|
-
version: 1,
|
|
515
|
-
resources: {
|
|
516
|
-
instance: LOCAL_SERVICE_ROUTES.instance,
|
|
517
|
-
preparations: LOCAL_SERVICE_ROUTES.preparations,
|
|
518
|
-
build_plans: LOCAL_SERVICE_ROUTES.buildPlans,
|
|
519
|
-
build_runs: LOCAL_SERVICE_ROUTES.buildRuns,
|
|
520
|
-
runs: LOCAL_SERVICE_ROUTES.runs,
|
|
521
|
-
action_proposals: LOCAL_SERVICE_ROUTES.actionProposals,
|
|
522
|
-
executor: LOCAL_SERVICE_ROUTES.executor,
|
|
523
|
-
agents: LOCAL_SERVICE_ROUTES.agents,
|
|
524
|
-
open_path: LOCAL_SERVICE_ROUTES.openPath,
|
|
525
|
-
},
|
|
526
|
-
}));
|
|
527
|
-
return true;
|
|
528
|
-
}
|
|
529
|
-
// GET /v1/instance — engine metadata.
|
|
530
|
-
if (method === "GET" && path === LOCAL_SERVICE_ROUTES.instance) {
|
|
531
|
-
const startedAtIso = runtime.startedAt ?? new Date().toISOString();
|
|
532
|
-
const startedMs = Date.parse(startedAtIso);
|
|
533
|
-
const uptimeSeconds = Number.isFinite(startedMs)
|
|
534
|
-
? Math.max(0, Math.floor((Date.now() - startedMs) / 1000))
|
|
535
|
-
: 0;
|
|
536
|
-
const agentSnapshot = runtime.getAgentsRegistry();
|
|
537
|
-
// 0.16 — `connection_kind` lets the UI know whether OS-open is even
|
|
538
|
-
// possible. A loopback bind means the engine has filesystem access
|
|
539
|
-
// to the same host as the user; non-loopback (cloud) returns
|
|
540
|
-
// "remote" so the UI routes to api-served / signed-URL paths
|
|
541
|
-
// instead. The current binary refuses non-loopback binds (see the
|
|
542
|
-
// host whitelist below), so the value is "local" today; the field
|
|
543
|
-
// is in place so the UI can read it without a CLI version sniff.
|
|
544
|
-
const isLoopback = LOCAL_SERVICE_LOOPBACK_HOSTS.includes(runtime.host);
|
|
545
|
-
sendJson(res, 200, {
|
|
546
|
-
kind: "interf-instance",
|
|
547
|
-
version: 1,
|
|
548
|
-
url: buildLocalServiceUrl({ host: runtime.host, port: runtime.port }),
|
|
549
|
-
host: runtime.host,
|
|
550
|
-
port: runtime.port,
|
|
551
|
-
pid: process.pid,
|
|
552
|
-
started_at: startedAtIso,
|
|
553
|
-
uptime_seconds: uptimeSeconds,
|
|
554
|
-
package_version: runtime.packageVersion ?? null,
|
|
555
|
-
preparation_count: listStoredPreparations().length,
|
|
556
|
-
auth_required: Boolean(runtime.authToken),
|
|
557
|
-
// 0.15 connected-agents fields:
|
|
558
|
-
agent_count: agentSnapshot.agents.length,
|
|
559
|
-
default_agent: agentSnapshot.active_agent?.name ?? null,
|
|
560
|
-
// 0.16 connection-mode flag:
|
|
561
|
-
connection_kind: isLoopback ? "local" : "remote",
|
|
562
|
-
});
|
|
563
|
-
return true;
|
|
564
|
-
}
|
|
565
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
566
|
-
// Preparation collection routes
|
|
567
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
568
|
-
// GET /v1/preparations — list every preparation on the instance.
|
|
569
|
-
if (method === "GET" && path === LOCAL_SERVICE_ROUTES.preparations) {
|
|
570
|
-
const items = listStoredPreparations().map(preparationWireShape);
|
|
571
|
-
sendJson(res, 200, { preparations: items });
|
|
572
|
-
return true;
|
|
573
|
-
}
|
|
574
|
-
// POST /v1/preparations — create a new preparation.
|
|
575
|
-
if (method === "POST" && path === LOCAL_SERVICE_ROUTES.preparations) {
|
|
576
|
-
try {
|
|
577
|
-
const body = PreparationCreateRequestSchema.parse(await readJsonBody(req));
|
|
578
|
-
// TODO(cloud): activate remote-folder validator branch when the
|
|
579
|
-
// cloud variant lands. The local binary only knows how to read
|
|
580
|
-
// bytes from a local path; a `remote-folder` binding requires
|
|
581
|
-
// the cloud variant's per-account object-storage mount layer.
|
|
582
|
-
// Until then, reject any kind other than `local-folder` so the
|
|
583
|
-
// wire shape and runtime stay consistent.
|
|
584
|
-
if (body.source.kind !== "local-folder") {
|
|
585
|
-
sendError(res, 400, `Unsupported source kind: ${body.source.kind}. The local engine only accepts local-folder bindings; remote-folder requires the cloud variant.`);
|
|
586
|
-
return true;
|
|
587
|
-
}
|
|
588
|
-
const stored = createStoredPreparation(runtime, {
|
|
589
|
-
id: body.id,
|
|
590
|
-
source: { kind: "local-folder", locator: body.source.locator },
|
|
591
|
-
build_plan_id: body.build_plan_id,
|
|
592
|
-
about: body.about,
|
|
593
|
-
requested_artifacts: body.requested_artifacts,
|
|
594
|
-
source_profile: body.source_profile,
|
|
595
|
-
checks: body.checks,
|
|
596
|
-
max_attempts: body.max_attempts,
|
|
597
|
-
max_loops: body.max_loops,
|
|
598
|
-
});
|
|
599
|
-
sendJson(res, 201, preparationWireShape(stored));
|
|
600
|
-
}
|
|
601
|
-
catch (error) {
|
|
602
|
-
sendError(res, 400, error instanceof Error ? error.message : String(error));
|
|
603
|
-
}
|
|
604
|
-
return true;
|
|
605
|
-
}
|
|
606
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
607
|
-
// Per-preparation routes (under /v1/preparations/{id}/…)
|
|
608
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
609
|
-
if (path.startsWith(`${LOCAL_SERVICE_ROUTES.preparations}/`)) {
|
|
610
|
-
const tail = path.slice(LOCAL_SERVICE_ROUTES.preparations.length + 1);
|
|
611
|
-
const slashIndex = tail.indexOf("/");
|
|
612
|
-
const prepId = slashIndex === -1 ? tail : tail.slice(0, slashIndex);
|
|
613
|
-
const subPath = slashIndex === -1 ? "" : tail.slice(slashIndex + 1);
|
|
614
|
-
const decodedPrepId = decodeURIComponent(prepId);
|
|
615
|
-
const storedPrep = getStoredPreparation(decodedPrepId);
|
|
616
|
-
if (!storedPrep) {
|
|
617
|
-
sendError(res, 404, `Preparation not found: ${decodedPrepId}`);
|
|
618
|
-
return true;
|
|
619
|
-
}
|
|
620
|
-
runtime.touchPreparation(storedPrep.prepDataDir);
|
|
621
|
-
if (subPath === "") {
|
|
622
|
-
// Bare /v1/preparations/{id}
|
|
623
|
-
if (method === "GET") {
|
|
624
|
-
const preparation = runtime.getPreparation(storedPrep.prepDataDir, storedPrep.id);
|
|
625
|
-
sendJson(res, 200, preparation
|
|
626
|
-
? {
|
|
627
|
-
...preparation,
|
|
628
|
-
source: storedPrep.source,
|
|
629
|
-
about: storedPrep.about,
|
|
630
|
-
config_path: storedPrep.configPath,
|
|
631
|
-
}
|
|
632
|
-
: preparationWireShape(storedPrep));
|
|
633
|
-
return true;
|
|
634
|
-
}
|
|
635
|
-
if (method === "PATCH") {
|
|
636
|
-
try {
|
|
637
|
-
const body = PreparationUpdateRequestSchema.parse(await readJsonBody(req));
|
|
638
|
-
const updated = updateStoredPreparation(decodedPrepId, {
|
|
639
|
-
build_plan_id: body.build_plan_id,
|
|
640
|
-
about: body.about,
|
|
641
|
-
...(body.checks !== undefined ? { checks: body.checks } : {}),
|
|
642
|
-
...(body.requested_artifacts !== undefined ? { requested_artifacts: body.requested_artifacts } : {}),
|
|
643
|
-
...(body.source_profile !== undefined ? { source_profile: body.source_profile } : {}),
|
|
644
|
-
});
|
|
645
|
-
sendJson(res, 200, preparationWireShape(updated));
|
|
646
|
-
}
|
|
647
|
-
catch (error) {
|
|
648
|
-
sendError(res, 400, error instanceof Error ? error.message : String(error));
|
|
649
|
-
}
|
|
650
|
-
return true;
|
|
651
|
-
}
|
|
652
|
-
if (method === "DELETE") {
|
|
653
|
-
deleteStoredPreparation(runtime, decodedPrepId);
|
|
654
|
-
sendJson(res, 200, { id: decodedPrepId, deleted: true });
|
|
655
|
-
return true;
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
else if (subPath === PREPARATION_SUBRESOURCES.buildRuns) {
|
|
659
|
-
if (method === "POST") {
|
|
660
|
-
if (!storedPrep.buildPlanId) {
|
|
661
|
-
sendError(res, 400, `Preparation ${storedPrep.id} has no Build Plan selected. Select one via PATCH /v1/preparations/${storedPrep.id} { "build_plan_id": "<id>" } before building.`);
|
|
662
|
-
return true;
|
|
663
|
-
}
|
|
664
|
-
try {
|
|
665
|
-
const body = (await readJsonBody(req));
|
|
666
|
-
const request = { preparation: storedPrep.id, ...(body ?? {}) };
|
|
667
|
-
const idempotencyKeyRaw = req.headers["x-interf-idempotency-key"];
|
|
668
|
-
const idempotencyKey = Array.isArray(idempotencyKeyRaw)
|
|
669
|
-
? idempotencyKeyRaw[0]
|
|
670
|
-
: idempotencyKeyRaw;
|
|
671
|
-
const trimmedKey = typeof idempotencyKey === "string" ? idempotencyKey.trim() : "";
|
|
672
|
-
const dedupedRunId = trimmedKey
|
|
673
|
-
? runtime.findIdempotentBuildRun(storedPrep.prepDataDir, trimmedKey)
|
|
674
|
-
: null;
|
|
675
|
-
if (dedupedRunId) {
|
|
676
|
-
const existing = runtime.getBuildRun(storedPrep.prepDataDir, dedupedRunId);
|
|
677
|
-
if (existing) {
|
|
678
|
-
sendJson(res, 200, existing);
|
|
679
|
-
return true;
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
const resource = await runtime.createBuildRun(storedPrep.prepDataDir, request);
|
|
683
|
-
if (trimmedKey) {
|
|
684
|
-
runtime.recordIdempotentBuildRun(storedPrep.prepDataDir, trimmedKey, resource.run.run_id);
|
|
685
|
-
}
|
|
686
|
-
sendJson(res, 201, resource);
|
|
687
|
-
}
|
|
688
|
-
catch (error) {
|
|
689
|
-
sendError(res, 400, error instanceof Error ? error.message : String(error));
|
|
690
|
-
}
|
|
691
|
-
return true;
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
else if (subPath === PREPARATION_SUBRESOURCES.verifyRuns) {
|
|
695
|
-
if (method === "POST") {
|
|
696
|
-
if (!storedPrep.buildPlanId) {
|
|
697
|
-
sendError(res, 400, `Preparation ${storedPrep.id} has no Build Plan selected. Select one via PATCH /v1/preparations/${storedPrep.id} { "build_plan_id": "<id>" } before verifying.`);
|
|
698
|
-
return true;
|
|
699
|
-
}
|
|
700
|
-
try {
|
|
701
|
-
const body = (await readJsonBody(req));
|
|
702
|
-
const request = { preparation: storedPrep.id, ...(body ?? {}) };
|
|
703
|
-
const resource = await runtime.createVerifyRun(storedPrep.prepDataDir, request);
|
|
704
|
-
sendJson(res, 201, resource);
|
|
705
|
-
}
|
|
706
|
-
catch (error) {
|
|
707
|
-
sendError(res, 400, error instanceof Error ? error.message : String(error));
|
|
708
|
-
}
|
|
709
|
-
return true;
|
|
710
|
-
}
|
|
711
|
-
}
|
|
712
|
-
else if (subPath === PREPARATION_SUBRESOURCES.buildPlanDraftRuns) {
|
|
713
|
-
if (method === "POST") {
|
|
714
|
-
try {
|
|
715
|
-
const body = (await readJsonBody(req));
|
|
716
|
-
const job = await runtime.createBuildPlanAuthoringRun(storedPrep.prepDataDir, body);
|
|
717
|
-
sendJson(res, 202, job);
|
|
718
|
-
}
|
|
719
|
-
catch (error) {
|
|
720
|
-
sendError(res, 400, error instanceof Error ? error.message : String(error));
|
|
721
|
-
}
|
|
722
|
-
return true;
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
else if (subPath === PREPARATION_SUBRESOURCES.buildPlanImprovementRuns) {
|
|
726
|
-
if (method === "POST") {
|
|
727
|
-
try {
|
|
728
|
-
const body = (await readJsonBody(req));
|
|
729
|
-
const job = await runtime.createBuildPlanAuthoringRun(storedPrep.prepDataDir, body, "build-plan-improvement");
|
|
730
|
-
sendJson(res, 202, job);
|
|
731
|
-
}
|
|
732
|
-
catch (error) {
|
|
733
|
-
sendError(res, 400, error instanceof Error ? error.message : String(error));
|
|
734
|
-
}
|
|
735
|
-
return true;
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
else if (subPath === PREPARATION_SUBRESOURCES.readinessCheckDrafts) {
|
|
739
|
-
if (method === "POST") {
|
|
740
|
-
try {
|
|
741
|
-
const body = (await readJsonBody(req));
|
|
742
|
-
const job = await runtime.createReadinessCheckDraftRun(storedPrep.prepDataDir, body);
|
|
743
|
-
sendJson(res, 202, job);
|
|
744
|
-
}
|
|
745
|
-
catch (error) {
|
|
746
|
-
sendError(res, 400, error instanceof Error ? error.message : String(error));
|
|
747
|
-
}
|
|
748
|
-
return true;
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
else if (subPath === PREPARATION_SUBRESOURCES.changes) {
|
|
752
|
-
if (method === "POST") {
|
|
753
|
-
try {
|
|
754
|
-
const body = (await readJsonBody(req));
|
|
755
|
-
sendJson(res, 202, runtime.applyPreparationChange(storedPrep.prepDataDir, body));
|
|
756
|
-
}
|
|
757
|
-
catch (error) {
|
|
758
|
-
sendError(res, 409, error instanceof Error ? error.message : String(error));
|
|
759
|
-
}
|
|
760
|
-
return true;
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
|
-
else if (subPath === PREPARATION_SUBRESOURCES.reset) {
|
|
764
|
-
if (method === "POST") {
|
|
765
|
-
try {
|
|
766
|
-
const body = (await readJsonBody(req));
|
|
767
|
-
const request = { preparation: storedPrep.id, scope: "build", ...(body ?? {}) };
|
|
768
|
-
const result = runtime.applyReset(storedPrep.prepDataDir, request);
|
|
769
|
-
sendJson(res, 200, result);
|
|
770
|
-
}
|
|
771
|
-
catch (error) {
|
|
772
|
-
sendError(res, 400, error instanceof Error ? error.message : String(error));
|
|
773
|
-
}
|
|
774
|
-
return true;
|
|
775
|
-
}
|
|
776
|
-
}
|
|
777
|
-
else if (subPath === PREPARATION_SUBRESOURCES.readiness) {
|
|
778
|
-
if (method === "GET") {
|
|
779
|
-
const readiness = runtime.getReadiness(storedPrep.prepDataDir, storedPrep.id);
|
|
780
|
-
sendJson(res, 200, readiness);
|
|
781
|
-
return true;
|
|
782
|
-
}
|
|
783
|
-
}
|
|
784
|
-
else if (subPath === PREPARATION_SUBRESOURCES.runs) {
|
|
785
|
-
if (method === "GET") {
|
|
786
|
-
const runs = runtime.listBuildRunsForPreparation(storedPrep.prepDataDir, storedPrep.id);
|
|
787
|
-
sendJson(res, 200, { runs });
|
|
788
|
-
return true;
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
|
-
else if (subPath === PREPARATION_SUBRESOURCES.sourceFiles) {
|
|
792
|
-
if (method === "GET") {
|
|
793
|
-
sendJson(res, 200, {
|
|
794
|
-
source_files: runtime.listSourceFiles(storedPrep.prepDataDir, storedPrep.id),
|
|
795
|
-
});
|
|
796
|
-
return true;
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
|
-
else if (subPath === PREPARATION_SUBRESOURCES.verifiableContext) {
|
|
800
|
-
if (method === "GET") {
|
|
801
|
-
const context = runtime.getVerifiableContext(storedPrep.prepDataDir, storedPrep.id);
|
|
802
|
-
if (!context)
|
|
803
|
-
sendError(res, 404, "Verifiable context not found.");
|
|
804
|
-
else
|
|
805
|
-
sendJson(res, 200, context);
|
|
806
|
-
return true;
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
else if (subPath.startsWith(`${PREPARATION_SUBRESOURCES.files}/`)) {
|
|
810
|
-
// GET /v1/preparations/<id>/files/<relpath> — read-only file
|
|
811
|
-
// serving inside the prep's verifiable-context root. Used by the
|
|
812
|
-
// locator pattern's `api-served` kind.
|
|
813
|
-
if (method === "GET") {
|
|
814
|
-
const rawRelPath = subPath.slice(PREPARATION_SUBRESOURCES.files.length + 1);
|
|
815
|
-
let relPath;
|
|
816
|
-
try {
|
|
817
|
-
relPath = decodeURIComponent(rawRelPath);
|
|
818
|
-
}
|
|
819
|
-
catch {
|
|
820
|
-
sendError(res, 400, "File path is not valid URI-encoded UTF-8.");
|
|
821
|
-
return true;
|
|
822
|
-
}
|
|
823
|
-
if (isTraversalRelativePath(relPath)) {
|
|
824
|
-
sendError(res, 400, "File path escapes verifiable-context root.");
|
|
825
|
-
return true;
|
|
826
|
-
}
|
|
827
|
-
const safePath = safeStaticPath(storedPrep.verifiableContextPath, relPath);
|
|
828
|
-
if (!safePath) {
|
|
829
|
-
sendError(res, 400, "File path escapes verifiable-context root.");
|
|
830
|
-
return true;
|
|
831
|
-
}
|
|
832
|
-
if (!sendFile(res, safePath)) {
|
|
833
|
-
sendError(res, 404, `File not found: ${relPath}`);
|
|
834
|
-
}
|
|
835
|
-
return true;
|
|
836
|
-
}
|
|
837
|
-
}
|
|
838
|
-
else if (subPath.startsWith(`${PREPARATION_SUBRESOURCES.artifacts}/`)) {
|
|
839
|
-
// GET /v1/preparations/<id>/artifacts/<artifact-id> — per-Artifact
|
|
840
|
-
// status from the latest Build run.
|
|
841
|
-
if (method === "GET") {
|
|
842
|
-
const rawArtifactId = subPath.slice(PREPARATION_SUBRESOURCES.artifacts.length + 1);
|
|
843
|
-
let artifactId;
|
|
844
|
-
try {
|
|
845
|
-
artifactId = decodeURIComponent(rawArtifactId);
|
|
846
|
-
}
|
|
847
|
-
catch {
|
|
848
|
-
sendError(res, 400, "Artifact id is not valid URI-encoded UTF-8.");
|
|
849
|
-
return true;
|
|
850
|
-
}
|
|
851
|
-
const preparation = runtime.getPreparation(storedPrep.prepDataDir, storedPrep.id);
|
|
852
|
-
const status = preparation?.artifacts.find((entry) => entry.artifact_id === artifactId);
|
|
853
|
-
if (!status) {
|
|
854
|
-
sendError(res, 404, `Artifact not found: ${artifactId}`);
|
|
855
|
-
return true;
|
|
856
|
-
}
|
|
857
|
-
sendJson(res, 200, status);
|
|
858
|
-
return true;
|
|
859
|
-
}
|
|
860
|
-
}
|
|
861
|
-
sendError(res, 404, `Unknown preparation sub-route: ${subPath}`);
|
|
862
|
-
return true;
|
|
863
|
-
}
|
|
864
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
865
|
-
// Build Plan resources — preparation-independent (Preparation-local + user lib + bundled).
|
|
866
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
867
|
-
if (method === "GET" && path === LOCAL_SERVICE_ROUTES.buildPlans) {
|
|
868
|
-
sendJson(res, 200, { build_plans: listInstanceBuildPlans() });
|
|
869
|
-
return true;
|
|
870
|
-
}
|
|
871
|
-
if (method === "POST" && path === LOCAL_SERVICE_ROUTES.buildPlans) {
|
|
872
|
-
try {
|
|
873
|
-
const body = BuildPlanSaveRequestSchema.parse(await readJsonBody(req));
|
|
874
|
-
const installed = installUserBuildPlan({
|
|
875
|
-
sourceDir: body.source_path,
|
|
876
|
-
id: body.id,
|
|
877
|
-
overwrite: body.overwrite,
|
|
878
|
-
});
|
|
879
|
-
sendJson(res, 201, {
|
|
880
|
-
build_plan_id: installed.id,
|
|
881
|
-
installed: true,
|
|
882
|
-
path: installed.path,
|
|
883
|
-
});
|
|
884
|
-
}
|
|
885
|
-
catch (error) {
|
|
886
|
-
sendError(res, 400, error instanceof Error ? error.message : String(error));
|
|
887
|
-
}
|
|
888
|
-
return true;
|
|
889
|
-
}
|
|
890
|
-
const buildPlanMatch = path.match(/^\/v1\/build-plans\/([^/]+)$/);
|
|
891
|
-
if (method === "GET" && buildPlanMatch?.[1]) {
|
|
892
|
-
const buildPlanResource = findInstanceBuildPlan(decodeURIComponent(buildPlanMatch[1]));
|
|
893
|
-
if (!buildPlanResource)
|
|
894
|
-
sendError(res, 404, "Build Plan not found.");
|
|
895
|
-
else
|
|
896
|
-
sendJson(res, 200, buildPlanResource);
|
|
897
|
-
return true;
|
|
898
|
-
}
|
|
899
|
-
const buildPlanRunsMatch = path.match(/^\/v1\/build-plans\/([^/]+)\/runs$/);
|
|
900
|
-
if (method === "GET" && buildPlanRunsMatch?.[1]) {
|
|
901
|
-
const runs = listInstanceBuildPlanRuns(decodeURIComponent(buildPlanRunsMatch[1]));
|
|
902
|
-
sendJson(res, 200, { runs });
|
|
903
|
-
return true;
|
|
904
|
-
}
|
|
905
|
-
// GET /v1/build-plans/<id>/files/<relpath> — read-only file serving inside
|
|
906
|
-
// the Build Plan package root. Used by the locator pattern's `api-served` kind so
|
|
907
|
-
// the UI can render SKILL.md / contract files in a side drawer over a
|
|
908
|
-
// remote engine. Resolution: prep-draft (when first prep exists) →
|
|
909
|
-
// user library → built-in. Path-guard rejects any traversal outside
|
|
910
|
-
// the resolved root.
|
|
911
|
-
const buildPlanFilesMatch = path.match(/^\/v1\/build-plans\/([^/]+)\/files\/(.+)$/);
|
|
912
|
-
if (method === "GET" && buildPlanFilesMatch?.[1] && buildPlanFilesMatch[2]) {
|
|
913
|
-
const buildPlanId = decodeURIComponent(buildPlanFilesMatch[1]);
|
|
914
|
-
let relPath;
|
|
915
|
-
try {
|
|
916
|
-
relPath = decodeURIComponent(buildPlanFilesMatch[2]);
|
|
917
|
-
}
|
|
918
|
-
catch {
|
|
919
|
-
sendError(res, 400, "File path is not valid URI-encoded UTF-8.");
|
|
920
|
-
return true;
|
|
921
|
-
}
|
|
922
|
-
if (isTraversalRelativePath(relPath)) {
|
|
923
|
-
sendError(res, 400, "File path escapes Build Plan root.");
|
|
924
|
-
return true;
|
|
925
|
-
}
|
|
926
|
-
const root = resolveInstanceBuildPlanPackageRoot(buildPlanId);
|
|
927
|
-
if (!root) {
|
|
928
|
-
sendError(res, 404, `Build Plan not found: ${buildPlanId}`);
|
|
929
|
-
return true;
|
|
930
|
-
}
|
|
931
|
-
const safePath = safeStaticPath(root, relPath);
|
|
932
|
-
if (!safePath) {
|
|
933
|
-
sendError(res, 400, "File path escapes Build Plan root.");
|
|
934
|
-
return true;
|
|
935
|
-
}
|
|
936
|
-
if (!sendFile(res, safePath)) {
|
|
937
|
-
sendError(res, 404, `File not found: ${relPath}`);
|
|
938
|
-
}
|
|
939
|
-
return true;
|
|
940
|
-
}
|
|
941
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
942
|
-
// Run observability — instance-wide. The runtime takes a "first prep" hint
|
|
943
|
-
// to scan registered Preparations.
|
|
944
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
945
|
-
if (method === "GET" && path === LOCAL_SERVICE_ROUTES.runs) {
|
|
946
|
-
sendJson(res, 200, { runs: listInstanceRuns() });
|
|
947
|
-
return true;
|
|
948
|
-
}
|
|
949
|
-
const observableRunMatch = path.match(/^\/v1\/runs\/([^/]+)$/);
|
|
950
|
-
if (method === "GET" && observableRunMatch?.[1]) {
|
|
951
|
-
const run = findInstanceRun(decodeURIComponent(observableRunMatch[1]));
|
|
952
|
-
if (!run)
|
|
953
|
-
sendError(res, 404, "Run not found.");
|
|
954
|
-
else
|
|
955
|
-
sendJson(res, 200, run);
|
|
956
|
-
return true;
|
|
957
|
-
}
|
|
958
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
959
|
-
// Action proposals (chat / wizard) — instance-wide.
|
|
960
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
961
|
-
if (method === "GET" && path === LOCAL_SERVICE_ROUTES.actionProposals) {
|
|
962
|
-
const firstPrep = listStoredPreparations()[0];
|
|
963
|
-
sendJson(res, 200, {
|
|
964
|
-
action_proposals: runtime.listActionProposals(firstPrep?.prepDataDir ?? runtime.rootPath),
|
|
965
|
-
});
|
|
966
|
-
return true;
|
|
967
|
-
}
|
|
968
|
-
if (method === "POST" && path === LOCAL_SERVICE_ROUTES.actionProposals) {
|
|
969
|
-
const body = await readJsonBody(req);
|
|
970
|
-
const firstPrep = listStoredPreparations()[0];
|
|
971
|
-
sendJson(res, 202, await runtime.createActionProposal(firstPrep?.prepDataDir ?? runtime.rootPath, body));
|
|
972
|
-
return true;
|
|
973
|
-
}
|
|
974
|
-
const actionProposalMatch = path.match(/^\/v1\/action-proposals\/([^/]+)(?:\/([^/]+))?$/);
|
|
975
|
-
if (actionProposalMatch?.[1]) {
|
|
976
|
-
const proposalId = decodeURIComponent(actionProposalMatch[1]);
|
|
977
|
-
const child = actionProposalMatch[2];
|
|
978
|
-
const firstPrep = listStoredPreparations()[0];
|
|
979
|
-
const prepDataDir = firstPrep?.prepDataDir ?? runtime.rootPath;
|
|
980
|
-
if (method === "GET" && !child) {
|
|
981
|
-
const proposal = runtime.getActionProposal(prepDataDir, proposalId);
|
|
982
|
-
if (!proposal)
|
|
983
|
-
sendError(res, 404, "Action proposal not found.");
|
|
984
|
-
else
|
|
985
|
-
sendJson(res, 200, proposal);
|
|
986
|
-
return true;
|
|
987
|
-
}
|
|
988
|
-
if (method === "POST" && child === "decision") {
|
|
989
|
-
const body = await readJsonBody(req);
|
|
990
|
-
const proposal = await runtime.decideActionProposal(prepDataDir, proposalId, body);
|
|
991
|
-
if (!proposal)
|
|
992
|
-
sendError(res, 404, "Action proposal not found.");
|
|
993
|
-
else
|
|
994
|
-
sendJson(res, 202, proposal);
|
|
995
|
-
return true;
|
|
996
|
-
}
|
|
997
|
-
}
|
|
998
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
999
|
-
// Build-run sub-resources (instance-wide; runs are addressable by run_id).
|
|
1000
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1001
|
-
const buildRunMatch = path.match(/^\/v1\/build-runs\/([^/]+)(?:\/([^/]+))?$/);
|
|
1002
|
-
if (buildRunMatch?.[1]) {
|
|
1003
|
-
const runId = decodeURIComponent(buildRunMatch[1]);
|
|
1004
|
-
const child = buildRunMatch[2];
|
|
1005
|
-
const firstPrep = listStoredPreparations()[0];
|
|
1006
|
-
const prepDataDir = firstPrep?.prepDataDir ?? runtime.rootPath;
|
|
1007
|
-
if (method === "GET" && !child) {
|
|
1008
|
-
const run = runtime.getBuildRun(prepDataDir, runId);
|
|
1009
|
-
if (!run)
|
|
1010
|
-
sendError(res, 404, "Build run not found.");
|
|
1011
|
-
else
|
|
1012
|
-
sendJson(res, 200, run);
|
|
1013
|
-
return true;
|
|
1014
|
-
}
|
|
1015
|
-
if (method === "GET" && child === "events") {
|
|
1016
|
-
const events = runtime.getBuildRunEvents(prepDataDir, runId);
|
|
1017
|
-
if (!events)
|
|
1018
|
-
sendError(res, 404, "Build run not found.");
|
|
1019
|
-
else
|
|
1020
|
-
sendJson(res, 200, { events });
|
|
1021
|
-
return true;
|
|
1022
|
-
}
|
|
1023
|
-
if (method === "GET" && child === "proof") {
|
|
1024
|
-
const proof = runtime.getBuildRunProof(prepDataDir, runId);
|
|
1025
|
-
if (!proof)
|
|
1026
|
-
sendError(res, 404, "Build run not found.");
|
|
1027
|
-
else
|
|
1028
|
-
sendJson(res, 200, { proof });
|
|
1029
|
-
return true;
|
|
1030
|
-
}
|
|
1031
|
-
if (method === "GET" && child === "artifacts") {
|
|
1032
|
-
const artifacts = runtime.getBuildRunArtifacts(prepDataDir, runId);
|
|
1033
|
-
if (!artifacts)
|
|
1034
|
-
sendError(res, 404, "Build run not found.");
|
|
1035
|
-
else
|
|
1036
|
-
sendJson(res, 200, { artifacts });
|
|
1037
|
-
return true;
|
|
1038
|
-
}
|
|
1039
|
-
if (method === "POST" && child === "cancel") {
|
|
1040
|
-
const existing = runtime.getBuildRun(prepDataDir, runId);
|
|
1041
|
-
if (!existing) {
|
|
1042
|
-
sendError(res, 404, "Build run not found.");
|
|
1043
|
-
return true;
|
|
1044
|
-
}
|
|
1045
|
-
const result = runtime.cancelBuildRun(runId);
|
|
1046
|
-
sendJson(res, 200, result);
|
|
1047
|
-
return true;
|
|
1048
|
-
}
|
|
1049
|
-
}
|
|
1050
|
-
const verifyRunMatch = path.match(/^\/v1\/verify-runs\/([^/]+)$/);
|
|
1051
|
-
if (method === "GET" && verifyRunMatch?.[1]) {
|
|
1052
|
-
const firstPrep = listStoredPreparations()[0];
|
|
1053
|
-
const run = runtime.getVerifyRun(firstPrep?.prepDataDir ?? runtime.rootPath, decodeURIComponent(verifyRunMatch[1]));
|
|
1054
|
-
if (!run)
|
|
1055
|
-
sendError(res, 404, "Verify run not found.");
|
|
1056
|
-
else
|
|
1057
|
-
sendJson(res, 200, run);
|
|
1058
|
-
return true;
|
|
1059
|
-
}
|
|
1060
|
-
const jobMatch = path.match(/^\/v1\/jobs\/([^/]+)(?:\/([^/]+))?$/);
|
|
1061
|
-
if (jobMatch?.[1]) {
|
|
1062
|
-
const runId = decodeURIComponent(jobMatch[1]);
|
|
1063
|
-
const child = jobMatch[2];
|
|
1064
|
-
const found = findInstanceJob(runId);
|
|
1065
|
-
if (method === "GET" && !child) {
|
|
1066
|
-
if (!found)
|
|
1067
|
-
sendError(res, 404, "Job run not found.");
|
|
1068
|
-
else
|
|
1069
|
-
sendJson(res, 200, found.job);
|
|
1070
|
-
return true;
|
|
1071
|
-
}
|
|
1072
|
-
if (method === "GET" && child === "events") {
|
|
1073
|
-
const events = found ? runtime.getJobEvents(found.prepDataDir, runId) : null;
|
|
1074
|
-
if (!events)
|
|
1075
|
-
sendError(res, 404, "Job run not found.");
|
|
1076
|
-
else
|
|
1077
|
-
sendJson(res, 200, { events });
|
|
1078
|
-
return true;
|
|
1079
|
-
}
|
|
1080
|
-
}
|
|
1081
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1082
|
-
// Executor + open-path — instance-wide.
|
|
1083
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1084
|
-
if (method === "GET" && path === LOCAL_SERVICE_ROUTES.executor) {
|
|
1085
|
-
sendJson(res, 200, runtime.getExecutorStatus());
|
|
1086
|
-
return true;
|
|
1087
|
-
}
|
|
1088
|
-
if (method === "POST" && path === LOCAL_SERVICE_ROUTES.executor) {
|
|
1089
|
-
const body = await readJsonBody(req);
|
|
1090
|
-
sendJson(res, 202, runtime.selectExecutor(body));
|
|
1091
|
-
return true;
|
|
1092
|
-
}
|
|
1093
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1094
|
-
// Connected agents (0.15) — registry, role-map, role list.
|
|
1095
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1096
|
-
if (path === LOCAL_SERVICE_ROUTES.agents) {
|
|
1097
|
-
if (method === "GET") {
|
|
1098
|
-
sendJson(res, 200, runtime.getAgentsRegistry());
|
|
1099
|
-
return true;
|
|
1100
|
-
}
|
|
1101
|
-
if (method === "POST") {
|
|
1102
|
-
try {
|
|
1103
|
-
const body = (await readJsonBody(req));
|
|
1104
|
-
if (!body || typeof body !== "object") {
|
|
1105
|
-
sendError(res, 400, "Request body must be a JSON object.");
|
|
1106
|
-
return true;
|
|
1107
|
-
}
|
|
1108
|
-
if (!body.name || typeof body.name !== "string") {
|
|
1109
|
-
sendError(res, 400, "Missing required field: name");
|
|
1110
|
-
return true;
|
|
1111
|
-
}
|
|
1112
|
-
if (!body.display_name || typeof body.display_name !== "string") {
|
|
1113
|
-
sendError(res, 400, "Missing required field: display_name");
|
|
1114
|
-
return true;
|
|
1115
|
-
}
|
|
1116
|
-
if (!body.command || typeof body.command !== "string") {
|
|
1117
|
-
sendError(res, 400, "Missing required field: command");
|
|
1118
|
-
return true;
|
|
1119
|
-
}
|
|
1120
|
-
const updated = runtime.registerCustomAgent({
|
|
1121
|
-
name: body.name,
|
|
1122
|
-
display_name: body.display_name,
|
|
1123
|
-
command: body.command,
|
|
1124
|
-
});
|
|
1125
|
-
sendJson(res, 201, updated);
|
|
1126
|
-
}
|
|
1127
|
-
catch (error) {
|
|
1128
|
-
sendError(res, 400, error instanceof Error ? error.message : String(error));
|
|
1129
|
-
}
|
|
1130
|
-
return true;
|
|
1131
|
-
}
|
|
1132
|
-
}
|
|
1133
|
-
if (method === "GET" && path === `${LOCAL_SERVICE_ROUTES.agents}/roles`) {
|
|
1134
|
-
sendJson(res, 200, {
|
|
1135
|
-
kind: "interf-canonical-roles",
|
|
1136
|
-
version: 1,
|
|
1137
|
-
roles: ["extractor", "summarizer", "structurer", "verifier", "general"],
|
|
1138
|
-
});
|
|
1139
|
-
return true;
|
|
1140
|
-
}
|
|
1141
|
-
if (path === `${LOCAL_SERVICE_ROUTES.agents}/role-map`) {
|
|
1142
|
-
if (method === "GET") {
|
|
1143
|
-
const snapshot = runtime.getAgentsRegistry();
|
|
1144
|
-
sendJson(res, 200, {
|
|
1145
|
-
role_map: snapshot.role_map,
|
|
1146
|
-
active_agent: snapshot.active_agent,
|
|
1147
|
-
});
|
|
1148
|
-
return true;
|
|
1149
|
-
}
|
|
1150
|
-
if (method === "PATCH") {
|
|
1151
|
-
try {
|
|
1152
|
-
const body = (await readJsonBody(req));
|
|
1153
|
-
if (!body || typeof body !== "object") {
|
|
1154
|
-
sendError(res, 400, "Request body must be a JSON object.");
|
|
1155
|
-
return true;
|
|
1156
|
-
}
|
|
1157
|
-
const patch = {};
|
|
1158
|
-
for (const [role, value] of Object.entries(body)) {
|
|
1159
|
-
if (typeof value !== "string") {
|
|
1160
|
-
sendError(res, 400, `Role-map values must be strings (or "" to clear). Got ${typeof value} for role "${role}".`);
|
|
1161
|
-
return true;
|
|
1162
|
-
}
|
|
1163
|
-
patch[role] = value;
|
|
1164
|
-
}
|
|
1165
|
-
const updated = runtime.patchAgentsRoleMap(patch);
|
|
1166
|
-
sendJson(res, 200, updated);
|
|
1167
|
-
}
|
|
1168
|
-
catch (error) {
|
|
1169
|
-
sendError(res, 400, error instanceof Error ? error.message : String(error));
|
|
1170
|
-
}
|
|
1171
|
-
return true;
|
|
1172
|
-
}
|
|
1173
|
-
}
|
|
1174
|
-
// /v1/agents/<name> — only DELETE; built-in names return 400.
|
|
1175
|
-
const agentMatch = path.match(/^\/v1\/agents\/([^/]+)$/);
|
|
1176
|
-
if (agentMatch?.[1] && method === "DELETE") {
|
|
1177
|
-
const name = decodeURIComponent(agentMatch[1]);
|
|
1178
|
-
try {
|
|
1179
|
-
const updated = runtime.unregisterCustomAgent(name);
|
|
1180
|
-
sendJson(res, 200, updated);
|
|
1181
|
-
}
|
|
1182
|
-
catch (error) {
|
|
1183
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1184
|
-
// Rejecting a built-in unregister is a 400 (bad request, the
|
|
1185
|
-
// caller is asking for something the contract doesn't allow).
|
|
1186
|
-
const status = /built-in agent/.test(message) ? 400 : 404;
|
|
1187
|
-
sendError(res, status, message);
|
|
1188
|
-
}
|
|
1189
|
-
return true;
|
|
1190
|
-
}
|
|
1191
|
-
if (method === "POST" && path === LOCAL_SERVICE_ROUTES.openPath) {
|
|
1192
|
-
const body = OpenPathRequestSchema.parse(await readJsonBody(req));
|
|
1193
|
-
// Permit opening:
|
|
1194
|
-
// - any registered preparation root or its bound source folder
|
|
1195
|
-
// - the user buildPlan library at `~/.interf/build-plans/`
|
|
1196
|
-
// - the bundled built-in buildPlan root inside the installed package
|
|
1197
|
-
// (so SKILL.md / contract files in `interf-default` open correctly)
|
|
1198
|
-
const allowedRoots = [];
|
|
1199
|
-
for (const stored of listStoredPreparations()) {
|
|
1200
|
-
allowedRoots.push(stored.prepDataDir);
|
|
1201
|
-
if (stored.source.locator)
|
|
1202
|
-
allowedRoots.push(stored.source.locator);
|
|
1203
|
-
}
|
|
1204
|
-
allowedRoots.push(userBuildPlansRoot());
|
|
1205
|
-
allowedRoots.push(packageRoot()); // covers <pkg>/public-repo/build-plans/...
|
|
1206
|
-
const openedPath = await openLocalPath(allowedRoots, body.path);
|
|
1207
|
-
sendJson(res, 202, { opened: true, path: openedPath });
|
|
1208
|
-
return true;
|
|
1209
|
-
}
|
|
1210
|
-
return false;
|
|
1211
|
-
}
|
|
1212
|
-
export function createLocalServiceServer(runtime) {
|
|
1213
|
-
return createServer((req, res) => {
|
|
1214
|
-
void (async () => {
|
|
1215
|
-
// Pre-attach a CORS context so static-asset GETs and the 404
|
|
1216
|
-
// fallback emit the right headers even before routeApi has a
|
|
1217
|
-
// chance to set its own. The OPTIONS preflight handling and the
|
|
1218
|
-
// mutating-buildPlan guards still happen inside routeApi.
|
|
1219
|
-
const origin = originHeaderValue(req);
|
|
1220
|
-
const allowed = buildAllowedOrigins(runtime.host, runtime.port);
|
|
1221
|
-
attachResponseContext(res, { cors: corsHeadersFor(origin, allowed) });
|
|
1222
|
-
try {
|
|
1223
|
-
const routed = await routeApi(req, res, runtime);
|
|
1224
|
-
if (routed)
|
|
1225
|
-
return;
|
|
1226
|
-
if (sendInterfUiAsset(req, res, runtime))
|
|
1227
|
-
return;
|
|
1228
|
-
sendText(res, 404, "Not found.\n");
|
|
1229
|
-
}
|
|
1230
|
-
catch (error) {
|
|
1231
|
-
sendError(res, 500, error instanceof Error ? error.message : String(error));
|
|
1232
|
-
}
|
|
1233
|
-
})();
|
|
1234
|
-
});
|
|
1235
|
-
}
|
|
1236
|
-
function resolvePort(optionPort) {
|
|
1237
|
-
if (typeof optionPort === "number")
|
|
1238
|
-
return { port: optionPort, pinned: true };
|
|
1239
|
-
const envPort = Number.parseInt(process.env.INTERF_SERVICE_PORT ?? "", 10);
|
|
1240
|
-
if (Number.isInteger(envPort))
|
|
1241
|
-
return { port: envPort, pinned: true };
|
|
1242
|
-
return { port: LOCAL_SERVICE_DEFAULT_PORT, pinned: false };
|
|
1243
|
-
}
|
|
1244
|
-
const LOCAL_SERVICE_PORT_FALLBACK_RANGE = 50;
|
|
1245
|
-
async function listenWithFallback(server, host, startPort, pinned) {
|
|
1246
|
-
const maxPort = pinned ? startPort : startPort + LOCAL_SERVICE_PORT_FALLBACK_RANGE;
|
|
1247
|
-
for (let port = startPort; port <= maxPort; port += 1) {
|
|
1248
|
-
try {
|
|
1249
|
-
await new Promise((resolveListen, rejectListen) => {
|
|
1250
|
-
const onError = (error) => {
|
|
1251
|
-
server.off("listening", onListening);
|
|
1252
|
-
rejectListen(error);
|
|
1253
|
-
};
|
|
1254
|
-
const onListening = () => {
|
|
1255
|
-
server.off("error", onError);
|
|
1256
|
-
resolveListen();
|
|
1257
|
-
};
|
|
1258
|
-
server.once("error", onError);
|
|
1259
|
-
server.once("listening", onListening);
|
|
1260
|
-
server.listen(port, host);
|
|
1261
|
-
});
|
|
1262
|
-
return port;
|
|
1263
|
-
}
|
|
1264
|
-
catch (error) {
|
|
1265
|
-
const code = error?.code;
|
|
1266
|
-
if (code !== "EADDRINUSE" || port === maxPort)
|
|
1267
|
-
throw error;
|
|
1268
|
-
// Retry on the next port; the server will emit a fresh "listening" event on the next attempt.
|
|
1269
|
-
}
|
|
1270
|
-
}
|
|
1271
|
-
throw new Error(`Could not bind a free port between ${startPort} and ${maxPort}.`);
|
|
1272
|
-
}
|
|
1273
|
-
export async function startLocalService(options = {}) {
|
|
1274
|
-
const requestedPort = typeof options.searchFromPort === "number"
|
|
1275
|
-
? { port: options.searchFromPort, pinned: false }
|
|
1276
|
-
: resolvePort(options.port);
|
|
1277
|
-
const requestedHost = options.host ?? process.env.INTERF_SERVICE_HOST ?? LOCAL_SERVICE_DEFAULT_HOST;
|
|
1278
|
-
// Reject non-loopback host bindings before we even configure anything.
|
|
1279
|
-
// This guards against `INTERF_SERVICE_HOST=0.0.0.0` env injection or a
|
|
1280
|
-
// call site that passes a LAN IP. The schema-level refinement also
|
|
1281
|
-
// rejects, but we want a clearer error message.
|
|
1282
|
-
if (!LOCAL_SERVICE_LOOPBACK_HOSTS.includes(requestedHost)) {
|
|
1283
|
-
throw new Error(`Refusing to bind local service to non-loopback host '${requestedHost}'. ` +
|
|
1284
|
-
`Use one of ${LOCAL_SERVICE_LOOPBACK_HOSTS.join(", ")} ` +
|
|
1285
|
-
`(adjust the --host flag or INTERF_SERVICE_HOST env var).`);
|
|
1286
|
-
}
|
|
1287
|
-
const config = LocalServiceConfigSchema.parse({
|
|
1288
|
-
host: requestedHost,
|
|
1289
|
-
port: requestedPort.port,
|
|
1290
|
-
});
|
|
1291
|
-
const rootPath = options.rootPath ?? process.cwd();
|
|
1292
|
-
const resolvedRoot = resolve(rootPath);
|
|
1293
|
-
// 0.13 auth-token policy: default to `null` (no token) on loopback. CORS
|
|
1294
|
-
// + loopback bind enforces the security boundary. Callers can opt in by
|
|
1295
|
-
// passing `"auto"` (generate fresh) or a pinned string (tests).
|
|
1296
|
-
const authToken = (() => {
|
|
1297
|
-
if (options.authToken === null)
|
|
1298
|
-
return null;
|
|
1299
|
-
if (options.authToken === "auto")
|
|
1300
|
-
return createLocalServiceAuthToken();
|
|
1301
|
-
if (typeof options.authToken === "string")
|
|
1302
|
-
return options.authToken;
|
|
1303
|
-
return null;
|
|
1304
|
-
})();
|
|
1305
|
-
const runtime = createLocalServiceRuntime({
|
|
1306
|
-
rootPath: resolvedRoot,
|
|
1307
|
-
host: config.host,
|
|
1308
|
-
port: config.port,
|
|
1309
|
-
packageVersion: options.packageVersion,
|
|
1310
|
-
handlers: options.handlers,
|
|
1311
|
-
...(authToken ? { authToken } : {}),
|
|
1312
|
-
// 0.17 — forward cloud-variant injection points. Local default
|
|
1313
|
-
// ignores idempotencyStore / runLeaseStore (the runtime's
|
|
1314
|
-
// in-process maps stay authoritative); they're accepted on the
|
|
1315
|
-
// options surface so cloud forks have a stable target.
|
|
1316
|
-
...(options.billingEventSink ? { billingEventSink: options.billingEventSink } : {}),
|
|
1317
|
-
...(options.idempotencyStore ? { idempotencyStore: options.idempotencyStore } : {}),
|
|
1318
|
-
...(options.runLeaseStore ? { runLeaseStore: options.runLeaseStore } : {}),
|
|
1319
|
-
});
|
|
1320
|
-
// 0.17 — token validator (B4.3). Local default: undefined. Cloud
|
|
1321
|
-
// variants pass an async per-account validator. Stashed on the
|
|
1322
|
-
// runtime via `setTokenValidator` so `isAuthorizedMutation` can
|
|
1323
|
-
// pick it up; the local engine ignores it (the static bearer
|
|
1324
|
-
// check still runs).
|
|
1325
|
-
if (options.tokenValidator) {
|
|
1326
|
-
runtime.setTokenValidator(options.tokenValidator);
|
|
1327
|
-
}
|
|
1328
|
-
// Rehydrate saved preparations so subsequent
|
|
1329
|
-
// build / test / readiness calls find them after a service restart.
|
|
1330
|
-
try {
|
|
1331
|
-
rehydratePreparations(runtime);
|
|
1332
|
-
}
|
|
1333
|
-
catch {
|
|
1334
|
-
// best effort
|
|
1335
|
-
}
|
|
1336
|
-
const server = createLocalServiceServer(runtime);
|
|
1337
|
-
const boundPort = await listenWithFallback(server, config.host, config.port, requestedPort.pinned);
|
|
1338
|
-
runtime.setBoundPort(boundPort);
|
|
1339
|
-
const health = runtime.health();
|
|
1340
|
-
const url = health.service_url;
|
|
1341
|
-
const startedAt = health.instance_started_at ?? new Date().toISOString();
|
|
1342
|
-
// Central stop-lookup registry — one entry per running instance.
|
|
1343
|
-
// (Server-side write only; CLI no longer reads this.)
|
|
1344
|
-
const writeRegistryEntry = () => {
|
|
1345
|
-
try {
|
|
1346
|
-
registerServiceLocally(ServiceRegistryEntrySchema.parse({
|
|
1347
|
-
pid: process.pid,
|
|
1348
|
-
host: config.host,
|
|
1349
|
-
port: boundPort,
|
|
1350
|
-
url,
|
|
1351
|
-
started_at: startedAt,
|
|
1352
|
-
...(authToken ? { auth_token: authToken } : {}),
|
|
1353
|
-
}));
|
|
1354
|
-
}
|
|
1355
|
-
catch {
|
|
1356
|
-
// best effort: registry corruption shouldn't take the service down
|
|
1357
|
-
}
|
|
1358
|
-
};
|
|
1359
|
-
writeRegistryEntry();
|
|
1360
|
-
runtime.setOnRegistryChanged(writeRegistryEntry);
|
|
1361
|
-
// Write the single-record CLI connection so `interf prep`,
|
|
1362
|
-
// `interf build`, etc. can find this engine without a pointer file.
|
|
1363
|
-
try {
|
|
1364
|
-
writeConnection({ url, auth_token: authToken });
|
|
1365
|
-
}
|
|
1366
|
-
catch {
|
|
1367
|
-
// best effort: connection file is convenience, not correctness
|
|
1368
|
-
}
|
|
1369
|
-
return {
|
|
1370
|
-
runtime,
|
|
1371
|
-
server,
|
|
1372
|
-
url,
|
|
1373
|
-
close: () => new Promise((resolveClose, rejectClose) => {
|
|
1374
|
-
runtime.setOnRegistryChanged(null);
|
|
1375
|
-
server.close((error) => {
|
|
1376
|
-
if (error) {
|
|
1377
|
-
rejectClose(error);
|
|
1378
|
-
return;
|
|
1379
|
-
}
|
|
1380
|
-
try {
|
|
1381
|
-
unregisterService(process.pid);
|
|
1382
|
-
}
|
|
1383
|
-
catch {
|
|
1384
|
-
// best effort
|
|
1385
|
-
}
|
|
1386
|
-
try {
|
|
1387
|
-
// Clear connection record only if it still points at us.
|
|
1388
|
-
const current = readActiveConnection();
|
|
1389
|
-
if (current?.url === url)
|
|
1390
|
-
clearConnection();
|
|
1391
|
-
}
|
|
1392
|
-
catch {
|
|
1393
|
-
// best effort
|
|
1394
|
-
}
|
|
1395
|
-
resolveClose();
|
|
1396
|
-
});
|
|
1397
|
-
}),
|
|
1398
|
-
};
|
|
1399
|
-
}
|