@moretea-labs/forge 1.6.0 → 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +4 -0
- package/CHANGELOG.md +39 -0
- package/CLAUDE.md +4 -0
- package/README.md +2 -2
- package/README.zh-CN.md +1 -1
- package/SKILL.md +2 -2
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/agents/openai.yaml +1 -1
- package/assets/hooks/hook-input.sh +11 -8
- package/assets/hooks/lib/session-state.sh +10 -7
- package/assets/hooks/lib/workflow-state.sh +13 -177
- package/assets/hooks/post-tool-observer.sh +8 -2
- package/assets/hooks/prompt-guard.sh +5 -81
- package/assets/hooks/session-start-context.sh +56 -1
- package/assets/partials/05-workflow.partial.md +1 -1
- package/assets/partials-agents/02-operating-mode.partial.md +2 -0
- package/assets/partials-agents/03-orchestration.partial.md +1 -1
- package/assets/plugin-registry.v1.json +3 -3
- package/assets/reference-configs/document-generation.md +1 -1
- package/assets/reference-configs/harness-overview.md +1 -1
- package/assets/reference-configs/sprint-contracts.md +3 -3
- package/assets/skill-version.json +2 -2
- package/assets/templates/contract.template.md +2 -7
- package/assets/templates/helpers/capture-plan.sh +2 -2
- package/assets/templates/helpers/check-architecture-sync.sh +17 -0
- package/assets/templates/helpers/check-task-workflow.sh +4 -4
- package/assets/templates/helpers/contract-worktree.sh +1 -21
- package/assets/templates/helpers/ensure-task-workflow.sh +11 -36
- package/assets/templates/helpers/inspect-project-state.ts +8 -6
- package/assets/templates/helpers/migrate-project-template.sh +22 -45
- package/assets/templates/helpers/migrate-workflow-docs.ts +9 -7
- package/assets/templates/helpers/new-plan.sh +1 -1
- package/assets/templates/helpers/plan-to-todo.sh +12 -37
- package/assets/templates/helpers/ship-worktrees.sh +2 -29
- package/assets/templates/helpers/verify-contract.sh +1 -111
- package/assets/templates/helpers/verify-sprint.sh +3 -180
- package/assets/templates/helpers/workflow-contract.ts +5 -1
- package/assets/templates/plan.template.md +1 -1
- package/assets/templates/review.template.md +9 -34
- package/assets/workflow-contract.v1.json +10 -19
- package/docs/README.md +10 -10
- package/docs/architecture/CURRENT.md +178 -0
- package/docs/architecture/EVOLUTION.md +42 -0
- package/docs/architecture/current/README.md +4 -156
- package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +2 -2
- package/docs/architecture/decisions/20260823-immediate-controller-relay.md +53 -0
- package/docs/architecture/decisions/20260826-browser-runtime-v2.md +73 -0
- package/docs/architecture/history/README.md +7 -0
- package/docs/architecture/{chatgpt-handoff-facade.md → history/chatgpt-handoff-facade.md} +4 -0
- package/docs/architecture/{global-hook-runtime.md → history/global-hook-runtime.md} +7 -3
- package/docs/architecture/{ios-semantic-automation-provider-v2.md → history/ios-semantic-automation-provider-v2.md} +6 -2
- package/docs/architecture/history.md +3 -34
- package/docs/architecture/index.md +15 -98
- package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +1 -1
- package/docs/architecture/reviews/2026-08-22-semantic-harness-convergence-acceptance.md +39 -0
- package/docs/architecture/versions/1.6.md +38 -0
- package/docs/architecture/versions/1.7.md +41 -0
- package/docs/forge-chatgpt-browser-engine.md +2 -2
- package/docs/forge-chatgpt-mcp-setup.md +4 -28
- package/docs/forge-chatgpt-supervised-automation.md +3 -3
- package/docs/forge-github-issue-launcher.md +1 -1
- package/docs/forge-plugin-management.md +1 -1
- package/docs/forge-runtime-self-healing-loop.md +1 -1
- package/docs/operations/controller-browser-plugin.md +26 -6
- package/docs/operations/controller-performance-and-502.md +143 -0
- package/docs/operations/controller-reliability-runbook.md +1 -1
- package/docs/operations/features.md +2 -2
- package/docs/operations/github-repository.md +1 -1
- package/docs/operations/homebrew.md +2 -2
- package/docs/operations/mcp-tool-exposure.md +5 -3
- package/docs/wiki/Architecture.md +6 -6
- package/docs/wiki/Implementation.md +2 -2
- package/docs/wiki/Integrations.md +2 -2
- package/docs/wiki/Work-Lifecycle.md +1 -1
- package/package.json +33 -11
- package/scripts/activate-source-baseline.ts +17 -13
- package/scripts/architecture-event.ts +2 -820
- package/scripts/architecture-queue.sh +2 -645
- package/scripts/archive-architecture-request.sh +2 -242
- package/scripts/archive-workflow.sh +1 -291
- package/scripts/benchmark-background-check-overlap.ts +228 -0
- package/scripts/benchmark-mcp-host.sh +117 -0
- package/scripts/benchmark-mcp-transport.ts +170 -0
- package/scripts/benchmark-quality-harness.ts +146 -0
- package/scripts/benchmark-route-session-concurrency.ts +207 -6
- package/scripts/benchmark-thin-harness-mcp-e2e.ts +7 -1
- package/scripts/benchmark-typescript-navigation.ts +186 -0
- package/scripts/benchmark-work-efficiency.ts +334 -0
- package/scripts/capability-config.ts +2 -392
- package/scripts/capability-resolver.ts +2 -503
- package/scripts/capture-plan.sh +1 -332
- package/scripts/check-agent-tooling.sh +2 -1558
- package/scripts/check-architecture-sync.sh +2 -371
- package/scripts/check-brain-manifest.sh +2 -253
- package/scripts/check-context-files.sh +2 -47
- package/scripts/check-deploy-sql-order.sh +2 -115
- package/scripts/check-mcp-compatibility.ts +14 -0
- package/scripts/check-runtime-architecture.mjs +349 -79
- package/scripts/check-skill-version.ts +3 -191
- package/scripts/check-source-duplication.mjs +61 -0
- package/scripts/check-stable-baseline.ts +92 -0
- package/scripts/check-task-sync.sh +2 -63
- package/scripts/check-task-workflow.sh +2 -1000
- package/scripts/codex-handoff-resume.sh +2 -267
- package/scripts/context-contract-sync.sh +2 -514
- package/scripts/contract-run.ts +2 -463
- package/scripts/contract-worktree.sh +1 -722
- package/scripts/ensure-task-workflow.sh +1 -1225
- package/scripts/host-temporary-cloud-mcp-e2e.sh +265 -0
- package/scripts/inspect-project-state.ts +4 -279
- package/scripts/install-standalone-recovery.ts +24 -0
- package/scripts/lib/project-init-lib.sh +22 -46
- package/scripts/lib/run-forge-helper.sh +19 -0
- package/scripts/lib/run-forge-helper.ts +26 -0
- package/scripts/migrate-project-template.sh +1 -1189
- package/scripts/migrate-workflow-docs.ts +4 -412
- package/scripts/new-plan.sh +1 -180
- package/scripts/new-spec.sh +1 -47
- package/scripts/new-sprint.sh +1 -46
- package/scripts/plan-to-todo.sh +1 -796
- package/scripts/prepare-codex-handoff.sh +2 -201
- package/scripts/prepare-handoff.sh +1 -78
- package/scripts/refresh-current-status.sh +2 -467
- package/scripts/run-governed-gate.ts +5 -1
- package/scripts/select-agent-context-blocks.sh +2 -64
- package/scripts/ship-worktrees.sh +1 -558
- package/scripts/smoke-ios-physical-device.ts +4 -2
- package/scripts/smoke-runtime-recovery.ts +1 -35
- package/scripts/smoke-schedule-engine.ts +1 -20
- package/scripts/sprint-backlog.sh +1 -834
- package/scripts/stage-runtime-release.ts +2 -1
- package/scripts/summarize-failures.sh +2 -123
- package/scripts/switch-plan.sh +1 -150
- package/scripts/sync-brain-docs.sh +2 -379
- package/scripts/sync-generated-authority.mjs +175 -0
- package/scripts/verify-contract.sh +2 -703
- package/scripts/verify-forge-runtime.sh +0 -1
- package/scripts/verify-sprint.sh +1 -677
- package/scripts/workflow-contract.ts +4 -205
- package/scripts/workstream-sync.sh +2 -261
- package/src/cli/agent-jobs/executor-health.ts +1 -1
- package/src/cli/commands/init-hook.ts +3 -3
- package/src/cli/commands/mcp.ts +9 -5
- package/src/cli/commands/plugin.ts +12 -3
- package/src/cli/commands/recovery.ts +25 -3
- package/src/cli/commands/runtime.ts +55 -3
- package/src/cli/commands/security.ts +12 -1
- package/src/cli/commands/setup-profile.ts +31 -21
- package/src/cli/controller/check-runner.ts +113 -5
- package/src/cli/controller/composite-result.ts +1 -1
- package/src/cli/controller/context/focus.ts +51 -0
- package/src/cli/controller/context/instruction-context.ts +114 -0
- package/src/cli/controller/context/known-paths.ts +76 -0
- package/src/cli/controller/context/query-planning.ts +110 -0
- package/src/cli/controller/context/rule-resolution.ts +532 -0
- package/src/cli/controller/context/source-materializer.ts +278 -0
- package/src/cli/controller/context/structural.ts +16 -0
- package/src/cli/controller/context/types.ts +286 -0
- package/src/cli/controller/context-pack.ts +828 -487
- package/src/cli/controller/launch-agents.ts +77 -2
- package/src/cli/controller/operational-plan.ts +2 -2
- package/src/cli/controller/work-mode.ts +6 -4
- package/src/cli/editing/edit-session.ts +60 -7
- package/src/cli/github/contracts.ts +16 -0
- package/src/cli/github/github.ts +135 -16
- package/src/cli/github/session.ts +2 -1
- package/src/cli/installer/managed-entries.ts +15 -1
- package/src/cli/local-bridge/console-automations.ts +54 -78
- package/src/cli/local-bridge/console-view-models.ts +1 -35
- package/src/cli/local-bridge/facade-api.ts +37 -264
- package/src/cli/local-bridge/frontend/src/App.tsx +3 -3
- package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +23 -13
- package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +37 -4
- package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +6 -4
- package/src/cli/local-bridge/frontend/src/lib/api.ts +1 -6
- package/src/cli/local-bridge/frontend/src/lib/routes.ts +1 -1
- package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +2 -2
- package/src/cli/local-bridge/frontend/src/styles.css +1 -1
- package/src/cli/local-bridge/frontend/src/types.ts +5 -8
- package/src/cli/local-bridge/job-store.ts +1 -490
- package/src/cli/local-bridge/server.ts +2 -515
- package/src/cli/local-bridge/ui-dist/app.css +1 -1
- package/src/cli/local-bridge/ui-dist/app.js +3703 -3757
- package/src/cli/mcp/auth.ts +5 -5
- package/src/cli/mcp/instructions.ts +3 -2
- package/src/cli/mcp/legacy-tool-service.ts +10 -222
- package/src/cli/mcp/multi-repository.ts +16 -5
- package/src/cli/mcp/oauth.ts +175 -26
- package/src/cli/mcp/post-finalize-work-attribution.ts +150 -0
- package/src/cli/mcp/repository-tools.ts +282 -146
- package/src/cli/mcp/server.ts +586 -116
- package/src/cli/mcp/tool-capability-cache.ts +1 -1
- package/src/cli/mcp/tool-contract.ts +14 -0
- package/src/cli/mcp/toolset-selection.ts +1 -1
- package/src/cli/mcp/toolset.ts +44 -7
- package/src/cli/mcp/transports/http.ts +124 -14
- package/src/cli/mcp/transports/session-registry.ts +43 -5
- package/src/cli/mcp/types.ts +3 -3
- package/src/cli/repositories/command-classifier.ts +103 -21
- package/src/cli/repositories/command-executor.ts +78 -526
- package/src/cli/repositories/command-process.ts +108 -0
- package/src/cli/repositories/command-scope.ts +99 -41
- package/src/cli/repositories/controller-home.ts +7 -8
- package/src/cli/repositories/git-command-executor.ts +2 -0
- package/src/cli/repositories/locks.ts +53 -16
- package/src/cli/repositories/repository-snapshot.ts +124 -0
- package/src/cli/repositories/selected-path-actions.ts +17 -9
- package/src/cli/repositories/structured-git.ts +118 -6
- package/src/cli/repository/inspector.ts +563 -65
- package/src/cli/repository/session-cache.ts +58 -17
- package/src/cli/v81-entry.ts +1 -4
- package/src/effects/fs-transaction.ts +85 -17
- package/src/runtime/context/codegraph-read-provider.ts +233 -2
- package/src/runtime/context/codegraph-sidecar.cjs +74 -40
- package/src/runtime/context/generic-lsp-provider.ts +355 -0
- package/src/runtime/context/lsp-client.ts +283 -0
- package/src/runtime/context/semantic-navigation-contract.ts +61 -0
- package/src/runtime/context/semantic-navigation.ts +223 -0
- package/src/runtime/context/swift-navigation.ts +565 -0
- package/src/runtime/context/typescript-navigation.ts +226 -0
- package/src/runtime/control-plane/dispatch-priority.ts +28 -0
- package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +48 -16
- package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +86 -24
- package/src/runtime/control-plane/execution/repository-work-attribution.ts +106 -0
- package/src/runtime/control-plane/execution/retained-work-resume.ts +203 -0
- package/src/runtime/control-plane/execution/session-store.ts +18 -0
- package/src/runtime/control-plane/execution/verification-evidence.ts +58 -3
- package/src/runtime/control-plane/execution/work-completion-authority.ts +58 -0
- package/src/runtime/control-plane/execution/work-evidence-policy.ts +200 -0
- package/src/runtime/control-plane/execution/work-execution-support.ts +386 -0
- package/src/runtime/control-plane/execution/work-finalization-service.ts +1913 -0
- package/src/runtime/control-plane/execution/work-handle-authority.ts +96 -0
- package/src/runtime/control-plane/execution/work-handle-store.ts +27 -2
- package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +258 -0
- package/src/runtime/control-plane/execution/work-operation-service.ts +468 -0
- package/src/runtime/control-plane/execution/work-path-scope.ts +37 -0
- package/src/runtime/control-plane/execution/work-preparation-service.ts +479 -0
- package/src/runtime/control-plane/execution/work-process-evidence.ts +129 -0
- package/src/runtime/control-plane/execution/work-remote-delivery.ts +215 -0
- package/src/runtime/control-plane/execution/work-task-receipt.ts +22 -18
- package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +156 -11
- package/src/runtime/control-plane/execution/work-validation-reconciler.ts +22 -14
- package/src/runtime/control-plane/execution/work-verification-context.ts +56 -0
- package/src/runtime/control-plane/execution/work-verification-service.ts +388 -0
- package/src/runtime/control-plane/execution/work-verification-snapshot.ts +90 -9
- package/src/runtime/control-plane/facade/capability-registry.ts +129 -25
- package/src/runtime/control-plane/facade/controller-round-relay.ts +1118 -0
- package/src/runtime/control-plane/facade/controller-session-store.ts +302 -12
- package/src/runtime/control-plane/facade/goal-workloop-access.ts +17 -1
- package/src/runtime/control-plane/facade/goal-workloop.ts +928 -190
- package/src/runtime/control-plane/facade/index.ts +3 -0
- package/src/runtime/control-plane/facade/plan-contract-store.ts +275 -42
- package/src/runtime/control-plane/facade/repository-work-admission.ts +147 -0
- package/src/runtime/control-plane/facade/requirement-authority.ts +83 -0
- package/src/runtime/control-plane/facade/semantic-admission.ts +205 -0
- package/src/runtime/control-plane/facade/suggested-actions.ts +5 -1
- package/src/runtime/control-plane/facade/types.ts +90 -10
- package/src/runtime/control-plane/facade/work-contract-store.ts +157 -261
- package/src/runtime/control-plane/facade/work-state-machine.ts +250 -0
- package/src/runtime/control-plane/generated-cache-retention.ts +271 -0
- package/src/runtime/control-plane/global-scheduler/config.ts +44 -0
- package/src/runtime/control-plane/global-scheduler/dispatch-capacity.ts +80 -0
- package/src/runtime/control-plane/global-scheduler/durable-admission.ts +46 -0
- package/src/runtime/control-plane/global-scheduler/maintenance.ts +152 -0
- package/src/runtime/control-plane/global-scheduler/projection-refresh.ts +84 -0
- package/src/runtime/control-plane/global-scheduler/reconciliation.ts +3 -3
- package/src/runtime/control-plane/global-scheduler/scheduler.ts +278 -543
- package/src/runtime/control-plane/global-scheduler/source-scan.ts +59 -0
- package/src/runtime/control-plane/global-scheduler/state.ts +88 -0
- package/src/runtime/control-plane/global-scheduler/worker-attachment.ts +57 -0
- package/src/runtime/control-plane/global-scheduler/worker-exit-decision.ts +38 -0
- package/src/runtime/control-plane/global-scheduler/worker-exit-reconciler.ts +175 -0
- package/src/runtime/control-plane/global-scheduler/worker-launch.ts +111 -0
- package/src/runtime/control-plane/global-scheduler/worker-lifecycle-store.ts +64 -0
- package/src/runtime/control-plane/global-scheduler/worker-lifecycle.ts +150 -0
- package/src/runtime/control-plane/global-scheduler/worker-process.ts +119 -0
- package/src/runtime/control-plane/global-scheduler/worker-stderr.ts +60 -0
- package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +63 -0
- package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +365 -117
- package/src/runtime/control-plane/launcher/launch-reservation-store.ts +89 -0
- package/src/runtime/control-plane/launcher/thin-launcher.ts +244 -11
- package/src/runtime/control-plane/persistence/controller-home-migration.ts +487 -0
- package/src/runtime/control-plane/persistence/requirement-store.ts +0 -12
- package/src/runtime/control-plane/persistence/sqlite-store.ts +33 -2
- package/src/runtime/control-plane/release-retention.ts +62 -2
- package/src/runtime/control-plane/repo-actor/registry.ts +4 -2
- package/src/runtime/control-plane/routing/route-policy.ts +24 -21
- package/src/runtime/control-plane/routing/workspace-admission.ts +70 -0
- package/src/runtime/control-plane/runtime-status-client.ts +0 -2
- package/src/runtime/diagnostics/mcp-timing.ts +13 -0
- package/src/runtime/diagnostics/mcp-transport-benchmark.ts +129 -0
- package/src/runtime/diagnostics/performance.ts +24 -1
- package/src/runtime/evidence/event-ledger.ts +1 -1
- package/src/runtime/evidence/process-check-execution.ts +18 -0
- package/src/runtime/execution/jobs/child-reference.ts +6 -2
- package/src/runtime/execution/jobs/store.ts +58 -4
- package/src/runtime/execution/jobs/types.ts +1 -1
- package/src/runtime/execution/managed-workspace.ts +203 -1
- package/src/runtime/execution/process-runtime/check-facade.ts +51 -4
- package/src/runtime/execution/process-runtime/check-result.ts +88 -0
- package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +7 -1
- package/src/runtime/execution/process-runtime/check-scheduling.ts +114 -0
- package/src/runtime/execution/process-runtime/command-facade.ts +367 -40
- package/src/runtime/execution/process-runtime/gc.ts +38 -6
- package/src/runtime/execution/process-runtime/lightweight-managed.ts +1201 -0
- package/src/runtime/execution/process-runtime/persisted-check.ts +51 -5
- package/src/runtime/execution/process-runtime/resource-claims.ts +10 -6
- package/src/runtime/execution/process-runtime/runtime.ts +53 -3
- package/src/runtime/execution/process-runtime/store.ts +1 -0
- package/src/runtime/execution/process-runtime/types.ts +4 -12
- package/src/runtime/execution/thin-harness/async-process.ts +12 -2
- package/src/runtime/execution/thin-harness/search-worker.ts +54 -5
- package/src/runtime/execution/work-activity.ts +21 -0
- package/src/runtime/execution/workers/executor.ts +18 -61
- package/src/runtime/gateway/mcp/execution-tools.ts +35 -1852
- package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +61 -0
- package/src/runtime/gateway/mcp/process-tools.ts +83 -37
- package/src/runtime/gateway/mcp/router.ts +186 -84
- package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +565 -0
- package/src/runtime/gateway/mcp/runtime-tools.ts +2378 -1947
- package/src/runtime/plugins/app-store-connect-adapter.ts +216 -47
- package/src/runtime/plugins/browser-adapter.ts +2662 -829
- package/src/runtime/plugins/browser-automation-service.ts +10 -0
- package/src/runtime/plugins/browser-handoff-host.ts +2 -25
- package/src/runtime/plugins/browser-handoff.ts +110 -5
- package/src/runtime/plugins/browser-macos-bridge.ts +701 -63
- package/src/runtime/plugins/browser-manifest-surface.ts +603 -0
- package/src/runtime/plugins/browser-native-session-service.ts +222 -0
- package/src/runtime/plugins/browser-node-bridge.ts +6 -34
- package/src/runtime/plugins/browser-provider-registry.ts +109 -0
- package/src/runtime/plugins/browser-runtime-contract.ts +154 -0
- package/src/runtime/plugins/browser-runtime.ts +193 -0
- package/src/runtime/plugins/browser-session-authority.ts +332 -0
- package/src/runtime/plugins/browser-session-store.ts +121 -0
- package/src/runtime/plugins/browser-session-types.ts +98 -0
- package/src/runtime/plugins/capability-authorization-grants.ts +354 -0
- package/src/runtime/plugins/desktop-operator-registration.ts +10 -4
- package/src/runtime/plugins/external-adapter.ts +358 -3
- package/src/runtime/plugins/external-unix-socket.ts +7 -1
- package/src/runtime/plugins/first-party-registry.ts +2 -0
- package/src/runtime/plugins/github-adapter.ts +125 -0
- package/src/runtime/plugins/ios/agent-device-provider.ts +1 -1
- package/src/runtime/plugins/ios/agent-device-typed-provider.ts +52 -9
- package/src/runtime/plugins/ios/remote-xpc-hid.ts +1171 -0
- package/src/runtime/plugins/ios-adapter.ts +106 -16
- package/src/runtime/plugins/ios-agent-device.ts +234 -64
- package/src/runtime/plugins/ios-physical-device.ts +1143 -392
- package/src/runtime/plugins/lightweight-action.ts +153 -0
- package/src/runtime/plugins/local-system-adapter.ts +123 -6
- package/src/runtime/plugins/macos-capability-broker.ts +47 -9
- package/src/runtime/plugins/plugin-action-sidecar.ts +59 -0
- package/src/runtime/plugins/plugin-management-adapter.ts +49 -8
- package/src/runtime/plugins/resend-adapter.ts +1 -1
- package/src/runtime/plugins/store.ts +330 -33
- package/src/runtime/plugins/types.ts +38 -3
- package/src/runtime/plugins/xiaohongshu-publish.ts +515 -0
- package/src/runtime/projections/git-status-sampler.ts +16 -16
- package/src/runtime/projections/materialized-view.ts +28 -10
- package/src/runtime/recovery/maintenance-executor.ts +426 -25
- package/src/runtime/root/mcp-transport.ts +180 -10
- package/src/runtime/root/package-connector-service.ts +230 -10
- package/src/runtime/root/package-runtime-release.ts +143 -17
- package/src/runtime/root/package-runtime-service.ts +461 -22
- package/src/runtime/root/release-manifest.ts +144 -18
- package/src/runtime/root/release-materialize.ts +125 -32
- package/src/runtime/root/release-store.ts +48 -11
- package/src/runtime/root/runtime.ts +23 -5
- package/src/runtime/root/service.ts +100 -29
- package/src/runtime/root/types.ts +12 -0
- package/src/runtime/safe-tooling/ios-development.ts +321 -63
- package/src/runtime/shared/trusted-executable.ts +62 -0
- package/src/runtime/shared/trusted-node-executable.ts +48 -0
- package/src/runtime/standalone-recovery/core.ts +1079 -240
- package/src/runtime/standalone-recovery/entry.ts +100 -24
- package/src/runtime/standalone-recovery/installer.ts +20 -4
- package/src/runtime/standalone-recovery/service-labels.ts +2 -0
- package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +160 -0
- package/src/runtime/workflow/schedules/browser-probe.ts +81 -3
- package/src/runtime/workflow/schedules/engine.ts +364 -202
- package/src/runtime/workflow/schedules/github-issue-watch.ts +158 -0
- package/src/runtime/workflow/schedules/settlement.ts +64 -17
- package/src/runtime/workflow/schedules/store.ts +209 -19
- package/src/runtime/workflow/schedules/types.ts +2 -4
- package/src/runtime/workflow/schedules/work-continuation.ts +26 -13
- package/src/testing/test-governance.ts +8 -40
- package/assets/reference-configs/heartbeat-triage.md +0 -57
- package/assets/templates/helpers/harness-trace-grade.sh +0 -150
- package/assets/templates/helpers/heartbeat-triage.sh +0 -361
- package/assets/templates/helpers/maintenance-triage.sh +0 -167
- package/docs/architecture/current/approved-target-architecture.zh-CN.md +0 -1501
- package/docs/architecture/current/architecture-invariants.md +0 -331
- package/docs/architecture/current/automation-and-schedule-engine.md +0 -459
- package/docs/architecture/current/codegraph-context-provider.md +0 -238
- package/docs/architecture/current/control-plane-authority-inventory.md +0 -94
- package/docs/architecture/current/controller-repository-decoupling.md +0 -148
- package/docs/architecture/current/dispatch-and-agent-strategy.md +0 -373
- package/docs/architecture/current/entity-model.md +0 -612
- package/docs/architecture/current/external-plugin-governance.md +0 -396
- package/docs/architecture/current/failure-recovery.md +0 -447
- package/docs/architecture/current/governance.md +0 -213
- package/docs/architecture/current/human-interaction-plane.md +0 -87
- package/docs/architecture/current/implementation-status.md +0 -128
- package/docs/architecture/current/job-and-run-lifecycle.md +0 -413
- package/docs/architecture/current/legacy-governance.md +0 -58
- package/docs/architecture/current/local-workspace-target-grants.md +0 -70
- package/docs/architecture/current/migration-roadmap.md +0 -105
- package/docs/architecture/current/multi-repository-execution.md +0 -371
- package/docs/architecture/current/operations-runbook.md +0 -45
- package/docs/architecture/current/peer-supercontroller-runtime.md +0 -32
- package/docs/architecture/current/personal-assistant-plugin-baseline.md +0 -206
- package/docs/architecture/current/repository-access-modes.md +0 -102
- package/docs/architecture/current/route-policy.md +0 -15
- package/docs/architecture/current/route-session-concurrency.md +0 -85
- package/docs/architecture/current/runtime-architecture-simplification.md +0 -214
- package/docs/architecture/current/runtime-directory-map.md +0 -49
- package/docs/architecture/current/runtime-health-and-resource-lifecycle.md +0 -85
- package/docs/architecture/current/scheduler-and-resource-claims.md +0 -422
- package/docs/architecture/current/session-aware-execution-and-authorization.md +0 -77
- package/docs/architecture/current/system-overview.md +0 -169
- package/docs/architecture/current/target-requirements-traceability.md +0 -202
- package/docs/architecture/current/thin-harness-governance-policy.md +0 -32
- package/docs/architecture/current/thin-harness-v1.md +0 -321
- package/docs/architecture/current/verification-and-release-gates.md +0 -391
- package/docs/forge-autonomous-goal-loop.md +0 -9
- package/docs/forge-provider-configuration.md +0 -7
- package/docs/operations/chatgpt-personal-assistant-runtime.md +0 -93
- package/docs/operations/gmail-assistant-routines.md +0 -186
- package/docs/operations/personal-assistant-operator-guide.md +0 -61
- package/docs/operations/provider-configuration.md +0 -31
- package/scripts/harness-trace-grade.sh +0 -150
- package/scripts/heartbeat-triage.sh +0 -361
- package/scripts/maintenance-triage.sh +0 -167
- package/src/cli/local-bridge/frontend/src/features/settings/SettingsView.tsx +0 -2
- package/src/cli/repositories/goal-registry.ts +0 -239
- package/src/runtime/assistant/action-proposals.ts +0 -279
- package/src/runtime/assistant/intent.ts +0 -395
- package/src/runtime/assistant/model-provider.ts +0 -460
- package/src/runtime/assistant/openapi.ts +0 -223
- package/src/runtime/assistant/policy.ts +0 -89
- package/src/runtime/assistant/readiness.ts +0 -258
- package/src/runtime/assistant/routine-runtime.ts +0 -477
- package/src/runtime/assistant/schedule-binding.ts +0 -171
- package/src/runtime/assistant/standing-grants.ts +0 -375
- package/src/runtime/assistant/store.ts +0 -243
- package/src/runtime/assistant/types.ts +0 -135
- package/src/runtime/control-plane/goal-loop/config-facade.ts +0 -700
- package/src/runtime/control-plane/goal-loop/config-store.ts +0 -570
- package/src/runtime/control-plane/goal-loop/config-types.ts +0 -213
- package/src/runtime/control-plane/goal-loop/executor-router.ts +0 -109
- package/src/runtime/control-plane/goal-loop/failure-taxonomy.ts +0 -149
- package/src/runtime/control-plane/goal-loop/goal-contract-store.ts +0 -304
- package/src/runtime/control-plane/goal-loop/goal-loop-engine.ts +0 -1001
- package/src/runtime/control-plane/goal-loop/handoff-packet.ts +0 -160
- package/src/runtime/control-plane/goal-loop/index.ts +0 -13
- package/src/runtime/control-plane/goal-loop/local-tools.ts +0 -290
- package/src/runtime/control-plane/goal-loop/policy-gates.ts +0 -203
- package/src/runtime/control-plane/goal-loop/provider-dispatch.ts +0 -93
- package/src/runtime/control-plane/goal-loop/provider-registry.ts +0 -545
- package/src/runtime/control-plane/goal-loop/types.ts +0 -378
- package/src/runtime/control-plane/startup-recovery.ts +0 -198
- package/src/runtime/gateway/mcp/resource-policy.ts +0 -186
- package/src/runtime/personal-assistant/gmail-triage-manager.ts +0 -231
- package/src/runtime/personal-assistant/reporting-runtime.ts +0 -344
- package/src/runtime/personal-assistant/triage-runtime.ts +0 -748
- package/src/runtime/workflow/findings/store.ts +0 -204
- package/src/runtime/workflow/findings/types.ts +0 -58
- package/src/runtime/workflow/portfolio/engine.ts +0 -20
- package/src/runtime/workflow/portfolio/store.ts +0 -185
- package/src/runtime/workflow/portfolio/types.ts +0 -29
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { createPrivateKey, sign } from 'crypto';
|
|
2
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
2
|
+
import { accessSync, constants as fsConstants, existsSync, mkdirSync, promises as fsPromises, readFileSync, statSync, writeFileSync } from 'fs';
|
|
3
3
|
import { dirname, join } from 'path';
|
|
4
|
+
import { CONTROLLER_SCOPE_REPO_ID, controllerSystemRoot } from '../../cli/repositories/controller-home';
|
|
4
5
|
import type {
|
|
5
6
|
AssistantPluginActionDescriptor,
|
|
6
7
|
AssistantPluginActionExecutionInput,
|
|
8
|
+
AssistantPluginBuildContext,
|
|
7
9
|
AssistantPluginCapability,
|
|
8
10
|
AssistantPluginHealth,
|
|
9
11
|
AssistantPluginManifest,
|
|
@@ -17,6 +19,8 @@ const CONFIG_ROOT = '.forge/plugins';
|
|
|
17
19
|
const LEGACY_CONFIG_ROOT = '.repo-harness/plugins';
|
|
18
20
|
const API_BASE_URL = 'https://api.appstoreconnect.apple.com';
|
|
19
21
|
const DEFAULT_TIMEOUT_MS = 60_000;
|
|
22
|
+
const PRIVATE_KEY_READ_MAX_ATTEMPTS = 4;
|
|
23
|
+
const PRIVATE_KEY_READ_RETRY_BASE_MS = 10;
|
|
20
24
|
|
|
21
25
|
type AppStoreConnectProviderKind = 'mock' | 'app-store-connect-api';
|
|
22
26
|
|
|
@@ -45,6 +49,12 @@ interface AppStoreConnectAuthState {
|
|
|
45
49
|
warnings: string[];
|
|
46
50
|
}
|
|
47
51
|
|
|
52
|
+
export interface AppStoreConnectXcodeAuthenticationReference {
|
|
53
|
+
privateKeyPath: string;
|
|
54
|
+
keyId: string;
|
|
55
|
+
issuerId: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
48
58
|
function now(): string {
|
|
49
59
|
return new Date().toISOString();
|
|
50
60
|
}
|
|
@@ -70,6 +80,10 @@ function legacyConfigPath(repoRoot: string): string {
|
|
|
70
80
|
return join(repoRoot, LEGACY_CONFIG_ROOT, 'app-store-connect.json');
|
|
71
81
|
}
|
|
72
82
|
|
|
83
|
+
function globalConfigPath(controllerHome: string): string {
|
|
84
|
+
return join(controllerSystemRoot(controllerHome), 'plugins', 'profiles', 'app-store-connect.json');
|
|
85
|
+
}
|
|
86
|
+
|
|
73
87
|
function normalizeConfig(raw: Partial<AppStoreConnectPluginConfig>): AppStoreConnectPluginConfig {
|
|
74
88
|
return {
|
|
75
89
|
schemaVersion: 1,
|
|
@@ -85,46 +99,139 @@ function normalizeConfig(raw: Partial<AppStoreConnectPluginConfig>): AppStoreCon
|
|
|
85
99
|
};
|
|
86
100
|
}
|
|
87
101
|
|
|
88
|
-
function
|
|
89
|
-
|
|
90
|
-
// the Forge config-root migration may still have the App Store Connect config
|
|
91
|
-
// under `.repo-harness/plugins`. Read that legacy file only when the current
|
|
92
|
-
// path is absent; the next explicit configure action writes the current path.
|
|
93
|
-
const path = existsSync(configPath(repoRoot)) ? configPath(repoRoot) : legacyConfigPath(repoRoot);
|
|
94
|
-
if (!existsSync(path)) return normalizeConfig({});
|
|
102
|
+
function readConfigFile(path: string): Partial<AppStoreConnectPluginConfig> | undefined {
|
|
103
|
+
if (!existsSync(path)) return undefined;
|
|
95
104
|
try {
|
|
96
|
-
|
|
105
|
+
const parsed = JSON.parse(readFileSync(path, 'utf-8')) as Partial<AppStoreConnectPluginConfig>;
|
|
106
|
+
return parsed && typeof parsed === 'object' ? parsed : undefined;
|
|
97
107
|
} catch {
|
|
98
|
-
return
|
|
108
|
+
return undefined;
|
|
99
109
|
}
|
|
100
110
|
}
|
|
101
111
|
|
|
102
|
-
function
|
|
103
|
-
const
|
|
112
|
+
function repositoryConfig(repoRoot: string): { raw?: Partial<AppStoreConnectPluginConfig>; source?: 'current' | 'legacy' } {
|
|
113
|
+
const current = readConfigFile(configPath(repoRoot));
|
|
114
|
+
if (current) return { raw: current, source: 'current' };
|
|
115
|
+
const legacy = readConfigFile(legacyConfigPath(repoRoot));
|
|
116
|
+
return legacy ? { raw: legacy, source: 'legacy' } : {};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function loadConfig(repoRoot: string, controllerHome?: string, repoId?: string): AppStoreConnectPluginConfig {
|
|
120
|
+
const global = controllerHome ? readConfigFile(globalConfigPath(controllerHome)) : undefined;
|
|
121
|
+
if (repoId === CONTROLLER_SCOPE_REPO_ID) return normalizeConfig(global ?? {});
|
|
122
|
+
const local = repositoryConfig(repoRoot).raw;
|
|
123
|
+
return normalizeConfig({ ...(global ?? {}), ...(local ?? {}) });
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function definedPatch(patch: Partial<AppStoreConnectPluginConfig>): Partial<AppStoreConnectPluginConfig> {
|
|
127
|
+
return Object.fromEntries(Object.entries(patch).filter(([, value]) => value !== undefined)) as Partial<AppStoreConnectPluginConfig>;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function saveConfig(
|
|
131
|
+
repoRoot: string,
|
|
132
|
+
patch: Partial<AppStoreConnectPluginConfig>,
|
|
133
|
+
controllerHome?: string,
|
|
134
|
+
repoId?: string,
|
|
135
|
+
): AppStoreConnectPluginConfig {
|
|
136
|
+
const cleanPatch = definedPatch(patch);
|
|
137
|
+
if (controllerHome && repoId === CONTROLLER_SCOPE_REPO_ID) {
|
|
138
|
+
const path = globalConfigPath(controllerHome);
|
|
139
|
+
const next = normalizeConfig({ ...(readConfigFile(path) ?? {}), ...cleanPatch });
|
|
140
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
141
|
+
writeFileSync(path, `${JSON.stringify(next, null, 2)}\n`, 'utf-8');
|
|
142
|
+
return next;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Repository state is an overlay only. Do not materialize inherited global
|
|
146
|
+
// credential references into every repository just because configure ran.
|
|
147
|
+
const existing = repositoryConfig(repoRoot).raw ?? {};
|
|
148
|
+
const overlay = { schemaVersion: 1 as const, ...existing, ...cleanPatch };
|
|
104
149
|
const path = configPath(repoRoot);
|
|
105
150
|
mkdirSync(dirname(path), { recursive: true });
|
|
106
|
-
writeFileSync(path, `${JSON.stringify(
|
|
107
|
-
return
|
|
151
|
+
writeFileSync(path, `${JSON.stringify(overlay, null, 2)}\n`, 'utf-8');
|
|
152
|
+
return loadConfig(repoRoot, controllerHome, repoId);
|
|
108
153
|
}
|
|
109
154
|
|
|
110
155
|
function envValue(name: string): string | undefined {
|
|
111
156
|
return stringValue(process.env[name]);
|
|
112
157
|
}
|
|
113
158
|
|
|
114
|
-
|
|
159
|
+
interface AppStoreConnectPrivateKeyReference {
|
|
160
|
+
available: boolean;
|
|
161
|
+
source?: string;
|
|
162
|
+
warning?: string;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function privateKeyPathReference(path: string, source: string): AppStoreConnectPrivateKeyReference {
|
|
166
|
+
if (!existsSync(path)) {
|
|
167
|
+
return { available: false, source, warning: 'Configured App Store Connect private key path does not exist.' };
|
|
168
|
+
}
|
|
169
|
+
try {
|
|
170
|
+
if (!statSync(path).isFile()) {
|
|
171
|
+
return { available: false, source, warning: 'Configured App Store Connect private key path is not a regular file.' };
|
|
172
|
+
}
|
|
173
|
+
accessSync(path, fsConstants.R_OK);
|
|
174
|
+
return { available: true, source };
|
|
175
|
+
} catch {
|
|
176
|
+
// Capability discovery and auth_status are metadata reads. They must never
|
|
177
|
+
// synchronously consume secret bytes just to decide whether a credential
|
|
178
|
+
// reference is usable; filesystem contention (including macOS EDEADLK)
|
|
179
|
+
// therefore degrades readiness instead of escaping as a raw read error.
|
|
180
|
+
return { available: false, source, warning: 'Configured App Store Connect private key path is not readable.' };
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function privateKeyReference(config: AppStoreConnectPluginConfig): AppStoreConnectPrivateKeyReference {
|
|
115
185
|
const inline = envValue('FORGE_ASC_PRIVATE_KEY');
|
|
116
|
-
if (inline) return {
|
|
186
|
+
if (inline) return { available: true, source: 'env:FORGE_ASC_PRIVATE_KEY' };
|
|
117
187
|
const envKeyPath = envValue('FORGE_ASC_PRIVATE_KEY_PATH');
|
|
118
|
-
if (envKeyPath)
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
188
|
+
if (envKeyPath) return privateKeyPathReference(envKeyPath, 'env:FORGE_ASC_PRIVATE_KEY_PATH');
|
|
189
|
+
if (config.privateKeyPath) return privateKeyPathReference(config.privateKeyPath, 'config:privateKeyPath');
|
|
190
|
+
return { available: false };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function fsErrorCode(error: unknown): string | undefined {
|
|
194
|
+
if (!error || typeof error !== 'object' || !('code' in error)) return undefined;
|
|
195
|
+
const code = (error as { code?: unknown }).code;
|
|
196
|
+
return typeof code === 'string' ? code : undefined;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async function readPrivateKeyFile(path: string, source: string): Promise<{ key?: string; source: string; warning?: string }> {
|
|
200
|
+
for (let attempt = 1; attempt <= PRIVATE_KEY_READ_MAX_ATTEMPTS; attempt += 1) {
|
|
201
|
+
try {
|
|
202
|
+
return { key: await fsPromises.readFile(path, 'utf-8'), source };
|
|
203
|
+
} catch (error) {
|
|
204
|
+
const code = fsErrorCode(error);
|
|
205
|
+
if (code === 'ENOENT') {
|
|
206
|
+
return { source, warning: 'Configured App Store Connect private key path does not exist.' };
|
|
207
|
+
}
|
|
208
|
+
if (code === 'EDEADLK' && attempt < PRIVATE_KEY_READ_MAX_ATTEMPTS) {
|
|
209
|
+
await new Promise((resolve) => setTimeout(resolve, PRIVATE_KEY_READ_RETRY_BASE_MS * attempt));
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
if (code === 'EDEADLK') {
|
|
213
|
+
throw new AssistantPluginError(
|
|
214
|
+
'PLUGIN_PROVIDER_UNAVAILABLE',
|
|
215
|
+
'App Store Connect private key is temporarily unavailable because the filesystem is busy.',
|
|
216
|
+
{ retryable: true, details: { credentialSource: source, code } },
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
throw new AssistantPluginError(
|
|
220
|
+
'PLUGIN_AUTH_REQUIRED',
|
|
221
|
+
'Configured App Store Connect private key could not be read.',
|
|
222
|
+
{ retryable: false, details: { credentialSource: source, code } },
|
|
223
|
+
);
|
|
224
|
+
}
|
|
127
225
|
}
|
|
226
|
+
throw new AssistantPluginError('PLUGIN_PROVIDER_UNAVAILABLE', 'App Store Connect private key is temporarily unavailable.', { retryable: true });
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
async function privateKeyMaterial(config: AppStoreConnectPluginConfig): Promise<{ key?: string; source?: string; warning?: string }> {
|
|
230
|
+
const inline = envValue('FORGE_ASC_PRIVATE_KEY');
|
|
231
|
+
if (inline) return { key: inline.replace(/\\n/g, '\n'), source: 'env:FORGE_ASC_PRIVATE_KEY' };
|
|
232
|
+
const envKeyPath = envValue('FORGE_ASC_PRIVATE_KEY_PATH');
|
|
233
|
+
if (envKeyPath) return readPrivateKeyFile(envKeyPath, 'env:FORGE_ASC_PRIVATE_KEY_PATH');
|
|
234
|
+
if (config.privateKeyPath) return readPrivateKeyFile(config.privateKeyPath, 'config:privateKeyPath');
|
|
128
235
|
return {};
|
|
129
236
|
}
|
|
130
237
|
|
|
@@ -145,12 +252,12 @@ function resolveAuth(config: AppStoreConnectPluginConfig): AppStoreConnectAuthSt
|
|
|
145
252
|
|
|
146
253
|
const issuerId = envValue('FORGE_ASC_ISSUER_ID') ?? config.issuerId;
|
|
147
254
|
const keyId = envValue('FORGE_ASC_KEY_ID') ?? config.keyId;
|
|
148
|
-
const key =
|
|
255
|
+
const key = privateKeyReference(config);
|
|
149
256
|
const errors: string[] = [];
|
|
150
257
|
const warnings: string[] = key.warning ? [key.warning] : [];
|
|
151
258
|
if (!issuerId) errors.push('Set FORGE_ASC_ISSUER_ID or configure issuer_id.');
|
|
152
259
|
if (!keyId) errors.push('Set FORGE_ASC_KEY_ID or configure key_id.');
|
|
153
|
-
if (!key.
|
|
260
|
+
if (!key.available) errors.push('Set FORGE_ASC_PRIVATE_KEY, FORGE_ASC_PRIVATE_KEY_PATH, or configure private_key_path.');
|
|
154
261
|
|
|
155
262
|
return {
|
|
156
263
|
provider: 'app-store-connect-api',
|
|
@@ -165,6 +272,37 @@ function resolveAuth(config: AppStoreConnectPluginConfig): AppStoreConnectAuthSt
|
|
|
165
272
|
};
|
|
166
273
|
}
|
|
167
274
|
|
|
275
|
+
export function resolveAppStoreConnectXcodeAuthenticationReference(
|
|
276
|
+
repoRoot: string,
|
|
277
|
+
controllerHome: string,
|
|
278
|
+
repoId: string,
|
|
279
|
+
): AppStoreConnectXcodeAuthenticationReference {
|
|
280
|
+
const config = loadConfig(repoRoot, controllerHome, repoId);
|
|
281
|
+
if (!config.enabled) {
|
|
282
|
+
throw new AssistantPluginError('PLUGIN_DISABLED', 'App Store Connect is disabled for this repository.', { retryable: false });
|
|
283
|
+
}
|
|
284
|
+
if (config.provider !== 'app-store-connect-api') {
|
|
285
|
+
throw new AssistantPluginError('PLUGIN_DEPENDENCY_MISSING', 'Xcode provisioning requires the App Store Connect API provider.', { retryable: false });
|
|
286
|
+
}
|
|
287
|
+
const privateKeyPath = envValue('FORGE_ASC_PRIVATE_KEY_PATH') ?? config.privateKeyPath;
|
|
288
|
+
const keyId = envValue('FORGE_ASC_KEY_ID') ?? config.keyId;
|
|
289
|
+
const issuerId = envValue('FORGE_ASC_ISSUER_ID') ?? config.issuerId;
|
|
290
|
+
if (!privateKeyPath) {
|
|
291
|
+
throw new AssistantPluginError(
|
|
292
|
+
'PLUGIN_DEPENDENCY_MISSING',
|
|
293
|
+
'Xcode provisioning requires a file-backed App Store Connect private key path; inline key material is not accepted for this path.',
|
|
294
|
+
{ retryable: false },
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
if (!existsSync(privateKeyPath)) {
|
|
298
|
+
throw new AssistantPluginError('PLUGIN_DEPENDENCY_MISSING', 'Configured App Store Connect private key path does not exist.', { retryable: false });
|
|
299
|
+
}
|
|
300
|
+
if (!keyId || !issuerId) {
|
|
301
|
+
throw new AssistantPluginError('PLUGIN_DEPENDENCY_MISSING', 'App Store Connect key id and issuer id are required for Xcode provisioning.', { retryable: false });
|
|
302
|
+
}
|
|
303
|
+
return { privateKeyPath, keyId, issuerId };
|
|
304
|
+
}
|
|
305
|
+
|
|
168
306
|
function readLength(buffer: Buffer, offset: number): { length: number; next: number } {
|
|
169
307
|
const first = buffer[offset];
|
|
170
308
|
if (first < 0x80) return { length: first, next: offset + 1 };
|
|
@@ -195,10 +333,10 @@ function derSignatureToJose(signature: Buffer): string {
|
|
|
195
333
|
return Buffer.concat([derIntegerToJose(r), derIntegerToJose(s)]).toString('base64url');
|
|
196
334
|
}
|
|
197
335
|
|
|
198
|
-
function createJwt(config: AppStoreConnectPluginConfig): string {
|
|
336
|
+
async function createJwt(config: AppStoreConnectPluginConfig): Promise<string> {
|
|
199
337
|
const issuerId = envValue('FORGE_ASC_ISSUER_ID') ?? config.issuerId;
|
|
200
338
|
const keyId = envValue('FORGE_ASC_KEY_ID') ?? config.keyId;
|
|
201
|
-
const key = privateKeyMaterial(config).key;
|
|
339
|
+
const key = (await privateKeyMaterial(config)).key;
|
|
202
340
|
if (!issuerId || !keyId || !key) throw new AssistantPluginError('PLUGIN_AUTH_REQUIRED', 'App Store Connect API credentials are incomplete.', { retryable: false });
|
|
203
341
|
|
|
204
342
|
const issuedAt = Math.floor(Date.now() / 1000);
|
|
@@ -224,7 +362,7 @@ async function apiRequest<T>(config: AppStoreConnectPluginConfig, options: {
|
|
|
224
362
|
const response = await fetch(url, {
|
|
225
363
|
method: options.method ?? 'GET',
|
|
226
364
|
headers: {
|
|
227
|
-
authorization: `Bearer ${createJwt(config)}`,
|
|
365
|
+
authorization: `Bearer ${await createJwt(config)}`,
|
|
228
366
|
accept: 'application/json',
|
|
229
367
|
...(options.body === undefined ? {} : { 'content-type': 'application/json' }),
|
|
230
368
|
},
|
|
@@ -308,6 +446,7 @@ function permission(scope: string, mode: 'read' | 'write', description: string,
|
|
|
308
446
|
function permissions(ready: boolean): AssistantPluginPermissionScope[] {
|
|
309
447
|
return [
|
|
310
448
|
permission('appstoreconnect.apps.read', 'read', 'Read App Store Connect apps, versions, localizations, builds, and TestFlight groups.', ready),
|
|
449
|
+
permission('appstoreconnect.developer_resources.read', 'read', 'Read Apple Developer bundle IDs, capabilities, certificates, devices, and provisioning profiles.', ready),
|
|
311
450
|
permission('appstoreconnect.metadata.write', 'write', 'Patch App Store Connect metadata after dry-run review and authorization.', ready),
|
|
312
451
|
permission('appstoreconnect.testflight.write', 'write', 'Assign builds to TestFlight groups and prepare beta review submissions.', ready),
|
|
313
452
|
permission('appstoreconnect.release.write', 'write', 'Create App Store versions and gated review submissions.', ready),
|
|
@@ -329,6 +468,13 @@ function capabilities(): AssistantPluginCapability[] {
|
|
|
329
468
|
'list_beta_groups', 'list_beta_testers', 'list_review_submissions',
|
|
330
469
|
],
|
|
331
470
|
},
|
|
471
|
+
{
|
|
472
|
+
capabilityId: 'developer-resources-read',
|
|
473
|
+
title: 'Apple Developer Resources',
|
|
474
|
+
description: 'Read Certificates, Identifiers & Profiles resources to diagnose provisioning authorization without mutating Apple Developer state.',
|
|
475
|
+
scopes: ['appstoreconnect.developer_resources.read'],
|
|
476
|
+
actions: ['list_bundle_ids', 'list_bundle_id_capabilities', 'list_certificates', 'list_devices', 'list_profiles'],
|
|
477
|
+
},
|
|
332
478
|
{
|
|
333
479
|
capabilityId: 'app-store-metadata',
|
|
334
480
|
title: 'App Metadata Update',
|
|
@@ -369,11 +515,16 @@ function actions(): AssistantPluginActionDescriptor[] {
|
|
|
369
515
|
return [
|
|
370
516
|
{
|
|
371
517
|
actionId: 'configure', title: 'Configure App Store Connect plugin', description: 'Enable official App Store Connect API access and save non-secret defaults.', readOnly: false, risk: 'workspace_write', confirmation: 'authorization', defaultTimeoutMs: 30_000, cancellable: true, idempotent: true,
|
|
372
|
-
scopes: ['appstoreconnect.apps.read', 'appstoreconnect.metadata.write', 'appstoreconnect.testflight.write', 'appstoreconnect.release.write', 'appstoreconnect.xcodecloud.read', 'appstoreconnect.xcodecloud.write'], resourceClaims: [{ resource: 'repo-state', mode: 'write' }],
|
|
518
|
+
scopes: ['appstoreconnect.apps.read', 'appstoreconnect.developer_resources.read', 'appstoreconnect.metadata.write', 'appstoreconnect.testflight.write', 'appstoreconnect.release.write', 'appstoreconnect.xcodecloud.read', 'appstoreconnect.xcodecloud.write'], resourceClaims: [{ resource: 'repo-state', mode: 'write' }],
|
|
373
519
|
argumentsSchema: { type: 'object', properties: { enabled: { type: 'boolean' }, provider: { type: 'string', enum: ['mock', 'app-store-connect-api'] }, issuer_id: { type: 'string' }, key_id: { type: 'string' }, private_key_path: { type: 'string' }, clear_private_key_path: { type: 'boolean' }, clear_api_identity: { type: 'boolean' }, team_id: { type: 'string' }, clear_team_id: { type: 'boolean' }, default_app_id: { type: 'string' }, clear_default_app_id: { type: 'boolean' }, default_locale: { type: 'string' }, default_timeout_ms: { type: 'number' } }, additionalProperties: false },
|
|
374
520
|
},
|
|
375
521
|
{ actionId: 'auth_status', title: 'Check App Store Connect auth', description: 'Report API readiness without returning secrets.', readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 10_000, cancellable: true, idempotent: true, scopes: ['appstoreconnect.apps.read'], resourceClaims: [], argumentsSchema: { type: 'object', properties: {}, additionalProperties: false } },
|
|
376
522
|
{ actionId: 'list_apps', title: 'List apps', description: 'List App Store Connect apps, optionally filtered by bundle ID or name.', readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 45_000, cancellable: true, idempotent: true, scopes: ['appstoreconnect.apps.read'], resourceClaims: readRemote, argumentsSchema: { type: 'object', properties: { bundle_id: { type: 'string' }, name: { type: 'string' }, limit: { type: 'number' } }, additionalProperties: false } },
|
|
523
|
+
{ actionId: 'list_bundle_ids', title: 'List Apple Developer bundle IDs', description: 'Read registered Bundle IDs, optionally filtered by exact identifier or name.', readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 45_000, cancellable: true, idempotent: true, scopes: ['appstoreconnect.developer_resources.read'], resourceClaims: readRemote, argumentsSchema: { type: 'object', properties: { identifier: { type: 'string' }, name: { type: 'string' }, limit: { type: 'number' } }, additionalProperties: false } },
|
|
524
|
+
{ actionId: 'list_bundle_id_capabilities', title: 'List Bundle ID capabilities', description: 'Read enabled capabilities for one exact Apple Developer Bundle ID resource.', readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 45_000, cancellable: true, idempotent: true, scopes: ['appstoreconnect.developer_resources.read'], resourceClaims: readRemote, argumentsSchema: { type: 'object', properties: { bundle_id_resource_id: { type: 'string' }, limit: { type: 'number' } }, required: ['bundle_id_resource_id'], additionalProperties: false } },
|
|
525
|
+
{ actionId: 'list_certificates', title: 'List Apple Developer certificates', description: 'Read certificates available to the current Apple Developer API identity.', readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 45_000, cancellable: true, idempotent: true, scopes: ['appstoreconnect.developer_resources.read'], resourceClaims: readRemote, argumentsSchema: { type: 'object', properties: { certificate_type: { type: 'string' }, limit: { type: 'number' } }, additionalProperties: false } },
|
|
526
|
+
{ actionId: 'list_devices', title: 'List Apple Developer devices', description: 'Read registered devices, optionally filtered by UDID.', readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 45_000, cancellable: true, idempotent: true, scopes: ['appstoreconnect.developer_resources.read'], resourceClaims: readRemote, argumentsSchema: { type: 'object', properties: { udid: { type: 'string' }, limit: { type: 'number' } }, additionalProperties: false } },
|
|
527
|
+
{ actionId: 'list_profiles', title: 'List provisioning profiles', description: 'Read Apple Developer provisioning profiles, optionally filtered by profile name or type.', readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 45_000, cancellable: true, idempotent: true, scopes: ['appstoreconnect.developer_resources.read'], resourceClaims: readRemote, argumentsSchema: { type: 'object', properties: { name: { type: 'string' }, profile_type: { type: 'string' }, limit: { type: 'number' } }, additionalProperties: false } },
|
|
377
528
|
{ actionId: 'list_app_store_versions', title: 'List App Store versions', description: 'List App Store versions for one app.', readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 45_000, cancellable: true, idempotent: true, scopes: ['appstoreconnect.apps.read'], resourceClaims: readRemote, argumentsSchema: { type: 'object', properties: { app_id: { type: 'string' }, limit: { type: 'number' } }, additionalProperties: false } },
|
|
378
529
|
{ actionId: 'list_app_store_version_localizations', title: 'List App Store version localizations', description: 'List localizations for one App Store version.', readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 45_000, cancellable: true, idempotent: true, scopes: ['appstoreconnect.apps.read'], resourceClaims: readRemote, argumentsSchema: { type: 'object', properties: { version_id: { type: 'string' }, limit: { type: 'number' } }, required: ['version_id'], additionalProperties: false } },
|
|
379
530
|
{ actionId: 'get_app_info', title: 'Get app info', description: 'Get App Info records and localizations for one app.', readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 45_000, cancellable: true, idempotent: true, scopes: ['appstoreconnect.apps.read'], resourceClaims: readRemote, argumentsSchema: { type: 'object', properties: { app_id: { type: 'string' } }, additionalProperties: false } },
|
|
@@ -579,6 +730,11 @@ function mockResponse(actionId: string, args: Record<string, unknown>, config: A
|
|
|
579
730
|
}],
|
|
580
731
|
};
|
|
581
732
|
}
|
|
733
|
+
if (actionId === 'list_bundle_ids') return { data: [{ type: 'bundleIds', id: stableMockId('bundle_id', args), attributes: { identifier: stringValue(args.identifier) ?? 'com.example.app', name: stringValue(args.name) ?? 'Mock Bundle ID', platform: 'IOS' } }] };
|
|
734
|
+
if (actionId === 'list_bundle_id_capabilities') return { data: [{ type: 'bundleIdCapabilities', id: stableMockId('bundle_capability', args), attributes: { capabilityType: 'ICLOUD', settings: [] } }] };
|
|
735
|
+
if (actionId === 'list_certificates') return { data: [{ type: 'certificates', id: stableMockId('certificate', args), attributes: { certificateType: stringValue(args.certificate_type) ?? 'DEVELOPMENT', displayName: 'Mock Certificate' } }] };
|
|
736
|
+
if (actionId === 'list_devices') return { data: [{ type: 'devices', id: stableMockId('device', args), attributes: { udid: stringValue(args.udid) ?? 'MOCK-UDID', name: 'Mock iPhone', platform: 'IOS', status: 'ENABLED' } }] };
|
|
737
|
+
if (actionId === 'list_profiles') return { data: [{ type: 'profiles', id: stableMockId('profile', args), attributes: { name: stringValue(args.name) ?? 'Mock Profile', profileType: stringValue(args.profile_type) ?? 'IOS_APP_DEVELOPMENT', profileState: 'ACTIVE' } }] };
|
|
582
738
|
if (actionId === 'list_xcode_cloud_products') return { data: [{ type: 'ciProducts', id: stableMockId('ci_product', { id }), attributes: { name: 'Mock Xcode Cloud Product', productType: 'APP' } }] };
|
|
583
739
|
if (actionId === 'list_xcode_cloud_workflows') return { data: [{ type: 'ciWorkflows', id: stableMockId('ci_workflow', { product: args.product_id }), attributes: { name: 'CI', isEnabled: true, clean: false } }] };
|
|
584
740
|
if (actionId === 'get_xcode_cloud_workflow') return { data: { type: 'ciWorkflows', id: requiredArg(args, 'workflow_id'), attributes: { name: 'CI', isEnabled: true, branchStartCondition: {} } } };
|
|
@@ -671,8 +827,9 @@ function mockResponse(actionId: string, args: Record<string, unknown>, config: A
|
|
|
671
827
|
throw new AssistantPluginError('PLUGIN_ACTION_NOT_SUPPORTED', `app_store_connect/${actionId} is not supported.`, { retryable: false });
|
|
672
828
|
}
|
|
673
829
|
|
|
674
|
-
export function buildAppStoreConnectPluginManifest(previousRevision = 0, previousUpdatedAt?: string, repoRoot?: string): AssistantPluginManifest {
|
|
675
|
-
const
|
|
830
|
+
export function buildAppStoreConnectPluginManifest(previousRevision = 0, previousUpdatedAt?: string, repoRoot?: string, context?: AssistantPluginBuildContext): AssistantPluginManifest {
|
|
831
|
+
const effectiveRepoRoot = repoRoot ?? context?.repoRoot ?? process.cwd();
|
|
832
|
+
const config = loadConfig(effectiveRepoRoot, context?.controllerHome, context?.repoId);
|
|
676
833
|
const auth = resolveAuth(config);
|
|
677
834
|
const state = pluginState(config, auth);
|
|
678
835
|
return {
|
|
@@ -683,7 +840,7 @@ export function buildAppStoreConnectPluginManifest(previousRevision = 0, previou
|
|
|
683
840
|
provider: 'apple',
|
|
684
841
|
displayName: 'App Store Connect API Plugin',
|
|
685
842
|
pluginVersion: '1.1.1',
|
|
686
|
-
authority: { strategy: 'derived', duplicateStateAllowed: false, sourceOfTruth: [`repo-
|
|
843
|
+
authority: { strategy: 'derived', duplicateStateAllowed: false, sourceOfTruth: ['controller-global:system/plugins/profiles/app-store-connect.json', `repo-overlay:${CONFIG_ROOT}/app-store-connect.json`, `legacy-read-fallback:${LEGACY_CONFIG_ROOT}/app-store-connect.json`, 'env:FORGE_ASC_*'] },
|
|
687
844
|
enabled: config.enabled,
|
|
688
845
|
lifecycle: { state: state.lifecycleState, reason: !config.enabled ? 'App Store Connect plugin is disabled.' : auth.ready ? 'App Store Connect API credentials are ready.' : auth.errors[0] ?? auth.warnings[0] },
|
|
689
846
|
health: state.health,
|
|
@@ -695,7 +852,7 @@ export function buildAppStoreConnectPluginManifest(previousRevision = 0, previou
|
|
|
695
852
|
}
|
|
696
853
|
|
|
697
854
|
export async function executeAppStoreConnectPluginAction(input: AssistantPluginActionExecutionInput): Promise<Record<string, unknown>> {
|
|
698
|
-
const config = loadConfig(input.repoRoot);
|
|
855
|
+
const config = loadConfig(input.repoRoot, input.controllerHome, input.repoId);
|
|
699
856
|
if (input.actionId === 'configure') {
|
|
700
857
|
const args = input.args;
|
|
701
858
|
const next = saveConfig(input.repoRoot, {
|
|
@@ -708,28 +865,39 @@ export async function executeAppStoreConnectPluginAction(input: AssistantPluginA
|
|
|
708
865
|
defaultAppId: args.clear_default_app_id === true ? '' : stringValue(args.default_app_id),
|
|
709
866
|
defaultLocale: stringValue(args.default_locale),
|
|
710
867
|
defaultTimeoutMs: boundedTimeout(args.default_timeout_ms),
|
|
711
|
-
});
|
|
868
|
+
}, input.controllerHome, input.repoId);
|
|
712
869
|
return { config: next, auth: resolveAuth(next) };
|
|
713
870
|
}
|
|
714
871
|
|
|
715
872
|
if (config.provider === 'mock') return mockResponse(input.actionId, input.args, config);
|
|
716
873
|
const auth = resolveAuth(config);
|
|
874
|
+
if (input.actionId === 'auth_status') {
|
|
875
|
+
return {
|
|
876
|
+
ready: config.enabled && auth.ready,
|
|
877
|
+
provider: auth.provider,
|
|
878
|
+
issuerId: auth.issuerId ? 'configured' : undefined,
|
|
879
|
+
keyId: auth.keyId ? 'configured' : undefined,
|
|
880
|
+
credentialSource: auth.credentialSource,
|
|
881
|
+
errors: auth.errors,
|
|
882
|
+
warnings: auth.warnings,
|
|
883
|
+
userFacingStatus: userFacingAscStatus(config, auth),
|
|
884
|
+
};
|
|
885
|
+
}
|
|
717
886
|
if (!config.enabled || !auth.ready) throw new AssistantPluginError('PLUGIN_NOT_READY', 'App Store Connect plugin is not ready.', { retryable: false, details: { enabled: config.enabled, errors: auth.errors } });
|
|
718
887
|
|
|
719
888
|
switch (input.actionId) {
|
|
720
|
-
case 'auth_status':
|
|
721
|
-
return {
|
|
722
|
-
ready: auth.ready,
|
|
723
|
-
provider: auth.provider,
|
|
724
|
-
issuerId: auth.issuerId ? 'configured' : undefined,
|
|
725
|
-
keyId: auth.keyId ? 'configured' : undefined,
|
|
726
|
-
credentialSource: auth.credentialSource,
|
|
727
|
-
errors: auth.errors,
|
|
728
|
-
warnings: auth.warnings,
|
|
729
|
-
userFacingStatus: userFacingAscStatus(config, auth),
|
|
730
|
-
};
|
|
731
889
|
case 'list_apps':
|
|
732
890
|
return apiRequest(config, { path: '/v1/apps', query: { 'filter[bundleId]': stringValue(input.args.bundle_id), 'filter[name]': stringValue(input.args.name), limit: limit(input.args.limit) } });
|
|
891
|
+
case 'list_bundle_ids':
|
|
892
|
+
return apiRequest(config, { path: '/v1/bundleIds', query: { 'filter[identifier]': stringValue(input.args.identifier), 'filter[name]': stringValue(input.args.name), limit: limit(input.args.limit) } });
|
|
893
|
+
case 'list_bundle_id_capabilities':
|
|
894
|
+
return apiRequest(config, { path: `/v1/bundleIds/${encodeURIComponent(requiredArg(input.args, 'bundle_id_resource_id'))}/bundleIdCapabilities`, query: { limit: limit(input.args.limit) } });
|
|
895
|
+
case 'list_certificates':
|
|
896
|
+
return apiRequest(config, { path: '/v1/certificates', query: { 'filter[certificateType]': stringValue(input.args.certificate_type), limit: limit(input.args.limit) } });
|
|
897
|
+
case 'list_devices':
|
|
898
|
+
return apiRequest(config, { path: '/v1/devices', query: { 'filter[udid]': stringValue(input.args.udid), limit: limit(input.args.limit) } });
|
|
899
|
+
case 'list_profiles':
|
|
900
|
+
return apiRequest(config, { path: '/v1/profiles', query: { 'filter[name]': stringValue(input.args.name), 'filter[profileType]': stringValue(input.args.profile_type), limit: limit(input.args.limit) } });
|
|
733
901
|
case 'list_app_store_versions':
|
|
734
902
|
return apiRequest(config, { path: '/v1/appStoreVersions', query: { 'filter[app]': appId(input.args, config), limit: limit(input.args.limit) } });
|
|
735
903
|
case 'list_app_store_version_localizations':
|
|
@@ -863,6 +1031,7 @@ export async function executeAppStoreConnectPluginAction(input: AssistantPluginA
|
|
|
863
1031
|
|
|
864
1032
|
export const appStoreConnectPluginAdapter = {
|
|
865
1033
|
pluginId: APP_STORE_CONNECT_PLUGIN_ID,
|
|
1034
|
+
scope: 'controller_with_repository_overlay' as const,
|
|
866
1035
|
buildManifest: buildAppStoreConnectPluginManifest,
|
|
867
1036
|
executeAction: executeAppStoreConnectPluginAction,
|
|
868
1037
|
};
|