@the-open-engine/zeroshot 6.38.0 → 6.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli/event-copy.js +8 -7
- package/cli/event-copy.ts +12 -0
- package/cli/index.js +7 -31
- package/cli/json-export.js +61 -64
- package/cli/json-export.ts +112 -0
- package/cli/message-formatter-utils.js +68 -56
- package/cli/message-formatter-utils.ts +121 -0
- package/cli/message-formatters-normal.js +124 -259
- package/cli/message-formatters-normal.ts +250 -0
- package/cli/message-formatters-watch.js +81 -84
- package/cli/message-formatters-watch.ts +189 -0
- package/lib/agent-cli-provider/adapters/codex.d.ts.map +1 -1
- package/lib/agent-cli-provider/adapters/codex.js +3 -1
- package/lib/agent-cli-provider/adapters/codex.js.map +1 -1
- package/lib/cluster/generated/protocol-schema.cjs +1 -1
- package/lib/cluster/generated/protocol-schema.mjs +1 -1
- package/lib/cluster/generated/protocol.d.cts +8 -0
- package/lib/cluster/generated/protocol.d.mts +8 -0
- package/lib/cluster/generated/protocol.d.ts +8 -0
- package/lib/clusters-registry.d.ts +23 -0
- package/lib/clusters-registry.js +17 -29
- package/lib/completion.d.ts +12 -0
- package/lib/completion.js +84 -94
- package/lib/compose-utils.d.ts +32 -0
- package/lib/compose-utils.js +57 -72
- package/lib/detached-startup.d.ts +83 -0
- package/lib/detached-startup.js +231 -302
- package/lib/docker-config.d.ts +43 -0
- package/lib/docker-config.js +150 -325
- package/lib/docker-env-auth.d.ts +17 -0
- package/lib/docker-env-auth.js +75 -0
- package/lib/git-remote-utils.d.ts +48 -0
- package/lib/git-remote-utils.js +147 -213
- package/lib/id-detector.d.ts +15 -0
- package/lib/id-detector.js +37 -41
- package/lib/path-check.d.ts +30 -0
- package/lib/path-check.js +42 -45
- package/lib/process-liveness.d.ts +16 -0
- package/lib/process-liveness.js +19 -12
- package/lib/provider-credential-path.d.ts +16 -0
- package/lib/provider-credential-path.js +31 -29
- package/lib/provider-defaults.d.ts +30 -0
- package/lib/provider-defaults.js +47 -42
- package/lib/provider-detection.d.ts +11 -0
- package/lib/provider-detection.js +91 -54
- package/lib/provider-names.d.ts +38 -0
- package/lib/provider-names.js +56 -64
- package/lib/repo-settings.d.ts +28 -0
- package/lib/repo-settings.js +83 -59
- package/lib/run-mode.d.ts +21 -0
- package/lib/run-mode.js +21 -20
- package/lib/run-plan.d.ts +28 -0
- package/lib/run-plan.js +17 -16
- package/lib/settings/claude-auth.d.ts +35 -0
- package/lib/settings/claude-auth.js +30 -47
- package/lib/settings-defaults.d.ts +20 -0
- package/lib/settings-defaults.js +149 -0
- package/lib/settings-error.d.ts +7 -0
- package/lib/settings-error.js +8 -0
- package/lib/settings-issue-providers.d.ts +10 -0
- package/lib/settings-issue-providers.js +12 -0
- package/lib/settings-models.d.ts +22 -0
- package/lib/settings-models.js +93 -0
- package/lib/settings-storage.d.ts +23 -0
- package/lib/settings-storage.js +205 -0
- package/lib/settings-validation.d.ts +7 -0
- package/lib/settings-validation.js +182 -0
- package/lib/settings.d.ts +29 -0
- package/lib/settings.js +41 -736
- package/lib/setup-apply-types.d.ts +66 -0
- package/lib/setup-apply-types.js +2 -0
- package/lib/setup-apply.d.ts +39 -0
- package/lib/setup-apply.js +267 -264
- package/lib/setup-journal.d.ts +41 -0
- package/lib/setup-journal.js +119 -76
- package/lib/setup-plan-types.d.ts +102 -0
- package/lib/setup-plan-types.js +2 -0
- package/lib/setup-plan.d.ts +30 -0
- package/lib/setup-plan.js +300 -324
- package/lib/setup-undo.d.ts +44 -0
- package/lib/setup-undo.js +61 -74
- package/lib/start-cluster-config.d.ts +26 -0
- package/lib/start-cluster-config.js +56 -0
- package/lib/start-cluster-environment.d.ts +40 -0
- package/lib/start-cluster-environment.js +134 -0
- package/lib/start-cluster-run-options.d.ts +53 -0
- package/lib/start-cluster-run-options.js +147 -0
- package/lib/start-cluster.d.ts +64 -0
- package/lib/start-cluster.js +98 -454
- package/lib/stream-json-parser.d.ts +17 -0
- package/lib/stream-json-parser.js +51 -52
- package/npm-shrinkwrap.json +2 -2
- package/package.json +12 -6
- package/scripts/postinstall.js +27 -0
- package/scripts/rust-distribution.js +23 -14
- package/src/agent/agent-command-proofs-context.js +35 -45
- package/src/agent/agent-command-proofs-context.ts +87 -0
- package/src/agent/agent-config-settings.js +19 -0
- package/src/agent/agent-config-settings.ts +41 -0
- package/src/agent/agent-config.js +122 -231
- package/src/agent/agent-config.ts +285 -0
- package/src/agent/agent-context-builder.js +38 -273
- package/src/agent/agent-context-builder.ts +51 -0
- package/src/agent/agent-context-environment-sections.js +105 -0
- package/src/agent/agent-context-environment-sections.ts +134 -0
- package/src/agent/agent-context-packs.js +165 -0
- package/src/agent/agent-context-packs.ts +296 -0
- package/src/agent/agent-context-prompt-sections.js +100 -0
- package/src/agent/agent-context-prompt-sections.ts +148 -0
- package/src/agent/agent-context-sections.js +25 -334
- package/src/agent/agent-context-sections.ts +36 -0
- package/src/agent/agent-context-sources.js +96 -149
- package/src/agent/agent-context-sources.ts +253 -0
- package/src/agent/agent-context-types.js +2 -0
- package/src/agent/agent-context-types.ts +132 -0
- package/src/agent/agent-context-validation-sections.js +73 -0
- package/src/agent/agent-context-validation-sections.ts +129 -0
- package/src/agent/agent-input-injector.js +99 -118
- package/src/agent/agent-input-injector.ts +195 -0
- package/src/agent/agent-liveness-poll.js +74 -78
- package/src/agent/agent-liveness-poll.ts +163 -0
- package/src/agent/agent-quality-gate-schema.js +77 -86
- package/src/agent/agent-quality-gate-schema.ts +135 -0
- package/src/agent/agent-quality-gates-context.js +44 -50
- package/src/agent/agent-quality-gates-context.ts +91 -0
- package/src/agent/agent-stuck-detector.js +171 -239
- package/src/agent/agent-stuck-detector.ts +288 -0
- package/src/agent/agent-trigger-evaluator.js +29 -46
- package/src/agent/agent-trigger-evaluator.ts +98 -0
- package/src/agent/context-metrics.js +110 -141
- package/src/agent/context-metrics.ts +261 -0
- package/src/agent/context-pack-builder.js +72 -358
- package/src/agent/context-pack-builder.ts +105 -0
- package/src/agent/context-pack-core.js +70 -0
- package/src/agent/context-pack-core.ts +96 -0
- package/src/agent/context-pack-selection.js +119 -0
- package/src/agent/context-pack-selection.ts +187 -0
- package/src/agent/context-pack-truncation.js +139 -0
- package/src/agent/context-pack-truncation.ts +208 -0
- package/src/agent/context-pack-types.js +2 -0
- package/src/agent/context-pack-types.ts +98 -0
- package/src/agent/context-replay-policy.js +30 -39
- package/src/agent/context-replay-policy.ts +71 -0
- package/src/agent/critical-agent-policy.js +6 -10
- package/src/agent/critical-agent-policy.ts +20 -0
- package/src/agent/guidance-queue.js +74 -87
- package/src/agent/guidance-queue.ts +166 -0
- package/src/agent/output-extraction-error-detail.js +45 -0
- package/src/agent/output-extraction-error-detail.ts +49 -0
- package/src/agent/output-extraction-failures.js +125 -0
- package/src/agent/output-extraction-failures.ts +149 -0
- package/src/agent/output-extraction-json.js +234 -0
- package/src/agent/output-extraction-json.ts +235 -0
- package/src/agent/output-extraction-pi.js +124 -0
- package/src/agent/output-extraction-pi.ts +137 -0
- package/src/agent/output-extraction-types.js +2 -0
- package/src/agent/output-extraction-types.ts +49 -0
- package/src/agent/output-extraction.js +17 -617
- package/src/agent/output-extraction.ts +30 -0
- package/src/agent/output-reformatter-errors.js +83 -0
- package/src/agent/output-reformatter-errors.ts +106 -0
- package/src/agent/output-reformatter-types.js +2 -0
- package/src/agent/output-reformatter-types.ts +86 -0
- package/src/agent/output-reformatter.js +140 -185
- package/src/agent/output-reformatter.ts +238 -0
- package/src/agent/pi-terminal-lifecycle-types.js +2 -0
- package/src/agent/pi-terminal-lifecycle-types.ts +41 -0
- package/src/agent/pi-terminal-lifecycle.js +186 -206
- package/src/agent/pi-terminal-lifecycle.ts +297 -0
- package/src/agent/provider-control-plane.js +22 -21
- package/src/agent/provider-control-plane.ts +52 -0
- package/src/agent/provider-terminal-failure-types.js +2 -0
- package/src/agent/provider-terminal-failure-types.ts +60 -0
- package/src/agent/provider-terminal-failure.js +194 -191
- package/src/agent/provider-terminal-failure.ts +262 -0
- package/src/agent/rate-limit-backoff.js +31 -74
- package/src/agent/rate-limit-backoff.ts +71 -0
- package/src/agent/schema-utils.js +85 -125
- package/src/agent/schema-utils.ts +148 -0
- package/src/agent/structured-output-error.js +34 -33
- package/src/agent/structured-output-error.ts +105 -0
- package/src/agent/validation-platform.js +27 -27
- package/src/agent/validation-platform.ts +49 -0
- package/src/agent-cli-provider/adapters/codex.ts +3 -1
- package/src/attach/attach-client.js +232 -405
- package/src/attach/attach-client.ts +300 -0
- package/src/attach/attach-server-cleanup.js +43 -0
- package/src/attach/attach-server-cleanup.ts +39 -0
- package/src/attach/attach-server-clients.js +117 -0
- package/src/attach/attach-server-clients.ts +122 -0
- package/src/attach/attach-server-events.js +38 -0
- package/src/attach/attach-server-events.ts +36 -0
- package/src/attach/attach-server-pty.js +45 -0
- package/src/attach/attach-server-pty.ts +64 -0
- package/src/attach/attach-server-runtime.js +73 -0
- package/src/attach/attach-server-runtime.ts +62 -0
- package/src/attach/attach-server-socket.js +28 -0
- package/src/attach/attach-server-socket.ts +23 -0
- package/src/attach/attach-server-types.js +2 -0
- package/src/attach/attach-server-types.ts +82 -0
- package/src/attach/attach-server.js +106 -523
- package/src/attach/attach-server.ts +158 -0
- package/src/attach/index.js +18 -35
- package/src/attach/index.ts +18 -0
- package/src/attach/protocol.js +95 -184
- package/src/attach/protocol.ts +181 -0
- package/src/attach/ring-buffer.js +76 -102
- package/src/attach/ring-buffer.ts +109 -0
- package/src/attach/send-input.js +64 -75
- package/src/attach/send-input.ts +94 -0
- package/src/attach/socket-discovery.js +164 -218
- package/src/attach/socket-discovery.ts +230 -0
- package/src/attach/socket-paths.js +42 -61
- package/src/attach/socket-paths.ts +81 -0
- package/src/claude-credentials.js +51 -54
- package/src/claude-credentials.ts +89 -0
- package/src/cluster/generated/protocol-schema.ts +1 -1
- package/src/cluster/generated/protocol.ts +2 -1
- package/src/command-proofs.js +110 -141
- package/src/command-proofs.ts +216 -0
- package/src/config-router.js +81 -73
- package/src/config-router.ts +131 -0
- package/src/copy-worker.js +53 -29
- package/src/copy-worker.ts +75 -0
- package/src/darwin-keychain-boundary.js +80 -111
- package/src/darwin-keychain-boundary.ts +148 -0
- package/src/guidance-topics.js +4 -5
- package/src/guidance-topics.ts +10 -0
- package/src/input-helpers.js +33 -54
- package/src/input-helpers.ts +58 -0
- package/src/ledger-sequence.js +45 -48
- package/src/ledger-sequence.ts +63 -0
- package/src/legacy-lib/clusters-registry.ts +40 -0
- package/src/legacy-lib/completion.ts +148 -0
- package/src/legacy-lib/compose-utils.ts +102 -0
- package/src/legacy-lib/detached-startup.ts +449 -0
- package/src/legacy-lib/docker-config.ts +282 -0
- package/src/legacy-lib/docker-env-auth.ts +116 -0
- package/src/legacy-lib/git-remote-utils.ts +244 -0
- package/src/legacy-lib/id-detector.ts +57 -0
- package/src/legacy-lib/js-yaml.d.ts +3 -0
- package/src/legacy-lib/omelette.d.ts +15 -0
- package/src/legacy-lib/path-check.ts +89 -0
- package/src/legacy-lib/process-liveness.ts +36 -0
- package/src/legacy-lib/provider-credential-path.ts +57 -0
- package/src/legacy-lib/provider-defaults.ts +114 -0
- package/src/legacy-lib/provider-detection.ts +67 -0
- package/src/legacy-lib/provider-names.ts +126 -0
- package/src/legacy-lib/repo-settings.ts +88 -0
- package/src/legacy-lib/run-mode.ts +58 -0
- package/src/legacy-lib/run-plan.ts +49 -0
- package/src/legacy-lib/settings/claude-auth.ts +80 -0
- package/src/legacy-lib/settings-defaults.ts +212 -0
- package/src/legacy-lib/settings-error.ts +8 -0
- package/src/legacy-lib/settings-issue-providers.ts +18 -0
- package/src/legacy-lib/settings-models.ts +124 -0
- package/src/legacy-lib/settings-storage.ts +277 -0
- package/src/legacy-lib/settings-validation.ts +220 -0
- package/src/legacy-lib/settings.ts +87 -0
- package/src/legacy-lib/setup-apply-types.ts +70 -0
- package/src/legacy-lib/setup-apply.ts +456 -0
- package/src/legacy-lib/setup-journal.ts +185 -0
- package/src/legacy-lib/setup-plan-types.ts +96 -0
- package/src/legacy-lib/setup-plan.ts +500 -0
- package/src/legacy-lib/setup-undo.ts +142 -0
- package/src/legacy-lib/start-cluster-config.ts +124 -0
- package/src/legacy-lib/start-cluster-environment.ts +165 -0
- package/src/legacy-lib/start-cluster-run-options.ts +279 -0
- package/src/legacy-lib/start-cluster.ts +237 -0
- package/src/legacy-lib/stream-json-parser.ts +88 -0
- package/src/message-buffer.js +55 -71
- package/src/message-buffer.ts +107 -0
- package/src/message-bus-bridge.js +92 -133
- package/src/message-bus-bridge.ts +149 -0
- package/src/name-generator-shared.js +220 -0
- package/src/name-generator-shared.ts +231 -0
- package/src/name-generator.js +8 -217
- package/src/name-generator.ts +27 -0
- package/src/omp-blob-root.js +83 -64
- package/src/omp-blob-root.ts +116 -0
- package/src/omp-config-overlay.js +65 -38
- package/src/omp-config-overlay.ts +110 -0
- package/src/omp-execution-fingerprint.js +38 -43
- package/src/omp-execution-fingerprint.ts +92 -0
- package/src/omp-session-limits.js +10 -15
- package/src/omp-session-limits.ts +53 -0
- package/src/omp-session-partition.js +2 -2
- package/src/omp-session-verifier.js +1 -1
- package/src/orchestrator.js +19 -4
- package/src/providers/anthropic/index.js +3 -1
- package/src/providers/anthropic/index.ts +13 -0
- package/src/providers/capabilities.js +19 -27
- package/src/providers/capabilities.ts +44 -0
- package/src/providers/google/index.js +3 -1
- package/src/providers/google/index.ts +13 -0
- package/src/providers/openai/index.js +3 -1
- package/src/providers/openai/index.ts +13 -0
- package/src/providers/opencode/index.js +3 -1
- package/src/providers/opencode/index.ts +13 -0
- package/src/quality-gates.js +67 -127
- package/src/quality-gates.ts +149 -0
- package/src/repo-settings-access.js +40 -0
- package/src/repo-settings-access.ts +64 -0
- package/src/schemas/sub-cluster.js +127 -183
- package/src/schemas/sub-cluster.ts +238 -0
- package/src/state-snapshot-normalization.js +224 -0
- package/src/state-snapshot-normalization.ts +222 -0
- package/src/state-snapshot.js +192 -359
- package/src/state-snapshot.ts +291 -0
- package/src/state-snapshotter.js +118 -130
- package/src/state-snapshotter.ts +167 -0
- package/src/sub-cluster-wrapper.js +4 -13
- package/src/task-run-model-args.js +87 -124
- package/src/task-run-model-args.ts +204 -0
- package/src/task-runner.js +5 -25
- package/src/task-runner.ts +28 -0
- package/src/task-startup-error.js +73 -58
- package/src/task-startup-error.ts +126 -0
- package/src/template-validation/consensus-gate-contracts.js +2 -0
- package/src/template-validation/consensus-gate-contracts.ts +70 -0
- package/src/template-validation/consensus-gate-scenarios.js +76 -0
- package/src/template-validation/consensus-gate-scenarios.ts +130 -0
- package/src/template-validation/consensus-gate-stage.js +34 -0
- package/src/template-validation/consensus-gate-stage.ts +56 -0
- package/src/template-validation/random-topology-contracts.js +2 -0
- package/src/template-validation/random-topology-contracts.ts +54 -0
- package/src/template-validation/random-topology-dispatch.js +25 -0
- package/src/template-validation/random-topology-dispatch.ts +33 -0
- package/src/template-validation/random-topology-message-loop.js +53 -0
- package/src/template-validation/random-topology-message-loop.ts +73 -0
- package/src/template-validation/random-topology-operations.js +79 -0
- package/src/template-validation/random-topology-operations.ts +101 -0
- package/src/template-validation/random-topology-runtime.js +25 -0
- package/src/template-validation/random-topology-runtime.ts +43 -0
- package/src/template-validation/random-topology-scenario.js +52 -0
- package/src/template-validation/random-topology-scenario.ts +57 -0
- package/src/template-validation/random-topology-schema.js +159 -0
- package/src/template-validation/random-topology-schema.ts +190 -0
- package/src/template-validation/random-topology-state.js +28 -0
- package/src/template-validation/random-topology-state.ts +30 -0
- package/src/template-validation/random-topology-trigger-evaluation.js +45 -0
- package/src/template-validation/random-topology-trigger-evaluation.ts +65 -0
- package/src/template-validation/random-topology-trigger.js +75 -0
- package/src/template-validation/random-topology-trigger.ts +88 -0
- package/src/template-validation/report-formatter.js +40 -46
- package/src/template-validation/report-formatter.ts +101 -0
- package/src/template-validation/simulate-consensus-gates.js +111 -302
- package/src/template-validation/simulate-consensus-gates.ts +196 -0
- package/src/template-validation/simulate-random-topology.js +26 -519
- package/src/template-validation/simulate-random-topology.ts +57 -0
- package/src/template-validation/simulate-two-stage-validation.js +41 -265
- package/src/template-validation/simulate-two-stage-validation.ts +55 -0
- package/src/template-validation/simulation-agent-runtime.js +25 -0
- package/src/template-validation/simulation-agent-runtime.ts +40 -0
- package/src/template-validation/simulation-agent.js +29 -0
- package/src/template-validation/simulation-agent.ts +73 -0
- package/src/template-validation/simulation-runtime.js +34 -0
- package/src/template-validation/simulation-runtime.ts +72 -0
- package/src/template-validation/two-stage-contracts.js +2 -0
- package/src/template-validation/two-stage-contracts.ts +41 -0
- package/src/template-validation/two-stage-inputs.js +21 -0
- package/src/template-validation/two-stage-inputs.ts +29 -0
- package/src/template-validation/two-stage-results.js +30 -0
- package/src/template-validation/two-stage-results.ts +40 -0
- package/src/template-validation/two-stage-scenario.js +86 -0
- package/src/template-validation/two-stage-scenario.ts +120 -0
- package/src/worktree-claude-config.js +182 -220
- package/src/worktree-claude-config.ts +272 -0
- package/src/worktree-tooling-env.js +103 -125
- package/src/worktree-tooling-env.ts +142 -0
- package/task-lib/completion.js +2 -3
- package/task-lib/completion.ts +9 -0
- package/task-lib/config.js +1 -4
- package/task-lib/config.ts +11 -0
- package/task-lib/name-generator.js +4 -226
- package/task-lib/name-generator.ts +11 -0
- package/task-lib/omp-session-cleanup.js +1 -1
- package/task-lib/omp-storage-root.js +8 -12
- package/task-lib/omp-storage-root.ts +62 -0
- package/task-lib/process-termination.js +143 -166
- package/task-lib/process-termination.ts +268 -0
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
interface MountPreset {
|
|
2
|
+
host: string;
|
|
3
|
+
container: string;
|
|
4
|
+
readonly: boolean;
|
|
5
|
+
hostEnv?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface ProviderDockerConfig {
|
|
9
|
+
envPassthrough: readonly string[];
|
|
10
|
+
mount?: MountPreset;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface ProviderMetadata {
|
|
14
|
+
docker: ProviderDockerConfig;
|
|
15
|
+
id: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface ProviderNamesFacade {
|
|
19
|
+
listProviderMetadata(): readonly ProviderMetadata[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface CredentialPathFacade {
|
|
23
|
+
expandProviderCredentialPath(pathValue: string): string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface ProviderEnvAuthResult {
|
|
27
|
+
ok: boolean;
|
|
28
|
+
satisfiedOneOf: boolean;
|
|
29
|
+
malformed: string[];
|
|
30
|
+
message: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface DockerEnvAuthFacade {
|
|
34
|
+
isUsableEnvValue(value: unknown): value is string;
|
|
35
|
+
isUsableHttpUrl(value: unknown): boolean;
|
|
36
|
+
validateEnvPassthrough(value: unknown): string | null;
|
|
37
|
+
validateProviderEnvAuth(
|
|
38
|
+
providerId: string,
|
|
39
|
+
forwardedEnv: Readonly<Record<string, string | undefined>> | null | undefined
|
|
40
|
+
): ProviderEnvAuthResult;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface CustomMount {
|
|
44
|
+
host?: string;
|
|
45
|
+
container?: string;
|
|
46
|
+
readonly?: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface ResolvedMount {
|
|
50
|
+
host: string;
|
|
51
|
+
container: string;
|
|
52
|
+
readonly: boolean;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
interface ResolveMountOptions {
|
|
56
|
+
containerHome?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
interface ExpandedEnv {
|
|
60
|
+
name: string;
|
|
61
|
+
value: string | null;
|
|
62
|
+
forced: boolean;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
66
|
+
const providerNames: ProviderNamesFacade = require('./provider-names');
|
|
67
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
68
|
+
const credentialPaths: CredentialPathFacade = require('./provider-credential-path');
|
|
69
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
70
|
+
const dockerEnvAuth: DockerEnvAuthFacade = require('./docker-env-auth');
|
|
71
|
+
|
|
72
|
+
const { listProviderMetadata } = providerNames;
|
|
73
|
+
const { expandProviderCredentialPath } = credentialPaths;
|
|
74
|
+
const {
|
|
75
|
+
isUsableEnvValue,
|
|
76
|
+
isUsableHttpUrl,
|
|
77
|
+
validateEnvPassthrough,
|
|
78
|
+
validateProviderEnvAuth,
|
|
79
|
+
} = dockerEnvAuth;
|
|
80
|
+
|
|
81
|
+
const BASE_MOUNT_PRESETS: Readonly<Record<string, MountPreset>> = {
|
|
82
|
+
gh: { host: '~/.config/gh', container: '$HOME/.config/gh', readonly: false },
|
|
83
|
+
git: { host: '~/.gitconfig', container: '$HOME/.gitconfig', readonly: true },
|
|
84
|
+
ssh: { host: '~/.ssh', container: '$HOME/.ssh', readonly: true },
|
|
85
|
+
aws: { host: '~/.aws', container: '$HOME/.aws', readonly: true },
|
|
86
|
+
azure: { host: '~/.azure', container: '$HOME/.azure', readonly: true },
|
|
87
|
+
kube: { host: '~/.kube', container: '$HOME/.kube', readonly: true },
|
|
88
|
+
terraform: { host: '~/.terraform.d', container: '$HOME/.terraform.d', readonly: false },
|
|
89
|
+
gcloud: { host: '~/.config/gcloud', container: '$HOME/.config/gcloud', readonly: true },
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const BASE_ENV_PRESETS: Readonly<Record<string, readonly string[]>> = {
|
|
93
|
+
aws: ['AWS_REGION', 'AWS_DEFAULT_REGION', 'AWS_PROFILE', 'AWS_PAGER='],
|
|
94
|
+
azure: ['AZURE_SUBSCRIPTION_ID', 'AZURE_TENANT_ID', 'AZURE_CLIENT_ID'],
|
|
95
|
+
gcloud: ['CLOUDSDK_CORE_PROJECT', 'GOOGLE_CLOUD_PROJECT'],
|
|
96
|
+
kube: ['KUBECONFIG'],
|
|
97
|
+
terraform: ['TF_VAR_*'],
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
function listProviderDockerPresetEntries(): Array<readonly [string, ProviderDockerConfig]> {
|
|
101
|
+
return listProviderMetadata().map((metadata) => [metadata.id, metadata.docker]);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function listProviderMountEntries(): Array<readonly [string, MountPreset]> {
|
|
105
|
+
const entries: Array<readonly [string, MountPreset]> = [];
|
|
106
|
+
for (const [providerId, docker] of listProviderDockerPresetEntries()) {
|
|
107
|
+
if (docker.mount) {
|
|
108
|
+
entries.push([providerId, docker.mount]);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return entries;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const MOUNT_PRESETS: Readonly<Record<string, MountPreset>> = Object.freeze({
|
|
115
|
+
...BASE_MOUNT_PRESETS,
|
|
116
|
+
...Object.fromEntries(listProviderMountEntries()),
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const ENV_PRESETS: Readonly<Record<string, readonly string[]>> = Object.freeze({
|
|
120
|
+
...BASE_ENV_PRESETS,
|
|
121
|
+
...Object.fromEntries(
|
|
122
|
+
listProviderDockerPresetEntries().map(([providerId, docker]) => [
|
|
123
|
+
providerId,
|
|
124
|
+
[...docker.envPassthrough],
|
|
125
|
+
])
|
|
126
|
+
),
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
const PROVIDER_ENV_ONLY_PRESETS: ReadonlySet<string> = Object.freeze(
|
|
130
|
+
new Set(
|
|
131
|
+
listProviderDockerPresetEntries()
|
|
132
|
+
.filter(([, docker]) => !docker.mount && docker.envPassthrough.length > 0)
|
|
133
|
+
.map(([providerId]) => providerId)
|
|
134
|
+
)
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
function isCustomMount(value: unknown): value is CustomMount {
|
|
138
|
+
return typeof value === 'object' && value !== null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function resolveMountItem(item: unknown, containerHome: string): ResolvedMount[] {
|
|
142
|
+
if (typeof item === 'string') {
|
|
143
|
+
const preset = MOUNT_PRESETS[item];
|
|
144
|
+
if (!preset) {
|
|
145
|
+
if (PROVIDER_ENV_ONLY_PRESETS.has(item)) {
|
|
146
|
+
return [];
|
|
147
|
+
}
|
|
148
|
+
throw new Error(
|
|
149
|
+
`Unknown mount preset: "${item}". Valid presets: ${Object.keys(MOUNT_PRESETS).join(', ')}. ` +
|
|
150
|
+
`Env-only presets (no mount): ${[...PROVIDER_ENV_ONLY_PRESETS].join(', ')}`
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
const overriddenHost =
|
|
154
|
+
typeof preset.hostEnv === 'string' && process.env[preset.hostEnv]?.trim()
|
|
155
|
+
? expandProviderCredentialPath(`$${preset.hostEnv}`)
|
|
156
|
+
: preset.host;
|
|
157
|
+
return [
|
|
158
|
+
{
|
|
159
|
+
host: overriddenHost,
|
|
160
|
+
container: preset.container.replace(/\$HOME/g, containerHome),
|
|
161
|
+
readonly: preset.readonly,
|
|
162
|
+
},
|
|
163
|
+
];
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (isCustomMount(item)) {
|
|
167
|
+
if (!item.host || !item.container) {
|
|
168
|
+
throw new Error('Custom mount must have "host" and "container" properties');
|
|
169
|
+
}
|
|
170
|
+
return [
|
|
171
|
+
{
|
|
172
|
+
host: item.host,
|
|
173
|
+
container: item.container.replace(/\$HOME/g, containerHome),
|
|
174
|
+
readonly: item.readonly !== false,
|
|
175
|
+
},
|
|
176
|
+
];
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
throw new Error(
|
|
180
|
+
`Invalid mount config: ${JSON.stringify(item)}. Use preset name or {host, container, readonly?}`
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function resolveMounts(config: unknown, options: ResolveMountOptions = {}): ResolvedMount[] {
|
|
185
|
+
if (!Array.isArray(config)) {
|
|
186
|
+
throw new Error('dockerMounts must be an array');
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const containerHome = options.containerHome || '/root';
|
|
190
|
+
return config.flatMap((item: unknown) => resolveMountItem(item, containerHome));
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function resolveEnvs(mountConfig: readonly unknown[], extraEnvs: readonly string[] = []): string[] {
|
|
194
|
+
const envs = new Set(extraEnvs);
|
|
195
|
+
|
|
196
|
+
for (const item of mountConfig) {
|
|
197
|
+
if (typeof item === 'string') {
|
|
198
|
+
const preset = ENV_PRESETS[item];
|
|
199
|
+
if (preset) {
|
|
200
|
+
for (const envVar of preset) {
|
|
201
|
+
envs.add(envVar);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return [...envs];
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function expandEnvPatterns(
|
|
211
|
+
envVars: readonly string[],
|
|
212
|
+
env: Readonly<Record<string, string | undefined>> = process.env
|
|
213
|
+
): ExpandedEnv[] {
|
|
214
|
+
const result: ExpandedEnv[] = [];
|
|
215
|
+
|
|
216
|
+
for (const envVar of envVars) {
|
|
217
|
+
if (envVar.includes('=')) {
|
|
218
|
+
const separator = envVar.indexOf('=');
|
|
219
|
+
result.push({
|
|
220
|
+
name: envVar.slice(0, separator),
|
|
221
|
+
value: envVar.slice(separator + 1),
|
|
222
|
+
forced: true,
|
|
223
|
+
});
|
|
224
|
+
} else if (envVar.endsWith('*')) {
|
|
225
|
+
const prefix = envVar.slice(0, -1);
|
|
226
|
+
for (const key of Object.keys(env)) {
|
|
227
|
+
if (key.startsWith(prefix)) {
|
|
228
|
+
result.push({ name: key, value: null, forced: false });
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
} else {
|
|
232
|
+
result.push({ name: envVar, value: null, forced: false });
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return result;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function validateMountConfig(value: unknown): string | null {
|
|
240
|
+
if (!Array.isArray(value)) {
|
|
241
|
+
return 'dockerMounts must be an array';
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
for (const item of value) {
|
|
245
|
+
if (typeof item === 'string') {
|
|
246
|
+
if (!MOUNT_PRESETS[item] && !PROVIDER_ENV_ONLY_PRESETS.has(item)) {
|
|
247
|
+
return (
|
|
248
|
+
`Unknown mount preset: "${item}". Valid: ${Object.keys(MOUNT_PRESETS).join(', ')}. ` +
|
|
249
|
+
`Env-only presets (no mount): ${[...PROVIDER_ENV_ONLY_PRESETS].join(', ')}`
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
} else if (isCustomMount(item)) {
|
|
253
|
+
if (!item.host) {
|
|
254
|
+
return 'Custom mount missing "host" property';
|
|
255
|
+
}
|
|
256
|
+
if (!item.container) {
|
|
257
|
+
return 'Custom mount missing "container" property';
|
|
258
|
+
}
|
|
259
|
+
if (item.readonly !== undefined && typeof item.readonly !== 'boolean') {
|
|
260
|
+
return '"readonly" must be a boolean';
|
|
261
|
+
}
|
|
262
|
+
} else {
|
|
263
|
+
return `Invalid mount: ${JSON.stringify(item)}. Use preset name or {host, container, readonly?}`;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export = {
|
|
271
|
+
MOUNT_PRESETS,
|
|
272
|
+
ENV_PRESETS,
|
|
273
|
+
PROVIDER_ENV_ONLY_PRESETS,
|
|
274
|
+
resolveMounts,
|
|
275
|
+
resolveEnvs,
|
|
276
|
+
expandEnvPatterns,
|
|
277
|
+
isUsableEnvValue,
|
|
278
|
+
isUsableHttpUrl,
|
|
279
|
+
validateMountConfig,
|
|
280
|
+
validateEnvPassthrough,
|
|
281
|
+
validateProviderEnvAuth,
|
|
282
|
+
};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { URL } from 'url';
|
|
2
|
+
|
|
3
|
+
interface ProviderEnvAuth {
|
|
4
|
+
requireOneOf?: readonly string[];
|
|
5
|
+
requireTogether?: readonly (readonly string[])[];
|
|
6
|
+
requireUrl?: readonly string[];
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface ProviderMetadata {
|
|
10
|
+
docker?: {
|
|
11
|
+
envAuth?: ProviderEnvAuth;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface ProviderNamesFacade {
|
|
16
|
+
getProviderMetadata(providerId: string): ProviderMetadata;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface ProviderEnvAuthResult {
|
|
20
|
+
ok: boolean;
|
|
21
|
+
satisfiedOneOf: boolean;
|
|
22
|
+
malformed: string[];
|
|
23
|
+
message: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
27
|
+
const providerNames: ProviderNamesFacade = require('./provider-names');
|
|
28
|
+
|
|
29
|
+
const ENV_SPEC_PATTERN = /^[A-Z_][A-Z0-9_]*(\*|=.*)?$/i;
|
|
30
|
+
|
|
31
|
+
function validateEnvPassthrough(value: unknown): string | null {
|
|
32
|
+
if (!Array.isArray(value)) {
|
|
33
|
+
return 'dockerEnvPassthrough must be an array';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
for (const item of value) {
|
|
37
|
+
if (typeof item !== 'string') {
|
|
38
|
+
return `Invalid env var: ${JSON.stringify(item)}. Must be a string`;
|
|
39
|
+
}
|
|
40
|
+
if (!ENV_SPEC_PATTERN.test(item)) {
|
|
41
|
+
return `Invalid env var spec: "${item}". Use VAR, VAR_*, or VAR=value`;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function isUsableEnvValue(value: unknown): value is string {
|
|
49
|
+
return typeof value === 'string' && value.trim() !== '';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function isUsableHttpUrl(value: unknown): boolean {
|
|
53
|
+
if (!isUsableEnvValue(value)) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
let parsed: URL;
|
|
57
|
+
try {
|
|
58
|
+
parsed = new URL(value.trim());
|
|
59
|
+
} catch {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
return parsed.protocol === 'http:' || parsed.protocol === 'https:';
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function validateProviderEnvAuth(
|
|
66
|
+
providerId: string,
|
|
67
|
+
forwardedEnv: Readonly<Record<string, string | undefined>> | null | undefined
|
|
68
|
+
): ProviderEnvAuthResult {
|
|
69
|
+
const metadata = providerNames.getProviderMetadata(providerId);
|
|
70
|
+
const envAuth = metadata.docker && metadata.docker.envAuth;
|
|
71
|
+
if (!envAuth) {
|
|
72
|
+
return { ok: true, satisfiedOneOf: true, malformed: [], message: '' };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const requireUrl = new Set(envAuth.requireUrl || []);
|
|
76
|
+
const valueOf = (name: string): string | undefined =>
|
|
77
|
+
forwardedEnv ? forwardedEnv[name] : undefined;
|
|
78
|
+
const isSet = (name: string): boolean =>
|
|
79
|
+
requireUrl.has(name) ? isUsableHttpUrl(valueOf(name)) : isUsableEnvValue(valueOf(name));
|
|
80
|
+
|
|
81
|
+
const requireOneOf = envAuth.requireOneOf || [];
|
|
82
|
+
const satisfiedOneOf = requireOneOf.length === 0 || requireOneOf.some(isSet);
|
|
83
|
+
|
|
84
|
+
const malformed: string[] = [];
|
|
85
|
+
for (const name of requireUrl) {
|
|
86
|
+
if (isUsableEnvValue(valueOf(name)) && !isUsableHttpUrl(valueOf(name))) {
|
|
87
|
+
malformed.push(`${name} is set but is not a usable http(s) URL`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
for (const group of envAuth.requireTogether || []) {
|
|
92
|
+
const missing = group.filter((name) => !isSet(name));
|
|
93
|
+
if (missing.length > 0 && missing.length < group.length) {
|
|
94
|
+
malformed.push(`${group.join(' + ')} must be set together (missing ${missing.join(', ')})`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const reasons = [...malformed];
|
|
99
|
+
if (!satisfiedOneOf) {
|
|
100
|
+
reasons.push(`one of ${requireOneOf.join(', ')} must be set to a non-empty value`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
ok: malformed.length === 0 && satisfiedOneOf,
|
|
105
|
+
satisfiedOneOf,
|
|
106
|
+
malformed,
|
|
107
|
+
message: reasons.join('; '),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export = {
|
|
112
|
+
isUsableEnvValue,
|
|
113
|
+
isUsableHttpUrl,
|
|
114
|
+
validateEnvPassthrough,
|
|
115
|
+
validateProviderEnvAuth,
|
|
116
|
+
};
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git remote URL parsing and provider detection.
|
|
3
|
+
* Automatically detects issue provider from git remote URL.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
interface ExecSyncOptions {
|
|
7
|
+
cwd: string;
|
|
8
|
+
encoding: 'utf8';
|
|
9
|
+
stdio: 'pipe';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
type ExecSync = (command: string, options: ExecSyncOptions) => string;
|
|
13
|
+
|
|
14
|
+
interface SafeExecFacade {
|
|
15
|
+
execSync: ExecSync;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface HostedGitContext {
|
|
19
|
+
provider: 'github' | 'gitlab';
|
|
20
|
+
host: string;
|
|
21
|
+
org: string;
|
|
22
|
+
repo: string;
|
|
23
|
+
fullRepo: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface AzureDevOpsGitContext {
|
|
27
|
+
provider: 'azure-devops';
|
|
28
|
+
host: string;
|
|
29
|
+
azureOrg: string;
|
|
30
|
+
azureProject: string;
|
|
31
|
+
repo: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
type GitContext = HostedGitContext | AzureDevOpsGitContext;
|
|
35
|
+
type GitContextWithRemote = GitContext & { remote: string };
|
|
36
|
+
|
|
37
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
38
|
+
const safeExec: SafeExecFacade = require('../src/lib/safe-exec');
|
|
39
|
+
const { execSync } = safeExec;
|
|
40
|
+
|
|
41
|
+
function hasInvalidGitRefCharacter(value: string): boolean {
|
|
42
|
+
for (const character of value) {
|
|
43
|
+
const codePoint = character.codePointAt(0);
|
|
44
|
+
if (
|
|
45
|
+
codePoint === undefined ||
|
|
46
|
+
codePoint <= 0x20 ||
|
|
47
|
+
codePoint === 0x7f ||
|
|
48
|
+
'~^:?*[\\'.includes(character)
|
|
49
|
+
) {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Normalize a Git remote name using the same ref-format rules Git applies to
|
|
58
|
+
* refs/remotes/<name>. Keeping this next to detection prevents a discovered
|
|
59
|
+
* remote from being rejected later by a narrower consumer-specific allowlist.
|
|
60
|
+
*/
|
|
61
|
+
function normalizeGitRemoteName(value: unknown): string | null {
|
|
62
|
+
if (typeof value !== 'string') {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const name = value.trim();
|
|
67
|
+
if (
|
|
68
|
+
name.length === 0 ||
|
|
69
|
+
name.endsWith('.') ||
|
|
70
|
+
name.includes('..') ||
|
|
71
|
+
name.includes('@{') ||
|
|
72
|
+
hasInvalidGitRefCharacter(name)
|
|
73
|
+
) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const components = name.split('/');
|
|
78
|
+
if (
|
|
79
|
+
components.some(
|
|
80
|
+
(component) =>
|
|
81
|
+
component.length === 0 || component.startsWith('.') || component.endsWith('.lock')
|
|
82
|
+
)
|
|
83
|
+
) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return name;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Quote one argument for the POSIX shell snippets embedded in agent prompts. */
|
|
91
|
+
function quoteShellArgument(value: string): string {
|
|
92
|
+
return `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Parse git remote URL into structured provider context.
|
|
97
|
+
* Supports GitHub, GitLab, and Azure DevOps (cloud + self-hosted).
|
|
98
|
+
* Handles both HTTPS and SSH URL formats.
|
|
99
|
+
*/
|
|
100
|
+
function parseGitRemoteUrl(remoteUrl: unknown): GitContext | null {
|
|
101
|
+
if (!remoteUrl || typeof remoteUrl !== 'string') {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const url = remoteUrl.trim();
|
|
106
|
+
|
|
107
|
+
// Normalize SSH URLs to HTTPS format for easier parsing
|
|
108
|
+
// git@host:path → https://host/path
|
|
109
|
+
let normalizedUrl = url;
|
|
110
|
+
const sshMatch = /^git@([^:]+):(.+)$/.exec(url);
|
|
111
|
+
if (sshMatch) {
|
|
112
|
+
const host = sshMatch[1];
|
|
113
|
+
const remotePath = sshMatch[2];
|
|
114
|
+
if (host !== undefined && remotePath !== undefined) {
|
|
115
|
+
normalizedUrl = `https://${host}/${remotePath}`;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Remove .git suffix if present
|
|
120
|
+
normalizedUrl = normalizedUrl.replace(/\.git$/, '');
|
|
121
|
+
|
|
122
|
+
// Azure DevOps: https://dev.azure.com/org/project/_git/repo
|
|
123
|
+
// Azure Legacy: https://org.visualstudio.com/project/_git/repo
|
|
124
|
+
// Azure SSH: git@ssh.dev.azure.com:v3/org/project/repo
|
|
125
|
+
const azureMatch =
|
|
126
|
+
/https:\/\/dev\.azure\.com\/([^/]+)\/([^/]+)\/_git\/([^/]+)/.exec(normalizedUrl) ||
|
|
127
|
+
/https:\/\/([^.]+)\.visualstudio\.com\/([^/]+)\/_git\/([^/]+)/.exec(normalizedUrl) ||
|
|
128
|
+
/https:\/\/ssh\.dev\.azure\.com\/v3\/([^/]+)\/([^/]+)\/([^/]+)/.exec(normalizedUrl);
|
|
129
|
+
|
|
130
|
+
if (azureMatch) {
|
|
131
|
+
const orgPart = azureMatch[1];
|
|
132
|
+
const project = azureMatch[2];
|
|
133
|
+
const repo = azureMatch[3];
|
|
134
|
+
if (orgPart === undefined || project === undefined || repo === undefined) {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
const isLegacy = normalizedUrl.includes('visualstudio.com');
|
|
138
|
+
const azureOrg = isLegacy
|
|
139
|
+
? `https://${orgPart}.visualstudio.com`
|
|
140
|
+
: `https://dev.azure.com/${orgPart}`;
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
provider: 'azure-devops',
|
|
144
|
+
host: isLegacy ? `${orgPart}.visualstudio.com` : 'dev.azure.com',
|
|
145
|
+
azureOrg,
|
|
146
|
+
azureProject: project,
|
|
147
|
+
repo,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// GitHub: https://github.com/org/repo
|
|
152
|
+
// GitLab: https://gitlab.com/org/repo (or self-hosted)
|
|
153
|
+
// Generic: https://host/org/repo
|
|
154
|
+
const httpsMatch = /https?:\/\/([^/]+)\/([^/]+)\/([^/]+)/.exec(normalizedUrl);
|
|
155
|
+
if (httpsMatch) {
|
|
156
|
+
const host = httpsMatch[1];
|
|
157
|
+
const org = httpsMatch[2];
|
|
158
|
+
const repo = httpsMatch[3];
|
|
159
|
+
if (host === undefined || org === undefined || repo === undefined) {
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
let provider: HostedGitContext['provider'];
|
|
164
|
+
if (host === 'github.com') {
|
|
165
|
+
provider = 'github';
|
|
166
|
+
} else if (host.includes('gitlab')) {
|
|
167
|
+
provider = 'gitlab';
|
|
168
|
+
} else {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
provider,
|
|
174
|
+
host,
|
|
175
|
+
org,
|
|
176
|
+
repo,
|
|
177
|
+
fullRepo: `${org}/${repo}`,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Detect git repository context from current working directory.
|
|
186
|
+
* Returns provider context extracted from git remote URL.
|
|
187
|
+
*/
|
|
188
|
+
function detectGitContext(cwd = process.cwd()): GitContextWithRemote | null {
|
|
189
|
+
try {
|
|
190
|
+
execSync('git rev-parse --git-dir', {
|
|
191
|
+
cwd,
|
|
192
|
+
stdio: 'pipe',
|
|
193
|
+
encoding: 'utf8',
|
|
194
|
+
});
|
|
195
|
+
} catch {
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
try {
|
|
200
|
+
const remoteOutput = execSync('git remote -v', {
|
|
201
|
+
cwd,
|
|
202
|
+
stdio: 'pipe',
|
|
203
|
+
encoding: 'utf8',
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
const supportedRemotes = new Map<string, GitContextWithRemote>();
|
|
207
|
+
for (const line of remoteOutput.split(/\r?\n/)) {
|
|
208
|
+
const match = /^(\S+)\s+(.+)\s+\(fetch\)$/.exec(line);
|
|
209
|
+
if (!match) {
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const remoteCandidate = match[1];
|
|
214
|
+
const remoteUrl = match[2];
|
|
215
|
+
const remote = normalizeGitRemoteName(remoteCandidate);
|
|
216
|
+
if (!remote || remoteUrl === undefined) {
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
const context = parseGitRemoteUrl(remoteUrl);
|
|
220
|
+
if (context && !supportedRemotes.has(remote)) {
|
|
221
|
+
supportedRemotes.set(remote, { ...context, remote });
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (supportedRemotes.has('origin')) {
|
|
226
|
+
return supportedRemotes.get('origin') ?? null;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (supportedRemotes.size === 1) {
|
|
230
|
+
return supportedRemotes.values().next().value ?? null;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return null;
|
|
234
|
+
} catch {
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export = {
|
|
240
|
+
normalizeGitRemoteName,
|
|
241
|
+
quoteShellArgument,
|
|
242
|
+
parseGitRemoteUrl,
|
|
243
|
+
detectGitContext,
|
|
244
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ID Detector - Determines if an ID is a task or cluster
|
|
3
|
+
*
|
|
4
|
+
* Strategy:
|
|
5
|
+
* 1. Check if ID exists in cluster storage
|
|
6
|
+
* 2. If not, check if ID exists in task storage (SQLite)
|
|
7
|
+
* 3. Return type: 'cluster', 'task', or null
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import path = require('path');
|
|
11
|
+
import fs = require('fs');
|
|
12
|
+
import os = require('os');
|
|
13
|
+
import Database = require('better-sqlite3');
|
|
14
|
+
import clustersRegistry = require('./clusters-registry');
|
|
15
|
+
|
|
16
|
+
type IdType = 'cluster' | 'task' | null;
|
|
17
|
+
|
|
18
|
+
function hasClusterId(clusters: unknown, id: string): boolean {
|
|
19
|
+
return Boolean(Reflect.get(Object(clusters), id));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Detect if an ID belongs to a cluster or task. */
|
|
23
|
+
function detectIdType(id: string): IdType {
|
|
24
|
+
const homeDir =
|
|
25
|
+
process.env.ZEROSHOT_HOME ||
|
|
26
|
+
process.env.HOME ||
|
|
27
|
+
process.env.USERPROFILE ||
|
|
28
|
+
os.homedir();
|
|
29
|
+
const storageDir = path.join(homeDir, '.zeroshot');
|
|
30
|
+
const taskDbFile = path.join(homeDir, '.claude-zeroshot', 'store.db');
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const clusters = clustersRegistry.readClustersFileSync(storageDir);
|
|
34
|
+
if (hasClusterId(clusters, id)) {
|
|
35
|
+
return 'cluster';
|
|
36
|
+
}
|
|
37
|
+
} catch {
|
|
38
|
+
// Ignore parse errors
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (fs.existsSync(taskDbFile)) {
|
|
42
|
+
try {
|
|
43
|
+
const db = new Database(taskDbFile, { readonly: true, timeout: 5000 });
|
|
44
|
+
const row: unknown = db.prepare('SELECT id FROM tasks WHERE id = ?').get(id);
|
|
45
|
+
db.close();
|
|
46
|
+
if (row) {
|
|
47
|
+
return 'task';
|
|
48
|
+
}
|
|
49
|
+
} catch {
|
|
50
|
+
// Ignore DB errors
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export = { detectIdType };
|