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,151 @@
|
|
|
1
|
+
// Derived from griffinmartin/opencode-claude-auth@0f0ff6f12c367339130cbfd250393863ed2c8d9e.
|
|
2
|
+
// Licensed under MIT. See THIRD_PARTY_NOTICES.md.
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
4
|
+
import { claudeModelBetas, excludeClaudeBeta, isClaudeLongContextError, nextClaudeBetaToExclude, } from "./compat-betas";
|
|
5
|
+
import { transformClaudeResponse } from "./compat-response";
|
|
6
|
+
import { transformClaudeBody } from "./compat-transform";
|
|
7
|
+
const sessionId = randomUUID();
|
|
8
|
+
function requestUrl(input) {
|
|
9
|
+
const raw = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
|
10
|
+
const url = new URL(raw);
|
|
11
|
+
if (url.pathname === "/v1/messages" && !url.searchParams.has("beta")) {
|
|
12
|
+
url.searchParams.set("beta", "true");
|
|
13
|
+
}
|
|
14
|
+
return typeof input === "string" ? url.toString() : url;
|
|
15
|
+
}
|
|
16
|
+
function modelFromBody(body) {
|
|
17
|
+
if (typeof body !== "string") {
|
|
18
|
+
return "unknown";
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
const parsed = JSON.parse(body);
|
|
22
|
+
if (typeof parsed === "object" && parsed !== null && "model" in parsed) {
|
|
23
|
+
const model = Reflect.get(parsed, "model");
|
|
24
|
+
return typeof model === "string" ? model : "unknown";
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
catch { }
|
|
28
|
+
return "unknown";
|
|
29
|
+
}
|
|
30
|
+
export function createClaudeCompatibilityHeaders(options) {
|
|
31
|
+
const headers = new Headers(options.incoming);
|
|
32
|
+
const incoming = (headers.get("anthropic-beta") ?? "")
|
|
33
|
+
.split(",")
|
|
34
|
+
.map((value) => value.trim())
|
|
35
|
+
.filter((value) => value.length > 0);
|
|
36
|
+
headers.set("authorization", `Bearer ${options.accessToken}`);
|
|
37
|
+
headers.set("anthropic-version", "2023-06-01");
|
|
38
|
+
headers.set("anthropic-beta", [...new Set([...claudeModelBetas(options.modelId), ...incoming])].join(","));
|
|
39
|
+
headers.set("anthropic-dangerous-direct-browser-access", "true");
|
|
40
|
+
headers.set("x-app", "cli");
|
|
41
|
+
headers.set("user-agent", process.env["ANTHROPIC_USER_AGENT"] ?? `claude-cli/${options.version} (external, sdk-cli)`);
|
|
42
|
+
headers.set("x-client-request-id", randomUUID());
|
|
43
|
+
headers.set("X-Claude-Code-Session-Id", sessionId);
|
|
44
|
+
const stainless = {
|
|
45
|
+
"x-stainless-arch": process.arch === "arm64" ? "arm64" : process.arch,
|
|
46
|
+
"x-stainless-lang": "js",
|
|
47
|
+
"x-stainless-os": process.platform === "darwin" ? "MacOS" : process.platform,
|
|
48
|
+
"x-stainless-package-version": "0.81.0",
|
|
49
|
+
"x-stainless-retry-count": "0",
|
|
50
|
+
"x-stainless-runtime": "node",
|
|
51
|
+
"x-stainless-runtime-version": process.version,
|
|
52
|
+
"x-stainless-timeout": "600",
|
|
53
|
+
};
|
|
54
|
+
for (const [key, value] of Object.entries(stainless)) {
|
|
55
|
+
if (!headers.has(key)) {
|
|
56
|
+
headers.set(key, value);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
headers.delete("x-api-key");
|
|
60
|
+
return headers;
|
|
61
|
+
}
|
|
62
|
+
function maxRetryDelayMs() {
|
|
63
|
+
const value = Number.parseInt(process.env["OPENCODE_CLAUDE_AUTH_MAX_RETRY_MS"] ?? "", 10);
|
|
64
|
+
return Number.isFinite(value) && value > 0 ? value : 30_000;
|
|
65
|
+
}
|
|
66
|
+
async function sleepUnlessAborted(ms, signal) {
|
|
67
|
+
await new Promise((resolve) => {
|
|
68
|
+
if (signal?.aborted === true) {
|
|
69
|
+
resolve();
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const finish = () => {
|
|
73
|
+
clearTimeout(timer);
|
|
74
|
+
signal?.removeEventListener("abort", finish);
|
|
75
|
+
resolve();
|
|
76
|
+
};
|
|
77
|
+
const timer = setTimeout(finish, ms);
|
|
78
|
+
signal?.addEventListener("abort", finish, { once: true });
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
async function fetchWithRetry(input, init) {
|
|
82
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
83
|
+
const response = await globalThis.fetch(input, init);
|
|
84
|
+
if ((response.status !== 429 && response.status !== 529) || attempt === 2) {
|
|
85
|
+
return response;
|
|
86
|
+
}
|
|
87
|
+
const retryAfter = Number.parseInt(response.headers.get("retry-after") ?? "", 10);
|
|
88
|
+
const delay = Number.isFinite(retryAfter) ? retryAfter * 1_000 : (attempt + 1) * 2_000;
|
|
89
|
+
if (delay > maxRetryDelayMs()) {
|
|
90
|
+
return response;
|
|
91
|
+
}
|
|
92
|
+
await sleepUnlessAborted(delay, init.signal);
|
|
93
|
+
if (init.signal?.aborted === true) {
|
|
94
|
+
return response;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return globalThis.fetch(input, init);
|
|
98
|
+
}
|
|
99
|
+
export function createClaudeCompatibilityFetch(options) {
|
|
100
|
+
return async (input, requestInit = {}) => {
|
|
101
|
+
const signal = requestInit.signal ?? new AbortController().signal;
|
|
102
|
+
let token = await options.readAccessToken(signal);
|
|
103
|
+
if (token === null) {
|
|
104
|
+
throw new Error("Claude Code credentials are unavailable or expired. Provide a valid `~/.claude/.credentials.json`.");
|
|
105
|
+
}
|
|
106
|
+
const version = await options.readVersion(signal);
|
|
107
|
+
if (version === null) {
|
|
108
|
+
throw new Error("Claude Code client version is unavailable. Set ANTHROPIC_CLI_VERSION or allow access to registry.npmjs.org.");
|
|
109
|
+
}
|
|
110
|
+
const modelId = modelFromBody(requestInit.body);
|
|
111
|
+
const body = transformClaudeBody(requestInit.body, version);
|
|
112
|
+
const url = requestUrl(input);
|
|
113
|
+
const send = (accessToken) => {
|
|
114
|
+
const incoming = new Headers(input instanceof Request ? input.headers : undefined);
|
|
115
|
+
new Headers(requestInit.headers).forEach((value, key) => {
|
|
116
|
+
incoming.set(key, value);
|
|
117
|
+
});
|
|
118
|
+
return fetchWithRetry(url, {
|
|
119
|
+
...requestInit,
|
|
120
|
+
body,
|
|
121
|
+
headers: createClaudeCompatibilityHeaders({
|
|
122
|
+
accessToken,
|
|
123
|
+
modelId,
|
|
124
|
+
version,
|
|
125
|
+
incoming,
|
|
126
|
+
}),
|
|
127
|
+
});
|
|
128
|
+
};
|
|
129
|
+
let response = await send(token);
|
|
130
|
+
if (response.status === 401) {
|
|
131
|
+
const refreshed = await options.forceRefreshAccessToken(signal);
|
|
132
|
+
if (refreshed !== null) {
|
|
133
|
+
token = refreshed;
|
|
134
|
+
response = await send(refreshed);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
for (let attempt = 0; !response.ok && attempt < 2; attempt += 1) {
|
|
138
|
+
const bodyText = await response.clone().text();
|
|
139
|
+
if (!isClaudeLongContextError(bodyText)) {
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
const excluded = nextClaudeBetaToExclude(modelId);
|
|
143
|
+
if (excluded === null) {
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
excludeClaudeBeta(modelId, excluded);
|
|
147
|
+
response = await send(token);
|
|
148
|
+
}
|
|
149
|
+
return transformClaudeResponse(response);
|
|
150
|
+
};
|
|
151
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function transformClaudeResponse(response: Response): Response;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// Derived from griffinmartin/opencode-claude-auth@0f0ff6f12c367339130cbfd250393863ed2c8d9e.
|
|
2
|
+
// Licensed under MIT. See THIRD_PARTY_NOTICES.md.
|
|
3
|
+
import { stripClaudeToolPrefix } from "./compat-transform";
|
|
4
|
+
export function transformClaudeResponse(response) {
|
|
5
|
+
if (response.body === null) {
|
|
6
|
+
return response;
|
|
7
|
+
}
|
|
8
|
+
const reader = response.body.getReader();
|
|
9
|
+
const decoder = new TextDecoder();
|
|
10
|
+
const encoder = new TextEncoder();
|
|
11
|
+
let buffer = "";
|
|
12
|
+
const stream = new ReadableStream({
|
|
13
|
+
async pull(controller) {
|
|
14
|
+
if (!response.ok) {
|
|
15
|
+
const next = await reader.read();
|
|
16
|
+
if (next.done) {
|
|
17
|
+
controller.close();
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
controller.enqueue(encoder.encode(stripClaudeToolPrefix(decoder.decode(next.value))));
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
for (;;) {
|
|
24
|
+
const boundary = buffer.indexOf("\n\n");
|
|
25
|
+
if (boundary !== -1) {
|
|
26
|
+
const event = buffer.slice(0, boundary + 2);
|
|
27
|
+
buffer = buffer.slice(boundary + 2);
|
|
28
|
+
controller.enqueue(encoder.encode(stripClaudeToolPrefix(event)));
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const next = await reader.read();
|
|
32
|
+
if (next.done) {
|
|
33
|
+
if (buffer.length > 0) {
|
|
34
|
+
controller.enqueue(encoder.encode(stripClaudeToolPrefix(buffer)));
|
|
35
|
+
buffer = "";
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
controller.close();
|
|
39
|
+
}
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
buffer += decoder.decode(next.value, { stream: true });
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
async cancel(reason) {
|
|
46
|
+
await reader.cancel(reason);
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
return new Response(stream, {
|
|
50
|
+
status: response.status,
|
|
51
|
+
statusText: response.statusText,
|
|
52
|
+
headers: response.headers,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
type SigningMessage = {
|
|
2
|
+
readonly role?: string;
|
|
3
|
+
readonly content?: string | readonly {
|
|
4
|
+
readonly type?: string;
|
|
5
|
+
readonly text?: string;
|
|
6
|
+
}[];
|
|
7
|
+
};
|
|
8
|
+
export declare function claudeBillingHeader(messages: readonly SigningMessage[], version: string, entrypoint: string): string;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Derived from griffinmartin/opencode-claude-auth@0f0ff6f12c367339130cbfd250393863ed2c8d9e.
|
|
2
|
+
// Licensed under MIT. See THIRD_PARTY_NOTICES.md.
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
const BILLING_SALT = "59cf53e54c78";
|
|
5
|
+
function firstUserText(messages) {
|
|
6
|
+
const message = messages.find((candidate) => candidate.role === "user");
|
|
7
|
+
if (message === undefined) {
|
|
8
|
+
return "";
|
|
9
|
+
}
|
|
10
|
+
if (typeof message.content === "string") {
|
|
11
|
+
return message.content;
|
|
12
|
+
}
|
|
13
|
+
return message.content?.find((block) => block.type === "text")?.text ?? "";
|
|
14
|
+
}
|
|
15
|
+
function hashPrefix(value, length) {
|
|
16
|
+
return createHash("sha256").update(value).digest("hex").slice(0, length);
|
|
17
|
+
}
|
|
18
|
+
export function claudeBillingHeader(messages, version, entrypoint) {
|
|
19
|
+
const text = firstUserText(messages);
|
|
20
|
+
const sampled = [4, 7, 20].map((index) => text[index] ?? "0").join("");
|
|
21
|
+
const suffix = hashPrefix(`${BILLING_SALT}${sampled}${version}`, 3);
|
|
22
|
+
const cch = hashPrefix(text, 5);
|
|
23
|
+
return `x-anthropic-billing-header: cc_version=${version}.${suffix}; cc_entrypoint=${entrypoint}; cch=${cch};`;
|
|
24
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare const CLAUDE_SYSTEM_IDENTITY = "You are Claude Code, Anthropic's official CLI for Claude.";
|
|
2
|
+
export declare function transformClaudeBody(body: RequestInit["body"], version: string): RequestInit["body"];
|
|
3
|
+
export declare function stripClaudeToolPrefix(text: string): string;
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
// Derived from griffinmartin/opencode-claude-auth@0f0ff6f12c367339130cbfd250393863ed2c8d9e.
|
|
2
|
+
// Licensed under MIT. See THIRD_PARTY_NOTICES.md.
|
|
3
|
+
import { claudeModelOverride } from "./compat-config";
|
|
4
|
+
import { claudeBillingHeader } from "./compat-signing";
|
|
5
|
+
export const CLAUDE_SYSTEM_IDENTITY = "You are Claude Code, Anthropic's official CLI for Claude.";
|
|
6
|
+
const BILLING_PREFIX = "x-anthropic-billing-header";
|
|
7
|
+
const TOOL_PREFIX = "mcp_";
|
|
8
|
+
const TOOL_RESULT_PLACEHOLDER = "Tool result unavailable (removed during context compaction).";
|
|
9
|
+
function record(value) {
|
|
10
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
const result = {};
|
|
14
|
+
for (const key of Object.keys(value)) {
|
|
15
|
+
result[key] = Reflect.get(value, key);
|
|
16
|
+
}
|
|
17
|
+
return result;
|
|
18
|
+
}
|
|
19
|
+
function stringField(value, key) {
|
|
20
|
+
const field = value[key];
|
|
21
|
+
return typeof field === "string" ? field : undefined;
|
|
22
|
+
}
|
|
23
|
+
function prefixToolName(name) {
|
|
24
|
+
return `${TOOL_PREFIX}${name.charAt(0).toUpperCase()}${name.slice(1)}`;
|
|
25
|
+
}
|
|
26
|
+
function contentRecords(message) {
|
|
27
|
+
const content = message["content"];
|
|
28
|
+
if (!Array.isArray(content)) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
return content.map(record).filter((part) => part !== null);
|
|
32
|
+
}
|
|
33
|
+
function toolIds(message, type) {
|
|
34
|
+
if (message === undefined) {
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
37
|
+
const key = type === "tool_use" ? "id" : "tool_use_id";
|
|
38
|
+
return (contentRecords(message) ?? [])
|
|
39
|
+
.filter((part) => part["type"] === type)
|
|
40
|
+
.map((part) => stringField(part, key))
|
|
41
|
+
.filter((id) => id !== undefined);
|
|
42
|
+
}
|
|
43
|
+
function repairToolPairs(messages) {
|
|
44
|
+
const withoutOrphanResults = [];
|
|
45
|
+
for (let index = 0; index < messages.length; index += 1) {
|
|
46
|
+
const message = messages[index];
|
|
47
|
+
if (message === undefined) {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const content = contentRecords(message);
|
|
51
|
+
if (content === null) {
|
|
52
|
+
withoutOrphanResults.push(message);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
const previousUses = new Set(toolIds(messages[index - 1], "tool_use"));
|
|
56
|
+
const filtered = content.filter((part) => {
|
|
57
|
+
const resultId = part["type"] === "tool_result" ? stringField(part, "tool_use_id") : undefined;
|
|
58
|
+
return resultId === undefined || previousUses.has(resultId);
|
|
59
|
+
});
|
|
60
|
+
if (filtered.length > 0 || content.length === 0) {
|
|
61
|
+
withoutOrphanResults.push({ ...message, content: filtered });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const output = [];
|
|
65
|
+
for (let index = 0; index < withoutOrphanResults.length; index += 1) {
|
|
66
|
+
const message = withoutOrphanResults[index];
|
|
67
|
+
if (message === undefined) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
output.push(message);
|
|
71
|
+
const uses = toolIds(message, "tool_use");
|
|
72
|
+
if (uses.length === 0) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const next = withoutOrphanResults[index + 1];
|
|
76
|
+
const results = new Set(toolIds(next, "tool_result"));
|
|
77
|
+
const missing = uses.filter((id) => !results.has(id));
|
|
78
|
+
if (missing.length === 0) {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
const synthetic = missing.map((id) => ({
|
|
82
|
+
type: "tool_result",
|
|
83
|
+
tool_use_id: id,
|
|
84
|
+
content: TOOL_RESULT_PLACEHOLDER,
|
|
85
|
+
is_error: true,
|
|
86
|
+
}));
|
|
87
|
+
if (next?.["role"] === "user") {
|
|
88
|
+
const nextContent = next["content"];
|
|
89
|
+
const merged = typeof nextContent === "string"
|
|
90
|
+
? [...synthetic, ...(nextContent.length > 0 ? [{ type: "text", text: nextContent }] : [])]
|
|
91
|
+
: [...synthetic, ...(contentRecords(next) ?? [])];
|
|
92
|
+
output.push({ ...next, content: merged });
|
|
93
|
+
index += 1;
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
output.push({ role: "user", content: synthetic });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return output;
|
|
100
|
+
}
|
|
101
|
+
function transformTools(body) {
|
|
102
|
+
const tools = body["tools"];
|
|
103
|
+
if (Array.isArray(tools)) {
|
|
104
|
+
body["tools"] = tools.map((value) => {
|
|
105
|
+
const tool = record(value);
|
|
106
|
+
if (tool === null) {
|
|
107
|
+
return value;
|
|
108
|
+
}
|
|
109
|
+
const name = stringField(tool, "name");
|
|
110
|
+
return name === undefined ? tool : { ...tool, name: prefixToolName(name) };
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function transformMessages(body) {
|
|
115
|
+
const raw = body["messages"];
|
|
116
|
+
if (!Array.isArray(raw)) {
|
|
117
|
+
return [];
|
|
118
|
+
}
|
|
119
|
+
const messages = raw
|
|
120
|
+
.map(record)
|
|
121
|
+
.filter((value) => value !== null);
|
|
122
|
+
const prefixed = messages.map((message) => {
|
|
123
|
+
const content = contentRecords(message);
|
|
124
|
+
if (content === null) {
|
|
125
|
+
return message;
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
...message,
|
|
129
|
+
content: content.map((part) => {
|
|
130
|
+
const name = part["type"] === "tool_use" ? stringField(part, "name") : undefined;
|
|
131
|
+
return name === undefined ? part : { ...part, name: prefixToolName(name) };
|
|
132
|
+
}),
|
|
133
|
+
};
|
|
134
|
+
});
|
|
135
|
+
return repairToolPairs(prefixed);
|
|
136
|
+
}
|
|
137
|
+
function transformSystem(body, messages, version) {
|
|
138
|
+
const system = body["system"];
|
|
139
|
+
const entries = Array.isArray(system)
|
|
140
|
+
? system.map(record).filter((value) => value !== null)
|
|
141
|
+
: typeof system === "string"
|
|
142
|
+
? [{ type: "text", text: system }]
|
|
143
|
+
: [];
|
|
144
|
+
const billing = claudeBillingHeader(messages, version, process.env["CLAUDE_CODE_ENTRYPOINT"] ?? "sdk-cli");
|
|
145
|
+
const moved = [];
|
|
146
|
+
const kept = [{ type: "text", text: billing }];
|
|
147
|
+
let hasIdentity = false;
|
|
148
|
+
for (const entry of entries) {
|
|
149
|
+
const text = stringField(entry, "text") ?? "";
|
|
150
|
+
if (text.startsWith(BILLING_PREFIX)) {
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (text.startsWith(CLAUDE_SYSTEM_IDENTITY)) {
|
|
154
|
+
kept.push({ type: "text", text: CLAUDE_SYSTEM_IDENTITY });
|
|
155
|
+
hasIdentity = true;
|
|
156
|
+
const remainder = text.slice(CLAUDE_SYSTEM_IDENTITY.length).replace(/^\n+/, "");
|
|
157
|
+
if (remainder.length > 0) {
|
|
158
|
+
moved.push(remainder);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
else if (text.length > 0) {
|
|
162
|
+
moved.push(text);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (!hasIdentity) {
|
|
166
|
+
kept.push({ type: "text", text: CLAUDE_SYSTEM_IDENTITY });
|
|
167
|
+
}
|
|
168
|
+
if (moved.length > 0) {
|
|
169
|
+
const firstUser = messages.find((message) => message["role"] === "user");
|
|
170
|
+
if (firstUser !== undefined) {
|
|
171
|
+
const prefix = moved.join("\n\n");
|
|
172
|
+
const content = firstUser["content"];
|
|
173
|
+
firstUser["content"] =
|
|
174
|
+
typeof content === "string"
|
|
175
|
+
? `${prefix}\n\n${content}`
|
|
176
|
+
: [{ type: "text", text: prefix }, ...(contentRecords(firstUser) ?? [])];
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
body["system"] = kept;
|
|
180
|
+
}
|
|
181
|
+
function stripUnsupportedEffort(body) {
|
|
182
|
+
const model = typeof body["model"] === "string" ? body["model"] : "";
|
|
183
|
+
if (claudeModelOverride(model)?.disableEffort !== true) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
for (const key of ["output_config", "thinking"]) {
|
|
187
|
+
const value = record(body[key]);
|
|
188
|
+
if (value === null) {
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
delete value["effort"];
|
|
192
|
+
if (Object.keys(value).length === 0) {
|
|
193
|
+
delete body[key];
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
body[key] = value;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
export function transformClaudeBody(body, version) {
|
|
201
|
+
if (typeof body !== "string") {
|
|
202
|
+
return body;
|
|
203
|
+
}
|
|
204
|
+
try {
|
|
205
|
+
const parsed = record(JSON.parse(body));
|
|
206
|
+
if (parsed === null) {
|
|
207
|
+
return body;
|
|
208
|
+
}
|
|
209
|
+
const messages = transformMessages(parsed);
|
|
210
|
+
parsed["messages"] = messages;
|
|
211
|
+
transformSystem(parsed, messages, version);
|
|
212
|
+
transformTools(parsed);
|
|
213
|
+
stripUnsupportedEffort(parsed);
|
|
214
|
+
return JSON.stringify(parsed);
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
return body;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
export function stripClaudeToolPrefix(text) {
|
|
221
|
+
return text.replace(/"name"\s*:\s*"mcp_([^"]+)"/g, (_match, name) => {
|
|
222
|
+
return `"name": "${name.charAt(0).toLowerCase()}${name.slice(1)}"`;
|
|
223
|
+
});
|
|
224
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type ClaudeCredentials = {
|
|
2
|
+
readonly accessToken: string;
|
|
3
|
+
readonly refreshToken: string | null;
|
|
4
|
+
readonly expiresAtMs: number | null;
|
|
5
|
+
};
|
|
6
|
+
export declare function parseClaudeCredentials(value: unknown): ClaudeCredentials | null;
|
|
7
|
+
export declare function claudeAccessNeedsRefresh(credentials: ClaudeCredentials, nowMs: number, skewMs?: number): boolean;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
function stringField(value, key) {
|
|
2
|
+
if (!(key in value)) {
|
|
3
|
+
return null;
|
|
4
|
+
}
|
|
5
|
+
const field = Reflect.get(value, key);
|
|
6
|
+
return typeof field === "string" && field.length > 0 ? field : null;
|
|
7
|
+
}
|
|
8
|
+
function numberField(value, key) {
|
|
9
|
+
if (!(key in value)) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
const field = Reflect.get(value, key);
|
|
13
|
+
return typeof field === "number" && Number.isFinite(field) ? Math.trunc(field) : null;
|
|
14
|
+
}
|
|
15
|
+
export function parseClaudeCredentials(value) {
|
|
16
|
+
if (typeof value !== "object" || value === null) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
const oauth = "claudeAiOauth" in value ? Reflect.get(value, "claudeAiOauth") : value;
|
|
20
|
+
if (typeof oauth !== "object" || oauth === null) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
const accessToken = stringField(oauth, "accessToken");
|
|
24
|
+
const refreshToken = stringField(oauth, "refreshToken");
|
|
25
|
+
const expiresAtMs = numberField(oauth, "expiresAt");
|
|
26
|
+
if (accessToken === null || refreshToken === null || expiresAtMs === null) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
accessToken,
|
|
31
|
+
refreshToken,
|
|
32
|
+
expiresAtMs,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export function claudeAccessNeedsRefresh(credentials, nowMs, skewMs = 60_000) {
|
|
36
|
+
return credentials.expiresAtMs !== null && credentials.expiresAtMs - skewMs <= nowMs;
|
|
37
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Derived from griffinmartin/opencode-claude-auth@0f0ff6f12c367339130cbfd250393863ed2c8d9e.
|
|
2
|
+
// Licensed under MIT. See THIRD_PARTY_NOTICES.md.
|
|
3
|
+
import { createSseParseState, mapStopReason, parseAnthropicSse } from "./sse";
|
|
4
|
+
export async function emitClaudeSseChunks(chunks, controller) {
|
|
5
|
+
controller.enqueue({ type: "stream-start", warnings: [] });
|
|
6
|
+
let finishReason = { unified: "stop", raw: "end_turn" };
|
|
7
|
+
let textBlockIndex = 0;
|
|
8
|
+
let toolBlockIndex = 0;
|
|
9
|
+
let state = createSseParseState();
|
|
10
|
+
const emit = (chunk) => {
|
|
11
|
+
const parsed = parseAnthropicSse(chunk, state);
|
|
12
|
+
state = parsed.state;
|
|
13
|
+
for (const event of parsed.events) {
|
|
14
|
+
if (event.kind === "part") {
|
|
15
|
+
const part = event.part;
|
|
16
|
+
if (part.type === "text-start") {
|
|
17
|
+
textBlockIndex += 1;
|
|
18
|
+
controller.enqueue({ ...part, id: `text-${textBlockIndex}` });
|
|
19
|
+
}
|
|
20
|
+
else if (part.type === "text-delta" || part.type === "text-end") {
|
|
21
|
+
controller.enqueue({ ...part, id: `text-${textBlockIndex}` });
|
|
22
|
+
}
|
|
23
|
+
else if (part.type === "tool-input-start") {
|
|
24
|
+
toolBlockIndex += 1;
|
|
25
|
+
controller.enqueue({ ...part, id: part.id });
|
|
26
|
+
}
|
|
27
|
+
else if (part.type === "tool-input-delta" || part.type === "tool-input-end") {
|
|
28
|
+
controller.enqueue({ ...part, id: `tool-${toolBlockIndex}` });
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
controller.enqueue(part);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
else if (event.kind === "finish") {
|
|
35
|
+
finishReason = mapStopReason(event.stopReason);
|
|
36
|
+
}
|
|
37
|
+
else if (event.kind === "error") {
|
|
38
|
+
controller.error(event.error);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
for await (const chunk of chunks) {
|
|
44
|
+
emit(chunk);
|
|
45
|
+
}
|
|
46
|
+
if (state.buffer.length > 0 || state.event !== null) {
|
|
47
|
+
emit(new TextEncoder().encode("\n"));
|
|
48
|
+
}
|
|
49
|
+
controller.enqueue({
|
|
50
|
+
type: "finish",
|
|
51
|
+
finishReason,
|
|
52
|
+
usage: {
|
|
53
|
+
inputTokens: {
|
|
54
|
+
total: undefined,
|
|
55
|
+
noCache: undefined,
|
|
56
|
+
cacheRead: undefined,
|
|
57
|
+
cacheWrite: undefined,
|
|
58
|
+
},
|
|
59
|
+
outputTokens: { total: undefined, text: undefined, reasoning: undefined },
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function parseKeychainAccount(dump: string): string | null;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const ACCT_MARKER = '"acct"<blob>="';
|
|
2
|
+
export function parseKeychainAccount(dump) {
|
|
3
|
+
const start = dump.indexOf(ACCT_MARKER);
|
|
4
|
+
if (start === -1) {
|
|
5
|
+
return null;
|
|
6
|
+
}
|
|
7
|
+
const valueStart = start + ACCT_MARKER.length;
|
|
8
|
+
const end = dump.indexOf('"', valueStart);
|
|
9
|
+
if (end === -1) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
const account = dump.slice(valueStart, end);
|
|
13
|
+
return account.length > 0 ? account : null;
|
|
14
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { LanguageModelV3 } from "@ai-sdk/provider";
|
|
2
|
+
import type { HttpTransport } from "../../core/http";
|
|
3
|
+
export type ClaudeLanguageModelOptions = {
|
|
4
|
+
readonly modelId: string;
|
|
5
|
+
readonly transport: HttpTransport;
|
|
6
|
+
readonly readAccessToken: (signal: AbortSignal) => Promise<string | null>;
|
|
7
|
+
};
|
|
8
|
+
export declare function createClaudeLanguageModel(options: ClaudeLanguageModelOptions): LanguageModelV3;
|