@phuetz/code-buddy 1.1.0 → 1.2.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/README.md +160 -165
- package/dist/agent/autonomous/agentic-coding-contract.d.ts +6 -6
- package/dist/agent/codebuddy-agent.d.ts +1 -1
- package/dist/agent/codebuddy-agent.js +69 -14
- package/dist/agent/execution/agent-executor.js +122 -12
- package/dist/agent/execution/tool-dependency-graph.js +7 -0
- package/dist/agent/execution/tool-hooks.d.ts +6 -0
- package/dist/agent/execution/tool-hooks.js +13 -5
- package/dist/agent/execution/tool-selection-strategy.d.ts +11 -2
- package/dist/agent/execution/tool-selection-strategy.js +67 -10
- package/dist/agent/extended-thinking.d.ts +8 -0
- package/dist/agent/extended-thinking.js +27 -0
- package/dist/agent/hermes-browser-backends.js +151 -11
- package/dist/agent/hermes-claw-migrate.d.ts +59 -0
- package/dist/agent/hermes-claw-migrate.js +427 -26
- package/dist/agent/hermes-parity-manifest.js +78 -71
- package/dist/agent/hermes-runtime-lifecycle.d.ts +67 -0
- package/dist/agent/hermes-runtime-lifecycle.js +468 -0
- package/dist/agent/middleware/index.d.ts +1 -0
- package/dist/agent/middleware/index.js +1 -0
- package/dist/agent/middleware/visual-validation-middleware.d.ts +17 -0
- package/dist/agent/middleware/visual-validation-middleware.js +67 -0
- package/dist/agent/model-benchmark.d.ts +77 -0
- package/dist/agent/model-benchmark.js +309 -0
- package/dist/agent/model-tier.d.ts +14 -0
- package/dist/agent/model-tier.js +71 -0
- package/dist/agent/multi-agent/agents/orchestrator-agent.js +17 -6
- package/dist/agent/multi-agent/multi-agent-system.js +3 -1
- package/dist/agent/multi-agent/session-fleet-bridge.d.ts +2 -0
- package/dist/agent/multi-agent/session-fleet-bridge.js +9 -1
- package/dist/agent/multi-agent/session-tools.js +22 -5
- package/dist/agent/reasoning/mcts.js +20 -6
- package/dist/agent/session-end-flush.d.ts +6 -1
- package/dist/agent/session-end-flush.js +20 -4
- package/dist/agent/streaming/streaming-handler.d.ts +7 -0
- package/dist/agent/streaming/streaming-handler.js +8 -1
- package/dist/agent/tool-executor.js +1 -0
- package/dist/agent/tool-handler.d.ts +5 -0
- package/dist/agent/tool-handler.js +130 -4
- package/dist/browser-automation/browser-operator-executor.js +15 -0
- package/dist/browser-automation/browser-use-runner.d.ts +96 -0
- package/dist/browser-automation/browser-use-runner.js +492 -0
- package/dist/browser-automation/camofox-runner.d.ts +107 -0
- package/dist/browser-automation/camofox-runner.js +287 -0
- package/dist/channels/dingtalk/index.js +3 -0
- package/dist/channels/discord/client.d.ts +9 -0
- package/dist/channels/discord/client.js +12 -0
- package/dist/channels/feishu/index.d.ts +167 -1
- package/dist/channels/feishu/index.js +432 -7
- package/dist/channels/gateway-lifecycle.d.ts +132 -0
- package/dist/channels/gateway-lifecycle.js +219 -0
- package/dist/channels/google-chat/index.d.ts +5 -1
- package/dist/channels/google-chat/index.js +5 -1
- package/dist/channels/imessage/index.d.ts +14 -0
- package/dist/channels/imessage/index.js +64 -17
- package/dist/channels/index.d.ts +4 -0
- package/dist/channels/index.js +4 -0
- package/dist/channels/irc/index.d.ts +73 -3
- package/dist/channels/irc/index.js +446 -11
- package/dist/channels/line/index.js +3 -0
- package/dist/channels/mattermost/index.d.ts +51 -4
- package/dist/channels/mattermost/index.js +303 -20
- package/dist/channels/nextcloud-talk/index.d.ts +89 -8
- package/dist/channels/nextcloud-talk/index.js +367 -16
- package/dist/channels/nostr/index.d.ts +121 -1
- package/dist/channels/nostr/index.js +396 -8
- package/dist/channels/ntfy/index.js +3 -0
- package/dist/channels/qq/index.js +3 -0
- package/dist/channels/slash-parity.d.ts +117 -0
- package/dist/channels/slash-parity.js +185 -0
- package/dist/channels/synology-chat/index.js +3 -0
- package/dist/channels/teams/index.d.ts +3 -0
- package/dist/channels/teams/index.js +3 -0
- package/dist/channels/telegram/client.d.ts +9 -0
- package/dist/channels/telegram/client.js +12 -0
- package/dist/channels/twilio-voice/index.js +3 -0
- package/dist/channels/webchat/index.d.ts +7 -1
- package/dist/channels/webchat/index.js +7 -1
- package/dist/channels/wecom/index.js +3 -0
- package/dist/channels/weixin/index.js +3 -0
- package/dist/channels/zalo/index.js +3 -0
- package/dist/cli/config-loader.js +1 -1
- package/dist/codebuddy/client.d.ts +30 -1
- package/dist/codebuddy/client.js +167 -20
- package/dist/codebuddy/providers/provider-chatgpt-responses.d.ts +5 -0
- package/dist/codebuddy/providers/provider-chatgpt-responses.js +1 -0
- package/dist/codebuddy/providers/provider-gemini-native.js +1 -1
- package/dist/codebuddy/providers/provider-openai-compat.d.ts +5 -0
- package/dist/codebuddy/providers/provider-openai-compat.js +82 -2
- package/dist/codebuddy/tool-definitions/computer-control-tools.js +32 -2
- package/dist/codebuddy/tool-definitions/core-tools.js +3 -3
- package/dist/codebuddy/tool-definitions/index.d.ts +2 -1
- package/dist/codebuddy/tool-definitions/index.js +2 -1
- package/dist/codebuddy/tool-definitions/multimodal-tools.d.ts +1 -0
- package/dist/codebuddy/tool-definitions/multimodal-tools.js +44 -1
- package/dist/codebuddy/tool-definitions/windows-tools.d.ts +2 -0
- package/dist/codebuddy/tool-definitions/windows-tools.js +34 -0
- package/dist/codebuddy/tools.d.ts +1 -1
- package/dist/codebuddy/tools.js +27 -24
- package/dist/commands/cli/hermes-commands.js +108 -0
- package/dist/commands/cli/native-engine-commands.js +102 -3
- package/dist/commands/cli/utility-commands.js +45 -0
- package/dist/commands/enhanced-command-handler.js +1 -1
- package/dist/commands/goal-cli.d.ts +30 -0
- package/dist/commands/goal-cli.js +210 -27
- package/dist/commands/handlers/goal-handler.d.ts +9 -2
- package/dist/commands/handlers/goal-handler.js +36 -9
- package/dist/commands/handlers/infra-handlers.js +36 -0
- package/dist/commands/handlers/memory-handlers.js +96 -1
- package/dist/commands/headless-slash.d.ts +2 -0
- package/dist/commands/headless-slash.js +12 -0
- package/dist/commands/llm-provider-resolution.d.ts +5 -3
- package/dist/commands/llm-provider-resolution.js +87 -33
- package/dist/commands/ollama.d.ts +25 -0
- package/dist/commands/ollama.js +100 -0
- package/dist/commands/provider.d.ts +5 -0
- package/dist/commands/provider.js +106 -55
- package/dist/commands/spec-next.js +2 -1
- package/dist/commands/spec-plan.js +5 -16
- package/dist/commands/tunnel.d.ts +2 -0
- package/dist/commands/tunnel.js +48 -0
- package/dist/config/config-resolver.d.ts +2 -1
- package/dist/config/config-resolver.js +54 -41
- package/dist/config/constants.d.ts +28 -0
- package/dist/config/constants.js +7 -0
- package/dist/config/env-schema.js +756 -0
- package/dist/config/model-tools.js +4 -4
- package/dist/config/toml-config.d.ts +16 -0
- package/dist/config/toml-config.js +3 -0
- package/dist/context/context-manager-v2.js +1 -0
- package/dist/daemon/autonomous-daemon.d.ts +1 -1
- package/dist/daemon/autonomous-daemon.js +3 -3
- package/dist/daemon/autonomous-loop.js +11 -2
- package/dist/daemon/colab-goal.js +9 -1
- package/dist/daemon/cron-agent-bridge.d.ts +12 -3
- package/dist/daemon/cron-agent-bridge.js +25 -9
- package/dist/desktop/codebuddy-engine-adapter.d.ts +32 -0
- package/dist/desktop/codebuddy-engine-adapter.js +257 -80
- package/dist/desktop/engine-adapter.d.ts +14 -0
- package/dist/desktop-automation/automation-manager.js +16 -0
- package/dist/desktop-automation/omniparser-runner.d.ts +63 -0
- package/dist/desktop-automation/omniparser-runner.js +115 -0
- package/dist/desktop-automation/smart-snapshot.d.ts +11 -0
- package/dist/desktop-automation/smart-snapshot.js +72 -5
- package/dist/doctor/index.js +40 -23
- package/dist/events/types.d.ts +11 -0
- package/dist/fleet/colab-store.js +14 -2
- package/dist/fleet/peer-session-bridge.js +50 -12
- package/dist/fleet/peer-tool-bridge.js +14 -0
- package/dist/goals/goal-decomposer.d.ts +27 -0
- package/dist/goals/goal-decomposer.js +293 -0
- package/dist/goals/goal-judge-client.d.ts +15 -0
- package/dist/goals/goal-judge-client.js +45 -0
- package/dist/goals/goal-judge.d.ts +2 -0
- package/dist/goals/goal-judge.js +35 -6
- package/dist/goals/goal-loop.d.ts +14 -0
- package/dist/goals/goal-loop.js +47 -2
- package/dist/goals/goal-manager.d.ts +6 -0
- package/dist/goals/goal-manager.js +67 -14
- package/dist/goals/goal-state.d.ts +13 -0
- package/dist/goals/goal-state.js +83 -13
- package/dist/goals/goal-store.d.ts +3 -0
- package/dist/goals/goal-store.js +31 -15
- package/dist/hooks/user-hooks.js +17 -3
- package/dist/index.js +464 -25
- package/dist/input/text-to-speech.d.ts +2 -6
- package/dist/input/text-to-speech.js +2 -27
- package/dist/integrations/screenpipe/screenpipe-client.d.ts +10 -0
- package/dist/integrations/screenpipe/screenpipe-client.js +12 -2
- package/dist/integrations/tailscale.d.ts +13 -0
- package/dist/integrations/tailscale.js +87 -34
- package/dist/knowledge/workspace-indexer.js +53 -9
- package/dist/mcp/client.js +1 -0
- package/dist/mcp/mcp-desktop-tools.d.ts +23 -0
- package/dist/mcp/mcp-desktop-tools.js +158 -0
- package/dist/mcp/mcp-server.js +2 -0
- package/dist/memory/enhanced-memory.d.ts +10 -0
- package/dist/memory/enhanced-memory.js +33 -2
- package/dist/memory/index.d.ts +3 -1
- package/dist/memory/index.js +4 -1
- package/dist/memory/memory-auto-proposer.d.ts +23 -0
- package/dist/memory/memory-auto-proposer.js +308 -0
- package/dist/memory/memory-candidate-queue.d.ts +92 -0
- package/dist/memory/memory-candidate-queue.js +261 -0
- package/dist/memory/persistent-memory.d.ts +45 -2
- package/dist/memory/persistent-memory.js +236 -40
- package/dist/ml/bayesian-qualifier.d.ts +1 -1
- package/dist/ml/bayesian-qualifier.js +35 -2
- package/dist/openclaw/gateway-bridge.js +5 -0
- package/dist/plugins/marketplace.d.ts +1 -0
- package/dist/plugins/marketplace.js +7 -0
- package/dist/prompts/execution-discipline.d.ts +14 -0
- package/dist/prompts/execution-discipline.js +29 -0
- package/dist/prompts/variation-injector.js +9 -2
- package/dist/protocols/acp/acp-agentic-runner.d.ts +7 -6
- package/dist/protocols/acp/acp-agentic-runner.js +115 -18
- package/dist/protocols/acp/acp-session-store.d.ts +23 -0
- package/dist/protocols/acp/acp-session-store.js +77 -0
- package/dist/protocols/acp/acp-stdio-server.d.ts +7 -0
- package/dist/protocols/acp/acp-stdio-server.js +71 -16
- package/dist/providers/active-llm-registry.d.ts +37 -0
- package/dist/providers/active-llm-registry.js +186 -0
- package/dist/providers/auxiliary-provider.d.ts +25 -0
- package/dist/providers/auxiliary-provider.js +192 -0
- package/dist/providers/codex-oauth.d.ts +1 -1
- package/dist/providers/codex-oauth.js +27 -4
- package/dist/providers/index.d.ts +3 -0
- package/dist/providers/index.js +4 -0
- package/dist/providers/provider-catalog.d.ts +70 -0
- package/dist/providers/provider-catalog.js +738 -0
- package/dist/providers/provider-fallback.d.ts +35 -0
- package/dist/providers/provider-fallback.js +171 -0
- package/dist/providers/xai-oauth.d.ts +128 -0
- package/dist/providers/xai-oauth.js +735 -0
- package/dist/scheduler/cron-scheduler.d.ts +15 -3
- package/dist/scheduler/cron-scheduler.js +53 -7
- package/dist/scheduler/script-runner.d.ts +2 -0
- package/dist/scheduler/script-runner.js +19 -4
- package/dist/scheduler/watchdog-handlers.js +7 -20
- package/dist/search/usearch-index.js +7 -2
- package/dist/security/tool-policy/tool-groups.js +2 -0
- package/dist/server/index.js +17 -2
- package/dist/server/routes/mobile.d.ts +12 -4
- package/dist/server/routes/mobile.js +116 -1
- package/dist/server/tls-config.d.ts +35 -0
- package/dist/server/tls-config.js +142 -0
- package/dist/server/tunnel-manager.d.ts +20 -0
- package/dist/server/tunnel-manager.js +58 -0
- package/dist/server/websocket/fleet-bridge.js +5 -0
- package/dist/services/prompt-builder.d.ts +1 -0
- package/dist/services/prompt-builder.js +49 -8
- package/dist/shared/engine-types.d.ts +15 -1
- package/dist/sidecar/sidecar-bridge.d.ts +1 -0
- package/dist/sidecar/sidecar-bridge.js +21 -10
- package/dist/spec/spec-store.js +8 -1
- package/dist/talk-mode/providers/audioreader-tts.d.ts +1 -1
- package/dist/talk-mode/providers/audioreader-tts.js +8 -8
- package/dist/templates/project-scaffolding.js +3 -2
- package/dist/themes/theme-schema.d.ts +10 -10
- package/dist/tools/application-profiles.js +38 -0
- package/dist/tools/bash/bash-tool.d.ts +1 -0
- package/dist/tools/bash/bash-tool.js +16 -2
- package/dist/tools/bash/command-validator.js +3 -0
- package/dist/tools/bash/rtk-rewrite.d.ts +16 -0
- package/dist/tools/bash/rtk-rewrite.js +100 -0
- package/dist/tools/bash/streaming-executor.js +14 -1
- package/dist/tools/computer-control-tool.d.ts +16 -1
- package/dist/tools/computer-control-tool.js +317 -5
- package/dist/tools/document-generator.d.ts +14 -0
- package/dist/tools/document-generator.js +79 -0
- package/dist/tools/execute-code-rpc-invoker.d.ts +26 -2
- package/dist/tools/execute-code-rpc-invoker.js +46 -5
- package/dist/tools/metadata.js +28 -0
- package/dist/tools/office-macro-tool.d.ts +10 -0
- package/dist/tools/office-macro-tool.js +93 -0
- package/dist/tools/registry/index.d.ts +2 -1
- package/dist/tools/registry/index.js +2 -1
- package/dist/tools/registry/memory-tools.d.ts +20 -0
- package/dist/tools/registry/memory-tools.js +225 -3
- package/dist/tools/registry/multimodal-tools.js +2 -2
- package/dist/tools/registry/vision-tools.d.ts +33 -0
- package/dist/tools/registry/vision-tools.js +308 -6
- package/dist/tools/registry/windows-tools.d.ts +2 -0
- package/dist/tools/registry/windows-tools.js +47 -0
- package/dist/tools/route-peer-tool.js +14 -0
- package/dist/tools/screenshot-tool.js +14 -2
- package/dist/tools/text-to-speech-tool.d.ts +1 -1
- package/dist/tools/text-to-speech-tool.js +2 -12
- package/dist/tools/tool-selector.js +3 -59
- package/dist/utils/config-validation/schema.d.ts +40 -7
- package/dist/utils/config-validation/schema.js +130 -1
- package/dist/utils/cost-tracker.js +21 -1
- package/dist/utils/disk-guard.d.ts +180 -0
- package/dist/utils/disk-guard.js +404 -0
- package/dist/utils/interactive-setup.js +35 -2
- package/dist/utils/model-utils.js +11 -2
- package/dist/utils/output-sanitizer.js +11 -0
- package/dist/utils/provider-detector.d.ts +8 -7
- package/dist/utils/provider-detector.js +19 -77
- package/dist/utils/settings-manager.d.ts +7 -0
- package/dist/utils/token-display.d.ts +3 -3
- package/dist/utils/token-display.js +22 -4
- package/dist/wizard/onboarding.d.ts +39 -0
- package/dist/wizard/onboarding.js +433 -21
- package/dist/wizard/provider-onboarding.d.ts +8 -23
- package/dist/wizard/provider-onboarding.js +39 -158
- package/package.json +4 -2
- package/dist/plugins/provider-onboarding.d.ts +0 -23
- package/dist/plugins/provider-onboarding.js +0 -116
- package/dist/utils/kokoro-tts.d.ts +0 -8
- package/dist/utils/kokoro-tts.js +0 -67
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
* backed by client `fs/*` + `session/request_permission`, and MCP passthrough.
|
|
31
31
|
*/
|
|
32
32
|
import { randomUUID } from 'crypto';
|
|
33
|
+
import { AcpSessionStore } from './acp-session-store.js';
|
|
33
34
|
export const ACP_PROTOCOL_VERSION = 1;
|
|
34
35
|
function asString(value) {
|
|
35
36
|
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
@@ -56,6 +57,8 @@ export class AcpStdioServer {
|
|
|
56
57
|
clientRequestTimeoutMs;
|
|
57
58
|
sessions = new Map();
|
|
58
59
|
pendingClientRequests = new Map();
|
|
60
|
+
store;
|
|
61
|
+
storeLoaded = false;
|
|
59
62
|
clientCapabilities = {};
|
|
60
63
|
nextClientRequestId = 0;
|
|
61
64
|
buffer = '';
|
|
@@ -68,6 +71,7 @@ export class AcpStdioServer {
|
|
|
68
71
|
this.agentInfo = options.agentInfo ?? { name: 'Code Buddy', title: 'Code Buddy', version: '1.0.0' };
|
|
69
72
|
this.protocolVersion = options.protocolVersion ?? ACP_PROTOCOL_VERSION;
|
|
70
73
|
this.clientRequestTimeoutMs = options.clientRequestTimeoutMs ?? 120_000;
|
|
74
|
+
this.store = options.store ?? new AcpSessionStore();
|
|
71
75
|
}
|
|
72
76
|
start() {
|
|
73
77
|
if (this.started)
|
|
@@ -100,9 +104,45 @@ export class AcpStdioServer {
|
|
|
100
104
|
sendUpdate(sessionId, update, options = {}) {
|
|
101
105
|
if (options.record !== false) {
|
|
102
106
|
this.sessions.get(sessionId)?.history.push(update);
|
|
107
|
+
void this.persistSession(sessionId);
|
|
103
108
|
}
|
|
104
109
|
this.write({ jsonrpc: '2.0', method: 'session/update', params: { sessionId, update } });
|
|
105
110
|
}
|
|
111
|
+
ensureStoreLoaded() {
|
|
112
|
+
if (this.storeLoaded)
|
|
113
|
+
return;
|
|
114
|
+
this.storeLoaded = true;
|
|
115
|
+
return this.store.listAll().then((loaded) => {
|
|
116
|
+
for (const s of loaded) {
|
|
117
|
+
if (!this.sessions.has(s.sessionId)) {
|
|
118
|
+
this.sessions.set(s.sessionId, {
|
|
119
|
+
cwd: s.cwd,
|
|
120
|
+
active: null,
|
|
121
|
+
history: s.history,
|
|
122
|
+
mcpServers: s.mcpServers,
|
|
123
|
+
title: s.title,
|
|
124
|
+
updatedAt: s.updatedAt,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}).catch((err) => {
|
|
129
|
+
this.storeLoaded = false;
|
|
130
|
+
throw err;
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
async persistSession(sessionId) {
|
|
134
|
+
const s = this.sessions.get(sessionId);
|
|
135
|
+
if (!s)
|
|
136
|
+
return;
|
|
137
|
+
await this.store.save({
|
|
138
|
+
sessionId,
|
|
139
|
+
cwd: s.cwd,
|
|
140
|
+
history: s.history,
|
|
141
|
+
mcpServers: s.mcpServers,
|
|
142
|
+
title: s.title,
|
|
143
|
+
updatedAt: s.updatedAt,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
106
146
|
requestClient(method, params = {}, options = {}) {
|
|
107
147
|
const clientCapabilities = options.clientCapabilities ?? this.clientCapabilities;
|
|
108
148
|
if (!canRequestClientWithCapabilities(method, clientCapabilities)) {
|
|
@@ -230,24 +270,32 @@ export class AcpStdioServer {
|
|
|
230
270
|
});
|
|
231
271
|
}
|
|
232
272
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
273
|
+
dispatch(method, params) {
|
|
274
|
+
const run = () => {
|
|
275
|
+
switch (method) {
|
|
276
|
+
case 'initialize':
|
|
277
|
+
return this.handleInitialize(params);
|
|
278
|
+
case 'session/new':
|
|
279
|
+
return this.handleNewSession(params);
|
|
280
|
+
case 'session/list':
|
|
281
|
+
return this.handleListSessions(params);
|
|
282
|
+
case 'session/load':
|
|
283
|
+
return this.handleLoadSession(params);
|
|
284
|
+
case 'session/prompt':
|
|
285
|
+
return this.handlePrompt(params);
|
|
286
|
+
default: {
|
|
287
|
+
const error = new Error(`Method not found: ${method}`);
|
|
288
|
+
error.code = -32601;
|
|
289
|
+
throw error;
|
|
290
|
+
}
|
|
249
291
|
}
|
|
292
|
+
};
|
|
293
|
+
if (method.startsWith('session/') && !this.storeLoaded) {
|
|
294
|
+
const p = this.ensureStoreLoaded();
|
|
295
|
+
if (p)
|
|
296
|
+
return p.then(run);
|
|
250
297
|
}
|
|
298
|
+
return run();
|
|
251
299
|
}
|
|
252
300
|
handleInitialize(params) {
|
|
253
301
|
if (params.protocolVersion !== this.protocolVersion) {
|
|
@@ -275,8 +323,10 @@ export class AcpStdioServer {
|
|
|
275
323
|
cwd,
|
|
276
324
|
active: null,
|
|
277
325
|
history: [],
|
|
326
|
+
mcpServers: params.mcpServers,
|
|
278
327
|
updatedAt: new Date().toISOString(),
|
|
279
328
|
});
|
|
329
|
+
void this.persistSession(sessionId);
|
|
280
330
|
return { sessionId };
|
|
281
331
|
}
|
|
282
332
|
handleListSessions(params) {
|
|
@@ -317,7 +367,10 @@ export class AcpStdioServer {
|
|
|
317
367
|
const cwd = optionalStringParam(params, 'cwd', 'Invalid session/load cwd');
|
|
318
368
|
if (cwd)
|
|
319
369
|
session.cwd = cwd;
|
|
370
|
+
if ('mcpServers' in params)
|
|
371
|
+
session.mcpServers = params.mcpServers;
|
|
320
372
|
session.updatedAt = new Date().toISOString();
|
|
373
|
+
void this.persistSession(sessionId);
|
|
321
374
|
for (const update of session.history) {
|
|
322
375
|
this.sendUpdate(sessionId, update, { record: false });
|
|
323
376
|
}
|
|
@@ -349,6 +402,7 @@ export class AcpStdioServer {
|
|
|
349
402
|
sessionId,
|
|
350
403
|
cwd: session.cwd,
|
|
351
404
|
clientCapabilities,
|
|
405
|
+
mcpServers: session.mcpServers,
|
|
352
406
|
canRequestClient: (method) => canRequestClientWithCapabilities(method, clientCapabilities),
|
|
353
407
|
prompt,
|
|
354
408
|
signal: controller.signal,
|
|
@@ -359,6 +413,7 @@ export class AcpStdioServer {
|
|
|
359
413
|
sendUpdate: (update) => this.sendUpdate(sessionId, update),
|
|
360
414
|
});
|
|
361
415
|
session.updatedAt = new Date().toISOString();
|
|
416
|
+
void this.persistSession(sessionId);
|
|
362
417
|
return { stopReason: controller.signal.aborted ? 'cancelled' : stopReason };
|
|
363
418
|
}
|
|
364
419
|
catch (err) {
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type RuntimeFallbackProvider } from './provider-fallback.js';
|
|
2
|
+
export type FailoverOrderPolicy = 'resilience' | 'free-first' | 'manual';
|
|
3
|
+
export interface ActiveLlm extends RuntimeFallbackProvider {
|
|
4
|
+
/** Local runtime (Ollama / LM Studio) — pushed last under the resilience policy. */
|
|
5
|
+
isLocal: boolean;
|
|
6
|
+
/** Best-effort: cloud-keyed/OAuth providers are assumed reachable; the reactive
|
|
7
|
+
* failover loop is the final arbiter, so this is advisory only. */
|
|
8
|
+
reachable: boolean;
|
|
9
|
+
/** Catalog priority (lower = preferred). */
|
|
10
|
+
priority: number;
|
|
11
|
+
/** Rough $/Mtok input cost for free-first ordering + the `/llm list` display. */
|
|
12
|
+
costInputUsdPerMtok: number;
|
|
13
|
+
}
|
|
14
|
+
export interface ActiveLlmRegistry {
|
|
15
|
+
/** Canonical id of the detected primary (never appears in `fallbacks`). */
|
|
16
|
+
primaryProvider: string | null;
|
|
17
|
+
/** Other active LLMs, ordered by policy — ready for `fallbackProviders`. */
|
|
18
|
+
fallbacks: ActiveLlm[];
|
|
19
|
+
/** Primary + fallbacks, for the `/llm list` surface. */
|
|
20
|
+
all: ActiveLlm[];
|
|
21
|
+
}
|
|
22
|
+
export interface BuildActiveLlmRegistryOptions {
|
|
23
|
+
/** The already-resolved session primary (from the index.ts bootstrap). */
|
|
24
|
+
primary?: {
|
|
25
|
+
provider?: string;
|
|
26
|
+
apiKey?: string;
|
|
27
|
+
baseURL?: string;
|
|
28
|
+
model?: string;
|
|
29
|
+
};
|
|
30
|
+
policy?: FailoverOrderPolicy;
|
|
31
|
+
manualOrder?: string[];
|
|
32
|
+
localOnly?: boolean;
|
|
33
|
+
env?: Record<string, string | undefined>;
|
|
34
|
+
force?: boolean;
|
|
35
|
+
}
|
|
36
|
+
export declare function orderFallbacks(list: ActiveLlm[], policy: FailoverOrderPolicy, manualOrder?: string[]): ActiveLlm[];
|
|
37
|
+
export declare function buildActiveLlmRegistry(opts?: BuildActiveLlmRegistryOptions): Promise<ActiveLlmRegistry>;
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Active-LLM registry — the set of providers/models the user is actually
|
|
3
|
+
* authenticated to (and that are reachable), used to drive auto-failover and
|
|
4
|
+
* "together" (ensemble) execution.
|
|
5
|
+
*
|
|
6
|
+
* This does NOT add new failover logic: it produces a `RuntimeFallbackProvider[]`
|
|
7
|
+
* that drops straight into `CodeBuddyClient`'s existing `fallbackProviders` slot
|
|
8
|
+
* (see `src/codebuddy/client.ts` `chatWithProviderFallback`). It joins existing
|
|
9
|
+
* resolvers — the provider catalog (`resolveProviderFromCatalog`), the ChatGPT /
|
|
10
|
+
* xAI OAuth credential checks, and the env-configured fallback list — rather than
|
|
11
|
+
* reinventing detection. The primary provider is passed in by the caller (the
|
|
12
|
+
* session bootstrap already resolved it) to avoid a circular import on
|
|
13
|
+
* `getDetectedProvider` in `src/index.ts`.
|
|
14
|
+
*/
|
|
15
|
+
import { getDirectRuntimeProviderCatalog, findRuntimeProvider, resolveProviderFromCatalog, } from './provider-catalog.js';
|
|
16
|
+
import { resolveRuntimeFallbackProviders, } from './provider-fallback.js';
|
|
17
|
+
import { hasCodexCredentials } from './codex-oauth.js';
|
|
18
|
+
import { logger } from '../utils/logger.js';
|
|
19
|
+
/** Rough input $/Mtok for ordering + display. 0 for local runtimes and
|
|
20
|
+
* subscription-OAuth backends (no per-token metering). */
|
|
21
|
+
const APPROX_COST_USD_PER_MTOK = {
|
|
22
|
+
chatgpt: 0,
|
|
23
|
+
ollama: 0,
|
|
24
|
+
lmstudio: 0,
|
|
25
|
+
'gemini-cli': 0,
|
|
26
|
+
grok: 0.5,
|
|
27
|
+
gemini: 0.3,
|
|
28
|
+
groq: 0.3,
|
|
29
|
+
mistral: 1,
|
|
30
|
+
anthropic: 3,
|
|
31
|
+
openai: 5,
|
|
32
|
+
};
|
|
33
|
+
function canonicalProviderId(provider) {
|
|
34
|
+
if (!provider)
|
|
35
|
+
return undefined;
|
|
36
|
+
return findRuntimeProvider(provider)?.id ?? provider.trim().toLowerCase();
|
|
37
|
+
}
|
|
38
|
+
/** Local runtimes that actually responded to a probe → their first real
|
|
39
|
+
* installed model (reuses the fleet capability registry's Ollama/LM Studio
|
|
40
|
+
* HTTP probes; `probeOllama` sets `id` to the actual model name). */
|
|
41
|
+
async function getReachableLocalModels(force) {
|
|
42
|
+
const map = new Map();
|
|
43
|
+
try {
|
|
44
|
+
const { getLocalCapabilities } = await import('../fleet/capability-registry.js');
|
|
45
|
+
const caps = await getLocalCapabilities({ force });
|
|
46
|
+
for (const m of caps.models) {
|
|
47
|
+
const id = canonicalProviderId(m.provider);
|
|
48
|
+
if ((id === 'ollama' || id === 'lmstudio') && !map.has(id)) {
|
|
49
|
+
map.set(id, m.id);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
/* no local runtimes reachable */
|
|
55
|
+
}
|
|
56
|
+
return map;
|
|
57
|
+
}
|
|
58
|
+
/** Resolve the xAI subscription-login bearer when there's no GROK_API_KEY,
|
|
59
|
+
* mirroring `getDetectedProvider` in src/index.ts. */
|
|
60
|
+
async function resolveXaiOAuthProvider(entry, env) {
|
|
61
|
+
try {
|
|
62
|
+
const { hasXaiCredentials, getValidXaiAccessToken } = await import('./xai-oauth.js');
|
|
63
|
+
if (!hasXaiCredentials())
|
|
64
|
+
return null;
|
|
65
|
+
const token = await getValidXaiAccessToken();
|
|
66
|
+
if (!token)
|
|
67
|
+
return null;
|
|
68
|
+
return {
|
|
69
|
+
provider: 'grok',
|
|
70
|
+
label: entry.label,
|
|
71
|
+
apiMode: entry.apiMode,
|
|
72
|
+
authMode: entry.authMode,
|
|
73
|
+
apiKey: token,
|
|
74
|
+
baseURL: 'https://api.x.ai/v1',
|
|
75
|
+
defaultModel: env.GROK_MODEL || 'grok-4-latest',
|
|
76
|
+
source: 'override',
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
logger.debug('xAI OAuth resolution skipped in active-llm-registry', {
|
|
81
|
+
error: err instanceof Error ? err.message : String(err),
|
|
82
|
+
});
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function toActiveLlm(resolved, entry) {
|
|
87
|
+
const isLocal = (entry?.authMode ?? resolved.authMode) === 'local';
|
|
88
|
+
return {
|
|
89
|
+
...resolved,
|
|
90
|
+
model: resolved.defaultModel,
|
|
91
|
+
rawSpec: `active:${resolved.provider}`,
|
|
92
|
+
fallbackSource: 'environment',
|
|
93
|
+
isLocal,
|
|
94
|
+
reachable: true,
|
|
95
|
+
priority: entry?.priority ?? 500,
|
|
96
|
+
costInputUsdPerMtok: APPROX_COST_USD_PER_MTOK[resolved.provider] ?? 1,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
export function orderFallbacks(list, policy, manualOrder) {
|
|
100
|
+
const sorted = [...list];
|
|
101
|
+
if (policy === 'free-first') {
|
|
102
|
+
sorted.sort((a, b) => a.costInputUsdPerMtok - b.costInputUsdPerMtok || a.priority - b.priority);
|
|
103
|
+
}
|
|
104
|
+
else if (policy === 'manual' && manualOrder && manualOrder.length > 0) {
|
|
105
|
+
const rank = (p) => {
|
|
106
|
+
const i = manualOrder.findIndex((m) => canonicalProviderId(m) === canonicalProviderId(p.provider));
|
|
107
|
+
return i === -1 ? Number.MAX_SAFE_INTEGER : i;
|
|
108
|
+
};
|
|
109
|
+
sorted.sort((a, b) => rank(a) - rank(b));
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
// resilience (default): capable cloud/subscription first (by catalog
|
|
113
|
+
// priority), local runtimes LAST as a free safety net.
|
|
114
|
+
sorted.sort((a, b) => Number(a.isLocal) - Number(b.isLocal) || a.priority - b.priority);
|
|
115
|
+
}
|
|
116
|
+
return sorted;
|
|
117
|
+
}
|
|
118
|
+
export async function buildActiveLlmRegistry(opts = {}) {
|
|
119
|
+
const env = opts.env ?? process.env;
|
|
120
|
+
const policy = opts.policy ?? 'resilience';
|
|
121
|
+
const hasOAuth = hasCodexCredentials();
|
|
122
|
+
const primaryProvider = canonicalProviderId(opts.primary?.provider) ?? null;
|
|
123
|
+
const catalog = getDirectRuntimeProviderCatalog();
|
|
124
|
+
const reachableLocalModels = await getReachableLocalModels(opts.force);
|
|
125
|
+
const all = [];
|
|
126
|
+
const seen = new Set();
|
|
127
|
+
for (const entry of catalog) {
|
|
128
|
+
let resolved = resolveProviderFromCatalog({
|
|
129
|
+
providerOverride: entry.id,
|
|
130
|
+
env,
|
|
131
|
+
hasChatGptOAuth: hasOAuth,
|
|
132
|
+
requireConfigured: true,
|
|
133
|
+
});
|
|
134
|
+
// xAI subscription login (no GROK_API_KEY) — the catalog can't see the
|
|
135
|
+
// OAuth token, so resolve it directly.
|
|
136
|
+
if (!resolved && entry.id === 'grok') {
|
|
137
|
+
resolved = await resolveXaiOAuthProvider(entry, env);
|
|
138
|
+
}
|
|
139
|
+
if (!resolved)
|
|
140
|
+
continue;
|
|
141
|
+
const isLocal = (entry.authMode ?? resolved.authMode) === 'local';
|
|
142
|
+
if (opts.localOnly && !isLocal)
|
|
143
|
+
continue;
|
|
144
|
+
// The catalog reports a local provider as "configured" even when it isn't
|
|
145
|
+
// running — only keep locals we actually probed as reachable, and use their
|
|
146
|
+
// real installed model (not the catalog default, which may not be pulled).
|
|
147
|
+
if (isLocal) {
|
|
148
|
+
const realModel = reachableLocalModels.get(canonicalProviderId(resolved.provider) ?? '');
|
|
149
|
+
if (!realModel)
|
|
150
|
+
continue;
|
|
151
|
+
resolved = { ...resolved, defaultModel: realModel };
|
|
152
|
+
}
|
|
153
|
+
const key = `${canonicalProviderId(resolved.provider)}:${resolved.baseURL.replace(/\/+$/, '')}`;
|
|
154
|
+
if (seen.has(key))
|
|
155
|
+
continue;
|
|
156
|
+
seen.add(key);
|
|
157
|
+
all.push(toActiveLlm(resolved, entry));
|
|
158
|
+
}
|
|
159
|
+
// Fallbacks = active set minus the primary provider.
|
|
160
|
+
const fallbacks = all.filter((p) => canonicalProviderId(p.provider) !== primaryProvider);
|
|
161
|
+
// Preserve any explicit env-configured fallbacks (CODEBUDDY_FALLBACK_PROVIDERS)
|
|
162
|
+
// — concat + dedupe so setting the env var never regresses.
|
|
163
|
+
for (const f of resolveRuntimeFallbackProviders({
|
|
164
|
+
env,
|
|
165
|
+
hasChatGptOAuth: hasOAuth,
|
|
166
|
+
active: opts.primary,
|
|
167
|
+
})) {
|
|
168
|
+
const key = `${canonicalProviderId(f.provider)}:${f.baseURL.replace(/\/+$/, '')}`;
|
|
169
|
+
if (seen.has(key))
|
|
170
|
+
continue;
|
|
171
|
+
seen.add(key);
|
|
172
|
+
fallbacks.push({
|
|
173
|
+
...f,
|
|
174
|
+
isLocal: false,
|
|
175
|
+
reachable: true,
|
|
176
|
+
priority: 900,
|
|
177
|
+
costInputUsdPerMtok: APPROX_COST_USD_PER_MTOK[f.provider] ?? 1,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
primaryProvider,
|
|
182
|
+
fallbacks: orderFallbacks(fallbacks, policy, opts.manualOrder),
|
|
183
|
+
all,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=active-llm-registry.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { CodeBuddyClient, type ChatOptions } from '../codebuddy/client.js';
|
|
2
|
+
import { type ResolvedRuntimeProvider } from './provider-catalog.js';
|
|
3
|
+
type EnvLike = Record<string, string | undefined>;
|
|
4
|
+
export type RuntimeAuxiliaryTask = 'vision' | 'browser_vision' | 'web_extract' | 'approval' | 'compression' | 'skills_hub' | 'mcp' | 'triage_specifier' | 'session_title';
|
|
5
|
+
export interface RuntimeAuxiliaryMainProvider extends ResolvedRuntimeProvider {
|
|
6
|
+
model?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface RuntimeAuxiliaryResolveOptions {
|
|
9
|
+
task: RuntimeAuxiliaryTask;
|
|
10
|
+
env?: EnvLike;
|
|
11
|
+
hasChatGptOAuth?: boolean;
|
|
12
|
+
mainProvider?: RuntimeAuxiliaryMainProvider | null;
|
|
13
|
+
defaultTimeoutMs?: number;
|
|
14
|
+
}
|
|
15
|
+
export interface ResolvedRuntimeAuxiliaryProvider extends ResolvedRuntimeProvider {
|
|
16
|
+
task: RuntimeAuxiliaryTask;
|
|
17
|
+
model: string;
|
|
18
|
+
timeoutMs: number;
|
|
19
|
+
providerSetting: string;
|
|
20
|
+
extraBody?: Record<string, unknown>;
|
|
21
|
+
}
|
|
22
|
+
export declare function resolveRuntimeAuxiliaryProvider(options: RuntimeAuxiliaryResolveOptions): ResolvedRuntimeAuxiliaryProvider | null;
|
|
23
|
+
export declare function createAuxiliaryCodeBuddyClient(provider: ResolvedRuntimeAuxiliaryProvider): CodeBuddyClient;
|
|
24
|
+
export declare function createAuxiliaryChatOptions(provider: ResolvedRuntimeAuxiliaryProvider, overrides?: ChatOptions): ChatOptions;
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { CodeBuddyClient, } from '../codebuddy/client.js';
|
|
2
|
+
import { hasCodexCredentials } from './codex-oauth.js';
|
|
3
|
+
import { findRuntimeProvider, resolveProviderFromCatalog, } from './provider-catalog.js';
|
|
4
|
+
const DEFAULT_AUXILIARY_TIMEOUT_MS = {
|
|
5
|
+
vision: 120_000,
|
|
6
|
+
browser_vision: 120_000,
|
|
7
|
+
web_extract: 360_000,
|
|
8
|
+
approval: 30_000,
|
|
9
|
+
compression: 120_000,
|
|
10
|
+
skills_hub: 30_000,
|
|
11
|
+
mcp: 30_000,
|
|
12
|
+
triage_specifier: 120_000,
|
|
13
|
+
session_title: 30_000,
|
|
14
|
+
};
|
|
15
|
+
const OPENROUTER_VISION_MODEL = 'google/gemini-2.5-flash';
|
|
16
|
+
export function resolveRuntimeAuxiliaryProvider(options) {
|
|
17
|
+
const env = options.env ?? process.env;
|
|
18
|
+
const config = readAuxiliaryEnvConfig(options.task, env);
|
|
19
|
+
const providerSetting = (config.provider || 'auto').trim().toLowerCase();
|
|
20
|
+
const timeoutMs = config.timeoutMs ?? options.defaultTimeoutMs ?? DEFAULT_AUXILIARY_TIMEOUT_MS[options.task];
|
|
21
|
+
const hasOAuth = options.hasChatGptOAuth ?? hasCodexCredentials();
|
|
22
|
+
if (providerSetting === 'main') {
|
|
23
|
+
return resolveFromMainProvider(options, config, timeoutMs, providerSetting);
|
|
24
|
+
}
|
|
25
|
+
if (providerSetting === 'auto') {
|
|
26
|
+
const autoResolved = resolveAutoAuxiliaryProvider(options, config, timeoutMs, hasOAuth);
|
|
27
|
+
if (autoResolved)
|
|
28
|
+
return autoResolved;
|
|
29
|
+
return resolveFromMainProvider(options, config, timeoutMs, providerSetting);
|
|
30
|
+
}
|
|
31
|
+
return resolveSpecificAuxiliaryProvider(options.task, providerSetting, env, config, timeoutMs, hasOAuth);
|
|
32
|
+
}
|
|
33
|
+
export function createAuxiliaryCodeBuddyClient(provider) {
|
|
34
|
+
return new CodeBuddyClient(provider.apiKey, provider.model, provider.baseURL, {
|
|
35
|
+
enableFallbacks: false,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
export function createAuxiliaryChatOptions(provider, overrides = {}) {
|
|
39
|
+
return {
|
|
40
|
+
...overrides,
|
|
41
|
+
model: overrides.model ?? provider.model,
|
|
42
|
+
timeoutMs: overrides.timeoutMs ?? provider.timeoutMs,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function resolveAutoAuxiliaryProvider(options, config, timeoutMs, hasOAuth) {
|
|
46
|
+
if ((options.task === 'vision' || options.task === 'browser_vision') && hasEnvValue(options.env ?? process.env, 'OPENROUTER_API_KEY')) {
|
|
47
|
+
return resolveSpecificAuxiliaryProvider(options.task, 'openrouter', options.env ?? process.env, {
|
|
48
|
+
...config,
|
|
49
|
+
model: config.model || OPENROUTER_VISION_MODEL,
|
|
50
|
+
}, timeoutMs, hasOAuth);
|
|
51
|
+
}
|
|
52
|
+
if (options.mainProvider) {
|
|
53
|
+
return resolveFromMainProvider(options, config, timeoutMs, 'auto');
|
|
54
|
+
}
|
|
55
|
+
const resolved = resolveProviderFromCatalog({
|
|
56
|
+
env: options.env,
|
|
57
|
+
hasChatGptOAuth: hasOAuth,
|
|
58
|
+
requireConfigured: true,
|
|
59
|
+
});
|
|
60
|
+
if (!resolved)
|
|
61
|
+
return null;
|
|
62
|
+
return finalizeAuxiliaryProvider(options.task, resolved, config, timeoutMs, 'auto');
|
|
63
|
+
}
|
|
64
|
+
function resolveFromMainProvider(options, config, timeoutMs, providerSetting) {
|
|
65
|
+
const mainProvider = options.mainProvider;
|
|
66
|
+
if (!mainProvider)
|
|
67
|
+
return null;
|
|
68
|
+
return finalizeAuxiliaryProvider(options.task, mainProvider, config, timeoutMs, providerSetting);
|
|
69
|
+
}
|
|
70
|
+
function resolveSpecificAuxiliaryProvider(task, providerSetting, env, config, timeoutMs, hasOAuth) {
|
|
71
|
+
const entry = findRuntimeProvider(providerSetting);
|
|
72
|
+
if (!entry || entry.runtimeSupport !== 'direct')
|
|
73
|
+
return null;
|
|
74
|
+
const envOverlay = { ...env };
|
|
75
|
+
if (config.apiKey && entry.apiKeyEnvKeys[0]) {
|
|
76
|
+
envOverlay[entry.apiKeyEnvKeys[0]] = config.apiKey;
|
|
77
|
+
}
|
|
78
|
+
if (config.baseURL && entry.baseUrlEnvKeys[0]) {
|
|
79
|
+
envOverlay[entry.baseUrlEnvKeys[0]] = config.baseURL;
|
|
80
|
+
}
|
|
81
|
+
if (config.model && entry.modelEnvKeys[0]) {
|
|
82
|
+
envOverlay[entry.modelEnvKeys[0]] = config.model;
|
|
83
|
+
}
|
|
84
|
+
const resolved = resolveProviderFromCatalog({
|
|
85
|
+
providerOverride: providerSetting,
|
|
86
|
+
env: envOverlay,
|
|
87
|
+
hasChatGptOAuth: hasOAuth,
|
|
88
|
+
requireConfigured: true,
|
|
89
|
+
});
|
|
90
|
+
if (!resolved)
|
|
91
|
+
return null;
|
|
92
|
+
return finalizeAuxiliaryProvider(task, resolved, config, timeoutMs, providerSetting);
|
|
93
|
+
}
|
|
94
|
+
function finalizeAuxiliaryProvider(task, provider, config, timeoutMs, providerSetting) {
|
|
95
|
+
return {
|
|
96
|
+
...provider,
|
|
97
|
+
apiKey: config.apiKey || provider.apiKey,
|
|
98
|
+
baseURL: (config.baseURL || provider.baseURL).replace(/\/+$/, ''),
|
|
99
|
+
defaultModel: config.model || provider.model || provider.defaultModel,
|
|
100
|
+
task,
|
|
101
|
+
model: config.model || provider.model || provider.defaultModel,
|
|
102
|
+
timeoutMs,
|
|
103
|
+
providerSetting,
|
|
104
|
+
...(config.extraBody ? { extraBody: config.extraBody } : {}),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function readAuxiliaryEnvConfig(task, env) {
|
|
108
|
+
const taskKey = task.toUpperCase();
|
|
109
|
+
const provider = readFirstEnv(env, [
|
|
110
|
+
`CODEBUDDY_AUXILIARY_${taskKey}_PROVIDER`,
|
|
111
|
+
`AUXILIARY_${taskKey}_PROVIDER`,
|
|
112
|
+
'CODEBUDDY_AUXILIARY_PROVIDER',
|
|
113
|
+
'AUXILIARY_PROVIDER',
|
|
114
|
+
]);
|
|
115
|
+
const model = readFirstEnv(env, [
|
|
116
|
+
`CODEBUDDY_AUXILIARY_${taskKey}_MODEL`,
|
|
117
|
+
`AUXILIARY_${taskKey}_MODEL`,
|
|
118
|
+
'CODEBUDDY_AUXILIARY_MODEL',
|
|
119
|
+
'AUXILIARY_MODEL',
|
|
120
|
+
]);
|
|
121
|
+
const baseURL = readFirstEnv(env, [
|
|
122
|
+
`CODEBUDDY_AUXILIARY_${taskKey}_BASE_URL`,
|
|
123
|
+
`AUXILIARY_${taskKey}_BASE_URL`,
|
|
124
|
+
'CODEBUDDY_AUXILIARY_BASE_URL',
|
|
125
|
+
'AUXILIARY_BASE_URL',
|
|
126
|
+
]);
|
|
127
|
+
const apiKey = readFirstEnv(env, [
|
|
128
|
+
`CODEBUDDY_AUXILIARY_${taskKey}_API_KEY`,
|
|
129
|
+
`AUXILIARY_${taskKey}_API_KEY`,
|
|
130
|
+
'CODEBUDDY_AUXILIARY_API_KEY',
|
|
131
|
+
'AUXILIARY_API_KEY',
|
|
132
|
+
]);
|
|
133
|
+
const timeoutEntry = readFirstEnvEntry(env, [
|
|
134
|
+
`CODEBUDDY_AUXILIARY_${taskKey}_TIMEOUT_MS`,
|
|
135
|
+
`AUXILIARY_${taskKey}_TIMEOUT_MS`,
|
|
136
|
+
`AUXILIARY_${taskKey}_TIMEOUT`,
|
|
137
|
+
'CODEBUDDY_AUXILIARY_TIMEOUT_MS',
|
|
138
|
+
'AUXILIARY_TIMEOUT_MS',
|
|
139
|
+
]);
|
|
140
|
+
const extraBodyRaw = readFirstEnv(env, [
|
|
141
|
+
`CODEBUDDY_AUXILIARY_${taskKey}_EXTRA_BODY`,
|
|
142
|
+
`AUXILIARY_${taskKey}_EXTRA_BODY`,
|
|
143
|
+
'CODEBUDDY_AUXILIARY_EXTRA_BODY',
|
|
144
|
+
'AUXILIARY_EXTRA_BODY',
|
|
145
|
+
]);
|
|
146
|
+
return {
|
|
147
|
+
...(provider ? { provider } : {}),
|
|
148
|
+
...(model ? { model } : {}),
|
|
149
|
+
...(baseURL ? { baseURL } : {}),
|
|
150
|
+
...(apiKey ? { apiKey } : {}),
|
|
151
|
+
...(timeoutEntry ? { timeoutMs: parseTimeoutMs(timeoutEntry.value, timeoutEntry.key.endsWith('_TIMEOUT')) } : {}),
|
|
152
|
+
...(extraBodyRaw ? { extraBody: parseExtraBody(extraBodyRaw) } : {}),
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
function readFirstEnv(env, keys) {
|
|
156
|
+
for (const key of keys) {
|
|
157
|
+
const value = env[key]?.trim();
|
|
158
|
+
if (value)
|
|
159
|
+
return value;
|
|
160
|
+
}
|
|
161
|
+
return undefined;
|
|
162
|
+
}
|
|
163
|
+
function readFirstEnvEntry(env, keys) {
|
|
164
|
+
for (const key of keys) {
|
|
165
|
+
const value = env[key]?.trim();
|
|
166
|
+
if (value)
|
|
167
|
+
return { key, value };
|
|
168
|
+
}
|
|
169
|
+
return undefined;
|
|
170
|
+
}
|
|
171
|
+
function hasEnvValue(env, key) {
|
|
172
|
+
return readFirstEnv(env, [key]) !== undefined;
|
|
173
|
+
}
|
|
174
|
+
function parseTimeoutMs(value, seconds) {
|
|
175
|
+
const numeric = Number(value);
|
|
176
|
+
if (!Number.isFinite(numeric) || numeric <= 0)
|
|
177
|
+
return undefined;
|
|
178
|
+
return Math.round(seconds ? numeric * 1000 : numeric);
|
|
179
|
+
}
|
|
180
|
+
function parseExtraBody(value) {
|
|
181
|
+
try {
|
|
182
|
+
const parsed = JSON.parse(value);
|
|
183
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
184
|
+
return parsed;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
return undefined;
|
|
189
|
+
}
|
|
190
|
+
return undefined;
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=auxiliary-provider.js.map
|
|
@@ -109,7 +109,7 @@ declare function bindCallbackServer(ports: number[], handler: http.RequestListen
|
|
|
109
109
|
* Returns the parsed `ChatGptAuth` so callers can show the user their
|
|
110
110
|
* email/plan immediately. Throws on failure / timeout (5 minutes).
|
|
111
111
|
*/
|
|
112
|
-
export declare function loginInteractive(): Promise<ChatGptAuth>;
|
|
112
|
+
export declare function loginInteractive(openUrl?: (url: string) => void | Promise<void>): Promise<ChatGptAuth>;
|
|
113
113
|
/**
|
|
114
114
|
* Returns the current ChatGptAuth (with claims), refreshing the access
|
|
115
115
|
* token if `last_refresh` is older than 1 hour. Returns `null` when no
|
|
@@ -35,6 +35,9 @@ import { logger } from '../utils/logger.js';
|
|
|
35
35
|
const CLIENT_ID = 'app_EMoamEEZ73f0CkXaXp7hrann';
|
|
36
36
|
/** OAuth issuer — both authorize and token endpoints live under here. */
|
|
37
37
|
const ISSUER = 'https://auth.openai.com';
|
|
38
|
+
/** Hard ceiling on the token exchange/refresh fetches so a stalled IdP can't
|
|
39
|
+
* hang an inline refresh (runs on the first chat call when tokens are stale). */
|
|
40
|
+
const OAUTH_TOKEN_TIMEOUT_MS = 30_000;
|
|
38
41
|
/** Primary callback port. Must match OpenAI's allow-list of registered
|
|
39
42
|
* redirect URIs for the Codex CLI client_id. */
|
|
40
43
|
const CALLBACK_PORT = 1455;
|
|
@@ -189,6 +192,8 @@ async function exchangeCodeForTokens(code, codeVerifier, redirectUri) {
|
|
|
189
192
|
method: 'POST',
|
|
190
193
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
191
194
|
body: params.toString(),
|
|
195
|
+
// Bound the network await so a stalled IdP can't hang the caller indefinitely.
|
|
196
|
+
signal: AbortSignal.timeout(OAUTH_TOKEN_TIMEOUT_MS),
|
|
192
197
|
});
|
|
193
198
|
if (!response.ok) {
|
|
194
199
|
const body = await response.text().catch(() => '');
|
|
@@ -208,6 +213,9 @@ async function refreshTokens(refreshToken) {
|
|
|
208
213
|
refresh_token: refreshToken,
|
|
209
214
|
client_id: CLIENT_ID,
|
|
210
215
|
}),
|
|
216
|
+
// A stale-token refresh runs inline on the first chat call; bound it so a
|
|
217
|
+
// stalled IdP surfaces as a timeout error instead of an indefinite hang.
|
|
218
|
+
signal: AbortSignal.timeout(OAUTH_TOKEN_TIMEOUT_MS),
|
|
211
219
|
});
|
|
212
220
|
if (!response.ok) {
|
|
213
221
|
const body = await response.text().catch(() => '');
|
|
@@ -344,7 +352,7 @@ async function bindCallbackServer(ports, handler) {
|
|
|
344
352
|
* Returns the parsed `ChatGptAuth` so callers can show the user their
|
|
345
353
|
* email/plan immediately. Throws on failure / timeout (5 minutes).
|
|
346
354
|
*/
|
|
347
|
-
export
|
|
355
|
+
export function loginInteractive(openUrl) {
|
|
348
356
|
const pkce = generatePkce();
|
|
349
357
|
const state = randomState();
|
|
350
358
|
return new Promise((resolve, reject) => {
|
|
@@ -433,9 +441,24 @@ export async function loginInteractive() {
|
|
|
433
441
|
}, 5 * 60 * 1000);
|
|
434
442
|
// Open the browser. If it fails, log the URL so the user can
|
|
435
443
|
// copy-paste manually.
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
444
|
+
if (openUrl) {
|
|
445
|
+
try {
|
|
446
|
+
const res = openUrl(authUrl);
|
|
447
|
+
if (res instanceof Promise) {
|
|
448
|
+
res.catch(() => {
|
|
449
|
+
console.error(`Couldn't auto-open the browser. Open this URL manually:\n${authUrl}`);
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
catch {
|
|
454
|
+
console.error(`Couldn't auto-open the browser. Open this URL manually:\n${authUrl}`);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
else {
|
|
458
|
+
open(authUrl).catch(() => {
|
|
459
|
+
console.error(`Couldn't auto-open the browser. Open this URL manually:\n${authUrl}`);
|
|
460
|
+
});
|
|
461
|
+
}
|
|
439
462
|
})
|
|
440
463
|
.catch(reject);
|
|
441
464
|
});
|
|
@@ -16,6 +16,9 @@ export { ProviderManager, getProviderManager, resetProviderManager, autoConfigur
|
|
|
16
16
|
export * from './local-llm-provider.js';
|
|
17
17
|
export { ADDITIONAL_PROVIDERS, getAdditionalProvider, getProviderForModel, listAvailableProviders, listAllProviders, resolveProviderConfig, } from './additional-providers.js';
|
|
18
18
|
export type { ProviderConfig as AdditionalProviderConfig } from './additional-providers.js';
|
|
19
|
+
export * from './provider-catalog.js';
|
|
20
|
+
export * from './provider-fallback.js';
|
|
21
|
+
export * from './auxiliary-provider.js';
|
|
19
22
|
export { ProviderFallbackChain, getFallbackChain, resetFallbackChain, } from './fallback-chain.js';
|
|
20
23
|
export type { FallbackConfig, ProviderHealth, FallbackChainEvents, } from './fallback-chain.js';
|
|
21
24
|
export { SmartModelRouter, getSmartRouter, resetSmartRouter, } from './smart-router.js';
|