apex-code 0.0.1-alpha.0 → 0.0.1-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/README.md +66 -681
- package/dist/cli/args.d.ts +5 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +45 -6
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/auth-command.d.ts.map +1 -1
- package/dist/cli/auth-command.js +3 -3
- package/dist/cli/auth-command.js.map +1 -1
- package/dist/cli/cost-command.d.ts +19 -0
- package/dist/cli/cost-command.d.ts.map +1 -0
- package/dist/cli/cost-command.js +107 -0
- package/dist/cli/cost-command.js.map +1 -0
- package/dist/cli/startup-ui.d.ts +1 -1
- package/dist/cli/startup-ui.d.ts.map +1 -1
- package/dist/cli/startup-ui.js +1 -2
- package/dist/cli/startup-ui.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +45 -16
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +2 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +14 -7
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +2 -0
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session-services.d.ts +9 -0
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +30 -4
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +46 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +137 -19
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +6 -6
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +1 -1
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/context/deferred-schemas.d.ts +92 -0
- package/dist/core/context/deferred-schemas.d.ts.map +1 -0
- package/dist/core/context/deferred-schemas.js +74 -0
- package/dist/core/context/deferred-schemas.js.map +1 -0
- package/dist/core/context/eviction.d.ts +47 -0
- package/dist/core/context/eviction.d.ts.map +1 -0
- package/dist/core/context/eviction.js +83 -0
- package/dist/core/context/eviction.js.map +1 -0
- package/dist/core/context/pipeline.d.ts +90 -0
- package/dist/core/context/pipeline.d.ts.map +1 -0
- package/dist/core/context/pipeline.js +109 -0
- package/dist/core/context/pipeline.js.map +1 -0
- package/dist/core/credential-failover.d.ts +30 -0
- package/dist/core/credential-failover.d.ts.map +1 -0
- package/dist/core/credential-failover.js +46 -0
- package/dist/core/credential-failover.js.map +1 -0
- package/dist/core/credential-pool.d.ts +60 -0
- package/dist/core/credential-pool.d.ts.map +1 -0
- package/dist/core/credential-pool.js +84 -0
- package/dist/core/credential-pool.js.map +1 -0
- package/dist/core/delegation/agents.d.ts +15 -0
- package/dist/core/delegation/agents.d.ts.map +1 -0
- package/dist/core/delegation/agents.js +55 -0
- package/dist/core/delegation/agents.js.map +1 -0
- package/dist/core/delegation/ceiling.d.ts +29 -0
- package/dist/core/delegation/ceiling.d.ts.map +1 -0
- package/dist/core/delegation/ceiling.js +34 -0
- package/dist/core/delegation/ceiling.js.map +1 -0
- package/dist/core/delegation/runtime.d.ts +94 -0
- package/dist/core/delegation/runtime.d.ts.map +1 -0
- package/dist/core/delegation/runtime.js +117 -0
- package/dist/core/delegation/runtime.js.map +1 -0
- package/dist/core/durable-state/daemon.d.ts +37 -0
- package/dist/core/durable-state/daemon.d.ts.map +1 -0
- package/dist/core/durable-state/daemon.js +62 -0
- package/dist/core/durable-state/daemon.js.map +1 -0
- package/dist/core/durable-state/provenance.d.ts +7 -0
- package/dist/core/durable-state/provenance.d.ts.map +1 -0
- package/dist/core/durable-state/provenance.js +24 -0
- package/dist/core/durable-state/provenance.js.map +1 -0
- package/dist/core/durable-state/sqlite.d.ts +84 -0
- package/dist/core/durable-state/sqlite.d.ts.map +1 -0
- package/dist/core/durable-state/sqlite.js +331 -0
- package/dist/core/durable-state/sqlite.js.map +1 -0
- package/dist/core/environment.d.ts +12 -0
- package/dist/core/environment.d.ts.map +1 -0
- package/dist/core/environment.js +69 -0
- package/dist/core/environment.js.map +1 -0
- package/dist/core/evidence-policy.d.ts +17 -0
- package/dist/core/evidence-policy.d.ts.map +1 -0
- package/dist/core/evidence-policy.js +25 -0
- package/dist/core/evidence-policy.js.map +1 -0
- package/dist/core/evidence.d.ts +16 -0
- package/dist/core/evidence.d.ts.map +1 -0
- package/dist/core/evidence.js +51 -0
- package/dist/core/evidence.js.map +1 -0
- package/dist/core/experimental.d.ts.map +1 -1
- package/dist/core/experimental.js +2 -1
- package/dist/core/experimental.js.map +1 -1
- package/dist/core/extensions/types.d.ts +2 -0
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/model-config.d.ts +8 -0
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-config.js +21 -5
- package/dist/core/model-config.js.map +1 -1
- package/dist/core/model-resolver.d.ts +20 -0
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +59 -0
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/model-runtime.d.ts +73 -0
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +225 -8
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/observability/aggregate.d.ts +21 -0
- package/dist/core/observability/aggregate.d.ts.map +1 -0
- package/dist/core/observability/aggregate.js +36 -0
- package/dist/core/observability/aggregate.js.map +1 -0
- package/dist/core/observability/otlp.d.ts +66 -0
- package/dist/core/observability/otlp.d.ts.map +1 -0
- package/dist/core/observability/otlp.js +95 -0
- package/dist/core/observability/otlp.js.map +1 -0
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +3 -2
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/permissions/gate.d.ts +35 -0
- package/dist/core/permissions/gate.d.ts.map +1 -0
- package/dist/core/permissions/gate.js +72 -0
- package/dist/core/permissions/gate.js.map +1 -0
- package/dist/core/permissions/modes.d.ts +18 -0
- package/dist/core/permissions/modes.d.ts.map +1 -0
- package/dist/core/permissions/modes.js +57 -0
- package/dist/core/permissions/modes.js.map +1 -0
- package/dist/core/permissions/responder.d.ts +30 -0
- package/dist/core/permissions/responder.d.ts.map +1 -0
- package/dist/core/permissions/responder.js +32 -0
- package/dist/core/permissions/responder.js.map +1 -0
- package/dist/core/permissions/rules.d.ts +34 -0
- package/dist/core/permissions/rules.d.ts.map +1 -0
- package/dist/core/permissions/rules.js +74 -0
- package/dist/core/permissions/rules.js.map +1 -0
- package/dist/core/permissions/startup.d.ts +33 -0
- package/dist/core/permissions/startup.d.ts.map +1 -0
- package/dist/core/permissions/startup.js +55 -0
- package/dist/core/permissions/startup.js.map +1 -0
- package/dist/core/permissions/store.d.ts +114 -0
- package/dist/core/permissions/store.d.ts.map +1 -0
- package/dist/core/permissions/store.js +229 -0
- package/dist/core/permissions/store.js.map +1 -0
- package/dist/core/project-trust.d.ts.map +1 -1
- package/dist/core/project-trust.js +1 -1
- package/dist/core/project-trust.js.map +1 -1
- package/dist/core/provider-attribution.d.ts.map +1 -1
- package/dist/core/provider-attribution.js +6 -7
- package/dist/core/provider-attribution.js.map +1 -1
- package/dist/core/remote-catalog-provider.d.ts +2 -2
- package/dist/core/remote-catalog-provider.d.ts.map +1 -1
- package/dist/core/remote-catalog-provider.js +20 -16
- package/dist/core/remote-catalog-provider.js.map +1 -1
- package/dist/core/sandbox/child-entry.d.ts +2 -0
- package/dist/core/sandbox/child-entry.d.ts.map +1 -0
- package/dist/core/sandbox/child-entry.js +11 -0
- package/dist/core/sandbox/child-entry.js.map +1 -0
- package/dist/core/sandbox/cli-launch.d.ts +27 -0
- package/dist/core/sandbox/cli-launch.d.ts.map +1 -0
- package/dist/core/sandbox/cli-launch.js +133 -0
- package/dist/core/sandbox/cli-launch.js.map +1 -0
- package/dist/core/sandbox/cli-supervisor.d.ts +25 -0
- package/dist/core/sandbox/cli-supervisor.d.ts.map +1 -0
- package/dist/core/sandbox/cli-supervisor.js +57 -0
- package/dist/core/sandbox/cli-supervisor.js.map +1 -0
- package/dist/core/sandbox/linux-backend.d.ts +18 -0
- package/dist/core/sandbox/linux-backend.d.ts.map +1 -0
- package/dist/core/sandbox/linux-backend.js +186 -0
- package/dist/core/sandbox/linux-backend.js.map +1 -0
- package/dist/core/sandbox/macos-backend.d.ts +18 -0
- package/dist/core/sandbox/macos-backend.d.ts.map +1 -0
- package/dist/core/sandbox/macos-backend.js +195 -0
- package/dist/core/sandbox/macos-backend.js.map +1 -0
- package/dist/core/sandbox/network-proxy.d.ts +19 -0
- package/dist/core/sandbox/network-proxy.d.ts.map +1 -0
- package/dist/core/sandbox/network-proxy.js +79 -0
- package/dist/core/sandbox/network-proxy.js.map +1 -0
- package/dist/core/sandbox/policy.d.ts +32 -0
- package/dist/core/sandbox/policy.d.ts.map +1 -0
- package/dist/core/sandbox/policy.js +38 -0
- package/dist/core/sandbox/policy.js.map +1 -0
- package/dist/core/sandbox/supervisor.d.ts +31 -0
- package/dist/core/sandbox/supervisor.d.ts.map +1 -0
- package/dist/core/sandbox/supervisor.js +18 -0
- package/dist/core/sandbox/supervisor.js.map +1 -0
- package/dist/core/sandbox/violations.d.ts +23 -0
- package/dist/core/sandbox/violations.d.ts.map +1 -0
- package/dist/core/sandbox/violations.js +30 -0
- package/dist/core/sandbox/violations.js.map +1 -0
- package/dist/core/sdk.d.ts +20 -36
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +129 -2
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +41 -2
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +57 -0
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-share.d.ts +16 -0
- package/dist/core/session-share.d.ts.map +1 -0
- package/dist/core/session-share.js +22 -0
- package/dist/core/session-share.js.map +1 -0
- package/dist/core/settings-manager.d.ts +38 -9
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +39 -24
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +5 -5
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/timings.d.ts +1 -1
- package/dist/core/timings.d.ts.map +1 -1
- package/dist/core/timings.js +3 -2
- package/dist/core/timings.js.map +1 -1
- package/dist/core/tools/ask-user.d.ts +25 -0
- package/dist/core/tools/ask-user.d.ts.map +1 -0
- package/dist/core/tools/ask-user.js +49 -0
- package/dist/core/tools/ask-user.js.map +1 -0
- package/dist/core/tools/bash-command-segments.d.ts +22 -0
- package/dist/core/tools/bash-command-segments.d.ts.map +1 -0
- package/dist/core/tools/bash-command-segments.js +128 -0
- package/dist/core/tools/bash-command-segments.js.map +1 -0
- package/dist/core/tools/bash.d.ts +26 -3
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +105 -16
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/contract.d.ts +156 -0
- package/dist/core/tools/contract.d.ts.map +1 -0
- package/dist/core/tools/contract.js +48 -0
- package/dist/core/tools/contract.js.map +1 -0
- package/dist/core/tools/delegate.d.ts +38 -0
- package/dist/core/tools/delegate.d.ts.map +1 -0
- package/dist/core/tools/delegate.js +66 -0
- package/dist/core/tools/delegate.js.map +1 -0
- package/dist/core/tools/edit.d.ts +2 -2
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +24 -0
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/find.d.ts +2 -2
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +12 -0
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts +2 -2
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +12 -0
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/index.d.ts +29 -4
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +118 -2
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/ls.d.ts +2 -2
- package/dist/core/tools/ls.d.ts.map +1 -1
- package/dist/core/tools/ls.js +12 -0
- package/dist/core/tools/ls.js.map +1 -1
- package/dist/core/tools/output-accumulator.d.ts.map +1 -1
- package/dist/core/tools/output-accumulator.js +1 -1
- package/dist/core/tools/output-accumulator.js.map +1 -1
- package/dist/core/tools/path-permission.d.ts +18 -0
- package/dist/core/tools/path-permission.d.ts.map +1 -0
- package/dist/core/tools/path-permission.js +28 -0
- package/dist/core/tools/path-permission.js.map +1 -0
- package/dist/core/tools/plan-present.d.ts +27 -0
- package/dist/core/tools/plan-present.d.ts.map +1 -0
- package/dist/core/tools/plan-present.js +55 -0
- package/dist/core/tools/plan-present.js.map +1 -0
- package/dist/core/tools/read.d.ts +2 -2
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +12 -0
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/test.d.ts +34 -0
- package/dist/core/tools/test.d.ts.map +1 -0
- package/dist/core/tools/test.js +75 -0
- package/dist/core/tools/test.js.map +1 -0
- package/dist/core/tools/todo-write.d.ts +31 -0
- package/dist/core/tools/todo-write.d.ts.map +1 -0
- package/dist/core/tools/todo-write.js +51 -0
- package/dist/core/tools/todo-write.js.map +1 -0
- package/dist/core/tools/tool-definition-wrapper.d.ts +13 -2
- package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
- package/dist/core/tools/tool-definition-wrapper.js +11 -1
- package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
- package/dist/core/tools/tool-schema.d.ts +22 -0
- package/dist/core/tools/tool-schema.d.ts.map +1 -0
- package/dist/core/tools/tool-schema.js +45 -0
- package/dist/core/tools/tool-schema.js.map +1 -0
- package/dist/core/tools/web-fetch.d.ts +26 -0
- package/dist/core/tools/web-fetch.d.ts.map +1 -0
- package/dist/core/tools/web-fetch.js +62 -0
- package/dist/core/tools/web-fetch.js.map +1 -0
- package/dist/core/tools/web-search.d.ts +26 -0
- package/dist/core/tools/web-search.d.ts.map +1 -0
- package/dist/core/tools/web-search.js +53 -0
- package/dist/core/tools/web-search.js.map +1 -0
- package/dist/core/tools/write.d.ts +6 -2
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +32 -1
- package/dist/core/tools/write.js.map +1 -1
- package/dist/core/usage-performance-store.d.ts +56 -0
- package/dist/core/usage-performance-store.d.ts.map +1 -0
- package/dist/core/usage-performance-store.js +85 -0
- package/dist/core/usage-performance-store.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +48 -6
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +2 -2
- package/dist/migrations.js.map +1 -1
- package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/config-selector.js +9 -6
- package/dist/modes/interactive/components/config-selector.js.map +1 -1
- package/dist/modes/interactive/components/first-time-setup.d.ts +1 -4
- package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -1
- package/dist/modes/interactive/components/first-time-setup.js +11 -39
- package/dist/modes/interactive/components/first-time-setup.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts +8 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +49 -20
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +2 -2
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +6 -6
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +2 -1
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/external-editor.d.ts.map +1 -1
- package/dist/modes/interactive/external-editor.js +58 -9
- package/dist/modes/interactive/external-editor.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +2 -3
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +92 -104
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/theme-schema.json +2 -2
- package/dist/package-manager-cli.d.ts.map +1 -1
- package/dist/package-manager-cli.js +15 -10
- package/dist/package-manager-cli.js.map +1 -1
- package/dist/rpc-entry.d.ts.map +1 -1
- package/dist/rpc-entry.js +2 -1
- package/dist/rpc-entry.js.map +1 -1
- package/dist/server/create-harness.d.ts +1 -1
- package/dist/server/create-harness.d.ts.map +1 -1
- package/dist/server/create-harness.js +6 -5
- package/dist/server/create-harness.js.map +1 -1
- package/dist/testing/replay/metrics.d.ts +24 -0
- package/dist/testing/replay/metrics.d.ts.map +1 -0
- package/dist/testing/replay/metrics.js +35 -0
- package/dist/testing/replay/metrics.js.map +1 -0
- package/dist/testing/replay/recorded-provider.d.ts +10 -0
- package/dist/testing/replay/recorded-provider.d.ts.map +1 -0
- package/dist/testing/replay/recorded-provider.js +108 -0
- package/dist/testing/replay/recorded-provider.js.map +1 -0
- package/dist/testing/replay/runner.d.ts +56 -0
- package/dist/testing/replay/runner.d.ts.map +1 -0
- package/dist/testing/replay/runner.js +406 -0
- package/dist/testing/replay/runner.js.map +1 -0
- package/dist/utils/apex-code-user-agent.d.ts +2 -0
- package/dist/utils/apex-code-user-agent.d.ts.map +1 -0
- package/dist/utils/apex-code-user-agent.js +5 -0
- package/dist/utils/apex-code-user-agent.js.map +1 -0
- package/dist/utils/clipboard-image.d.ts.map +1 -1
- package/dist/utils/clipboard-image.js +1 -1
- package/dist/utils/clipboard-image.js.map +1 -1
- package/dist/utils/tools-manager.d.ts +32 -0
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +198 -100
- package/dist/utils/tools-manager.js.map +1 -1
- package/dist/utils/version-check.d.ts +5 -5
- package/dist/utils/version-check.d.ts.map +1 -1
- package/dist/utils/version-check.js +23 -14
- package/dist/utils/version-check.js.map +1 -1
- package/dist/utils/windows-self-update.d.ts.map +1 -1
- package/dist/utils/windows-self-update.js +1 -1
- package/dist/utils/windows-self-update.js.map +1 -1
- package/docs/compaction.md +21 -21
- package/docs/containerization.md +24 -21
- package/docs/custom-provider.md +9 -9
- package/docs/development.md +11 -11
- package/docs/environment-variables.md +36 -77
- package/docs/extensions.md +47 -47
- package/docs/index.md +12 -24
- package/docs/json.md +6 -6
- package/docs/keybindings.md +3 -3
- package/docs/llama-cpp.md +7 -7
- package/docs/models.md +11 -11
- package/docs/packages.md +46 -42
- package/docs/prompt-templates.md +3 -3
- package/docs/providers.md +10 -10
- package/docs/quickstart.md +49 -32
- package/docs/rpc.md +11 -11
- package/docs/sdk.md +4 -4
- package/docs/security.md +18 -18
- package/docs/session-format.md +9 -9
- package/docs/sessions.md +13 -13
- package/docs/settings.md +10 -14
- package/docs/shell-aliases.md +1 -1
- package/docs/skills.md +8 -8
- package/docs/terminal-setup.md +12 -12
- package/docs/termux.md +4 -4
- package/docs/themes.md +8 -8
- package/docs/tmux.md +4 -4
- package/docs/tui.md +3 -3
- package/docs/usage.md +54 -51
- package/docs/windows.md +1 -1
- package/npm-shrinkwrap.json +5 -5
- package/package.json +2 -3
- package/dist/core/telemetry.d.ts +0 -3
- package/dist/core/telemetry.d.ts.map +0 -1
- package/dist/core/telemetry.js +0 -9
- package/dist/core/telemetry.js.map +0 -1
- package/dist/utils/pi-user-agent.d.ts +0 -2
- package/dist/utils/pi-user-agent.d.ts.map +0 -1
- package/dist/utils/pi-user-agent.js +0 -5
- package/dist/utils/pi-user-agent.js.map +0 -1
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persistence for the permission rule model (ADR 0004). Four of the eight sources
|
|
3
|
+
* are file-backed (policy, local, project, user); the other four are runtime-only
|
|
4
|
+
* (flag, cliArg — parsed once from argv, not stored here; command, session — held
|
|
5
|
+
* in memory for the life of the process). `session`-source updates are never
|
|
6
|
+
* written to disk: "allow for this session" dies with the session by design.
|
|
7
|
+
*
|
|
8
|
+
* Reuses the locked-JSON-file backend already proven for Phase 1's operational
|
|
9
|
+
* state (FileAuthStorageBackend) rather than widening `SettingsScope`, which only
|
|
10
|
+
* covers two of these four file-backed sources and represents a different, larger
|
|
11
|
+
* settings domain.
|
|
12
|
+
*/
|
|
13
|
+
import { type AuthStorageBackend } from "../auth-storage.ts";
|
|
14
|
+
import type { PermissionRule, PermissionSource } from "./rules.ts";
|
|
15
|
+
export declare const PERMISSION_MODES: readonly ["default", "plan", "acceptEdits", "bypassPermissions", "dontAsk"];
|
|
16
|
+
export type PermissionMode = (typeof PERMISSION_MODES)[number];
|
|
17
|
+
/** Sources a PermissionUpdate may target. Excludes `policy` (administrator-managed,
|
|
18
|
+
* never user-writable) and `flag`/`cliArg` (parsed once from argv at startup, not
|
|
19
|
+
* something this store updates after the fact). */
|
|
20
|
+
export declare const WRITABLE_PERMISSION_SOURCES: readonly ["local", "project", "user", "command", "session"];
|
|
21
|
+
export type WritablePermissionSource = (typeof WRITABLE_PERMISSION_SOURCES)[number];
|
|
22
|
+
declare const FILE_BACKED_SOURCES: readonly ["local", "project", "user"];
|
|
23
|
+
type FileBackedSource = (typeof FILE_BACKED_SOURCES)[number];
|
|
24
|
+
/** A rule as persisted: `source` is never stored — it is implied by which file it came from. */
|
|
25
|
+
export type StoredPermissionRule = Omit<PermissionRule, "source">;
|
|
26
|
+
export interface RuleMatcher {
|
|
27
|
+
toolName: string;
|
|
28
|
+
ruleContent?: string;
|
|
29
|
+
}
|
|
30
|
+
export type PermissionUpdate = {
|
|
31
|
+
type: "addRules";
|
|
32
|
+
destination: WritablePermissionSource;
|
|
33
|
+
rules: readonly StoredPermissionRule[];
|
|
34
|
+
} | {
|
|
35
|
+
type: "replaceRules";
|
|
36
|
+
destination: WritablePermissionSource;
|
|
37
|
+
rules: readonly StoredPermissionRule[];
|
|
38
|
+
} | {
|
|
39
|
+
type: "removeRules";
|
|
40
|
+
destination: WritablePermissionSource;
|
|
41
|
+
matching: readonly RuleMatcher[];
|
|
42
|
+
} | {
|
|
43
|
+
type: "setMode";
|
|
44
|
+
destination: WritablePermissionSource;
|
|
45
|
+
mode: PermissionMode;
|
|
46
|
+
};
|
|
47
|
+
export interface PermissionStoreError {
|
|
48
|
+
source: PermissionSource;
|
|
49
|
+
error: Error;
|
|
50
|
+
}
|
|
51
|
+
export interface PermissionStoreSnapshot {
|
|
52
|
+
rules: readonly PermissionRule[];
|
|
53
|
+
modesBySource: ReadonlyMap<WritablePermissionSource, PermissionMode>;
|
|
54
|
+
errors: readonly PermissionStoreError[];
|
|
55
|
+
}
|
|
56
|
+
export interface PermissionRuleStore {
|
|
57
|
+
snapshot(): Promise<PermissionStoreSnapshot>;
|
|
58
|
+
apply(update: PermissionUpdate): Promise<void>;
|
|
59
|
+
}
|
|
60
|
+
export interface CreateFilePermissionRuleStoreOptions {
|
|
61
|
+
cwd: string;
|
|
62
|
+
agentDir?: string;
|
|
63
|
+
/** Overrides the managed policy file path. Defaults to $APEX_CODE_POLICY_PATH, else an OS-specific system location. */
|
|
64
|
+
policyPath?: string;
|
|
65
|
+
/** Test seam: inject backends for the three writable file-backed sources instead of touching real files. */
|
|
66
|
+
backends?: Partial<Record<FileBackedSource, AuthStorageBackend>>;
|
|
67
|
+
/** Immutable argv layers (`flag` / `cliArg`), validated by the CLI before construction. */
|
|
68
|
+
initialRules?: readonly PermissionRule[];
|
|
69
|
+
}
|
|
70
|
+
/** File-backed store for policy/local/project/user, in-memory for command/session. */
|
|
71
|
+
export declare class FilePermissionRuleStore implements PermissionRuleStore {
|
|
72
|
+
private readonly backends;
|
|
73
|
+
private readonly policyPath;
|
|
74
|
+
private readonly initialRules;
|
|
75
|
+
private readonly runtimeRules;
|
|
76
|
+
private readonly runtimeModes;
|
|
77
|
+
constructor(options: CreateFilePermissionRuleStoreOptions);
|
|
78
|
+
private readFileBackedScope;
|
|
79
|
+
private readPolicy;
|
|
80
|
+
snapshot(): Promise<PermissionStoreSnapshot>;
|
|
81
|
+
apply(update: PermissionUpdate): Promise<void>;
|
|
82
|
+
private applyRuntime;
|
|
83
|
+
}
|
|
84
|
+
export interface DerivedPermissionRuleStoreOptions {
|
|
85
|
+
/** The parent's live store. Read fresh on every `snapshot()`, never cached, so a
|
|
86
|
+
* rule the parent picks up mid-session (e.g. a human's "always allow") is visible
|
|
87
|
+
* to the child on its very next call. */
|
|
88
|
+
parent: PermissionRuleStore;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* A delegated child's permission store (roadmap Phase 5, ADR 0008). A read-through
|
|
92
|
+
* view over the parent's live snapshot -- including its runtime-only sources
|
|
93
|
+
* (`flag`, `cliArg`, `command`, `session`), which a subprocess-spawned child could
|
|
94
|
+
* never observe -- plus a child-local runtime overlay for the child's own writes.
|
|
95
|
+
*
|
|
96
|
+
* `apply()` never reaches the parent. It writes only into this instance's own
|
|
97
|
+
* overlay, and only for the two runtime-only writable sources (`command`, `session`);
|
|
98
|
+
* a file-backed destination (`local`, `project`, `user`) is rejected outright, not
|
|
99
|
+
* silently redirected -- a delegated child must never be able to persist to the
|
|
100
|
+
* parent's settings files, and "runtime-only" is enforced by construction here, not
|
|
101
|
+
* by a convention a future caller could forget. The overlay is discarded with the
|
|
102
|
+
* child: there is no code path from a child's approval back into the parent's store,
|
|
103
|
+
* which is what keeps a child's `persist: true` from widening its parent.
|
|
104
|
+
*/
|
|
105
|
+
export declare class DerivedPermissionRuleStore implements PermissionRuleStore {
|
|
106
|
+
private readonly parent;
|
|
107
|
+
private readonly overlayRules;
|
|
108
|
+
private readonly overlayModes;
|
|
109
|
+
constructor(options: DerivedPermissionRuleStoreOptions);
|
|
110
|
+
snapshot(): Promise<PermissionStoreSnapshot>;
|
|
111
|
+
apply(update: PermissionUpdate): Promise<void>;
|
|
112
|
+
}
|
|
113
|
+
export {};
|
|
114
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../src/core/permissions/store.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Persistence for the permission rule model (ADR 0004). Four of the eight sources\n * are file-backed (policy, local, project, user); the other four are runtime-only\n * (flag, cliArg — parsed once from argv, not stored here; command, session — held\n * in memory for the life of the process). `session`-source updates are never\n * written to disk: \"allow for this session\" dies with the session by design.\n *\n * Reuses the locked-JSON-file backend already proven for Phase 1's operational\n * state (FileAuthStorageBackend) rather than widening `SettingsScope`, which only\n * covers two of these four file-backed sources and represents a different, larger\n * settings domain.\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { getAgentDir } from \"../../config.ts\";\nimport { resolvePath } from \"../../utils/paths.ts\";\nimport { type AuthStorageBackend, FileAuthStorageBackend } from \"../auth-storage.ts\";\nimport type { PermissionBehavior } from \"../tools/contract.ts\";\nimport type { PermissionRule, PermissionSource } from \"./rules.ts\";\n\nexport const PERMISSION_MODES = [\"default\", \"plan\", \"acceptEdits\", \"bypassPermissions\", \"dontAsk\"] as const;\nexport type PermissionMode = (typeof PERMISSION_MODES)[number];\n\n/** Sources a PermissionUpdate may target. Excludes `policy` (administrator-managed,\n * never user-writable) and `flag`/`cliArg` (parsed once from argv at startup, not\n * something this store updates after the fact). */\nexport const WRITABLE_PERMISSION_SOURCES = [\"local\", \"project\", \"user\", \"command\", \"session\"] as const;\nexport type WritablePermissionSource = (typeof WRITABLE_PERMISSION_SOURCES)[number];\n\nconst FILE_BACKED_SOURCES = [\"local\", \"project\", \"user\"] as const;\ntype FileBackedSource = (typeof FILE_BACKED_SOURCES)[number];\n\nconst RUNTIME_SOURCES = [\"command\", \"session\"] as const;\ntype RuntimeSource = (typeof RUNTIME_SOURCES)[number];\n\n/** A rule as persisted: `source` is never stored — it is implied by which file it came from. */\nexport type StoredPermissionRule = Omit<PermissionRule, \"source\">;\n\nexport interface RuleMatcher {\n\ttoolName: string;\n\truleContent?: string;\n}\n\nexport type PermissionUpdate =\n\t| { type: \"addRules\"; destination: WritablePermissionSource; rules: readonly StoredPermissionRule[] }\n\t| { type: \"replaceRules\"; destination: WritablePermissionSource; rules: readonly StoredPermissionRule[] }\n\t| { type: \"removeRules\"; destination: WritablePermissionSource; matching: readonly RuleMatcher[] }\n\t| { type: \"setMode\"; destination: WritablePermissionSource; mode: PermissionMode };\n\nexport interface PermissionStoreError {\n\tsource: PermissionSource;\n\terror: Error;\n}\n\nexport interface PermissionStoreSnapshot {\n\trules: readonly PermissionRule[];\n\tmodesBySource: ReadonlyMap<WritablePermissionSource, PermissionMode>;\n\terrors: readonly PermissionStoreError[];\n}\n\nexport interface PermissionRuleStore {\n\tsnapshot(): Promise<PermissionStoreSnapshot>;\n\tapply(update: PermissionUpdate): Promise<void>;\n}\n\nconst STORE_VERSION = 1;\n\ninterface StoredPermissionScope {\n\tversion: number;\n\trules: StoredPermissionRule[];\n\tmode?: PermissionMode;\n}\n\nfunction emptyScope(): StoredPermissionScope {\n\treturn { version: STORE_VERSION, rules: [] };\n}\n\nfunction isPermissionBehavior(value: unknown): value is PermissionBehavior {\n\treturn value === \"allow\" || value === \"deny\" || value === \"ask\";\n}\n\nfunction parseScope(content: string | undefined): StoredPermissionScope {\n\tif (!content) return emptyScope();\n\tconst parsed = JSON.parse(content);\n\tif (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n\t\tthrow new Error(\"Invalid permission rules file: unexpected shape\");\n\t}\n\t// FileAuthStorageBackend (shared with auth.json) seeds a freshly created file\n\t// with the literal text \"{}\". Treat a content-free object as \"nothing written\n\t// yet\" rather than malformed; a real malformed file has some recognizable but\n\t// wrong-shaped field, caught below.\n\tif (!(\"version\" in parsed) && !(\"rules\" in parsed) && !(\"mode\" in parsed)) {\n\t\treturn emptyScope();\n\t}\n\tif (\n\t\tparsed.version !== STORE_VERSION ||\n\t\t(parsed.mode !== undefined && !(PERMISSION_MODES as readonly string[]).includes(parsed.mode)) ||\n\t\t!Array.isArray(parsed.rules) ||\n\t\t!parsed.rules.every(\n\t\t\t(rule: unknown) =>\n\t\t\t\ttypeof rule === \"object\" &&\n\t\t\t\trule !== null &&\n\t\t\t\ttypeof (rule as StoredPermissionRule).toolName === \"string\" &&\n\t\t\t\tisPermissionBehavior((rule as StoredPermissionRule).behavior) &&\n\t\t\t\t((rule as StoredPermissionRule).ruleContent === undefined ||\n\t\t\t\t\ttypeof (rule as StoredPermissionRule).ruleContent === \"string\"),\n\t\t)\n\t) {\n\t\tthrow new Error(\"Invalid permission rules file: unexpected shape\");\n\t}\n\treturn parsed as StoredPermissionScope;\n}\n\nfunction applyToScope(scope: StoredPermissionScope, update: PermissionUpdate): StoredPermissionScope {\n\tswitch (update.type) {\n\t\tcase \"addRules\":\n\t\t\treturn { ...scope, rules: [...scope.rules, ...update.rules] };\n\t\tcase \"replaceRules\":\n\t\t\treturn { ...scope, rules: [...update.rules] };\n\t\tcase \"removeRules\":\n\t\t\treturn {\n\t\t\t\t...scope,\n\t\t\t\trules: scope.rules.filter(\n\t\t\t\t\t(rule) =>\n\t\t\t\t\t\t!update.matching.some((m) => m.toolName === rule.toolName && m.ruleContent === rule.ruleContent),\n\t\t\t\t),\n\t\t\t};\n\t\tcase \"setMode\":\n\t\t\treturn { ...scope, mode: update.mode };\n\t}\n}\n\nfunction defaultPolicyPath(): string {\n\tif (process.env.APEX_CODE_POLICY_PATH) return process.env.APEX_CODE_POLICY_PATH;\n\treturn process.platform === \"win32\"\n\t\t? join(process.env.ProgramData ?? \"C:\\\\ProgramData\", \"apex-code\", \"policy.json\")\n\t\t: \"/etc/apex-code/policy.json\";\n}\n\nexport interface CreateFilePermissionRuleStoreOptions {\n\tcwd: string;\n\tagentDir?: string;\n\t/** Overrides the managed policy file path. Defaults to $APEX_CODE_POLICY_PATH, else an OS-specific system location. */\n\tpolicyPath?: string;\n\t/** Test seam: inject backends for the three writable file-backed sources instead of touching real files. */\n\tbackends?: Partial<Record<FileBackedSource, AuthStorageBackend>>;\n\t/** Immutable argv layers (`flag` / `cliArg`), validated by the CLI before construction. */\n\tinitialRules?: readonly PermissionRule[];\n}\n\n/** File-backed store for policy/local/project/user, in-memory for command/session. */\nexport class FilePermissionRuleStore implements PermissionRuleStore {\n\tprivate readonly backends: Record<FileBackedSource, AuthStorageBackend>;\n\tprivate readonly policyPath: string;\n\tprivate readonly initialRules: readonly PermissionRule[];\n\tprivate readonly runtimeRules: Record<RuntimeSource, StoredPermissionRule[]> = { command: [], session: [] };\n\tprivate readonly runtimeModes: Partial<Record<RuntimeSource, PermissionMode>> = {};\n\n\tconstructor(options: CreateFilePermissionRuleStoreOptions) {\n\t\tconst agentDir = options.agentDir ?? getAgentDir();\n\t\tconst cwd = resolvePath(options.cwd);\n\t\tthis.backends = {\n\t\t\tuser: new FileAuthStorageBackend(join(agentDir, \"permissions.json\")),\n\t\t\tproject: new FileAuthStorageBackend(join(cwd, \".apex-code\", \"permissions.json\")),\n\t\t\tlocal: new FileAuthStorageBackend(join(cwd, \".apex-code\", \"permissions.local.json\")),\n\t\t\t...options.backends,\n\t\t};\n\t\tthis.policyPath = options.policyPath ?? defaultPolicyPath();\n\t\tthis.initialRules = options.initialRules ?? [];\n\t}\n\n\tprivate async readFileBackedScope(\n\t\tsource: FileBackedSource,\n\t): Promise<{ scope: StoredPermissionScope; error?: Error }> {\n\t\ttry {\n\t\t\tlet content: string | undefined;\n\t\t\tawait this.backends[source].withLockAsync(async (current) => {\n\t\t\t\tcontent = current;\n\t\t\t\treturn { result: undefined };\n\t\t\t});\n\t\t\treturn { scope: parseScope(content) };\n\t\t} catch (error) {\n\t\t\treturn { scope: emptyScope(), error: error instanceof Error ? error : new Error(String(error)) };\n\t\t}\n\t}\n\n\tprivate async readPolicy(): Promise<{ scope: StoredPermissionScope; error?: Error }> {\n\t\ttry {\n\t\t\tconst content = await readFile(this.policyPath, \"utf-8\");\n\t\t\treturn { scope: parseScope(content) };\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") return { scope: emptyScope() };\n\t\t\treturn { scope: emptyScope(), error: error instanceof Error ? error : new Error(String(error)) };\n\t\t}\n\t}\n\n\tasync snapshot(): Promise<PermissionStoreSnapshot> {\n\t\tconst [policy, local, project, user] = await Promise.all([\n\t\t\tthis.readPolicy(),\n\t\t\tthis.readFileBackedScope(\"local\"),\n\t\t\tthis.readFileBackedScope(\"project\"),\n\t\t\tthis.readFileBackedScope(\"user\"),\n\t\t]);\n\t\tconst errors: PermissionStoreError[] = [];\n\t\tconst withSource = (\n\t\t\tsource: PermissionSource,\n\t\t\tentry: { scope: StoredPermissionScope; error?: Error },\n\t\t): PermissionRule[] => {\n\t\t\tif (entry.error) errors.push({ source, error: entry.error });\n\t\t\treturn entry.scope.rules.map((rule) => ({ ...rule, source }));\n\t\t};\n\n\t\tconst rules: PermissionRule[] = [\n\t\t\t...withSource(\"policy\", policy),\n\t\t\t...this.initialRules,\n\t\t\t...withSource(\"local\", local),\n\t\t\t...withSource(\"project\", project),\n\t\t\t...withSource(\"user\", user),\n\t\t\t...this.runtimeRules.command.map((rule) => ({ ...rule, source: \"command\" as const })),\n\t\t\t...this.runtimeRules.session.map((rule) => ({ ...rule, source: \"session\" as const })),\n\t\t];\n\n\t\tconst modesBySource = new Map<WritablePermissionSource, PermissionMode>();\n\t\tif (local.scope.mode) modesBySource.set(\"local\", local.scope.mode);\n\t\tif (project.scope.mode) modesBySource.set(\"project\", project.scope.mode);\n\t\tif (user.scope.mode) modesBySource.set(\"user\", user.scope.mode);\n\t\tif (this.runtimeModes.command) modesBySource.set(\"command\", this.runtimeModes.command);\n\t\tif (this.runtimeModes.session) modesBySource.set(\"session\", this.runtimeModes.session);\n\n\t\treturn { rules, modesBySource, errors };\n\t}\n\n\tasync apply(update: PermissionUpdate): Promise<void> {\n\t\tif (update.destination === \"command\" || update.destination === \"session\") {\n\t\t\tthis.applyRuntime(update.destination, update);\n\t\t\treturn;\n\t\t}\n\t\tawait this.backends[update.destination].withLockAsync(async (content) => {\n\t\t\tconst next = applyToScope(parseScope(content), update);\n\t\t\treturn { result: undefined, next: JSON.stringify(next, null, 2) };\n\t\t});\n\t}\n\n\tprivate applyRuntime(destination: RuntimeSource, update: PermissionUpdate): void {\n\t\tif (update.type === \"setMode\") {\n\t\t\tthis.runtimeModes[destination] = update.mode;\n\t\t\treturn;\n\t\t}\n\t\tconst current: StoredPermissionScope = { version: STORE_VERSION, rules: this.runtimeRules[destination] };\n\t\tthis.runtimeRules[destination] = applyToScope(current, update).rules;\n\t}\n}\n\nexport interface DerivedPermissionRuleStoreOptions {\n\t/** The parent's live store. Read fresh on every `snapshot()`, never cached, so a\n\t * rule the parent picks up mid-session (e.g. a human's \"always allow\") is visible\n\t * to the child on its very next call. */\n\tparent: PermissionRuleStore;\n}\n\n/**\n * A delegated child's permission store (roadmap Phase 5, ADR 0008). A read-through\n * view over the parent's live snapshot -- including its runtime-only sources\n * (`flag`, `cliArg`, `command`, `session`), which a subprocess-spawned child could\n * never observe -- plus a child-local runtime overlay for the child's own writes.\n *\n * `apply()` never reaches the parent. It writes only into this instance's own\n * overlay, and only for the two runtime-only writable sources (`command`, `session`);\n * a file-backed destination (`local`, `project`, `user`) is rejected outright, not\n * silently redirected -- a delegated child must never be able to persist to the\n * parent's settings files, and \"runtime-only\" is enforced by construction here, not\n * by a convention a future caller could forget. The overlay is discarded with the\n * child: there is no code path from a child's approval back into the parent's store,\n * which is what keeps a child's `persist: true` from widening its parent.\n */\nexport class DerivedPermissionRuleStore implements PermissionRuleStore {\n\tprivate readonly parent: PermissionRuleStore;\n\tprivate readonly overlayRules: Record<RuntimeSource, StoredPermissionRule[]> = { command: [], session: [] };\n\tprivate readonly overlayModes: Partial<Record<RuntimeSource, PermissionMode>> = {};\n\n\tconstructor(options: DerivedPermissionRuleStoreOptions) {\n\t\tthis.parent = options.parent;\n\t}\n\n\tasync snapshot(): Promise<PermissionStoreSnapshot> {\n\t\tconst parentSnapshot = await this.parent.snapshot();\n\t\tconst rules: PermissionRule[] = [\n\t\t\t...parentSnapshot.rules,\n\t\t\t...this.overlayRules.command.map((rule) => ({ ...rule, source: \"command\" as const })),\n\t\t\t...this.overlayRules.session.map((rule) => ({ ...rule, source: \"session\" as const })),\n\t\t];\n\t\tconst modesBySource = new Map(parentSnapshot.modesBySource);\n\t\tif (this.overlayModes.command !== undefined) modesBySource.set(\"command\", this.overlayModes.command);\n\t\tif (this.overlayModes.session !== undefined) modesBySource.set(\"session\", this.overlayModes.session);\n\t\treturn { rules, modesBySource, errors: parentSnapshot.errors };\n\t}\n\n\tasync apply(update: PermissionUpdate): Promise<void> {\n\t\tif (update.destination !== \"command\" && update.destination !== \"session\") {\n\t\t\tthrow new Error(\n\t\t\t\t`A delegated child cannot persist permission rules to \"${update.destination}\" -- only \"command\" and \"session\" are writable from a derived store (ADR 0008).`,\n\t\t\t);\n\t\t}\n\t\tif (update.type === \"setMode\") {\n\t\t\tthis.overlayModes[update.destination] = update.mode;\n\t\t\treturn;\n\t\t}\n\t\tconst current: StoredPermissionScope = {\n\t\t\tversion: STORE_VERSION,\n\t\t\trules: this.overlayRules[update.destination],\n\t\t};\n\t\tthis.overlayRules[update.destination] = applyToScope(current, update).rules;\n\t}\n}\n"]}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persistence for the permission rule model (ADR 0004). Four of the eight sources
|
|
3
|
+
* are file-backed (policy, local, project, user); the other four are runtime-only
|
|
4
|
+
* (flag, cliArg — parsed once from argv, not stored here; command, session — held
|
|
5
|
+
* in memory for the life of the process). `session`-source updates are never
|
|
6
|
+
* written to disk: "allow for this session" dies with the session by design.
|
|
7
|
+
*
|
|
8
|
+
* Reuses the locked-JSON-file backend already proven for Phase 1's operational
|
|
9
|
+
* state (FileAuthStorageBackend) rather than widening `SettingsScope`, which only
|
|
10
|
+
* covers two of these four file-backed sources and represents a different, larger
|
|
11
|
+
* settings domain.
|
|
12
|
+
*/
|
|
13
|
+
import { readFile } from "node:fs/promises";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { getAgentDir } from "../../config.js";
|
|
16
|
+
import { resolvePath } from "../../utils/paths.js";
|
|
17
|
+
import { FileAuthStorageBackend } from "../auth-storage.js";
|
|
18
|
+
export const PERMISSION_MODES = ["default", "plan", "acceptEdits", "bypassPermissions", "dontAsk"];
|
|
19
|
+
/** Sources a PermissionUpdate may target. Excludes `policy` (administrator-managed,
|
|
20
|
+
* never user-writable) and `flag`/`cliArg` (parsed once from argv at startup, not
|
|
21
|
+
* something this store updates after the fact). */
|
|
22
|
+
export const WRITABLE_PERMISSION_SOURCES = ["local", "project", "user", "command", "session"];
|
|
23
|
+
const FILE_BACKED_SOURCES = ["local", "project", "user"];
|
|
24
|
+
const RUNTIME_SOURCES = ["command", "session"];
|
|
25
|
+
const STORE_VERSION = 1;
|
|
26
|
+
function emptyScope() {
|
|
27
|
+
return { version: STORE_VERSION, rules: [] };
|
|
28
|
+
}
|
|
29
|
+
function isPermissionBehavior(value) {
|
|
30
|
+
return value === "allow" || value === "deny" || value === "ask";
|
|
31
|
+
}
|
|
32
|
+
function parseScope(content) {
|
|
33
|
+
if (!content)
|
|
34
|
+
return emptyScope();
|
|
35
|
+
const parsed = JSON.parse(content);
|
|
36
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
37
|
+
throw new Error("Invalid permission rules file: unexpected shape");
|
|
38
|
+
}
|
|
39
|
+
// FileAuthStorageBackend (shared with auth.json) seeds a freshly created file
|
|
40
|
+
// with the literal text "{}". Treat a content-free object as "nothing written
|
|
41
|
+
// yet" rather than malformed; a real malformed file has some recognizable but
|
|
42
|
+
// wrong-shaped field, caught below.
|
|
43
|
+
if (!("version" in parsed) && !("rules" in parsed) && !("mode" in parsed)) {
|
|
44
|
+
return emptyScope();
|
|
45
|
+
}
|
|
46
|
+
if (parsed.version !== STORE_VERSION ||
|
|
47
|
+
(parsed.mode !== undefined && !PERMISSION_MODES.includes(parsed.mode)) ||
|
|
48
|
+
!Array.isArray(parsed.rules) ||
|
|
49
|
+
!parsed.rules.every((rule) => typeof rule === "object" &&
|
|
50
|
+
rule !== null &&
|
|
51
|
+
typeof rule.toolName === "string" &&
|
|
52
|
+
isPermissionBehavior(rule.behavior) &&
|
|
53
|
+
(rule.ruleContent === undefined ||
|
|
54
|
+
typeof rule.ruleContent === "string"))) {
|
|
55
|
+
throw new Error("Invalid permission rules file: unexpected shape");
|
|
56
|
+
}
|
|
57
|
+
return parsed;
|
|
58
|
+
}
|
|
59
|
+
function applyToScope(scope, update) {
|
|
60
|
+
switch (update.type) {
|
|
61
|
+
case "addRules":
|
|
62
|
+
return { ...scope, rules: [...scope.rules, ...update.rules] };
|
|
63
|
+
case "replaceRules":
|
|
64
|
+
return { ...scope, rules: [...update.rules] };
|
|
65
|
+
case "removeRules":
|
|
66
|
+
return {
|
|
67
|
+
...scope,
|
|
68
|
+
rules: scope.rules.filter((rule) => !update.matching.some((m) => m.toolName === rule.toolName && m.ruleContent === rule.ruleContent)),
|
|
69
|
+
};
|
|
70
|
+
case "setMode":
|
|
71
|
+
return { ...scope, mode: update.mode };
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function defaultPolicyPath() {
|
|
75
|
+
if (process.env.APEX_CODE_POLICY_PATH)
|
|
76
|
+
return process.env.APEX_CODE_POLICY_PATH;
|
|
77
|
+
return process.platform === "win32"
|
|
78
|
+
? join(process.env.ProgramData ?? "C:\\ProgramData", "apex-code", "policy.json")
|
|
79
|
+
: "/etc/apex-code/policy.json";
|
|
80
|
+
}
|
|
81
|
+
/** File-backed store for policy/local/project/user, in-memory for command/session. */
|
|
82
|
+
export class FilePermissionRuleStore {
|
|
83
|
+
backends;
|
|
84
|
+
policyPath;
|
|
85
|
+
initialRules;
|
|
86
|
+
runtimeRules = { command: [], session: [] };
|
|
87
|
+
runtimeModes = {};
|
|
88
|
+
constructor(options) {
|
|
89
|
+
const agentDir = options.agentDir ?? getAgentDir();
|
|
90
|
+
const cwd = resolvePath(options.cwd);
|
|
91
|
+
this.backends = {
|
|
92
|
+
user: new FileAuthStorageBackend(join(agentDir, "permissions.json")),
|
|
93
|
+
project: new FileAuthStorageBackend(join(cwd, ".apex-code", "permissions.json")),
|
|
94
|
+
local: new FileAuthStorageBackend(join(cwd, ".apex-code", "permissions.local.json")),
|
|
95
|
+
...options.backends,
|
|
96
|
+
};
|
|
97
|
+
this.policyPath = options.policyPath ?? defaultPolicyPath();
|
|
98
|
+
this.initialRules = options.initialRules ?? [];
|
|
99
|
+
}
|
|
100
|
+
async readFileBackedScope(source) {
|
|
101
|
+
try {
|
|
102
|
+
let content;
|
|
103
|
+
await this.backends[source].withLockAsync(async (current) => {
|
|
104
|
+
content = current;
|
|
105
|
+
return { result: undefined };
|
|
106
|
+
});
|
|
107
|
+
return { scope: parseScope(content) };
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
return { scope: emptyScope(), error: error instanceof Error ? error : new Error(String(error)) };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async readPolicy() {
|
|
114
|
+
try {
|
|
115
|
+
const content = await readFile(this.policyPath, "utf-8");
|
|
116
|
+
return { scope: parseScope(content) };
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
if (error.code === "ENOENT")
|
|
120
|
+
return { scope: emptyScope() };
|
|
121
|
+
return { scope: emptyScope(), error: error instanceof Error ? error : new Error(String(error)) };
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
async snapshot() {
|
|
125
|
+
const [policy, local, project, user] = await Promise.all([
|
|
126
|
+
this.readPolicy(),
|
|
127
|
+
this.readFileBackedScope("local"),
|
|
128
|
+
this.readFileBackedScope("project"),
|
|
129
|
+
this.readFileBackedScope("user"),
|
|
130
|
+
]);
|
|
131
|
+
const errors = [];
|
|
132
|
+
const withSource = (source, entry) => {
|
|
133
|
+
if (entry.error)
|
|
134
|
+
errors.push({ source, error: entry.error });
|
|
135
|
+
return entry.scope.rules.map((rule) => ({ ...rule, source }));
|
|
136
|
+
};
|
|
137
|
+
const rules = [
|
|
138
|
+
...withSource("policy", policy),
|
|
139
|
+
...this.initialRules,
|
|
140
|
+
...withSource("local", local),
|
|
141
|
+
...withSource("project", project),
|
|
142
|
+
...withSource("user", user),
|
|
143
|
+
...this.runtimeRules.command.map((rule) => ({ ...rule, source: "command" })),
|
|
144
|
+
...this.runtimeRules.session.map((rule) => ({ ...rule, source: "session" })),
|
|
145
|
+
];
|
|
146
|
+
const modesBySource = new Map();
|
|
147
|
+
if (local.scope.mode)
|
|
148
|
+
modesBySource.set("local", local.scope.mode);
|
|
149
|
+
if (project.scope.mode)
|
|
150
|
+
modesBySource.set("project", project.scope.mode);
|
|
151
|
+
if (user.scope.mode)
|
|
152
|
+
modesBySource.set("user", user.scope.mode);
|
|
153
|
+
if (this.runtimeModes.command)
|
|
154
|
+
modesBySource.set("command", this.runtimeModes.command);
|
|
155
|
+
if (this.runtimeModes.session)
|
|
156
|
+
modesBySource.set("session", this.runtimeModes.session);
|
|
157
|
+
return { rules, modesBySource, errors };
|
|
158
|
+
}
|
|
159
|
+
async apply(update) {
|
|
160
|
+
if (update.destination === "command" || update.destination === "session") {
|
|
161
|
+
this.applyRuntime(update.destination, update);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
await this.backends[update.destination].withLockAsync(async (content) => {
|
|
165
|
+
const next = applyToScope(parseScope(content), update);
|
|
166
|
+
return { result: undefined, next: JSON.stringify(next, null, 2) };
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
applyRuntime(destination, update) {
|
|
170
|
+
if (update.type === "setMode") {
|
|
171
|
+
this.runtimeModes[destination] = update.mode;
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
const current = { version: STORE_VERSION, rules: this.runtimeRules[destination] };
|
|
175
|
+
this.runtimeRules[destination] = applyToScope(current, update).rules;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* A delegated child's permission store (roadmap Phase 5, ADR 0008). A read-through
|
|
180
|
+
* view over the parent's live snapshot -- including its runtime-only sources
|
|
181
|
+
* (`flag`, `cliArg`, `command`, `session`), which a subprocess-spawned child could
|
|
182
|
+
* never observe -- plus a child-local runtime overlay for the child's own writes.
|
|
183
|
+
*
|
|
184
|
+
* `apply()` never reaches the parent. It writes only into this instance's own
|
|
185
|
+
* overlay, and only for the two runtime-only writable sources (`command`, `session`);
|
|
186
|
+
* a file-backed destination (`local`, `project`, `user`) is rejected outright, not
|
|
187
|
+
* silently redirected -- a delegated child must never be able to persist to the
|
|
188
|
+
* parent's settings files, and "runtime-only" is enforced by construction here, not
|
|
189
|
+
* by a convention a future caller could forget. The overlay is discarded with the
|
|
190
|
+
* child: there is no code path from a child's approval back into the parent's store,
|
|
191
|
+
* which is what keeps a child's `persist: true` from widening its parent.
|
|
192
|
+
*/
|
|
193
|
+
export class DerivedPermissionRuleStore {
|
|
194
|
+
parent;
|
|
195
|
+
overlayRules = { command: [], session: [] };
|
|
196
|
+
overlayModes = {};
|
|
197
|
+
constructor(options) {
|
|
198
|
+
this.parent = options.parent;
|
|
199
|
+
}
|
|
200
|
+
async snapshot() {
|
|
201
|
+
const parentSnapshot = await this.parent.snapshot();
|
|
202
|
+
const rules = [
|
|
203
|
+
...parentSnapshot.rules,
|
|
204
|
+
...this.overlayRules.command.map((rule) => ({ ...rule, source: "command" })),
|
|
205
|
+
...this.overlayRules.session.map((rule) => ({ ...rule, source: "session" })),
|
|
206
|
+
];
|
|
207
|
+
const modesBySource = new Map(parentSnapshot.modesBySource);
|
|
208
|
+
if (this.overlayModes.command !== undefined)
|
|
209
|
+
modesBySource.set("command", this.overlayModes.command);
|
|
210
|
+
if (this.overlayModes.session !== undefined)
|
|
211
|
+
modesBySource.set("session", this.overlayModes.session);
|
|
212
|
+
return { rules, modesBySource, errors: parentSnapshot.errors };
|
|
213
|
+
}
|
|
214
|
+
async apply(update) {
|
|
215
|
+
if (update.destination !== "command" && update.destination !== "session") {
|
|
216
|
+
throw new Error(`A delegated child cannot persist permission rules to "${update.destination}" -- only "command" and "session" are writable from a derived store (ADR 0008).`);
|
|
217
|
+
}
|
|
218
|
+
if (update.type === "setMode") {
|
|
219
|
+
this.overlayModes[update.destination] = update.mode;
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
const current = {
|
|
223
|
+
version: STORE_VERSION,
|
|
224
|
+
rules: this.overlayRules[update.destination],
|
|
225
|
+
};
|
|
226
|
+
this.overlayRules[update.destination] = applyToScope(current, update).rules;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../../src/core/permissions/store.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Persistence for the permission rule model (ADR 0004). Four of the eight sources\n * are file-backed (policy, local, project, user); the other four are runtime-only\n * (flag, cliArg — parsed once from argv, not stored here; command, session — held\n * in memory for the life of the process). `session`-source updates are never\n * written to disk: \"allow for this session\" dies with the session by design.\n *\n * Reuses the locked-JSON-file backend already proven for Phase 1's operational\n * state (FileAuthStorageBackend) rather than widening `SettingsScope`, which only\n * covers two of these four file-backed sources and represents a different, larger\n * settings domain.\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { getAgentDir } from \"../../config.ts\";\nimport { resolvePath } from \"../../utils/paths.ts\";\nimport { type AuthStorageBackend, FileAuthStorageBackend } from \"../auth-storage.ts\";\nimport type { PermissionBehavior } from \"../tools/contract.ts\";\nimport type { PermissionRule, PermissionSource } from \"./rules.ts\";\n\nexport const PERMISSION_MODES = [\"default\", \"plan\", \"acceptEdits\", \"bypassPermissions\", \"dontAsk\"] as const;\nexport type PermissionMode = (typeof PERMISSION_MODES)[number];\n\n/** Sources a PermissionUpdate may target. Excludes `policy` (administrator-managed,\n * never user-writable) and `flag`/`cliArg` (parsed once from argv at startup, not\n * something this store updates after the fact). */\nexport const WRITABLE_PERMISSION_SOURCES = [\"local\", \"project\", \"user\", \"command\", \"session\"] as const;\nexport type WritablePermissionSource = (typeof WRITABLE_PERMISSION_SOURCES)[number];\n\nconst FILE_BACKED_SOURCES = [\"local\", \"project\", \"user\"] as const;\ntype FileBackedSource = (typeof FILE_BACKED_SOURCES)[number];\n\nconst RUNTIME_SOURCES = [\"command\", \"session\"] as const;\ntype RuntimeSource = (typeof RUNTIME_SOURCES)[number];\n\n/** A rule as persisted: `source` is never stored — it is implied by which file it came from. */\nexport type StoredPermissionRule = Omit<PermissionRule, \"source\">;\n\nexport interface RuleMatcher {\n\ttoolName: string;\n\truleContent?: string;\n}\n\nexport type PermissionUpdate =\n\t| { type: \"addRules\"; destination: WritablePermissionSource; rules: readonly StoredPermissionRule[] }\n\t| { type: \"replaceRules\"; destination: WritablePermissionSource; rules: readonly StoredPermissionRule[] }\n\t| { type: \"removeRules\"; destination: WritablePermissionSource; matching: readonly RuleMatcher[] }\n\t| { type: \"setMode\"; destination: WritablePermissionSource; mode: PermissionMode };\n\nexport interface PermissionStoreError {\n\tsource: PermissionSource;\n\terror: Error;\n}\n\nexport interface PermissionStoreSnapshot {\n\trules: readonly PermissionRule[];\n\tmodesBySource: ReadonlyMap<WritablePermissionSource, PermissionMode>;\n\terrors: readonly PermissionStoreError[];\n}\n\nexport interface PermissionRuleStore {\n\tsnapshot(): Promise<PermissionStoreSnapshot>;\n\tapply(update: PermissionUpdate): Promise<void>;\n}\n\nconst STORE_VERSION = 1;\n\ninterface StoredPermissionScope {\n\tversion: number;\n\trules: StoredPermissionRule[];\n\tmode?: PermissionMode;\n}\n\nfunction emptyScope(): StoredPermissionScope {\n\treturn { version: STORE_VERSION, rules: [] };\n}\n\nfunction isPermissionBehavior(value: unknown): value is PermissionBehavior {\n\treturn value === \"allow\" || value === \"deny\" || value === \"ask\";\n}\n\nfunction parseScope(content: string | undefined): StoredPermissionScope {\n\tif (!content) return emptyScope();\n\tconst parsed = JSON.parse(content);\n\tif (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n\t\tthrow new Error(\"Invalid permission rules file: unexpected shape\");\n\t}\n\t// FileAuthStorageBackend (shared with auth.json) seeds a freshly created file\n\t// with the literal text \"{}\". Treat a content-free object as \"nothing written\n\t// yet\" rather than malformed; a real malformed file has some recognizable but\n\t// wrong-shaped field, caught below.\n\tif (!(\"version\" in parsed) && !(\"rules\" in parsed) && !(\"mode\" in parsed)) {\n\t\treturn emptyScope();\n\t}\n\tif (\n\t\tparsed.version !== STORE_VERSION ||\n\t\t(parsed.mode !== undefined && !(PERMISSION_MODES as readonly string[]).includes(parsed.mode)) ||\n\t\t!Array.isArray(parsed.rules) ||\n\t\t!parsed.rules.every(\n\t\t\t(rule: unknown) =>\n\t\t\t\ttypeof rule === \"object\" &&\n\t\t\t\trule !== null &&\n\t\t\t\ttypeof (rule as StoredPermissionRule).toolName === \"string\" &&\n\t\t\t\tisPermissionBehavior((rule as StoredPermissionRule).behavior) &&\n\t\t\t\t((rule as StoredPermissionRule).ruleContent === undefined ||\n\t\t\t\t\ttypeof (rule as StoredPermissionRule).ruleContent === \"string\"),\n\t\t)\n\t) {\n\t\tthrow new Error(\"Invalid permission rules file: unexpected shape\");\n\t}\n\treturn parsed as StoredPermissionScope;\n}\n\nfunction applyToScope(scope: StoredPermissionScope, update: PermissionUpdate): StoredPermissionScope {\n\tswitch (update.type) {\n\t\tcase \"addRules\":\n\t\t\treturn { ...scope, rules: [...scope.rules, ...update.rules] };\n\t\tcase \"replaceRules\":\n\t\t\treturn { ...scope, rules: [...update.rules] };\n\t\tcase \"removeRules\":\n\t\t\treturn {\n\t\t\t\t...scope,\n\t\t\t\trules: scope.rules.filter(\n\t\t\t\t\t(rule) =>\n\t\t\t\t\t\t!update.matching.some((m) => m.toolName === rule.toolName && m.ruleContent === rule.ruleContent),\n\t\t\t\t),\n\t\t\t};\n\t\tcase \"setMode\":\n\t\t\treturn { ...scope, mode: update.mode };\n\t}\n}\n\nfunction defaultPolicyPath(): string {\n\tif (process.env.APEX_CODE_POLICY_PATH) return process.env.APEX_CODE_POLICY_PATH;\n\treturn process.platform === \"win32\"\n\t\t? join(process.env.ProgramData ?? \"C:\\\\ProgramData\", \"apex-code\", \"policy.json\")\n\t\t: \"/etc/apex-code/policy.json\";\n}\n\nexport interface CreateFilePermissionRuleStoreOptions {\n\tcwd: string;\n\tagentDir?: string;\n\t/** Overrides the managed policy file path. Defaults to $APEX_CODE_POLICY_PATH, else an OS-specific system location. */\n\tpolicyPath?: string;\n\t/** Test seam: inject backends for the three writable file-backed sources instead of touching real files. */\n\tbackends?: Partial<Record<FileBackedSource, AuthStorageBackend>>;\n\t/** Immutable argv layers (`flag` / `cliArg`), validated by the CLI before construction. */\n\tinitialRules?: readonly PermissionRule[];\n}\n\n/** File-backed store for policy/local/project/user, in-memory for command/session. */\nexport class FilePermissionRuleStore implements PermissionRuleStore {\n\tprivate readonly backends: Record<FileBackedSource, AuthStorageBackend>;\n\tprivate readonly policyPath: string;\n\tprivate readonly initialRules: readonly PermissionRule[];\n\tprivate readonly runtimeRules: Record<RuntimeSource, StoredPermissionRule[]> = { command: [], session: [] };\n\tprivate readonly runtimeModes: Partial<Record<RuntimeSource, PermissionMode>> = {};\n\n\tconstructor(options: CreateFilePermissionRuleStoreOptions) {\n\t\tconst agentDir = options.agentDir ?? getAgentDir();\n\t\tconst cwd = resolvePath(options.cwd);\n\t\tthis.backends = {\n\t\t\tuser: new FileAuthStorageBackend(join(agentDir, \"permissions.json\")),\n\t\t\tproject: new FileAuthStorageBackend(join(cwd, \".apex-code\", \"permissions.json\")),\n\t\t\tlocal: new FileAuthStorageBackend(join(cwd, \".apex-code\", \"permissions.local.json\")),\n\t\t\t...options.backends,\n\t\t};\n\t\tthis.policyPath = options.policyPath ?? defaultPolicyPath();\n\t\tthis.initialRules = options.initialRules ?? [];\n\t}\n\n\tprivate async readFileBackedScope(\n\t\tsource: FileBackedSource,\n\t): Promise<{ scope: StoredPermissionScope; error?: Error }> {\n\t\ttry {\n\t\t\tlet content: string | undefined;\n\t\t\tawait this.backends[source].withLockAsync(async (current) => {\n\t\t\t\tcontent = current;\n\t\t\t\treturn { result: undefined };\n\t\t\t});\n\t\t\treturn { scope: parseScope(content) };\n\t\t} catch (error) {\n\t\t\treturn { scope: emptyScope(), error: error instanceof Error ? error : new Error(String(error)) };\n\t\t}\n\t}\n\n\tprivate async readPolicy(): Promise<{ scope: StoredPermissionScope; error?: Error }> {\n\t\ttry {\n\t\t\tconst content = await readFile(this.policyPath, \"utf-8\");\n\t\t\treturn { scope: parseScope(content) };\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") return { scope: emptyScope() };\n\t\t\treturn { scope: emptyScope(), error: error instanceof Error ? error : new Error(String(error)) };\n\t\t}\n\t}\n\n\tasync snapshot(): Promise<PermissionStoreSnapshot> {\n\t\tconst [policy, local, project, user] = await Promise.all([\n\t\t\tthis.readPolicy(),\n\t\t\tthis.readFileBackedScope(\"local\"),\n\t\t\tthis.readFileBackedScope(\"project\"),\n\t\t\tthis.readFileBackedScope(\"user\"),\n\t\t]);\n\t\tconst errors: PermissionStoreError[] = [];\n\t\tconst withSource = (\n\t\t\tsource: PermissionSource,\n\t\t\tentry: { scope: StoredPermissionScope; error?: Error },\n\t\t): PermissionRule[] => {\n\t\t\tif (entry.error) errors.push({ source, error: entry.error });\n\t\t\treturn entry.scope.rules.map((rule) => ({ ...rule, source }));\n\t\t};\n\n\t\tconst rules: PermissionRule[] = [\n\t\t\t...withSource(\"policy\", policy),\n\t\t\t...this.initialRules,\n\t\t\t...withSource(\"local\", local),\n\t\t\t...withSource(\"project\", project),\n\t\t\t...withSource(\"user\", user),\n\t\t\t...this.runtimeRules.command.map((rule) => ({ ...rule, source: \"command\" as const })),\n\t\t\t...this.runtimeRules.session.map((rule) => ({ ...rule, source: \"session\" as const })),\n\t\t];\n\n\t\tconst modesBySource = new Map<WritablePermissionSource, PermissionMode>();\n\t\tif (local.scope.mode) modesBySource.set(\"local\", local.scope.mode);\n\t\tif (project.scope.mode) modesBySource.set(\"project\", project.scope.mode);\n\t\tif (user.scope.mode) modesBySource.set(\"user\", user.scope.mode);\n\t\tif (this.runtimeModes.command) modesBySource.set(\"command\", this.runtimeModes.command);\n\t\tif (this.runtimeModes.session) modesBySource.set(\"session\", this.runtimeModes.session);\n\n\t\treturn { rules, modesBySource, errors };\n\t}\n\n\tasync apply(update: PermissionUpdate): Promise<void> {\n\t\tif (update.destination === \"command\" || update.destination === \"session\") {\n\t\t\tthis.applyRuntime(update.destination, update);\n\t\t\treturn;\n\t\t}\n\t\tawait this.backends[update.destination].withLockAsync(async (content) => {\n\t\t\tconst next = applyToScope(parseScope(content), update);\n\t\t\treturn { result: undefined, next: JSON.stringify(next, null, 2) };\n\t\t});\n\t}\n\n\tprivate applyRuntime(destination: RuntimeSource, update: PermissionUpdate): void {\n\t\tif (update.type === \"setMode\") {\n\t\t\tthis.runtimeModes[destination] = update.mode;\n\t\t\treturn;\n\t\t}\n\t\tconst current: StoredPermissionScope = { version: STORE_VERSION, rules: this.runtimeRules[destination] };\n\t\tthis.runtimeRules[destination] = applyToScope(current, update).rules;\n\t}\n}\n\nexport interface DerivedPermissionRuleStoreOptions {\n\t/** The parent's live store. Read fresh on every `snapshot()`, never cached, so a\n\t * rule the parent picks up mid-session (e.g. a human's \"always allow\") is visible\n\t * to the child on its very next call. */\n\tparent: PermissionRuleStore;\n}\n\n/**\n * A delegated child's permission store (roadmap Phase 5, ADR 0008). A read-through\n * view over the parent's live snapshot -- including its runtime-only sources\n * (`flag`, `cliArg`, `command`, `session`), which a subprocess-spawned child could\n * never observe -- plus a child-local runtime overlay for the child's own writes.\n *\n * `apply()` never reaches the parent. It writes only into this instance's own\n * overlay, and only for the two runtime-only writable sources (`command`, `session`);\n * a file-backed destination (`local`, `project`, `user`) is rejected outright, not\n * silently redirected -- a delegated child must never be able to persist to the\n * parent's settings files, and \"runtime-only\" is enforced by construction here, not\n * by a convention a future caller could forget. The overlay is discarded with the\n * child: there is no code path from a child's approval back into the parent's store,\n * which is what keeps a child's `persist: true` from widening its parent.\n */\nexport class DerivedPermissionRuleStore implements PermissionRuleStore {\n\tprivate readonly parent: PermissionRuleStore;\n\tprivate readonly overlayRules: Record<RuntimeSource, StoredPermissionRule[]> = { command: [], session: [] };\n\tprivate readonly overlayModes: Partial<Record<RuntimeSource, PermissionMode>> = {};\n\n\tconstructor(options: DerivedPermissionRuleStoreOptions) {\n\t\tthis.parent = options.parent;\n\t}\n\n\tasync snapshot(): Promise<PermissionStoreSnapshot> {\n\t\tconst parentSnapshot = await this.parent.snapshot();\n\t\tconst rules: PermissionRule[] = [\n\t\t\t...parentSnapshot.rules,\n\t\t\t...this.overlayRules.command.map((rule) => ({ ...rule, source: \"command\" as const })),\n\t\t\t...this.overlayRules.session.map((rule) => ({ ...rule, source: \"session\" as const })),\n\t\t];\n\t\tconst modesBySource = new Map(parentSnapshot.modesBySource);\n\t\tif (this.overlayModes.command !== undefined) modesBySource.set(\"command\", this.overlayModes.command);\n\t\tif (this.overlayModes.session !== undefined) modesBySource.set(\"session\", this.overlayModes.session);\n\t\treturn { rules, modesBySource, errors: parentSnapshot.errors };\n\t}\n\n\tasync apply(update: PermissionUpdate): Promise<void> {\n\t\tif (update.destination !== \"command\" && update.destination !== \"session\") {\n\t\t\tthrow new Error(\n\t\t\t\t`A delegated child cannot persist permission rules to \"${update.destination}\" -- only \"command\" and \"session\" are writable from a derived store (ADR 0008).`,\n\t\t\t);\n\t\t}\n\t\tif (update.type === \"setMode\") {\n\t\t\tthis.overlayModes[update.destination] = update.mode;\n\t\t\treturn;\n\t\t}\n\t\tconst current: StoredPermissionScope = {\n\t\t\tversion: STORE_VERSION,\n\t\t\trules: this.overlayRules[update.destination],\n\t\t};\n\t\tthis.overlayRules[update.destination] = applyToScope(current, update).rules;\n\t}\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-trust.d.ts","sourceRoot":"","sources":["../../src/core/project-trust.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACvF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EAIN,KAAK,iBAAiB,EACtB,MAAM,oBAAoB,CAAC;AAE5B,MAAM,MAAM,OAAO,GAAG,aAAa,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,CAAC;AAE/D,MAAM,WAAW,4BAA4B;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,iBAAiB,CAAC;IAC9B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IAC1C,gBAAgB,CAAC,EAAE,oBAAoB,CAAC;IACxC,mBAAmB,EAAE,mBAAmB,CAAC;IACzC,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7C;AAwBD,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,4BAA4B,GAAG,OAAO,CAAC,OAAO,CAAC,CAkDnG","sourcesContent":["import { CONFIG_DIR_NAME } from \"../config.ts\";\nimport { emitProjectTrustEvent } from \"./extensions/runner.ts\";\nimport type { LoadExtensionsResult, ProjectTrustContext } from \"./extensions/types.ts\";\nimport type { DefaultProjectTrust } from \"./settings-manager.ts\";\nimport {\n\tgetProjectTrustOptions,\n\thasTrustRequiringProjectResources,\n\ttype ProjectTrustOption,\n\ttype ProjectTrustStore,\n} from \"./trust-manager.ts\";\n\nexport type AppMode = \"interactive\" | \"print\" | \"json\" | \"rpc\";\n\nexport interface ResolveProjectTrustedOptions {\n\tcwd: string;\n\ttrustStore: ProjectTrustStore;\n\ttrustOverride?: boolean;\n\tdefaultProjectTrust?: DefaultProjectTrust;\n\textensionsResult?: LoadExtensionsResult;\n\tprojectTrustContext: ProjectTrustContext;\n\tonExtensionError?: (message: string) => void;\n}\n\nfunction formatProjectTrustPrompt(cwd: string): string {\n\treturn `Trust project folder?\\n${cwd}\\n\\nThis allows
|
|
1
|
+
{"version":3,"file":"project-trust.d.ts","sourceRoot":"","sources":["../../src/core/project-trust.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACvF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EAIN,KAAK,iBAAiB,EACtB,MAAM,oBAAoB,CAAC;AAE5B,MAAM,MAAM,OAAO,GAAG,aAAa,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,CAAC;AAE/D,MAAM,WAAW,4BAA4B;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,iBAAiB,CAAC;IAC9B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IAC1C,gBAAgB,CAAC,EAAE,oBAAoB,CAAC;IACxC,mBAAmB,EAAE,mBAAmB,CAAC;IACzC,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7C;AAwBD,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,4BAA4B,GAAG,OAAO,CAAC,OAAO,CAAC,CAkDnG","sourcesContent":["import { CONFIG_DIR_NAME } from \"../config.ts\";\nimport { emitProjectTrustEvent } from \"./extensions/runner.ts\";\nimport type { LoadExtensionsResult, ProjectTrustContext } from \"./extensions/types.ts\";\nimport type { DefaultProjectTrust } from \"./settings-manager.ts\";\nimport {\n\tgetProjectTrustOptions,\n\thasTrustRequiringProjectResources,\n\ttype ProjectTrustOption,\n\ttype ProjectTrustStore,\n} from \"./trust-manager.ts\";\n\nexport type AppMode = \"interactive\" | \"print\" | \"json\" | \"rpc\";\n\nexport interface ResolveProjectTrustedOptions {\n\tcwd: string;\n\ttrustStore: ProjectTrustStore;\n\ttrustOverride?: boolean;\n\tdefaultProjectTrust?: DefaultProjectTrust;\n\textensionsResult?: LoadExtensionsResult;\n\tprojectTrustContext: ProjectTrustContext;\n\tonExtensionError?: (message: string) => void;\n}\n\nfunction formatProjectTrustPrompt(cwd: string): string {\n\treturn `Trust project folder?\\n${cwd}\\n\\nThis allows Apex Code to load ${CONFIG_DIR_NAME} settings and resources, install missing project packages, and execute project extensions.`;\n}\n\nasync function selectProjectTrustOption(\n\tcwd: string,\n\tctx: ProjectTrustContext,\n): Promise<ProjectTrustOption | undefined> {\n\tconst options = getProjectTrustOptions(cwd, { includeSessionOnly: true });\n\tconst selected = await ctx.ui.select(\n\t\tformatProjectTrustPrompt(cwd),\n\t\toptions.map((option) => option.label),\n\t);\n\treturn options.find((option) => option.label === selected);\n}\n\nfunction saveProjectTrustPromptResult(trustStore: ProjectTrustStore, result: ProjectTrustOption): void {\n\tif (result.updates.length > 0) {\n\t\ttrustStore.setMany(result.updates);\n\t}\n}\n\nexport async function resolveProjectTrusted(options: ResolveProjectTrustedOptions): Promise<boolean> {\n\tif (options.trustOverride !== undefined) {\n\t\treturn options.trustOverride;\n\t}\n\tif (!hasTrustRequiringProjectResources(options.cwd)) {\n\t\treturn true;\n\t}\n\n\tif (options.extensionsResult) {\n\t\tconst { result, errors } = await emitProjectTrustEvent(\n\t\t\toptions.extensionsResult,\n\t\t\t{ type: \"project_trust\", cwd: options.cwd },\n\t\t\toptions.projectTrustContext,\n\t\t);\n\t\tfor (const error of errors) {\n\t\t\toptions.onExtensionError?.(`Extension \"${error.extensionPath}\" project_trust error: ${error.error}`);\n\t\t}\n\t\tif (result) {\n\t\t\tconst trusted = result.trusted === \"yes\";\n\t\t\tif (result.remember === true) {\n\t\t\t\toptions.trustStore.set(options.cwd, trusted);\n\t\t\t}\n\t\t\treturn trusted;\n\t\t}\n\t}\n\n\tconst decision = options.trustStore.get(options.cwd);\n\tif (decision !== null) {\n\t\treturn decision;\n\t}\n\n\tswitch (options.defaultProjectTrust ?? \"ask\") {\n\t\tcase \"always\":\n\t\t\treturn true;\n\t\tcase \"never\":\n\t\t\treturn false;\n\t\tcase \"ask\":\n\t\t\tbreak;\n\t}\n\n\tif (!options.projectTrustContext.hasUI) {\n\t\treturn false;\n\t}\n\n\tconst selected = await selectProjectTrustOption(options.cwd, options.projectTrustContext);\n\tif (selected !== undefined) {\n\t\tsaveProjectTrustPromptResult(options.trustStore, selected);\n\t\treturn selected.trusted;\n\t}\n\treturn false;\n}\n"]}
|
|
@@ -2,7 +2,7 @@ import { CONFIG_DIR_NAME } from "../config.js";
|
|
|
2
2
|
import { emitProjectTrustEvent } from "./extensions/runner.js";
|
|
3
3
|
import { getProjectTrustOptions, hasTrustRequiringProjectResources, } from "./trust-manager.js";
|
|
4
4
|
function formatProjectTrustPrompt(cwd) {
|
|
5
|
-
return `Trust project folder?\n${cwd}\n\nThis allows
|
|
5
|
+
return `Trust project folder?\n${cwd}\n\nThis allows Apex Code to load ${CONFIG_DIR_NAME} settings and resources, install missing project packages, and execute project extensions.`;
|
|
6
6
|
}
|
|
7
7
|
async function selectProjectTrustOption(cwd, ctx) {
|
|
8
8
|
const options = getProjectTrustOptions(cwd, { includeSessionOnly: true });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-trust.js","sourceRoot":"","sources":["../../src/core/project-trust.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAG/D,OAAO,EACN,sBAAsB,EACtB,iCAAiC,GAGjC,MAAM,oBAAoB,CAAC;AAc5B,SAAS,wBAAwB,CAAC,GAAW,EAAU;IACtD,OAAO,0BAA0B,GAAG,
|
|
1
|
+
{"version":3,"file":"project-trust.js","sourceRoot":"","sources":["../../src/core/project-trust.ts"],"names":[],"mappings":"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","sourcesContent":["import { CONFIG_DIR_NAME } from \"../config.ts\";\nimport { emitProjectTrustEvent } from \"./extensions/runner.ts\";\nimport type { LoadExtensionsResult, ProjectTrustContext } from \"./extensions/types.ts\";\nimport type { DefaultProjectTrust } from \"./settings-manager.ts\";\nimport {\n\tgetProjectTrustOptions,\n\thasTrustRequiringProjectResources,\n\ttype ProjectTrustOption,\n\ttype ProjectTrustStore,\n} from \"./trust-manager.ts\";\n\nexport type AppMode = \"interactive\" | \"print\" | \"json\" | \"rpc\";\n\nexport interface ResolveProjectTrustedOptions {\n\tcwd: string;\n\ttrustStore: ProjectTrustStore;\n\ttrustOverride?: boolean;\n\tdefaultProjectTrust?: DefaultProjectTrust;\n\textensionsResult?: LoadExtensionsResult;\n\tprojectTrustContext: ProjectTrustContext;\n\tonExtensionError?: (message: string) => void;\n}\n\nfunction formatProjectTrustPrompt(cwd: string): string {\n\treturn `Trust project folder?\\n${cwd}\\n\\nThis allows Apex Code to load ${CONFIG_DIR_NAME} settings and resources, install missing project packages, and execute project extensions.`;\n}\n\nasync function selectProjectTrustOption(\n\tcwd: string,\n\tctx: ProjectTrustContext,\n): Promise<ProjectTrustOption | undefined> {\n\tconst options = getProjectTrustOptions(cwd, { includeSessionOnly: true });\n\tconst selected = await ctx.ui.select(\n\t\tformatProjectTrustPrompt(cwd),\n\t\toptions.map((option) => option.label),\n\t);\n\treturn options.find((option) => option.label === selected);\n}\n\nfunction saveProjectTrustPromptResult(trustStore: ProjectTrustStore, result: ProjectTrustOption): void {\n\tif (result.updates.length > 0) {\n\t\ttrustStore.setMany(result.updates);\n\t}\n}\n\nexport async function resolveProjectTrusted(options: ResolveProjectTrustedOptions): Promise<boolean> {\n\tif (options.trustOverride !== undefined) {\n\t\treturn options.trustOverride;\n\t}\n\tif (!hasTrustRequiringProjectResources(options.cwd)) {\n\t\treturn true;\n\t}\n\n\tif (options.extensionsResult) {\n\t\tconst { result, errors } = await emitProjectTrustEvent(\n\t\t\toptions.extensionsResult,\n\t\t\t{ type: \"project_trust\", cwd: options.cwd },\n\t\t\toptions.projectTrustContext,\n\t\t);\n\t\tfor (const error of errors) {\n\t\t\toptions.onExtensionError?.(`Extension \"${error.extensionPath}\" project_trust error: ${error.error}`);\n\t\t}\n\t\tif (result) {\n\t\t\tconst trusted = result.trusted === \"yes\";\n\t\t\tif (result.remember === true) {\n\t\t\t\toptions.trustStore.set(options.cwd, trusted);\n\t\t\t}\n\t\t\treturn trusted;\n\t\t}\n\t}\n\n\tconst decision = options.trustStore.get(options.cwd);\n\tif (decision !== null) {\n\t\treturn decision;\n\t}\n\n\tswitch (options.defaultProjectTrust ?? \"ask\") {\n\t\tcase \"always\":\n\t\t\treturn true;\n\t\tcase \"never\":\n\t\t\treturn false;\n\t\tcase \"ask\":\n\t\t\tbreak;\n\t}\n\n\tif (!options.projectTrustContext.hasUI) {\n\t\treturn false;\n\t}\n\n\tconst selected = await selectProjectTrustOption(options.cwd, options.projectTrustContext);\n\tif (selected !== undefined) {\n\t\tsaveProjectTrustPromptResult(options.trustStore, selected);\n\t\treturn selected.trusted;\n\t}\n\treturn false;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider-attribution.d.ts","sourceRoot":"","sources":["../../src/core/provider-attribution.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACzE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"provider-attribution.d.ts","sourceRoot":"","sources":["../../src/core/provider-attribution.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACzE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AA4E7D,wBAAgB,+BAA+B,CAC9C,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EACjB,eAAe,EAAE,eAAe,EAChC,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,GAAG,aAAa,EAAE,KAAK,CAAC,eAAe,GAAG,SAAS,CAAC,GAClD,eAAe,GAAG,SAAS,CAa7B","sourcesContent":["import type { Api, Model, ProviderHeaders } from \"@earendil-works/pi-ai\";\nimport type { SettingsManager } from \"./settings-manager.ts\";\n\nconst OPENROUTER_HOST = \"openrouter.ai\";\nconst NVIDIA_NIM_HOST = \"integrate.api.nvidia.com\";\nconst CLOUDFLARE_API_HOST = \"api.cloudflare.com\";\nconst CLOUDFLARE_AI_GATEWAY_HOST = \"gateway.ai.cloudflare.com\";\nconst OPENCODE_HOST = \"opencode.ai\";\n\nfunction matchesHost(baseUrl: string, expectedHost: string): boolean {\n\ttry {\n\t\treturn new URL(baseUrl).hostname === expectedHost;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction isOpenRouterModel(model: Model<Api>): boolean {\n\treturn model.provider === \"openrouter\" || model.baseUrl.includes(OPENROUTER_HOST);\n}\n\nfunction isNvidiaNimModel(model: Model<Api>): boolean {\n\treturn model.provider === \"nvidia\" || matchesHost(model.baseUrl, NVIDIA_NIM_HOST);\n}\n\nfunction isCloudflareModel(model: Model<Api>): boolean {\n\treturn (\n\t\tmodel.provider === \"cloudflare-workers-ai\" ||\n\t\tmodel.provider === \"cloudflare-ai-gateway\" ||\n\t\tmatchesHost(model.baseUrl, CLOUDFLARE_API_HOST) ||\n\t\tmatchesHost(model.baseUrl, CLOUDFLARE_AI_GATEWAY_HOST)\n\t);\n}\n\nfunction getDefaultAttributionHeaders(\n\tmodel: Model<Api>,\n\tsettingsManager: SettingsManager,\n): Record<string, string> | undefined {\n\tif (!settingsManager.getSendProviderAttribution()) {\n\t\treturn undefined;\n\t}\n\n\tif (isOpenRouterModel(model)) {\n\t\treturn {\n\t\t\t\"HTTP-Referer\": \"https://github.com/Fchery87/apex-code\",\n\t\t\t\"X-OpenRouter-Title\": \"apex-code\",\n\t\t\t\"X-OpenRouter-Categories\": \"cli-agent\",\n\t\t};\n\t}\n\n\tif (isNvidiaNimModel(model)) {\n\t\treturn {\n\t\t\t\"X-BILLING-INVOKE-ORIGIN\": \"Apex-Code\",\n\t\t};\n\t}\n\n\tif (isCloudflareModel(model)) {\n\t\treturn {\n\t\t\t\"User-Agent\": \"apex-code\",\n\t\t};\n\t}\n\n\treturn undefined;\n}\n\nfunction getSessionHeaders(model: Model<Api>, sessionId: string | undefined): Record<string, string> | undefined {\n\tif (!sessionId) return undefined;\n\tif (\n\t\tmodel.provider !== \"opencode\" &&\n\t\tmodel.provider !== \"opencode-go\" &&\n\t\t!matchesHost(model.baseUrl, OPENCODE_HOST)\n\t) {\n\t\treturn undefined;\n\t}\n\treturn { \"x-opencode-session\": sessionId, \"x-opencode-client\": \"apex-code\" };\n}\n\nexport function mergeProviderAttributionHeaders(\n\tmodel: Model<Api>,\n\tsettingsManager: SettingsManager,\n\tsessionId: string | undefined,\n\t...headerSources: Array<ProviderHeaders | undefined>\n): ProviderHeaders | undefined {\n\tconst merged: ProviderHeaders = {\n\t\t...getSessionHeaders(model, sessionId),\n\t\t...getDefaultAttributionHeaders(model, settingsManager),\n\t};\n\n\tfor (const headers of headerSources) {\n\t\tif (headers) {\n\t\t\tObject.assign(merged, headers);\n\t\t}\n\t}\n\n\treturn Object.keys(merged).length > 0 ? merged : undefined;\n}\n"]}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { isInstallTelemetryEnabled } from "./telemetry.js";
|
|
2
1
|
const OPENROUTER_HOST = "openrouter.ai";
|
|
3
2
|
const NVIDIA_NIM_HOST = "integrate.api.nvidia.com";
|
|
4
3
|
const CLOUDFLARE_API_HOST = "api.cloudflare.com";
|
|
@@ -25,24 +24,24 @@ function isCloudflareModel(model) {
|
|
|
25
24
|
matchesHost(model.baseUrl, CLOUDFLARE_AI_GATEWAY_HOST));
|
|
26
25
|
}
|
|
27
26
|
function getDefaultAttributionHeaders(model, settingsManager) {
|
|
28
|
-
if (!
|
|
27
|
+
if (!settingsManager.getSendProviderAttribution()) {
|
|
29
28
|
return undefined;
|
|
30
29
|
}
|
|
31
30
|
if (isOpenRouterModel(model)) {
|
|
32
31
|
return {
|
|
33
|
-
"HTTP-Referer": "https://
|
|
34
|
-
"X-OpenRouter-Title": "
|
|
32
|
+
"HTTP-Referer": "https://github.com/Fchery87/apex-code",
|
|
33
|
+
"X-OpenRouter-Title": "apex-code",
|
|
35
34
|
"X-OpenRouter-Categories": "cli-agent",
|
|
36
35
|
};
|
|
37
36
|
}
|
|
38
37
|
if (isNvidiaNimModel(model)) {
|
|
39
38
|
return {
|
|
40
|
-
"X-BILLING-INVOKE-ORIGIN": "
|
|
39
|
+
"X-BILLING-INVOKE-ORIGIN": "Apex-Code",
|
|
41
40
|
};
|
|
42
41
|
}
|
|
43
42
|
if (isCloudflareModel(model)) {
|
|
44
43
|
return {
|
|
45
|
-
"User-Agent": "
|
|
44
|
+
"User-Agent": "apex-code",
|
|
46
45
|
};
|
|
47
46
|
}
|
|
48
47
|
return undefined;
|
|
@@ -55,7 +54,7 @@ function getSessionHeaders(model, sessionId) {
|
|
|
55
54
|
!matchesHost(model.baseUrl, OPENCODE_HOST)) {
|
|
56
55
|
return undefined;
|
|
57
56
|
}
|
|
58
|
-
return { "x-opencode-session": sessionId, "x-opencode-client": "
|
|
57
|
+
return { "x-opencode-session": sessionId, "x-opencode-client": "apex-code" };
|
|
59
58
|
}
|
|
60
59
|
export function mergeProviderAttributionHeaders(model, settingsManager, sessionId, ...headerSources) {
|
|
61
60
|
const merged = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider-attribution.js","sourceRoot":"","sources":["../../src/core/provider-attribution.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"provider-attribution.js","sourceRoot":"","sources":["../../src/core/provider-attribution.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Api, Model, ProviderHeaders } from \"@earendil-works/pi-ai\";\nimport type { SettingsManager } from \"./settings-manager.ts\";\n\nconst OPENROUTER_HOST = \"openrouter.ai\";\nconst NVIDIA_NIM_HOST = \"integrate.api.nvidia.com\";\nconst CLOUDFLARE_API_HOST = \"api.cloudflare.com\";\nconst CLOUDFLARE_AI_GATEWAY_HOST = \"gateway.ai.cloudflare.com\";\nconst OPENCODE_HOST = \"opencode.ai\";\n\nfunction matchesHost(baseUrl: string, expectedHost: string): boolean {\n\ttry {\n\t\treturn new URL(baseUrl).hostname === expectedHost;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction isOpenRouterModel(model: Model<Api>): boolean {\n\treturn model.provider === \"openrouter\" || model.baseUrl.includes(OPENROUTER_HOST);\n}\n\nfunction isNvidiaNimModel(model: Model<Api>): boolean {\n\treturn model.provider === \"nvidia\" || matchesHost(model.baseUrl, NVIDIA_NIM_HOST);\n}\n\nfunction isCloudflareModel(model: Model<Api>): boolean {\n\treturn (\n\t\tmodel.provider === \"cloudflare-workers-ai\" ||\n\t\tmodel.provider === \"cloudflare-ai-gateway\" ||\n\t\tmatchesHost(model.baseUrl, CLOUDFLARE_API_HOST) ||\n\t\tmatchesHost(model.baseUrl, CLOUDFLARE_AI_GATEWAY_HOST)\n\t);\n}\n\nfunction getDefaultAttributionHeaders(\n\tmodel: Model<Api>,\n\tsettingsManager: SettingsManager,\n): Record<string, string> | undefined {\n\tif (!settingsManager.getSendProviderAttribution()) {\n\t\treturn undefined;\n\t}\n\n\tif (isOpenRouterModel(model)) {\n\t\treturn {\n\t\t\t\"HTTP-Referer\": \"https://github.com/Fchery87/apex-code\",\n\t\t\t\"X-OpenRouter-Title\": \"apex-code\",\n\t\t\t\"X-OpenRouter-Categories\": \"cli-agent\",\n\t\t};\n\t}\n\n\tif (isNvidiaNimModel(model)) {\n\t\treturn {\n\t\t\t\"X-BILLING-INVOKE-ORIGIN\": \"Apex-Code\",\n\t\t};\n\t}\n\n\tif (isCloudflareModel(model)) {\n\t\treturn {\n\t\t\t\"User-Agent\": \"apex-code\",\n\t\t};\n\t}\n\n\treturn undefined;\n}\n\nfunction getSessionHeaders(model: Model<Api>, sessionId: string | undefined): Record<string, string> | undefined {\n\tif (!sessionId) return undefined;\n\tif (\n\t\tmodel.provider !== \"opencode\" &&\n\t\tmodel.provider !== \"opencode-go\" &&\n\t\t!matchesHost(model.baseUrl, OPENCODE_HOST)\n\t) {\n\t\treturn undefined;\n\t}\n\treturn { \"x-opencode-session\": sessionId, \"x-opencode-client\": \"apex-code\" };\n}\n\nexport function mergeProviderAttributionHeaders(\n\tmodel: Model<Api>,\n\tsettingsManager: SettingsManager,\n\tsessionId: string | undefined,\n\t...headerSources: Array<ProviderHeaders | undefined>\n): ProviderHeaders | undefined {\n\tconst merged: ProviderHeaders = {\n\t\t...getSessionHeaders(model, sessionId),\n\t\t...getDefaultAttributionHeaders(model, settingsManager),\n\t};\n\n\tfor (const headers of headerSources) {\n\t\tif (headers) {\n\t\t\tObject.assign(merged, headers);\n\t\t}\n\t}\n\n\treturn Object.keys(merged).length > 0 ? merged : undefined;\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Provider } from "@earendil-works/pi-ai";
|
|
2
2
|
export declare const REMOTE_CATALOG_REFRESH_INTERVAL_MS: number;
|
|
3
|
-
/** Add a persisted
|
|
4
|
-
export declare function withRemoteCatalog(provider: Provider, catalogBaseUrl
|
|
3
|
+
/** Add a persisted configured catalog overlay to a static built-in provider. */
|
|
4
|
+
export declare function withRemoteCatalog(provider: Provider, catalogBaseUrl: string, localGeneratedAt?: number): Provider;
|
|
5
5
|
//# sourceMappingURL=remote-catalog-provider.d.ts.map
|