opencode-ext-connector 0.3.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 +39 -0
- package/LICENSE +29 -0
- package/README.md +207 -0
- package/THIRD_PARTY_NOTICES.md +201 -0
- package/dist/catalog/parse-ids.d.ts +2 -0
- package/dist/catalog/parse-ids.js +49 -0
- package/dist/core/adapter.d.ts +16 -0
- package/dist/core/adapter.js +20 -0
- package/dist/core/clock.d.ts +7 -0
- package/dist/core/clock.js +1 -0
- package/dist/core/deadline.d.ts +13 -0
- package/dist/core/deadline.js +54 -0
- package/dist/core/errors.d.ts +57 -0
- package/dist/core/errors.js +81 -0
- package/dist/core/health.d.ts +22 -0
- package/dist/core/health.js +30 -0
- package/dist/core/http.d.ts +28 -0
- package/dist/core/http.js +1 -0
- package/dist/core/ids.d.ts +7 -0
- package/dist/core/ids.js +23 -0
- package/dist/core/lifecycle.d.ts +4 -0
- package/dist/core/lifecycle.js +16 -0
- package/dist/core/logger.d.ts +21 -0
- package/dist/core/logger.js +61 -0
- package/dist/core/models.d.ts +41 -0
- package/dist/core/models.js +59 -0
- package/dist/core/options.d.ts +31 -0
- package/dist/core/options.js +62 -0
- package/dist/core/process.d.ts +20 -0
- package/dist/core/process.js +1 -0
- package/dist/http/fetch-transport.d.ts +2 -0
- package/dist/http/fetch-transport.js +75 -0
- package/dist/http/package-version.d.ts +7 -0
- package/dist/http/package-version.js +46 -0
- package/dist/http/read-body.d.ts +9 -0
- package/dist/http/read-body.js +22 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +5 -0
- package/dist/logging/logger.d.ts +4 -0
- package/dist/logging/logger.js +11 -0
- package/dist/opencode/auth-store.d.ts +19 -0
- package/dist/opencode/auth-store.js +107 -0
- package/dist/opencode/beta-api.d.ts +3 -0
- package/dist/opencode/beta-api.js +1 -0
- package/dist/opencode/catalog-bridge.d.ts +3 -0
- package/dist/opencode/catalog-bridge.js +85 -0
- package/dist/opencode/health-refresh.d.ts +16 -0
- package/dist/opencode/health-refresh.js +49 -0
- package/dist/opencode/host-options.d.ts +2 -0
- package/dist/opencode/host-options.js +49 -0
- package/dist/opencode/language-factory.d.ts +13 -0
- package/dist/opencode/language-factory.js +37 -0
- package/dist/opencode/ollama-probe.d.ts +2 -0
- package/dist/opencode/ollama-probe.js +15 -0
- package/dist/opencode/ollama-production.d.ts +4 -0
- package/dist/opencode/ollama-production.js +10 -0
- package/dist/opencode/plugin.d.ts +33 -0
- package/dist/opencode/plugin.js +40 -0
- package/dist/opencode/process-exit.d.ts +4 -0
- package/dist/opencode/process-exit.js +22 -0
- package/dist/opencode/provider-entry.d.ts +25 -0
- package/dist/opencode/provider-entry.js +1 -0
- package/dist/opencode/providers.d.ts +16 -0
- package/dist/opencode/providers.js +140 -0
- package/dist/opencode/register-integrations.d.ts +3 -0
- package/dist/opencode/register-integrations.js +8 -0
- package/dist/opencode/reload.d.ts +9 -0
- package/dist/opencode/reload.js +30 -0
- package/dist/opencode/v1-anthropic-auth.d.ts +9 -0
- package/dist/opencode/v1-anthropic-auth.js +55 -0
- package/dist/opencode/v1-catalog.d.ts +13 -0
- package/dist/opencode/v1-catalog.js +80 -0
- package/dist/opencode/v1-language.d.ts +3 -0
- package/dist/opencode/v1-language.js +64 -0
- package/dist/opencode/v1-module.d.ts +23 -0
- package/dist/opencode/v1-module.js +74 -0
- package/dist/opencode/v1-session-auth.d.ts +5 -0
- package/dist/opencode/v1-session-auth.js +98 -0
- package/dist/providers/claude/adapter.d.ts +7 -0
- package/dist/providers/claude/adapter.js +37 -0
- package/dist/providers/claude/atomic-private-file.d.ts +13 -0
- package/dist/providers/claude/atomic-private-file.js +94 -0
- package/dist/providers/claude/auth-json.d.ts +2 -0
- package/dist/providers/claude/auth-json.js +14 -0
- package/dist/providers/claude/auth.d.ts +24 -0
- package/dist/providers/claude/auth.js +200 -0
- package/dist/providers/claude/cli-version.d.ts +10 -0
- package/dist/providers/claude/cli-version.js +36 -0
- package/dist/providers/claude/compat-betas.d.ts +5 -0
- package/dist/providers/claude/compat-betas.js +54 -0
- package/dist/providers/claude/compat-config.d.ts +8 -0
- package/dist/providers/claude/compat-config.js +31 -0
- package/dist/providers/claude/compat-request.d.ts +12 -0
- package/dist/providers/claude/compat-request.js +151 -0
- package/dist/providers/claude/compat-response.d.ts +1 -0
- package/dist/providers/claude/compat-response.js +54 -0
- package/dist/providers/claude/compat-signing.d.ts +9 -0
- package/dist/providers/claude/compat-signing.js +24 -0
- package/dist/providers/claude/compat-transform.d.ts +3 -0
- package/dist/providers/claude/compat-transform.js +224 -0
- package/dist/providers/claude/credentials.d.ts +7 -0
- package/dist/providers/claude/credentials.js +37 -0
- package/dist/providers/claude/emit-stream.d.ts +2 -0
- package/dist/providers/claude/emit-stream.js +62 -0
- package/dist/providers/claude/keychain-account.d.ts +1 -0
- package/dist/providers/claude/keychain-account.js +14 -0
- package/dist/providers/claude/language-model.d.ts +8 -0
- package/dist/providers/claude/language-model.js +109 -0
- package/dist/providers/claude/models.d.ts +9 -0
- package/dist/providers/claude/models.js +24 -0
- package/dist/providers/claude/prompt.d.ts +42 -0
- package/dist/providers/claude/prompt.js +134 -0
- package/dist/providers/claude/refresh.d.ts +19 -0
- package/dist/providers/claude/refresh.js +85 -0
- package/dist/providers/claude/sse-convert.d.ts +6 -0
- package/dist/providers/claude/sse-convert.js +74 -0
- package/dist/providers/claude/sse.d.ts +71 -0
- package/dist/providers/claude/sse.js +123 -0
- package/dist/providers/claude/transform.d.ts +32 -0
- package/dist/providers/claude/transform.js +52 -0
- package/dist/providers/claude/writeback.d.ts +18 -0
- package/dist/providers/claude/writeback.js +140 -0
- package/dist/providers/command-code/adapter.d.ts +7 -0
- package/dist/providers/command-code/adapter.js +37 -0
- package/dist/providers/command-code/auth.d.ts +5 -0
- package/dist/providers/command-code/auth.js +88 -0
- package/dist/providers/command-code/cli-version.d.ts +10 -0
- package/dist/providers/command-code/cli-version.js +36 -0
- package/dist/providers/command-code/emit-stream.d.ts +2 -0
- package/dist/providers/command-code/emit-stream.js +226 -0
- package/dist/providers/command-code/errors.d.ts +33 -0
- package/dist/providers/command-code/errors.js +105 -0
- package/dist/providers/command-code/language-model.d.ts +15 -0
- package/dist/providers/command-code/language-model.js +203 -0
- package/dist/providers/command-code/models.d.ts +3 -0
- package/dist/providers/command-code/models.js +18 -0
- package/dist/providers/command-code/record-stream.d.ts +1 -0
- package/dist/providers/command-code/record-stream.js +61 -0
- package/dist/providers/command-code/request-lifecycle.d.ts +8 -0
- package/dist/providers/command-code/request-lifecycle.js +33 -0
- package/dist/providers/command-code/request.d.ts +14 -0
- package/dist/providers/command-code/request.js +167 -0
- package/dist/providers/command-code/session.d.ts +5 -0
- package/dist/providers/command-code/session.js +9 -0
- package/dist/providers/cursor/adapter.d.ts +7 -0
- package/dist/providers/cursor/adapter.js +37 -0
- package/dist/providers/cursor/auth.d.ts +6 -0
- package/dist/providers/cursor/auth.js +56 -0
- package/dist/providers/cursor/blob-store.d.ts +26 -0
- package/dist/providers/cursor/blob-store.js +119 -0
- package/dist/providers/cursor/bridge-client.d.ts +33 -0
- package/dist/providers/cursor/bridge-client.js +200 -0
- package/dist/providers/cursor/bridge-event-sanitize.d.ts +3 -0
- package/dist/providers/cursor/bridge-event-sanitize.js +49 -0
- package/dist/providers/cursor/bridge-limits.d.ts +3 -0
- package/dist/providers/cursor/bridge-limits.js +10 -0
- package/dist/providers/cursor/bridge-process.d.ts +45 -0
- package/dist/providers/cursor/bridge-process.js +211 -0
- package/dist/providers/cursor/bridge-protocol.d.ts +68 -0
- package/dist/providers/cursor/bridge-protocol.js +49 -0
- package/dist/providers/cursor/bridge-serialize.d.ts +3 -0
- package/dist/providers/cursor/bridge-serialize.js +53 -0
- package/dist/providers/cursor/bridge-wire.d.ts +3 -0
- package/dist/providers/cursor/bridge-wire.js +163 -0
- package/dist/providers/cursor/checkpoint-store.d.ts +28 -0
- package/dist/providers/cursor/checkpoint-store.js +94 -0
- package/dist/providers/cursor/connect-frame-stream.d.ts +19 -0
- package/dist/providers/cursor/connect-frame-stream.js +160 -0
- package/dist/providers/cursor/connect-frame.d.ts +31 -0
- package/dist/providers/cursor/connect-frame.js +121 -0
- package/dist/providers/cursor/credentials.d.ts +5 -0
- package/dist/providers/cursor/credentials.js +22 -0
- package/dist/providers/cursor/direct-run-types.d.ts +33 -0
- package/dist/providers/cursor/direct-run-types.js +1 -0
- package/dist/providers/cursor/direct-run.d.ts +5 -0
- package/dist/providers/cursor/direct-run.js +213 -0
- package/dist/providers/cursor/direct-runtime.d.ts +23 -0
- package/dist/providers/cursor/direct-runtime.js +111 -0
- package/dist/providers/cursor/direct-stream.d.ts +28 -0
- package/dist/providers/cursor/direct-stream.js +190 -0
- package/dist/providers/cursor/exec-reply.d.ts +13 -0
- package/dist/providers/cursor/exec-reply.js +102 -0
- package/dist/providers/cursor/h2-bridge-response.d.ts +9 -0
- package/dist/providers/cursor/h2-bridge-response.js +51 -0
- package/dist/providers/cursor/h2-bridge-session.d.ts +24 -0
- package/dist/providers/cursor/h2-bridge-session.js +197 -0
- package/dist/providers/cursor/h2-bridge.d.ts +2 -0
- package/dist/providers/cursor/h2-bridge.js +12865 -0
- package/dist/providers/cursor/http2.d.ts +18 -0
- package/dist/providers/cursor/http2.js +144 -0
- package/dist/providers/cursor/interaction-reply.d.ts +6 -0
- package/dist/providers/cursor/interaction-reply.js +52 -0
- package/dist/providers/cursor/language-generate.d.ts +2 -0
- package/dist/providers/cursor/language-generate.js +35 -0
- package/dist/providers/cursor/language-model.d.ts +13 -0
- package/dist/providers/cursor/language-model.js +77 -0
- package/dist/providers/cursor/legacy-stream.d.ts +11 -0
- package/dist/providers/cursor/legacy-stream.js +115 -0
- package/dist/providers/cursor/mcp-tools.d.ts +7 -0
- package/dist/providers/cursor/mcp-tools.js +15 -0
- package/dist/providers/cursor/models.d.ts +14 -0
- package/dist/providers/cursor/models.js +116 -0
- package/dist/providers/cursor/ndjson.d.ts +3 -0
- package/dist/providers/cursor/ndjson.js +100 -0
- package/dist/providers/cursor/prompt.d.ts +4 -0
- package/dist/providers/cursor/prompt.js +142 -0
- package/dist/providers/cursor/proto/checkpoint.d.ts +35 -0
- package/dist/providers/cursor/proto/checkpoint.js +114 -0
- package/dist/providers/cursor/proto/context.d.ts +64 -0
- package/dist/providers/cursor/proto/context.js +178 -0
- package/dist/providers/cursor/proto/errors.d.ts +16 -0
- package/dist/providers/cursor/proto/errors.js +26 -0
- package/dist/providers/cursor/proto/exec-control.d.ts +20 -0
- package/dist/providers/cursor/proto/exec-control.js +54 -0
- package/dist/providers/cursor/proto/exec.d.ts +47 -0
- package/dist/providers/cursor/proto/exec.js +154 -0
- package/dist/providers/cursor/proto/fields.d.ts +22 -0
- package/dist/providers/cursor/proto/fields.js +97 -0
- package/dist/providers/cursor/proto/interaction-query.d.ts +15 -0
- package/dist/providers/cursor/proto/interaction-query.js +79 -0
- package/dist/providers/cursor/proto/interaction-tool-update.d.ts +10 -0
- package/dist/providers/cursor/proto/interaction-tool-update.js +58 -0
- package/dist/providers/cursor/proto/interaction-turn-ended.d.ts +6 -0
- package/dist/providers/cursor/proto/interaction-turn-ended.js +18 -0
- package/dist/providers/cursor/proto/interaction.d.ts +65 -0
- package/dist/providers/cursor/proto/interaction.js +185 -0
- package/dist/providers/cursor/proto/kv.d.ts +25 -0
- package/dist/providers/cursor/proto/kv.js +109 -0
- package/dist/providers/cursor/proto/mcp-result.d.ts +40 -0
- package/dist/providers/cursor/proto/mcp-result.js +150 -0
- package/dist/providers/cursor/proto/mcp.d.ts +19 -0
- package/dist/providers/cursor/proto/mcp.js +78 -0
- package/dist/providers/cursor/proto/model.d.ts +11 -0
- package/dist/providers/cursor/proto/model.js +36 -0
- package/dist/providers/cursor/proto/output-location.d.ts +7 -0
- package/dist/providers/cursor/proto/output-location.js +53 -0
- package/dist/providers/cursor/proto/prewarm.d.ts +16 -0
- package/dist/providers/cursor/proto/prewarm.js +68 -0
- package/dist/providers/cursor/proto/request-context.d.ts +16 -0
- package/dist/providers/cursor/proto/request-context.js +58 -0
- package/dist/providers/cursor/proto/request.d.ts +44 -0
- package/dist/providers/cursor/proto/request.js +111 -0
- package/dist/providers/cursor/proto/run-request.d.ts +19 -0
- package/dist/providers/cursor/proto/run-request.js +68 -0
- package/dist/providers/cursor/proto/server.d.ts +35 -0
- package/dist/providers/cursor/proto/server.js +93 -0
- package/dist/providers/cursor/proto/unknown-field.d.ts +1 -0
- package/dist/providers/cursor/proto/unknown-field.js +22 -0
- package/dist/providers/cursor/proto-value.d.ts +3 -0
- package/dist/providers/cursor/proto-value.js +170 -0
- package/dist/providers/cursor/proto-wire.d.ts +22 -0
- package/dist/providers/cursor/proto-wire.js +224 -0
- package/dist/providers/cursor/protocol-failure.d.ts +18 -0
- package/dist/providers/cursor/protocol-failure.js +45 -0
- package/dist/providers/cursor/recovery.d.ts +48 -0
- package/dist/providers/cursor/recovery.js +102 -0
- package/dist/providers/cursor/request-blob-ownership.d.ts +15 -0
- package/dist/providers/cursor/request-blob-ownership.js +39 -0
- package/dist/providers/cursor/request-build.d.ts +18 -0
- package/dist/providers/cursor/request-build.js +123 -0
- package/dist/providers/cursor/request-history.d.ts +32 -0
- package/dist/providers/cursor/request-history.js +163 -0
- package/dist/providers/cursor/request-input.d.ts +75 -0
- package/dist/providers/cursor/request-input.js +123 -0
- package/dist/providers/cursor/run-events.d.ts +15 -0
- package/dist/providers/cursor/run-events.js +43 -0
- package/dist/providers/cursor/run-session-expiry.d.ts +15 -0
- package/dist/providers/cursor/run-session-expiry.js +21 -0
- package/dist/providers/cursor/run-session.d.ts +50 -0
- package/dist/providers/cursor/run-session.js +205 -0
- package/dist/providers/cursor/run.d.ts +9 -0
- package/dist/providers/cursor/run.js +151 -0
- package/dist/providers/cursor/runner.d.ts +1 -0
- package/dist/providers/cursor/runner.js +55 -0
- package/dist/providers/cursor/server-dispatch-types.d.ts +83 -0
- package/dist/providers/cursor/server-dispatch-types.js +1 -0
- package/dist/providers/cursor/server-dispatch.d.ts +3 -0
- package/dist/providers/cursor/server-dispatch.js +178 -0
- package/dist/providers/cursor/session-state.d.ts +21 -0
- package/dist/providers/cursor/session-state.js +118 -0
- package/dist/providers/cursor/session.d.ts +1 -0
- package/dist/providers/cursor/session.js +26 -0
- package/dist/providers/cursor/settle-cleanup.d.ts +1 -0
- package/dist/providers/cursor/settle-cleanup.js +11 -0
- package/dist/providers/cursor/stream-adapter.d.ts +17 -0
- package/dist/providers/cursor/stream-adapter.js +99 -0
- package/dist/providers/cursor/tool-continuation.d.ts +14 -0
- package/dist/providers/cursor/tool-continuation.js +134 -0
- package/dist/providers/cursor/tool-stream.d.ts +2 -0
- package/dist/providers/cursor/tool-stream.js +220 -0
- package/dist/providers/cursor/usage.d.ts +3 -0
- package/dist/providers/cursor/usage.js +35 -0
- package/dist/providers/ollama/adapter.d.ts +8 -0
- package/dist/providers/ollama/adapter.js +65 -0
- package/dist/providers/ollama/catalog-state.d.ts +17 -0
- package/dist/providers/ollama/catalog-state.js +40 -0
- package/dist/providers/ollama/cloud-catalog.d.ts +3 -0
- package/dist/providers/ollama/cloud-catalog.js +82 -0
- package/dist/providers/ollama/errors.d.ts +14 -0
- package/dist/providers/ollama/errors.js +19 -0
- package/dist/providers/ollama/generate.d.ts +2 -0
- package/dist/providers/ollama/generate.js +49 -0
- package/dist/providers/ollama/html-links.d.ts +1 -0
- package/dist/providers/ollama/html-links.js +30 -0
- package/dist/providers/ollama/http.d.ts +12 -0
- package/dist/providers/ollama/http.js +67 -0
- package/dist/providers/ollama/index.d.ts +8 -0
- package/dist/providers/ollama/index.js +8 -0
- package/dist/providers/ollama/language-model.d.ts +11 -0
- package/dist/providers/ollama/language-model.js +62 -0
- package/dist/providers/ollama/local-catalog.d.ts +3 -0
- package/dist/providers/ollama/local-catalog.js +38 -0
- package/dist/providers/ollama/ndjson.d.ts +3 -0
- package/dist/providers/ollama/ndjson.js +51 -0
- package/dist/providers/ollama/prompt.d.ts +8 -0
- package/dist/providers/ollama/prompt.js +141 -0
- package/dist/providers/ollama/protocol.d.ts +56 -0
- package/dist/providers/ollama/protocol.js +26 -0
- package/dist/providers/ollama/runtime.d.ts +11 -0
- package/dist/providers/ollama/runtime.js +128 -0
- package/dist/providers/ollama/stream.d.ts +8 -0
- package/dist/providers/ollama/stream.js +110 -0
- package/dist/sdk/command-code.d.ts +5 -0
- package/dist/sdk/command-code.js +6 -0
- package/dist/sdk/cursor.d.ts +5 -0
- package/dist/sdk/cursor.js +6 -0
- package/dist/sdk/ollama.d.ts +5 -0
- package/dist/sdk/ollama.js +6 -0
- package/dist/server.d.ts +12 -0
- package/dist/server.js +94 -0
- package/docs/README.ko.md +189 -0
- package/package.json +75 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { readCommandCodeAccessToken } from "../providers/command-code/auth";
|
|
2
|
+
import { readCursorAccessToken } from "../providers/cursor/auth";
|
|
3
|
+
import { productionOllamaFetch } from "../providers/ollama/http";
|
|
4
|
+
import { probeLocalOllama } from "./ollama-probe";
|
|
5
|
+
const CURSOR_SESSION_MARKER = "cli-session:cursor";
|
|
6
|
+
const COMMAND_CODE_SESSION_MARKER = "cli-session:command-code";
|
|
7
|
+
const OLLAMA_SESSION_MARKER = "cli-session:ollama";
|
|
8
|
+
const OLLAMA_AVAILABLE_INSTRUCTIONS = "The connector will reuse the running local Ollama daemon. Cloud access remains managed by `ollama signin`; this plugin does not run sign-in.";
|
|
9
|
+
const OLLAMA_UNAVAILABLE_INSTRUCTIONS = "Start the local Ollama daemon, then retry.";
|
|
10
|
+
function sessionMethod(options) {
|
|
11
|
+
return {
|
|
12
|
+
type: "oauth",
|
|
13
|
+
label: options.label,
|
|
14
|
+
authorize: async () => {
|
|
15
|
+
const available = await options.verify();
|
|
16
|
+
return {
|
|
17
|
+
url: "",
|
|
18
|
+
instructions: available
|
|
19
|
+
? options.instructions
|
|
20
|
+
: `No logged-in ${options.label} session was found. Log in with the vendor CLI, then retry.`,
|
|
21
|
+
method: "auto",
|
|
22
|
+
callback: async () => available
|
|
23
|
+
? {
|
|
24
|
+
type: "success",
|
|
25
|
+
provider: options.provider,
|
|
26
|
+
key: options.marker,
|
|
27
|
+
}
|
|
28
|
+
: { type: "failed" },
|
|
29
|
+
};
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
async function hasCursorSession(env) {
|
|
34
|
+
return (await readCursorAccessToken(env, new AbortController().signal)) !== null;
|
|
35
|
+
}
|
|
36
|
+
async function hasCommandCodeSession(env) {
|
|
37
|
+
return (await readCommandCodeAccessToken(env, new AbortController().signal)) !== null;
|
|
38
|
+
}
|
|
39
|
+
export function createCursorSessionAuth(env) {
|
|
40
|
+
return {
|
|
41
|
+
provider: "cursor",
|
|
42
|
+
methods: [
|
|
43
|
+
sessionMethod({
|
|
44
|
+
provider: "cursor",
|
|
45
|
+
label: "Cursor CLI login",
|
|
46
|
+
instructions: "Using the existing Cursor CLI login/token.",
|
|
47
|
+
marker: CURSOR_SESSION_MARKER,
|
|
48
|
+
verify: () => hasCursorSession(env),
|
|
49
|
+
}),
|
|
50
|
+
],
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
export function createCommandCodeSessionAuth(env) {
|
|
54
|
+
return {
|
|
55
|
+
provider: "command-code",
|
|
56
|
+
methods: [
|
|
57
|
+
sessionMethod({
|
|
58
|
+
provider: "command-code",
|
|
59
|
+
label: "Command Code CLI login",
|
|
60
|
+
instructions: "Using the existing Command Code CLI session.",
|
|
61
|
+
marker: COMMAND_CODE_SESSION_MARKER,
|
|
62
|
+
verify: () => hasCommandCodeSession(env),
|
|
63
|
+
}),
|
|
64
|
+
{
|
|
65
|
+
type: "api",
|
|
66
|
+
label: "Command Code API key",
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
export function createOllamaSessionAuth(fetch = productionOllamaFetch) {
|
|
72
|
+
return {
|
|
73
|
+
provider: "ollama",
|
|
74
|
+
methods: [
|
|
75
|
+
{
|
|
76
|
+
type: "oauth",
|
|
77
|
+
label: "Ollama local daemon",
|
|
78
|
+
authorize: async () => {
|
|
79
|
+
const available = await probeLocalOllama(fetch);
|
|
80
|
+
return {
|
|
81
|
+
url: "",
|
|
82
|
+
instructions: available
|
|
83
|
+
? OLLAMA_AVAILABLE_INSTRUCTIONS
|
|
84
|
+
: OLLAMA_UNAVAILABLE_INSTRUCTIONS,
|
|
85
|
+
method: "auto",
|
|
86
|
+
callback: async () => (await probeLocalOllama(fetch))
|
|
87
|
+
? {
|
|
88
|
+
type: "success",
|
|
89
|
+
provider: "ollama",
|
|
90
|
+
key: OLLAMA_SESSION_MARKER,
|
|
91
|
+
}
|
|
92
|
+
: { type: "failed" },
|
|
93
|
+
};
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
};
|
|
98
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ProviderAdapter } from "../../core/adapter";
|
|
2
|
+
import type { AdapterModel } from "../../core/models";
|
|
3
|
+
export type ClaudeAdapterOptions = {
|
|
4
|
+
readonly readAccessToken: (signal: AbortSignal) => Promise<string | null>;
|
|
5
|
+
readonly listModels: (token: string, signal: AbortSignal) => Promise<readonly AdapterModel[]>;
|
|
6
|
+
};
|
|
7
|
+
export declare function createClaudeAdapter(options: ClaudeAdapterOptions): ProviderAdapter;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { OperationCancelledError } from "../../core/errors";
|
|
2
|
+
import { parseProviderId } from "../../core/ids";
|
|
3
|
+
import { createAsyncDisposable } from "../../core/lifecycle";
|
|
4
|
+
export function createClaudeAdapter(options) {
|
|
5
|
+
const providerId = parseProviderId("claude");
|
|
6
|
+
const disposal = createAsyncDisposable(() => undefined);
|
|
7
|
+
let lastModels = null;
|
|
8
|
+
return {
|
|
9
|
+
providerId,
|
|
10
|
+
snapshot: async (signal) => {
|
|
11
|
+
if (signal.aborted) {
|
|
12
|
+
throw new OperationCancelledError("claude-snapshot");
|
|
13
|
+
}
|
|
14
|
+
const token = await options.readAccessToken(signal);
|
|
15
|
+
if (token === null) {
|
|
16
|
+
return { status: "unavailable", providerId, reason: "invalid-data" };
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
const models = await options.listModels(token, signal);
|
|
20
|
+
if (models.length === 0) {
|
|
21
|
+
return lastModels === null
|
|
22
|
+
? { status: "unavailable", providerId, reason: "invalid-data" }
|
|
23
|
+
: { status: "stale", providerId, models: lastModels, reason: "invalid-data" };
|
|
24
|
+
}
|
|
25
|
+
lastModels = models;
|
|
26
|
+
return { status: "ready", providerId, models };
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return lastModels === null
|
|
30
|
+
? { status: "unavailable", providerId, reason: "transport-error" }
|
|
31
|
+
: { status: "stale", providerId, models: lastModels, reason: "transport-error" };
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
dispose: disposal.dispose,
|
|
35
|
+
[Symbol.asyncDispose]: disposal[Symbol.asyncDispose],
|
|
36
|
+
};
|
|
37
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare class ClaudeCredentialSymlinkError extends Error {
|
|
2
|
+
readonly path: string;
|
|
3
|
+
readonly name = "ClaudeCredentialSymlinkError";
|
|
4
|
+
constructor(path: string);
|
|
5
|
+
}
|
|
6
|
+
export declare class ClaudeCredentialCleanupError extends Error {
|
|
7
|
+
readonly path: string;
|
|
8
|
+
readonly primaryFailure: unknown;
|
|
9
|
+
readonly cleanupFailures: readonly unknown[];
|
|
10
|
+
readonly name = "ClaudeCredentialCleanupError";
|
|
11
|
+
constructor(path: string, primaryFailure: unknown, cleanupFailures: readonly unknown[]);
|
|
12
|
+
}
|
|
13
|
+
export declare function writeClaudePrivateFile(path: string, body: string): Promise<void>;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { lstat, mkdir, open, rename, unlink } from "node:fs/promises";
|
|
3
|
+
import { basename, dirname, join } from "node:path";
|
|
4
|
+
export class ClaudeCredentialSymlinkError extends Error {
|
|
5
|
+
path;
|
|
6
|
+
name = "ClaudeCredentialSymlinkError";
|
|
7
|
+
constructor(path) {
|
|
8
|
+
super(`refusing to replace symbolic link at ${path}`);
|
|
9
|
+
this.path = path;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export class ClaudeCredentialCleanupError extends Error {
|
|
13
|
+
path;
|
|
14
|
+
primaryFailure;
|
|
15
|
+
cleanupFailures;
|
|
16
|
+
name = "ClaudeCredentialCleanupError";
|
|
17
|
+
constructor(path, primaryFailure, cleanupFailures) {
|
|
18
|
+
super(`credential write failed and temporary-file cleanup also failed for ${path}`, {
|
|
19
|
+
cause: primaryFailure,
|
|
20
|
+
});
|
|
21
|
+
this.path = path;
|
|
22
|
+
this.primaryFailure = primaryFailure;
|
|
23
|
+
this.cleanupFailures = cleanupFailures;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
class CleanupRejectionError extends Error {
|
|
27
|
+
rejection;
|
|
28
|
+
name = "CleanupRejectionError";
|
|
29
|
+
constructor(rejection) {
|
|
30
|
+
super("temporary-file cleanup rejected with a non-Error value", { cause: rejection });
|
|
31
|
+
this.rejection = rejection;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function errorCode(error) {
|
|
35
|
+
return "code" in error ? Reflect.get(error, "code") : undefined;
|
|
36
|
+
}
|
|
37
|
+
async function rejectSymbolicLink(path) {
|
|
38
|
+
try {
|
|
39
|
+
const destination = await lstat(path);
|
|
40
|
+
if (destination.isSymbolicLink()) {
|
|
41
|
+
throw new ClaudeCredentialSymlinkError(path);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
if (error instanceof Error && errorCode(error) === "ENOENT") {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export async function writeClaudePrivateFile(path, body) {
|
|
52
|
+
const directory = dirname(path);
|
|
53
|
+
await mkdir(directory, { recursive: true });
|
|
54
|
+
await rejectSymbolicLink(path);
|
|
55
|
+
const candidate = join(directory, `.${basename(path)}.${randomUUID()}.tmp`);
|
|
56
|
+
let temporaryPath = null;
|
|
57
|
+
let handle = null;
|
|
58
|
+
try {
|
|
59
|
+
handle = await open(candidate, "wx", 0o600);
|
|
60
|
+
temporaryPath = candidate;
|
|
61
|
+
await handle.writeFile(body, { encoding: "utf8" });
|
|
62
|
+
await handle.chmod(0o600);
|
|
63
|
+
await handle.sync();
|
|
64
|
+
await handle.close();
|
|
65
|
+
handle = null;
|
|
66
|
+
await rename(candidate, path);
|
|
67
|
+
temporaryPath = null;
|
|
68
|
+
}
|
|
69
|
+
catch (primaryFailure) {
|
|
70
|
+
const cleanupFailures = [];
|
|
71
|
+
if (handle !== null) {
|
|
72
|
+
try {
|
|
73
|
+
await handle.close();
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
cleanupFailures.push(error instanceof Error ? error : new CleanupRejectionError(error));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (temporaryPath !== null) {
|
|
80
|
+
try {
|
|
81
|
+
await unlink(temporaryPath);
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
if (!(error instanceof Error && errorCode(error) === "ENOENT")) {
|
|
85
|
+
cleanupFailures.push(error);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (cleanupFailures.length > 0) {
|
|
90
|
+
throw new ClaudeCredentialCleanupError(path, primaryFailure, cleanupFailures);
|
|
91
|
+
}
|
|
92
|
+
throw primaryFailure;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function mergeOpencodeAuthJson(existing, credentials) {
|
|
2
|
+
const root = typeof existing === "object" && existing !== null && !Array.isArray(existing)
|
|
3
|
+
? { ...existing }
|
|
4
|
+
: {};
|
|
5
|
+
return `${JSON.stringify({
|
|
6
|
+
...root,
|
|
7
|
+
anthropic: {
|
|
8
|
+
type: "oauth",
|
|
9
|
+
access: credentials.accessToken,
|
|
10
|
+
refresh: credentials.refreshToken,
|
|
11
|
+
expires: credentials.expiresAtMs,
|
|
12
|
+
},
|
|
13
|
+
}, null, 2)}\n`;
|
|
14
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Clock } from "../../core/clock";
|
|
2
|
+
import type { HttpTransport } from "../../core/http";
|
|
3
|
+
import type { CredentialRefreshPolicy } from "../../core/options";
|
|
4
|
+
import { type ClaudeCredentials } from "./credentials";
|
|
5
|
+
export type ClaudeAuthLookup = {
|
|
6
|
+
readonly readKeychain?: (service: string, signal: AbortSignal) => Promise<string | null>;
|
|
7
|
+
};
|
|
8
|
+
export declare function readClaudeAccessToken(env: Readonly<Record<string, string | undefined>>, signal: AbortSignal, lookup?: ClaudeAuthLookup): Promise<string | null>;
|
|
9
|
+
export declare function readClaudeCredentials(env: Readonly<Record<string, string | undefined>>, signal: AbortSignal, lookup?: ClaudeAuthLookup): Promise<ClaudeCredentials | null>;
|
|
10
|
+
type ClaudeTokenOptions = {
|
|
11
|
+
readonly env: Readonly<Record<string, string | undefined>>;
|
|
12
|
+
readonly clock: Clock;
|
|
13
|
+
readonly transport: HttpTransport;
|
|
14
|
+
readonly lookup?: ClaudeAuthLookup;
|
|
15
|
+
readonly refresh?: CredentialRefreshPolicy;
|
|
16
|
+
readonly writeBack?: (credentials: ClaudeCredentials) => Promise<void>;
|
|
17
|
+
};
|
|
18
|
+
export type ClaudeTokenManager = {
|
|
19
|
+
readonly readAccessToken: (signal: AbortSignal) => Promise<string | null>;
|
|
20
|
+
readonly forceRefreshAccessToken: (signal: AbortSignal) => Promise<string | null>;
|
|
21
|
+
};
|
|
22
|
+
export declare function createClaudeTokenManager(options: ClaudeTokenOptions): ClaudeTokenManager;
|
|
23
|
+
export declare function createClaudeTokenReader(options: ClaudeTokenOptions): (signal: AbortSignal) => Promise<string | null>;
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { promisify } from "node:util";
|
|
6
|
+
import { OperationCancelledError } from "../../core/errors";
|
|
7
|
+
import { claudeAccessNeedsRefresh, parseClaudeCredentials, } from "./credentials";
|
|
8
|
+
import { refreshClaudeAccessTokenResult } from "./refresh";
|
|
9
|
+
const execFileAsync = promisify(execFile);
|
|
10
|
+
const CLAUDE_KEYCHAIN_SERVICE = "Claude Code-credentials";
|
|
11
|
+
function credentialsFromRaw(raw) {
|
|
12
|
+
try {
|
|
13
|
+
return parseClaudeCredentials(JSON.parse(raw));
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function keychainFailure(error) {
|
|
20
|
+
if (typeof error !== "object" || error === null) {
|
|
21
|
+
return new Error("Failed to read Claude Code credentials from macOS Keychain.", {
|
|
22
|
+
cause: error,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
const status = "status" in error ? error.status : undefined;
|
|
26
|
+
const code = "code" in error ? error.code : undefined;
|
|
27
|
+
const killed = "killed" in error ? error.killed : undefined;
|
|
28
|
+
if (killed === true || code === "ETIMEDOUT") {
|
|
29
|
+
return new Error("Claude Code Keychain read timed out.", { cause: error });
|
|
30
|
+
}
|
|
31
|
+
if (status === 36) {
|
|
32
|
+
return new Error("macOS Keychain is locked.", { cause: error });
|
|
33
|
+
}
|
|
34
|
+
if (status === 128) {
|
|
35
|
+
return new Error("macOS Keychain access was denied.", { cause: error });
|
|
36
|
+
}
|
|
37
|
+
return status === 44
|
|
38
|
+
? null
|
|
39
|
+
: new Error("Failed to read Claude Code credentials.", { cause: error });
|
|
40
|
+
}
|
|
41
|
+
async function defaultReadKeychain(service, signal) {
|
|
42
|
+
if (process.platform !== "darwin") {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
const result = await execFileAsync("security", ["find-generic-password", "-s", service, "-w"], {
|
|
47
|
+
timeout: 2_000,
|
|
48
|
+
signal,
|
|
49
|
+
});
|
|
50
|
+
const text = result.stdout.trim();
|
|
51
|
+
return text.length > 0 ? text : null;
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
const failure = keychainFailure(error);
|
|
55
|
+
if (failure !== null) {
|
|
56
|
+
throw failure;
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export async function readClaudeAccessToken(env, signal, lookup = {}) {
|
|
62
|
+
const credentials = await readClaudeCredentials(env, signal, lookup);
|
|
63
|
+
return credentials?.accessToken ?? null;
|
|
64
|
+
}
|
|
65
|
+
export async function readClaudeCredentials(env, signal, lookup = {}) {
|
|
66
|
+
if (signal.aborted) {
|
|
67
|
+
throw new OperationCancelledError("claude-read-credentials");
|
|
68
|
+
}
|
|
69
|
+
const readKeychain = lookup.readKeychain ?? defaultReadKeychain;
|
|
70
|
+
const keychainRaw = await readKeychain(CLAUDE_KEYCHAIN_SERVICE, signal);
|
|
71
|
+
if (signal.aborted) {
|
|
72
|
+
throw new OperationCancelledError("claude-read-credentials");
|
|
73
|
+
}
|
|
74
|
+
if (keychainRaw !== null) {
|
|
75
|
+
const fromKeychain = credentialsFromRaw(keychainRaw);
|
|
76
|
+
if (fromKeychain !== null) {
|
|
77
|
+
return fromKeychain;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const configDir = env["CLAUDE_CONFIG_DIR"] ?? join(homedir(), ".claude");
|
|
81
|
+
const credentialPath = join(configDir, ".credentials.json");
|
|
82
|
+
try {
|
|
83
|
+
const text = await readFile(credentialPath, "utf8");
|
|
84
|
+
if (signal.aborted) {
|
|
85
|
+
throw new OperationCancelledError("claude-read-credentials");
|
|
86
|
+
}
|
|
87
|
+
return parseClaudeCredentials(JSON.parse(text));
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
if (error instanceof OperationCancelledError) {
|
|
91
|
+
throw error;
|
|
92
|
+
}
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const DefaultRefreshPolicy = { mode: "auto", leadMs: 60_000 };
|
|
97
|
+
export function createClaudeTokenManager(options) {
|
|
98
|
+
const policy = options.refresh ?? DefaultRefreshPolicy;
|
|
99
|
+
let cached = null;
|
|
100
|
+
let lastStored = null;
|
|
101
|
+
let refreshing = null;
|
|
102
|
+
let refreshRetryAtMs = 0;
|
|
103
|
+
let consecutiveRefreshFailures = 0;
|
|
104
|
+
const persist = async (credentials) => {
|
|
105
|
+
cached = credentials;
|
|
106
|
+
if (options.writeBack !== undefined) {
|
|
107
|
+
await options.writeBack(credentials);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
const readSource = async (signal) => {
|
|
111
|
+
const latest = await readClaudeCredentials(options.env, signal, options.lookup ?? {});
|
|
112
|
+
if (latest !== null &&
|
|
113
|
+
(lastStored === null ||
|
|
114
|
+
latest.accessToken !== lastStored.accessToken ||
|
|
115
|
+
latest.refreshToken !== lastStored.refreshToken ||
|
|
116
|
+
latest.expiresAtMs !== lastStored.expiresAtMs)) {
|
|
117
|
+
lastStored = latest;
|
|
118
|
+
cached = latest;
|
|
119
|
+
}
|
|
120
|
+
return cached;
|
|
121
|
+
};
|
|
122
|
+
const refresh = async (credentials, signal) => {
|
|
123
|
+
if (credentials.refreshToken === null) {
|
|
124
|
+
return credentials.accessToken;
|
|
125
|
+
}
|
|
126
|
+
if (options.clock.nowMs() < refreshRetryAtMs) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
if (refreshing !== null) {
|
|
130
|
+
return refreshing;
|
|
131
|
+
}
|
|
132
|
+
const operation = (async () => {
|
|
133
|
+
const result = await refreshClaudeAccessTokenResult({
|
|
134
|
+
transport: options.transport,
|
|
135
|
+
clock: options.clock,
|
|
136
|
+
refreshToken: credentials.refreshToken ?? "",
|
|
137
|
+
signal,
|
|
138
|
+
});
|
|
139
|
+
if (!result.ok) {
|
|
140
|
+
if (result.kind === "transient") {
|
|
141
|
+
consecutiveRefreshFailures += 1;
|
|
142
|
+
const scheduled = Math.min(60_000, 15_000 * 2 ** Math.max(0, consecutiveRefreshFailures - 1));
|
|
143
|
+
refreshRetryAtMs =
|
|
144
|
+
options.clock.nowMs() + Math.min(60_000, result.retryAfterMs ?? scheduled);
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
consecutiveRefreshFailures = 0;
|
|
148
|
+
refreshRetryAtMs = 0;
|
|
149
|
+
}
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
const refreshed = result.credentials;
|
|
153
|
+
consecutiveRefreshFailures = 0;
|
|
154
|
+
refreshRetryAtMs = 0;
|
|
155
|
+
await persist(refreshed);
|
|
156
|
+
return refreshed.accessToken;
|
|
157
|
+
})();
|
|
158
|
+
refreshing = operation;
|
|
159
|
+
try {
|
|
160
|
+
return await operation;
|
|
161
|
+
}
|
|
162
|
+
finally {
|
|
163
|
+
if (refreshing === operation) {
|
|
164
|
+
refreshing = null;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
const readAccessToken = async (signal) => {
|
|
169
|
+
const source = await readSource(signal);
|
|
170
|
+
if (source === null) {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
if (policy.mode === "never" ||
|
|
174
|
+
source.refreshToken === null ||
|
|
175
|
+
!claudeAccessNeedsRefresh(source, options.clock.nowMs(), policy.leadMs)) {
|
|
176
|
+
return source.accessToken;
|
|
177
|
+
}
|
|
178
|
+
const refreshedToken = await refresh(source, signal);
|
|
179
|
+
if (refreshedToken === null) {
|
|
180
|
+
return source.accessToken;
|
|
181
|
+
}
|
|
182
|
+
return refreshedToken;
|
|
183
|
+
};
|
|
184
|
+
const forceRefreshAccessToken = async (signal) => {
|
|
185
|
+
const previous = cached?.accessToken ?? null;
|
|
186
|
+
const source = await readSource(signal);
|
|
187
|
+
if (source === null) {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
if (policy.mode === "never") {
|
|
191
|
+
return source.accessToken === previous ? null : source.accessToken;
|
|
192
|
+
}
|
|
193
|
+
return refresh(source, signal);
|
|
194
|
+
};
|
|
195
|
+
return { readAccessToken, forceRefreshAccessToken };
|
|
196
|
+
}
|
|
197
|
+
export function createClaudeTokenReader(options) {
|
|
198
|
+
const manager = createClaudeTokenManager(options);
|
|
199
|
+
return manager.readAccessToken;
|
|
200
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { HttpTransport } from "../../core/http";
|
|
2
|
+
export type ClaudeVersionResolver = (signal: AbortSignal) => Promise<string | null>;
|
|
3
|
+
export type ClaudeVersionResolverOptions = {
|
|
4
|
+
readonly env: Readonly<Record<string, string | undefined>>;
|
|
5
|
+
readonly transport: HttpTransport;
|
|
6
|
+
readonly readInstalledVersion?: () => string | null;
|
|
7
|
+
};
|
|
8
|
+
export declare function parseClaudeCliVersion(stdout: string): string | null;
|
|
9
|
+
export declare function readInstalledClaudeVersion(env: Readonly<Record<string, string | undefined>>): string | null;
|
|
10
|
+
export declare function createClaudeVersionResolver(options: ClaudeVersionResolverOptions): ClaudeVersionResolver;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { createPackageVersionResolver } from "../../http/package-version";
|
|
3
|
+
const CLAUDE_CODE_PACKAGE = "@anthropic-ai/claude-code";
|
|
4
|
+
export function parseClaudeCliVersion(stdout) {
|
|
5
|
+
const match = /(\d+\.\d+\.\d+)/.exec(stdout);
|
|
6
|
+
return match?.[1] ?? null;
|
|
7
|
+
}
|
|
8
|
+
export function readInstalledClaudeVersion(env) {
|
|
9
|
+
const result = spawnSync("claude", ["--version"], {
|
|
10
|
+
encoding: "utf8",
|
|
11
|
+
timeout: 3_000,
|
|
12
|
+
env: { ...env },
|
|
13
|
+
});
|
|
14
|
+
if (result.error !== undefined || result.status !== 0) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
return parseClaudeCliVersion(result.stdout);
|
|
18
|
+
}
|
|
19
|
+
export function createClaudeVersionResolver(options) {
|
|
20
|
+
const readInstalled = options.readInstalledVersion ?? (() => readInstalledClaudeVersion(options.env));
|
|
21
|
+
const readPublished = createPackageVersionResolver({
|
|
22
|
+
transport: options.transport,
|
|
23
|
+
packageName: CLAUDE_CODE_PACKAGE,
|
|
24
|
+
});
|
|
25
|
+
let local;
|
|
26
|
+
return async (signal) => {
|
|
27
|
+
if (local === undefined) {
|
|
28
|
+
const override = options.env["ANTHROPIC_CLI_VERSION"]?.trim();
|
|
29
|
+
local =
|
|
30
|
+
override !== undefined && override.length > 0
|
|
31
|
+
? parseClaudeCliVersion(override)
|
|
32
|
+
: readInstalled();
|
|
33
|
+
}
|
|
34
|
+
return local ?? readPublished(signal);
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function claudeExcludedBetas(modelId: string): ReadonlySet<string>;
|
|
2
|
+
export declare function excludeClaudeBeta(modelId: string, beta: string): void;
|
|
3
|
+
export declare function nextClaudeBetaToExclude(modelId: string): string | null;
|
|
4
|
+
export declare function isClaudeLongContextError(body: string): boolean;
|
|
5
|
+
export declare function claudeModelBetas(modelId: string): readonly string[];
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// Derived from griffinmartin/opencode-claude-auth@0f0ff6f12c367339130cbfd250393863ed2c8d9e.
|
|
2
|
+
// Licensed under MIT. See THIRD_PARTY_NOTICES.md.
|
|
3
|
+
import { CLAUDE_BASE_BETAS, CLAUDE_LONG_CONTEXT_BETAS, claudeModelOverride } from "./compat-config";
|
|
4
|
+
const excludedBetas = new Map();
|
|
5
|
+
let lastFlags = process.env["ANTHROPIC_BETA_FLAGS"];
|
|
6
|
+
let lastModelId;
|
|
7
|
+
function requiredBetas() {
|
|
8
|
+
return (process.env["ANTHROPIC_BETA_FLAGS"] ?? CLAUDE_BASE_BETAS.join(","))
|
|
9
|
+
.split(",")
|
|
10
|
+
.map((value) => value.trim())
|
|
11
|
+
.filter((value) => value.length > 0);
|
|
12
|
+
}
|
|
13
|
+
export function claudeExcludedBetas(modelId) {
|
|
14
|
+
const flags = process.env["ANTHROPIC_BETA_FLAGS"];
|
|
15
|
+
if (flags !== lastFlags || (lastModelId !== undefined && lastModelId !== modelId)) {
|
|
16
|
+
excludedBetas.clear();
|
|
17
|
+
}
|
|
18
|
+
lastFlags = flags;
|
|
19
|
+
lastModelId = modelId;
|
|
20
|
+
return excludedBetas.get(modelId) ?? new Set();
|
|
21
|
+
}
|
|
22
|
+
export function excludeClaudeBeta(modelId, beta) {
|
|
23
|
+
const excluded = excludedBetas.get(modelId) ?? new Set();
|
|
24
|
+
excluded.add(beta);
|
|
25
|
+
excludedBetas.set(modelId, excluded);
|
|
26
|
+
}
|
|
27
|
+
export function nextClaudeBetaToExclude(modelId) {
|
|
28
|
+
const excluded = claudeExcludedBetas(modelId);
|
|
29
|
+
for (const beta of CLAUDE_LONG_CONTEXT_BETAS) {
|
|
30
|
+
if (!excluded.has(beta)) {
|
|
31
|
+
return beta;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
export function isClaudeLongContextError(body) {
|
|
37
|
+
return (body.includes("Extra usage is required for long context requests") ||
|
|
38
|
+
body.includes("long context beta is not yet available") ||
|
|
39
|
+
body.includes("You're out of extra usage"));
|
|
40
|
+
}
|
|
41
|
+
export function claudeModelBetas(modelId) {
|
|
42
|
+
let betas = requiredBetas();
|
|
43
|
+
const override = claudeModelOverride(modelId);
|
|
44
|
+
if (override?.exclude !== undefined) {
|
|
45
|
+
betas = betas.filter((beta) => !override.exclude?.includes(beta));
|
|
46
|
+
}
|
|
47
|
+
for (const beta of override?.add ?? []) {
|
|
48
|
+
if (!betas.includes(beta)) {
|
|
49
|
+
betas.push(beta);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const excluded = claudeExcludedBetas(modelId);
|
|
53
|
+
return betas.filter((beta) => !excluded.has(beta));
|
|
54
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type ClaudeModelOverride = {
|
|
2
|
+
readonly exclude?: readonly string[];
|
|
3
|
+
readonly add?: readonly string[];
|
|
4
|
+
readonly disableEffort?: boolean;
|
|
5
|
+
};
|
|
6
|
+
export declare const CLAUDE_BASE_BETAS: readonly string[];
|
|
7
|
+
export declare const CLAUDE_LONG_CONTEXT_BETAS: readonly string[];
|
|
8
|
+
export declare function claudeModelOverride(modelId: string): ClaudeModelOverride | null;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Derived from griffinmartin/opencode-claude-auth@0f0ff6f12c367339130cbfd250393863ed2c8d9e.
|
|
2
|
+
// Licensed under MIT. See THIRD_PARTY_NOTICES.md.
|
|
3
|
+
export const CLAUDE_BASE_BETAS = [
|
|
4
|
+
"claude-code-20250219",
|
|
5
|
+
"oauth-2025-04-20",
|
|
6
|
+
"interleaved-thinking-2025-05-14",
|
|
7
|
+
"prompt-caching-scope-2026-01-05",
|
|
8
|
+
"context-management-2025-06-27",
|
|
9
|
+
"advisor-tool-2026-03-01",
|
|
10
|
+
"thinking-token-count-2026-05-13",
|
|
11
|
+
"extended-cache-ttl-2025-04-11",
|
|
12
|
+
];
|
|
13
|
+
export const CLAUDE_LONG_CONTEXT_BETAS = [
|
|
14
|
+
"context-1m-2025-08-07",
|
|
15
|
+
"interleaved-thinking-2025-05-14",
|
|
16
|
+
];
|
|
17
|
+
const MODEL_OVERRIDES = {
|
|
18
|
+
sonnet: { exclude: ["effort-2025-11-24"] },
|
|
19
|
+
haiku: { exclude: ["effort-2025-11-24"], disableEffort: true },
|
|
20
|
+
"4-6": { add: ["effort-2025-11-24"] },
|
|
21
|
+
"4-7": { add: ["effort-2025-11-24"] },
|
|
22
|
+
};
|
|
23
|
+
export function claudeModelOverride(modelId) {
|
|
24
|
+
const lower = modelId.toLowerCase();
|
|
25
|
+
for (const [pattern, override] of Object.entries(MODEL_OVERRIDES)) {
|
|
26
|
+
if (lower.includes(pattern)) {
|
|
27
|
+
return override;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type ClaudeCompatibilityHeaderOptions = {
|
|
2
|
+
readonly accessToken: string;
|
|
3
|
+
readonly modelId: string;
|
|
4
|
+
readonly version: string;
|
|
5
|
+
readonly incoming?: Headers;
|
|
6
|
+
};
|
|
7
|
+
export declare function createClaudeCompatibilityHeaders(options: ClaudeCompatibilityHeaderOptions): Headers;
|
|
8
|
+
export declare function createClaudeCompatibilityFetch(options: {
|
|
9
|
+
readonly readVersion: (signal: AbortSignal) => Promise<string | null>;
|
|
10
|
+
readonly readAccessToken: (signal: AbortSignal) => Promise<string | null>;
|
|
11
|
+
readonly forceRefreshAccessToken: (signal: AbortSignal) => Promise<string | null>;
|
|
12
|
+
}): (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
|