@the-open-engine/zeroshot 5.4.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/CHANGELOG.md +539 -0
- package/LICENSE +21 -0
- package/README.md +508 -0
- package/cli/commands/inspect-render.js +135 -0
- package/cli/commands/inspect.js +294 -0
- package/cli/commands/providers.js +149 -0
- package/cli/index.js +5431 -0
- package/cli/lib/first-run.js +211 -0
- package/cli/lib/update-checker.js +281 -0
- package/cli/message-formatter-utils.js +75 -0
- package/cli/message-formatters-normal.js +275 -0
- package/cli/message-formatters-watch.js +185 -0
- package/cluster-templates/base-templates/debug-workflow.json +422 -0
- package/cluster-templates/base-templates/full-workflow.json +727 -0
- package/cluster-templates/base-templates/heavy-validation.json +272 -0
- package/cluster-templates/base-templates/quick-validation.json +285 -0
- package/cluster-templates/base-templates/single-worker.json +71 -0
- package/cluster-templates/base-templates/worker-validator.json +230 -0
- package/cluster-templates/conductor-bootstrap.json +130 -0
- package/docker/zeroshot-cluster/Dockerfile +153 -0
- package/docker/zeroshot-cluster/pre-baked-deps.json +28 -0
- package/lib/agent-cli-provider/adapters/claude-parser.d.ts +3 -0
- package/lib/agent-cli-provider/adapters/claude-parser.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/claude-parser.js +122 -0
- package/lib/agent-cli-provider/adapters/claude-parser.js.map +1 -0
- package/lib/agent-cli-provider/adapters/claude-recovery.d.ts +15 -0
- package/lib/agent-cli-provider/adapters/claude-recovery.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/claude-recovery.js +165 -0
- package/lib/agent-cli-provider/adapters/claude-recovery.js.map +1 -0
- package/lib/agent-cli-provider/adapters/claude.d.ts +3 -0
- package/lib/agent-cli-provider/adapters/claude.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/claude.js +181 -0
- package/lib/agent-cli-provider/adapters/claude.js.map +1 -0
- package/lib/agent-cli-provider/adapters/codex-parser.d.ts +3 -0
- package/lib/agent-cli-provider/adapters/codex-parser.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/codex-parser.js +192 -0
- package/lib/agent-cli-provider/adapters/codex-parser.js.map +1 -0
- package/lib/agent-cli-provider/adapters/codex.d.ts +3 -0
- package/lib/agent-cli-provider/adapters/codex.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/codex.js +151 -0
- package/lib/agent-cli-provider/adapters/codex.js.map +1 -0
- package/lib/agent-cli-provider/adapters/common.d.ts +28 -0
- package/lib/agent-cli-provider/adapters/common.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/common.js +71 -0
- package/lib/agent-cli-provider/adapters/common.js.map +1 -0
- package/lib/agent-cli-provider/adapters/gemini.d.ts +3 -0
- package/lib/agent-cli-provider/adapters/gemini.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/gemini.js +176 -0
- package/lib/agent-cli-provider/adapters/gemini.js.map +1 -0
- package/lib/agent-cli-provider/adapters/index.d.ts +9 -0
- package/lib/agent-cli-provider/adapters/index.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/index.js +95 -0
- package/lib/agent-cli-provider/adapters/index.js.map +1 -0
- package/lib/agent-cli-provider/adapters/opencode.d.ts +3 -0
- package/lib/agent-cli-provider/adapters/opencode.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/opencode.js +228 -0
- package/lib/agent-cli-provider/adapters/opencode.js.map +1 -0
- package/lib/agent-cli-provider/contract-actions.d.ts +5 -0
- package/lib/agent-cli-provider/contract-actions.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-actions.js +140 -0
- package/lib/agent-cli-provider/contract-actions.js.map +1 -0
- package/lib/agent-cli-provider/contract-env.d.ts +11 -0
- package/lib/agent-cli-provider/contract-env.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-env.js +113 -0
- package/lib/agent-cli-provider/contract-env.js.map +1 -0
- package/lib/agent-cli-provider/contract-envelope.d.ts +49 -0
- package/lib/agent-cli-provider/contract-envelope.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-envelope.js +52 -0
- package/lib/agent-cli-provider/contract-envelope.js.map +1 -0
- package/lib/agent-cli-provider/contract-errors.d.ts +22 -0
- package/lib/agent-cli-provider/contract-errors.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-errors.js +71 -0
- package/lib/agent-cli-provider/contract-errors.js.map +1 -0
- package/lib/agent-cli-provider/contract-fallback.d.ts +7 -0
- package/lib/agent-cli-provider/contract-fallback.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-fallback.js +102 -0
- package/lib/agent-cli-provider/contract-fallback.js.map +1 -0
- package/lib/agent-cli-provider/contract-invoke.d.ts +5 -0
- package/lib/agent-cli-provider/contract-invoke.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-invoke.js +89 -0
- package/lib/agent-cli-provider/contract-invoke.js.map +1 -0
- package/lib/agent-cli-provider/contract-options.d.ts +3 -0
- package/lib/agent-cli-provider/contract-options.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-options.js +140 -0
- package/lib/agent-cli-provider/contract-options.js.map +1 -0
- package/lib/agent-cli-provider/contract-parse.d.ts +15 -0
- package/lib/agent-cli-provider/contract-parse.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-parse.js +81 -0
- package/lib/agent-cli-provider/contract-parse.js.map +1 -0
- package/lib/agent-cli-provider/contract-support.d.ts +18 -0
- package/lib/agent-cli-provider/contract-support.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-support.js +154 -0
- package/lib/agent-cli-provider/contract-support.js.map +1 -0
- package/lib/agent-cli-provider/contract.d.ts +13 -0
- package/lib/agent-cli-provider/contract.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract.js +30 -0
- package/lib/agent-cli-provider/contract.js.map +1 -0
- package/lib/agent-cli-provider/env-safety.d.ts +11 -0
- package/lib/agent-cli-provider/env-safety.d.ts.map +1 -0
- package/lib/agent-cli-provider/env-safety.js +83 -0
- package/lib/agent-cli-provider/env-safety.js.map +1 -0
- package/lib/agent-cli-provider/errors.d.ts +5 -0
- package/lib/agent-cli-provider/errors.d.ts.map +1 -0
- package/lib/agent-cli-provider/errors.js +115 -0
- package/lib/agent-cli-provider/errors.js.map +1 -0
- package/lib/agent-cli-provider/executable.d.ts +3 -0
- package/lib/agent-cli-provider/executable.d.ts.map +1 -0
- package/lib/agent-cli-provider/executable.js +24 -0
- package/lib/agent-cli-provider/executable.js.map +1 -0
- package/lib/agent-cli-provider/index.d.ts +8 -0
- package/lib/agent-cli-provider/index.d.ts.map +1 -0
- package/lib/agent-cli-provider/index.js +31 -0
- package/lib/agent-cli-provider/index.js.map +1 -0
- package/lib/agent-cli-provider/invoke-evidence.d.ts +4 -0
- package/lib/agent-cli-provider/invoke-evidence.d.ts.map +1 -0
- package/lib/agent-cli-provider/invoke-evidence.js +17 -0
- package/lib/agent-cli-provider/invoke-evidence.js.map +1 -0
- package/lib/agent-cli-provider/json.d.ts +16 -0
- package/lib/agent-cli-provider/json.d.ts.map +1 -0
- package/lib/agent-cli-provider/json.js +110 -0
- package/lib/agent-cli-provider/json.js.map +1 -0
- package/lib/agent-cli-provider/log-prefix.d.ts +2 -0
- package/lib/agent-cli-provider/log-prefix.d.ts.map +1 -0
- package/lib/agent-cli-provider/log-prefix.js +22 -0
- package/lib/agent-cli-provider/log-prefix.js.map +1 -0
- package/lib/agent-cli-provider/process-runner.d.ts +17 -0
- package/lib/agent-cli-provider/process-runner.d.ts.map +1 -0
- package/lib/agent-cli-provider/process-runner.js +89 -0
- package/lib/agent-cli-provider/process-runner.js.map +1 -0
- package/lib/agent-cli-provider/redaction.d.ts +9 -0
- package/lib/agent-cli-provider/redaction.d.ts.map +1 -0
- package/lib/agent-cli-provider/redaction.js +227 -0
- package/lib/agent-cli-provider/redaction.js.map +1 -0
- package/lib/agent-cli-provider/schema.d.ts +5 -0
- package/lib/agent-cli-provider/schema.d.ts.map +1 -0
- package/lib/agent-cli-provider/schema.js +136 -0
- package/lib/agent-cli-provider/schema.js.map +1 -0
- package/lib/agent-cli-provider/single-agent-runtime.d.ts +15 -0
- package/lib/agent-cli-provider/single-agent-runtime.d.ts.map +1 -0
- package/lib/agent-cli-provider/single-agent-runtime.js +228 -0
- package/lib/agent-cli-provider/single-agent-runtime.js.map +1 -0
- package/lib/agent-cli-provider/types.d.ts +194 -0
- package/lib/agent-cli-provider/types.d.ts.map +1 -0
- package/lib/agent-cli-provider/types.js +12 -0
- package/lib/agent-cli-provider/types.js.map +1 -0
- package/lib/completion.js +174 -0
- package/lib/detached-startup.js +220 -0
- package/lib/docker-config.js +220 -0
- package/lib/git-remote-utils.js +165 -0
- package/lib/id-detector.js +55 -0
- package/lib/provider-defaults.js +62 -0
- package/lib/provider-detection.js +59 -0
- package/lib/provider-names.js +57 -0
- package/lib/repo-settings.js +69 -0
- package/lib/settings/claude-auth.js +78 -0
- package/lib/settings.js +542 -0
- package/lib/start-cluster.js +321 -0
- package/lib/stream-json-parser.js +67 -0
- package/package.json +162 -0
- package/scripts/fix-node-pty-permissions.js +75 -0
- package/scripts/record-demo.sh +279 -0
- package/scripts/setup-merge-queue.sh +170 -0
- package/scripts/test-install.sh +40 -0
- package/scripts/validate-templates.js +107 -0
- package/src/agent/agent-config.js +266 -0
- package/src/agent/agent-context-builder.js +189 -0
- package/src/agent/agent-context-sections.js +338 -0
- package/src/agent/agent-context-sources.js +147 -0
- package/src/agent/agent-hook-executor.js +721 -0
- package/src/agent/agent-input-injector.js +141 -0
- package/src/agent/agent-lifecycle.js +982 -0
- package/src/agent/agent-quality-gate-schema.js +93 -0
- package/src/agent/agent-quality-gates-context.js +51 -0
- package/src/agent/agent-stuck-detector.js +256 -0
- package/src/agent/agent-task-executor.js +2028 -0
- package/src/agent/agent-trigger-evaluator.js +67 -0
- package/src/agent/context-metrics.js +160 -0
- package/src/agent/context-pack-builder.js +367 -0
- package/src/agent/context-replay-policy.js +51 -0
- package/src/agent/guidance-queue.js +77 -0
- package/src/agent/output-extraction.js +367 -0
- package/src/agent/output-reformatter.js +175 -0
- package/src/agent/pr-verification.js +653 -0
- package/src/agent/rate-limit-backoff.js +82 -0
- package/src/agent/schema-utils.js +146 -0
- package/src/agent/validation-platform.js +35 -0
- package/src/agent-cli-provider/adapters/claude-parser.ts +133 -0
- package/src/agent-cli-provider/adapters/claude-recovery.ts +203 -0
- package/src/agent-cli-provider/adapters/claude.ts +247 -0
- package/src/agent-cli-provider/adapters/codex-parser.ts +211 -0
- package/src/agent-cli-provider/adapters/codex.ts +217 -0
- package/src/agent-cli-provider/adapters/common.ts +124 -0
- package/src/agent-cli-provider/adapters/gemini.ts +243 -0
- package/src/agent-cli-provider/adapters/index.ts +126 -0
- package/src/agent-cli-provider/adapters/opencode.ts +286 -0
- package/src/agent-cli-provider/contract-actions.ts +150 -0
- package/src/agent-cli-provider/contract-env.ts +111 -0
- package/src/agent-cli-provider/contract-envelope.ts +110 -0
- package/src/agent-cli-provider/contract-errors.ts +66 -0
- package/src/agent-cli-provider/contract-fallback.ts +121 -0
- package/src/agent-cli-provider/contract-invoke.ts +104 -0
- package/src/agent-cli-provider/contract-options.ts +173 -0
- package/src/agent-cli-provider/contract-parse.ts +94 -0
- package/src/agent-cli-provider/contract-support.ts +167 -0
- package/src/agent-cli-provider/contract.ts +56 -0
- package/src/agent-cli-provider/env-safety.ts +82 -0
- package/src/agent-cli-provider/errors.ts +122 -0
- package/src/agent-cli-provider/executable.ts +24 -0
- package/src/agent-cli-provider/index.ts +83 -0
- package/src/agent-cli-provider/invoke-evidence.ts +18 -0
- package/src/agent-cli-provider/json.ts +114 -0
- package/src/agent-cli-provider/log-prefix.ts +20 -0
- package/src/agent-cli-provider/process-runner.ts +145 -0
- package/src/agent-cli-provider/redaction.ts +282 -0
- package/src/agent-cli-provider/schema.ts +115 -0
- package/src/agent-cli-provider/single-agent-runtime.ts +311 -0
- package/src/agent-cli-provider/types.ts +237 -0
- package/src/agent-wrapper.js +615 -0
- package/src/agents/git-pusher-template.js +705 -0
- package/src/attach/attach-client.js +438 -0
- package/src/attach/attach-server.js +543 -0
- package/src/attach/index.js +37 -0
- package/src/attach/protocol.js +220 -0
- package/src/attach/ring-buffer.js +121 -0
- package/src/attach/send-input.js +88 -0
- package/src/attach/socket-discovery.js +267 -0
- package/src/claude-task-runner.js +661 -0
- package/src/config-router.js +89 -0
- package/src/config-validator.js +2202 -0
- package/src/copy-worker.js +43 -0
- package/src/guidance-topics.js +10 -0
- package/src/input-helpers.js +65 -0
- package/src/isolation-manager.js +1734 -0
- package/src/issue-providers/README.md +305 -0
- package/src/issue-providers/azure-devops-provider.js +307 -0
- package/src/issue-providers/base-provider.js +232 -0
- package/src/issue-providers/github-provider.js +210 -0
- package/src/issue-providers/gitlab-provider.js +262 -0
- package/src/issue-providers/index.js +196 -0
- package/src/issue-providers/jira-provider.js +260 -0
- package/src/ledger.js +692 -0
- package/src/lib/gc.js +232 -0
- package/src/lib/safe-exec.js +88 -0
- package/src/logic-engine.js +201 -0
- package/src/message-buffer.js +81 -0
- package/src/message-bus-bridge.js +144 -0
- package/src/message-bus.js +256 -0
- package/src/name-generator.js +232 -0
- package/src/orchestrator.js +3924 -0
- package/src/preflight.js +712 -0
- package/src/process-metrics.js +608 -0
- package/src/providers/anthropic/index.js +3 -0
- package/src/providers/base-provider.js +355 -0
- package/src/providers/capabilities.js +60 -0
- package/src/providers/google/index.js +3 -0
- package/src/providers/index.js +293 -0
- package/src/providers/openai/index.js +3 -0
- package/src/providers/opencode/index.js +3 -0
- package/src/quality-gates.js +143 -0
- package/src/schemas/sub-cluster.js +208 -0
- package/src/state-snapshot.js +398 -0
- package/src/state-snapshotter.js +142 -0
- package/src/status-footer.js +1026 -0
- package/src/sub-cluster-wrapper.js +693 -0
- package/src/task-runner.js +30 -0
- package/src/template-resolver.js +425 -0
- package/src/template-validation/index.js +338 -0
- package/src/template-validation/simulate-consensus-gates.js +324 -0
- package/src/template-validation/simulate-random-topology.js +541 -0
- package/src/template-validation/simulate-two-stage-validation.js +270 -0
- package/src/worktree-claude-config.js +135 -0
- package/src/worktree-tooling-env.js +150 -0
- package/task-lib/attachable-watcher.js +381 -0
- package/task-lib/commands/clean.js +50 -0
- package/task-lib/commands/episodes.js +105 -0
- package/task-lib/commands/get-log-path.js +23 -0
- package/task-lib/commands/kill.js +32 -0
- package/task-lib/commands/list.js +105 -0
- package/task-lib/commands/logs.js +439 -0
- package/task-lib/commands/resume.js +42 -0
- package/task-lib/commands/run.js +57 -0
- package/task-lib/commands/schedule.js +105 -0
- package/task-lib/commands/scheduler-cmd.js +96 -0
- package/task-lib/commands/schedules.js +148 -0
- package/task-lib/commands/status.js +44 -0
- package/task-lib/commands/unschedule.js +16 -0
- package/task-lib/completion.js +9 -0
- package/task-lib/config.js +11 -0
- package/task-lib/name-generator.js +230 -0
- package/task-lib/package.json +3 -0
- package/task-lib/provider-helper-runtime.js +29 -0
- package/task-lib/runner.js +190 -0
- package/task-lib/scheduler.js +252 -0
- package/task-lib/store.js +529 -0
- package/task-lib/watcher.js +305 -0
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BaseProvider - Abstract provider interface
|
|
3
|
+
*
|
|
4
|
+
* All providers support two execution paths:
|
|
5
|
+
* 1. SDK (fast) - Direct API calls using provider's SDK (requires API key)
|
|
6
|
+
* 2. CLI (slower) - Spawns provider's CLI tool (uses OAuth/login auth)
|
|
7
|
+
*
|
|
8
|
+
* Use callSimple() for simple prompts - it tries SDK first, falls back to CLI.
|
|
9
|
+
*/
|
|
10
|
+
class BaseProvider {
|
|
11
|
+
constructor(options = {}) {
|
|
12
|
+
this.name = options.name || 'base';
|
|
13
|
+
this.displayName = options.displayName || 'Base';
|
|
14
|
+
this.cliCommand = options.cliCommand || null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// ERROR CLASSIFICATION (Retry vs Permanent)
|
|
19
|
+
// ============================================================================
|
|
20
|
+
//
|
|
21
|
+
// Providers currently execute via CLI, so errors are often unstructured strings.
|
|
22
|
+
// These helpers are used by the agent retry loop to decide whether to retry.
|
|
23
|
+
//
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Patterns that indicate the error is retryable/transient.
|
|
27
|
+
* Override in provider implementations to add provider-specific patterns.
|
|
28
|
+
* @returns {Array<RegExp>} Retryable error patterns
|
|
29
|
+
*/
|
|
30
|
+
getRetryableErrorPatterns() {
|
|
31
|
+
return [
|
|
32
|
+
/rate.?limit/i,
|
|
33
|
+
/\b429\b/i,
|
|
34
|
+
/too many requests/i,
|
|
35
|
+
/overloaded/i,
|
|
36
|
+
/temporar(?:y|ily)/i,
|
|
37
|
+
/unavailable/i,
|
|
38
|
+
/try again/i,
|
|
39
|
+
/timeout/i,
|
|
40
|
+
/timed out/i,
|
|
41
|
+
/deadline exceeded/i,
|
|
42
|
+
/connection (?:reset|refused)/i,
|
|
43
|
+
/\b(econnreset|econnrefused|etimedout|eai_again)\b/i,
|
|
44
|
+
/network/i,
|
|
45
|
+
];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Patterns that indicate the error is permanent and should not be retried.
|
|
50
|
+
* Override in provider implementations to add provider-specific patterns.
|
|
51
|
+
* @returns {Array<RegExp>} Permanent error patterns
|
|
52
|
+
*/
|
|
53
|
+
getPermanentErrorPatterns() {
|
|
54
|
+
return [
|
|
55
|
+
/invalid[_ -]?api[_ -]?key/i,
|
|
56
|
+
/api[_ -]?key.*invalid/i,
|
|
57
|
+
/unauthorized/i,
|
|
58
|
+
/forbidden/i,
|
|
59
|
+
/authentication/i,
|
|
60
|
+
/permission denied/i,
|
|
61
|
+
/invalid argument/i,
|
|
62
|
+
/unknown option/i,
|
|
63
|
+
/\busage:\b/i,
|
|
64
|
+
/command not found/i,
|
|
65
|
+
/not recognized as an internal or external command/i,
|
|
66
|
+
/model not found/i,
|
|
67
|
+
/context length exceeded/i,
|
|
68
|
+
/insufficient quota/i,
|
|
69
|
+
];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Classify whether an error is retryable.
|
|
74
|
+
* Default behavior is conservative: unknown errors are treated as retryable
|
|
75
|
+
* to prevent stuck clusters from single transient failures.
|
|
76
|
+
* @param {any} err - Error object (often Error with message)
|
|
77
|
+
* @returns {boolean} True if retryable
|
|
78
|
+
*/
|
|
79
|
+
isRetryableError(err) {
|
|
80
|
+
const status =
|
|
81
|
+
err?.status ?? err?.statusCode ?? err?.response?.status ?? err?.response?.statusCode ?? null;
|
|
82
|
+
|
|
83
|
+
if (typeof status === 'number') {
|
|
84
|
+
if (status === 429 || status >= 500) return true;
|
|
85
|
+
if (status >= 400 && status < 500) return false;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const code = err?.code || null;
|
|
89
|
+
if (
|
|
90
|
+
typeof code === 'string' &&
|
|
91
|
+
/\b(econnreset|econnrefused|etimedout|eai_again)\b/i.test(code)
|
|
92
|
+
) {
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const message = (err?.message || String(err) || '').trim();
|
|
97
|
+
if (!message) {
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const permanent = this.getPermanentErrorPatterns();
|
|
102
|
+
if (Array.isArray(permanent) && permanent.some((p) => p.test(message))) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const retryable = this.getRetryableErrorPatterns();
|
|
107
|
+
if (Array.isArray(retryable) && retryable.some((p) => p.test(message))) {
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ============================================================================
|
|
115
|
+
// SDK SUPPORT (Future Extension Point)
|
|
116
|
+
// ============================================================================
|
|
117
|
+
//
|
|
118
|
+
// SDK support is NOT IMPLEMENTED. These methods exist for future extension.
|
|
119
|
+
// Currently, all providers use CLI (claude, codex, gemini) for execution.
|
|
120
|
+
//
|
|
121
|
+
// To add SDK support for a provider:
|
|
122
|
+
// 1. Override getSDKEnvVar() to return the API key env var
|
|
123
|
+
// 2. Override callSDK() to implement the actual API call
|
|
124
|
+
// 3. The callSimple() method will then work automatically
|
|
125
|
+
//
|
|
126
|
+
// ============================================================================
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Get the environment variable name for the API key
|
|
130
|
+
* @returns {string} Environment variable name (e.g., 'ANTHROPIC_API_KEY')
|
|
131
|
+
*/
|
|
132
|
+
getSDKEnvVar() {
|
|
133
|
+
throw new Error(`${this.name}: SDK not implemented. Use CLI instead.`);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Check if SDK is configured (API key is set)
|
|
138
|
+
* @returns {boolean} True if API key is available
|
|
139
|
+
*/
|
|
140
|
+
isSDKConfigured() {
|
|
141
|
+
try {
|
|
142
|
+
const envVar = this.getSDKEnvVar();
|
|
143
|
+
return !!process.env[envVar];
|
|
144
|
+
} catch {
|
|
145
|
+
// getSDKEnvVar() throws if not implemented
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Make a simple API call via SDK (fast path)
|
|
152
|
+
* NOT IMPLEMENTED - exists for future extension.
|
|
153
|
+
*
|
|
154
|
+
* @param {string} _prompt - The prompt to send
|
|
155
|
+
* @param {Object} _options - Call options
|
|
156
|
+
* @returns {Promise<{success: boolean, text: string, usage?: Object, error?: string}>}
|
|
157
|
+
*/
|
|
158
|
+
callSDK(_prompt, _options) {
|
|
159
|
+
return Promise.reject(new Error(`${this.name}: SDK not implemented. Use CLI instead.`));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Make a simple API call via SDK
|
|
164
|
+
* NOT IMPLEMENTED - exists for future extension.
|
|
165
|
+
*
|
|
166
|
+
* @param {string} _prompt - The prompt to send
|
|
167
|
+
* @param {Object} _options - Call options
|
|
168
|
+
* @returns {Promise<{success: boolean, text: string, usage?: Object, error?: string}>}
|
|
169
|
+
*/
|
|
170
|
+
callSimple(_prompt, _options = {}) {
|
|
171
|
+
return Promise.reject(new Error(`${this.name}: SDK not implemented. Use CLI instead.`));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
isAvailable() {
|
|
175
|
+
throw new Error('Not implemented');
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
getCliPath() {
|
|
179
|
+
throw new Error('Not implemented');
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
getInstallInstructions() {
|
|
183
|
+
throw new Error('Not implemented');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
getAuthInstructions() {
|
|
187
|
+
throw new Error('Not implemented');
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
getCliFeatures() {
|
|
191
|
+
throw new Error('Not implemented');
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
getCredentialPaths() {
|
|
195
|
+
return [];
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
buildCommand(_context, _options) {
|
|
199
|
+
throw new Error('Not implemented');
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
parseEvent(_line) {
|
|
203
|
+
throw new Error('Not implemented');
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
getModelCatalog() {
|
|
207
|
+
throw new Error('Not implemented');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
getLevelMapping() {
|
|
211
|
+
throw new Error('Not implemented');
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
resolveModelSpec(level, overrides = {}) {
|
|
215
|
+
const mapping = this.getLevelMapping();
|
|
216
|
+
const base = mapping[level] || mapping[this.getDefaultLevel()];
|
|
217
|
+
if (!base) {
|
|
218
|
+
throw new Error(`Unknown level "${level}" for provider "${this.name}"`);
|
|
219
|
+
}
|
|
220
|
+
const override = overrides[level] || {};
|
|
221
|
+
const resolvedModel = this.validateModelId(override.model || base.model);
|
|
222
|
+
return {
|
|
223
|
+
level,
|
|
224
|
+
model: resolvedModel,
|
|
225
|
+
reasoningEffort: override.reasoningEffort || base.reasoningEffort,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
validateLevel(level, minLevel, maxLevel) {
|
|
230
|
+
const mapping = this.getLevelMapping();
|
|
231
|
+
const rank = (key) => mapping[key]?.rank;
|
|
232
|
+
|
|
233
|
+
if (!mapping[level]) {
|
|
234
|
+
throw new Error(`Invalid level "${level}" for provider "${this.name}"`);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (minLevel && !mapping[minLevel]) {
|
|
238
|
+
throw new Error(`Invalid minLevel "${minLevel}" for provider "${this.name}"`);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (maxLevel && !mapping[maxLevel]) {
|
|
242
|
+
throw new Error(`Invalid maxLevel "${maxLevel}" for provider "${this.name}"`);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (minLevel && maxLevel && rank(minLevel) > rank(maxLevel)) {
|
|
246
|
+
throw new Error(
|
|
247
|
+
`minLevel "${minLevel}" exceeds maxLevel "${maxLevel}" for provider "${this.name}"`
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (maxLevel && rank(level) > rank(maxLevel)) {
|
|
252
|
+
throw new Error(
|
|
253
|
+
`Level "${level}" exceeds maxLevel "${maxLevel}" for provider "${this.name}"`
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (minLevel && rank(level) < rank(minLevel)) {
|
|
258
|
+
throw new Error(
|
|
259
|
+
`Level "${level}" is below minLevel "${minLevel}" for provider "${this.name}"`
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return level;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
validateModelId(modelId) {
|
|
267
|
+
const catalog = this.getModelCatalog();
|
|
268
|
+
if (modelId && !catalog[modelId]) {
|
|
269
|
+
const err = new Error(
|
|
270
|
+
`Invalid model "${modelId}" for provider "${this.name}". ` +
|
|
271
|
+
`Use a model listed in provider settings/catalog.`
|
|
272
|
+
);
|
|
273
|
+
// Permanent misconfiguration/user input error - retries cannot fix this.
|
|
274
|
+
err.permanent = true;
|
|
275
|
+
throw err;
|
|
276
|
+
}
|
|
277
|
+
return modelId;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Resolve a model name to its CLI-compatible identifier.
|
|
282
|
+
* Override in provider implementations that need model ID transformation
|
|
283
|
+
* (e.g., Anthropic Bedrock mapping).
|
|
284
|
+
* @param {string} model - Provider model identifier
|
|
285
|
+
* @param {Object} _authEnv - Authentication environment variables
|
|
286
|
+
* @returns {string} CLI-compatible model identifier
|
|
287
|
+
*/
|
|
288
|
+
resolveCliModel(model, _authEnv = {}) {
|
|
289
|
+
return model;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
getDefaultLevel() {
|
|
293
|
+
throw new Error('Not implemented');
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Get default settings for this provider
|
|
298
|
+
* Override in provider implementations to provide provider-specific defaults
|
|
299
|
+
* @returns {Object} Default settings object
|
|
300
|
+
*/
|
|
301
|
+
getDefaultSettings() {
|
|
302
|
+
return {
|
|
303
|
+
maxLevel: this.getDefaultMaxLevel?.() || 'level3',
|
|
304
|
+
minLevel: this.getDefaultMinLevel?.() || 'level1',
|
|
305
|
+
defaultLevel: this.getDefaultLevel() || 'level2',
|
|
306
|
+
levelOverrides: {},
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Validate provider-specific settings
|
|
312
|
+
* Override in provider implementations to add custom validation
|
|
313
|
+
* @param {Object} settings - Settings object to validate
|
|
314
|
+
* @returns {string|null} Error message if invalid, null if valid
|
|
315
|
+
*/
|
|
316
|
+
validateSettings(settings) {
|
|
317
|
+
if (typeof settings !== 'object' || settings === null) {
|
|
318
|
+
return `providerSettings.${this.name} must be an object`;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Validate level fields
|
|
322
|
+
const levelMapping = this.getLevelMapping();
|
|
323
|
+
const validLevels = Object.keys(levelMapping);
|
|
324
|
+
|
|
325
|
+
if (settings.maxLevel && !validLevels.includes(settings.maxLevel)) {
|
|
326
|
+
return `Invalid maxLevel for ${this.name}: ${settings.maxLevel}`;
|
|
327
|
+
}
|
|
328
|
+
if (settings.minLevel && !validLevels.includes(settings.minLevel)) {
|
|
329
|
+
return `Invalid minLevel for ${this.name}: ${settings.minLevel}`;
|
|
330
|
+
}
|
|
331
|
+
if (settings.defaultLevel && !validLevels.includes(settings.defaultLevel)) {
|
|
332
|
+
return `Invalid defaultLevel for ${this.name}: ${settings.defaultLevel}`;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (
|
|
336
|
+
settings.levelOverrides &&
|
|
337
|
+
(typeof settings.levelOverrides !== 'object' || Array.isArray(settings.levelOverrides))
|
|
338
|
+
) {
|
|
339
|
+
return `levelOverrides for ${this.name} must be an object`;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Get the list of setting field names specific to this provider
|
|
347
|
+
* Override in provider implementations to declare custom fields
|
|
348
|
+
* @returns {string[]} Array of field names
|
|
349
|
+
*/
|
|
350
|
+
getSettingsFields() {
|
|
351
|
+
return ['maxLevel', 'minLevel', 'defaultLevel', 'levelOverrides'];
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
module.exports = BaseProvider;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
const { normalizeProviderName } = require('../../lib/provider-names');
|
|
2
|
+
|
|
3
|
+
const CAPABILITIES = {
|
|
4
|
+
claude: {
|
|
5
|
+
dockerIsolation: true,
|
|
6
|
+
worktreeIsolation: true,
|
|
7
|
+
mcpServers: true,
|
|
8
|
+
jsonSchema: true,
|
|
9
|
+
streamJson: true,
|
|
10
|
+
thinkingMode: true,
|
|
11
|
+
reasoningEffort: false,
|
|
12
|
+
},
|
|
13
|
+
codex: {
|
|
14
|
+
dockerIsolation: true,
|
|
15
|
+
worktreeIsolation: true,
|
|
16
|
+
mcpServers: true,
|
|
17
|
+
jsonSchema: true,
|
|
18
|
+
streamJson: true,
|
|
19
|
+
thinkingMode: true,
|
|
20
|
+
reasoningEffort: true,
|
|
21
|
+
},
|
|
22
|
+
gemini: {
|
|
23
|
+
dockerIsolation: true,
|
|
24
|
+
worktreeIsolation: true,
|
|
25
|
+
mcpServers: true,
|
|
26
|
+
jsonSchema: 'experimental',
|
|
27
|
+
streamJson: true,
|
|
28
|
+
thinkingMode: true,
|
|
29
|
+
reasoningEffort: false,
|
|
30
|
+
},
|
|
31
|
+
opencode: {
|
|
32
|
+
dockerIsolation: true,
|
|
33
|
+
worktreeIsolation: true,
|
|
34
|
+
mcpServers: true,
|
|
35
|
+
jsonSchema: 'experimental',
|
|
36
|
+
streamJson: true,
|
|
37
|
+
thinkingMode: true,
|
|
38
|
+
reasoningEffort: true,
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
function checkCapability(provider, capability) {
|
|
43
|
+
const caps = CAPABILITIES[normalizeProviderName(provider)];
|
|
44
|
+
if (!caps) return false;
|
|
45
|
+
return caps[capability] === true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function warnIfExperimental(provider, capability) {
|
|
49
|
+
const normalized = normalizeProviderName(provider);
|
|
50
|
+
const caps = CAPABILITIES[normalized];
|
|
51
|
+
if (caps?.[capability] === 'experimental') {
|
|
52
|
+
console.warn(`⚠️ ${capability} is experimental for ${normalized} and may not work reliably`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
module.exports = {
|
|
57
|
+
CAPABILITIES,
|
|
58
|
+
checkCapability,
|
|
59
|
+
warnIfExperimental,
|
|
60
|
+
};
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
const BaseProvider = require('./base-provider');
|
|
2
|
+
const { getClaudeCommand } = require('../../lib/settings');
|
|
3
|
+
const { normalizeProviderName } = require('../../lib/provider-names');
|
|
4
|
+
const { commandExists, getCommandPath } = require('../../lib/provider-detection');
|
|
5
|
+
const helper = require('../../lib/agent-cli-provider');
|
|
6
|
+
|
|
7
|
+
const PROVIDER_METADATA = {
|
|
8
|
+
claude: {
|
|
9
|
+
displayName: 'Claude',
|
|
10
|
+
cliCommand: 'claude',
|
|
11
|
+
helpArgs: () => getClaudeCommand().args,
|
|
12
|
+
commandParts: () => getClaudeCommand(),
|
|
13
|
+
installInstructions:
|
|
14
|
+
'npm install -g @anthropic-ai/claude-code\nOr (macOS): brew install claude',
|
|
15
|
+
authInstructions: 'claude login',
|
|
16
|
+
credentialPaths: ['~/.claude'],
|
|
17
|
+
settingsFields: ['anthropicApiKey', 'bedrockApiKey', 'bedrockRegion'],
|
|
18
|
+
},
|
|
19
|
+
codex: {
|
|
20
|
+
displayName: 'Codex',
|
|
21
|
+
cliCommand: 'codex',
|
|
22
|
+
helpArgs: () => ['exec'],
|
|
23
|
+
commandParts: () => ({ command: 'codex', args: [] }),
|
|
24
|
+
installInstructions: 'npm install -g @openai/codex',
|
|
25
|
+
authInstructions: 'codex login',
|
|
26
|
+
credentialPaths: ['~/.config/codex', '~/.codex'],
|
|
27
|
+
settingsFields: [],
|
|
28
|
+
},
|
|
29
|
+
gemini: {
|
|
30
|
+
displayName: 'Gemini',
|
|
31
|
+
cliCommand: 'gemini',
|
|
32
|
+
helpArgs: () => [],
|
|
33
|
+
commandParts: () => ({ command: 'gemini', args: [] }),
|
|
34
|
+
installInstructions: 'npm install -g @google/gemini-cli',
|
|
35
|
+
authInstructions: 'gemini auth login',
|
|
36
|
+
credentialPaths: ['~/.config/gcloud', '~/.config/gemini', '~/.gemini'],
|
|
37
|
+
settingsFields: [],
|
|
38
|
+
},
|
|
39
|
+
opencode: {
|
|
40
|
+
displayName: 'Opencode',
|
|
41
|
+
cliCommand: 'opencode',
|
|
42
|
+
helpArgs: () => ['run'],
|
|
43
|
+
commandParts: () => ({ command: 'opencode', args: [] }),
|
|
44
|
+
installInstructions: 'See https://opencode.ai for installation instructions.',
|
|
45
|
+
authInstructions: 'opencode auth login',
|
|
46
|
+
credentialPaths: ['~/.local/share/opencode'],
|
|
47
|
+
settingsFields: [],
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const warned = new Set();
|
|
52
|
+
|
|
53
|
+
function metadataForProvider(name) {
|
|
54
|
+
const metadata = PROVIDER_METADATA[name];
|
|
55
|
+
if (!metadata) {
|
|
56
|
+
throw new Error(`Unknown provider: ${name}. Valid: ${listProviders().join(', ')}`);
|
|
57
|
+
}
|
|
58
|
+
return metadata;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
class RuntimeProvider extends BaseProvider {
|
|
62
|
+
constructor(name) {
|
|
63
|
+
const normalized = normalizeProviderName(name);
|
|
64
|
+
const metadata = metadataForProvider(normalized);
|
|
65
|
+
super({
|
|
66
|
+
name: normalized,
|
|
67
|
+
displayName: metadata.displayName,
|
|
68
|
+
cliCommand: metadata.cliCommand,
|
|
69
|
+
});
|
|
70
|
+
this._metadata = metadata;
|
|
71
|
+
this._adapter = helper.getProviderAdapter(normalized);
|
|
72
|
+
this._cliFeatures = null;
|
|
73
|
+
this._parserState = this._adapter.createParserState();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
get adapter() {
|
|
77
|
+
return this._adapter;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
isAvailable() {
|
|
81
|
+
const { command } = this._metadata.commandParts();
|
|
82
|
+
return commandExists(command);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
getCliPath() {
|
|
86
|
+
const { command } = this._metadata.commandParts();
|
|
87
|
+
return getCommandPath(command) || command;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
getInstallInstructions() {
|
|
91
|
+
return this._metadata.installInstructions;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
getAuthInstructions() {
|
|
95
|
+
return this._metadata.authInstructions;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
getCliFeatures() {
|
|
99
|
+
if (this._cliFeatures) return this._cliFeatures;
|
|
100
|
+
this._cliFeatures = helper.detectRuntimeProviderCliFeatures(this.name);
|
|
101
|
+
return this._cliFeatures;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
getCredentialPaths() {
|
|
105
|
+
return this._metadata.credentialPaths;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
buildCommand(context, options = {}) {
|
|
109
|
+
const prepared = helper.prepareSingleAgentProviderCommand({
|
|
110
|
+
provider: this.name,
|
|
111
|
+
context,
|
|
112
|
+
options,
|
|
113
|
+
});
|
|
114
|
+
this._cliFeatures = prepared.cliFeatures;
|
|
115
|
+
const commandSpec = prepared.commandSpec;
|
|
116
|
+
this._warnCommandSpec(commandSpec);
|
|
117
|
+
return commandSpec;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
_warnCommandSpec(commandSpec) {
|
|
121
|
+
for (const warning of commandSpec.warnings || []) {
|
|
122
|
+
const key = `${warning.provider}:${warning.code}`;
|
|
123
|
+
if (warned.has(key)) continue;
|
|
124
|
+
warned.add(key);
|
|
125
|
+
console.warn(`⚠️ ${warning.message}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
parseEvent(line) {
|
|
130
|
+
const event = this._adapter.parseEvent(line, this._parserState);
|
|
131
|
+
return event || null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
parseChunk(chunk) {
|
|
135
|
+
return parseChunkWithProvider(this, chunk);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
getModelCatalog() {
|
|
139
|
+
return this._adapter.modelCatalog;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
getLevelMapping() {
|
|
143
|
+
return this._adapter.levelMapping;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
getDefaultLevel() {
|
|
147
|
+
return this._adapter.defaultLevel;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
getDefaultMaxLevel() {
|
|
151
|
+
return this._adapter.defaultMaxLevel;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
getDefaultMinLevel() {
|
|
155
|
+
return this._adapter.defaultMinLevel;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
resolveModelSpec(level, overrides = {}) {
|
|
159
|
+
return helper.resolveModelSpec(this.name, level, overrides);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
validateModelId(modelId) {
|
|
163
|
+
return this._adapter.validateModelId(modelId);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
isRetryableError(err) {
|
|
167
|
+
return helper.classifyProviderError(this.name, err).retryable;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
getDefaultSettings() {
|
|
171
|
+
const settings = super.getDefaultSettings();
|
|
172
|
+
if (this.name !== 'claude') return settings;
|
|
173
|
+
return {
|
|
174
|
+
...settings,
|
|
175
|
+
anthropicApiKey: null,
|
|
176
|
+
bedrockApiKey: null,
|
|
177
|
+
bedrockRegion: null,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
validateSettings(settings) {
|
|
182
|
+
const baseError = super.validateSettings(settings);
|
|
183
|
+
if (baseError) return baseError;
|
|
184
|
+
if (this.name !== 'claude') return null;
|
|
185
|
+
|
|
186
|
+
const { isValidAnthropicKey, ANTHROPIC_KEY_PREFIX } = require('../../lib/settings/claude-auth');
|
|
187
|
+
for (const field of this._metadata.settingsFields) {
|
|
188
|
+
if (
|
|
189
|
+
settings[field] !== undefined &&
|
|
190
|
+
settings[field] !== null &&
|
|
191
|
+
typeof settings[field] !== 'string'
|
|
192
|
+
) {
|
|
193
|
+
return `providerSettings.claude.${field} must be a string or null`;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (settings.anthropicApiKey && !isValidAnthropicKey(settings.anthropicApiKey)) {
|
|
197
|
+
return `providerSettings.claude.anthropicApiKey must start with ${ANTHROPIC_KEY_PREFIX}`;
|
|
198
|
+
}
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
getSettingsFields() {
|
|
203
|
+
return [...super.getSettingsFields(), ...this._metadata.settingsFields];
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function getProvider(name) {
|
|
208
|
+
const normalized = normalizeProviderName(name || '');
|
|
209
|
+
if (!PROVIDER_METADATA[normalized]) {
|
|
210
|
+
throw new Error(`Unknown provider: ${name}. Valid: ${listProviders().join(', ')}`);
|
|
211
|
+
}
|
|
212
|
+
return new RuntimeProvider(normalized);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async function detectProviders() {
|
|
216
|
+
const results = {};
|
|
217
|
+
for (const name of listProviders()) {
|
|
218
|
+
const provider = getProvider(name);
|
|
219
|
+
results[name] = {
|
|
220
|
+
available: await provider.isAvailable(),
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
return results;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function listProviders() {
|
|
227
|
+
return helper.listProviderAdapters();
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function stripTimestampPrefix(line) {
|
|
231
|
+
if (!line || typeof line !== 'string') return '';
|
|
232
|
+
let trimmed = line.trim().replace(/\r$/, '');
|
|
233
|
+
if (!trimmed) return '';
|
|
234
|
+
|
|
235
|
+
const tsMatch = trimmed.match(/^\[(\d{13})\](.*)$/);
|
|
236
|
+
if (tsMatch) trimmed = (tsMatch[2] || '').trimStart();
|
|
237
|
+
|
|
238
|
+
if (!trimmed.startsWith('{') && !trimmed.startsWith('[')) {
|
|
239
|
+
const pipeMatch = trimmed.match(/^[^|]{1,40}\|\s*(.*)$/);
|
|
240
|
+
if (pipeMatch) {
|
|
241
|
+
const afterPipe = (pipeMatch[1] || '').trimStart();
|
|
242
|
+
if (afterPipe.startsWith('{') || afterPipe.startsWith('[')) return afterPipe;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return trimmed;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function collectEvents(events, event) {
|
|
250
|
+
if (!event) return;
|
|
251
|
+
if (Array.isArray(event)) {
|
|
252
|
+
events.push(...event);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
events.push(event);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function parseChunkWithProvider(providerOrName, chunk) {
|
|
259
|
+
if (!chunk) return [];
|
|
260
|
+
const provider =
|
|
261
|
+
typeof providerOrName === 'string' ? getProvider(providerOrName) : providerOrName;
|
|
262
|
+
const events = [];
|
|
263
|
+
const lines = chunk.split('\n');
|
|
264
|
+
|
|
265
|
+
for (const line of lines) {
|
|
266
|
+
const content = stripTimestampPrefix(line);
|
|
267
|
+
if (!content) continue;
|
|
268
|
+
collectEvents(events, provider.parseEvent(content));
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return events;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function parseProviderChunk(providerName, chunk) {
|
|
275
|
+
return parseChunkWithProvider(providerName || 'claude', chunk);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function createProviderClass(name) {
|
|
279
|
+
return class ProviderFacade extends RuntimeProvider {
|
|
280
|
+
constructor() {
|
|
281
|
+
super(name);
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
module.exports = {
|
|
287
|
+
getProvider,
|
|
288
|
+
detectProviders,
|
|
289
|
+
listProviders,
|
|
290
|
+
parseProviderChunk,
|
|
291
|
+
parseChunkWithProvider,
|
|
292
|
+
createProviderClass,
|
|
293
|
+
};
|