feat: 个人定制版 —— API Key 模式显示剩余积分 + 悬浮卡片

基于 @axiaohungry/dsh-llm-workbuddy 1.3.21 (MIT)。
详见 README.CUSTOM.md。
This commit is contained in:
dsh-custom committed 2026-10-10 13:59:09 +08:00
commit a44227b69a
14 files changed
+5517

No files matched your search

+982
View File
@@ -0,0 +1,982 @@
import { AsyncLocalStorage } from "node:async_hooks";
import { credentialRef } from "@deepseek-ai/dsh-credentials";
import { launchEnvironmentOf } from "@deepseek-ai/dsh-launch-environment";
import { LlmError, assertUsableApiKey, resolveRetryPolicy } from "@deepseek-ai/dsh-llm";
import { Config, PiAiAdapter } from "@deepseek-ai/dsh-llm-pi-ai";
import * as dshSettings from "@deepseek-ai/dsh-settings";
import { createProvider } from "@earendil-works/pi-ai";
import * as openAICompletionsApi from "@earendil-works/pi-ai/api/openai-completions";
import * as openAIResponsesApi from "@earendil-works/pi-ai/api/openai-responses";
import * as anthropicMessagesApi from "@earendil-works/pi-ai/api/anthropic-messages";
import { builtinProviders } from "@earendil-works/pi-ai/providers/all";
import {
WORKBUDDY_SESSION_REF,
WORKBUDDY_SESSIONS_REF,
WORKBUDDY_SESSION_ROUTING_REF,
LEGACY_SESSION_REF,
LEGACY_SESSIONS_REF,
activeWorkBuddySession,
createWorkBuddySessionStore,
createWorkBuddySessionRoutingState,
parseWorkBuddySession,
parseWorkBuddySessions,
parseWorkBuddySessionRouting,
refreshWorkBuddySession,
serializeWorkBuddySession,
serializeWorkBuddySessionRouting,
serializeWorkBuddySessions,
sessionCacheDeadline,
sessionNeedsRefresh,
upsertWorkBuddySession,
} from "./workbuddy-auth.js";
import { installWorkBuddyWeb } from "./workbuddy-web.js";
import { probeEndpoint } from "./workbuddy-discovery.js";
export { Config };
export const name = "llm-workbuddy";
export const inject = ["llm"];
const NS = typeof dshSettings.settingsNamespace === "function" ? dshSettings.settingsNamespace("llm-pi-ai") : "llm-pi-ai";
const PROVIDER = "workbuddy-cn";
const LEGACY_PROVIDER = "codebuddy-cn";
const WORKBUDDY_PROVIDERS = new Set([PROVIDER, LEGACY_PROVIDER]);
const WORKBUDDY_PROVIDER_PATTERN = /(?:^|-)(?:work-?buddy|code-?buddy)(?:-|$)/;
const DISPLAY_NAME = "WorkBuddy";
const API_KEY_ENV = "WORKBUDDY_API_KEY";
const LEGACY_API_KEY_ENV = "CODEBUDDY_API_KEY";
const BASE_URL = "https://copilot.tencent.com/v2";
const CONFIG_URL = "https://copilot.tencent.com/v3/config";
const USER_AGENT = "CLI/unknown CodeBuddy/2.137.1";
const STREAM_IDLE_TIMEOUT_MS = 300_000;
const traceContext = new AsyncLocalStorage();
let nextTraceId = 0;
const NO_COST = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
const EFFORTS = ["minimal", "low", "medium", "high", "xhigh", "max"];
const THINKING_LEVELS = ["off", ...EFFORTS];
const COMPAT = {
supportsStore: false,
supportsDeveloperRole: false,
supportsReasoningEffort: true,
maxTokensField: "max_tokens",
thinkingFormat: "openai",
};
function workBuddyRequestOptions(options) {
const trace = traceContext.getStore();
const fetchImpl = options?.fetch ?? globalThis.fetch;
return {
...options,
timeoutMs: options?.timeoutMs ?? STREAM_IDLE_TIMEOUT_MS,
headers: { ...(options?.headers ?? {}), "user-agent": USER_AGENT },
...(trace ? { fetch: async (input, init) => {
const started = Date.now();
traceEvent(trace, "http.start");
try {
const response = await fetchImpl(input, init);
traceEvent(trace, "http.headers", { elapsedMs: Date.now() - started, status: response.status });
return response;
} catch (error) {
traceEvent(trace, "http.error", { elapsedMs: Date.now() - started, ...errorFields(error) });
throw error;
}
} } : {}),
};
}
function errorFields(error) {
const safe = (value) => typeof value === "string" && /^[A-Za-z_][A-Za-z_0-9-]{0,39}$/.test(value) ? value : undefined;
return {
...(safe(error?.name) ? { errorName: safe(error.name) } : {}),
...(safe(error?.code) ? { errorCode: safe(error.code) } : {}),
...(Number.isInteger(error?.status) ? { status: error.status } : {}),
};
}
function traceEvent(trace, stage, details = {}) {
if (!trace) return;
console.info("[dsh-llm-workbuddy]", JSON.stringify({ request: trace.id, stage, ...details }));
}
function observedWorkBuddyStream(model, context, options) {
const trace = traceContext.getStore();
const started = Date.now();
const stream = openAICompletionsApi.streamSimple(model, context, workBuddyRequestOptions(options));
return (async function* () {
traceEvent(trace, "model.start");
let first = true;
let completed = false;
let failed = false;
try {
for await (const chunk of stream) {
if (first) {
first = false;
traceEvent(trace, "model.first-chunk", { elapsedMs: Date.now() - started });
}
yield chunk;
}
completed = true;
traceEvent(trace, "model.done", { elapsedMs: Date.now() - started });
} catch (error) {
failed = true;
traceEvent(trace, "model.error", { elapsedMs: Date.now() - started, ...errorFields(error) });
throw error;
} finally {
if (!completed && !failed) traceEvent(trace, "model.cancelled", { elapsedMs: Date.now() - started });
}
})();
}
const workBuddyApi = {
...openAICompletionsApi,
stream: (model, context, options) => openAICompletionsApi.stream(model, context, workBuddyRequestOptions(options)),
streamSimple: observedWorkBuddyStream,
};
const FALLBACK_MODELS = [
["hy3", "Hy3", 192000, 64000, true],
["glm-5.2", "GLM-5.2", 1000000, 48000, false],
["glm-5.1", "GLM-5.1", 200000, 48000, false],
["glm-5v-turbo", "GLM-5v-Turbo", 200000, 64000, true],
["minimax-m3-pay", "MiniMax-M3", 512000, 128000, true],
["minimax-m2.7", "MiniMax-M2.7", 200000, 48000, true],
["kimi-k3-2", "Kimi-K3", 1000000, 32000, true],
["kimi-k2.7", "Kimi-K2.7-Code", 256000, 32000, true],
["kimi-k2.6", "Kimi-K2.6", 256000, 32000, true],
["deepseek-v4-pro", "DeepSeek V4 Pro", 1000000, 50000, true],
["deepseek-v4-flash", "DeepSeek V4 Flash", 1000000, 50000, true],
].map(([id, modelName, contextWindow, maxTokens, images]) =>
workBuddyModel({ id, name: modelName, contextWindow, maxTokens, images }),
);
function workBuddyModel({ provider = PROVIDER, id, name: modelName, contextWindow, maxTokens, images, reasoning = true, thinkingLevelMap = { off: null }, defaultReasoningEffort, thinkingFormat }) {
return {
id,
name: modelName,
api: "openai-completions",
provider,
baseUrl: BASE_URL,
reasoning,
...(reasoning ? { thinkingLevelMap: { ...thinkingLevelMap } } : {}),
...(defaultReasoningEffort ? { defaultReasoningEffort } : {}),
input: images ? ["text", "image"] : ["text"],
cost: { ...NO_COST },
contextWindow,
maxTokens,
compat: { ...COMPAT, ...(thinkingFormat ? { thinkingFormat } : {}) },
};
}
function remoteReasoning(raw, fallback) {
const reasoning = raw.supportsReasoning ?? fallback?.reasoning ?? raw.onlyReasoning === true;
if (!reasoning) return { reasoning: false };
const declared = raw.thinkingLevelMap && typeof raw.thinkingLevelMap === "object" ? raw.thinkingLevelMap : undefined;
const thinkingLevelMap = declared
? Object.fromEntries(THINKING_LEVELS.map((level) => [level,
Object.hasOwn(declared, level) && (typeof declared[level] === "string" || declared[level] === null) ? declared[level] : null]))
: { ...(fallback?.thinkingLevelMap ?? {}), ...(raw.onlyReasoning === true ? { off: null } : {}) };
const effort = raw.reasoning?.effort;
const defaultReasoningEffort = EFFORTS.includes(effort) && thinkingLevelMap[effort] !== null ? effort : undefined;
return {
reasoning: true,
thinkingLevelMap,
...(defaultReasoningEffort ? { defaultReasoningEffort } : {}),
...(typeof raw.thinkingFormat === "string" ? { thinkingFormat: raw.thinkingFormat } : {}),
};
}
function configuredReasoning(entry, base) {
if (entry.reasoningEfforts === false) return { reasoning: false };
if (!entry.reasoningEfforts || typeof entry.reasoningEfforts !== "object") {
return base ? {
reasoning: base.reasoning,
thinkingLevelMap: base.thinkingLevelMap,
defaultReasoningEffort: base.defaultReasoningEffort,
thinkingFormat: base.compat?.thinkingFormat,
} : { reasoning: false };
}
const map = {};
for (const level of THINKING_LEVELS) {
if (!Object.hasOwn(entry.reasoningEfforts, level)) map[level] = null;
else if (!(level === "off" && entry.reasoningEfforts[level] === null)) map[level] = entry.reasoningEfforts[level];
}
return { reasoning: true, thinkingLevelMap: map, thinkingFormat: entry.compat?.thinkingFormat };
}
function positiveInteger(...values) {
return values.find((value) => Number.isSafeInteger(value) && value > 0);
}
function text(...values) {
return values.find((value) => typeof value === "string" && value.length > 0);
}
function modelsFromConfig(data) {
const agents = Array.isArray(data?.agents) ? data.agents : data?.agent?.agents;
const cli = Array.isArray(agents) ? agents.find((agent) => agent?.name === "cli") : undefined;
const allowed = Array.isArray(cli?.models) ? cli.models : [];
const source = Array.isArray(data?.models) ? data.models : [];
const byId = new Map(source.map((model) => [model?.id, model]));
return allowed.flatMap((id) => {
const raw = byId.get(id);
if (!raw) return [];
const fallback = FALLBACK_MODELS.find((model) => model.id === id);
const contextWindow = positiveInteger(raw.maxInputTokens, raw.maxAllowedSize, fallback?.contextWindow);
const maxTokens = positiveInteger(raw.maxOutputTokens, fallback?.maxTokens);
if (!contextWindow || !maxTokens) return [];
return [workBuddyModel({
id,
name: text(raw.name, fallback?.name, id),
contextWindow,
maxTokens,
images: raw.supportsImages === true || fallback?.input.includes("image") === true,
...remoteReasoning(raw, fallback),
})];
});
}
function authenticationHeaders(credential) {
const value = assertUsableApiKey(credential.value, name, credential.ref ?? API_KEY_ENV);
return credential.kind === "bearer" ? { authorization: `Bearer ${value}` } : { "x-api-key": value };
}
async function fetchWorkBuddyModels(credential, signal) {
let response;
try {
response = await fetch(CONFIG_URL, {
headers: {
accept: "application/json",
...authenticationHeaders(credential),
"user-agent": USER_AGENT,
"x-product": "SaaS",
},
signal,
});
} catch (error) {
if (signal?.aborted) throw new LlmError("WorkBuddy 模型列表获取已取消", "ABORTED", { cause: error });
throw new LlmError("无法连接 WorkBuddy 模型配置接口", "DISCOVERY_FAILED", { cause: error });
}
if (!response.ok) throw new LlmError(`WorkBuddy 模型配置接口返回 ${response.status}`, "DISCOVERY_FAILED");
const body = await response.json();
if (body?.code !== 0) throw new LlmError(`WorkBuddy 模型配置接口错误:${body?.msg ?? body?.code}`, "DISCOVERY_FAILED");
const models = modelsFromConfig(body.data);
if (models.length === 0) throw new LlmError("WorkBuddy 没有返回 CLI 可用模型", "DISCOVERY_FAILED");
return models;
}
/**
* WorkBuddy's credential is already resolved by the DSH adapter. Do not
* reuse pi-ai's DeepSeek envApiKeyAuth here: newer pi-ai releases require a
* signal argument while older DSH adapters call auth resolvers without one.
* This small adapter accepts both contracts and keeps bearer/API-key values
* opaque to the provider implementation.
*/
function workBuddyApiKeyAuth() {
return {
name: `${DISPLAY_NAME} API Key`,
login: async (interaction) => {
const signal = interaction?.signal;
signal?.throwIfAborted?.();
const key = await interaction.prompt({ type: "secret", message: `Enter ${DISPLAY_NAME} API Key` });
signal?.throwIfAborted?.();
return { type: "api_key", key };
},
resolve: async ({ credential, signal } = {}) => {
signal?.throwIfAborted?.();
if (!credential?.key) return undefined;
return {
auth: { apiKey: credential.key },
...(credential.env ? { env: credential.env } : {}),
source: "DSH credential",
};
},
};
}
function workBuddyProvider(models, provider = PROVIDER) {
return createProvider({
id: provider,
name: DISPLAY_NAME,
baseUrl: BASE_URL,
auth: { apiKey: workBuddyApiKeyAuth() },
models: models.map((model) => ({ ...model, provider })),
api: workBuddyApi,
});
}
const GENERIC_APIS = Object.freeze({
"openai-completions": openAICompletionsApi,
"openai-responses": openAIResponsesApi,
"anthropic-messages": anthropicMessagesApi,
});
const GENERIC_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
function genericApiKeyAuth(provider) {
return {
name: `${provider} API Key`,
resolve: async ({ credential, signal } = {}) => {
signal?.throwIfAborted?.();
if (!credential?.key) return undefined;
return { auth: { apiKey: credential.key }, source: "DSH credential" };
},
};
}
function genericModel(provider, source, entry) {
const reasoningEfforts = entry.reasoningEfforts;
const reasoning = reasoningEfforts !== false && reasoningEfforts && typeof reasoningEfforts === "object";
const thinkingLevelMap = reasoning
? Object.fromEntries(GENERIC_LEVELS.filter((level) => Object.hasOwn(reasoningEfforts, level)).map((level) => [level, reasoningEfforts[level]]))
: undefined;
return {
id: entry.id,
name: entry.name ?? entry.id,
api: source.api,
provider,
baseUrl: source.baseURL,
input: Array.isArray(entry.input) && entry.input.length > 0 ? [...entry.input] : [...source.defaultInput ?? ["text"]],
cost: { ...NO_COST },
contextWindow: entry.contextWindow ?? source.defaultContextWindow ?? 262144,
maxTokens: entry.maxTokens ?? source.defaultMaxTokens ?? 32768,
...(reasoning ? { reasoning: true, thinkingLevelMap } : {}),
...(entry.compat ?? source.compat ? { compat: { ...(source.compat ?? {}), ...(entry.compat ?? {}) } } : {}),
};
}
function genericProvider(provider, source = {}) {
const api = GENERIC_APIS[source.api];
if (!api || !source.baseURL || !Array.isArray(source.models) || source.models.length === 0) return undefined;
return createProvider({
id: provider,
name: source.displayName ?? provider,
baseUrl: source.baseURL,
headers: source.headers,
auth: { apiKey: genericApiKeyAuth(provider) },
models: source.models.map((entry) => genericModel(provider, source, entry)),
api,
});
}
function resolvedProfile(provider, source, piProvider, configuredMaxTokens = new Map(), requestContext) {
const apiKeyEnv = source.apiKeyEnv === undefined ? undefined : credentialRef(source.apiKeyEnv);
return {
...source,
headers: runtimeHeaders(source.headers, requestContext),
provider,
displayName: source.displayName ?? piProvider.name ?? provider,
// dsh-llm-pi-ai reads this map for every exact model during catalog
// resolution. WorkBuddy profiles have no per-model validation failures
// here, but must still provide the empty map for the shared adapter API.
modelErrors: new Map(),
...(apiKeyEnv === undefined ? {} : { apiKeyEnv }),
streamIdleTimeoutMs: source.streamIdleTimeoutMs ?? STREAM_IDLE_TIMEOUT_MS,
retryPolicy: resolveRetryPolicy(source.retryPolicy, `${name}: provider "${provider}" retryPolicy`),
configuredMaxTokens,
piProvider,
};
}
function selectBuiltinModels(base, entries) {
if (!Array.isArray(entries) || entries.length === 0) return base;
const byId = new Map(base.getModels().map((model) => [model.id, model]));
const selected = entries.flatMap((entry) => {
const model = byId.get(entry.id);
if (!model) return [];
return [{
...model,
...(entry.name ? { name: entry.name } : {}),
...(entry.contextWindow ? { contextWindow: entry.contextWindow } : {}),
...(entry.maxTokens ? { maxTokens: entry.maxTokens } : {}),
...(Array.isArray(entry.input) && entry.input.length ? { input: [...entry.input] } : {}),
}];
});
return { ...base, getModels: () => selected };
}
function selectWorkBuddyModels(base, entries) {
if (!Array.isArray(entries) || entries.length === 0) return base;
const byId = new Map(base.map((model) => [model.id, model]));
return entries.map((entry) => {
const model = byId.get(entry.id);
const reasoning = configuredReasoning(entry, model);
return workBuddyModel({
id: entry.id,
name: entry.name ?? model?.name ?? entry.id,
contextWindow: entry.contextWindow ?? model?.contextWindow ?? 262144,
maxTokens: entry.maxTokens ?? model?.maxTokens ?? 32768,
images: entry.input?.includes("image") ?? model?.input.includes("image") ?? false,
...reasoning,
});
});
}
function ownsProvider(provider, builtins, source) {
return WORKBUDDY_PROVIDERS.has(provider) || builtins.has(provider) || genericProvider(provider, source) !== undefined;
}
function runtimeHeaders(headers, requestContext) {
const base = { ...(headers ?? {}) };
if (!requestContext) return base;
return new Proxy(base, {
ownKeys(target) {
const extra = requestContext.getStore()?.headers ?? {};
return [...new Set([...Reflect.ownKeys(target), ...Reflect.ownKeys(extra)])];
},
getOwnPropertyDescriptor(target, property) {
const extra = requestContext.getStore()?.headers ?? {};
if (!Reflect.has(target, property) && !Reflect.has(extra, property)) return undefined;
return { configurable: true, enumerable: true, writable: true, value: this.get(target, property) };
},
get(target, property, receiver) {
const extra = requestContext.getStore()?.headers ?? {};
return Reflect.has(extra, property) ? extra[property] : Reflect.get(target, property, receiver);
},
});
}
function sessionBindingFor(routing, sessionId) {
if (!routing?.enabled) return undefined;
const id = typeof sessionId === "string" && sessionId.trim() ? sessionId.trim() : undefined;
return id && Object.hasOwn(routing.bindings, id) ? routing.bindings[id] : undefined;
}
function normalizedProviderName(value) {
return String(value ?? "")
.trim()
.toLowerCase()
.replace(/[^a-z0-9]+/g, "-")
.replace(/^-+|-+$/g, "");
}
function isWorkBuddyProviderName(value) {
const normalized = normalizedProviderName(value);
return normalized.length > 0 && WORKBUDDY_PROVIDER_PATTERN.test(normalized);
}
function directWorkBuddyProvider(value) {
const normalized = normalizedProviderName(value);
if (normalized === PROVIDER || normalized === LEGACY_PROVIDER) return normalized;
return undefined;
}
function interruptedToolTailAssistantIndex(messages) {
let tailIndex = messages.length - 1;
while (tailIndex >= 0 && messages[tailIndex]?.role === "system") tailIndex -= 1;
if (tailIndex < 0) return -1;
const tail = messages[tailIndex];
if (tail?.role !== "user" || !Array.isArray(tail.content)) return -1;
if (!tail.content.some((block) => block?.type === "tool-result" && block.isError === true)) return -1;
for (let index = tailIndex - 1; index >= 0; index -= 1) {
const message = messages[index];
if (message?.role !== "assistant") continue;
return Array.isArray(message.content) && message.content.some((block) => block?.type === "tool-call") ? index : -1;
}
return -1;
}
/**
* Make WorkBuddy replay metadata safe across direct and wrapped provider ids.
* The returned messages are request-only copies; durable session history is
* never rewritten. An interrupted tool result deliberately loses only the
* preceding assistant replayState so the model receives ordinary history.
*/
function normalizeWorkBuddyReplay(options) {
if (!Array.isArray(options?.messages)) return options;
const currentProvider = options.provider;
if (!isWorkBuddyProviderName(currentProvider)) return options;
const interruptedIndex = interruptedToolTailAssistantIndex(options.messages);
let changed = false;
const messages = options.messages.map((message, index) => {
const source = message?.source;
const state = source?.replayState;
if (message?.role !== "assistant" || !source || state?.kind !== "pi-ai" || state?.version !== 1) return message;
let nextSource = source;
const sourceProvider = source.provider;
const replayProvider = state.provider;
if (isWorkBuddyProviderName(sourceProvider) && isWorkBuddyProviderName(replayProvider)) {
const canonical = directWorkBuddyProvider(replayProvider)
?? directWorkBuddyProvider(sourceProvider)
?? directWorkBuddyProvider(currentProvider);
if (canonical && (sourceProvider !== canonical || replayProvider !== canonical)) {
nextSource = {
...nextSource,
provider: canonical,
replayState: { ...state, provider: canonical },
};
}
}
if (index === interruptedIndex && nextSource.replayState !== undefined) {
const { replayState: _ignored, ...withoutReplay } = nextSource;
nextSource = withoutReplay;
}
if (nextSource === source) return message;
changed = true;
return { ...message, source: nextSource };
});
return changed ? { ...options, messages } : options;
}
// The rc.6 pi-ai adapter rejects replay metadata it does not understand. A
// newer DSH may persist a v2 envelope, so let old adapters use the durable
// message content as provider-neutral history instead of failing the request.
function stripUnsupportedReplay(options) {
if (!Array.isArray(options?.messages)) return options;
let changed = false;
const messages = options.messages.map((message) => {
const source = message?.source;
const state = source?.replayState;
if (state === undefined || (state?.kind === "pi-ai" && state?.version === 1)) return message;
changed = true;
const { replayState: _ignored, ...sourceWithoutReplay } = source;
return { ...message, source: sourceWithoutReplay };
});
return changed ? { ...options, messages } : options;
}
function prepareWorkBuddyOptions(options, legacyReplay = true) {
const normalized = normalizeWorkBuddyReplay(options);
return legacyReplay ? stripUnsupportedReplay(normalized) : normalized;
}
function workBuddySource(config, source) {
const providers = providerSettings(config);
return Object.hasOwn(providers, PROVIDER) || Object.hasOwn(providers, LEGACY_PROVIDER)
? source
: { ...source, apiKeyEnv: source.apiKeyEnv ?? API_KEY_ENV };
}
function providerSettings(config) {
return typeof config?.providers?.get === "function" ? config.providers.get() : config?.providers ?? {};
}
function installSettingsCompat(ctx, ns, schema, entry, hooks) {
if (typeof entry?.providers?.get === "function") {
return ctx.inject(["settings"], (child) => {
child.effect(() => child.settings.configure({ auto: false }, ctx.fiber));
});
}
if (typeof dshSettings.installSettingsSection === "function") {
return dshSettings.installSettingsSection(ctx, ns, schema, entry, hooks);
}
return ctx.inject(["settings"], (settingsCtx) => {
if (!settingsCtx.settings || typeof settingsCtx.settings.installSection !== "function") {
throw new Error(`${name}: DSH settings service does not provide installSection`);
}
return settingsCtx.settings.installSection(ctx, ns, schema, entry, hooks);
});
}
export const __testing = Object.freeze({ authenticationHeaders, workBuddyApiKeyAuth, workBuddyRequestOptions, workBuddySource, providerSettings, genericProvider, modelsFromConfig, ownsProvider, runtimeHeaders, stripUnsupportedReplay, normalizeWorkBuddyReplay, prepareWorkBuddyOptions, selectWorkBuddyModels, sessionBindingFor });
export function apply(ctx, config) {
const modernSettings = typeof config?.providers?.get === "function";
const settingsNs = modernSettings ? ctx.fiber?.entry?.options?.id ?? name : NS;
installWorkBuddyWeb(ctx, settingsNs);
let current = () => config;
const requestContext = new AsyncLocalStorage();
let remoteModels;
let generation = 0;
let memoRaw;
let memoProviders;
let memoGeneration = -1;
let memoized;
const loginSessionPromises = new Map();
let remoteModelsKey;
const builtins = new Map(builtinProviders().map((provider) => [provider.id, provider]));
const effectiveConfig = () => {
const raw = current() ?? {};
const providers = providerSettings(raw);
const configured = providers[PROVIDER] ?? providers[LEGACY_PROVIDER];
return {
...raw,
providers: {
...providers,
[PROVIDER]: configured ?? { apiKeyEnv: API_KEY_ENV },
},
};
};
const profiles = () => {
const raw = effectiveConfig();
const configuredProviders = providerSettings(current());
if (memoRaw === current() && memoProviders === configuredProviders && memoGeneration === generation && memoized) return memoized;
const result = new Map();
for (const [provider, source] of Object.entries(raw.providers)) {
if (modernSettings && !WORKBUDDY_PROVIDERS.has(provider)) continue;
if (!ownsProvider(provider, builtins, source)) continue;
if (WORKBUDDY_PROVIDERS.has(provider)) {
const sourceWithAuth = workBuddySource(current(), source);
const models = selectWorkBuddyModels(remoteModels ?? FALLBACK_MODELS, source.models);
const configured = new Map((source.models ?? []).flatMap((model) =>
Number.isSafeInteger(model.maxTokens) && model.maxTokens > 0 ? [[model.id, model.maxTokens]] : [],
));
result.set(provider, resolvedProfile(provider, {
...sourceWithAuth,
headers: runtimeHeaders(sourceWithAuth.headers, requestContext),
displayName: DISPLAY_NAME,
}, workBuddyProvider(models, provider), configured, requestContext));
continue;
}
const base = builtins.get(provider);
if (!base) {
const generic = genericProvider(provider, source);
if (!generic) continue;
const configured = new Map((source.models ?? []).flatMap((model) =>
Number.isSafeInteger(model.maxTokens) && model.maxTokens > 0 ? [[model.id, model.maxTokens]] : [],
));
result.set(provider, resolvedProfile(provider, source, generic, configured));
continue;
}
const selected = selectBuiltinModels(base, source.models);
const configured = new Map((source.models ?? []).flatMap((model) =>
Number.isSafeInteger(model.maxTokens) && model.maxTokens > 0 ? [[model.id, model.maxTokens]] : [],
));
result.set(provider, resolvedProfile(provider, source, selected, configured));
}
memoRaw = current();
memoProviders = configuredProviders;
memoGeneration = generation;
memoized = result;
return result;
};
const readSessionRouting = async () => {
const credentials = ctx.get("credentials");
const env = launchEnvironmentOf(ctx);
const ref = credentialRef(WORKBUDDY_SESSION_ROUTING_REF);
const stored = await credentials?.resolve(ref);
const value = stored?.value ?? env.get(ref)?.value;
return value ? parseWorkBuddySessionRouting(value) : createWorkBuddySessionRoutingState();
};
let routingBindingQueue = Promise.resolve();
const persistDefaultSessionBinding = (sessionId, fallbackBinding) => {
const task = routingBindingQueue.then(async () => {
const latest = await readSessionRouting();
const existing = sessionBindingFor(latest, sessionId);
if (existing) return existing;
const binding = latest.lastUsed ?? fallbackBinding;
if (!binding) return undefined;
const credentials = ctx.get("credentials");
if (!credentials) throw new Error("DSH 凭据服务不可用,无法保存会话认证");
await credentials.set(credentialRef(WORKBUDDY_SESSION_ROUTING_REF), serializeWorkBuddySessionRouting({
...latest,
bindings: { ...latest.bindings, [sessionId]: binding },
}));
return binding;
});
routingBindingQueue = task.then(() => undefined, () => undefined);
return task;
};
const resolveLoginSession = async (requestedId) => {
const key = typeof requestedId === "string" && requestedId ? requestedId : "active";
let promise = loginSessionPromises.get(key);
if (!promise) {
promise = (async () => {
const credentials = ctx.get("credentials");
const env = launchEnvironmentOf(ctx);
const sessionsRef = credentialRef(WORKBUDDY_SESSIONS_REF);
const sessionRefs = [sessionsRef, credentialRef(LEGACY_SESSIONS_REF)];
let sessionsValue;
for (const ref of sessionRefs) {
const storedSessions = await credentials?.resolve(ref);
sessionsValue = storedSessions?.value ?? env.get(ref)?.value;
if (sessionsValue) break;
}
let store;
if (sessionsValue) {
store = parseWorkBuddySessions(sessionsValue);
} else {
const legacyRefs = [credentialRef(WORKBUDDY_SESSION_REF), credentialRef(LEGACY_SESSION_REF)];
let legacyValue;
for (const ref of legacyRefs) {
const storedLegacy = await credentials?.resolve(ref);
legacyValue = storedLegacy?.value ?? env.get(ref)?.value;
if (legacyValue) break;
}
if (!legacyValue) throw new Error("未找到 WorkBuddy 登录凭据");
store = createWorkBuddySessionStore([parseWorkBuddySession(legacyValue)]);
}
const active = typeof requestedId === "string" && requestedId
? store.sessions.find((entry) => entry.id === requestedId)
: activeWorkBuddySession(store);
if (!active) throw new Error("未找到 WorkBuddy 登录账号");
let session = active;
if (sessionNeedsRefresh(session)) {
const trace = traceContext.getStore();
const started = Date.now();
traceEvent(trace, "auth.refresh-start");
try {
session = { ...session, ...(await refreshWorkBuddySession(session)), updatedAt: Date.now() };
traceEvent(trace, "auth.refresh-done", { elapsedMs: Date.now() - started });
} catch (error) {
traceEvent(trace, "auth.refresh-error", { elapsedMs: Date.now() - started, ...errorFields(error) });
throw error;
}
const nextStore = {
...store,
sessions: store.sessions.map((entry) => entry.id === session.id ? session : entry),
};
await credentials?.set(sessionsRef, serializeWorkBuddySessions(nextStore));
if (nextStore.activeId === session.id) await credentials?.set(credentialRef(WORKBUDDY_SESSION_REF), serializeWorkBuddySession(session));
}
return { ...session, sessionId: active.id, expiresAt: sessionCacheDeadline(session) };
})().finally(() => {
loginSessionPromises.delete(key);
});
loginSessionPromises.set(key, promise);
}
return promise;
};
const resolveCredential = async (provider, profile, context = requestContext.getStore()) => {
context ??= {};
const ref = profile.apiKeyEnv;
const routing = WORKBUDDY_PROVIDERS.has(provider) ? await readSessionRouting() : createWorkBuddySessionRoutingState();
const sessionId = context?.sessionId ? String(context.sessionId) : undefined;
let binding = sessionBindingFor(routing, sessionId);
if (WORKBUDDY_PROVIDERS.has(provider) && routing.enabled && sessionId && !binding) {
let fallbackBinding;
if (!routing.lastUsed) {
if (ref) fallbackBinding = { mode: "api-key", apiKeyRef: ref };
else {
try {
const active = await resolveLoginSession();
fallbackBinding = { mode: "token", accountId: active.sessionId };
} catch (error) {
throw new LlmError(`${name}: 没有可用于当前会话的默认 WorkBuddy 凭证`, "MISSING_CREDENTIAL", { cause: error });
}
}
}
try {
binding = await persistDefaultSessionBinding(sessionId, fallbackBinding);
} catch (error) {
throw new LlmError(`${name}: 无法保存当前会话的 WorkBuddy 凭证绑定`, "MISSING_CREDENTIAL", { cause: error });
}
if (!binding) throw new LlmError(`${name}: 没有可用于当前会话的默认 WorkBuddy 凭证`, "MISSING_CREDENTIAL");
}
if (WORKBUDDY_PROVIDERS.has(provider) && binding?.mode === "token") {
let session;
try {
session = await resolveLoginSession(binding.accountId);
} catch (error) {
throw new LlmError(`${name}: 当前会话绑定的 WorkBuddy 登录账号不可用`, "MISSING_CREDENTIAL", { cause: error });
}
context.headers = {
...(session.account.userId ? { "X-User-Id": session.account.userId } : {}),
...(session.account.enterpriseId ? { "X-Enterprise-Id": session.account.enterpriseId, "X-Tenant-Id": session.account.enterpriseId } : {}),
...(session.auth.domain ? { "X-Domain": session.auth.domain } : {}),
};
return { value: assertUsableApiKey(session.auth.accessToken, name, "WorkBuddy login session"), kind: "bearer", sessionId: session.sessionId };
}
if (WORKBUDDY_PROVIDERS.has(provider) && binding?.mode === "api-key") {
const bindingRef = binding.apiKeyRef;
if (!bindingRef) throw new LlmError(`${name}: 当前会话绑定的 API Key 引用无效`, "MISSING_CREDENTIAL");
const stored = await ctx.get("credentials")?.resolve(credentialRef(bindingRef));
const value = stored?.value ?? launchEnvironmentOf(ctx).get(credentialRef(bindingRef))?.value;
if (value) return { value: assertUsableApiKey(value, name, bindingRef), kind: "api-key", ref: bindingRef };
throw new LlmError(`${name}: 当前会话绑定的 API Key 不可用`, "MISSING_CREDENTIAL");
}
if (!ref && WORKBUDDY_PROVIDERS.has(provider)) {
let session;
try {
session = await resolveLoginSession();
} catch (error) {
throw new LlmError(`${name}: 未找到可用的 WorkBuddy 登录令牌,请运行 dsh-llm-workbuddy login`, "MISSING_CREDENTIAL", { cause: error });
}
context.headers = {
...(session.account.userId ? { "X-User-Id": session.account.userId } : {}),
...(session.account.enterpriseId ? { "X-Enterprise-Id": session.account.enterpriseId, "X-Tenant-Id": session.account.enterpriseId } : {}),
...(session.auth.domain ? { "X-Domain": session.auth.domain } : {}),
};
return { value: assertUsableApiKey(session.auth.accessToken, name, "WorkBuddy login session"), kind: "bearer", sessionId: session.sessionId };
}
if (!ref) return { value: undefined, kind: "none" };
const stored = await ctx.get("credentials")?.resolve(ref);
let value = stored?.value ?? launchEnvironmentOf(ctx).get(ref)?.value;
if (!value && ref === API_KEY_ENV) {
const legacyRef = credentialRef(LEGACY_API_KEY_ENV);
const legacyStored = await ctx.get("credentials")?.resolve(legacyRef);
value = legacyStored?.value ?? launchEnvironmentOf(ctx).get(legacyRef)?.value;
}
if (value) return { value: assertUsableApiKey(value, name, ref), kind: "api-key", ref };
throw new LlmError(`${name}: Provider "${provider}" 缺少 API Key,请在 WebUI 的模型设置中填写`, "MISSING_CREDENTIAL");
};
const resolveApiKey = async (provider, profile) => {
const trace = WORKBUDDY_PROVIDERS.has(provider) ? traceContext.getStore() : undefined;
const started = Date.now();
traceEvent(trace, "auth.start");
try {
const credential = await resolveCredential(provider, profile);
traceEvent(trace, "auth.done", { elapsedMs: Date.now() - started, mode: credential.kind });
return credential.value;
} catch (error) {
traceEvent(trace, "auth.error", { elapsedMs: Date.now() - started, ...errorFields(error) });
throw error;
}
};
const adapter = new PiAiAdapter({
profiles,
resolveApiKey,
resolveAttachments: () => ctx.get("attachments"),
});
const refreshPromises = new Map();
const refreshWorkBuddyModels = async (provider, signal) => {
const profile = profiles().get(provider);
const credential = await resolveCredential(provider, profile);
const cacheKey = credential.kind === "bearer" ? `token:${credential.sessionId ?? "active"}` : `api:${credential.ref ?? API_KEY_ENV}`;
if (remoteModels && remoteModelsKey === cacheKey) return;
let pending = refreshPromises.get(cacheKey);
if (!pending) {
pending = (async () => {
remoteModels = await fetchWorkBuddyModels(credential, signal);
remoteModelsKey = cacheKey;
generation += 1;
})().finally(() => refreshPromises.delete(cacheKey));
refreshPromises.set(cacheKey, pending);
}
return pending;
};
const ensureWorkBuddyModel = async (provider, model, signal) => {
if (!WORKBUDDY_PROVIDERS.has(provider)) return;
if (profiles().get(provider)?.piProvider.getModels().some((entry) => entry.id === model)) return;
await refreshWorkBuddyModels(provider, signal);
};
const sessionScopedStream = (stream, options) => {
const context = { sessionId: options?.sessionId === undefined ? undefined : String(options.sessionId), headers: {}, id: ++nextTraceId };
const run = (action) => traceContext.run(context, () => requestContext.run(context, action));
const source = run(() => stream(options));
const iterator = source[Symbol.asyncIterator]();
return {
[Symbol.asyncIterator]() { return this; },
next(value) { return run(() => iterator.next(value)); },
return(value) { return run(() => iterator.return?.(value) ?? Promise.resolve({ done: true, value })); },
throw(error) { return run(() => iterator.throw?.(error) ?? Promise.reject(error)); },
};
};
const adapterStream = adapter.stream.bind(adapter);
const legacyAdapter = typeof adapter.prepareCall !== "function";
const invokeAdapterStream = (options) => adapterStream(prepareWorkBuddyOptions(options, legacyAdapter));
adapter.stream = (options) => sessionScopedStream(invokeAdapterStream, options);
// `prepareCall` was added after the DSH rc.6 adapter. Keep the direct
// `stream` path working on older hosts while wrapping prepared calls on
// newer hosts, whose runtime dispatches through the returned stream handle.
if (!legacyAdapter) {
const adapterPrepareCall = adapter.prepareCall.bind(adapter);
adapter.prepareCall = async (...args) => {
await ensureWorkBuddyModel(args[0], args[1], args[2]);
const prepared = await adapterPrepareCall(...args);
return {
...prepared,
stream: (options) => sessionScopedStream((preparedOptions) => prepared.stream(prepareWorkBuddyOptions(preparedOptions, false)), options),
};
};
} else {
// DSH 0.1.5 calls prepareCall unconditionally, while the rc.6 pi-ai
// adapter shipped without it. Keep that older adapter usable by exposing
// the same prepared-call shape from its existing methods.
adapter.prepareCall = async (provider, model, signal) => ({
model: await adapter.resolveModel(provider, model, signal),
stream: (options) => sessionScopedStream(invokeAdapterStream, options),
});
}
const resolveModel = adapter.resolveModel.bind(adapter);
adapter.resolveModel = async (provider, model, signal) => {
await ensureWorkBuddyModel(provider, model, signal);
const resolved = await resolveModel(provider, model, signal);
if (!WORKBUDDY_PROVIDERS.has(provider) || !resolved.reasoning) return resolved;
const configured = profiles().get(provider)?.piProvider.getModels().find((entry) => entry.id === model);
const effort = configured?.defaultReasoningEffort;
if (!effort || !resolved.reasoning.efforts.some((entry) => entry.id === effort)) return resolved;
return { ...resolved, reasoning: { ...resolved.reasoning, defaultEffort: effort } };
};
const listModels = adapter.listModels.bind(adapter);
adapter.listModels = async (provider) => {
if (WORKBUDDY_PROVIDERS.has(provider)) {
try {
await refreshWorkBuddyModels(provider);
} catch {
// Keep the built-in catalog available while the key or network is absent.
}
}
return listModels(provider);
};
const workBuddyDirectory = () => [{
provider: PROVIDER,
displayName: DISPLAY_NAME,
settingsNs,
settingsPath: ["providers", PROVIDER],
declared: false,
}];
const directoryEntries = () => modernSettings ? workBuddyDirectory() : [...workBuddyDirectory(), ...[...builtins.values()].flatMap((provider) => provider.auth?.apiKey ? [{
provider: provider.id,
displayName: provider.name,
settingsNs,
settingsPath: ["providers", provider.id],
declared: false,
}] : []), ...Object.entries(effectiveConfig().providers ?? {}).flatMap(([provider, source]) => {
if (WORKBUDDY_PROVIDERS.has(provider) || builtins.has(provider) || !genericProvider(provider, source)) return [];
return [{
provider,
displayName: source.displayName ?? provider,
settingsNs,
settingsPath: ["providers", provider],
declared: true,
}];
})];
let directory = ctx.llm.registerConfigurableProviders(directoryEntries());
let registration = ctx.llm.registerAdapter([...profiles().keys()], adapter);
ctx.llm.registerModelDiscovery(settingsNs, async (request, signal) => {
const discoverySignal = signal ?? request.signal;
if (WORKBUDDY_PROVIDERS.has(request.provider)) {
const profile = profiles().get(request.provider);
const credential = request.apiKey
? { value: request.apiKey, kind: "api-key", ref: API_KEY_ENV }
: await resolveCredential(request.provider, profile);
remoteModels = await fetchWorkBuddyModels(credential, discoverySignal);
remoteModelsKey = credential.kind === "bearer" ? `token:${credential.sessionId ?? "active"}` : `api:${credential.ref ?? API_KEY_ENV}`;
generation += 1;
return remoteModels.map((model) => ({
id: model.id,
name: model.name,
contextWindow: model.contextWindow,
maxTokens: model.maxTokens,
}));
}
const provider = builtins.get(request.provider);
if (!provider) return probeEndpoint({ ...request, signal: discoverySignal }, { profiles, resolveCredential });
return provider.getModels().map((model) => ({
id: model.id,
name: model.name,
contextWindow: model.contextWindow,
maxTokens: model.maxTokens,
}));
});
// Keep WorkBuddy out of the settings base layer so it appears in WebUI's
// "Add provider" dropdown. The runtime profile above still exists as the
// built-in implementation; selecting it only persists the credential ref.
const refreshRegistrations = () => {
memoRaw = undefined;
const providers = profiles();
registration.replace([...providers.keys()]);
directory.replace(directoryEntries());
};
if (modernSettings) ctx.on("loader/volatile-update", refreshRegistrations);
installSettingsCompat(ctx, settingsNs, Config, config ?? { providers: {} }, {
setSource(source) {
current = source;
},
onChange() {
refreshRegistrations();
},
});
}