opencode-fixes-huihui 0.1.4-beta.2 → 0.1.4-beta.3
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"combined-plugin.d.ts","sourceRoot":"","sources":["../src/combined-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAS,MAAM,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"combined-plugin.d.ts","sourceRoot":"","sources":["../src/combined-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAS,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAkBzD,eAAO,MAAM,cAAc,EAAE,MA4E5B,CAAC;AAEF,eAAe,cAAc,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,51 @@
|
|
|
1
|
+
// src/logger.ts
|
|
2
|
+
var ENV_DEBUG = "OPENCODE_ANTHROPIC_CACHE_DEBUG";
|
|
3
|
+
var SERVICE_PREFIX = "anthropic-cache";
|
|
4
|
+
var _client = null;
|
|
5
|
+
function isDebugEnabled() {
|
|
6
|
+
const val = process.env[ENV_DEBUG];
|
|
7
|
+
return val === "1" || val?.toLowerCase() === "true";
|
|
8
|
+
}
|
|
9
|
+
function initLogger(client) {
|
|
10
|
+
_client = client;
|
|
11
|
+
}
|
|
12
|
+
function createLogger(module) {
|
|
13
|
+
const service = `${SERVICE_PREFIX}.${module}`;
|
|
14
|
+
const log2 = (level, message, extra) => {
|
|
15
|
+
if (_client?.app && typeof _client.app.log === "function") {
|
|
16
|
+
_client.app.log({ body: { service, level, message, extra } }).catch(() => {
|
|
17
|
+
});
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (isDebugEnabled()) {
|
|
21
|
+
const prefix = `[${service}]`;
|
|
22
|
+
const args = extra ? [prefix, message, extra] : [prefix, message];
|
|
23
|
+
switch (level) {
|
|
24
|
+
case "debug":
|
|
25
|
+
console.debug(...args);
|
|
26
|
+
break;
|
|
27
|
+
case "info":
|
|
28
|
+
console.info(...args);
|
|
29
|
+
break;
|
|
30
|
+
case "warn":
|
|
31
|
+
console.warn(...args);
|
|
32
|
+
break;
|
|
33
|
+
case "error":
|
|
34
|
+
console.error(...args);
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
return {
|
|
40
|
+
debug: (message, extra) => log2("debug", message, extra),
|
|
41
|
+
info: (message, extra) => log2("info", message, extra),
|
|
42
|
+
warn: (message, extra) => log2("warn", message, extra),
|
|
43
|
+
error: (message, extra) => log2("error", message, extra)
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
1
47
|
// src/opencode-anthropic-cache.ts
|
|
48
|
+
var log = createLogger("fetch");
|
|
2
49
|
var currentSessionId = "";
|
|
3
50
|
var projectId = "";
|
|
4
51
|
var isInitialized = false;
|
|
@@ -17,7 +64,7 @@ function parseBody(body) {
|
|
|
17
64
|
if (body instanceof Uint8Array) return new TextDecoder().decode(body);
|
|
18
65
|
return null;
|
|
19
66
|
}
|
|
20
|
-
function createCacheFetch() {
|
|
67
|
+
function createCacheFetch(providerName) {
|
|
21
68
|
return async (url, init) => {
|
|
22
69
|
const headers = new Headers(init?.headers);
|
|
23
70
|
const method = (init?.method || "GET").toUpperCase();
|
|
@@ -30,6 +77,7 @@ function createCacheFetch() {
|
|
|
30
77
|
try {
|
|
31
78
|
payload = JSON.parse(rawBody);
|
|
32
79
|
} catch {
|
|
80
|
+
log.warn(`[${providerName}] Failed to parse request body as JSON`);
|
|
33
81
|
return fetch(url, init);
|
|
34
82
|
}
|
|
35
83
|
if (payload && typeof payload === "object") {
|
|
@@ -39,6 +87,9 @@ function createCacheFetch() {
|
|
|
39
87
|
const meta = payload.metadata;
|
|
40
88
|
if (meta.user_id == null) {
|
|
41
89
|
meta.user_id = buildUserId();
|
|
90
|
+
log.info(`[${providerName}] Injected user_id`, {
|
|
91
|
+
user_id: meta.user_id
|
|
92
|
+
});
|
|
42
93
|
}
|
|
43
94
|
}
|
|
44
95
|
headers.delete("content-length");
|
|
@@ -50,6 +101,7 @@ function createEventHandler() {
|
|
|
50
101
|
if (event.type === "session.created" || event.type === "session.updated") {
|
|
51
102
|
const props = event.properties;
|
|
52
103
|
currentSessionId = props?.info?.id || "";
|
|
104
|
+
log.debug("Session updated", { sessionId: currentSessionId });
|
|
53
105
|
}
|
|
54
106
|
};
|
|
55
107
|
}
|
|
@@ -58,14 +110,16 @@ function createProviderPlugin(providerName) {
|
|
|
58
110
|
if (!isInitialized) {
|
|
59
111
|
projectId = project?.id || "";
|
|
60
112
|
isInitialized = true;
|
|
113
|
+
log.info("Plugin initialized", { projectId });
|
|
61
114
|
}
|
|
115
|
+
log.info(`Auth handler registered for provider: ${providerName}`);
|
|
62
116
|
return {
|
|
63
117
|
event: createEventHandler(),
|
|
64
118
|
auth: {
|
|
65
119
|
provider: providerName,
|
|
66
120
|
methods: [{ type: "api", label: "key" }],
|
|
67
121
|
loader: async () => ({
|
|
68
|
-
fetch: createCacheFetch()
|
|
122
|
+
fetch: createCacheFetch(providerName)
|
|
69
123
|
})
|
|
70
124
|
}
|
|
71
125
|
};
|
|
@@ -144,7 +198,12 @@ var mergeHook = (first, second) => {
|
|
|
144
198
|
await second(...args);
|
|
145
199
|
};
|
|
146
200
|
};
|
|
201
|
+
var isInitialized2 = false;
|
|
147
202
|
var CombinedPlugin = async (input) => {
|
|
203
|
+
if (!isInitialized2) {
|
|
204
|
+
initLogger(input.client);
|
|
205
|
+
isInitialized2 = true;
|
|
206
|
+
}
|
|
148
207
|
const stickyHooks = await StickySessionPlugin(input);
|
|
149
208
|
const anthropicHooks = await opencode_anthropic_cache_default(input);
|
|
150
209
|
const merged = {
|
package/dist/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/opencode-anthropic-cache.ts", "../src/sticky-session-plugin.ts", "../src/combined-plugin.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Hooks, Plugin } from \"@opencode-ai/plugin\";\n\n// Session and project tracking\nlet currentSessionId = \"\";\nlet projectId = \"\";\nlet isInitialized = false;\nfunction buildUserId() {\n const pid = projectId || \"unknown\";\n const sid = currentSessionId || \"unknown\";\n return `user_${pid}_account__session_${sid}`;\n}\nfunction isJsonContentType(headers: Headers) {\n const ct = headers.get(\"content-type\") || \"\";\n return ct.includes(\"application/json\");\n}\nfunction parseBody(body?: unknown) {\n if (!body) return null;\n if (typeof body === \"string\") return body;\n if (body instanceof Uint8Array) return new TextDecoder().decode(body);\n return null;\n}\ntype CachePayload = {\n metadata?: {\n user_id?: string;\n [key: string]: unknown;\n };\n [key: string]: unknown;\n};\n\n// Create the fetch interceptor that injects metadata.user_id\nfunction createCacheFetch() {\n return async (url: string, init?: RequestInit) => {\n const headers = new Headers(init?.headers);\n const method = (init?.method || \"GET\").toUpperCase();\n if (method !== \"POST\" || !isJsonContentType(headers)) {\n return fetch(url, init);\n }\n const rawBody = parseBody(init?.body);\n if (!rawBody) return fetch(url, init);\n let payload: CachePayload;\n try {\n payload = JSON.parse(rawBody) as CachePayload;\n } catch {\n return fetch(url, init);\n }\n if (payload && typeof payload === \"object\") {\n if (!payload.metadata || typeof payload.metadata !== \"object\") {\n payload.metadata = {};\n }\n const meta = payload.metadata;\n if (meta.user_id == null) {\n meta.user_id = buildUserId();\n }\n }\n headers.delete(\"content-length\");\n return fetch(url, { ...init, headers, body: JSON.stringify(payload) });\n };\n}\n// Shared event handler\nfunction createEventHandler(): Hooks[\"event\"] {\n return async ({ event }) => {\n if (event.type === \"session.created\" || event.type === \"session.updated\") {\n const props = event.properties;\n currentSessionId = props?.info?.id || \"\";\n }\n };\n}\n// Factory to create plugin for a specific provider\nfunction createProviderPlugin(providerName: string): Plugin {\n return async ({ project }) => {\n if (!isInitialized) {\n projectId = project?.id || \"\";\n isInitialized = true;\n }\n return {\n event: createEventHandler(),\n auth: {\n provider: providerName,\n methods: [{ type: \"api\", label: \"key\" }],\n loader: async () => ({\n fetch: createCacheFetch(),\n }),\n },\n };\n };\n}\n// Pre-built plugins for common providers\nexport const FoxcodeAwsCache = createProviderPlugin(\"foxcode-aws\");\nexport const AnthropicCache = createProviderPlugin(\"anthropic\");\nexport const AnthropicUltraCache = createProviderPlugin(\"anthropic-ultra\");\n// Generic factory for custom provider names\nexport const createAnthropicCachePlugin = createProviderPlugin;\nexport default AnthropicCache;\n", "import type { Plugin } from \"@opencode-ai/plugin\";\n\nconst OPENAI_PROVIDER_NPM = \"@ai-sdk/openai\";\nconst SESSION_HEADER_KEYS = [\n \"x-session-id\",\n \"conversation_id\",\n \"session_id\",\n] as const;\n\ntype HeaderMap = Record<string, unknown>;\n\nconst normalize = (value: unknown): string =>\n typeof value === \"string\" ? value.trim() : \"\";\n\nconst pickHeaderSessionValue = (headers: HeaderMap): string => {\n for (const key of SESSION_HEADER_KEYS) {\n const value = normalize(headers[key]);\n if (value) return value;\n }\n return \"\";\n};\n\nconst ensureHeaders = (model: { headers?: unknown }): HeaderMap => {\n if (model.headers && typeof model.headers === \"object\") {\n return model.headers as HeaderMap;\n }\n const headers: HeaderMap = {};\n model.headers = headers;\n return headers;\n};\n\n/**\n * set a session-scoped prompt cache key and session-scoped sticky routing headers for right.codes.\n *\n * Injects HTTP headers via `input.model.headers` so upstream proxies/load\n * balancers can keep a conversation pinned to one account.\n *\n * Headers injected for @ai-sdk/openai providers:\n * - x-session-id\n * - conversation_id\n * - session_id\n *\n * Source of truth (in order):\n * - env: OPENCODE_PROMPT_CACHE_KEY (manual override)\n * - env: OPENCODE_STICKY_SESSION_ID (manual override)\n * - model headers (x-session-id / conversation_id / session_id)\n * - opencode sessionID (default)\n */\nexport const StickySessionPlugin: Plugin = async ({ client }) => {\n const envPromptCacheKey = normalize(process.env.OPENCODE_PROMPT_CACHE_KEY);\n const envStickySessionID = normalize(process.env.OPENCODE_STICKY_SESSION_ID);\n const envOverride = envPromptCacheKey || envStickySessionID;\n const isProviderCodexMap: Record<string, boolean> = {};\n let inited = false;\n const init = async () => {\n if (inited) return;\n const resp = await client.config.providers();\n const providers = resp.data?.providers ?? [];\n for (const provider of providers) {\n isProviderCodexMap[provider.id] = Boolean(provider.options?.isCodex);\n }\n inited = true;\n };\n\n return {\n \"chat.params\": async (input, output) => {\n if (!inited) await init();\n const providerNpm = normalize(input.model.api.npm);\n const isCodex = isProviderCodexMap[input.model.providerID] ?? false;\n if (!providerNpm.includes(OPENAI_PROVIDER_NPM) && !isCodex) return;\n\n const headers = ensureHeaders(input.model);\n const sessionValue =\n envOverride ||\n pickHeaderSessionValue(headers) ||\n normalize(input.sessionID);\n if (!sessionValue) return;\n\n for (const key of SESSION_HEADER_KEYS) {\n headers[key] = sessionValue;\n }\n\n output.options.promptCacheKey = sessionValue;\n if (isCodex && !output.options.instructions) {\n output.options.instructions = \"\";\n }\n },\n };\n};\n\nexport default StickySessionPlugin;\n", "import type { Hooks, Plugin } from \"@opencode-ai/plugin\";\nimport AnthropicCachePlugin from \"./opencode-anthropic-cache\";\nimport { StickySessionPlugin } from \"./sticky-session-plugin\";\n\nconst mergeHook = <TArgs extends unknown[]>(\n first?: (...args: TArgs) => Promise<void>,\n second?: (...args: TArgs) => Promise<void>,\n): ((...args: TArgs) => Promise<void>) | undefined => {\n if (!first) return second;\n if (!second) return first;\n return async (...args: TArgs) => {\n await first(...args);\n await second(...args);\n };\n};\n\nexport const CombinedPlugin: Plugin = async (input) => {\n const stickyHooks = await StickySessionPlugin(input);\n const anthropicHooks = await AnthropicCachePlugin(input);\n\n const merged: Hooks = {\n ...anthropicHooks,\n ...stickyHooks,\n tool: {\n ...(anthropicHooks.tool ?? {}),\n ...(stickyHooks.tool ?? {}),\n },\n \"chat.message\": mergeHook(\n anthropicHooks[\"chat.message\"],\n stickyHooks[\"chat.message\"],\n ),\n \"chat.params\": mergeHook(\n anthropicHooks[\"chat.params\"],\n stickyHooks[\"chat.params\"],\n ),\n \"chat.headers\": mergeHook(\n anthropicHooks[\"chat.headers\"],\n stickyHooks[\"chat.headers\"],\n ),\n \"permission.ask\": mergeHook(\n anthropicHooks[\"permission.ask\"],\n stickyHooks[\"permission.ask\"],\n ),\n \"command.execute.before\": mergeHook(\n anthropicHooks[\"command.execute.before\"],\n stickyHooks[\"command.execute.before\"],\n ),\n \"tool.execute.before\": mergeHook(\n anthropicHooks[\"tool.execute.before\"],\n stickyHooks[\"tool.execute.before\"],\n ),\n \"shell.env\": mergeHook(\n anthropicHooks[\"shell.env\"],\n stickyHooks[\"shell.env\"],\n ),\n \"tool.execute.after\": mergeHook(\n anthropicHooks[\"tool.execute.after\"],\n stickyHooks[\"tool.execute.after\"],\n ),\n \"experimental.chat.messages.transform\": mergeHook(\n anthropicHooks[\"experimental.chat.messages.transform\"],\n stickyHooks[\"experimental.chat.messages.transform\"],\n ),\n \"experimental.chat.system.transform\": mergeHook(\n anthropicHooks[\"experimental.chat.system.transform\"],\n stickyHooks[\"experimental.chat.system.transform\"],\n ),\n \"experimental.session.compacting\": mergeHook(\n anthropicHooks[\"experimental.session.compacting\"],\n stickyHooks[\"experimental.session.compacting\"],\n ),\n \"experimental.text.complete\": mergeHook(\n anthropicHooks[\"experimental.text.complete\"],\n stickyHooks[\"experimental.text.complete\"],\n ),\n \"tool.definition\": mergeHook(\n anthropicHooks[\"tool.definition\"],\n stickyHooks[\"tool.definition\"],\n ),\n event: mergeHook(anthropicHooks.event, stickyHooks.event),\n config: mergeHook(anthropicHooks.config, stickyHooks.config),\n };\n\n if (merged.auth === undefined) {\n merged.auth = anthropicHooks.auth ?? stickyHooks.auth;\n }\n\n return merged;\n};\n\nexport default CombinedPlugin;\n"],
|
|
5
|
-
"mappings": ";
|
|
6
|
-
"names": []
|
|
3
|
+
"sources": ["../src/logger.ts", "../src/opencode-anthropic-cache.ts", "../src/sticky-session-plugin.ts", "../src/combined-plugin.ts"],
|
|
4
|
+
"sourcesContent": ["import type { PluginInput } from \"@opencode-ai/plugin\";\n// Logger module for debugging\nconst ENV_DEBUG = \"OPENCODE_ANTHROPIC_CACHE_DEBUG\";\nconst SERVICE_PREFIX = \"anthropic-cache\";\nlet _client: PluginInput[\"client\"] | null = null;\nfunction isDebugEnabled() {\n const val = process.env[ENV_DEBUG];\n return val === \"1\" || val?.toLowerCase() === \"true\";\n}\nexport function initLogger(client: PluginInput[\"client\"]) {\n _client = client;\n}\nexport function createLogger(module: string) {\n const service = `${SERVICE_PREFIX}.${module}`;\n const log = (\n level: \"debug\" | \"info\" | \"warn\" | \"error\",\n message: string,\n extra?: any,\n ) => {\n if (_client?.app && typeof _client.app.log === \"function\") {\n _client.app\n .log({ body: { service, level, message, extra } })\n .catch(() => {});\n return;\n }\n if (isDebugEnabled()) {\n const prefix = `[${service}]`;\n const args = extra ? [prefix, message, extra] : [prefix, message];\n switch (level) {\n case \"debug\":\n console.debug(...args);\n break;\n case \"info\":\n console.info(...args);\n break;\n case \"warn\":\n console.warn(...args);\n break;\n case \"error\":\n console.error(...args);\n break;\n }\n }\n };\n return {\n debug: (message: string, extra?: any) => log(\"debug\", message, extra),\n info: (message: string, extra?: any) => log(\"info\", message, extra),\n warn: (message: string, extra?: any) => log(\"warn\", message, extra),\n error: (message: string, extra?: any) => log(\"error\", message, extra),\n };\n}\n", "import type { Hooks, Plugin } from \"@opencode-ai/plugin\";\nimport { createLogger } from \"./logger\";\n\nconst log = createLogger(\"fetch\");\n// Session and project tracking\nlet currentSessionId = \"\";\nlet projectId = \"\";\nlet isInitialized = false;\nfunction buildUserId() {\n const pid = projectId || \"unknown\";\n const sid = currentSessionId || \"unknown\";\n return `user_${pid}_account__session_${sid}`;\n}\nfunction isJsonContentType(headers: Headers) {\n const ct = headers.get(\"content-type\") || \"\";\n return ct.includes(\"application/json\");\n}\nfunction parseBody(body?: unknown) {\n if (!body) return null;\n if (typeof body === \"string\") return body;\n if (body instanceof Uint8Array) return new TextDecoder().decode(body);\n return null;\n}\ntype CachePayload = {\n metadata?: {\n user_id?: string;\n [key: string]: unknown;\n };\n [key: string]: unknown;\n};\n\n// Create the fetch interceptor that injects metadata.user_id\nfunction createCacheFetch(providerName: string) {\n return async (url: string, init?: RequestInit) => {\n const headers = new Headers(init?.headers);\n const method = (init?.method || \"GET\").toUpperCase();\n if (method !== \"POST\" || !isJsonContentType(headers)) {\n return fetch(url, init);\n }\n const rawBody = parseBody(init?.body);\n if (!rawBody) return fetch(url, init);\n let payload: CachePayload;\n try {\n payload = JSON.parse(rawBody) as CachePayload;\n } catch {\n log.warn(`[${providerName}] Failed to parse request body as JSON`);\n return fetch(url, init);\n }\n if (payload && typeof payload === \"object\") {\n if (!payload.metadata || typeof payload.metadata !== \"object\") {\n payload.metadata = {};\n }\n const meta = payload.metadata;\n if (meta.user_id == null) {\n meta.user_id = buildUserId();\n log.info(`[${providerName}] Injected user_id`, {\n user_id: meta.user_id,\n });\n }\n }\n headers.delete(\"content-length\");\n return fetch(url, { ...init, headers, body: JSON.stringify(payload) });\n };\n}\n// Shared event handler\nfunction createEventHandler(): Hooks[\"event\"] {\n return async ({ event }) => {\n if (event.type === \"session.created\" || event.type === \"session.updated\") {\n const props = event.properties;\n currentSessionId = props?.info?.id || \"\";\n log.debug(\"Session updated\", { sessionId: currentSessionId });\n }\n };\n}\n// Factory to create plugin for a specific provider\nfunction createProviderPlugin(providerName: string): Plugin {\n return async ({ project }) => {\n if (!isInitialized) {\n projectId = project?.id || \"\";\n isInitialized = true;\n log.info(\"Plugin initialized\", { projectId });\n }\n log.info(`Auth handler registered for provider: ${providerName}`);\n return {\n event: createEventHandler(),\n auth: {\n provider: providerName,\n methods: [{ type: \"api\", label: \"key\" }],\n loader: async () => ({\n fetch: createCacheFetch(providerName),\n }),\n },\n };\n };\n}\n// Pre-built plugins for common providers\nexport const FoxcodeAwsCache = createProviderPlugin(\"foxcode-aws\");\nexport const AnthropicCache = createProviderPlugin(\"anthropic\");\nexport const AnthropicUltraCache = createProviderPlugin(\"anthropic-ultra\");\n// Generic factory for custom provider names\nexport const createAnthropicCachePlugin = createProviderPlugin;\nexport default AnthropicCache;\n", "import type { Plugin } from \"@opencode-ai/plugin\";\n\nconst OPENAI_PROVIDER_NPM = \"@ai-sdk/openai\";\nconst SESSION_HEADER_KEYS = [\n \"x-session-id\",\n \"conversation_id\",\n \"session_id\",\n] as const;\n\ntype HeaderMap = Record<string, unknown>;\n\nconst normalize = (value: unknown): string =>\n typeof value === \"string\" ? value.trim() : \"\";\n\nconst pickHeaderSessionValue = (headers: HeaderMap): string => {\n for (const key of SESSION_HEADER_KEYS) {\n const value = normalize(headers[key]);\n if (value) return value;\n }\n return \"\";\n};\n\nconst ensureHeaders = (model: { headers?: unknown }): HeaderMap => {\n if (model.headers && typeof model.headers === \"object\") {\n return model.headers as HeaderMap;\n }\n const headers: HeaderMap = {};\n model.headers = headers;\n return headers;\n};\n\n/**\n * set a session-scoped prompt cache key and session-scoped sticky routing headers for right.codes.\n *\n * Injects HTTP headers via `input.model.headers` so upstream proxies/load\n * balancers can keep a conversation pinned to one account.\n *\n * Headers injected for @ai-sdk/openai providers:\n * - x-session-id\n * - conversation_id\n * - session_id\n *\n * Source of truth (in order):\n * - env: OPENCODE_PROMPT_CACHE_KEY (manual override)\n * - env: OPENCODE_STICKY_SESSION_ID (manual override)\n * - model headers (x-session-id / conversation_id / session_id)\n * - opencode sessionID (default)\n */\nexport const StickySessionPlugin: Plugin = async ({ client }) => {\n const envPromptCacheKey = normalize(process.env.OPENCODE_PROMPT_CACHE_KEY);\n const envStickySessionID = normalize(process.env.OPENCODE_STICKY_SESSION_ID);\n const envOverride = envPromptCacheKey || envStickySessionID;\n const isProviderCodexMap: Record<string, boolean> = {};\n let inited = false;\n const init = async () => {\n if (inited) return;\n const resp = await client.config.providers();\n const providers = resp.data?.providers ?? [];\n for (const provider of providers) {\n isProviderCodexMap[provider.id] = Boolean(provider.options?.isCodex);\n }\n inited = true;\n };\n\n return {\n \"chat.params\": async (input, output) => {\n if (!inited) await init();\n const providerNpm = normalize(input.model.api.npm);\n const isCodex = isProviderCodexMap[input.model.providerID] ?? false;\n if (!providerNpm.includes(OPENAI_PROVIDER_NPM) && !isCodex) return;\n\n const headers = ensureHeaders(input.model);\n const sessionValue =\n envOverride ||\n pickHeaderSessionValue(headers) ||\n normalize(input.sessionID);\n if (!sessionValue) return;\n\n for (const key of SESSION_HEADER_KEYS) {\n headers[key] = sessionValue;\n }\n\n output.options.promptCacheKey = sessionValue;\n if (isCodex && !output.options.instructions) {\n output.options.instructions = \"\";\n }\n },\n };\n};\n\nexport default StickySessionPlugin;\n", "import type { Hooks, Plugin } from \"@opencode-ai/plugin\";\nimport AnthropicCachePlugin from \"./opencode-anthropic-cache\";\nimport { StickySessionPlugin } from \"./sticky-session-plugin\";\nimport { initLogger } from \"./logger\";\n\nconst mergeHook = <TArgs extends unknown[]>(\n first?: (...args: TArgs) => Promise<void>,\n second?: (...args: TArgs) => Promise<void>,\n): ((...args: TArgs) => Promise<void>) | undefined => {\n if (!first) return second;\n if (!second) return first;\n return async (...args: TArgs) => {\n await first(...args);\n await second(...args);\n };\n};\n\nlet isInitialized = false;\nexport const CombinedPlugin: Plugin = async (input) => {\n if (!isInitialized) {\n initLogger(input.client);\n isInitialized = true;\n }\n const stickyHooks = await StickySessionPlugin(input);\n const anthropicHooks = await AnthropicCachePlugin(input);\n\n const merged: Hooks = {\n ...anthropicHooks,\n ...stickyHooks,\n tool: {\n ...(anthropicHooks.tool ?? {}),\n ...(stickyHooks.tool ?? {}),\n },\n \"chat.message\": mergeHook(\n anthropicHooks[\"chat.message\"],\n stickyHooks[\"chat.message\"],\n ),\n \"chat.params\": mergeHook(\n anthropicHooks[\"chat.params\"],\n stickyHooks[\"chat.params\"],\n ),\n \"chat.headers\": mergeHook(\n anthropicHooks[\"chat.headers\"],\n stickyHooks[\"chat.headers\"],\n ),\n \"permission.ask\": mergeHook(\n anthropicHooks[\"permission.ask\"],\n stickyHooks[\"permission.ask\"],\n ),\n \"command.execute.before\": mergeHook(\n anthropicHooks[\"command.execute.before\"],\n stickyHooks[\"command.execute.before\"],\n ),\n \"tool.execute.before\": mergeHook(\n anthropicHooks[\"tool.execute.before\"],\n stickyHooks[\"tool.execute.before\"],\n ),\n \"shell.env\": mergeHook(\n anthropicHooks[\"shell.env\"],\n stickyHooks[\"shell.env\"],\n ),\n \"tool.execute.after\": mergeHook(\n anthropicHooks[\"tool.execute.after\"],\n stickyHooks[\"tool.execute.after\"],\n ),\n \"experimental.chat.messages.transform\": mergeHook(\n anthropicHooks[\"experimental.chat.messages.transform\"],\n stickyHooks[\"experimental.chat.messages.transform\"],\n ),\n \"experimental.chat.system.transform\": mergeHook(\n anthropicHooks[\"experimental.chat.system.transform\"],\n stickyHooks[\"experimental.chat.system.transform\"],\n ),\n \"experimental.session.compacting\": mergeHook(\n anthropicHooks[\"experimental.session.compacting\"],\n stickyHooks[\"experimental.session.compacting\"],\n ),\n \"experimental.text.complete\": mergeHook(\n anthropicHooks[\"experimental.text.complete\"],\n stickyHooks[\"experimental.text.complete\"],\n ),\n \"tool.definition\": mergeHook(\n anthropicHooks[\"tool.definition\"],\n stickyHooks[\"tool.definition\"],\n ),\n event: mergeHook(anthropicHooks.event, stickyHooks.event),\n config: mergeHook(anthropicHooks.config, stickyHooks.config),\n };\n\n if (merged.auth === undefined) {\n merged.auth = anthropicHooks.auth ?? stickyHooks.auth;\n }\n\n return merged;\n};\n\nexport default CombinedPlugin;\n"],
|
|
5
|
+
"mappings": ";AAEA,IAAM,YAAY;AAClB,IAAM,iBAAiB;AACvB,IAAI,UAAwC;AAC5C,SAAS,iBAAiB;AACxB,QAAM,MAAM,QAAQ,IAAI,SAAS;AACjC,SAAO,QAAQ,OAAO,KAAK,YAAY,MAAM;AAC/C;AACO,SAAS,WAAW,QAA+B;AACxD,YAAU;AACZ;AACO,SAAS,aAAa,QAAgB;AAC3C,QAAM,UAAU,GAAG,cAAc,IAAI,MAAM;AAC3C,QAAMA,OAAM,CACV,OACA,SACA,UACG;AACH,QAAI,SAAS,OAAO,OAAO,QAAQ,IAAI,QAAQ,YAAY;AACzD,cAAQ,IACL,IAAI,EAAE,MAAM,EAAE,SAAS,OAAO,SAAS,MAAM,EAAE,CAAC,EAChD,MAAM,MAAM;AAAA,MAAC,CAAC;AACjB;AAAA,IACF;AACA,QAAI,eAAe,GAAG;AACpB,YAAM,SAAS,IAAI,OAAO;AAC1B,YAAM,OAAO,QAAQ,CAAC,QAAQ,SAAS,KAAK,IAAI,CAAC,QAAQ,OAAO;AAChE,cAAQ,OAAO;AAAA,QACb,KAAK;AACH,kBAAQ,MAAM,GAAG,IAAI;AACrB;AAAA,QACF,KAAK;AACH,kBAAQ,KAAK,GAAG,IAAI;AACpB;AAAA,QACF,KAAK;AACH,kBAAQ,KAAK,GAAG,IAAI;AACpB;AAAA,QACF,KAAK;AACH,kBAAQ,MAAM,GAAG,IAAI;AACrB;AAAA,MACJ;AAAA,IACF;AAAA,EACF;AACA,SAAO;AAAA,IACL,OAAO,CAAC,SAAiB,UAAgBA,KAAI,SAAS,SAAS,KAAK;AAAA,IACpE,MAAM,CAAC,SAAiB,UAAgBA,KAAI,QAAQ,SAAS,KAAK;AAAA,IAClE,MAAM,CAAC,SAAiB,UAAgBA,KAAI,QAAQ,SAAS,KAAK;AAAA,IAClE,OAAO,CAAC,SAAiB,UAAgBA,KAAI,SAAS,SAAS,KAAK;AAAA,EACtE;AACF;;;AC/CA,IAAM,MAAM,aAAa,OAAO;AAEhC,IAAI,mBAAmB;AACvB,IAAI,YAAY;AAChB,IAAI,gBAAgB;AACpB,SAAS,cAAc;AACrB,QAAM,MAAM,aAAa;AACzB,QAAM,MAAM,oBAAoB;AAChC,SAAO,QAAQ,GAAG,qBAAqB,GAAG;AAC5C;AACA,SAAS,kBAAkB,SAAkB;AAC3C,QAAM,KAAK,QAAQ,IAAI,cAAc,KAAK;AAC1C,SAAO,GAAG,SAAS,kBAAkB;AACvC;AACA,SAAS,UAAU,MAAgB;AACjC,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI,OAAO,SAAS,SAAU,QAAO;AACrC,MAAI,gBAAgB,WAAY,QAAO,IAAI,YAAY,EAAE,OAAO,IAAI;AACpE,SAAO;AACT;AAUA,SAAS,iBAAiB,cAAsB;AAC9C,SAAO,OAAO,KAAa,SAAuB;AAChD,UAAM,UAAU,IAAI,QAAQ,MAAM,OAAO;AACzC,UAAM,UAAU,MAAM,UAAU,OAAO,YAAY;AACnD,QAAI,WAAW,UAAU,CAAC,kBAAkB,OAAO,GAAG;AACpD,aAAO,MAAM,KAAK,IAAI;AAAA,IACxB;AACA,UAAM,UAAU,UAAU,MAAM,IAAI;AACpC,QAAI,CAAC,QAAS,QAAO,MAAM,KAAK,IAAI;AACpC,QAAI;AACJ,QAAI;AACF,gBAAU,KAAK,MAAM,OAAO;AAAA,IAC9B,QAAQ;AACN,UAAI,KAAK,IAAI,YAAY,wCAAwC;AACjE,aAAO,MAAM,KAAK,IAAI;AAAA,IACxB;AACA,QAAI,WAAW,OAAO,YAAY,UAAU;AAC1C,UAAI,CAAC,QAAQ,YAAY,OAAO,QAAQ,aAAa,UAAU;AAC7D,gBAAQ,WAAW,CAAC;AAAA,MACtB;AACA,YAAM,OAAO,QAAQ;AACrB,UAAI,KAAK,WAAW,MAAM;AACxB,aAAK,UAAU,YAAY;AAC3B,YAAI,KAAK,IAAI,YAAY,sBAAsB;AAAA,UAC7C,SAAS,KAAK;AAAA,QAChB,CAAC;AAAA,MACH;AAAA,IACF;AACA,YAAQ,OAAO,gBAAgB;AAC/B,WAAO,MAAM,KAAK,EAAE,GAAG,MAAM,SAAS,MAAM,KAAK,UAAU,OAAO,EAAE,CAAC;AAAA,EACvE;AACF;AAEA,SAAS,qBAAqC;AAC5C,SAAO,OAAO,EAAE,MAAM,MAAM;AAC1B,QAAI,MAAM,SAAS,qBAAqB,MAAM,SAAS,mBAAmB;AACxE,YAAM,QAAQ,MAAM;AACpB,yBAAmB,OAAO,MAAM,MAAM;AACtC,UAAI,MAAM,mBAAmB,EAAE,WAAW,iBAAiB,CAAC;AAAA,IAC9D;AAAA,EACF;AACF;AAEA,SAAS,qBAAqB,cAA8B;AAC1D,SAAO,OAAO,EAAE,QAAQ,MAAM;AAC5B,QAAI,CAAC,eAAe;AAClB,kBAAY,SAAS,MAAM;AAC3B,sBAAgB;AAChB,UAAI,KAAK,sBAAsB,EAAE,UAAU,CAAC;AAAA,IAC9C;AACA,QAAI,KAAK,yCAAyC,YAAY,EAAE;AAChE,WAAO;AAAA,MACL,OAAO,mBAAmB;AAAA,MAC1B,MAAM;AAAA,QACJ,UAAU;AAAA,QACV,SAAS,CAAC,EAAE,MAAM,OAAO,OAAO,MAAM,CAAC;AAAA,QACvC,QAAQ,aAAa;AAAA,UACnB,OAAO,iBAAiB,YAAY;AAAA,QACtC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,kBAAkB,qBAAqB,aAAa;AAC1D,IAAM,iBAAiB,qBAAqB,WAAW;AACvD,IAAM,sBAAsB,qBAAqB,iBAAiB;AAElE,IAAM,6BAA6B;AAC1C,IAAO,mCAAQ;;;ACnGf,IAAM,sBAAsB;AAC5B,IAAM,sBAAsB;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AACF;AAIA,IAAM,YAAY,CAAC,UACjB,OAAO,UAAU,WAAW,MAAM,KAAK,IAAI;AAE7C,IAAM,yBAAyB,CAAC,YAA+B;AAC7D,aAAW,OAAO,qBAAqB;AACrC,UAAM,QAAQ,UAAU,QAAQ,GAAG,CAAC;AACpC,QAAI,MAAO,QAAO;AAAA,EACpB;AACA,SAAO;AACT;AAEA,IAAM,gBAAgB,CAAC,UAA4C;AACjE,MAAI,MAAM,WAAW,OAAO,MAAM,YAAY,UAAU;AACtD,WAAO,MAAM;AAAA,EACf;AACA,QAAM,UAAqB,CAAC;AAC5B,QAAM,UAAU;AAChB,SAAO;AACT;AAmBO,IAAM,sBAA8B,OAAO,EAAE,OAAO,MAAM;AAC/D,QAAM,oBAAoB,UAAU,QAAQ,IAAI,yBAAyB;AACzE,QAAM,qBAAqB,UAAU,QAAQ,IAAI,0BAA0B;AAC3E,QAAM,cAAc,qBAAqB;AACzC,QAAM,qBAA8C,CAAC;AACrD,MAAI,SAAS;AACb,QAAM,OAAO,YAAY;AACvB,QAAI,OAAQ;AACZ,UAAM,OAAO,MAAM,OAAO,OAAO,UAAU;AAC3C,UAAM,YAAY,KAAK,MAAM,aAAa,CAAC;AAC3C,eAAW,YAAY,WAAW;AAChC,yBAAmB,SAAS,EAAE,IAAI,QAAQ,SAAS,SAAS,OAAO;AAAA,IACrE;AACA,aAAS;AAAA,EACX;AAEA,SAAO;AAAA,IACL,eAAe,OAAO,OAAO,WAAW;AACtC,UAAI,CAAC,OAAQ,OAAM,KAAK;AACxB,YAAM,cAAc,UAAU,MAAM,MAAM,IAAI,GAAG;AACjD,YAAM,UAAU,mBAAmB,MAAM,MAAM,UAAU,KAAK;AAC9D,UAAI,CAAC,YAAY,SAAS,mBAAmB,KAAK,CAAC,QAAS;AAE5D,YAAM,UAAU,cAAc,MAAM,KAAK;AACzC,YAAM,eACJ,eACA,uBAAuB,OAAO,KAC9B,UAAU,MAAM,SAAS;AAC3B,UAAI,CAAC,aAAc;AAEnB,iBAAW,OAAO,qBAAqB;AACrC,gBAAQ,GAAG,IAAI;AAAA,MACjB;AAEA,aAAO,QAAQ,iBAAiB;AAChC,UAAI,WAAW,CAAC,OAAO,QAAQ,cAAc;AAC3C,eAAO,QAAQ,eAAe;AAAA,MAChC;AAAA,IACF;AAAA,EACF;AACF;;;ACnFA,IAAM,YAAY,CAChB,OACA,WACoD;AACpD,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,CAAC,OAAQ,QAAO;AACpB,SAAO,UAAU,SAAgB;AAC/B,UAAM,MAAM,GAAG,IAAI;AACnB,UAAM,OAAO,GAAG,IAAI;AAAA,EACtB;AACF;AAEA,IAAIC,iBAAgB;AACb,IAAM,iBAAyB,OAAO,UAAU;AACrD,MAAI,CAACA,gBAAe;AAClB,eAAW,MAAM,MAAM;AACvB,IAAAA,iBAAgB;AAAA,EAClB;AACA,QAAM,cAAc,MAAM,oBAAoB,KAAK;AACnD,QAAM,iBAAiB,MAAM,iCAAqB,KAAK;AAEvD,QAAM,SAAgB;AAAA,IACpB,GAAG;AAAA,IACH,GAAG;AAAA,IACH,MAAM;AAAA,MACJ,GAAI,eAAe,QAAQ,CAAC;AAAA,MAC5B,GAAI,YAAY,QAAQ,CAAC;AAAA,IAC3B;AAAA,IACA,gBAAgB;AAAA,MACd,eAAe,cAAc;AAAA,MAC7B,YAAY,cAAc;AAAA,IAC5B;AAAA,IACA,eAAe;AAAA,MACb,eAAe,aAAa;AAAA,MAC5B,YAAY,aAAa;AAAA,IAC3B;AAAA,IACA,gBAAgB;AAAA,MACd,eAAe,cAAc;AAAA,MAC7B,YAAY,cAAc;AAAA,IAC5B;AAAA,IACA,kBAAkB;AAAA,MAChB,eAAe,gBAAgB;AAAA,MAC/B,YAAY,gBAAgB;AAAA,IAC9B;AAAA,IACA,0BAA0B;AAAA,MACxB,eAAe,wBAAwB;AAAA,MACvC,YAAY,wBAAwB;AAAA,IACtC;AAAA,IACA,uBAAuB;AAAA,MACrB,eAAe,qBAAqB;AAAA,MACpC,YAAY,qBAAqB;AAAA,IACnC;AAAA,IACA,aAAa;AAAA,MACX,eAAe,WAAW;AAAA,MAC1B,YAAY,WAAW;AAAA,IACzB;AAAA,IACA,sBAAsB;AAAA,MACpB,eAAe,oBAAoB;AAAA,MACnC,YAAY,oBAAoB;AAAA,IAClC;AAAA,IACA,wCAAwC;AAAA,MACtC,eAAe,sCAAsC;AAAA,MACrD,YAAY,sCAAsC;AAAA,IACpD;AAAA,IACA,sCAAsC;AAAA,MACpC,eAAe,oCAAoC;AAAA,MACnD,YAAY,oCAAoC;AAAA,IAClD;AAAA,IACA,mCAAmC;AAAA,MACjC,eAAe,iCAAiC;AAAA,MAChD,YAAY,iCAAiC;AAAA,IAC/C;AAAA,IACA,8BAA8B;AAAA,MAC5B,eAAe,4BAA4B;AAAA,MAC3C,YAAY,4BAA4B;AAAA,IAC1C;AAAA,IACA,mBAAmB;AAAA,MACjB,eAAe,iBAAiB;AAAA,MAChC,YAAY,iBAAiB;AAAA,IAC/B;AAAA,IACA,OAAO,UAAU,eAAe,OAAO,YAAY,KAAK;AAAA,IACxD,QAAQ,UAAU,eAAe,QAAQ,YAAY,MAAM;AAAA,EAC7D;AAEA,MAAI,OAAO,SAAS,QAAW;AAC7B,WAAO,OAAO,eAAe,QAAQ,YAAY;AAAA,EACnD;AAEA,SAAO;AACT;AAEA,IAAO,0BAAQ;",
|
|
6
|
+
"names": ["log", "isInitialized"]
|
|
7
7
|
}
|
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { PluginInput } from "@opencode-ai/plugin";
|
|
2
|
+
export declare function initLogger(client: PluginInput["client"]): void;
|
|
3
|
+
export declare function createLogger(module: string): {
|
|
4
|
+
debug: (message: string, extra?: any) => void;
|
|
5
|
+
info: (message: string, extra?: any) => void;
|
|
6
|
+
warn: (message: string, extra?: any) => void;
|
|
7
|
+
error: (message: string, extra?: any) => void;
|
|
8
|
+
};
|
|
9
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AASvD,wBAAgB,UAAU,CAAC,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,QAEvD;AACD,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM;qBAiCtB,MAAM,UAAU,GAAG;oBACpB,MAAM,UAAU,GAAG;oBACnB,MAAM,UAAU,GAAG;qBAClB,MAAM,UAAU,GAAG;EAEvC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"opencode-anthropic-cache.d.ts","sourceRoot":"","sources":["../src/opencode-anthropic-cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAS,MAAM,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"opencode-anthropic-cache.d.ts","sourceRoot":"","sources":["../src/opencode-anthropic-cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAS,MAAM,EAAE,MAAM,qBAAqB,CAAC;AA2EzD,iBAAS,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAmB1D;AAED,eAAO,MAAM,eAAe,QAAsC,CAAC;AACnE,eAAO,MAAM,cAAc,QAAoC,CAAC;AAChE,eAAO,MAAM,mBAAmB,QAA0C,CAAC;AAE3E,eAAO,MAAM,0BAA0B,6BAAuB,CAAC;AAC/D,eAAe,cAAc,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-fixes-huihui",
|
|
3
|
-
"version": "0.1.4-beta.
|
|
3
|
+
"version": "0.1.4-beta.3",
|
|
4
4
|
"description": "Unified sticky-session plugin for opencode with session headers and prompt cache key.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -54,5 +54,8 @@
|
|
|
54
54
|
"@types/node": "^25.2.3",
|
|
55
55
|
"esbuild": "^0.27.3",
|
|
56
56
|
"typescript": "^5.9.2"
|
|
57
|
+
},
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"opencode-anthropic-cache": "^1.0.0"
|
|
57
60
|
}
|
|
58
61
|
}
|