@defai.digital/automatosx 12.3.1 → 12.4.1
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 +1 -1
- package/dist/index.js +4123 -874
- package/dist/mcp/index.js +3756 -294
- package/package.json +4 -1
package/dist/mcp/index.js
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import * as path4 from 'path';
|
|
3
|
-
import path4__default, { dirname, join, extname as extname$1, basename, resolve, relative, isAbsolute, sep,
|
|
3
|
+
import path4__default, { dirname, join, extname as extname$1, basename, resolve, relative, isAbsolute, sep, delimiter, parse } from 'path';
|
|
4
4
|
import { fileURLToPath } from 'url';
|
|
5
5
|
import { mkdir, appendFile, readFile, readdir, writeFile, rename, unlink, copyFile, access, stat, realpath } from 'fs/promises';
|
|
6
6
|
import * as fs4 from 'fs';
|
|
7
7
|
import { existsSync, readFileSync, promises, mkdirSync, createWriteStream, writeFileSync, unlinkSync, constants } from 'fs';
|
|
8
8
|
import Database2 from 'better-sqlite3';
|
|
9
9
|
import { glob } from 'glob';
|
|
10
|
-
import { spawn, spawnSync } from 'child_process';
|
|
10
|
+
import { spawn, exec, spawnSync } from 'child_process';
|
|
11
11
|
import { z, ZodError } from 'zod';
|
|
12
12
|
import chalk4 from 'chalk';
|
|
13
13
|
import ora2 from 'ora';
|
|
14
14
|
import readline, { createInterface } from 'readline';
|
|
15
15
|
import { Mutex } from 'async-mutex';
|
|
16
|
+
import { promisify } from 'util';
|
|
16
17
|
import Ajv from 'ajv';
|
|
17
18
|
import addFormats from 'ajv-formats';
|
|
18
19
|
import os2, { cpus } from 'os';
|
|
@@ -37,6 +38,226 @@ var init_esm_shims = __esm({
|
|
|
37
38
|
"node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_tsx@4.20.6_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/esm_shims.js"() {
|
|
38
39
|
}
|
|
39
40
|
});
|
|
41
|
+
|
|
42
|
+
// src/core/validation-limits.ts
|
|
43
|
+
function isValidRelativePath(path7) {
|
|
44
|
+
if (!path7 || typeof path7 !== "string") {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
const normalizedPath = path7.replace(/\\/g, "/");
|
|
48
|
+
if (normalizedPath.startsWith("/")) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
if (normalizedPath.includes("..")) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
if (/^[a-zA-Z]:/.test(normalizedPath)) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
if (normalizedPath.startsWith("//")) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
function isValidCommand(command) {
|
|
63
|
+
if (!command || typeof command !== "string") {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
if (command.length > VALIDATION_LIMITS.MAX_COMMAND_LENGTH) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
if (!/^[a-z0-9_-]+$/i.test(command)) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
function isValidName(name) {
|
|
75
|
+
if (!name || typeof name !== "string") {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
if (name.length > VALIDATION_LIMITS.MAX_NAME_LENGTH) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
if (!/^[a-z0-9][a-z0-9-_]*$/i.test(name)) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
function isValidExtension(ext) {
|
|
87
|
+
if (!ext || typeof ext !== "string") {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
const normalized = ext.startsWith(".") ? ext : `.${ext}`;
|
|
91
|
+
if (normalized.length > 10 || normalized.length < 2) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
if (!/^\.[a-z0-9]+$/i.test(normalized)) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
function isPositiveInteger(value) {
|
|
100
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0;
|
|
101
|
+
}
|
|
102
|
+
function isNonNegativeInteger(value) {
|
|
103
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
104
|
+
}
|
|
105
|
+
var AX_PATHS, TIMEOUTS, DATABASE, VALIDATION_LIMITS;
|
|
106
|
+
var init_validation_limits = __esm({
|
|
107
|
+
"src/core/validation-limits.ts"() {
|
|
108
|
+
init_esm_shims();
|
|
109
|
+
AX_PATHS = {
|
|
110
|
+
/** Root directory for AutomatosX data */
|
|
111
|
+
ROOT: ".automatosx",
|
|
112
|
+
/** Log files directory */
|
|
113
|
+
LOGS: ".automatosx/logs",
|
|
114
|
+
/** Memory/database directory */
|
|
115
|
+
MEMORY: ".automatosx/memory",
|
|
116
|
+
/** Agent workspaces directory */
|
|
117
|
+
WORKSPACES: ".automatosx/workspaces",
|
|
118
|
+
/** Session data directory */
|
|
119
|
+
SESSIONS: ".automatosx/sessions",
|
|
120
|
+
/** Team configurations directory */
|
|
121
|
+
TEAMS: ".automatosx/teams",
|
|
122
|
+
/** Agent profiles directory */
|
|
123
|
+
AGENTS: ".automatosx/agents",
|
|
124
|
+
/** Workflow definitions directory */
|
|
125
|
+
WORKFLOWS: ".automatosx/workflows",
|
|
126
|
+
/** Abilities/skills directory */
|
|
127
|
+
ABILITIES: ".automatosx/abilities",
|
|
128
|
+
/** Checkpoints directory */
|
|
129
|
+
CHECKPOINTS: ".automatosx/checkpoints",
|
|
130
|
+
/** Cache directory */
|
|
131
|
+
CACHE: ".automatosx/cache",
|
|
132
|
+
/** Task database directory */
|
|
133
|
+
TASKS: ".automatosx/tasks",
|
|
134
|
+
/** Status files directory */
|
|
135
|
+
STATUS: ".automatosx/status",
|
|
136
|
+
/** Provider configurations directory */
|
|
137
|
+
PROVIDERS: ".automatosx/providers",
|
|
138
|
+
/** Iterate mode directory */
|
|
139
|
+
ITERATE: ".automatosx/iterate",
|
|
140
|
+
/** State directory (mode, provider overrides) */
|
|
141
|
+
STATE: ".automatosx/state",
|
|
142
|
+
/** Usage tracking directory */
|
|
143
|
+
USAGE: ".automatosx/usage",
|
|
144
|
+
/** Context store directory */
|
|
145
|
+
CONTEXT: ".automatosx/context",
|
|
146
|
+
/** Telemetry directory */
|
|
147
|
+
TELEMETRY: ".automatosx/telemetry",
|
|
148
|
+
/** Workspace index directory */
|
|
149
|
+
WORKSPACE: ".automatosx/workspace",
|
|
150
|
+
/** Config file paths */
|
|
151
|
+
CONFIG_YAML: ".automatosx/config.yaml",
|
|
152
|
+
CONFIG_JSON: ".automatosx/config.json"
|
|
153
|
+
};
|
|
154
|
+
TIMEOUTS = {
|
|
155
|
+
/** Default provider execution timeout (2 minutes) */
|
|
156
|
+
PROVIDER_DEFAULT: 12e4,
|
|
157
|
+
/** Provider detection/health check timeout (5 seconds) */
|
|
158
|
+
PROVIDER_DETECTION: 5e3,
|
|
159
|
+
/** Database busy timeout (5 seconds) */
|
|
160
|
+
DATABASE_BUSY: 5e3,
|
|
161
|
+
/** Provider cooldown after failure (30 seconds) */
|
|
162
|
+
PROVIDER_COOLDOWN: 3e4,
|
|
163
|
+
/** Circuit breaker recovery timeout (1 minute) */
|
|
164
|
+
CIRCUIT_BREAKER_RECOVERY: 6e4,
|
|
165
|
+
/** Circuit breaker failure window (5 minutes) */
|
|
166
|
+
CIRCUIT_BREAKER_WINDOW: 3e5,
|
|
167
|
+
/** Health check interval (5 minutes) */
|
|
168
|
+
HEALTH_CHECK_INTERVAL: 3e5,
|
|
169
|
+
/** Idle connection timeout (5 minutes) */
|
|
170
|
+
IDLE_CONNECTION: 3e5,
|
|
171
|
+
/** User decision/prompt timeout (10 minutes) */
|
|
172
|
+
USER_DECISION: 6e5,
|
|
173
|
+
/** Maximum execution timeout (1 hour) */
|
|
174
|
+
MAX_EXECUTION: 36e5,
|
|
175
|
+
/** Default global timeout (25 minutes) */
|
|
176
|
+
GLOBAL_DEFAULT: 15e5,
|
|
177
|
+
/** Config cache TTL (1 minute) */
|
|
178
|
+
CONFIG_CACHE_TTL: 6e4,
|
|
179
|
+
/** Score cache TTL (5 minutes) */
|
|
180
|
+
SCORE_CACHE_TTL: 3e5,
|
|
181
|
+
/** MCP health check interval (30 seconds) */
|
|
182
|
+
MCP_HEALTH_CHECK: 3e4,
|
|
183
|
+
/** CLI command confirmation delay (5 seconds) */
|
|
184
|
+
CLI_CONFIRM_DELAY: 5e3,
|
|
185
|
+
/** Kill switch confirmation delay (5 seconds) */
|
|
186
|
+
KILL_SWITCH_DELAY: 5e3,
|
|
187
|
+
/** Minimum rollout duration (1 hour) */
|
|
188
|
+
MIN_ROLLOUT_DURATION: 36e5,
|
|
189
|
+
/** Graceful shutdown timeout (30 seconds) */
|
|
190
|
+
GRACEFUL_SHUTDOWN: 3e4,
|
|
191
|
+
/** Package installation timeout (1 minute) */
|
|
192
|
+
PACKAGE_INSTALL: 6e4,
|
|
193
|
+
/** Quick CLI command timeout (3 seconds) */
|
|
194
|
+
QUICK_COMMAND: 3e3,
|
|
195
|
+
/** CLI version check timeout (10 seconds) */
|
|
196
|
+
VERSION_CHECK: 1e4
|
|
197
|
+
};
|
|
198
|
+
DATABASE = {
|
|
199
|
+
/** SQLite busy timeout in milliseconds */
|
|
200
|
+
BUSY_TIMEOUT: 5e3,
|
|
201
|
+
/** Maximum database connections in pool */
|
|
202
|
+
MAX_CONNECTIONS: 10,
|
|
203
|
+
/** Connection idle timeout (5 minutes) */
|
|
204
|
+
IDLE_TIMEOUT: 3e5,
|
|
205
|
+
/** Default query limit */
|
|
206
|
+
DEFAULT_QUERY_LIMIT: 1e3,
|
|
207
|
+
/** Maximum query limit */
|
|
208
|
+
MAX_QUERY_LIMIT: 1e4
|
|
209
|
+
};
|
|
210
|
+
VALIDATION_LIMITS = {
|
|
211
|
+
// Resource limits (prevent DoS)
|
|
212
|
+
MAX_ENTRIES: 1e6,
|
|
213
|
+
// 1 million entries (memory, cache)
|
|
214
|
+
MAX_TIMEOUT: 36e5,
|
|
215
|
+
// 1 hour (execution, delegation)
|
|
216
|
+
MAX_FILE_SIZE: 104857600,
|
|
217
|
+
// 100 MB (workspace, abilities)
|
|
218
|
+
MAX_CACHE_SIZE: 524288e3,
|
|
219
|
+
// 500 MB (cache storage)
|
|
220
|
+
MAX_SESSIONS: 1e4,
|
|
221
|
+
// 10k concurrent sessions
|
|
222
|
+
// Performance limits (prevent resource exhaustion)
|
|
223
|
+
MAX_CONCURRENT_AGENTS: 100,
|
|
224
|
+
// Maximum concurrent agents
|
|
225
|
+
MAX_CPU_PERCENT: 80,
|
|
226
|
+
// Maximum CPU usage percent
|
|
227
|
+
MAX_MEMORY_MB: 2048,
|
|
228
|
+
// Maximum memory usage in MB
|
|
229
|
+
// Config validation limits
|
|
230
|
+
MAX_CONFIG_FILE_SIZE: 1048576,
|
|
231
|
+
// 1MB max config file size
|
|
232
|
+
MAX_NAME_LENGTH: 50,
|
|
233
|
+
// Max name length for agents/providers
|
|
234
|
+
MAX_COMMAND_LENGTH: 100,
|
|
235
|
+
// Max command length
|
|
236
|
+
MAX_ARRAY_LENGTH: 1e4,
|
|
237
|
+
// Max array elements in config
|
|
238
|
+
// Path and file limits
|
|
239
|
+
MIN_FILE_SIZE: 1,
|
|
240
|
+
// Minimum file size in bytes
|
|
241
|
+
MIN_TIMEOUT: 1e3,
|
|
242
|
+
// Minimum timeout in ms (1 second)
|
|
243
|
+
MIN_INTERVAL: 100,
|
|
244
|
+
// Minimum interval in ms
|
|
245
|
+
MIN_BACKOFF_FACTOR: 1,
|
|
246
|
+
// Minimum backoff multiplier
|
|
247
|
+
MAX_BACKOFF_FACTOR: 5,
|
|
248
|
+
// Maximum backoff multiplier
|
|
249
|
+
MAX_TTL: 864e5,
|
|
250
|
+
// Maximum TTL in ms (24 hours)
|
|
251
|
+
MIN_BYTES: 1,
|
|
252
|
+
// Minimum bytes for file sizes
|
|
253
|
+
// Network limits
|
|
254
|
+
MIN_PORT: 1024,
|
|
255
|
+
// Minimum port number
|
|
256
|
+
MAX_PORT: 65535
|
|
257
|
+
// Maximum port number
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
});
|
|
40
261
|
function sanitizeObject(obj, maxDepth = 5, currentDepth = 0) {
|
|
41
262
|
if (currentDepth > maxDepth) {
|
|
42
263
|
return "[Max Depth Reached]";
|
|
@@ -581,7 +802,7 @@ function installExitHandlers() {
|
|
|
581
802
|
if (process.stderr.writable) {
|
|
582
803
|
process.stderr.end();
|
|
583
804
|
}
|
|
584
|
-
} catch (
|
|
805
|
+
} catch (_error) {
|
|
585
806
|
}
|
|
586
807
|
process.exit(signal === "SIGTERM" || signal === "SIGINT" ? 0 : 1);
|
|
587
808
|
};
|
|
@@ -602,6 +823,7 @@ var init_process_manager = __esm({
|
|
|
602
823
|
"src/shared/process/process-manager.ts"() {
|
|
603
824
|
init_esm_shims();
|
|
604
825
|
init_logger();
|
|
826
|
+
init_validation_limits();
|
|
605
827
|
ProcessManager = class {
|
|
606
828
|
childProcesses = /* @__PURE__ */ new Set();
|
|
607
829
|
isShuttingDown = false;
|
|
@@ -638,7 +860,7 @@ var init_process_manager = __esm({
|
|
|
638
860
|
*
|
|
639
861
|
* @param timeout Maximum time to wait for graceful shutdown (ms)
|
|
640
862
|
*/
|
|
641
|
-
async shutdown(timeout =
|
|
863
|
+
async shutdown(timeout = TIMEOUTS.PROVIDER_DETECTION) {
|
|
642
864
|
if (this.isShuttingDown) {
|
|
643
865
|
return;
|
|
644
866
|
}
|
|
@@ -767,9 +989,10 @@ var init_factory = __esm({
|
|
|
767
989
|
"src/core/database/factory.ts"() {
|
|
768
990
|
init_esm_shims();
|
|
769
991
|
init_logger();
|
|
992
|
+
init_validation_limits();
|
|
770
993
|
DEFAULT_OPTIONS = {
|
|
771
994
|
readonly: false,
|
|
772
|
-
busyTimeout:
|
|
995
|
+
busyTimeout: DATABASE.BUSY_TIMEOUT,
|
|
773
996
|
verbose: false,
|
|
774
997
|
enableWal: true,
|
|
775
998
|
createDir: true
|
|
@@ -1615,8 +1838,8 @@ var init_verbosity_manager = __esm({
|
|
|
1615
1838
|
}
|
|
1616
1839
|
});
|
|
1617
1840
|
function findOnPath(cmdBase) {
|
|
1618
|
-
const
|
|
1619
|
-
if (
|
|
1841
|
+
const isWindows = process.platform === "win32";
|
|
1842
|
+
if (isWindows) {
|
|
1620
1843
|
return findOnPathWindows(cmdBase);
|
|
1621
1844
|
}
|
|
1622
1845
|
return findOnPathUnix(cmdBase);
|
|
@@ -1964,9 +2187,10 @@ var init_streaming_progress_parser = __esm({
|
|
|
1964
2187
|
// src/providers/error-patterns.ts
|
|
1965
2188
|
function isQuotaError(error, providerName) {
|
|
1966
2189
|
const patterns = PROVIDER_ERROR_PATTERNS[providerName] || GENERIC_ERROR_PATTERNS;
|
|
1967
|
-
const
|
|
1968
|
-
const
|
|
1969
|
-
const
|
|
2190
|
+
const errorObj = error;
|
|
2191
|
+
const message = (errorObj?.message || "").toLowerCase();
|
|
2192
|
+
const code = errorObj?.code || "";
|
|
2193
|
+
const statusCode = errorObj?.status || errorObj?.statusCode;
|
|
1970
2194
|
if (patterns.quota.some((pattern) => message.includes(pattern.toLowerCase()))) {
|
|
1971
2195
|
return true;
|
|
1972
2196
|
}
|
|
@@ -1983,9 +2207,10 @@ function isQuotaError(error, providerName) {
|
|
|
1983
2207
|
}
|
|
1984
2208
|
function isRateLimitError(error, providerName) {
|
|
1985
2209
|
const patterns = PROVIDER_ERROR_PATTERNS[providerName] || GENERIC_ERROR_PATTERNS;
|
|
1986
|
-
const
|
|
1987
|
-
const
|
|
1988
|
-
const
|
|
2210
|
+
const errorObj = error;
|
|
2211
|
+
const message = (errorObj?.message || "").toLowerCase();
|
|
2212
|
+
const code = errorObj?.code || "";
|
|
2213
|
+
const statusCode = errorObj?.status || errorObj?.statusCode;
|
|
1989
2214
|
if (patterns.rateLimit.some((pattern) => message.includes(pattern.toLowerCase()))) {
|
|
1990
2215
|
return true;
|
|
1991
2216
|
}
|
|
@@ -2224,6 +2449,7 @@ var init_base_provider = __esm({
|
|
|
2224
2449
|
init_esm_shims();
|
|
2225
2450
|
init_logger();
|
|
2226
2451
|
init_errors();
|
|
2452
|
+
init_validation_limits();
|
|
2227
2453
|
init_cli_provider_detector();
|
|
2228
2454
|
init_provider_schemas();
|
|
2229
2455
|
init_streaming_progress_parser();
|
|
@@ -2266,15 +2492,16 @@ var init_base_provider = __esm({
|
|
|
2266
2492
|
DEBIAN_FRONTEND: "noninteractive"
|
|
2267
2493
|
};
|
|
2268
2494
|
/** Default CLI execution timeout in milliseconds */
|
|
2269
|
-
static DEFAULT_TIMEOUT_MS =
|
|
2495
|
+
static DEFAULT_TIMEOUT_MS = TIMEOUTS.PROVIDER_DEFAULT;
|
|
2270
2496
|
/** Time to wait after SIGTERM before escalating to SIGKILL */
|
|
2271
|
-
static SIGKILL_ESCALATION_MS =
|
|
2497
|
+
static SIGKILL_ESCALATION_MS = TIMEOUTS.PROVIDER_DETECTION;
|
|
2272
2498
|
config;
|
|
2273
2499
|
logger = logger;
|
|
2274
2500
|
health;
|
|
2275
2501
|
constructor(config) {
|
|
2276
2502
|
const providerName = config.name.toLowerCase();
|
|
2277
|
-
|
|
2503
|
+
const allowedNames = _BaseProvider.ALLOWED_PROVIDER_NAMES;
|
|
2504
|
+
if (!allowedNames.includes(providerName)) {
|
|
2278
2505
|
throw new ProviderError(
|
|
2279
2506
|
`Invalid provider name: ${config.name}. Allowed: ${_BaseProvider.ALLOWED_PROVIDER_NAMES.join(", ")}`,
|
|
2280
2507
|
"E1001" /* CONFIG_INVALID */
|
|
@@ -2319,8 +2546,8 @@ var init_base_provider = __esm({
|
|
|
2319
2546
|
const escapedPrompt = this.escapeShellArg(prompt);
|
|
2320
2547
|
const fullCommand = `${cliCommand} ${argsString}${escapedPrompt}`;
|
|
2321
2548
|
const commandLength = fullCommand.length;
|
|
2322
|
-
const
|
|
2323
|
-
const useStdin =
|
|
2549
|
+
const isWindows = process.platform === "win32";
|
|
2550
|
+
const useStdin = isWindows && commandLength > 7e3;
|
|
2324
2551
|
logger.debug(`Executing ${cliCommand} CLI with streaming support`, {
|
|
2325
2552
|
command: cliCommand,
|
|
2326
2553
|
promptLength: prompt.length,
|
|
@@ -2909,8 +3136,8 @@ ${fullPrompt}
|
|
|
2909
3136
|
* - Windows: hello"world → "hello\"world"
|
|
2910
3137
|
*/
|
|
2911
3138
|
escapeShellArg(arg) {
|
|
2912
|
-
const
|
|
2913
|
-
if (
|
|
3139
|
+
const isWindows = process.platform === "win32";
|
|
3140
|
+
if (isWindows) {
|
|
2914
3141
|
return '"' + arg.replace(/"/g, '\\"').replace(/%/g, "%%") + '"';
|
|
2915
3142
|
} else {
|
|
2916
3143
|
return "'" + arg.replace(/'/g, "'\\''") + "'";
|
|
@@ -3778,7 +4005,7 @@ var init_sdk_adapter = __esm({
|
|
|
3778
4005
|
this.codex = new this.sdkModule.Codex();
|
|
3779
4006
|
this.initialized = true;
|
|
3780
4007
|
logger.info("Codex SDK initialized");
|
|
3781
|
-
} catch (
|
|
4008
|
+
} catch (_error) {
|
|
3782
4009
|
throw new CodexError(
|
|
3783
4010
|
"CLI_NOT_FOUND" /* CLI_NOT_FOUND */,
|
|
3784
4011
|
"Codex SDK not available. Install with: npm install @openai/codex-sdk"
|
|
@@ -4050,117 +4277,3317 @@ var init_openai_provider = __esm({
|
|
|
4050
4277
|
getCLICommand() {
|
|
4051
4278
|
return "codex";
|
|
4052
4279
|
}
|
|
4053
|
-
getCLIArgs() {
|
|
4054
|
-
return ["--json"];
|
|
4280
|
+
getCLIArgs() {
|
|
4281
|
+
return ["--json"];
|
|
4282
|
+
}
|
|
4283
|
+
getMockResponse() {
|
|
4284
|
+
return "[Mock OpenAI/Codex Response]\n\nThis is a mock response for testing purposes.";
|
|
4285
|
+
}
|
|
4286
|
+
get capabilities() {
|
|
4287
|
+
return {
|
|
4288
|
+
supportsStreaming: true,
|
|
4289
|
+
supportsEmbedding: false,
|
|
4290
|
+
supportsVision: true,
|
|
4291
|
+
maxContextTokens: 128e3,
|
|
4292
|
+
supportedModels: ["codex"]
|
|
4293
|
+
};
|
|
4294
|
+
}
|
|
4295
|
+
getActiveMode() {
|
|
4296
|
+
return this.hybridAdapter?.getActiveMode() || null;
|
|
4297
|
+
}
|
|
4298
|
+
switchToCliMode() {
|
|
4299
|
+
this.hybridAdapter?.switchToCliMode();
|
|
4300
|
+
}
|
|
4301
|
+
async destroy() {
|
|
4302
|
+
this.isDestroyed = true;
|
|
4303
|
+
if (this.hybridAdapter) {
|
|
4304
|
+
await this.hybridAdapter.destroy();
|
|
4305
|
+
this.hybridAdapter = null;
|
|
4306
|
+
}
|
|
4307
|
+
}
|
|
4308
|
+
};
|
|
4309
|
+
}
|
|
4310
|
+
});
|
|
4311
|
+
|
|
4312
|
+
// src/providers/openai-provider-factory.ts
|
|
4313
|
+
var openai_provider_factory_exports = {};
|
|
4314
|
+
__export(openai_provider_factory_exports, {
|
|
4315
|
+
createOpenAIProviderAsync: () => createOpenAIProviderAsync,
|
|
4316
|
+
createOpenAIProviderSync: () => createOpenAIProviderSync,
|
|
4317
|
+
getIntegrationMode: () => getIntegrationMode,
|
|
4318
|
+
isCLIModeAvailable: () => isCLIModeAvailable,
|
|
4319
|
+
isSDKModeAvailable: () => isSDKModeAvailable
|
|
4320
|
+
});
|
|
4321
|
+
function createOpenAIProviderSync(config, integrationMode) {
|
|
4322
|
+
logger.debug("Created OpenAI CLI provider (v8.3.0: CLI-only)", {
|
|
4323
|
+
provider: config.name,
|
|
4324
|
+
mode: "cli"
|
|
4325
|
+
});
|
|
4326
|
+
return new OpenAIProvider(config);
|
|
4327
|
+
}
|
|
4328
|
+
async function createOpenAIProviderAsync(config, integrationMode, interactive = false) {
|
|
4329
|
+
logger.info("Creating OpenAI CLI provider (v8.3.0: CLI-only)", {
|
|
4330
|
+
provider: config.name,
|
|
4331
|
+
mode: "cli"
|
|
4332
|
+
});
|
|
4333
|
+
return new OpenAIProvider(config);
|
|
4334
|
+
}
|
|
4335
|
+
function getIntegrationMode(config) {
|
|
4336
|
+
return config.integration;
|
|
4337
|
+
}
|
|
4338
|
+
function isSDKModeAvailable() {
|
|
4339
|
+
const hasAPIKey = !!process.env.OPENAI_API_KEY;
|
|
4340
|
+
let hasSDK = false;
|
|
4341
|
+
try {
|
|
4342
|
+
if (typeof import.meta.resolve === "function") {
|
|
4343
|
+
import.meta.resolve("openai");
|
|
4344
|
+
hasSDK = true;
|
|
4345
|
+
} else {
|
|
4346
|
+
hasSDK = true;
|
|
4347
|
+
}
|
|
4348
|
+
} catch {
|
|
4349
|
+
hasSDK = false;
|
|
4350
|
+
}
|
|
4351
|
+
return hasAPIKey && hasSDK;
|
|
4352
|
+
}
|
|
4353
|
+
async function isCLIModeAvailable() {
|
|
4354
|
+
try {
|
|
4355
|
+
const { execSync } = await import('child_process');
|
|
4356
|
+
execSync("codex --version", {
|
|
4357
|
+
encoding: "utf-8",
|
|
4358
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
4359
|
+
timeout: TIMEOUTS.QUICK_COMMAND
|
|
4360
|
+
});
|
|
4361
|
+
return true;
|
|
4362
|
+
} catch {
|
|
4363
|
+
return false;
|
|
4364
|
+
}
|
|
4365
|
+
}
|
|
4366
|
+
var init_openai_provider_factory = __esm({
|
|
4367
|
+
"src/providers/openai-provider-factory.ts"() {
|
|
4368
|
+
init_esm_shims();
|
|
4369
|
+
init_openai_provider();
|
|
4370
|
+
init_logger();
|
|
4371
|
+
init_validation_limits();
|
|
4372
|
+
}
|
|
4373
|
+
});
|
|
4374
|
+
var FeatureFlagManager;
|
|
4375
|
+
var init_flag_manager = __esm({
|
|
4376
|
+
"src/core/feature-flags/flag-manager.ts"() {
|
|
4377
|
+
init_esm_shims();
|
|
4378
|
+
init_logger();
|
|
4379
|
+
FeatureFlagManager = class {
|
|
4380
|
+
flags = /* @__PURE__ */ new Map();
|
|
4381
|
+
storagePath;
|
|
4382
|
+
flagsDir;
|
|
4383
|
+
workspacePath;
|
|
4384
|
+
constructor(workspacePath = process.cwd()) {
|
|
4385
|
+
this.workspacePath = workspacePath;
|
|
4386
|
+
this.flagsDir = join(workspacePath, ".automatosx");
|
|
4387
|
+
this.storagePath = join(this.flagsDir, "feature-flags.json");
|
|
4388
|
+
this.loadFlags();
|
|
4389
|
+
}
|
|
4390
|
+
/**
|
|
4391
|
+
* Check if workspace has a storage directory
|
|
4392
|
+
*/
|
|
4393
|
+
hasStorage() {
|
|
4394
|
+
return existsSync(this.flagsDir);
|
|
4395
|
+
}
|
|
4396
|
+
/**
|
|
4397
|
+
* Get workspace path for diagnostics
|
|
4398
|
+
*/
|
|
4399
|
+
getWorkspacePath() {
|
|
4400
|
+
return this.workspacePath;
|
|
4401
|
+
}
|
|
4402
|
+
/**
|
|
4403
|
+
* Load flags from storage
|
|
4404
|
+
*/
|
|
4405
|
+
loadFlags() {
|
|
4406
|
+
try {
|
|
4407
|
+
if (!this.hasStorage()) {
|
|
4408
|
+
logger.debug("Feature flag storage not initialized", {
|
|
4409
|
+
workspacePath: this.workspacePath
|
|
4410
|
+
});
|
|
4411
|
+
return;
|
|
4412
|
+
}
|
|
4413
|
+
if (existsSync(this.storagePath)) {
|
|
4414
|
+
const data = readFileSync(this.storagePath, "utf-8");
|
|
4415
|
+
const flags = JSON.parse(data);
|
|
4416
|
+
for (const flag of flags) {
|
|
4417
|
+
this.flags.set(flag.name, flag);
|
|
4418
|
+
}
|
|
4419
|
+
logger.info("Feature flags loaded", {
|
|
4420
|
+
count: flags.length,
|
|
4421
|
+
path: this.storagePath
|
|
4422
|
+
});
|
|
4423
|
+
} else {
|
|
4424
|
+
logger.info("No feature flags file found, starting fresh");
|
|
4425
|
+
}
|
|
4426
|
+
} catch (error) {
|
|
4427
|
+
logger.error("Failed to load feature flags", {
|
|
4428
|
+
error: error.message
|
|
4429
|
+
});
|
|
4430
|
+
}
|
|
4431
|
+
}
|
|
4432
|
+
/**
|
|
4433
|
+
* Save flags to storage
|
|
4434
|
+
*/
|
|
4435
|
+
saveFlags() {
|
|
4436
|
+
try {
|
|
4437
|
+
this.ensureStorageDir();
|
|
4438
|
+
const flagsArray = Array.from(this.flags.values());
|
|
4439
|
+
const data = JSON.stringify(flagsArray, null, 2);
|
|
4440
|
+
writeFileSync(this.storagePath, data, "utf-8");
|
|
4441
|
+
} catch (error) {
|
|
4442
|
+
logger.error("Failed to save feature flags", {
|
|
4443
|
+
error: error.message
|
|
4444
|
+
});
|
|
4445
|
+
}
|
|
4446
|
+
}
|
|
4447
|
+
/**
|
|
4448
|
+
* Ensure storage directory exists before writing
|
|
4449
|
+
*/
|
|
4450
|
+
ensureStorageDir() {
|
|
4451
|
+
if (!existsSync(this.flagsDir)) {
|
|
4452
|
+
mkdirSync(this.flagsDir, { recursive: true });
|
|
4453
|
+
}
|
|
4454
|
+
}
|
|
4455
|
+
/**
|
|
4456
|
+
* Define a feature flag
|
|
4457
|
+
*/
|
|
4458
|
+
defineFlag(flag) {
|
|
4459
|
+
this.flags.set(flag.name, flag);
|
|
4460
|
+
this.saveFlags();
|
|
4461
|
+
logger.info("Feature flag defined", { name: flag.name });
|
|
4462
|
+
}
|
|
4463
|
+
/**
|
|
4464
|
+
* Check if flag is enabled
|
|
4465
|
+
*/
|
|
4466
|
+
isEnabled(flagName, context) {
|
|
4467
|
+
const flag = this.flags.get(flagName);
|
|
4468
|
+
if (!flag) return false;
|
|
4469
|
+
if (flag.killSwitch?.enabled) {
|
|
4470
|
+
logger.warn(`Feature flag ${flagName} killed`, {
|
|
4471
|
+
reason: flag.killSwitch.reason,
|
|
4472
|
+
timestamp: flag.killSwitch.timestamp
|
|
4473
|
+
});
|
|
4474
|
+
return false;
|
|
4475
|
+
}
|
|
4476
|
+
const envOverride = process.env[`FEATURE_${flagName.toUpperCase()}`];
|
|
4477
|
+
if (envOverride === "false" || envOverride === "0") {
|
|
4478
|
+
logger.warn(`Feature flag ${flagName} disabled by env var`);
|
|
4479
|
+
return false;
|
|
4480
|
+
}
|
|
4481
|
+
if (flag.rolloutPercentage !== void 0) {
|
|
4482
|
+
const hash = this.hashContext(context);
|
|
4483
|
+
const bucket = hash % 100;
|
|
4484
|
+
const enabled = bucket < flag.rolloutPercentage;
|
|
4485
|
+
return enabled;
|
|
4486
|
+
}
|
|
4487
|
+
return flag.enabled;
|
|
4488
|
+
}
|
|
4489
|
+
/**
|
|
4490
|
+
* Gradually increase rollout percentage
|
|
4491
|
+
*/
|
|
4492
|
+
async increaseRollout(flagName, newPercentage, options) {
|
|
4493
|
+
const flag = this.flags.get(flagName);
|
|
4494
|
+
if (!flag) throw new Error(`Flag ${flagName} not found`);
|
|
4495
|
+
const currentPercentage = flag.rolloutPercentage || 0;
|
|
4496
|
+
if (newPercentage > currentPercentage * 5 && newPercentage !== 100 && currentPercentage > 0) {
|
|
4497
|
+
throw new Error(
|
|
4498
|
+
`Rollout increase too aggressive: ${currentPercentage}% \u2192 ${newPercentage}%
|
|
4499
|
+
Maximum safe increase: ${currentPercentage * 5}%`
|
|
4500
|
+
);
|
|
4501
|
+
}
|
|
4502
|
+
if (options?.minimumDuration && flag.lastUpdated) {
|
|
4503
|
+
const timeSinceLastChange = Date.now() - flag.lastUpdated;
|
|
4504
|
+
if (timeSinceLastChange < options.minimumDuration) {
|
|
4505
|
+
throw new Error(
|
|
4506
|
+
`Must wait ${options.minimumDuration}ms before increasing rollout
|
|
4507
|
+
Time since last change: ${timeSinceLastChange}ms`
|
|
4508
|
+
);
|
|
4509
|
+
}
|
|
4510
|
+
}
|
|
4511
|
+
flag.rolloutPercentage = newPercentage;
|
|
4512
|
+
flag.lastUpdated = Date.now();
|
|
4513
|
+
this.flags.set(flagName, flag);
|
|
4514
|
+
this.saveFlags();
|
|
4515
|
+
logger.info(`Feature flag ${flagName} rollout increased`, {
|
|
4516
|
+
from: currentPercentage,
|
|
4517
|
+
to: newPercentage,
|
|
4518
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
4519
|
+
});
|
|
4520
|
+
}
|
|
4521
|
+
/**
|
|
4522
|
+
* Emergency kill switch
|
|
4523
|
+
*/
|
|
4524
|
+
async killSwitch(flagName, reason) {
|
|
4525
|
+
const flag = this.flags.get(flagName);
|
|
4526
|
+
if (!flag) throw new Error(`Flag ${flagName} not found`);
|
|
4527
|
+
flag.killSwitch = {
|
|
4528
|
+
enabled: true,
|
|
4529
|
+
reason,
|
|
4530
|
+
timestamp: Date.now(),
|
|
4531
|
+
by: process.env.USER || "unknown"
|
|
4532
|
+
};
|
|
4533
|
+
this.flags.set(flagName, flag);
|
|
4534
|
+
this.saveFlags();
|
|
4535
|
+
logger.error(`\u{1F6A8} KILL SWITCH ACTIVATED: ${flagName}`, {
|
|
4536
|
+
reason,
|
|
4537
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
4538
|
+
});
|
|
4539
|
+
}
|
|
4540
|
+
/**
|
|
4541
|
+
* Get all flags
|
|
4542
|
+
*/
|
|
4543
|
+
getAllFlags() {
|
|
4544
|
+
return Array.from(this.flags.values());
|
|
4545
|
+
}
|
|
4546
|
+
/**
|
|
4547
|
+
* Get a specific flag
|
|
4548
|
+
*/
|
|
4549
|
+
getFlag(flagName) {
|
|
4550
|
+
return this.flags.get(flagName);
|
|
4551
|
+
}
|
|
4552
|
+
/**
|
|
4553
|
+
* Hash context for deterministic bucketing
|
|
4554
|
+
*/
|
|
4555
|
+
hashContext(context) {
|
|
4556
|
+
const str = JSON.stringify(context || {});
|
|
4557
|
+
let hash = 0;
|
|
4558
|
+
for (let i = 0; i < str.length; i++) {
|
|
4559
|
+
const char = str.charCodeAt(i);
|
|
4560
|
+
hash = (hash << 5) - hash + char;
|
|
4561
|
+
hash = hash & hash;
|
|
4562
|
+
}
|
|
4563
|
+
return hash >>> 0;
|
|
4564
|
+
}
|
|
4565
|
+
};
|
|
4566
|
+
}
|
|
4567
|
+
});
|
|
4568
|
+
|
|
4569
|
+
// src/core/feature-flags/flags.ts
|
|
4570
|
+
function getFlagManager(workspacePath) {
|
|
4571
|
+
const path7 = process.cwd();
|
|
4572
|
+
if (!flagManagerInstances.has(path7)) {
|
|
4573
|
+
const manager = new FeatureFlagManager(path7);
|
|
4574
|
+
initializeFlags(manager);
|
|
4575
|
+
flagManagerInstances.set(path7, manager);
|
|
4576
|
+
}
|
|
4577
|
+
return flagManagerInstances.get(path7);
|
|
4578
|
+
}
|
|
4579
|
+
function initializeFlags(manager) {
|
|
4580
|
+
if (!manager.hasStorage()) {
|
|
4581
|
+
logger.debug("Skipping feature flag initialization; workspace not initialized", {
|
|
4582
|
+
workspacePath: manager.getWorkspacePath()
|
|
4583
|
+
});
|
|
4584
|
+
return;
|
|
4585
|
+
}
|
|
4586
|
+
initializeGeminiStreamingFlag(manager);
|
|
4587
|
+
initializeProviderArchitectureFlags(manager);
|
|
4588
|
+
}
|
|
4589
|
+
function initializeGeminiStreamingFlag(manager) {
|
|
4590
|
+
const existingFlag = manager.getFlag("gemini_streaming");
|
|
4591
|
+
if (!existingFlag) {
|
|
4592
|
+
manager.defineFlag({
|
|
4593
|
+
name: "gemini_streaming",
|
|
4594
|
+
description: "Enable Gemini as a valid option for streaming workloads",
|
|
4595
|
+
enabled: true,
|
|
4596
|
+
rolloutPercentage: 0,
|
|
4597
|
+
metadata: {
|
|
4598
|
+
owner: "platform-team",
|
|
4599
|
+
jiraTicket: "AUTO-1234",
|
|
4600
|
+
expectedImpact: "96% cost reduction on streaming tasks"
|
|
4601
|
+
}
|
|
4602
|
+
});
|
|
4603
|
+
logger.info("Feature flag gemini_streaming initialized at 0%");
|
|
4604
|
+
}
|
|
4605
|
+
}
|
|
4606
|
+
function initializeProviderArchitectureFlags(manager) {
|
|
4607
|
+
for (const flagDef of DEFAULT_PROVIDER_FLAGS) {
|
|
4608
|
+
const existingFlag = manager.getFlag(flagDef.name);
|
|
4609
|
+
if (!existingFlag) {
|
|
4610
|
+
manager.defineFlag(flagDef);
|
|
4611
|
+
logger.info(`Feature flag ${flagDef.name} initialized`, {
|
|
4612
|
+
enabled: flagDef.enabled,
|
|
4613
|
+
rolloutPercentage: flagDef.rolloutPercentage
|
|
4614
|
+
});
|
|
4615
|
+
}
|
|
4616
|
+
}
|
|
4617
|
+
}
|
|
4618
|
+
function isSDKFirstModeEnabled(context) {
|
|
4619
|
+
const manager = getFlagManager();
|
|
4620
|
+
return manager.isEnabled(PROVIDER_ARCHITECTURE_FLAGS.SDK_FIRST_MODE, context);
|
|
4621
|
+
}
|
|
4622
|
+
function isSDKFallbackEnabled() {
|
|
4623
|
+
const manager = getFlagManager();
|
|
4624
|
+
return manager.isEnabled(PROVIDER_ARCHITECTURE_FLAGS.SDK_FALLBACK_ENABLED);
|
|
4625
|
+
}
|
|
4626
|
+
function shouldCollectProviderMetrics() {
|
|
4627
|
+
const manager = getFlagManager();
|
|
4628
|
+
return manager.isEnabled(PROVIDER_ARCHITECTURE_FLAGS.PROVIDER_METRICS);
|
|
4629
|
+
}
|
|
4630
|
+
var flagManagerInstances, PROVIDER_ARCHITECTURE_FLAGS, DEFAULT_PROVIDER_FLAGS;
|
|
4631
|
+
var init_flags = __esm({
|
|
4632
|
+
"src/core/feature-flags/flags.ts"() {
|
|
4633
|
+
init_esm_shims();
|
|
4634
|
+
init_flag_manager();
|
|
4635
|
+
init_logger();
|
|
4636
|
+
flagManagerInstances = /* @__PURE__ */ new Map();
|
|
4637
|
+
PROVIDER_ARCHITECTURE_FLAGS = {
|
|
4638
|
+
/** Enable SDK-first execution pattern (SDK primary, CLI fallback) */
|
|
4639
|
+
SDK_FIRST_MODE: "sdk_first_mode",
|
|
4640
|
+
/** Enable MCP bidirectional communication for agentic providers */
|
|
4641
|
+
MCP_BIDIRECTIONAL: "mcp_bidirectional",
|
|
4642
|
+
/** Auto-inject MCP server config into provider config files */
|
|
4643
|
+
AUTO_INJECT_MCP_CONFIG: "auto_inject_mcp_config",
|
|
4644
|
+
/** Allow CLI fallback when SDK fails */
|
|
4645
|
+
SDK_FALLBACK_ENABLED: "sdk_fallback_enabled",
|
|
4646
|
+
/** Show deprecation warnings (legacy providers, etc.) */
|
|
4647
|
+
DEPRECATION_WARNINGS: "deprecation_warnings",
|
|
4648
|
+
/** Enable provider metrics collection */
|
|
4649
|
+
PROVIDER_METRICS: "provider_metrics"
|
|
4650
|
+
};
|
|
4651
|
+
DEFAULT_PROVIDER_FLAGS = [
|
|
4652
|
+
{
|
|
4653
|
+
name: PROVIDER_ARCHITECTURE_FLAGS.SDK_FIRST_MODE,
|
|
4654
|
+
description: "Enable SDK-first execution pattern (SDK primary, CLI fallback) for GLM, Grok, and Codex providers",
|
|
4655
|
+
enabled: false,
|
|
4656
|
+
// Disabled by default, enable in v11.7.0
|
|
4657
|
+
rolloutPercentage: 0,
|
|
4658
|
+
metadata: {
|
|
4659
|
+
owner: "platform-team",
|
|
4660
|
+
expectedImpact: "Lower latency (~5ms vs ~200ms), better type safety"
|
|
4661
|
+
}
|
|
4662
|
+
},
|
|
4663
|
+
{
|
|
4664
|
+
name: PROVIDER_ARCHITECTURE_FLAGS.MCP_BIDIRECTIONAL,
|
|
4665
|
+
description: "Enable MCP bidirectional communication for Claude and Gemini agents",
|
|
4666
|
+
enabled: false,
|
|
4667
|
+
// Disabled by default, enable in v11.8.0
|
|
4668
|
+
rolloutPercentage: 0,
|
|
4669
|
+
metadata: {
|
|
4670
|
+
owner: "platform-team",
|
|
4671
|
+
expectedImpact: "Faster agent communication via MCP instead of stdout parsing"
|
|
4672
|
+
}
|
|
4673
|
+
},
|
|
4674
|
+
{
|
|
4675
|
+
name: PROVIDER_ARCHITECTURE_FLAGS.AUTO_INJECT_MCP_CONFIG,
|
|
4676
|
+
description: "Automatically inject AutomatosX MCP server config into provider config files",
|
|
4677
|
+
enabled: false,
|
|
4678
|
+
// Disabled by default, requires careful testing
|
|
4679
|
+
rolloutPercentage: 0,
|
|
4680
|
+
metadata: {
|
|
4681
|
+
owner: "platform-team",
|
|
4682
|
+
expectedImpact: "Seamless MCP setup for agents"
|
|
4683
|
+
}
|
|
4684
|
+
},
|
|
4685
|
+
{
|
|
4686
|
+
name: PROVIDER_ARCHITECTURE_FLAGS.SDK_FALLBACK_ENABLED,
|
|
4687
|
+
description: "Allow CLI fallback when SDK execution fails",
|
|
4688
|
+
enabled: true,
|
|
4689
|
+
// Enabled by default for safety
|
|
4690
|
+
rolloutPercentage: 100,
|
|
4691
|
+
metadata: {
|
|
4692
|
+
owner: "platform-team",
|
|
4693
|
+
expectedImpact: "Graceful degradation when SDK unavailable"
|
|
4694
|
+
}
|
|
4695
|
+
},
|
|
4696
|
+
{
|
|
4697
|
+
name: PROVIDER_ARCHITECTURE_FLAGS.DEPRECATION_WARNINGS,
|
|
4698
|
+
description: "Show deprecation warnings for legacy features",
|
|
4699
|
+
enabled: true,
|
|
4700
|
+
// Enabled by default to warn users
|
|
4701
|
+
rolloutPercentage: 100,
|
|
4702
|
+
metadata: {
|
|
4703
|
+
owner: "platform-team",
|
|
4704
|
+
expectedImpact: "v13.0.0: ax-cli removed, GLM/Grok are SDK-first"
|
|
4705
|
+
}
|
|
4706
|
+
},
|
|
4707
|
+
{
|
|
4708
|
+
name: PROVIDER_ARCHITECTURE_FLAGS.PROVIDER_METRICS,
|
|
4709
|
+
description: "Enable provider metrics collection (SDK/CLI usage, fallback rate, MCP connections)",
|
|
4710
|
+
enabled: true,
|
|
4711
|
+
// Enabled by default for observability
|
|
4712
|
+
rolloutPercentage: 100,
|
|
4713
|
+
metadata: {
|
|
4714
|
+
owner: "platform-team",
|
|
4715
|
+
expectedImpact: "Better visibility into provider performance"
|
|
4716
|
+
}
|
|
4717
|
+
}
|
|
4718
|
+
];
|
|
4719
|
+
}
|
|
4720
|
+
});
|
|
4721
|
+
|
|
4722
|
+
// src/providers/fallback-decision.ts
|
|
4723
|
+
function decideFallback(error, providerName) {
|
|
4724
|
+
const errorMessage = getErrorMessage(error).toLowerCase();
|
|
4725
|
+
const errorCode = getErrorCode(error);
|
|
4726
|
+
const statusCode = getStatusCode(error);
|
|
4727
|
+
logger.debug("Classifying error for fallback decision", {
|
|
4728
|
+
provider: providerName,
|
|
4729
|
+
message: errorMessage.substring(0, 100),
|
|
4730
|
+
code: errorCode,
|
|
4731
|
+
statusCode
|
|
4732
|
+
});
|
|
4733
|
+
if (matchesPatterns(errorMessage, SDK_UNAVAILABLE_PATTERNS)) {
|
|
4734
|
+
return {
|
|
4735
|
+
decision: "use_cli" /* USE_CLI */,
|
|
4736
|
+
reason: "SDK not available or not installed",
|
|
4737
|
+
severity: "warn"
|
|
4738
|
+
};
|
|
4739
|
+
}
|
|
4740
|
+
if (matchesPatterns(errorMessage, AUTH_ERROR_PATTERNS) || statusCode === 401 || statusCode === 403) {
|
|
4741
|
+
return {
|
|
4742
|
+
decision: "propagate" /* PROPAGATE */,
|
|
4743
|
+
reason: "Authentication error - CLI would fail with same credentials",
|
|
4744
|
+
severity: "error"
|
|
4745
|
+
};
|
|
4746
|
+
}
|
|
4747
|
+
if (matchesPatterns(errorMessage, RATE_LIMIT_PATTERNS) || statusCode === 429) {
|
|
4748
|
+
return {
|
|
4749
|
+
decision: "retry_sdk" /* RETRY_SDK */,
|
|
4750
|
+
reason: "Rate limited - retrying with backoff",
|
|
4751
|
+
severity: "warn",
|
|
4752
|
+
retryDelayMs: 5e3
|
|
4753
|
+
// Longer delay for rate limits
|
|
4754
|
+
};
|
|
4755
|
+
}
|
|
4756
|
+
if (matchesPatterns(errorMessage, TRANSIENT_ERROR_PATTERNS)) {
|
|
4757
|
+
return {
|
|
4758
|
+
decision: "retry_sdk" /* RETRY_SDK */,
|
|
4759
|
+
reason: "Transient network error - retrying",
|
|
4760
|
+
severity: "warn",
|
|
4761
|
+
retryDelayMs: 1e3
|
|
4762
|
+
};
|
|
4763
|
+
}
|
|
4764
|
+
if (matchesPatterns(errorMessage, SERVER_ERROR_PATTERNS) || statusCode && statusCode >= 500 && statusCode < 600) {
|
|
4765
|
+
return {
|
|
4766
|
+
decision: "retry_sdk" /* RETRY_SDK */,
|
|
4767
|
+
reason: "Server error - retrying",
|
|
4768
|
+
severity: "warn",
|
|
4769
|
+
retryDelayMs: 2e3
|
|
4770
|
+
};
|
|
4771
|
+
}
|
|
4772
|
+
if (matchesPatterns(errorMessage, CLIENT_ERROR_PATTERNS) || statusCode && statusCode >= 400 && statusCode < 500) {
|
|
4773
|
+
return {
|
|
4774
|
+
decision: "propagate" /* PROPAGATE */,
|
|
4775
|
+
reason: "Client error - request is invalid",
|
|
4776
|
+
severity: "error"
|
|
4777
|
+
};
|
|
4778
|
+
}
|
|
4779
|
+
if (isSDKFallbackEnabled()) {
|
|
4780
|
+
return {
|
|
4781
|
+
decision: "use_cli" /* USE_CLI */,
|
|
4782
|
+
reason: "Unknown error - falling back to CLI",
|
|
4783
|
+
severity: "warn"
|
|
4784
|
+
};
|
|
4785
|
+
}
|
|
4786
|
+
return {
|
|
4787
|
+
decision: "propagate" /* PROPAGATE */,
|
|
4788
|
+
reason: "Unknown error and fallback is disabled",
|
|
4789
|
+
severity: "error"
|
|
4790
|
+
};
|
|
4791
|
+
}
|
|
4792
|
+
function getErrorMessage(error) {
|
|
4793
|
+
if (error instanceof Error) {
|
|
4794
|
+
return error.message;
|
|
4795
|
+
}
|
|
4796
|
+
if (typeof error === "string") {
|
|
4797
|
+
return error;
|
|
4798
|
+
}
|
|
4799
|
+
if (error && typeof error === "object") {
|
|
4800
|
+
const obj = error;
|
|
4801
|
+
if (typeof obj.message === "string") {
|
|
4802
|
+
return obj.message;
|
|
4803
|
+
}
|
|
4804
|
+
if (typeof obj.error === "string") {
|
|
4805
|
+
return obj.error;
|
|
4806
|
+
}
|
|
4807
|
+
if (obj.error && typeof obj.error === "object") {
|
|
4808
|
+
const innerError = obj.error;
|
|
4809
|
+
if (typeof innerError.message === "string") {
|
|
4810
|
+
return innerError.message;
|
|
4811
|
+
}
|
|
4812
|
+
}
|
|
4813
|
+
}
|
|
4814
|
+
return String(error);
|
|
4815
|
+
}
|
|
4816
|
+
function getErrorCode(error) {
|
|
4817
|
+
if (error && typeof error === "object") {
|
|
4818
|
+
const obj = error;
|
|
4819
|
+
if (typeof obj.code === "string") {
|
|
4820
|
+
return obj.code;
|
|
4821
|
+
}
|
|
4822
|
+
if (typeof obj.errorCode === "string") {
|
|
4823
|
+
return obj.errorCode;
|
|
4824
|
+
}
|
|
4825
|
+
}
|
|
4826
|
+
return void 0;
|
|
4827
|
+
}
|
|
4828
|
+
function getStatusCode(error) {
|
|
4829
|
+
if (error && typeof error === "object") {
|
|
4830
|
+
const obj = error;
|
|
4831
|
+
if (typeof obj.status === "number") {
|
|
4832
|
+
return obj.status;
|
|
4833
|
+
}
|
|
4834
|
+
if (typeof obj.statusCode === "number") {
|
|
4835
|
+
return obj.statusCode;
|
|
4836
|
+
}
|
|
4837
|
+
if (obj.response && typeof obj.response === "object") {
|
|
4838
|
+
const response = obj.response;
|
|
4839
|
+
if (typeof response.status === "number") {
|
|
4840
|
+
return response.status;
|
|
4841
|
+
}
|
|
4842
|
+
}
|
|
4843
|
+
}
|
|
4844
|
+
return void 0;
|
|
4845
|
+
}
|
|
4846
|
+
function matchesPatterns(message, patterns) {
|
|
4847
|
+
return patterns.some((pattern) => message.includes(pattern.toLowerCase()));
|
|
4848
|
+
}
|
|
4849
|
+
var SDK_UNAVAILABLE_PATTERNS, AUTH_ERROR_PATTERNS, TRANSIENT_ERROR_PATTERNS, RATE_LIMIT_PATTERNS, SERVER_ERROR_PATTERNS, CLIENT_ERROR_PATTERNS;
|
|
4850
|
+
var init_fallback_decision = __esm({
|
|
4851
|
+
"src/providers/fallback-decision.ts"() {
|
|
4852
|
+
init_esm_shims();
|
|
4853
|
+
init_logger();
|
|
4854
|
+
init_flags();
|
|
4855
|
+
SDK_UNAVAILABLE_PATTERNS = [
|
|
4856
|
+
"module not found",
|
|
4857
|
+
"cannot find module",
|
|
4858
|
+
"sdk not available",
|
|
4859
|
+
"sdk not installed",
|
|
4860
|
+
"failed to initialize sdk",
|
|
4861
|
+
"sdk initialization failed",
|
|
4862
|
+
"package not found",
|
|
4863
|
+
"@zhipuai/sdk",
|
|
4864
|
+
"@xai-org/grok-sdk",
|
|
4865
|
+
"@openai/codex-sdk",
|
|
4866
|
+
"enoent",
|
|
4867
|
+
// File not found (npm module missing)
|
|
4868
|
+
"cannot resolve",
|
|
4869
|
+
"dynamic import failed"
|
|
4870
|
+
];
|
|
4871
|
+
AUTH_ERROR_PATTERNS = [
|
|
4872
|
+
"authentication failed",
|
|
4873
|
+
"invalid api key",
|
|
4874
|
+
"invalid_api_key",
|
|
4875
|
+
"unauthorized",
|
|
4876
|
+
"api key not found",
|
|
4877
|
+
"api_key_invalid",
|
|
4878
|
+
"invalid credentials",
|
|
4879
|
+
"authentication required",
|
|
4880
|
+
"access denied",
|
|
4881
|
+
"401",
|
|
4882
|
+
"403",
|
|
4883
|
+
"forbidden",
|
|
4884
|
+
"invalid_grant",
|
|
4885
|
+
"token expired",
|
|
4886
|
+
"invalid token"
|
|
4887
|
+
];
|
|
4888
|
+
TRANSIENT_ERROR_PATTERNS = [
|
|
4889
|
+
"timeout",
|
|
4890
|
+
"etimedout",
|
|
4891
|
+
"econnreset",
|
|
4892
|
+
"econnrefused",
|
|
4893
|
+
"enotfound",
|
|
4894
|
+
"network error",
|
|
4895
|
+
"socket hang up",
|
|
4896
|
+
"connection reset",
|
|
4897
|
+
"temporary failure",
|
|
4898
|
+
"service unavailable",
|
|
4899
|
+
"503",
|
|
4900
|
+
"504",
|
|
4901
|
+
"gateway timeout",
|
|
4902
|
+
"bad gateway",
|
|
4903
|
+
"502"
|
|
4904
|
+
];
|
|
4905
|
+
RATE_LIMIT_PATTERNS = [
|
|
4906
|
+
"rate limit",
|
|
4907
|
+
"rate_limit",
|
|
4908
|
+
"too many requests",
|
|
4909
|
+
"429",
|
|
4910
|
+
"throttled",
|
|
4911
|
+
"quota exceeded",
|
|
4912
|
+
"resource_exhausted",
|
|
4913
|
+
"overloaded"
|
|
4914
|
+
];
|
|
4915
|
+
SERVER_ERROR_PATTERNS = [
|
|
4916
|
+
"500",
|
|
4917
|
+
"502",
|
|
4918
|
+
"503",
|
|
4919
|
+
"504",
|
|
4920
|
+
"internal server error",
|
|
4921
|
+
"internal error",
|
|
4922
|
+
"server error"
|
|
4923
|
+
];
|
|
4924
|
+
CLIENT_ERROR_PATTERNS = [
|
|
4925
|
+
"400",
|
|
4926
|
+
"bad request",
|
|
4927
|
+
"invalid request",
|
|
4928
|
+
"validation error",
|
|
4929
|
+
"invalid parameter",
|
|
4930
|
+
"missing parameter",
|
|
4931
|
+
"malformed request"
|
|
4932
|
+
];
|
|
4933
|
+
}
|
|
4934
|
+
});
|
|
4935
|
+
|
|
4936
|
+
// src/core/metrics/provider-metrics.ts
|
|
4937
|
+
function getProviderMetrics() {
|
|
4938
|
+
return ProviderMetricsCollector.getInstance();
|
|
4939
|
+
}
|
|
4940
|
+
var ProviderMetricsCollector;
|
|
4941
|
+
var init_provider_metrics = __esm({
|
|
4942
|
+
"src/core/metrics/provider-metrics.ts"() {
|
|
4943
|
+
init_esm_shims();
|
|
4944
|
+
init_logger();
|
|
4945
|
+
init_flags();
|
|
4946
|
+
ProviderMetricsCollector = class _ProviderMetricsCollector {
|
|
4947
|
+
static instance = null;
|
|
4948
|
+
// Per-provider metrics
|
|
4949
|
+
providerMetrics = /* @__PURE__ */ new Map();
|
|
4950
|
+
// MCP metrics
|
|
4951
|
+
mcpMetrics = {
|
|
4952
|
+
totalConnections: 0,
|
|
4953
|
+
activeConnections: 0,
|
|
4954
|
+
failedConnections: 0,
|
|
4955
|
+
totalToolCalls: 0,
|
|
4956
|
+
avgToolCallLatencyMs: 0,
|
|
4957
|
+
sessionsByProvider: {}
|
|
4958
|
+
};
|
|
4959
|
+
// Latency tracking (for averages)
|
|
4960
|
+
sdkLatencies = /* @__PURE__ */ new Map();
|
|
4961
|
+
cliLatencies = /* @__PURE__ */ new Map();
|
|
4962
|
+
mcpLatencies = [];
|
|
4963
|
+
// Max samples to keep for latency averaging
|
|
4964
|
+
MAX_LATENCY_SAMPLES = 100;
|
|
4965
|
+
constructor() {
|
|
4966
|
+
}
|
|
4967
|
+
/**
|
|
4968
|
+
* Get singleton instance
|
|
4969
|
+
*/
|
|
4970
|
+
static getInstance() {
|
|
4971
|
+
if (!_ProviderMetricsCollector.instance) {
|
|
4972
|
+
_ProviderMetricsCollector.instance = new _ProviderMetricsCollector();
|
|
4973
|
+
}
|
|
4974
|
+
return _ProviderMetricsCollector.instance;
|
|
4975
|
+
}
|
|
4976
|
+
/**
|
|
4977
|
+
* Reset singleton instance (for testing)
|
|
4978
|
+
*/
|
|
4979
|
+
static resetInstance() {
|
|
4980
|
+
_ProviderMetricsCollector.instance = null;
|
|
4981
|
+
}
|
|
4982
|
+
/**
|
|
4983
|
+
* Check if metrics collection is enabled
|
|
4984
|
+
*/
|
|
4985
|
+
isEnabled() {
|
|
4986
|
+
return shouldCollectProviderMetrics();
|
|
4987
|
+
}
|
|
4988
|
+
/**
|
|
4989
|
+
* Get or create provider metrics data
|
|
4990
|
+
*/
|
|
4991
|
+
getOrCreateProviderMetrics(providerName) {
|
|
4992
|
+
if (!this.providerMetrics.has(providerName)) {
|
|
4993
|
+
this.providerMetrics.set(providerName, {
|
|
4994
|
+
providerName,
|
|
4995
|
+
executionMode: "cli",
|
|
4996
|
+
// Default mode
|
|
4997
|
+
circuitBreakerState: "closed",
|
|
4998
|
+
metrics: {
|
|
4999
|
+
sdkExecutions: 0,
|
|
5000
|
+
cliExecutions: 0,
|
|
5001
|
+
mcpToolCalls: 0,
|
|
5002
|
+
sdkFallbacks: 0,
|
|
5003
|
+
sdkErrors: 0,
|
|
5004
|
+
cliErrors: 0,
|
|
5005
|
+
mcpConnectionFailures: 0,
|
|
5006
|
+
avgSdkLatencyMs: 0,
|
|
5007
|
+
avgCliLatencyMs: 0,
|
|
5008
|
+
avgMcpLatencyMs: 0,
|
|
5009
|
+
fallbackRate: 0,
|
|
5010
|
+
sdkSuccessRate: 1,
|
|
5011
|
+
cliSuccessRate: 1,
|
|
5012
|
+
mcpSuccessRate: 1
|
|
5013
|
+
},
|
|
5014
|
+
lastUpdated: Date.now()
|
|
5015
|
+
});
|
|
5016
|
+
}
|
|
5017
|
+
return this.providerMetrics.get(providerName);
|
|
5018
|
+
}
|
|
5019
|
+
/**
|
|
5020
|
+
* Record SDK execution
|
|
5021
|
+
*/
|
|
5022
|
+
recordSDKExecution(providerName, latencyMs, success) {
|
|
5023
|
+
if (!this.isEnabled()) return;
|
|
5024
|
+
const data = this.getOrCreateProviderMetrics(providerName);
|
|
5025
|
+
data.metrics.sdkExecutions++;
|
|
5026
|
+
if (!success) {
|
|
5027
|
+
data.metrics.sdkErrors++;
|
|
5028
|
+
}
|
|
5029
|
+
this.addLatencySample(this.sdkLatencies, providerName, latencyMs);
|
|
5030
|
+
data.metrics.avgSdkLatencyMs = this.calculateAverageLatency(
|
|
5031
|
+
this.sdkLatencies.get(providerName) || []
|
|
5032
|
+
);
|
|
5033
|
+
data.metrics.sdkSuccessRate = this.calculateSuccessRate(
|
|
5034
|
+
data.metrics.sdkExecutions,
|
|
5035
|
+
data.metrics.sdkErrors
|
|
5036
|
+
);
|
|
5037
|
+
data.lastUpdated = Date.now();
|
|
5038
|
+
logger.debug("SDK execution recorded", {
|
|
5039
|
+
provider: providerName,
|
|
5040
|
+
latencyMs,
|
|
5041
|
+
success,
|
|
5042
|
+
totalExecutions: data.metrics.sdkExecutions
|
|
5043
|
+
});
|
|
5044
|
+
}
|
|
5045
|
+
/**
|
|
5046
|
+
* Record CLI execution
|
|
5047
|
+
*/
|
|
5048
|
+
recordCLIExecution(providerName, latencyMs, success) {
|
|
5049
|
+
if (!this.isEnabled()) return;
|
|
5050
|
+
const data = this.getOrCreateProviderMetrics(providerName);
|
|
5051
|
+
data.metrics.cliExecutions++;
|
|
5052
|
+
if (!success) {
|
|
5053
|
+
data.metrics.cliErrors++;
|
|
5054
|
+
}
|
|
5055
|
+
this.addLatencySample(this.cliLatencies, providerName, latencyMs);
|
|
5056
|
+
data.metrics.avgCliLatencyMs = this.calculateAverageLatency(
|
|
5057
|
+
this.cliLatencies.get(providerName) || []
|
|
5058
|
+
);
|
|
5059
|
+
data.metrics.cliSuccessRate = this.calculateSuccessRate(
|
|
5060
|
+
data.metrics.cliExecutions,
|
|
5061
|
+
data.metrics.cliErrors
|
|
5062
|
+
);
|
|
5063
|
+
data.lastUpdated = Date.now();
|
|
5064
|
+
logger.debug("CLI execution recorded", {
|
|
5065
|
+
provider: providerName,
|
|
5066
|
+
latencyMs,
|
|
5067
|
+
success,
|
|
5068
|
+
totalExecutions: data.metrics.cliExecutions
|
|
5069
|
+
});
|
|
5070
|
+
}
|
|
5071
|
+
/**
|
|
5072
|
+
* Record SDK fallback to CLI
|
|
5073
|
+
*/
|
|
5074
|
+
recordSDKFallback(providerName, reason) {
|
|
5075
|
+
if (!this.isEnabled()) return;
|
|
5076
|
+
const data = this.getOrCreateProviderMetrics(providerName);
|
|
5077
|
+
data.metrics.sdkFallbacks++;
|
|
5078
|
+
if (data.metrics.sdkExecutions > 0) {
|
|
5079
|
+
data.metrics.fallbackRate = data.metrics.sdkFallbacks / data.metrics.sdkExecutions;
|
|
5080
|
+
}
|
|
5081
|
+
data.lastUpdated = Date.now();
|
|
5082
|
+
logger.info("SDK fallback recorded", {
|
|
5083
|
+
provider: providerName,
|
|
5084
|
+
reason,
|
|
5085
|
+
fallbackRate: data.metrics.fallbackRate
|
|
5086
|
+
});
|
|
5087
|
+
}
|
|
5088
|
+
/**
|
|
5089
|
+
* Record MCP connection
|
|
5090
|
+
*/
|
|
5091
|
+
recordMCPConnection(providerName, success) {
|
|
5092
|
+
if (!this.isEnabled()) return;
|
|
5093
|
+
this.mcpMetrics.totalConnections++;
|
|
5094
|
+
if (success) {
|
|
5095
|
+
this.mcpMetrics.activeConnections++;
|
|
5096
|
+
this.mcpMetrics.sessionsByProvider[providerName] = (this.mcpMetrics.sessionsByProvider[providerName] || 0) + 1;
|
|
5097
|
+
} else {
|
|
5098
|
+
this.mcpMetrics.failedConnections++;
|
|
5099
|
+
const data = this.getOrCreateProviderMetrics(providerName);
|
|
5100
|
+
data.metrics.mcpConnectionFailures++;
|
|
5101
|
+
data.lastUpdated = Date.now();
|
|
5102
|
+
}
|
|
5103
|
+
logger.debug("MCP connection recorded", {
|
|
5104
|
+
provider: providerName,
|
|
5105
|
+
success,
|
|
5106
|
+
activeConnections: this.mcpMetrics.activeConnections
|
|
5107
|
+
});
|
|
5108
|
+
}
|
|
5109
|
+
/**
|
|
5110
|
+
* Record MCP connection closed
|
|
5111
|
+
*/
|
|
5112
|
+
recordMCPDisconnection(providerName) {
|
|
5113
|
+
if (!this.isEnabled()) return;
|
|
5114
|
+
if (this.mcpMetrics.activeConnections > 0) {
|
|
5115
|
+
this.mcpMetrics.activeConnections--;
|
|
5116
|
+
}
|
|
5117
|
+
const currentCount = this.mcpMetrics.sessionsByProvider[providerName];
|
|
5118
|
+
if (currentCount !== void 0 && currentCount > 0) {
|
|
5119
|
+
this.mcpMetrics.sessionsByProvider[providerName] = currentCount - 1;
|
|
5120
|
+
}
|
|
5121
|
+
logger.debug("MCP disconnection recorded", {
|
|
5122
|
+
provider: providerName,
|
|
5123
|
+
activeConnections: this.mcpMetrics.activeConnections
|
|
5124
|
+
});
|
|
5125
|
+
}
|
|
5126
|
+
/**
|
|
5127
|
+
* Record MCP tool call
|
|
5128
|
+
*/
|
|
5129
|
+
recordMCPToolCall(providerName, toolName, latencyMs, success) {
|
|
5130
|
+
if (!this.isEnabled()) return;
|
|
5131
|
+
const data = this.getOrCreateProviderMetrics(providerName);
|
|
5132
|
+
data.metrics.mcpToolCalls++;
|
|
5133
|
+
this.mcpMetrics.totalToolCalls++;
|
|
5134
|
+
this.mcpLatencies.push(latencyMs);
|
|
5135
|
+
if (this.mcpLatencies.length > this.MAX_LATENCY_SAMPLES) {
|
|
5136
|
+
this.mcpLatencies.shift();
|
|
5137
|
+
}
|
|
5138
|
+
data.metrics.avgMcpLatencyMs = this.calculateAverageLatency(this.mcpLatencies);
|
|
5139
|
+
this.mcpMetrics.avgToolCallLatencyMs = data.metrics.avgMcpLatencyMs;
|
|
5140
|
+
data.lastUpdated = Date.now();
|
|
5141
|
+
logger.debug("MCP tool call recorded", {
|
|
5142
|
+
provider: providerName,
|
|
5143
|
+
tool: toolName,
|
|
5144
|
+
latencyMs,
|
|
5145
|
+
success
|
|
5146
|
+
});
|
|
5147
|
+
}
|
|
5148
|
+
/**
|
|
5149
|
+
* Update circuit breaker state
|
|
5150
|
+
*/
|
|
5151
|
+
updateCircuitBreakerState(providerName, state) {
|
|
5152
|
+
if (!this.isEnabled()) return;
|
|
5153
|
+
const data = this.getOrCreateProviderMetrics(providerName);
|
|
5154
|
+
const previousState = data.circuitBreakerState;
|
|
5155
|
+
data.circuitBreakerState = state;
|
|
5156
|
+
data.lastUpdated = Date.now();
|
|
5157
|
+
if (previousState !== state) {
|
|
5158
|
+
logger.info("Circuit breaker state changed", {
|
|
5159
|
+
provider: providerName,
|
|
5160
|
+
from: previousState,
|
|
5161
|
+
to: state
|
|
5162
|
+
});
|
|
5163
|
+
}
|
|
5164
|
+
}
|
|
5165
|
+
/**
|
|
5166
|
+
* Update execution mode
|
|
5167
|
+
*/
|
|
5168
|
+
updateExecutionMode(providerName, mode) {
|
|
5169
|
+
if (!this.isEnabled()) return;
|
|
5170
|
+
const data = this.getOrCreateProviderMetrics(providerName);
|
|
5171
|
+
data.executionMode = mode;
|
|
5172
|
+
data.lastUpdated = Date.now();
|
|
5173
|
+
}
|
|
5174
|
+
/**
|
|
5175
|
+
* Get metrics for a specific provider
|
|
5176
|
+
*/
|
|
5177
|
+
getProviderMetrics(providerName) {
|
|
5178
|
+
return this.providerMetrics.get(providerName);
|
|
5179
|
+
}
|
|
5180
|
+
/**
|
|
5181
|
+
* Get all provider metrics
|
|
5182
|
+
*/
|
|
5183
|
+
getAllProviderMetrics() {
|
|
5184
|
+
return Array.from(this.providerMetrics.values());
|
|
5185
|
+
}
|
|
5186
|
+
/**
|
|
5187
|
+
* Get MCP session metrics
|
|
5188
|
+
*/
|
|
5189
|
+
getMCPMetrics() {
|
|
5190
|
+
return { ...this.mcpMetrics };
|
|
5191
|
+
}
|
|
5192
|
+
/**
|
|
5193
|
+
* Get aggregated metrics summary
|
|
5194
|
+
*/
|
|
5195
|
+
getMetricsSummary() {
|
|
5196
|
+
let totalSDK = 0;
|
|
5197
|
+
let totalCLI = 0;
|
|
5198
|
+
let totalFallbacks = 0;
|
|
5199
|
+
let sdkLatencySum = 0;
|
|
5200
|
+
let cliLatencySum = 0;
|
|
5201
|
+
let sdkCount = 0;
|
|
5202
|
+
let cliCount = 0;
|
|
5203
|
+
const openCircuitBreakers = [];
|
|
5204
|
+
for (const data of this.providerMetrics.values()) {
|
|
5205
|
+
totalSDK += data.metrics.sdkExecutions;
|
|
5206
|
+
totalCLI += data.metrics.cliExecutions;
|
|
5207
|
+
totalFallbacks += data.metrics.sdkFallbacks;
|
|
5208
|
+
if (data.metrics.avgSdkLatencyMs > 0) {
|
|
5209
|
+
sdkLatencySum += data.metrics.avgSdkLatencyMs;
|
|
5210
|
+
sdkCount++;
|
|
5211
|
+
}
|
|
5212
|
+
if (data.metrics.avgCliLatencyMs > 0) {
|
|
5213
|
+
cliLatencySum += data.metrics.avgCliLatencyMs;
|
|
5214
|
+
cliCount++;
|
|
5215
|
+
}
|
|
5216
|
+
if (data.circuitBreakerState === "open") {
|
|
5217
|
+
openCircuitBreakers.push(data.providerName);
|
|
5218
|
+
}
|
|
5219
|
+
}
|
|
5220
|
+
return {
|
|
5221
|
+
totalSDKExecutions: totalSDK,
|
|
5222
|
+
totalCLIExecutions: totalCLI,
|
|
5223
|
+
totalMCPConnections: this.mcpMetrics.totalConnections,
|
|
5224
|
+
overallFallbackRate: totalSDK > 0 ? totalFallbacks / totalSDK : 0,
|
|
5225
|
+
avgSDKLatencyMs: sdkCount > 0 ? sdkLatencySum / sdkCount : 0,
|
|
5226
|
+
avgCLILatencyMs: cliCount > 0 ? cliLatencySum / cliCount : 0,
|
|
5227
|
+
providersWithOpenCircuitBreaker: openCircuitBreakers
|
|
5228
|
+
};
|
|
5229
|
+
}
|
|
5230
|
+
/**
|
|
5231
|
+
* Reset all metrics (for testing)
|
|
5232
|
+
*/
|
|
5233
|
+
reset() {
|
|
5234
|
+
this.providerMetrics.clear();
|
|
5235
|
+
this.sdkLatencies.clear();
|
|
5236
|
+
this.cliLatencies.clear();
|
|
5237
|
+
this.mcpLatencies = [];
|
|
5238
|
+
this.mcpMetrics = {
|
|
5239
|
+
totalConnections: 0,
|
|
5240
|
+
activeConnections: 0,
|
|
5241
|
+
failedConnections: 0,
|
|
5242
|
+
totalToolCalls: 0,
|
|
5243
|
+
avgToolCallLatencyMs: 0,
|
|
5244
|
+
sessionsByProvider: {}
|
|
5245
|
+
};
|
|
5246
|
+
}
|
|
5247
|
+
/**
|
|
5248
|
+
* Add latency sample to provider's latency array
|
|
5249
|
+
*/
|
|
5250
|
+
addLatencySample(latencyMap, providerName, latencyMs) {
|
|
5251
|
+
if (!latencyMap.has(providerName)) {
|
|
5252
|
+
latencyMap.set(providerName, []);
|
|
5253
|
+
}
|
|
5254
|
+
const latencies = latencyMap.get(providerName);
|
|
5255
|
+
latencies.push(latencyMs);
|
|
5256
|
+
if (latencies.length > this.MAX_LATENCY_SAMPLES) {
|
|
5257
|
+
latencies.shift();
|
|
5258
|
+
}
|
|
5259
|
+
}
|
|
5260
|
+
/**
|
|
5261
|
+
* Calculate average latency from samples
|
|
5262
|
+
*/
|
|
5263
|
+
calculateAverageLatency(latencies) {
|
|
5264
|
+
if (latencies.length === 0) return 0;
|
|
5265
|
+
const sum = latencies.reduce((a, b) => a + b, 0);
|
|
5266
|
+
return Math.round(sum / latencies.length);
|
|
5267
|
+
}
|
|
5268
|
+
/**
|
|
5269
|
+
* Calculate success rate
|
|
5270
|
+
*/
|
|
5271
|
+
calculateSuccessRate(total, errors) {
|
|
5272
|
+
if (total === 0) return 1;
|
|
5273
|
+
return Math.max(0, (total - errors) / total);
|
|
5274
|
+
}
|
|
5275
|
+
};
|
|
5276
|
+
}
|
|
5277
|
+
});
|
|
5278
|
+
|
|
5279
|
+
// src/providers/hybrid-adapter-base.ts
|
|
5280
|
+
var DEFAULT_CIRCUIT_BREAKER_CONFIG, CircuitBreaker2, HybridAdapterBase;
|
|
5281
|
+
var init_hybrid_adapter_base = __esm({
|
|
5282
|
+
"src/providers/hybrid-adapter-base.ts"() {
|
|
5283
|
+
init_esm_shims();
|
|
5284
|
+
init_logger();
|
|
5285
|
+
init_flags();
|
|
5286
|
+
init_fallback_decision();
|
|
5287
|
+
init_provider_metrics();
|
|
5288
|
+
DEFAULT_CIRCUIT_BREAKER_CONFIG = {
|
|
5289
|
+
failureThreshold: 3,
|
|
5290
|
+
resetTimeout: 6e4,
|
|
5291
|
+
// 1 minute
|
|
5292
|
+
halfOpenSuccessThreshold: 2
|
|
5293
|
+
};
|
|
5294
|
+
CircuitBreaker2 = class {
|
|
5295
|
+
state = "closed";
|
|
5296
|
+
failures = 0;
|
|
5297
|
+
successes = 0;
|
|
5298
|
+
lastFailureTime = 0;
|
|
5299
|
+
config;
|
|
5300
|
+
constructor(config = {}) {
|
|
5301
|
+
this.config = { ...DEFAULT_CIRCUIT_BREAKER_CONFIG, ...config };
|
|
5302
|
+
}
|
|
5303
|
+
/**
|
|
5304
|
+
* Check if circuit is open (requests should not be attempted)
|
|
5305
|
+
*/
|
|
5306
|
+
isOpen() {
|
|
5307
|
+
if (this.state === "open") {
|
|
5308
|
+
if (Date.now() - this.lastFailureTime >= this.config.resetTimeout) {
|
|
5309
|
+
this.state = "half-open";
|
|
5310
|
+
this.successes = 0;
|
|
5311
|
+
logger.debug("Circuit breaker transitioning to half-open");
|
|
5312
|
+
return false;
|
|
5313
|
+
}
|
|
5314
|
+
return true;
|
|
5315
|
+
}
|
|
5316
|
+
return false;
|
|
5317
|
+
}
|
|
5318
|
+
/**
|
|
5319
|
+
* Get current state
|
|
5320
|
+
*/
|
|
5321
|
+
getState() {
|
|
5322
|
+
if (this.state === "open" && Date.now() - this.lastFailureTime >= this.config.resetTimeout) {
|
|
5323
|
+
this.state = "half-open";
|
|
5324
|
+
this.successes = 0;
|
|
5325
|
+
}
|
|
5326
|
+
return this.state;
|
|
5327
|
+
}
|
|
5328
|
+
/**
|
|
5329
|
+
* Record successful execution
|
|
5330
|
+
*/
|
|
5331
|
+
recordSuccess() {
|
|
5332
|
+
if (this.state === "half-open") {
|
|
5333
|
+
this.successes++;
|
|
5334
|
+
if (this.successes >= this.config.halfOpenSuccessThreshold) {
|
|
5335
|
+
this.state = "closed";
|
|
5336
|
+
this.failures = 0;
|
|
5337
|
+
this.successes = 0;
|
|
5338
|
+
logger.debug("Circuit breaker closed after recovery");
|
|
5339
|
+
}
|
|
5340
|
+
} else if (this.state === "closed") {
|
|
5341
|
+
this.failures = 0;
|
|
5342
|
+
}
|
|
5343
|
+
}
|
|
5344
|
+
/**
|
|
5345
|
+
* Record failed execution
|
|
5346
|
+
*/
|
|
5347
|
+
recordFailure() {
|
|
5348
|
+
this.failures++;
|
|
5349
|
+
this.lastFailureTime = Date.now();
|
|
5350
|
+
if (this.state === "half-open") {
|
|
5351
|
+
this.state = "open";
|
|
5352
|
+
logger.warn("Circuit breaker opened after half-open failure");
|
|
5353
|
+
} else if (this.failures >= this.config.failureThreshold) {
|
|
5354
|
+
this.state = "open";
|
|
5355
|
+
logger.warn("Circuit breaker opened after consecutive failures", {
|
|
5356
|
+
failures: this.failures,
|
|
5357
|
+
threshold: this.config.failureThreshold
|
|
5358
|
+
});
|
|
5359
|
+
}
|
|
5360
|
+
}
|
|
5361
|
+
/**
|
|
5362
|
+
* Reset circuit breaker to closed state
|
|
5363
|
+
*/
|
|
5364
|
+
reset() {
|
|
5365
|
+
this.state = "closed";
|
|
5366
|
+
this.failures = 0;
|
|
5367
|
+
this.successes = 0;
|
|
5368
|
+
this.lastFailureTime = 0;
|
|
5369
|
+
}
|
|
5370
|
+
};
|
|
5371
|
+
HybridAdapterBase = class {
|
|
5372
|
+
mode;
|
|
5373
|
+
activeMode = null;
|
|
5374
|
+
providerName;
|
|
5375
|
+
sdkCircuitBreaker;
|
|
5376
|
+
cliCircuitBreaker;
|
|
5377
|
+
maxRetries;
|
|
5378
|
+
// Track initialization state
|
|
5379
|
+
sdkInitialized = false;
|
|
5380
|
+
cliInitialized = false;
|
|
5381
|
+
initPromise = null;
|
|
5382
|
+
constructor(options) {
|
|
5383
|
+
this.mode = options.mode || "auto";
|
|
5384
|
+
this.providerName = options.providerName;
|
|
5385
|
+
this.maxRetries = options.maxRetries ?? 1;
|
|
5386
|
+
this.sdkCircuitBreaker = new CircuitBreaker2(options.sdkCircuitBreaker);
|
|
5387
|
+
this.cliCircuitBreaker = new CircuitBreaker2(options.cliCircuitBreaker);
|
|
5388
|
+
logger.debug("HybridAdapterBase initialized", {
|
|
5389
|
+
provider: this.providerName,
|
|
5390
|
+
mode: this.mode,
|
|
5391
|
+
maxRetries: this.maxRetries
|
|
5392
|
+
});
|
|
5393
|
+
}
|
|
5394
|
+
/**
|
|
5395
|
+
* Execute a request using the appropriate mode
|
|
5396
|
+
*
|
|
5397
|
+
* Mode selection logic:
|
|
5398
|
+
* 1. If mode is 'cli' or SDK-first is disabled → use CLI directly
|
|
5399
|
+
* 2. If mode is 'sdk' → use SDK only (throw if unavailable)
|
|
5400
|
+
* 3. If mode is 'auto' → try SDK first, fallback to CLI
|
|
5401
|
+
*/
|
|
5402
|
+
async execute(request) {
|
|
5403
|
+
await this.ensureInitialized();
|
|
5404
|
+
const useSDKFirst = this.shouldUseSDKFirst();
|
|
5405
|
+
if (!useSDKFirst || this.mode === "cli") {
|
|
5406
|
+
return this.executeWithCLI(request);
|
|
5407
|
+
}
|
|
5408
|
+
if (this.mode === "sdk") {
|
|
5409
|
+
return this.executeWithSDK(request);
|
|
5410
|
+
}
|
|
5411
|
+
return this.executeWithFallback(request);
|
|
5412
|
+
}
|
|
5413
|
+
/**
|
|
5414
|
+
* Execute with SDK-first and CLI fallback
|
|
5415
|
+
*/
|
|
5416
|
+
async executeWithFallback(request) {
|
|
5417
|
+
if (this.sdkCircuitBreaker.isOpen()) {
|
|
5418
|
+
logger.debug("SDK circuit breaker is open, using CLI", {
|
|
5419
|
+
provider: this.providerName
|
|
5420
|
+
});
|
|
5421
|
+
return this.executeWithCLI(request);
|
|
5422
|
+
}
|
|
5423
|
+
const startTime = Date.now();
|
|
5424
|
+
let lastError;
|
|
5425
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
5426
|
+
try {
|
|
5427
|
+
const result = await this.executeViaSDK(request);
|
|
5428
|
+
this.sdkCircuitBreaker.recordSuccess();
|
|
5429
|
+
this.recordSDKMetrics(Date.now() - startTime, true);
|
|
5430
|
+
return result;
|
|
5431
|
+
} catch (error) {
|
|
5432
|
+
lastError = error;
|
|
5433
|
+
const classification = decideFallback(error, this.providerName);
|
|
5434
|
+
logger.debug("SDK execution failed", {
|
|
5435
|
+
provider: this.providerName,
|
|
5436
|
+
attempt: attempt + 1,
|
|
5437
|
+
decision: classification.decision,
|
|
5438
|
+
reason: classification.reason
|
|
5439
|
+
});
|
|
5440
|
+
if (classification.decision === "retry_sdk" /* RETRY_SDK */ && attempt < this.maxRetries) {
|
|
5441
|
+
const delay = classification.retryDelayMs || 1e3;
|
|
5442
|
+
await this.sleep(delay);
|
|
5443
|
+
continue;
|
|
5444
|
+
}
|
|
5445
|
+
if (classification.decision === "use_cli" /* USE_CLI */ || classification.decision === "retry_sdk" /* RETRY_SDK */ && attempt >= this.maxRetries) {
|
|
5446
|
+
this.sdkCircuitBreaker.recordFailure();
|
|
5447
|
+
this.recordSDKMetrics(Date.now() - startTime, false);
|
|
5448
|
+
this.recordFallbackMetrics(classification.reason || "max retries exceeded");
|
|
5449
|
+
if (isSDKFallbackEnabled()) {
|
|
5450
|
+
logger.info("Falling back to CLI execution", {
|
|
5451
|
+
provider: this.providerName,
|
|
5452
|
+
reason: classification.reason || "max retries exceeded"
|
|
5453
|
+
});
|
|
5454
|
+
return this.executeWithCLI(request);
|
|
5455
|
+
}
|
|
5456
|
+
}
|
|
5457
|
+
this.sdkCircuitBreaker.recordFailure();
|
|
5458
|
+
this.recordSDKMetrics(Date.now() - startTime, false);
|
|
5459
|
+
throw error;
|
|
5460
|
+
}
|
|
5461
|
+
}
|
|
5462
|
+
this.sdkCircuitBreaker.recordFailure();
|
|
5463
|
+
this.recordSDKMetrics(Date.now() - startTime, false);
|
|
5464
|
+
if (isSDKFallbackEnabled()) {
|
|
5465
|
+
this.recordFallbackMetrics("max retries exceeded");
|
|
5466
|
+
return this.executeWithCLI(request);
|
|
5467
|
+
}
|
|
5468
|
+
throw lastError || new Error(`SDK execution failed after ${this.maxRetries + 1} attempts`);
|
|
5469
|
+
}
|
|
5470
|
+
/**
|
|
5471
|
+
* Execute using SDK only
|
|
5472
|
+
*/
|
|
5473
|
+
async executeWithSDK(request) {
|
|
5474
|
+
if (this.sdkCircuitBreaker.isOpen()) {
|
|
5475
|
+
throw new Error(`SDK circuit breaker is open for ${this.providerName}`);
|
|
5476
|
+
}
|
|
5477
|
+
const startTime = Date.now();
|
|
5478
|
+
try {
|
|
5479
|
+
const result = await this.executeViaSDK(request);
|
|
5480
|
+
this.sdkCircuitBreaker.recordSuccess();
|
|
5481
|
+
this.recordSDKMetrics(Date.now() - startTime, true);
|
|
5482
|
+
return result;
|
|
5483
|
+
} catch (error) {
|
|
5484
|
+
this.sdkCircuitBreaker.recordFailure();
|
|
5485
|
+
this.recordSDKMetrics(Date.now() - startTime, false);
|
|
5486
|
+
throw error;
|
|
5487
|
+
}
|
|
5488
|
+
}
|
|
5489
|
+
/**
|
|
5490
|
+
* Execute using CLI only
|
|
5491
|
+
*/
|
|
5492
|
+
async executeWithCLI(request) {
|
|
5493
|
+
if (this.cliCircuitBreaker.isOpen()) {
|
|
5494
|
+
throw new Error(`CLI circuit breaker is open for ${this.providerName}`);
|
|
5495
|
+
}
|
|
5496
|
+
const startTime = Date.now();
|
|
5497
|
+
try {
|
|
5498
|
+
const result = await this.executeViaCLI(request);
|
|
5499
|
+
this.cliCircuitBreaker.recordSuccess();
|
|
5500
|
+
this.recordCLIMetrics(Date.now() - startTime, true);
|
|
5501
|
+
return result;
|
|
5502
|
+
} catch (error) {
|
|
5503
|
+
this.cliCircuitBreaker.recordFailure();
|
|
5504
|
+
this.recordCLIMetrics(Date.now() - startTime, false);
|
|
5505
|
+
throw error;
|
|
5506
|
+
}
|
|
5507
|
+
}
|
|
5508
|
+
/**
|
|
5509
|
+
* Check if SDK-first mode should be used
|
|
5510
|
+
*/
|
|
5511
|
+
shouldUseSDKFirst() {
|
|
5512
|
+
if (!isSDKFirstModeEnabled()) {
|
|
5513
|
+
return false;
|
|
5514
|
+
}
|
|
5515
|
+
if (this.mode === "cli") {
|
|
5516
|
+
return false;
|
|
5517
|
+
}
|
|
5518
|
+
return true;
|
|
5519
|
+
}
|
|
5520
|
+
/**
|
|
5521
|
+
* Ensure adapters are initialized
|
|
5522
|
+
*/
|
|
5523
|
+
async ensureInitialized() {
|
|
5524
|
+
if (this.initPromise) {
|
|
5525
|
+
return this.initPromise;
|
|
5526
|
+
}
|
|
5527
|
+
this.initPromise = this.initialize();
|
|
5528
|
+
return this.initPromise;
|
|
5529
|
+
}
|
|
5530
|
+
/**
|
|
5531
|
+
* Initialize adapters based on mode
|
|
5532
|
+
*/
|
|
5533
|
+
async initialize() {
|
|
5534
|
+
const useSDKFirst = this.shouldUseSDKFirst();
|
|
5535
|
+
if (this.mode === "cli" || !useSDKFirst) {
|
|
5536
|
+
if (!this.cliInitialized) {
|
|
5537
|
+
const available = await this.isCLIAvailable();
|
|
5538
|
+
if (!available) {
|
|
5539
|
+
throw new Error(`CLI not available for ${this.providerName}`);
|
|
5540
|
+
}
|
|
5541
|
+
await this.initializeCLI();
|
|
5542
|
+
this.cliInitialized = true;
|
|
5543
|
+
this.activeMode = "cli";
|
|
5544
|
+
}
|
|
5545
|
+
return;
|
|
5546
|
+
}
|
|
5547
|
+
if (this.mode === "sdk") {
|
|
5548
|
+
if (!this.sdkInitialized) {
|
|
5549
|
+
const available = await this.isSDKAvailable();
|
|
5550
|
+
if (!available) {
|
|
5551
|
+
throw new Error(`SDK not available for ${this.providerName}`);
|
|
5552
|
+
}
|
|
5553
|
+
await this.initializeSDK();
|
|
5554
|
+
this.sdkInitialized = true;
|
|
5555
|
+
this.activeMode = "sdk";
|
|
5556
|
+
}
|
|
5557
|
+
return;
|
|
5558
|
+
}
|
|
5559
|
+
if (!this.sdkInitialized) {
|
|
5560
|
+
const sdkAvailable = await this.isSDKAvailable();
|
|
5561
|
+
if (sdkAvailable) {
|
|
5562
|
+
try {
|
|
5563
|
+
await this.initializeSDK();
|
|
5564
|
+
this.sdkInitialized = true;
|
|
5565
|
+
this.activeMode = "sdk";
|
|
5566
|
+
} catch (error) {
|
|
5567
|
+
logger.warn("SDK initialization failed, will use CLI", {
|
|
5568
|
+
provider: this.providerName,
|
|
5569
|
+
error: error instanceof Error ? error.message : String(error)
|
|
5570
|
+
});
|
|
5571
|
+
}
|
|
5572
|
+
}
|
|
5573
|
+
}
|
|
5574
|
+
if (!this.cliInitialized) {
|
|
5575
|
+
const cliAvailable = await this.isCLIAvailable();
|
|
5576
|
+
if (cliAvailable) {
|
|
5577
|
+
await this.initializeCLI();
|
|
5578
|
+
this.cliInitialized = true;
|
|
5579
|
+
if (!this.sdkInitialized) {
|
|
5580
|
+
this.activeMode = "cli";
|
|
5581
|
+
}
|
|
5582
|
+
} else if (!this.sdkInitialized) {
|
|
5583
|
+
throw new Error(`Neither SDK nor CLI available for ${this.providerName}`);
|
|
5584
|
+
}
|
|
5585
|
+
}
|
|
5586
|
+
}
|
|
5587
|
+
/**
|
|
5588
|
+
* Record SDK execution metrics
|
|
5589
|
+
*/
|
|
5590
|
+
recordSDKMetrics(latencyMs, success) {
|
|
5591
|
+
try {
|
|
5592
|
+
const collector = getProviderMetrics();
|
|
5593
|
+
collector.recordSDKExecution(this.providerName, latencyMs, success);
|
|
5594
|
+
} catch {
|
|
5595
|
+
}
|
|
5596
|
+
}
|
|
5597
|
+
/**
|
|
5598
|
+
* Record CLI execution metrics
|
|
5599
|
+
*/
|
|
5600
|
+
recordCLIMetrics(latencyMs, success) {
|
|
5601
|
+
try {
|
|
5602
|
+
const collector = getProviderMetrics();
|
|
5603
|
+
collector.recordCLIExecution(this.providerName, latencyMs, success);
|
|
5604
|
+
} catch {
|
|
5605
|
+
}
|
|
5606
|
+
}
|
|
5607
|
+
/**
|
|
5608
|
+
* Record fallback event metrics
|
|
5609
|
+
*/
|
|
5610
|
+
recordFallbackMetrics(reason) {
|
|
5611
|
+
try {
|
|
5612
|
+
const collector = getProviderMetrics();
|
|
5613
|
+
collector.recordSDKFallback(this.providerName, reason);
|
|
5614
|
+
} catch {
|
|
5615
|
+
}
|
|
5616
|
+
}
|
|
5617
|
+
/**
|
|
5618
|
+
* Sleep utility
|
|
5619
|
+
*/
|
|
5620
|
+
sleep(ms) {
|
|
5621
|
+
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
5622
|
+
}
|
|
5623
|
+
/**
|
|
5624
|
+
* Get current active mode
|
|
5625
|
+
*/
|
|
5626
|
+
getActiveMode() {
|
|
5627
|
+
return this.activeMode;
|
|
5628
|
+
}
|
|
5629
|
+
/**
|
|
5630
|
+
* Get SDK circuit breaker state
|
|
5631
|
+
*/
|
|
5632
|
+
getSDKCircuitBreakerState() {
|
|
5633
|
+
return this.sdkCircuitBreaker.getState();
|
|
5634
|
+
}
|
|
5635
|
+
/**
|
|
5636
|
+
* Get CLI circuit breaker state
|
|
5637
|
+
*/
|
|
5638
|
+
getCLICircuitBreakerState() {
|
|
5639
|
+
return this.cliCircuitBreaker.getState();
|
|
5640
|
+
}
|
|
5641
|
+
/**
|
|
5642
|
+
* Reset both circuit breakers
|
|
5643
|
+
*/
|
|
5644
|
+
resetCircuitBreakers() {
|
|
5645
|
+
this.sdkCircuitBreaker.reset();
|
|
5646
|
+
this.cliCircuitBreaker.reset();
|
|
5647
|
+
logger.debug("Circuit breakers reset", { provider: this.providerName });
|
|
5648
|
+
}
|
|
5649
|
+
/**
|
|
5650
|
+
* Clean up resources
|
|
5651
|
+
*/
|
|
5652
|
+
async destroy() {
|
|
5653
|
+
await this.destroySDK();
|
|
5654
|
+
await this.destroyCLI();
|
|
5655
|
+
this.sdkInitialized = false;
|
|
5656
|
+
this.cliInitialized = false;
|
|
5657
|
+
this.activeMode = null;
|
|
5658
|
+
this.initPromise = null;
|
|
5659
|
+
}
|
|
5660
|
+
};
|
|
5661
|
+
}
|
|
5662
|
+
});
|
|
5663
|
+
|
|
5664
|
+
// src/integrations/ax-glm/types.ts
|
|
5665
|
+
function normalizeGLMModel(model) {
|
|
5666
|
+
return GLM_MODEL_MAPPING[model] || model;
|
|
5667
|
+
}
|
|
5668
|
+
var GLM_MODEL_MAPPING, GLM_DEFAULT_BASE_URL, GLM_DEFAULT_MODEL, GLM_DEFAULT_COMMAND;
|
|
5669
|
+
var init_types2 = __esm({
|
|
5670
|
+
"src/integrations/ax-glm/types.ts"() {
|
|
5671
|
+
init_esm_shims();
|
|
5672
|
+
GLM_MODEL_MAPPING = {
|
|
5673
|
+
"glm-4-plus": "glm-4.6",
|
|
5674
|
+
"glm-4v": "glm-4.5v",
|
|
5675
|
+
"glm-4-air": "glm-4-flash",
|
|
5676
|
+
"glm-4-airx": "glm-4-flash"
|
|
5677
|
+
};
|
|
5678
|
+
GLM_DEFAULT_BASE_URL = "https://open.bigmodel.cn/api/paas/v4";
|
|
5679
|
+
GLM_DEFAULT_MODEL = "glm-4";
|
|
5680
|
+
GLM_DEFAULT_COMMAND = "ax-glm";
|
|
5681
|
+
}
|
|
5682
|
+
});
|
|
5683
|
+
|
|
5684
|
+
// src/integrations/ax-glm/sdk-adapter.ts
|
|
5685
|
+
var GLMSdkAdapter;
|
|
5686
|
+
var init_sdk_adapter2 = __esm({
|
|
5687
|
+
"src/integrations/ax-glm/sdk-adapter.ts"() {
|
|
5688
|
+
init_esm_shims();
|
|
5689
|
+
init_logger();
|
|
5690
|
+
init_validation_limits();
|
|
5691
|
+
init_types2();
|
|
5692
|
+
GLMSdkAdapter = class {
|
|
5693
|
+
client = null;
|
|
5694
|
+
config;
|
|
5695
|
+
initialized = false;
|
|
5696
|
+
constructor(config = {}) {
|
|
5697
|
+
this.config = {
|
|
5698
|
+
apiKey: config.apiKey || process.env.ZAI_API_KEY || "",
|
|
5699
|
+
baseUrl: config.baseUrl || GLM_DEFAULT_BASE_URL,
|
|
5700
|
+
model: config.model || GLM_DEFAULT_MODEL,
|
|
5701
|
+
timeout: config.timeout || TIMEOUTS.PROVIDER_DEFAULT
|
|
5702
|
+
};
|
|
5703
|
+
logger.debug("[GLM SDK] Adapter created", {
|
|
5704
|
+
model: this.config.model,
|
|
5705
|
+
baseUrl: this.config.baseUrl,
|
|
5706
|
+
hasApiKey: !!this.config.apiKey
|
|
5707
|
+
});
|
|
5708
|
+
}
|
|
5709
|
+
/**
|
|
5710
|
+
* Check if SDK is available (OpenAI package installed)
|
|
5711
|
+
*/
|
|
5712
|
+
async isAvailable() {
|
|
5713
|
+
try {
|
|
5714
|
+
if (!this.config.apiKey) {
|
|
5715
|
+
logger.debug("[GLM SDK] No API key configured");
|
|
5716
|
+
return false;
|
|
5717
|
+
}
|
|
5718
|
+
await import('openai');
|
|
5719
|
+
return true;
|
|
5720
|
+
} catch (error) {
|
|
5721
|
+
logger.debug("[GLM SDK] OpenAI SDK not available", {
|
|
5722
|
+
error: error instanceof Error ? error.message : String(error)
|
|
5723
|
+
});
|
|
5724
|
+
return false;
|
|
5725
|
+
}
|
|
5726
|
+
}
|
|
5727
|
+
/**
|
|
5728
|
+
* Initialize the SDK client
|
|
5729
|
+
*/
|
|
5730
|
+
async initialize() {
|
|
5731
|
+
if (this.initialized) {
|
|
5732
|
+
return;
|
|
5733
|
+
}
|
|
5734
|
+
try {
|
|
5735
|
+
const OpenAI = (await import('openai')).default;
|
|
5736
|
+
this.client = new OpenAI({
|
|
5737
|
+
apiKey: this.config.apiKey,
|
|
5738
|
+
baseURL: this.config.baseUrl,
|
|
5739
|
+
timeout: this.config.timeout
|
|
5740
|
+
});
|
|
5741
|
+
this.initialized = true;
|
|
5742
|
+
logger.debug("[GLM SDK] Client initialized", {
|
|
5743
|
+
model: this.config.model
|
|
5744
|
+
});
|
|
5745
|
+
} catch (error) {
|
|
5746
|
+
throw new Error(
|
|
5747
|
+
`Failed to initialize GLM SDK: ${error instanceof Error ? error.message : String(error)}`
|
|
5748
|
+
);
|
|
5749
|
+
}
|
|
5750
|
+
}
|
|
5751
|
+
/**
|
|
5752
|
+
* Execute a request using the GLM SDK
|
|
5753
|
+
*/
|
|
5754
|
+
async execute(request) {
|
|
5755
|
+
if (!this.initialized) {
|
|
5756
|
+
await this.initialize();
|
|
5757
|
+
}
|
|
5758
|
+
const startTime = Date.now();
|
|
5759
|
+
try {
|
|
5760
|
+
const messages = [];
|
|
5761
|
+
if (request.systemPrompt) {
|
|
5762
|
+
messages.push({
|
|
5763
|
+
role: "system",
|
|
5764
|
+
content: request.systemPrompt
|
|
5765
|
+
});
|
|
5766
|
+
}
|
|
5767
|
+
messages.push({
|
|
5768
|
+
role: "user",
|
|
5769
|
+
content: request.prompt
|
|
5770
|
+
});
|
|
5771
|
+
const model = normalizeGLMModel(this.config.model);
|
|
5772
|
+
logger.debug("[GLM SDK] Executing request", {
|
|
5773
|
+
model,
|
|
5774
|
+
messageCount: messages.length,
|
|
5775
|
+
promptLength: request.prompt.length
|
|
5776
|
+
});
|
|
5777
|
+
const openaiClient = this.client;
|
|
5778
|
+
const response = await openaiClient.chat.completions.create({
|
|
5779
|
+
model,
|
|
5780
|
+
messages,
|
|
5781
|
+
max_tokens: request.maxTokens,
|
|
5782
|
+
temperature: request.temperature,
|
|
5783
|
+
stream: false
|
|
5784
|
+
});
|
|
5785
|
+
const latencyMs = Date.now() - startTime;
|
|
5786
|
+
if (!response.choices || response.choices.length === 0) {
|
|
5787
|
+
throw new Error("GLM API returned empty choices array");
|
|
5788
|
+
}
|
|
5789
|
+
const choice = response.choices[0];
|
|
5790
|
+
const content = choice?.message?.content || "";
|
|
5791
|
+
const finishReason = choice?.finish_reason || "unknown";
|
|
5792
|
+
logger.debug("[GLM SDK] Request completed", {
|
|
5793
|
+
model: response.model,
|
|
5794
|
+
latencyMs,
|
|
5795
|
+
tokensUsed: response.usage?.total_tokens
|
|
5796
|
+
});
|
|
5797
|
+
return {
|
|
5798
|
+
content,
|
|
5799
|
+
model: response.model,
|
|
5800
|
+
tokensUsed: response.usage ? {
|
|
5801
|
+
prompt: response.usage.prompt_tokens,
|
|
5802
|
+
completion: response.usage.completion_tokens,
|
|
5803
|
+
total: response.usage.total_tokens
|
|
5804
|
+
} : { prompt: 0, completion: 0, total: 0 },
|
|
5805
|
+
latencyMs,
|
|
5806
|
+
finishReason,
|
|
5807
|
+
cached: false
|
|
5808
|
+
};
|
|
5809
|
+
} catch (error) {
|
|
5810
|
+
const latencyMs = Date.now() - startTime;
|
|
5811
|
+
logger.error("[GLM SDK] Request failed", {
|
|
5812
|
+
error: error instanceof Error ? error.message : String(error),
|
|
5813
|
+
latencyMs
|
|
5814
|
+
});
|
|
5815
|
+
throw error;
|
|
5816
|
+
}
|
|
5817
|
+
}
|
|
5818
|
+
/**
|
|
5819
|
+
* Get the configured model
|
|
5820
|
+
*/
|
|
5821
|
+
getModel() {
|
|
5822
|
+
return this.config.model;
|
|
5823
|
+
}
|
|
5824
|
+
/**
|
|
5825
|
+
* Clean up resources
|
|
5826
|
+
*/
|
|
5827
|
+
async destroy() {
|
|
5828
|
+
this.client = null;
|
|
5829
|
+
this.initialized = false;
|
|
5830
|
+
logger.debug("[GLM SDK] Adapter destroyed");
|
|
5831
|
+
}
|
|
5832
|
+
};
|
|
5833
|
+
}
|
|
5834
|
+
});
|
|
5835
|
+
var execAsync, GLMCliWrapper;
|
|
5836
|
+
var init_cli_wrapper2 = __esm({
|
|
5837
|
+
"src/integrations/ax-glm/cli-wrapper.ts"() {
|
|
5838
|
+
init_esm_shims();
|
|
5839
|
+
init_logger();
|
|
5840
|
+
init_validation_limits();
|
|
5841
|
+
init_types2();
|
|
5842
|
+
execAsync = promisify(exec);
|
|
5843
|
+
GLMCliWrapper = class {
|
|
5844
|
+
config;
|
|
5845
|
+
cliPath = null;
|
|
5846
|
+
cliVersion = null;
|
|
5847
|
+
constructor(config = {}) {
|
|
5848
|
+
this.config = {
|
|
5849
|
+
command: config.command || GLM_DEFAULT_COMMAND,
|
|
5850
|
+
model: config.model || GLM_DEFAULT_MODEL,
|
|
5851
|
+
timeout: config.timeout || TIMEOUTS.PROVIDER_DEFAULT
|
|
5852
|
+
};
|
|
5853
|
+
logger.debug("[GLM CLI] Wrapper created", {
|
|
5854
|
+
command: this.config.command,
|
|
5855
|
+
model: this.config.model
|
|
5856
|
+
});
|
|
5857
|
+
}
|
|
5858
|
+
/**
|
|
5859
|
+
* Check if CLI is available
|
|
5860
|
+
*/
|
|
5861
|
+
async isAvailable() {
|
|
5862
|
+
try {
|
|
5863
|
+
const { stdout } = await execAsync(`which ${this.config.command}`, {
|
|
5864
|
+
timeout: TIMEOUTS.PROVIDER_DETECTION
|
|
5865
|
+
});
|
|
5866
|
+
this.cliPath = stdout.trim();
|
|
5867
|
+
if (this.cliPath) {
|
|
5868
|
+
try {
|
|
5869
|
+
const { stdout: versionOutput } = await execAsync(
|
|
5870
|
+
`${this.config.command} --version`,
|
|
5871
|
+
{ timeout: TIMEOUTS.PROVIDER_DETECTION }
|
|
5872
|
+
);
|
|
5873
|
+
this.cliVersion = versionOutput.trim();
|
|
5874
|
+
} catch {
|
|
5875
|
+
this.cliVersion = "unknown";
|
|
5876
|
+
}
|
|
5877
|
+
logger.debug("[GLM CLI] CLI available", {
|
|
5878
|
+
path: this.cliPath,
|
|
5879
|
+
version: this.cliVersion
|
|
5880
|
+
});
|
|
5881
|
+
return true;
|
|
5882
|
+
}
|
|
5883
|
+
return false;
|
|
5884
|
+
} catch (error) {
|
|
5885
|
+
logger.debug("[GLM CLI] CLI not available", {
|
|
5886
|
+
error: error instanceof Error ? error.message : String(error)
|
|
5887
|
+
});
|
|
5888
|
+
return false;
|
|
5889
|
+
}
|
|
5890
|
+
}
|
|
5891
|
+
/**
|
|
5892
|
+
* Initialize the CLI wrapper
|
|
5893
|
+
*/
|
|
5894
|
+
async initialize() {
|
|
5895
|
+
const available = await this.isAvailable();
|
|
5896
|
+
if (!available) {
|
|
5897
|
+
throw new Error(`${this.config.command} CLI is not installed or not in PATH`);
|
|
5898
|
+
}
|
|
5899
|
+
}
|
|
5900
|
+
/**
|
|
5901
|
+
* Execute a request using the CLI
|
|
5902
|
+
*/
|
|
5903
|
+
async execute(request) {
|
|
5904
|
+
const startTime = Date.now();
|
|
5905
|
+
try {
|
|
5906
|
+
const args2 = this.buildArgs(request);
|
|
5907
|
+
logger.debug("[GLM CLI] Executing", {
|
|
5908
|
+
command: this.config.command,
|
|
5909
|
+
args: args2,
|
|
5910
|
+
promptLength: request.prompt.length
|
|
5911
|
+
});
|
|
5912
|
+
const result = await this.spawnCLI(args2, request.prompt);
|
|
5913
|
+
const latencyMs = Date.now() - startTime;
|
|
5914
|
+
const response = this.parseResponse(result);
|
|
5915
|
+
logger.debug("[GLM CLI] Request completed", {
|
|
5916
|
+
latencyMs,
|
|
5917
|
+
contentLength: response.content.length
|
|
5918
|
+
});
|
|
5919
|
+
return {
|
|
5920
|
+
...response,
|
|
5921
|
+
latencyMs,
|
|
5922
|
+
cached: false
|
|
5923
|
+
};
|
|
5924
|
+
} catch (error) {
|
|
5925
|
+
const latencyMs = Date.now() - startTime;
|
|
5926
|
+
logger.error("[GLM CLI] Request failed", {
|
|
5927
|
+
error: error instanceof Error ? error.message : String(error),
|
|
5928
|
+
latencyMs
|
|
5929
|
+
});
|
|
5930
|
+
throw error;
|
|
5931
|
+
}
|
|
5932
|
+
}
|
|
5933
|
+
/**
|
|
5934
|
+
* Build CLI arguments
|
|
5935
|
+
*/
|
|
5936
|
+
buildArgs(request) {
|
|
5937
|
+
const args2 = ["-p"];
|
|
5938
|
+
const model = normalizeGLMModel(this.config.model);
|
|
5939
|
+
if (model !== "glm-4") {
|
|
5940
|
+
args2.push("--model", model);
|
|
5941
|
+
}
|
|
5942
|
+
if (request.systemPrompt) {
|
|
5943
|
+
args2.push("--system", request.systemPrompt);
|
|
5944
|
+
}
|
|
5945
|
+
if (request.maxTokens) {
|
|
5946
|
+
args2.push("--max-tokens", String(request.maxTokens));
|
|
5947
|
+
}
|
|
5948
|
+
if (request.temperature !== void 0) {
|
|
5949
|
+
args2.push("--temperature", String(request.temperature));
|
|
5950
|
+
}
|
|
5951
|
+
return args2;
|
|
5952
|
+
}
|
|
5953
|
+
/**
|
|
5954
|
+
* Spawn CLI process and get output
|
|
5955
|
+
*/
|
|
5956
|
+
spawnCLI(args2, prompt) {
|
|
5957
|
+
return new Promise((resolve5, reject) => {
|
|
5958
|
+
const process2 = spawn(this.config.command, args2, {
|
|
5959
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
5960
|
+
timeout: this.config.timeout
|
|
5961
|
+
});
|
|
5962
|
+
let stdout = "";
|
|
5963
|
+
let stderr = "";
|
|
5964
|
+
process2.stdout.on("data", (data) => {
|
|
5965
|
+
stdout += data.toString();
|
|
5966
|
+
});
|
|
5967
|
+
process2.stderr.on("data", (data) => {
|
|
5968
|
+
stderr += data.toString();
|
|
5969
|
+
});
|
|
5970
|
+
process2.on("error", (error) => {
|
|
5971
|
+
reject(new Error(`CLI process error: ${error.message}`));
|
|
5972
|
+
});
|
|
5973
|
+
process2.on("close", (code) => {
|
|
5974
|
+
if (code === 0) {
|
|
5975
|
+
resolve5(stdout);
|
|
5976
|
+
} else {
|
|
5977
|
+
reject(new Error(
|
|
5978
|
+
`CLI exited with code ${code}: ${stderr || "No error message"}`
|
|
5979
|
+
));
|
|
5980
|
+
}
|
|
5981
|
+
});
|
|
5982
|
+
process2.stdin.write(prompt);
|
|
5983
|
+
process2.stdin.end();
|
|
5984
|
+
});
|
|
5985
|
+
}
|
|
5986
|
+
/**
|
|
5987
|
+
* Parse CLI response
|
|
5988
|
+
*/
|
|
5989
|
+
parseResponse(output) {
|
|
5990
|
+
try {
|
|
5991
|
+
const parsed = JSON.parse(output.trim());
|
|
5992
|
+
if (parsed.content || parsed.message) {
|
|
5993
|
+
return {
|
|
5994
|
+
content: parsed.content || parsed.message || "",
|
|
5995
|
+
model: parsed.model || normalizeGLMModel(this.config.model),
|
|
5996
|
+
tokensUsed: parsed.usage ? {
|
|
5997
|
+
prompt: parsed.usage.prompt_tokens || 0,
|
|
5998
|
+
completion: parsed.usage.completion_tokens || 0,
|
|
5999
|
+
total: parsed.usage.total_tokens || 0
|
|
6000
|
+
} : { prompt: 0, completion: 0, total: 0 },
|
|
6001
|
+
finishReason: parsed.finish_reason || "stop"
|
|
6002
|
+
};
|
|
6003
|
+
}
|
|
6004
|
+
} catch {
|
|
6005
|
+
}
|
|
6006
|
+
return {
|
|
6007
|
+
content: output.trim(),
|
|
6008
|
+
model: normalizeGLMModel(this.config.model),
|
|
6009
|
+
tokensUsed: { prompt: 0, completion: 0, total: 0 },
|
|
6010
|
+
finishReason: "stop"
|
|
6011
|
+
};
|
|
6012
|
+
}
|
|
6013
|
+
/**
|
|
6014
|
+
* Get the configured model
|
|
6015
|
+
*/
|
|
6016
|
+
getModel() {
|
|
6017
|
+
return this.config.model;
|
|
6018
|
+
}
|
|
6019
|
+
/**
|
|
6020
|
+
* Get CLI version
|
|
6021
|
+
*/
|
|
6022
|
+
getVersion() {
|
|
6023
|
+
return this.cliVersion;
|
|
6024
|
+
}
|
|
6025
|
+
/**
|
|
6026
|
+
* Get CLI command
|
|
6027
|
+
*/
|
|
6028
|
+
getCommand() {
|
|
6029
|
+
return this.config.command;
|
|
6030
|
+
}
|
|
6031
|
+
/**
|
|
6032
|
+
* Clean up resources
|
|
6033
|
+
*/
|
|
6034
|
+
async destroy() {
|
|
6035
|
+
logger.debug("[GLM CLI] Wrapper destroyed");
|
|
6036
|
+
}
|
|
6037
|
+
};
|
|
6038
|
+
}
|
|
6039
|
+
});
|
|
6040
|
+
|
|
6041
|
+
// src/integrations/ax-glm/hybrid-adapter.ts
|
|
6042
|
+
var GLMHybridAdapter;
|
|
6043
|
+
var init_hybrid_adapter2 = __esm({
|
|
6044
|
+
"src/integrations/ax-glm/hybrid-adapter.ts"() {
|
|
6045
|
+
init_esm_shims();
|
|
6046
|
+
init_hybrid_adapter_base();
|
|
6047
|
+
init_sdk_adapter2();
|
|
6048
|
+
init_cli_wrapper2();
|
|
6049
|
+
init_types2();
|
|
6050
|
+
init_logger();
|
|
6051
|
+
GLMHybridAdapter = class extends HybridAdapterBase {
|
|
6052
|
+
sdkAdapter = null;
|
|
6053
|
+
cliWrapper = null;
|
|
6054
|
+
model;
|
|
6055
|
+
sdkConfig;
|
|
6056
|
+
cliConfig;
|
|
6057
|
+
constructor(options = {}) {
|
|
6058
|
+
const baseOptions = {
|
|
6059
|
+
mode: options.mode || "auto",
|
|
6060
|
+
providerName: "glm",
|
|
6061
|
+
maxRetries: options.maxRetries ?? 1
|
|
6062
|
+
};
|
|
6063
|
+
super(baseOptions);
|
|
6064
|
+
this.model = options.model || GLM_DEFAULT_MODEL;
|
|
6065
|
+
this.sdkConfig = {
|
|
6066
|
+
apiKey: options.apiKey,
|
|
6067
|
+
baseUrl: options.baseUrl,
|
|
6068
|
+
timeout: options.timeout
|
|
6069
|
+
};
|
|
6070
|
+
this.cliConfig = {
|
|
6071
|
+
command: options.command,
|
|
6072
|
+
timeout: options.timeout
|
|
6073
|
+
};
|
|
6074
|
+
logger.debug("[GLM Hybrid] Adapter created", {
|
|
6075
|
+
mode: this.mode,
|
|
6076
|
+
model: this.model
|
|
6077
|
+
});
|
|
6078
|
+
}
|
|
6079
|
+
/**
|
|
6080
|
+
* Execute request via SDK
|
|
6081
|
+
*/
|
|
6082
|
+
async executeViaSDK(request) {
|
|
6083
|
+
if (!this.sdkAdapter) {
|
|
6084
|
+
throw new Error("GLM SDK adapter not initialized");
|
|
6085
|
+
}
|
|
6086
|
+
return this.sdkAdapter.execute(request);
|
|
6087
|
+
}
|
|
6088
|
+
/**
|
|
6089
|
+
* Execute request via CLI
|
|
6090
|
+
*/
|
|
6091
|
+
async executeViaCLI(request) {
|
|
6092
|
+
if (!this.cliWrapper) {
|
|
6093
|
+
throw new Error("GLM CLI wrapper not initialized");
|
|
6094
|
+
}
|
|
6095
|
+
return this.cliWrapper.execute(request);
|
|
6096
|
+
}
|
|
6097
|
+
/**
|
|
6098
|
+
* Check if SDK is available
|
|
6099
|
+
*/
|
|
6100
|
+
async isSDKAvailable() {
|
|
6101
|
+
try {
|
|
6102
|
+
const adapter = new GLMSdkAdapter({
|
|
6103
|
+
...this.sdkConfig,
|
|
6104
|
+
model: this.model
|
|
6105
|
+
});
|
|
6106
|
+
const available = await adapter.isAvailable();
|
|
6107
|
+
if (!available) {
|
|
6108
|
+
return false;
|
|
6109
|
+
}
|
|
6110
|
+
this.sdkAdapter = adapter;
|
|
6111
|
+
return true;
|
|
6112
|
+
} catch (error) {
|
|
6113
|
+
logger.debug("[GLM Hybrid] SDK availability check failed", {
|
|
6114
|
+
error: error instanceof Error ? error.message : String(error)
|
|
6115
|
+
});
|
|
6116
|
+
return false;
|
|
6117
|
+
}
|
|
6118
|
+
}
|
|
6119
|
+
/**
|
|
6120
|
+
* Check if CLI is available
|
|
6121
|
+
*/
|
|
6122
|
+
async isCLIAvailable() {
|
|
6123
|
+
try {
|
|
6124
|
+
const wrapper = new GLMCliWrapper({
|
|
6125
|
+
...this.cliConfig,
|
|
6126
|
+
model: this.model
|
|
6127
|
+
});
|
|
6128
|
+
const available = await wrapper.isAvailable();
|
|
6129
|
+
if (!available) {
|
|
6130
|
+
return false;
|
|
6131
|
+
}
|
|
6132
|
+
this.cliWrapper = wrapper;
|
|
6133
|
+
return true;
|
|
6134
|
+
} catch (error) {
|
|
6135
|
+
logger.debug("[GLM Hybrid] CLI availability check failed", {
|
|
6136
|
+
error: error instanceof Error ? error.message : String(error)
|
|
6137
|
+
});
|
|
6138
|
+
return false;
|
|
6139
|
+
}
|
|
6140
|
+
}
|
|
6141
|
+
/**
|
|
6142
|
+
* Initialize SDK adapter
|
|
6143
|
+
*/
|
|
6144
|
+
async initializeSDK() {
|
|
6145
|
+
if (!this.sdkAdapter) {
|
|
6146
|
+
this.sdkAdapter = new GLMSdkAdapter({
|
|
6147
|
+
...this.sdkConfig,
|
|
6148
|
+
model: this.model
|
|
6149
|
+
});
|
|
6150
|
+
}
|
|
6151
|
+
await this.sdkAdapter.initialize();
|
|
6152
|
+
logger.debug("[GLM Hybrid] SDK initialized", {
|
|
6153
|
+
model: this.model
|
|
6154
|
+
});
|
|
6155
|
+
}
|
|
6156
|
+
/**
|
|
6157
|
+
* Initialize CLI wrapper
|
|
6158
|
+
*/
|
|
6159
|
+
async initializeCLI() {
|
|
6160
|
+
if (!this.cliWrapper) {
|
|
6161
|
+
this.cliWrapper = new GLMCliWrapper({
|
|
6162
|
+
...this.cliConfig,
|
|
6163
|
+
model: this.model
|
|
6164
|
+
});
|
|
6165
|
+
}
|
|
6166
|
+
await this.cliWrapper.initialize();
|
|
6167
|
+
logger.debug("[GLM Hybrid] CLI initialized", {
|
|
6168
|
+
model: this.model,
|
|
6169
|
+
version: this.cliWrapper.getVersion()
|
|
6170
|
+
});
|
|
6171
|
+
}
|
|
6172
|
+
/**
|
|
6173
|
+
* Clean up SDK resources
|
|
6174
|
+
*/
|
|
6175
|
+
async destroySDK() {
|
|
6176
|
+
if (this.sdkAdapter) {
|
|
6177
|
+
await this.sdkAdapter.destroy();
|
|
6178
|
+
this.sdkAdapter = null;
|
|
6179
|
+
}
|
|
6180
|
+
}
|
|
6181
|
+
/**
|
|
6182
|
+
* Clean up CLI resources
|
|
6183
|
+
*/
|
|
6184
|
+
async destroyCLI() {
|
|
6185
|
+
if (this.cliWrapper) {
|
|
6186
|
+
await this.cliWrapper.destroy();
|
|
6187
|
+
this.cliWrapper = null;
|
|
6188
|
+
}
|
|
6189
|
+
}
|
|
6190
|
+
/**
|
|
6191
|
+
* Get the configured model
|
|
6192
|
+
*/
|
|
6193
|
+
getModel() {
|
|
6194
|
+
return this.model;
|
|
6195
|
+
}
|
|
6196
|
+
/**
|
|
6197
|
+
* Get the CLI command being used
|
|
6198
|
+
*/
|
|
6199
|
+
getCommand() {
|
|
6200
|
+
return this.cliWrapper?.getCommand() || this.cliConfig.command || "ax-glm";
|
|
6201
|
+
}
|
|
6202
|
+
/**
|
|
6203
|
+
* Get CLI version
|
|
6204
|
+
*/
|
|
6205
|
+
getCLIVersion() {
|
|
6206
|
+
return this.cliWrapper?.getVersion() || null;
|
|
6207
|
+
}
|
|
6208
|
+
};
|
|
6209
|
+
}
|
|
6210
|
+
});
|
|
6211
|
+
|
|
6212
|
+
// src/integrations/ax-glm/sdk-only-adapter.ts
|
|
6213
|
+
var GLMSdkOnlyAdapter;
|
|
6214
|
+
var init_sdk_only_adapter = __esm({
|
|
6215
|
+
"src/integrations/ax-glm/sdk-only-adapter.ts"() {
|
|
6216
|
+
init_esm_shims();
|
|
6217
|
+
init_logger();
|
|
6218
|
+
init_sdk_adapter2();
|
|
6219
|
+
init_types2();
|
|
6220
|
+
GLMSdkOnlyAdapter = class {
|
|
6221
|
+
sdkAdapter;
|
|
6222
|
+
initialized = false;
|
|
6223
|
+
model;
|
|
6224
|
+
maxRetries;
|
|
6225
|
+
retryDelayMs;
|
|
6226
|
+
constructor(options = {}) {
|
|
6227
|
+
this.model = options.model || GLM_DEFAULT_MODEL;
|
|
6228
|
+
this.maxRetries = options.maxRetries ?? 2;
|
|
6229
|
+
this.retryDelayMs = options.retryDelayMs ?? 1e3;
|
|
6230
|
+
this.sdkAdapter = new GLMSdkAdapter({
|
|
6231
|
+
model: this.model,
|
|
6232
|
+
apiKey: options.apiKey,
|
|
6233
|
+
baseUrl: options.baseUrl,
|
|
6234
|
+
timeout: options.timeout
|
|
6235
|
+
});
|
|
6236
|
+
logger.debug("[GLM SDK-Only] Adapter created", {
|
|
6237
|
+
model: this.model,
|
|
6238
|
+
maxRetries: this.maxRetries
|
|
6239
|
+
});
|
|
6240
|
+
}
|
|
6241
|
+
/**
|
|
6242
|
+
* Check if SDK is available
|
|
6243
|
+
*/
|
|
6244
|
+
async isAvailable() {
|
|
6245
|
+
return this.sdkAdapter.isAvailable();
|
|
6246
|
+
}
|
|
6247
|
+
/**
|
|
6248
|
+
* Initialize the adapter
|
|
6249
|
+
*/
|
|
6250
|
+
async initialize() {
|
|
6251
|
+
if (this.initialized) {
|
|
6252
|
+
return;
|
|
6253
|
+
}
|
|
6254
|
+
const available = await this.sdkAdapter.isAvailable();
|
|
6255
|
+
if (!available) {
|
|
6256
|
+
throw new Error("GLM SDK not available - check API key and openai package");
|
|
6257
|
+
}
|
|
6258
|
+
await this.sdkAdapter.initialize();
|
|
6259
|
+
this.initialized = true;
|
|
6260
|
+
logger.debug("[GLM SDK-Only] Initialized", { model: this.model });
|
|
6261
|
+
}
|
|
6262
|
+
/**
|
|
6263
|
+
* Execute a request using GLM SDK
|
|
6264
|
+
*
|
|
6265
|
+
* Retries transient errors up to maxRetries times with exponential backoff.
|
|
6266
|
+
* Does NOT fall back to CLI - throws on persistent failure.
|
|
6267
|
+
*/
|
|
6268
|
+
async execute(request) {
|
|
6269
|
+
if (!this.initialized) {
|
|
6270
|
+
await this.initialize();
|
|
6271
|
+
}
|
|
6272
|
+
const startTime = Date.now();
|
|
6273
|
+
let lastError;
|
|
6274
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
6275
|
+
try {
|
|
6276
|
+
const response = await this.sdkAdapter.execute(request);
|
|
6277
|
+
logger.debug("[GLM SDK-Only] Execution succeeded", {
|
|
6278
|
+
model: this.model,
|
|
6279
|
+
attempt: attempt + 1,
|
|
6280
|
+
latencyMs: response.latencyMs
|
|
6281
|
+
});
|
|
6282
|
+
return response;
|
|
6283
|
+
} catch (error) {
|
|
6284
|
+
lastError = error;
|
|
6285
|
+
logger.warn("[GLM SDK-Only] Execution failed", {
|
|
6286
|
+
model: this.model,
|
|
6287
|
+
attempt: attempt + 1,
|
|
6288
|
+
maxRetries: this.maxRetries,
|
|
6289
|
+
error: lastError.message
|
|
6290
|
+
});
|
|
6291
|
+
if (attempt < this.maxRetries && this.isRetryableError(lastError)) {
|
|
6292
|
+
const delay = this.retryDelayMs * Math.pow(2, attempt);
|
|
6293
|
+
logger.debug("[GLM SDK-Only] Retrying after delay", {
|
|
6294
|
+
attempt: attempt + 1,
|
|
6295
|
+
delayMs: delay
|
|
6296
|
+
});
|
|
6297
|
+
await this.sleep(delay);
|
|
6298
|
+
continue;
|
|
6299
|
+
}
|
|
6300
|
+
break;
|
|
6301
|
+
}
|
|
6302
|
+
}
|
|
6303
|
+
const totalTime = Date.now() - startTime;
|
|
6304
|
+
logger.error("[GLM SDK-Only] All retries exhausted", {
|
|
6305
|
+
model: this.model,
|
|
6306
|
+
totalAttempts: this.maxRetries + 1,
|
|
6307
|
+
totalTimeMs: totalTime,
|
|
6308
|
+
lastError: lastError?.message
|
|
6309
|
+
});
|
|
6310
|
+
throw new Error(
|
|
6311
|
+
`GLM SDK execution failed after ${this.maxRetries + 1} attempts: ${lastError?.message || "Unknown error"}`
|
|
6312
|
+
);
|
|
6313
|
+
}
|
|
6314
|
+
/**
|
|
6315
|
+
* Check if error is retryable
|
|
6316
|
+
*/
|
|
6317
|
+
isRetryableError(error) {
|
|
6318
|
+
const message = error.message.toLowerCase();
|
|
6319
|
+
if (message.includes("rate limit") || message.includes("429")) {
|
|
6320
|
+
return true;
|
|
6321
|
+
}
|
|
6322
|
+
if (message.includes("500") || message.includes("502") || message.includes("503") || message.includes("504")) {
|
|
6323
|
+
return true;
|
|
6324
|
+
}
|
|
6325
|
+
if (message.includes("timeout") || message.includes("etimedout")) {
|
|
6326
|
+
return true;
|
|
6327
|
+
}
|
|
6328
|
+
if (message.includes("econnreset") || message.includes("econnrefused")) {
|
|
6329
|
+
return true;
|
|
6330
|
+
}
|
|
6331
|
+
return false;
|
|
6332
|
+
}
|
|
6333
|
+
/**
|
|
6334
|
+
* Sleep utility
|
|
6335
|
+
*/
|
|
6336
|
+
sleep(ms) {
|
|
6337
|
+
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
6338
|
+
}
|
|
6339
|
+
/**
|
|
6340
|
+
* Get the configured model
|
|
6341
|
+
*/
|
|
6342
|
+
getModel() {
|
|
6343
|
+
return this.model;
|
|
6344
|
+
}
|
|
6345
|
+
/**
|
|
6346
|
+
* Clean up resources
|
|
6347
|
+
*/
|
|
6348
|
+
async destroy() {
|
|
6349
|
+
await this.sdkAdapter.destroy();
|
|
6350
|
+
this.initialized = false;
|
|
6351
|
+
logger.debug("[GLM SDK-Only] Adapter destroyed");
|
|
6352
|
+
}
|
|
6353
|
+
};
|
|
6354
|
+
}
|
|
6355
|
+
});
|
|
6356
|
+
|
|
6357
|
+
// src/integrations/ax-glm/mcp-client-mode.ts
|
|
6358
|
+
var init_mcp_client_mode = __esm({
|
|
6359
|
+
"src/integrations/ax-glm/mcp-client-mode.ts"() {
|
|
6360
|
+
init_esm_shims();
|
|
6361
|
+
init_logger();
|
|
6362
|
+
init_sdk_adapter2();
|
|
6363
|
+
}
|
|
6364
|
+
});
|
|
6365
|
+
|
|
6366
|
+
// src/integrations/ax-glm/index.ts
|
|
6367
|
+
var init_ax_glm = __esm({
|
|
6368
|
+
"src/integrations/ax-glm/index.ts"() {
|
|
6369
|
+
init_esm_shims();
|
|
6370
|
+
init_hybrid_adapter2();
|
|
6371
|
+
init_sdk_adapter2();
|
|
6372
|
+
init_cli_wrapper2();
|
|
6373
|
+
init_sdk_only_adapter();
|
|
6374
|
+
init_mcp_client_mode();
|
|
6375
|
+
init_types2();
|
|
6376
|
+
}
|
|
6377
|
+
});
|
|
6378
|
+
|
|
6379
|
+
// src/providers/glm-provider.ts
|
|
6380
|
+
var glm_provider_exports = {};
|
|
6381
|
+
__export(glm_provider_exports, {
|
|
6382
|
+
GLMProvider: () => GLMProvider,
|
|
6383
|
+
default: () => GLMProvider
|
|
6384
|
+
});
|
|
6385
|
+
var MODEL_MAPPING, GLMProvider;
|
|
6386
|
+
var init_glm_provider = __esm({
|
|
6387
|
+
"src/providers/glm-provider.ts"() {
|
|
6388
|
+
init_esm_shims();
|
|
6389
|
+
init_base_provider();
|
|
6390
|
+
init_logger();
|
|
6391
|
+
init_ax_glm();
|
|
6392
|
+
MODEL_MAPPING = {
|
|
6393
|
+
"glm-4-plus": "glm-4.6",
|
|
6394
|
+
"glm-4v": "glm-4.5v",
|
|
6395
|
+
"glm-4-air": "glm-4-flash",
|
|
6396
|
+
"glm-4-airx": "glm-4-flash"
|
|
6397
|
+
};
|
|
6398
|
+
GLMProvider = class _GLMProvider extends BaseProvider {
|
|
6399
|
+
/** Selected model */
|
|
6400
|
+
model;
|
|
6401
|
+
/** SDK-only adapter for direct execution (v13.0.0) */
|
|
6402
|
+
sdkOnlyAdapter = null;
|
|
6403
|
+
/** Legacy hybrid adapter for 'auto' mode (backwards compatibility) */
|
|
6404
|
+
hybridAdapter = null;
|
|
6405
|
+
/** Provider configuration */
|
|
6406
|
+
glmConfig;
|
|
6407
|
+
/** Supported models */
|
|
6408
|
+
static SUPPORTED_MODELS = [
|
|
6409
|
+
"glm-4.6",
|
|
6410
|
+
"glm-4.5v",
|
|
6411
|
+
"glm-4",
|
|
6412
|
+
"glm-4-flash",
|
|
6413
|
+
// Legacy aliases
|
|
6414
|
+
"glm-4-plus",
|
|
6415
|
+
"glm-4v",
|
|
6416
|
+
"glm-4-air",
|
|
6417
|
+
"glm-4-airx"
|
|
6418
|
+
];
|
|
6419
|
+
constructor(config) {
|
|
6420
|
+
super({
|
|
6421
|
+
...config,
|
|
6422
|
+
command: "ax-glm"
|
|
6423
|
+
});
|
|
6424
|
+
this.glmConfig = config;
|
|
6425
|
+
const requestedModel = config.model || "glm-4";
|
|
6426
|
+
if (!_GLMProvider.SUPPORTED_MODELS.includes(requestedModel)) {
|
|
6427
|
+
logger.warn(`[GLM] Unknown model: ${requestedModel}. Using glm-4.`);
|
|
6428
|
+
this.model = "glm-4";
|
|
6429
|
+
} else {
|
|
6430
|
+
this.model = requestedModel;
|
|
6431
|
+
}
|
|
6432
|
+
logger.debug("[GLM Provider] Initialized", {
|
|
6433
|
+
model: this.model,
|
|
6434
|
+
mode: config.mode || "auto"
|
|
6435
|
+
});
|
|
6436
|
+
}
|
|
6437
|
+
/**
|
|
6438
|
+
* Get the normalized model name
|
|
6439
|
+
*/
|
|
6440
|
+
getNormalizedModel() {
|
|
6441
|
+
return MODEL_MAPPING[this.model] || this.model;
|
|
6442
|
+
}
|
|
6443
|
+
/**
|
|
6444
|
+
* Get or create SDK-only adapter (v13.0.0 default)
|
|
6445
|
+
*/
|
|
6446
|
+
getSdkOnlyAdapter() {
|
|
6447
|
+
if (!this.sdkOnlyAdapter) {
|
|
6448
|
+
this.sdkOnlyAdapter = new GLMSdkOnlyAdapter({
|
|
6449
|
+
model: this.model,
|
|
6450
|
+
apiKey: this.glmConfig.apiKey,
|
|
6451
|
+
baseUrl: this.glmConfig.baseUrl,
|
|
6452
|
+
timeout: this.glmConfig.timeout
|
|
6453
|
+
});
|
|
6454
|
+
}
|
|
6455
|
+
return this.sdkOnlyAdapter;
|
|
6456
|
+
}
|
|
6457
|
+
/**
|
|
6458
|
+
* Get or create hybrid adapter (legacy, for 'auto' mode only)
|
|
6459
|
+
*/
|
|
6460
|
+
getHybridAdapter() {
|
|
6461
|
+
if (!this.hybridAdapter) {
|
|
6462
|
+
const options = {
|
|
6463
|
+
mode: this.glmConfig.mode || "auto",
|
|
6464
|
+
model: this.model,
|
|
6465
|
+
apiKey: this.glmConfig.apiKey,
|
|
6466
|
+
baseUrl: this.glmConfig.baseUrl,
|
|
6467
|
+
command: "ax-glm",
|
|
6468
|
+
timeout: this.glmConfig.timeout
|
|
6469
|
+
};
|
|
6470
|
+
this.hybridAdapter = new GLMHybridAdapter(options);
|
|
6471
|
+
}
|
|
6472
|
+
return this.hybridAdapter;
|
|
6473
|
+
}
|
|
6474
|
+
/**
|
|
6475
|
+
* Execute a task using GLM
|
|
6476
|
+
*
|
|
6477
|
+
* Execution flow (v13.0.0):
|
|
6478
|
+
* 1. Mock mode → return mock response
|
|
6479
|
+
* 2. mode='sdk' (default) → use SDK-only adapter (NO CLI fallback)
|
|
6480
|
+
* 3. mode='auto' (legacy) → use hybrid adapter (SDK with CLI fallback)
|
|
6481
|
+
* 4. mode='cli' → use CLI via BaseProvider (deprecated for GLM)
|
|
6482
|
+
*/
|
|
6483
|
+
async execute(request) {
|
|
6484
|
+
if (process.env.AX_MOCK_PROVIDERS === "true") {
|
|
6485
|
+
return this.createMockResponse(request.prompt);
|
|
6486
|
+
}
|
|
6487
|
+
const effectiveMode = this.glmConfig.mode || "sdk";
|
|
6488
|
+
if (effectiveMode === "cli") {
|
|
6489
|
+
logger.warn("[GLM Provider] CLI mode is deprecated for GLM. Consider using SDK mode.", {
|
|
6490
|
+
model: this.model
|
|
6491
|
+
});
|
|
6492
|
+
return super.execute(request);
|
|
6493
|
+
}
|
|
6494
|
+
if (effectiveMode === "auto") {
|
|
6495
|
+
logger.debug("[GLM Provider] Executing via hybrid adapter (legacy auto mode)", {
|
|
6496
|
+
promptLength: request.prompt.length,
|
|
6497
|
+
model: this.model
|
|
6498
|
+
});
|
|
6499
|
+
const adapter2 = this.getHybridAdapter();
|
|
6500
|
+
return adapter2.execute(request);
|
|
6501
|
+
}
|
|
6502
|
+
logger.debug("[GLM Provider] Executing via SDK-only adapter", {
|
|
6503
|
+
promptLength: request.prompt.length,
|
|
6504
|
+
model: this.model
|
|
6505
|
+
});
|
|
6506
|
+
const adapter = this.getSdkOnlyAdapter();
|
|
6507
|
+
return adapter.execute(request);
|
|
6508
|
+
}
|
|
6509
|
+
/**
|
|
6510
|
+
* Get CLI command
|
|
6511
|
+
*/
|
|
6512
|
+
getCLICommand() {
|
|
6513
|
+
const adapter = this.hybridAdapter;
|
|
6514
|
+
if (adapter) {
|
|
6515
|
+
const activeMode = adapter.getActiveMode();
|
|
6516
|
+
if (activeMode === "sdk") {
|
|
6517
|
+
return "glm-sdk";
|
|
6518
|
+
}
|
|
6519
|
+
}
|
|
6520
|
+
return "ax-glm";
|
|
6521
|
+
}
|
|
6522
|
+
/**
|
|
6523
|
+
* Get CLI arguments for ax-glm headless mode
|
|
6524
|
+
*/
|
|
6525
|
+
getCLIArgs() {
|
|
6526
|
+
const args2 = ["-p"];
|
|
6527
|
+
const normalizedModel = this.getNormalizedModel();
|
|
6528
|
+
if (normalizedModel !== "glm-4") {
|
|
6529
|
+
args2.push("--model", normalizedModel);
|
|
6530
|
+
}
|
|
6531
|
+
return args2;
|
|
6532
|
+
}
|
|
6533
|
+
/**
|
|
6534
|
+
* Create mock response for testing
|
|
6535
|
+
*/
|
|
6536
|
+
createMockResponse(prompt) {
|
|
6537
|
+
return {
|
|
6538
|
+
content: this.getMockResponse(),
|
|
6539
|
+
model: this.getNormalizedModel(),
|
|
6540
|
+
tokensUsed: {
|
|
6541
|
+
prompt: this.estimateTokens(prompt),
|
|
6542
|
+
completion: 50,
|
|
6543
|
+
total: this.estimateTokens(prompt) + 50
|
|
6544
|
+
},
|
|
6545
|
+
latencyMs: 10,
|
|
6546
|
+
finishReason: "stop",
|
|
6547
|
+
cached: false
|
|
6548
|
+
};
|
|
6549
|
+
}
|
|
6550
|
+
/**
|
|
6551
|
+
* Estimate token count
|
|
6552
|
+
*/
|
|
6553
|
+
estimateTokens(text) {
|
|
6554
|
+
return Math.ceil(text.length / 4);
|
|
6555
|
+
}
|
|
6556
|
+
/**
|
|
6557
|
+
* Get mock response for testing
|
|
6558
|
+
*/
|
|
6559
|
+
getMockResponse() {
|
|
6560
|
+
return `[Mock GLM Response]
|
|
6561
|
+
|
|
6562
|
+
This is a mock response from the GLM provider (${this.getNormalizedModel()}).
|
|
6563
|
+
In production, this would be a response from ${this.glmConfig.mode === "sdk" ? "GLM SDK" : "ax-glm CLI"}.
|
|
6564
|
+
|
|
6565
|
+
Model: ${this.getNormalizedModel()}
|
|
6566
|
+
Provider: GLM (Zhipu AI)
|
|
6567
|
+
Mode: ${this.glmConfig.mode || "auto"}`;
|
|
6568
|
+
}
|
|
6569
|
+
/**
|
|
6570
|
+
* Get provider capabilities
|
|
6571
|
+
*/
|
|
6572
|
+
get capabilities() {
|
|
6573
|
+
const model = this.getNormalizedModel();
|
|
6574
|
+
const isVision = model.includes("v") || model === "glm-4.5v";
|
|
6575
|
+
let maxContextTokens = 128e3;
|
|
6576
|
+
if (model === "glm-4.6") maxContextTokens = 2e5;
|
|
6577
|
+
if (model === "glm-4.5v") maxContextTokens = 64e3;
|
|
6578
|
+
const activeMode = this.hybridAdapter?.getActiveMode();
|
|
6579
|
+
const integrationMode = activeMode === "sdk" ? "sdk" : "cli";
|
|
6580
|
+
return {
|
|
6581
|
+
...super.capabilities,
|
|
6582
|
+
supportsStreaming: true,
|
|
6583
|
+
supportsVision: isVision,
|
|
6584
|
+
maxContextTokens,
|
|
6585
|
+
supportedModels: _GLMProvider.SUPPORTED_MODELS,
|
|
6586
|
+
integrationMode
|
|
6587
|
+
};
|
|
6588
|
+
}
|
|
6589
|
+
/**
|
|
6590
|
+
* Get the active execution mode
|
|
6591
|
+
*/
|
|
6592
|
+
getActiveMode() {
|
|
6593
|
+
return this.hybridAdapter?.getActiveMode() || null;
|
|
6594
|
+
}
|
|
6595
|
+
/**
|
|
6596
|
+
* Reset circuit breakers
|
|
6597
|
+
*/
|
|
6598
|
+
resetCircuitBreakers() {
|
|
6599
|
+
this.hybridAdapter?.resetCircuitBreakers();
|
|
6600
|
+
}
|
|
6601
|
+
/**
|
|
6602
|
+
* Clean up resources
|
|
6603
|
+
*/
|
|
6604
|
+
async destroy() {
|
|
6605
|
+
if (this.sdkOnlyAdapter) {
|
|
6606
|
+
await this.sdkOnlyAdapter.destroy();
|
|
6607
|
+
this.sdkOnlyAdapter = null;
|
|
6608
|
+
}
|
|
6609
|
+
if (this.hybridAdapter) {
|
|
6610
|
+
await this.hybridAdapter.destroy();
|
|
6611
|
+
this.hybridAdapter = null;
|
|
6612
|
+
}
|
|
6613
|
+
}
|
|
6614
|
+
/**
|
|
6615
|
+
* Get the list of supported models
|
|
6616
|
+
*/
|
|
6617
|
+
static getSupportedModels() {
|
|
6618
|
+
return [..._GLMProvider.SUPPORTED_MODELS];
|
|
6619
|
+
}
|
|
6620
|
+
};
|
|
6621
|
+
}
|
|
6622
|
+
});
|
|
6623
|
+
|
|
6624
|
+
// src/integrations/ax-grok/types.ts
|
|
6625
|
+
function normalizeGrokModel(model) {
|
|
6626
|
+
return GROK_MODEL_MAPPING[model] || model;
|
|
6627
|
+
}
|
|
6628
|
+
var GROK_MODEL_MAPPING, GROK_DEFAULT_BASE_URL, GROK_DEFAULT_MODEL, GROK_DEFAULT_COMMAND;
|
|
6629
|
+
var init_types3 = __esm({
|
|
6630
|
+
"src/integrations/ax-grok/types.ts"() {
|
|
6631
|
+
init_esm_shims();
|
|
6632
|
+
GROK_MODEL_MAPPING = {
|
|
6633
|
+
"grok-beta": "grok-3"
|
|
6634
|
+
};
|
|
6635
|
+
GROK_DEFAULT_BASE_URL = "https://api.x.ai/v1";
|
|
6636
|
+
GROK_DEFAULT_MODEL = "grok-3";
|
|
6637
|
+
GROK_DEFAULT_COMMAND = "ax-grok";
|
|
6638
|
+
}
|
|
6639
|
+
});
|
|
6640
|
+
|
|
6641
|
+
// src/integrations/ax-grok/sdk-adapter.ts
|
|
6642
|
+
var GrokSdkAdapter;
|
|
6643
|
+
var init_sdk_adapter3 = __esm({
|
|
6644
|
+
"src/integrations/ax-grok/sdk-adapter.ts"() {
|
|
6645
|
+
init_esm_shims();
|
|
6646
|
+
init_logger();
|
|
6647
|
+
init_validation_limits();
|
|
6648
|
+
init_types3();
|
|
6649
|
+
GrokSdkAdapter = class {
|
|
6650
|
+
client = null;
|
|
6651
|
+
config;
|
|
6652
|
+
initialized = false;
|
|
6653
|
+
constructor(config = {}) {
|
|
6654
|
+
this.config = {
|
|
6655
|
+
apiKey: config.apiKey || process.env.XAI_API_KEY || "",
|
|
6656
|
+
baseUrl: config.baseUrl || GROK_DEFAULT_BASE_URL,
|
|
6657
|
+
model: config.model || GROK_DEFAULT_MODEL,
|
|
6658
|
+
timeout: config.timeout || TIMEOUTS.PROVIDER_DEFAULT
|
|
6659
|
+
};
|
|
6660
|
+
logger.debug("[Grok SDK] Adapter created", {
|
|
6661
|
+
model: this.config.model,
|
|
6662
|
+
baseUrl: this.config.baseUrl,
|
|
6663
|
+
hasApiKey: !!this.config.apiKey
|
|
6664
|
+
});
|
|
6665
|
+
}
|
|
6666
|
+
/**
|
|
6667
|
+
* Check if SDK is available (OpenAI package installed and API key present)
|
|
6668
|
+
*/
|
|
6669
|
+
async isAvailable() {
|
|
6670
|
+
try {
|
|
6671
|
+
if (!this.config.apiKey) {
|
|
6672
|
+
logger.debug("[Grok SDK] No API key configured");
|
|
6673
|
+
return false;
|
|
6674
|
+
}
|
|
6675
|
+
await import('openai');
|
|
6676
|
+
return true;
|
|
6677
|
+
} catch (error) {
|
|
6678
|
+
logger.debug("[Grok SDK] OpenAI SDK not available", {
|
|
6679
|
+
error: error instanceof Error ? error.message : String(error)
|
|
6680
|
+
});
|
|
6681
|
+
return false;
|
|
6682
|
+
}
|
|
6683
|
+
}
|
|
6684
|
+
/**
|
|
6685
|
+
* Initialize the SDK client
|
|
6686
|
+
*/
|
|
6687
|
+
async initialize() {
|
|
6688
|
+
if (this.initialized) {
|
|
6689
|
+
return;
|
|
6690
|
+
}
|
|
6691
|
+
try {
|
|
6692
|
+
const OpenAI = (await import('openai')).default;
|
|
6693
|
+
this.client = new OpenAI({
|
|
6694
|
+
apiKey: this.config.apiKey,
|
|
6695
|
+
baseURL: this.config.baseUrl,
|
|
6696
|
+
timeout: this.config.timeout
|
|
6697
|
+
});
|
|
6698
|
+
this.initialized = true;
|
|
6699
|
+
logger.debug("[Grok SDK] Client initialized", {
|
|
6700
|
+
model: this.config.model
|
|
6701
|
+
});
|
|
6702
|
+
} catch (error) {
|
|
6703
|
+
throw new Error(
|
|
6704
|
+
`Failed to initialize Grok SDK: ${error instanceof Error ? error.message : String(error)}`
|
|
6705
|
+
);
|
|
6706
|
+
}
|
|
6707
|
+
}
|
|
6708
|
+
/**
|
|
6709
|
+
* Execute a request using the Grok SDK
|
|
6710
|
+
*/
|
|
6711
|
+
async execute(request) {
|
|
6712
|
+
if (!this.initialized) {
|
|
6713
|
+
await this.initialize();
|
|
6714
|
+
}
|
|
6715
|
+
const startTime = Date.now();
|
|
6716
|
+
try {
|
|
6717
|
+
const messages = [];
|
|
6718
|
+
if (request.systemPrompt) {
|
|
6719
|
+
messages.push({
|
|
6720
|
+
role: "system",
|
|
6721
|
+
content: request.systemPrompt
|
|
6722
|
+
});
|
|
6723
|
+
}
|
|
6724
|
+
messages.push({
|
|
6725
|
+
role: "user",
|
|
6726
|
+
content: request.prompt
|
|
6727
|
+
});
|
|
6728
|
+
const model = normalizeGrokModel(this.config.model);
|
|
6729
|
+
logger.debug("[Grok SDK] Executing request", {
|
|
6730
|
+
model,
|
|
6731
|
+
messageCount: messages.length,
|
|
6732
|
+
promptLength: request.prompt.length
|
|
6733
|
+
});
|
|
6734
|
+
const openaiClient = this.client;
|
|
6735
|
+
const response = await openaiClient.chat.completions.create({
|
|
6736
|
+
model,
|
|
6737
|
+
messages,
|
|
6738
|
+
max_tokens: request.maxTokens,
|
|
6739
|
+
temperature: request.temperature,
|
|
6740
|
+
stream: false
|
|
6741
|
+
});
|
|
6742
|
+
const latencyMs = Date.now() - startTime;
|
|
6743
|
+
if (!response.choices || response.choices.length === 0) {
|
|
6744
|
+
throw new Error("Grok API returned empty choices array");
|
|
6745
|
+
}
|
|
6746
|
+
const choice = response.choices[0];
|
|
6747
|
+
const content = choice?.message?.content || "";
|
|
6748
|
+
const finishReason = choice?.finish_reason || "unknown";
|
|
6749
|
+
logger.debug("[Grok SDK] Request completed", {
|
|
6750
|
+
model: response.model,
|
|
6751
|
+
latencyMs,
|
|
6752
|
+
tokensUsed: response.usage?.total_tokens
|
|
6753
|
+
});
|
|
6754
|
+
return {
|
|
6755
|
+
content,
|
|
6756
|
+
model: response.model,
|
|
6757
|
+
tokensUsed: response.usage ? {
|
|
6758
|
+
prompt: response.usage.prompt_tokens,
|
|
6759
|
+
completion: response.usage.completion_tokens,
|
|
6760
|
+
total: response.usage.total_tokens
|
|
6761
|
+
} : { prompt: 0, completion: 0, total: 0 },
|
|
6762
|
+
latencyMs,
|
|
6763
|
+
finishReason,
|
|
6764
|
+
cached: false
|
|
6765
|
+
};
|
|
6766
|
+
} catch (error) {
|
|
6767
|
+
const latencyMs = Date.now() - startTime;
|
|
6768
|
+
logger.error("[Grok SDK] Request failed", {
|
|
6769
|
+
error: error instanceof Error ? error.message : String(error),
|
|
6770
|
+
latencyMs
|
|
6771
|
+
});
|
|
6772
|
+
throw error;
|
|
6773
|
+
}
|
|
6774
|
+
}
|
|
6775
|
+
/**
|
|
6776
|
+
* Get the configured model
|
|
6777
|
+
*/
|
|
6778
|
+
getModel() {
|
|
6779
|
+
return this.config.model;
|
|
6780
|
+
}
|
|
6781
|
+
/**
|
|
6782
|
+
* Clean up resources
|
|
6783
|
+
*/
|
|
6784
|
+
async destroy() {
|
|
6785
|
+
this.client = null;
|
|
6786
|
+
this.initialized = false;
|
|
6787
|
+
logger.debug("[Grok SDK] Adapter destroyed");
|
|
6788
|
+
}
|
|
6789
|
+
};
|
|
6790
|
+
}
|
|
6791
|
+
});
|
|
6792
|
+
var execAsync2, GrokCliWrapper;
|
|
6793
|
+
var init_cli_wrapper3 = __esm({
|
|
6794
|
+
"src/integrations/ax-grok/cli-wrapper.ts"() {
|
|
6795
|
+
init_esm_shims();
|
|
6796
|
+
init_logger();
|
|
6797
|
+
init_validation_limits();
|
|
6798
|
+
init_types3();
|
|
6799
|
+
execAsync2 = promisify(exec);
|
|
6800
|
+
GrokCliWrapper = class {
|
|
6801
|
+
config;
|
|
6802
|
+
cliPath = null;
|
|
6803
|
+
cliVersion = null;
|
|
6804
|
+
constructor(config = {}) {
|
|
6805
|
+
this.config = {
|
|
6806
|
+
command: config.command || GROK_DEFAULT_COMMAND,
|
|
6807
|
+
model: config.model || GROK_DEFAULT_MODEL,
|
|
6808
|
+
timeout: config.timeout || TIMEOUTS.PROVIDER_DEFAULT
|
|
6809
|
+
};
|
|
6810
|
+
logger.debug("[Grok CLI] Wrapper created", {
|
|
6811
|
+
command: this.config.command,
|
|
6812
|
+
model: this.config.model
|
|
6813
|
+
});
|
|
6814
|
+
}
|
|
6815
|
+
/**
|
|
6816
|
+
* Check if CLI is available
|
|
6817
|
+
*/
|
|
6818
|
+
async isAvailable() {
|
|
6819
|
+
try {
|
|
6820
|
+
const { stdout } = await execAsync2(`which ${this.config.command}`, {
|
|
6821
|
+
timeout: TIMEOUTS.PROVIDER_DETECTION
|
|
6822
|
+
});
|
|
6823
|
+
this.cliPath = stdout.trim();
|
|
6824
|
+
if (this.cliPath) {
|
|
6825
|
+
try {
|
|
6826
|
+
const { stdout: versionOutput } = await execAsync2(
|
|
6827
|
+
`${this.config.command} --version`,
|
|
6828
|
+
{ timeout: TIMEOUTS.PROVIDER_DETECTION }
|
|
6829
|
+
);
|
|
6830
|
+
this.cliVersion = versionOutput.trim();
|
|
6831
|
+
} catch {
|
|
6832
|
+
this.cliVersion = "unknown";
|
|
6833
|
+
}
|
|
6834
|
+
logger.debug("[Grok CLI] CLI available", {
|
|
6835
|
+
path: this.cliPath,
|
|
6836
|
+
version: this.cliVersion
|
|
6837
|
+
});
|
|
6838
|
+
return true;
|
|
6839
|
+
}
|
|
6840
|
+
return false;
|
|
6841
|
+
} catch (error) {
|
|
6842
|
+
logger.debug("[Grok CLI] CLI not available", {
|
|
6843
|
+
error: error instanceof Error ? error.message : String(error)
|
|
6844
|
+
});
|
|
6845
|
+
return false;
|
|
6846
|
+
}
|
|
6847
|
+
}
|
|
6848
|
+
/**
|
|
6849
|
+
* Initialize the CLI wrapper
|
|
6850
|
+
*/
|
|
6851
|
+
async initialize() {
|
|
6852
|
+
const available = await this.isAvailable();
|
|
6853
|
+
if (!available) {
|
|
6854
|
+
throw new Error(`${this.config.command} CLI is not installed or not in PATH`);
|
|
6855
|
+
}
|
|
6856
|
+
}
|
|
6857
|
+
/**
|
|
6858
|
+
* Execute a request using the CLI
|
|
6859
|
+
*/
|
|
6860
|
+
async execute(request) {
|
|
6861
|
+
const startTime = Date.now();
|
|
6862
|
+
try {
|
|
6863
|
+
const args2 = this.buildArgs(request);
|
|
6864
|
+
logger.debug("[Grok CLI] Executing", {
|
|
6865
|
+
command: this.config.command,
|
|
6866
|
+
args: args2,
|
|
6867
|
+
promptLength: request.prompt.length
|
|
6868
|
+
});
|
|
6869
|
+
const result = await this.spawnCLI(args2, request.prompt);
|
|
6870
|
+
const latencyMs = Date.now() - startTime;
|
|
6871
|
+
const response = this.parseResponse(result);
|
|
6872
|
+
logger.debug("[Grok CLI] Request completed", {
|
|
6873
|
+
latencyMs,
|
|
6874
|
+
contentLength: response.content.length
|
|
6875
|
+
});
|
|
6876
|
+
return {
|
|
6877
|
+
...response,
|
|
6878
|
+
latencyMs,
|
|
6879
|
+
cached: false
|
|
6880
|
+
};
|
|
6881
|
+
} catch (error) {
|
|
6882
|
+
const latencyMs = Date.now() - startTime;
|
|
6883
|
+
logger.error("[Grok CLI] Request failed", {
|
|
6884
|
+
error: error instanceof Error ? error.message : String(error),
|
|
6885
|
+
latencyMs
|
|
6886
|
+
});
|
|
6887
|
+
throw error;
|
|
6888
|
+
}
|
|
6889
|
+
}
|
|
6890
|
+
/**
|
|
6891
|
+
* Build CLI arguments
|
|
6892
|
+
*/
|
|
6893
|
+
buildArgs(request) {
|
|
6894
|
+
const args2 = ["-p"];
|
|
6895
|
+
const model = normalizeGrokModel(this.config.model);
|
|
6896
|
+
if (model !== "grok-3") {
|
|
6897
|
+
args2.push("--model", model);
|
|
6898
|
+
}
|
|
6899
|
+
if (request.systemPrompt) {
|
|
6900
|
+
args2.push("--system", request.systemPrompt);
|
|
6901
|
+
}
|
|
6902
|
+
if (request.maxTokens) {
|
|
6903
|
+
args2.push("--max-tokens", String(request.maxTokens));
|
|
6904
|
+
}
|
|
6905
|
+
if (request.temperature !== void 0) {
|
|
6906
|
+
args2.push("--temperature", String(request.temperature));
|
|
6907
|
+
}
|
|
6908
|
+
return args2;
|
|
6909
|
+
}
|
|
6910
|
+
/**
|
|
6911
|
+
* Spawn CLI process and get output
|
|
6912
|
+
*/
|
|
6913
|
+
spawnCLI(args2, prompt) {
|
|
6914
|
+
return new Promise((resolve5, reject) => {
|
|
6915
|
+
const process2 = spawn(this.config.command, args2, {
|
|
6916
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
6917
|
+
timeout: this.config.timeout
|
|
6918
|
+
});
|
|
6919
|
+
let stdout = "";
|
|
6920
|
+
let stderr = "";
|
|
6921
|
+
process2.stdout.on("data", (data) => {
|
|
6922
|
+
stdout += data.toString();
|
|
6923
|
+
});
|
|
6924
|
+
process2.stderr.on("data", (data) => {
|
|
6925
|
+
stderr += data.toString();
|
|
6926
|
+
});
|
|
6927
|
+
process2.on("error", (error) => {
|
|
6928
|
+
reject(new Error(`CLI process error: ${error.message}`));
|
|
6929
|
+
});
|
|
6930
|
+
process2.on("close", (code) => {
|
|
6931
|
+
if (code === 0) {
|
|
6932
|
+
resolve5(stdout);
|
|
6933
|
+
} else {
|
|
6934
|
+
reject(new Error(
|
|
6935
|
+
`CLI exited with code ${code}: ${stderr || "No error message"}`
|
|
6936
|
+
));
|
|
6937
|
+
}
|
|
6938
|
+
});
|
|
6939
|
+
process2.stdin.write(prompt);
|
|
6940
|
+
process2.stdin.end();
|
|
6941
|
+
});
|
|
6942
|
+
}
|
|
6943
|
+
/**
|
|
6944
|
+
* Parse CLI response
|
|
6945
|
+
*/
|
|
6946
|
+
parseResponse(output) {
|
|
6947
|
+
try {
|
|
6948
|
+
const parsed = JSON.parse(output.trim());
|
|
6949
|
+
if (parsed.content || parsed.message) {
|
|
6950
|
+
return {
|
|
6951
|
+
content: parsed.content || parsed.message || "",
|
|
6952
|
+
model: parsed.model || normalizeGrokModel(this.config.model),
|
|
6953
|
+
tokensUsed: parsed.usage ? {
|
|
6954
|
+
prompt: parsed.usage.prompt_tokens || 0,
|
|
6955
|
+
completion: parsed.usage.completion_tokens || 0,
|
|
6956
|
+
total: parsed.usage.total_tokens || 0
|
|
6957
|
+
} : { prompt: 0, completion: 0, total: 0 },
|
|
6958
|
+
finishReason: parsed.finish_reason || "stop"
|
|
6959
|
+
};
|
|
6960
|
+
}
|
|
6961
|
+
} catch {
|
|
6962
|
+
}
|
|
6963
|
+
return {
|
|
6964
|
+
content: output.trim(),
|
|
6965
|
+
model: normalizeGrokModel(this.config.model),
|
|
6966
|
+
tokensUsed: { prompt: 0, completion: 0, total: 0 },
|
|
6967
|
+
finishReason: "stop"
|
|
6968
|
+
};
|
|
6969
|
+
}
|
|
6970
|
+
/**
|
|
6971
|
+
* Get the configured model
|
|
6972
|
+
*/
|
|
6973
|
+
getModel() {
|
|
6974
|
+
return this.config.model;
|
|
6975
|
+
}
|
|
6976
|
+
/**
|
|
6977
|
+
* Get CLI version
|
|
6978
|
+
*/
|
|
6979
|
+
getVersion() {
|
|
6980
|
+
return this.cliVersion;
|
|
6981
|
+
}
|
|
6982
|
+
/**
|
|
6983
|
+
* Get CLI command
|
|
6984
|
+
*/
|
|
6985
|
+
getCommand() {
|
|
6986
|
+
return this.config.command;
|
|
6987
|
+
}
|
|
6988
|
+
/**
|
|
6989
|
+
* Clean up resources
|
|
6990
|
+
*/
|
|
6991
|
+
async destroy() {
|
|
6992
|
+
logger.debug("[Grok CLI] Wrapper destroyed");
|
|
6993
|
+
}
|
|
6994
|
+
};
|
|
6995
|
+
}
|
|
6996
|
+
});
|
|
6997
|
+
|
|
6998
|
+
// src/integrations/ax-grok/hybrid-adapter.ts
|
|
6999
|
+
var GrokHybridAdapter;
|
|
7000
|
+
var init_hybrid_adapter3 = __esm({
|
|
7001
|
+
"src/integrations/ax-grok/hybrid-adapter.ts"() {
|
|
7002
|
+
init_esm_shims();
|
|
7003
|
+
init_hybrid_adapter_base();
|
|
7004
|
+
init_sdk_adapter3();
|
|
7005
|
+
init_cli_wrapper3();
|
|
7006
|
+
init_types3();
|
|
7007
|
+
init_logger();
|
|
7008
|
+
GrokHybridAdapter = class extends HybridAdapterBase {
|
|
7009
|
+
sdkAdapter = null;
|
|
7010
|
+
cliWrapper = null;
|
|
7011
|
+
model;
|
|
7012
|
+
sdkConfig;
|
|
7013
|
+
cliConfig;
|
|
7014
|
+
constructor(options = {}) {
|
|
7015
|
+
const baseOptions = {
|
|
7016
|
+
mode: options.mode || "auto",
|
|
7017
|
+
providerName: "grok",
|
|
7018
|
+
maxRetries: options.maxRetries ?? 1
|
|
7019
|
+
};
|
|
7020
|
+
super(baseOptions);
|
|
7021
|
+
this.model = options.model || GROK_DEFAULT_MODEL;
|
|
7022
|
+
this.sdkConfig = {
|
|
7023
|
+
apiKey: options.apiKey,
|
|
7024
|
+
baseUrl: options.baseUrl,
|
|
7025
|
+
timeout: options.timeout
|
|
7026
|
+
};
|
|
7027
|
+
this.cliConfig = {
|
|
7028
|
+
command: options.command,
|
|
7029
|
+
timeout: options.timeout
|
|
7030
|
+
};
|
|
7031
|
+
logger.debug("[Grok Hybrid] Adapter created", {
|
|
7032
|
+
mode: this.mode,
|
|
7033
|
+
model: this.model
|
|
7034
|
+
});
|
|
7035
|
+
}
|
|
7036
|
+
/**
|
|
7037
|
+
* Execute request via SDK
|
|
7038
|
+
*/
|
|
7039
|
+
async executeViaSDK(request) {
|
|
7040
|
+
if (!this.sdkAdapter) {
|
|
7041
|
+
throw new Error("Grok SDK adapter not initialized");
|
|
7042
|
+
}
|
|
7043
|
+
return this.sdkAdapter.execute(request);
|
|
7044
|
+
}
|
|
7045
|
+
/**
|
|
7046
|
+
* Execute request via CLI
|
|
7047
|
+
*/
|
|
7048
|
+
async executeViaCLI(request) {
|
|
7049
|
+
if (!this.cliWrapper) {
|
|
7050
|
+
throw new Error("Grok CLI wrapper not initialized");
|
|
7051
|
+
}
|
|
7052
|
+
return this.cliWrapper.execute(request);
|
|
7053
|
+
}
|
|
7054
|
+
/**
|
|
7055
|
+
* Check if SDK is available
|
|
7056
|
+
*/
|
|
7057
|
+
async isSDKAvailable() {
|
|
7058
|
+
try {
|
|
7059
|
+
const adapter = new GrokSdkAdapter({
|
|
7060
|
+
...this.sdkConfig,
|
|
7061
|
+
model: this.model
|
|
7062
|
+
});
|
|
7063
|
+
const available = await adapter.isAvailable();
|
|
7064
|
+
if (!available) {
|
|
7065
|
+
return false;
|
|
7066
|
+
}
|
|
7067
|
+
this.sdkAdapter = adapter;
|
|
7068
|
+
return true;
|
|
7069
|
+
} catch (error) {
|
|
7070
|
+
logger.debug("[Grok Hybrid] SDK availability check failed", {
|
|
7071
|
+
error: error instanceof Error ? error.message : String(error)
|
|
7072
|
+
});
|
|
7073
|
+
return false;
|
|
7074
|
+
}
|
|
7075
|
+
}
|
|
7076
|
+
/**
|
|
7077
|
+
* Check if CLI is available
|
|
7078
|
+
*/
|
|
7079
|
+
async isCLIAvailable() {
|
|
7080
|
+
try {
|
|
7081
|
+
const wrapper = new GrokCliWrapper({
|
|
7082
|
+
...this.cliConfig,
|
|
7083
|
+
model: this.model
|
|
7084
|
+
});
|
|
7085
|
+
const available = await wrapper.isAvailable();
|
|
7086
|
+
if (!available) {
|
|
7087
|
+
return false;
|
|
7088
|
+
}
|
|
7089
|
+
this.cliWrapper = wrapper;
|
|
7090
|
+
return true;
|
|
7091
|
+
} catch (error) {
|
|
7092
|
+
logger.debug("[Grok Hybrid] CLI availability check failed", {
|
|
7093
|
+
error: error instanceof Error ? error.message : String(error)
|
|
7094
|
+
});
|
|
7095
|
+
return false;
|
|
7096
|
+
}
|
|
7097
|
+
}
|
|
7098
|
+
/**
|
|
7099
|
+
* Initialize SDK adapter
|
|
7100
|
+
*/
|
|
7101
|
+
async initializeSDK() {
|
|
7102
|
+
if (!this.sdkAdapter) {
|
|
7103
|
+
this.sdkAdapter = new GrokSdkAdapter({
|
|
7104
|
+
...this.sdkConfig,
|
|
7105
|
+
model: this.model
|
|
7106
|
+
});
|
|
7107
|
+
}
|
|
7108
|
+
await this.sdkAdapter.initialize();
|
|
7109
|
+
logger.debug("[Grok Hybrid] SDK initialized", {
|
|
7110
|
+
model: this.model
|
|
7111
|
+
});
|
|
7112
|
+
}
|
|
7113
|
+
/**
|
|
7114
|
+
* Initialize CLI wrapper
|
|
7115
|
+
*/
|
|
7116
|
+
async initializeCLI() {
|
|
7117
|
+
if (!this.cliWrapper) {
|
|
7118
|
+
this.cliWrapper = new GrokCliWrapper({
|
|
7119
|
+
...this.cliConfig,
|
|
7120
|
+
model: this.model
|
|
7121
|
+
});
|
|
7122
|
+
}
|
|
7123
|
+
await this.cliWrapper.initialize();
|
|
7124
|
+
logger.debug("[Grok Hybrid] CLI initialized", {
|
|
7125
|
+
model: this.model,
|
|
7126
|
+
version: this.cliWrapper.getVersion()
|
|
7127
|
+
});
|
|
7128
|
+
}
|
|
7129
|
+
/**
|
|
7130
|
+
* Clean up SDK resources
|
|
7131
|
+
*/
|
|
7132
|
+
async destroySDK() {
|
|
7133
|
+
if (this.sdkAdapter) {
|
|
7134
|
+
await this.sdkAdapter.destroy();
|
|
7135
|
+
this.sdkAdapter = null;
|
|
7136
|
+
}
|
|
7137
|
+
}
|
|
7138
|
+
/**
|
|
7139
|
+
* Clean up CLI resources
|
|
7140
|
+
*/
|
|
7141
|
+
async destroyCLI() {
|
|
7142
|
+
if (this.cliWrapper) {
|
|
7143
|
+
await this.cliWrapper.destroy();
|
|
7144
|
+
this.cliWrapper = null;
|
|
7145
|
+
}
|
|
7146
|
+
}
|
|
7147
|
+
/**
|
|
7148
|
+
* Get the configured model
|
|
7149
|
+
*/
|
|
7150
|
+
getModel() {
|
|
7151
|
+
return this.model;
|
|
7152
|
+
}
|
|
7153
|
+
/**
|
|
7154
|
+
* Get the CLI command being used
|
|
7155
|
+
*/
|
|
7156
|
+
getCommand() {
|
|
7157
|
+
return this.cliWrapper?.getCommand() || this.cliConfig.command || "ax-grok";
|
|
7158
|
+
}
|
|
7159
|
+
/**
|
|
7160
|
+
* Get CLI version
|
|
7161
|
+
*/
|
|
7162
|
+
getCLIVersion() {
|
|
7163
|
+
return this.cliWrapper?.getVersion() || null;
|
|
7164
|
+
}
|
|
7165
|
+
};
|
|
7166
|
+
}
|
|
7167
|
+
});
|
|
7168
|
+
|
|
7169
|
+
// src/integrations/ax-grok/sdk-only-adapter.ts
|
|
7170
|
+
var GrokSdkOnlyAdapter;
|
|
7171
|
+
var init_sdk_only_adapter2 = __esm({
|
|
7172
|
+
"src/integrations/ax-grok/sdk-only-adapter.ts"() {
|
|
7173
|
+
init_esm_shims();
|
|
7174
|
+
init_logger();
|
|
7175
|
+
init_sdk_adapter3();
|
|
7176
|
+
init_types3();
|
|
7177
|
+
GrokSdkOnlyAdapter = class {
|
|
7178
|
+
sdkAdapter;
|
|
7179
|
+
initialized = false;
|
|
7180
|
+
model;
|
|
7181
|
+
maxRetries;
|
|
7182
|
+
retryDelayMs;
|
|
7183
|
+
constructor(options = {}) {
|
|
7184
|
+
this.model = options.model || GROK_DEFAULT_MODEL;
|
|
7185
|
+
this.maxRetries = options.maxRetries ?? 2;
|
|
7186
|
+
this.retryDelayMs = options.retryDelayMs ?? 1e3;
|
|
7187
|
+
this.sdkAdapter = new GrokSdkAdapter({
|
|
7188
|
+
model: this.model,
|
|
7189
|
+
apiKey: options.apiKey,
|
|
7190
|
+
baseUrl: options.baseUrl,
|
|
7191
|
+
timeout: options.timeout
|
|
7192
|
+
});
|
|
7193
|
+
logger.debug("[Grok SDK-Only] Adapter created", {
|
|
7194
|
+
model: this.model,
|
|
7195
|
+
maxRetries: this.maxRetries
|
|
7196
|
+
});
|
|
7197
|
+
}
|
|
7198
|
+
/**
|
|
7199
|
+
* Check if SDK is available
|
|
7200
|
+
*/
|
|
7201
|
+
async isAvailable() {
|
|
7202
|
+
return this.sdkAdapter.isAvailable();
|
|
7203
|
+
}
|
|
7204
|
+
/**
|
|
7205
|
+
* Initialize the adapter
|
|
7206
|
+
*/
|
|
7207
|
+
async initialize() {
|
|
7208
|
+
if (this.initialized) {
|
|
7209
|
+
return;
|
|
7210
|
+
}
|
|
7211
|
+
const available = await this.sdkAdapter.isAvailable();
|
|
7212
|
+
if (!available) {
|
|
7213
|
+
throw new Error("Grok SDK not available - check API key and openai package");
|
|
7214
|
+
}
|
|
7215
|
+
await this.sdkAdapter.initialize();
|
|
7216
|
+
this.initialized = true;
|
|
7217
|
+
logger.debug("[Grok SDK-Only] Initialized", { model: this.model });
|
|
7218
|
+
}
|
|
7219
|
+
/**
|
|
7220
|
+
* Execute a request using Grok SDK
|
|
7221
|
+
*
|
|
7222
|
+
* Retries transient errors up to maxRetries times with exponential backoff.
|
|
7223
|
+
* Does NOT fall back to CLI - throws on persistent failure.
|
|
7224
|
+
*/
|
|
7225
|
+
async execute(request) {
|
|
7226
|
+
if (!this.initialized) {
|
|
7227
|
+
await this.initialize();
|
|
7228
|
+
}
|
|
7229
|
+
const startTime = Date.now();
|
|
7230
|
+
let lastError;
|
|
7231
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
7232
|
+
try {
|
|
7233
|
+
const response = await this.sdkAdapter.execute(request);
|
|
7234
|
+
logger.debug("[Grok SDK-Only] Execution succeeded", {
|
|
7235
|
+
model: this.model,
|
|
7236
|
+
attempt: attempt + 1,
|
|
7237
|
+
latencyMs: response.latencyMs
|
|
7238
|
+
});
|
|
7239
|
+
return response;
|
|
7240
|
+
} catch (error) {
|
|
7241
|
+
lastError = error;
|
|
7242
|
+
logger.warn("[Grok SDK-Only] Execution failed", {
|
|
7243
|
+
model: this.model,
|
|
7244
|
+
attempt: attempt + 1,
|
|
7245
|
+
maxRetries: this.maxRetries,
|
|
7246
|
+
error: lastError.message
|
|
7247
|
+
});
|
|
7248
|
+
if (attempt < this.maxRetries && this.isRetryableError(lastError)) {
|
|
7249
|
+
const delay = this.retryDelayMs * Math.pow(2, attempt);
|
|
7250
|
+
logger.debug("[Grok SDK-Only] Retrying after delay", {
|
|
7251
|
+
attempt: attempt + 1,
|
|
7252
|
+
delayMs: delay
|
|
7253
|
+
});
|
|
7254
|
+
await this.sleep(delay);
|
|
7255
|
+
continue;
|
|
7256
|
+
}
|
|
7257
|
+
break;
|
|
7258
|
+
}
|
|
7259
|
+
}
|
|
7260
|
+
const totalTime = Date.now() - startTime;
|
|
7261
|
+
logger.error("[Grok SDK-Only] All retries exhausted", {
|
|
7262
|
+
model: this.model,
|
|
7263
|
+
totalAttempts: this.maxRetries + 1,
|
|
7264
|
+
totalTimeMs: totalTime,
|
|
7265
|
+
lastError: lastError?.message
|
|
7266
|
+
});
|
|
7267
|
+
throw new Error(
|
|
7268
|
+
`Grok SDK execution failed after ${this.maxRetries + 1} attempts: ${lastError?.message || "Unknown error"}`
|
|
7269
|
+
);
|
|
7270
|
+
}
|
|
7271
|
+
/**
|
|
7272
|
+
* Check if error is retryable
|
|
7273
|
+
*/
|
|
7274
|
+
isRetryableError(error) {
|
|
7275
|
+
const message = error.message.toLowerCase();
|
|
7276
|
+
if (message.includes("rate limit") || message.includes("429")) {
|
|
7277
|
+
return true;
|
|
7278
|
+
}
|
|
7279
|
+
if (message.includes("500") || message.includes("502") || message.includes("503") || message.includes("504")) {
|
|
7280
|
+
return true;
|
|
7281
|
+
}
|
|
7282
|
+
if (message.includes("timeout") || message.includes("etimedout")) {
|
|
7283
|
+
return true;
|
|
7284
|
+
}
|
|
7285
|
+
if (message.includes("econnreset") || message.includes("econnrefused")) {
|
|
7286
|
+
return true;
|
|
7287
|
+
}
|
|
7288
|
+
return false;
|
|
7289
|
+
}
|
|
7290
|
+
/**
|
|
7291
|
+
* Sleep utility
|
|
7292
|
+
*/
|
|
7293
|
+
sleep(ms) {
|
|
7294
|
+
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
7295
|
+
}
|
|
7296
|
+
/**
|
|
7297
|
+
* Get the configured model
|
|
7298
|
+
*/
|
|
7299
|
+
getModel() {
|
|
7300
|
+
return this.model;
|
|
7301
|
+
}
|
|
7302
|
+
/**
|
|
7303
|
+
* Clean up resources
|
|
7304
|
+
*/
|
|
7305
|
+
async destroy() {
|
|
7306
|
+
await this.sdkAdapter.destroy();
|
|
7307
|
+
this.initialized = false;
|
|
7308
|
+
logger.debug("[Grok SDK-Only] Adapter destroyed");
|
|
7309
|
+
}
|
|
7310
|
+
};
|
|
7311
|
+
}
|
|
7312
|
+
});
|
|
7313
|
+
|
|
7314
|
+
// src/integrations/ax-grok/mcp-client-mode.ts
|
|
7315
|
+
var init_mcp_client_mode2 = __esm({
|
|
7316
|
+
"src/integrations/ax-grok/mcp-client-mode.ts"() {
|
|
7317
|
+
init_esm_shims();
|
|
7318
|
+
init_logger();
|
|
7319
|
+
init_sdk_adapter3();
|
|
7320
|
+
}
|
|
7321
|
+
});
|
|
7322
|
+
|
|
7323
|
+
// src/integrations/ax-grok/index.ts
|
|
7324
|
+
var init_ax_grok = __esm({
|
|
7325
|
+
"src/integrations/ax-grok/index.ts"() {
|
|
7326
|
+
init_esm_shims();
|
|
7327
|
+
init_hybrid_adapter3();
|
|
7328
|
+
init_sdk_adapter3();
|
|
7329
|
+
init_cli_wrapper3();
|
|
7330
|
+
init_sdk_only_adapter2();
|
|
7331
|
+
init_mcp_client_mode2();
|
|
7332
|
+
init_types3();
|
|
7333
|
+
}
|
|
7334
|
+
});
|
|
7335
|
+
|
|
7336
|
+
// src/providers/grok-provider.ts
|
|
7337
|
+
var grok_provider_exports = {};
|
|
7338
|
+
__export(grok_provider_exports, {
|
|
7339
|
+
GrokProvider: () => GrokProvider,
|
|
7340
|
+
default: () => GrokProvider
|
|
7341
|
+
});
|
|
7342
|
+
var MODEL_MAPPING2, GrokProvider;
|
|
7343
|
+
var init_grok_provider = __esm({
|
|
7344
|
+
"src/providers/grok-provider.ts"() {
|
|
7345
|
+
init_esm_shims();
|
|
7346
|
+
init_base_provider();
|
|
7347
|
+
init_logger();
|
|
7348
|
+
init_ax_grok();
|
|
7349
|
+
MODEL_MAPPING2 = {
|
|
7350
|
+
"grok-beta": "grok-3"
|
|
7351
|
+
};
|
|
7352
|
+
GrokProvider = class _GrokProvider extends BaseProvider {
|
|
7353
|
+
/** Selected model */
|
|
7354
|
+
model;
|
|
7355
|
+
/** SDK-only adapter for direct execution (v13.0.0) */
|
|
7356
|
+
sdkOnlyAdapter = null;
|
|
7357
|
+
/** Legacy hybrid adapter for 'auto' mode (backwards compatibility) */
|
|
7358
|
+
hybridAdapter = null;
|
|
7359
|
+
/** Provider configuration */
|
|
7360
|
+
grokConfig;
|
|
7361
|
+
/** Supported models */
|
|
7362
|
+
static SUPPORTED_MODELS = [
|
|
7363
|
+
"grok-3",
|
|
7364
|
+
"grok-3-mini",
|
|
7365
|
+
"grok-2-vision",
|
|
7366
|
+
"grok-2",
|
|
7367
|
+
// Legacy alias
|
|
7368
|
+
"grok-beta"
|
|
7369
|
+
];
|
|
7370
|
+
constructor(config) {
|
|
7371
|
+
super({
|
|
7372
|
+
...config,
|
|
7373
|
+
command: "ax-grok"
|
|
7374
|
+
});
|
|
7375
|
+
this.grokConfig = config;
|
|
7376
|
+
const requestedModel = config.model || "grok-3";
|
|
7377
|
+
if (!_GrokProvider.SUPPORTED_MODELS.includes(requestedModel)) {
|
|
7378
|
+
logger.warn(`[Grok] Unknown model: ${requestedModel}. Using grok-3.`);
|
|
7379
|
+
this.model = "grok-3";
|
|
7380
|
+
} else {
|
|
7381
|
+
this.model = requestedModel;
|
|
7382
|
+
}
|
|
7383
|
+
logger.debug("[Grok Provider] Initialized", {
|
|
7384
|
+
model: this.model,
|
|
7385
|
+
mode: config.mode || "auto"
|
|
7386
|
+
});
|
|
7387
|
+
}
|
|
7388
|
+
/**
|
|
7389
|
+
* Get the normalized model name
|
|
7390
|
+
*/
|
|
7391
|
+
getNormalizedModel() {
|
|
7392
|
+
return MODEL_MAPPING2[this.model] || this.model;
|
|
7393
|
+
}
|
|
7394
|
+
/**
|
|
7395
|
+
* Get or create SDK-only adapter (v13.0.0 default)
|
|
7396
|
+
*/
|
|
7397
|
+
getSdkOnlyAdapter() {
|
|
7398
|
+
if (!this.sdkOnlyAdapter) {
|
|
7399
|
+
this.sdkOnlyAdapter = new GrokSdkOnlyAdapter({
|
|
7400
|
+
model: this.model,
|
|
7401
|
+
apiKey: this.grokConfig.apiKey,
|
|
7402
|
+
baseUrl: this.grokConfig.baseUrl,
|
|
7403
|
+
timeout: this.grokConfig.timeout
|
|
7404
|
+
});
|
|
7405
|
+
}
|
|
7406
|
+
return this.sdkOnlyAdapter;
|
|
7407
|
+
}
|
|
7408
|
+
/**
|
|
7409
|
+
* Get or create hybrid adapter (legacy, for 'auto' mode only)
|
|
7410
|
+
*/
|
|
7411
|
+
getHybridAdapter() {
|
|
7412
|
+
if (!this.hybridAdapter) {
|
|
7413
|
+
const options = {
|
|
7414
|
+
mode: this.grokConfig.mode || "auto",
|
|
7415
|
+
model: this.model,
|
|
7416
|
+
apiKey: this.grokConfig.apiKey,
|
|
7417
|
+
baseUrl: this.grokConfig.baseUrl,
|
|
7418
|
+
command: "ax-grok",
|
|
7419
|
+
timeout: this.grokConfig.timeout
|
|
7420
|
+
};
|
|
7421
|
+
this.hybridAdapter = new GrokHybridAdapter(options);
|
|
7422
|
+
}
|
|
7423
|
+
return this.hybridAdapter;
|
|
7424
|
+
}
|
|
7425
|
+
/**
|
|
7426
|
+
* Execute a task using Grok
|
|
7427
|
+
*
|
|
7428
|
+
* Execution flow (v13.0.0):
|
|
7429
|
+
* 1. Mock mode → return mock response
|
|
7430
|
+
* 2. mode='sdk' (default) → use SDK-only adapter (NO CLI fallback)
|
|
7431
|
+
* 3. mode='auto' (legacy) → use hybrid adapter (SDK with CLI fallback)
|
|
7432
|
+
* 4. mode='cli' → use CLI via BaseProvider (deprecated for Grok)
|
|
7433
|
+
*/
|
|
7434
|
+
async execute(request) {
|
|
7435
|
+
if (process.env.AX_MOCK_PROVIDERS === "true") {
|
|
7436
|
+
return this.createMockResponse(request.prompt);
|
|
7437
|
+
}
|
|
7438
|
+
const effectiveMode = this.grokConfig.mode || "sdk";
|
|
7439
|
+
if (effectiveMode === "cli") {
|
|
7440
|
+
logger.warn("[Grok Provider] CLI mode is deprecated for Grok. Consider using SDK mode.", {
|
|
7441
|
+
model: this.model
|
|
7442
|
+
});
|
|
7443
|
+
return super.execute(request);
|
|
7444
|
+
}
|
|
7445
|
+
if (effectiveMode === "auto") {
|
|
7446
|
+
logger.debug("[Grok Provider] Executing via hybrid adapter (legacy auto mode)", {
|
|
7447
|
+
promptLength: request.prompt.length,
|
|
7448
|
+
model: this.model
|
|
7449
|
+
});
|
|
7450
|
+
const adapter2 = this.getHybridAdapter();
|
|
7451
|
+
return adapter2.execute(request);
|
|
7452
|
+
}
|
|
7453
|
+
logger.debug("[Grok Provider] Executing via SDK-only adapter", {
|
|
7454
|
+
promptLength: request.prompt.length,
|
|
7455
|
+
model: this.model
|
|
7456
|
+
});
|
|
7457
|
+
const adapter = this.getSdkOnlyAdapter();
|
|
7458
|
+
return adapter.execute(request);
|
|
7459
|
+
}
|
|
7460
|
+
/**
|
|
7461
|
+
* Get CLI command
|
|
7462
|
+
*/
|
|
7463
|
+
getCLICommand() {
|
|
7464
|
+
const adapter = this.hybridAdapter;
|
|
7465
|
+
if (adapter) {
|
|
7466
|
+
const activeMode = adapter.getActiveMode();
|
|
7467
|
+
if (activeMode === "sdk") {
|
|
7468
|
+
return "grok-sdk";
|
|
7469
|
+
}
|
|
7470
|
+
}
|
|
7471
|
+
return "ax-grok";
|
|
7472
|
+
}
|
|
7473
|
+
/**
|
|
7474
|
+
* Get CLI arguments for ax-grok headless mode
|
|
7475
|
+
*/
|
|
7476
|
+
getCLIArgs() {
|
|
7477
|
+
const args2 = ["-p"];
|
|
7478
|
+
const normalizedModel = this.getNormalizedModel();
|
|
7479
|
+
if (normalizedModel !== "grok-3") {
|
|
7480
|
+
args2.push("--model", normalizedModel);
|
|
7481
|
+
}
|
|
7482
|
+
return args2;
|
|
7483
|
+
}
|
|
7484
|
+
/**
|
|
7485
|
+
* Create mock response for testing
|
|
7486
|
+
*/
|
|
7487
|
+
createMockResponse(prompt) {
|
|
7488
|
+
return {
|
|
7489
|
+
content: this.getMockResponse(),
|
|
7490
|
+
model: this.getNormalizedModel(),
|
|
7491
|
+
tokensUsed: {
|
|
7492
|
+
prompt: this.estimateTokens(prompt),
|
|
7493
|
+
completion: 50,
|
|
7494
|
+
total: this.estimateTokens(prompt) + 50
|
|
7495
|
+
},
|
|
7496
|
+
latencyMs: 10,
|
|
7497
|
+
finishReason: "stop",
|
|
7498
|
+
cached: false
|
|
7499
|
+
};
|
|
7500
|
+
}
|
|
7501
|
+
/**
|
|
7502
|
+
* Estimate token count
|
|
7503
|
+
*/
|
|
7504
|
+
estimateTokens(text) {
|
|
7505
|
+
return Math.ceil(text.length / 4);
|
|
4055
7506
|
}
|
|
7507
|
+
/**
|
|
7508
|
+
* Get mock response for testing
|
|
7509
|
+
*/
|
|
4056
7510
|
getMockResponse() {
|
|
4057
|
-
return
|
|
7511
|
+
return `[Mock Grok Response]
|
|
7512
|
+
|
|
7513
|
+
This is a mock response from the Grok provider (${this.getNormalizedModel()}).
|
|
7514
|
+
In production, this would be a response from ${this.grokConfig.mode === "sdk" ? "Grok SDK" : "ax-grok CLI"}.
|
|
7515
|
+
|
|
7516
|
+
Model: ${this.getNormalizedModel()}
|
|
7517
|
+
Provider: Grok (xAI)
|
|
7518
|
+
Mode: ${this.grokConfig.mode || "auto"}`;
|
|
4058
7519
|
}
|
|
7520
|
+
/**
|
|
7521
|
+
* Get provider capabilities
|
|
7522
|
+
*/
|
|
4059
7523
|
get capabilities() {
|
|
7524
|
+
const model = this.getNormalizedModel();
|
|
7525
|
+
const isVision = model.includes("vision") || model === "grok-2-vision";
|
|
7526
|
+
const maxContextTokens = 131072;
|
|
7527
|
+
const activeMode = this.hybridAdapter?.getActiveMode();
|
|
7528
|
+
const integrationMode = activeMode === "sdk" ? "sdk" : "cli";
|
|
4060
7529
|
return {
|
|
7530
|
+
...super.capabilities,
|
|
4061
7531
|
supportsStreaming: true,
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
7532
|
+
supportsVision: isVision,
|
|
7533
|
+
maxContextTokens,
|
|
7534
|
+
supportedModels: _GrokProvider.SUPPORTED_MODELS,
|
|
7535
|
+
integrationMode
|
|
4066
7536
|
};
|
|
4067
7537
|
}
|
|
7538
|
+
/**
|
|
7539
|
+
* Get the active execution mode
|
|
7540
|
+
*/
|
|
4068
7541
|
getActiveMode() {
|
|
4069
7542
|
return this.hybridAdapter?.getActiveMode() || null;
|
|
4070
7543
|
}
|
|
4071
|
-
|
|
4072
|
-
|
|
7544
|
+
/**
|
|
7545
|
+
* Reset circuit breakers
|
|
7546
|
+
*/
|
|
7547
|
+
resetCircuitBreakers() {
|
|
7548
|
+
this.hybridAdapter?.resetCircuitBreakers();
|
|
4073
7549
|
}
|
|
7550
|
+
/**
|
|
7551
|
+
* Clean up resources
|
|
7552
|
+
*/
|
|
4074
7553
|
async destroy() {
|
|
4075
|
-
this.
|
|
7554
|
+
if (this.sdkOnlyAdapter) {
|
|
7555
|
+
await this.sdkOnlyAdapter.destroy();
|
|
7556
|
+
this.sdkOnlyAdapter = null;
|
|
7557
|
+
}
|
|
4076
7558
|
if (this.hybridAdapter) {
|
|
4077
7559
|
await this.hybridAdapter.destroy();
|
|
4078
7560
|
this.hybridAdapter = null;
|
|
4079
7561
|
}
|
|
4080
7562
|
}
|
|
7563
|
+
/**
|
|
7564
|
+
* Get the list of supported models
|
|
7565
|
+
*/
|
|
7566
|
+
static getSupportedModels() {
|
|
7567
|
+
return [..._GrokProvider.SUPPORTED_MODELS];
|
|
7568
|
+
}
|
|
4081
7569
|
};
|
|
4082
7570
|
}
|
|
4083
7571
|
});
|
|
4084
7572
|
|
|
4085
|
-
// src/providers/openai-provider-factory.ts
|
|
4086
|
-
var openai_provider_factory_exports = {};
|
|
4087
|
-
__export(openai_provider_factory_exports, {
|
|
4088
|
-
createOpenAIProviderAsync: () => createOpenAIProviderAsync,
|
|
4089
|
-
createOpenAIProviderSync: () => createOpenAIProviderSync,
|
|
4090
|
-
getIntegrationMode: () => getIntegrationMode,
|
|
4091
|
-
isCLIModeAvailable: () => isCLIModeAvailable,
|
|
4092
|
-
isSDKModeAvailable: () => isSDKModeAvailable
|
|
4093
|
-
});
|
|
4094
|
-
function createOpenAIProviderSync(config, integrationMode) {
|
|
4095
|
-
logger.debug("Created OpenAI CLI provider (v8.3.0: CLI-only)", {
|
|
4096
|
-
provider: config.name,
|
|
4097
|
-
mode: "cli"
|
|
4098
|
-
});
|
|
4099
|
-
return new OpenAIProvider(config);
|
|
4100
|
-
}
|
|
4101
|
-
async function createOpenAIProviderAsync(config, integrationMode, interactive = false) {
|
|
4102
|
-
logger.info("Creating OpenAI CLI provider (v8.3.0: CLI-only)", {
|
|
4103
|
-
provider: config.name,
|
|
4104
|
-
mode: "cli"
|
|
4105
|
-
});
|
|
4106
|
-
return new OpenAIProvider(config);
|
|
4107
|
-
}
|
|
4108
|
-
function getIntegrationMode(config) {
|
|
4109
|
-
return config.integration;
|
|
4110
|
-
}
|
|
4111
|
-
function isSDKModeAvailable() {
|
|
4112
|
-
const hasAPIKey = !!process.env.OPENAI_API_KEY;
|
|
4113
|
-
let hasSDK = false;
|
|
4114
|
-
try {
|
|
4115
|
-
if (typeof import.meta.resolve === "function") {
|
|
4116
|
-
import.meta.resolve("openai");
|
|
4117
|
-
hasSDK = true;
|
|
4118
|
-
} else {
|
|
4119
|
-
hasSDK = true;
|
|
4120
|
-
}
|
|
4121
|
-
} catch {
|
|
4122
|
-
hasSDK = false;
|
|
4123
|
-
}
|
|
4124
|
-
return hasAPIKey && hasSDK;
|
|
4125
|
-
}
|
|
4126
|
-
async function isCLIModeAvailable() {
|
|
4127
|
-
try {
|
|
4128
|
-
const { execSync } = await import('child_process');
|
|
4129
|
-
execSync("codex --version", {
|
|
4130
|
-
encoding: "utf-8",
|
|
4131
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
4132
|
-
timeout: 3e3
|
|
4133
|
-
});
|
|
4134
|
-
return true;
|
|
4135
|
-
} catch {
|
|
4136
|
-
return false;
|
|
4137
|
-
}
|
|
4138
|
-
}
|
|
4139
|
-
var init_openai_provider_factory = __esm({
|
|
4140
|
-
"src/providers/openai-provider-factory.ts"() {
|
|
4141
|
-
init_esm_shims();
|
|
4142
|
-
init_openai_provider();
|
|
4143
|
-
init_logger();
|
|
4144
|
-
}
|
|
4145
|
-
});
|
|
4146
|
-
|
|
4147
7573
|
// src/mcp/index.ts
|
|
4148
7574
|
init_esm_shims();
|
|
4149
7575
|
|
|
4150
7576
|
// src/mcp/server.ts
|
|
4151
7577
|
init_esm_shims();
|
|
7578
|
+
init_validation_limits();
|
|
4152
7579
|
|
|
4153
7580
|
// src/shared/helpers/version.ts
|
|
4154
7581
|
init_esm_shims();
|
|
4155
|
-
var
|
|
7582
|
+
var __dirname2 = dirname(fileURLToPath(import.meta.url));
|
|
4156
7583
|
function getVersion() {
|
|
4157
7584
|
try {
|
|
4158
7585
|
const possiblePaths = [
|
|
4159
|
-
join(
|
|
7586
|
+
join(__dirname2, "../../../package.json"),
|
|
4160
7587
|
// From src/shared/helpers
|
|
4161
|
-
join(
|
|
7588
|
+
join(__dirname2, "../../package.json"),
|
|
4162
7589
|
// From dist/mcp (bundled MCP entry)
|
|
4163
|
-
join(
|
|
7590
|
+
join(__dirname2, "../package.json")
|
|
4164
7591
|
// From dist
|
|
4165
7592
|
];
|
|
4166
7593
|
for (const pkgPath of possiblePaths) {
|
|
@@ -4182,8 +7609,8 @@ function getVersion() {
|
|
|
4182
7609
|
|
|
4183
7610
|
// src/mcp/types.ts
|
|
4184
7611
|
init_esm_shims();
|
|
4185
|
-
var MCP_PROTOCOL_VERSION = "
|
|
4186
|
-
var MCP_SUPPORTED_VERSIONS = ["2024-12-05", "2024-11-05"];
|
|
7612
|
+
var MCP_PROTOCOL_VERSION = "2025-11-25";
|
|
7613
|
+
var MCP_SUPPORTED_VERSIONS = ["2025-11-25", "2024-12-05", "2024-11-05"];
|
|
4187
7614
|
|
|
4188
7615
|
// src/mcp/server.ts
|
|
4189
7616
|
init_logger();
|
|
@@ -4619,119 +8046,8 @@ function deepMerge(defaults, user) {
|
|
|
4619
8046
|
return result;
|
|
4620
8047
|
}
|
|
4621
8048
|
|
|
4622
|
-
// src/core/
|
|
4623
|
-
|
|
4624
|
-
var VALIDATION_LIMITS = {
|
|
4625
|
-
// Resource limits (prevent DoS)
|
|
4626
|
-
MAX_ENTRIES: 1e6,
|
|
4627
|
-
// 1 million entries (memory, cache)
|
|
4628
|
-
MAX_TIMEOUT: 36e5,
|
|
4629
|
-
// 1 hour (execution, delegation)
|
|
4630
|
-
MAX_FILE_SIZE: 104857600,
|
|
4631
|
-
// 100 MB (workspace, abilities)
|
|
4632
|
-
MAX_CACHE_SIZE: 524288e3,
|
|
4633
|
-
// 500 MB (cache storage)
|
|
4634
|
-
MAX_SESSIONS: 1e4,
|
|
4635
|
-
// 10k concurrent sessions
|
|
4636
|
-
// Performance limits (prevent resource exhaustion)
|
|
4637
|
-
MAX_CONCURRENT_AGENTS: 100,
|
|
4638
|
-
// Maximum concurrent agents
|
|
4639
|
-
MAX_CPU_PERCENT: 80,
|
|
4640
|
-
// Maximum CPU usage percent
|
|
4641
|
-
MAX_MEMORY_MB: 2048,
|
|
4642
|
-
// Maximum memory usage in MB
|
|
4643
|
-
// Config validation limits
|
|
4644
|
-
MAX_CONFIG_FILE_SIZE: 1048576,
|
|
4645
|
-
// 1MB max config file size
|
|
4646
|
-
MAX_NAME_LENGTH: 50,
|
|
4647
|
-
// Max name length for agents/providers
|
|
4648
|
-
MAX_COMMAND_LENGTH: 100,
|
|
4649
|
-
// Max command length
|
|
4650
|
-
MAX_ARRAY_LENGTH: 1e4,
|
|
4651
|
-
// Max array elements in config
|
|
4652
|
-
// Path and file limits
|
|
4653
|
-
MIN_FILE_SIZE: 1,
|
|
4654
|
-
// Minimum file size in bytes
|
|
4655
|
-
MIN_TIMEOUT: 1e3,
|
|
4656
|
-
// Minimum timeout in ms (1 second)
|
|
4657
|
-
MIN_INTERVAL: 100,
|
|
4658
|
-
// Minimum interval in ms
|
|
4659
|
-
MIN_BACKOFF_FACTOR: 1,
|
|
4660
|
-
// Minimum backoff multiplier
|
|
4661
|
-
MAX_BACKOFF_FACTOR: 5,
|
|
4662
|
-
// Maximum backoff multiplier
|
|
4663
|
-
MAX_TTL: 864e5,
|
|
4664
|
-
// Maximum TTL in ms (24 hours)
|
|
4665
|
-
MIN_BYTES: 1,
|
|
4666
|
-
// Minimum bytes for file sizes
|
|
4667
|
-
// Network limits
|
|
4668
|
-
MIN_PORT: 1024,
|
|
4669
|
-
// Minimum port number
|
|
4670
|
-
MAX_PORT: 65535
|
|
4671
|
-
// Maximum port number
|
|
4672
|
-
};
|
|
4673
|
-
function isValidRelativePath(path7) {
|
|
4674
|
-
if (!path7 || typeof path7 !== "string") {
|
|
4675
|
-
return false;
|
|
4676
|
-
}
|
|
4677
|
-
const normalizedPath = path7.replace(/\\/g, "/");
|
|
4678
|
-
if (normalizedPath.startsWith("/")) {
|
|
4679
|
-
return false;
|
|
4680
|
-
}
|
|
4681
|
-
if (normalizedPath.includes("..")) {
|
|
4682
|
-
return false;
|
|
4683
|
-
}
|
|
4684
|
-
if (/^[a-zA-Z]:/.test(normalizedPath)) {
|
|
4685
|
-
return false;
|
|
4686
|
-
}
|
|
4687
|
-
if (normalizedPath.startsWith("//")) {
|
|
4688
|
-
return false;
|
|
4689
|
-
}
|
|
4690
|
-
return true;
|
|
4691
|
-
}
|
|
4692
|
-
function isValidCommand(command) {
|
|
4693
|
-
if (!command || typeof command !== "string") {
|
|
4694
|
-
return false;
|
|
4695
|
-
}
|
|
4696
|
-
if (command.length > VALIDATION_LIMITS.MAX_COMMAND_LENGTH) {
|
|
4697
|
-
return false;
|
|
4698
|
-
}
|
|
4699
|
-
if (!/^[a-z0-9_-]+$/i.test(command)) {
|
|
4700
|
-
return false;
|
|
4701
|
-
}
|
|
4702
|
-
return true;
|
|
4703
|
-
}
|
|
4704
|
-
function isValidName(name) {
|
|
4705
|
-
if (!name || typeof name !== "string") {
|
|
4706
|
-
return false;
|
|
4707
|
-
}
|
|
4708
|
-
if (name.length > VALIDATION_LIMITS.MAX_NAME_LENGTH) {
|
|
4709
|
-
return false;
|
|
4710
|
-
}
|
|
4711
|
-
if (!/^[a-z0-9][a-z0-9-_]*$/i.test(name)) {
|
|
4712
|
-
return false;
|
|
4713
|
-
}
|
|
4714
|
-
return true;
|
|
4715
|
-
}
|
|
4716
|
-
function isValidExtension(ext) {
|
|
4717
|
-
if (!ext || typeof ext !== "string") {
|
|
4718
|
-
return false;
|
|
4719
|
-
}
|
|
4720
|
-
const normalized = ext.startsWith(".") ? ext : `.${ext}`;
|
|
4721
|
-
if (normalized.length > 10 || normalized.length < 2) {
|
|
4722
|
-
return false;
|
|
4723
|
-
}
|
|
4724
|
-
if (!/^\.[a-z0-9]+$/i.test(normalized)) {
|
|
4725
|
-
return false;
|
|
4726
|
-
}
|
|
4727
|
-
return true;
|
|
4728
|
-
}
|
|
4729
|
-
function isPositiveInteger(value) {
|
|
4730
|
-
return typeof value === "number" && Number.isInteger(value) && value > 0;
|
|
4731
|
-
}
|
|
4732
|
-
function isNonNegativeInteger(value) {
|
|
4733
|
-
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
4734
|
-
}
|
|
8049
|
+
// src/core/config/loader.ts
|
|
8050
|
+
init_validation_limits();
|
|
4735
8051
|
|
|
4736
8052
|
// src/core/cache/cache.ts
|
|
4737
8053
|
init_esm_shims();
|
|
@@ -4807,7 +8123,7 @@ var TTLCache = class {
|
|
|
4807
8123
|
* @param value Value to cache
|
|
4808
8124
|
* @param customTTL Optional custom TTL for this entry (ms)
|
|
4809
8125
|
*/
|
|
4810
|
-
set(key, value,
|
|
8126
|
+
set(key, value, _customTTL) {
|
|
4811
8127
|
const size = this.estimateSize(value);
|
|
4812
8128
|
if (this.config.maxSize > 0 && size > this.config.maxSize) {
|
|
4813
8129
|
logger.warn("Entry too large for cache", {
|
|
@@ -5064,11 +8380,12 @@ function calculateMaxConcurrentAgents(staticLimit) {
|
|
|
5064
8380
|
init_esm_shims();
|
|
5065
8381
|
var PRECOMPILED_CONFIG = {
|
|
5066
8382
|
"providers": {
|
|
5067
|
-
"
|
|
8383
|
+
"gemini-cli": {
|
|
5068
8384
|
"enabled": true,
|
|
5069
|
-
"priority":
|
|
8385
|
+
"priority": 1,
|
|
5070
8386
|
"timeout": 27e5,
|
|
5071
|
-
"command": "
|
|
8387
|
+
"command": "gemini",
|
|
8388
|
+
"description": "#1 Frontend/Design, multimodal, free tier, WebDev Arena leader",
|
|
5072
8389
|
"healthCheck": {
|
|
5073
8390
|
"enabled": true,
|
|
5074
8391
|
"interval": 3e5,
|
|
@@ -5090,15 +8407,16 @@ var PRECOMPILED_CONFIG = {
|
|
|
5090
8407
|
},
|
|
5091
8408
|
"limitTracking": {
|
|
5092
8409
|
"enabled": true,
|
|
5093
|
-
"window": "
|
|
8410
|
+
"window": "daily",
|
|
5094
8411
|
"resetHourUtc": 0
|
|
5095
8412
|
}
|
|
5096
8413
|
},
|
|
5097
|
-
"
|
|
8414
|
+
"openai": {
|
|
5098
8415
|
"enabled": true,
|
|
5099
|
-
"priority":
|
|
8416
|
+
"priority": 3,
|
|
5100
8417
|
"timeout": 27e5,
|
|
5101
|
-
"command": "
|
|
8418
|
+
"command": "codex",
|
|
8419
|
+
"description": "Best reasoning (o3), strategy, 75% accuracy, 192k context",
|
|
5102
8420
|
"healthCheck": {
|
|
5103
8421
|
"enabled": true,
|
|
5104
8422
|
"interval": 3e5,
|
|
@@ -5124,11 +8442,12 @@ var PRECOMPILED_CONFIG = {
|
|
|
5124
8442
|
"resetHourUtc": 0
|
|
5125
8443
|
}
|
|
5126
8444
|
},
|
|
5127
|
-
"
|
|
8445
|
+
"claude-code": {
|
|
5128
8446
|
"enabled": true,
|
|
5129
|
-
"priority":
|
|
8447
|
+
"priority": 2,
|
|
5130
8448
|
"timeout": 27e5,
|
|
5131
|
-
"command": "
|
|
8449
|
+
"command": "claude",
|
|
8450
|
+
"description": "#1 Coding model, agentic workflows, security, 0% edit error rate",
|
|
5132
8451
|
"healthCheck": {
|
|
5133
8452
|
"enabled": true,
|
|
5134
8453
|
"interval": 3e5,
|
|
@@ -5148,6 +8467,50 @@ var PRECOMPILED_CONFIG = {
|
|
|
5148
8467
|
"forceKillDelay": 1e3,
|
|
5149
8468
|
"cacheEnabled": true
|
|
5150
8469
|
},
|
|
8470
|
+
"limitTracking": {
|
|
8471
|
+
"enabled": true,
|
|
8472
|
+
"window": "weekly",
|
|
8473
|
+
"resetHourUtc": 0
|
|
8474
|
+
}
|
|
8475
|
+
},
|
|
8476
|
+
"glm": {
|
|
8477
|
+
"enabled": true,
|
|
8478
|
+
"priority": 4,
|
|
8479
|
+
"timeout": 27e5,
|
|
8480
|
+
"type": "sdk",
|
|
8481
|
+
"description": "Near Claude coding (48.6%), low cost $3/mo, 200k context, agentic",
|
|
8482
|
+
"healthCheck": {
|
|
8483
|
+
"enabled": true,
|
|
8484
|
+
"interval": 3e5,
|
|
8485
|
+
"timeout": 5e3
|
|
8486
|
+
},
|
|
8487
|
+
"circuitBreaker": {
|
|
8488
|
+
"enabled": true,
|
|
8489
|
+
"failureThreshold": 3,
|
|
8490
|
+
"recoveryTimeout": 6e4
|
|
8491
|
+
},
|
|
8492
|
+
"limitTracking": {
|
|
8493
|
+
"enabled": true,
|
|
8494
|
+
"window": "daily",
|
|
8495
|
+
"resetHourUtc": 0
|
|
8496
|
+
}
|
|
8497
|
+
},
|
|
8498
|
+
"grok": {
|
|
8499
|
+
"enabled": true,
|
|
8500
|
+
"priority": 5,
|
|
8501
|
+
"timeout": 27e5,
|
|
8502
|
+
"type": "sdk",
|
|
8503
|
+
"description": "Fastest (67ms), reasoning (93% AIME), 1M context, DeepSearch",
|
|
8504
|
+
"healthCheck": {
|
|
8505
|
+
"enabled": true,
|
|
8506
|
+
"interval": 3e5,
|
|
8507
|
+
"timeout": 5e3
|
|
8508
|
+
},
|
|
8509
|
+
"circuitBreaker": {
|
|
8510
|
+
"enabled": true,
|
|
8511
|
+
"failureThreshold": 3,
|
|
8512
|
+
"recoveryTimeout": 6e4
|
|
8513
|
+
},
|
|
5151
8514
|
"limitTracking": {
|
|
5152
8515
|
"enabled": true,
|
|
5153
8516
|
"window": "daily",
|
|
@@ -5364,11 +8727,12 @@ var PRECOMPILED_CONFIG = {
|
|
|
5364
8727
|
"enableFreeTierPrioritization": true,
|
|
5365
8728
|
"enableWorkloadAwareRouting": true
|
|
5366
8729
|
},
|
|
5367
|
-
"version": "12.
|
|
8730
|
+
"version": "12.4.1"
|
|
5368
8731
|
};
|
|
5369
8732
|
|
|
5370
8733
|
// src/core/config/schemas.ts
|
|
5371
8734
|
init_esm_shims();
|
|
8735
|
+
init_validation_limits();
|
|
5372
8736
|
z.enum([
|
|
5373
8737
|
"claude",
|
|
5374
8738
|
"claude-code",
|
|
@@ -5431,17 +8795,35 @@ var limitTrackingConfigSchema = z.object({
|
|
|
5431
8795
|
resetHourUtc: z.number().int().min(0).max(23).default(0),
|
|
5432
8796
|
customResetMs: positiveIntSchema.optional()
|
|
5433
8797
|
}).describe("Provider limit tracking configuration");
|
|
8798
|
+
var providerTypeSchema = z.enum(["cli", "sdk", "hybrid"]).default("cli");
|
|
5434
8799
|
var providerConfigSchema = z.object({
|
|
5435
8800
|
enabled: z.boolean().default(true),
|
|
5436
8801
|
priority: positiveIntSchema,
|
|
5437
8802
|
timeout: timeoutSchema,
|
|
5438
|
-
|
|
8803
|
+
type: providerTypeSchema.optional(),
|
|
8804
|
+
command: commandSchema.optional(),
|
|
8805
|
+
// Optional for SDK providers (glm, grok)
|
|
8806
|
+
description: z.string().optional(),
|
|
8807
|
+
// Provider description
|
|
8808
|
+
model: z.string().optional(),
|
|
8809
|
+
// Model name override
|
|
5439
8810
|
healthCheck: healthCheckConfigSchema.optional(),
|
|
5440
8811
|
circuitBreaker: circuitBreakerConfigSchema.optional(),
|
|
5441
8812
|
processManagement: processManagementConfigSchema.optional(),
|
|
5442
8813
|
versionDetection: versionDetectionConfigSchema.optional(),
|
|
5443
8814
|
limitTracking: limitTrackingConfigSchema.optional()
|
|
5444
|
-
}).
|
|
8815
|
+
}).refine(
|
|
8816
|
+
(data) => {
|
|
8817
|
+
if (data.type === "sdk") {
|
|
8818
|
+
return true;
|
|
8819
|
+
}
|
|
8820
|
+
return data.command !== void 0;
|
|
8821
|
+
},
|
|
8822
|
+
{
|
|
8823
|
+
message: "CLI and hybrid providers require a command field",
|
|
8824
|
+
path: ["command"]
|
|
8825
|
+
}
|
|
8826
|
+
).describe("Provider configuration");
|
|
5445
8827
|
var providersConfigSchema = z.record(
|
|
5446
8828
|
safeNameSchema,
|
|
5447
8829
|
providerConfigSchema
|
|
@@ -5827,9 +9209,15 @@ function validateConfig(config) {
|
|
|
5827
9209
|
if (!isValidName(name)) {
|
|
5828
9210
|
errors.push(`Provider "${name}": name invalid (use alphanumeric, dash, underscore only, max 50 chars)`);
|
|
5829
9211
|
}
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
9212
|
+
const providerType = provider.type;
|
|
9213
|
+
const isSDKProvider = providerType === "sdk";
|
|
9214
|
+
if (!isSDKProvider) {
|
|
9215
|
+
if (!provider.command) {
|
|
9216
|
+
errors.push(`Provider ${name}: command is required for CLI/hybrid providers`);
|
|
9217
|
+
} else if (!isValidCommand(provider.command)) {
|
|
9218
|
+
errors.push(`Provider ${name}: command invalid (alphanumeric, dash, underscore only, max 100 chars, no shell metacharacters)`);
|
|
9219
|
+
}
|
|
9220
|
+
} else if (provider.command && !isValidCommand(provider.command)) {
|
|
5833
9221
|
errors.push(`Provider ${name}: command invalid (alphanumeric, dash, underscore only, max 100 chars, no shell metacharacters)`);
|
|
5834
9222
|
}
|
|
5835
9223
|
if (!isPositiveInteger(provider.priority)) {
|
|
@@ -6205,6 +9593,7 @@ function validateConfig(config) {
|
|
|
6205
9593
|
init_esm_shims();
|
|
6206
9594
|
init_logger();
|
|
6207
9595
|
init_errors();
|
|
9596
|
+
init_validation_limits();
|
|
6208
9597
|
|
|
6209
9598
|
// src/core/provider-limit-manager.ts
|
|
6210
9599
|
init_esm_shims();
|
|
@@ -6415,6 +9804,9 @@ var PathResolver = class {
|
|
|
6415
9804
|
}
|
|
6416
9805
|
}
|
|
6417
9806
|
};
|
|
9807
|
+
|
|
9808
|
+
// src/core/provider-limit-manager.ts
|
|
9809
|
+
init_validation_limits();
|
|
6418
9810
|
var ProviderLimitManager = class _ProviderLimitManager extends EventEmitter {
|
|
6419
9811
|
static instance = null;
|
|
6420
9812
|
stateFilePath;
|
|
@@ -6440,7 +9832,7 @@ var ProviderLimitManager = class _ProviderLimitManager extends EventEmitter {
|
|
|
6440
9832
|
/**
|
|
6441
9833
|
* Get singleton instance
|
|
6442
9834
|
*/
|
|
6443
|
-
static getInstance(stateDirectory =
|
|
9835
|
+
static getInstance(stateDirectory = AX_PATHS.STATE) {
|
|
6444
9836
|
if (!_ProviderLimitManager.instance) {
|
|
6445
9837
|
_ProviderLimitManager.instance = new _ProviderLimitManager(stateDirectory);
|
|
6446
9838
|
}
|
|
@@ -6528,7 +9920,9 @@ var ProviderLimitManager = class _ProviderLimitManager extends EventEmitter {
|
|
|
6528
9920
|
return { isLimited: false };
|
|
6529
9921
|
}
|
|
6530
9922
|
if (now >= state.resetAtMs) {
|
|
6531
|
-
void this.clearLimit(providerName, "expired")
|
|
9923
|
+
void this.clearLimit(providerName, "expired").catch((err) => {
|
|
9924
|
+
logger.warn("Failed to clear expired limit", { provider: providerName, error: err.message });
|
|
9925
|
+
});
|
|
6532
9926
|
return { isLimited: false };
|
|
6533
9927
|
}
|
|
6534
9928
|
const remainingMs = state.resetAtMs - now;
|
|
@@ -6617,9 +10011,10 @@ var ProviderLimitManager = class _ProviderLimitManager extends EventEmitter {
|
|
|
6617
10011
|
getManualOverride() {
|
|
6618
10012
|
if (this.manualOverride && this.manualOverride.expiresAtMs) {
|
|
6619
10013
|
if (Date.now() >= this.manualOverride.expiresAtMs) {
|
|
6620
|
-
this.manualOverride;
|
|
6621
10014
|
this.manualOverride = void 0;
|
|
6622
|
-
void this.clearManualOverride()
|
|
10015
|
+
void this.clearManualOverride().catch((err) => {
|
|
10016
|
+
logger.warn("Failed to clear expired manual override", { error: err.message });
|
|
10017
|
+
});
|
|
6623
10018
|
return void 0;
|
|
6624
10019
|
}
|
|
6625
10020
|
}
|
|
@@ -6756,10 +10151,10 @@ var ProviderLimitManager = class _ProviderLimitManager extends EventEmitter {
|
|
|
6756
10151
|
async function getProviderLimitManager(stateDirectory) {
|
|
6757
10152
|
if (!stateDirectory) {
|
|
6758
10153
|
if (process.env.NODE_ENV === "test" || process.env.VITEST) {
|
|
6759
|
-
stateDirectory =
|
|
10154
|
+
stateDirectory = AX_PATHS.STATE;
|
|
6760
10155
|
} else {
|
|
6761
10156
|
const projectRoot = await detectProjectRoot();
|
|
6762
|
-
stateDirectory = path4.join(projectRoot,
|
|
10157
|
+
stateDirectory = path4.join(projectRoot, AX_PATHS.STATE);
|
|
6763
10158
|
}
|
|
6764
10159
|
}
|
|
6765
10160
|
return ProviderLimitManager.getInstance(stateDirectory);
|
|
@@ -7742,13 +11137,12 @@ function getRoutingStrategyManager(config) {
|
|
|
7742
11137
|
// src/core/router/circuit-breaker.ts
|
|
7743
11138
|
init_esm_shims();
|
|
7744
11139
|
init_logger();
|
|
11140
|
+
init_validation_limits();
|
|
7745
11141
|
var DEFAULT_CONFIG2 = {
|
|
7746
11142
|
failureThreshold: 3,
|
|
7747
|
-
cooldownMs:
|
|
7748
|
-
// 1 minute
|
|
11143
|
+
cooldownMs: TIMEOUTS.CIRCUIT_BREAKER_RECOVERY,
|
|
7749
11144
|
successThreshold: 2,
|
|
7750
|
-
failureWindowMs:
|
|
7751
|
-
// v11.1.0: 5 minutes - failures outside this window don't count
|
|
11145
|
+
failureWindowMs: TIMEOUTS.CIRCUIT_BREAKER_WINDOW
|
|
7752
11146
|
};
|
|
7753
11147
|
var CircuitBreaker = class {
|
|
7754
11148
|
config;
|
|
@@ -7905,6 +11299,7 @@ var CircuitBreaker = class {
|
|
|
7905
11299
|
// src/core/router/trace-logger.ts
|
|
7906
11300
|
init_esm_shims();
|
|
7907
11301
|
init_logger();
|
|
11302
|
+
init_validation_limits();
|
|
7908
11303
|
var RouterTraceLogger = class {
|
|
7909
11304
|
traceFile;
|
|
7910
11305
|
stream;
|
|
@@ -7916,7 +11311,7 @@ var RouterTraceLogger = class {
|
|
|
7916
11311
|
sigintHandler = () => this.close();
|
|
7917
11312
|
sigtermHandler = () => this.close();
|
|
7918
11313
|
constructor(options) {
|
|
7919
|
-
this.traceFile = join(options.workspacePath, "
|
|
11314
|
+
this.traceFile = join(options.workspacePath, AX_PATHS.LOGS, "router.trace.jsonl");
|
|
7920
11315
|
this.enabled = options.enabled ?? true;
|
|
7921
11316
|
this.autoFlush = options.autoFlush ?? true;
|
|
7922
11317
|
if (this.enabled) {
|
|
@@ -8303,13 +11698,12 @@ var Router = class {
|
|
|
8303
11698
|
return a.priority - b.priority;
|
|
8304
11699
|
});
|
|
8305
11700
|
this.fallbackEnabled = config.fallbackEnabled;
|
|
8306
|
-
this.providerCooldownMs = config.providerCooldownMs ??
|
|
11701
|
+
this.providerCooldownMs = config.providerCooldownMs ?? TIMEOUTS.PROVIDER_COOLDOWN;
|
|
8307
11702
|
this.cache = config.cache;
|
|
8308
11703
|
this.circuitBreaker = new CircuitBreaker({
|
|
8309
11704
|
failureThreshold: config.circuitBreakerThreshold ?? 3,
|
|
8310
|
-
cooldownMs: config.providerCooldownMs ??
|
|
8311
|
-
failureWindowMs: config.circuitBreakerWindowMs ??
|
|
8312
|
-
// 5 min default
|
|
11705
|
+
cooldownMs: config.providerCooldownMs ?? TIMEOUTS.PROVIDER_COOLDOWN,
|
|
11706
|
+
failureWindowMs: config.circuitBreakerWindowMs ?? TIMEOUTS.CIRCUIT_BREAKER_WINDOW
|
|
8313
11707
|
});
|
|
8314
11708
|
void (async () => {
|
|
8315
11709
|
try {
|
|
@@ -10345,9 +13739,9 @@ var MemoryManager = class _MemoryManager {
|
|
|
10345
13739
|
throw new MemoryError("Memory manager not initialized", "DATABASE_ERROR");
|
|
10346
13740
|
}
|
|
10347
13741
|
try {
|
|
10348
|
-
const { mkdir:
|
|
13742
|
+
const { mkdir: mkdir4 } = await import('fs/promises');
|
|
10349
13743
|
const destDir = dirname3(destPath);
|
|
10350
|
-
await
|
|
13744
|
+
await mkdir4(destDir, { recursive: true });
|
|
10351
13745
|
await this.db.backup(destPath);
|
|
10352
13746
|
logger.info("Database backup created", { destPath: normalizePath(destPath) });
|
|
10353
13747
|
} catch (error) {
|
|
@@ -10795,7 +14189,7 @@ var LazyMemoryManager = class {
|
|
|
10795
14189
|
/**
|
|
10796
14190
|
* Backup database to destination path (lazy initialization on first call)
|
|
10797
14191
|
*/
|
|
10798
|
-
async backup(destPath,
|
|
14192
|
+
async backup(destPath, _onProgress) {
|
|
10799
14193
|
const manager = await this.ensureInitialized();
|
|
10800
14194
|
return manager.backup(destPath);
|
|
10801
14195
|
}
|
|
@@ -11689,12 +15083,13 @@ var SessionManager = class _SessionManager {
|
|
|
11689
15083
|
*/
|
|
11690
15084
|
async getStats() {
|
|
11691
15085
|
const sessions = Array.from(this.activeSessions.values());
|
|
11692
|
-
|
|
11693
|
-
|
|
11694
|
-
|
|
11695
|
-
|
|
11696
|
-
|
|
11697
|
-
}
|
|
15086
|
+
let active = 0, completed = 0, failed = 0;
|
|
15087
|
+
for (const s of sessions) {
|
|
15088
|
+
if (s.status === "active") active++;
|
|
15089
|
+
else if (s.status === "completed") completed++;
|
|
15090
|
+
else if (s.status === "failed") failed++;
|
|
15091
|
+
}
|
|
15092
|
+
return { total: sessions.length, active, completed, failed };
|
|
11698
15093
|
}
|
|
11699
15094
|
/**
|
|
11700
15095
|
* Load sessions from persistence file
|
|
@@ -12975,11 +16370,13 @@ var ContextManager = class {
|
|
|
12975
16370
|
* Cleanup context
|
|
12976
16371
|
*
|
|
12977
16372
|
* v5.2: No cleanup needed - agent workspaces no longer created
|
|
16373
|
+
* Note: Returns Promise for interface compatibility
|
|
12978
16374
|
*/
|
|
12979
|
-
|
|
16375
|
+
cleanup(context) {
|
|
12980
16376
|
logger.debug("Context cleanup (no-op in v5.2)", {
|
|
12981
16377
|
agent: context.agent.name
|
|
12982
16378
|
});
|
|
16379
|
+
return Promise.resolve();
|
|
12983
16380
|
}
|
|
12984
16381
|
};
|
|
12985
16382
|
|
|
@@ -13003,6 +16400,7 @@ var AgentNotFoundError = class extends Error {
|
|
|
13003
16400
|
|
|
13004
16401
|
// src/agents/profile-loader.ts
|
|
13005
16402
|
init_logger();
|
|
16403
|
+
init_validation_limits();
|
|
13006
16404
|
|
|
13007
16405
|
// src/agents/agent-schemas.ts
|
|
13008
16406
|
init_esm_shims();
|
|
@@ -13403,7 +16801,7 @@ var ProfileLoader = class {
|
|
|
13403
16801
|
const files = await readdir(this.fallbackProfilesDir);
|
|
13404
16802
|
const profiles = files.filter((file) => extname$1(file) === ".yaml" || extname$1(file) === ".yml").map((file) => basename(file, extname$1(file)));
|
|
13405
16803
|
profiles.forEach((p) => profileSet.add(p));
|
|
13406
|
-
} catch (
|
|
16804
|
+
} catch (_error) {
|
|
13407
16805
|
logger.debug("Fallback profiles directory not found", {
|
|
13408
16806
|
dir: this.fallbackProfilesDir
|
|
13409
16807
|
});
|
|
@@ -13680,7 +17078,7 @@ var ProfileLoader = class {
|
|
|
13680
17078
|
* Protected by mutex to prevent race conditions
|
|
13681
17079
|
*/
|
|
13682
17080
|
async clearCache() {
|
|
13683
|
-
await this.initMutex.runExclusive(
|
|
17081
|
+
await this.initMutex.runExclusive(() => {
|
|
13684
17082
|
this.cache.clear();
|
|
13685
17083
|
this.displayNameMap.clear();
|
|
13686
17084
|
this.mapInitialized = false;
|
|
@@ -14455,7 +17853,7 @@ var DelegationParser = class _DelegationParser {
|
|
|
14455
17853
|
await this.extractMatches(_DelegationParser.DELEGATION_PATTERNS.chineseRequest, response, fromAgent, delegations, "\u8ACB", resolvedNameCache);
|
|
14456
17854
|
await this.extractMatches(_DelegationParser.DELEGATION_PATTERNS.chineseFormal, response, fromAgent, delegations, "\u59D4\u6D3E\u7D66", resolvedNameCache);
|
|
14457
17855
|
delegations.sort((a, b) => a.position - b.position);
|
|
14458
|
-
const result = delegations.map(({ position, ...rest }) => rest);
|
|
17856
|
+
const result = delegations.map(({ position: _position, ...rest }) => rest);
|
|
14459
17857
|
logger.info(`Parsed ${delegations.length} delegation(s)`, {
|
|
14460
17858
|
fromAgent,
|
|
14461
17859
|
delegations: result.map((d) => ({ toAgent: d.toAgent, taskPreview: d.task.substring(0, 50) }))
|
|
@@ -15495,7 +18893,7 @@ var ParallelAgentExecutor = class {
|
|
|
15495
18893
|
/**
|
|
15496
18894
|
* Execute a batch of agents in parallel
|
|
15497
18895
|
*/
|
|
15498
|
-
async executeBatchParallel(batch, graph, context, options, results, timeline,
|
|
18896
|
+
async executeBatchParallel(batch, graph, context, options, results, timeline, _executionStartTime) {
|
|
15499
18897
|
logger.info("Executing batch in parallel", { agents: batch });
|
|
15500
18898
|
const promises = batch.map(async (agentName) => {
|
|
15501
18899
|
const node = graph.nodes.get(agentName);
|
|
@@ -15729,7 +19127,7 @@ var ParallelAgentExecutor = class {
|
|
|
15729
19127
|
/**
|
|
15730
19128
|
* Mark batch as cancelled
|
|
15731
19129
|
*/
|
|
15732
|
-
markBatchAsCancelled(batch, graph, timeline,
|
|
19130
|
+
markBatchAsCancelled(batch, graph, timeline, _executionStartTime) {
|
|
15733
19131
|
const now = Date.now();
|
|
15734
19132
|
for (const agentName of batch) {
|
|
15735
19133
|
const node = graph.nodes.get(agentName);
|
|
@@ -15869,6 +19267,9 @@ var ParallelAgentExecutor = class {
|
|
|
15869
19267
|
};
|
|
15870
19268
|
|
|
15871
19269
|
// src/agents/executor.ts
|
|
19270
|
+
function isErrorLike(error) {
|
|
19271
|
+
return typeof error === "object" && error !== null;
|
|
19272
|
+
}
|
|
15872
19273
|
var AgentExecutor = class {
|
|
15873
19274
|
sessionManager;
|
|
15874
19275
|
workspaceManager;
|
|
@@ -15998,7 +19399,7 @@ Retry attempt ${attempt}/${maxAttempts}...`));
|
|
|
15998
19399
|
}
|
|
15999
19400
|
return await this.executeInternal(context, options);
|
|
16000
19401
|
} catch (error) {
|
|
16001
|
-
lastError = error;
|
|
19402
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
16002
19403
|
const isRetryable = this.isRetryableError(error, retryableErrors);
|
|
16003
19404
|
if (!isRetryable || attempt >= maxAttempts) {
|
|
16004
19405
|
throw error;
|
|
@@ -16008,7 +19409,8 @@ Retry attempt ${attempt}/${maxAttempts}...`));
|
|
|
16008
19409
|
maxDelay
|
|
16009
19410
|
);
|
|
16010
19411
|
if (verbose) {
|
|
16011
|
-
|
|
19412
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
19413
|
+
console.log(chalk4.yellow(`Retryable error occurred: ${errorMessage}`));
|
|
16012
19414
|
console.log(chalk4.gray(`Waiting ${delay}ms before retry...`));
|
|
16013
19415
|
}
|
|
16014
19416
|
await this.sleep(delay, signal);
|
|
@@ -16023,7 +19425,7 @@ Retry attempt ${attempt}/${maxAttempts}...`));
|
|
|
16023
19425
|
*/
|
|
16024
19426
|
async executeWithTimeout(context, options) {
|
|
16025
19427
|
const timeout = options.timeout;
|
|
16026
|
-
const { verbose = false } = options;
|
|
19428
|
+
const { verbose: _verbose = false } = options;
|
|
16027
19429
|
let timeoutId = null;
|
|
16028
19430
|
const controller = new AbortController();
|
|
16029
19431
|
const executionOptions = {
|
|
@@ -16721,7 +20123,12 @@ ${context.task}`;
|
|
|
16721
20123
|
*/
|
|
16722
20124
|
isRetryableError(error, retryableErrors) {
|
|
16723
20125
|
const patterns = retryableErrors || this.defaultRetryConfig.retryableErrors;
|
|
16724
|
-
|
|
20126
|
+
let errorString = "";
|
|
20127
|
+
if (isErrorLike(error)) {
|
|
20128
|
+
errorString = (error.message || error.code || "").toLowerCase();
|
|
20129
|
+
} else if (typeof error === "string") {
|
|
20130
|
+
errorString = error.toLowerCase();
|
|
20131
|
+
}
|
|
16725
20132
|
return patterns.some(
|
|
16726
20133
|
(pattern) => errorString.includes(pattern.toLowerCase())
|
|
16727
20134
|
);
|
|
@@ -18149,7 +21556,7 @@ function categorizeTools(name) {
|
|
|
18149
21556
|
if (name.includes("context")) return "context";
|
|
18150
21557
|
return "execution";
|
|
18151
21558
|
}
|
|
18152
|
-
function getExecutionMode(providerName,
|
|
21559
|
+
function getExecutionMode(providerName, _providerConfig) {
|
|
18153
21560
|
if (providerName === "glm" || providerName === "grok") {
|
|
18154
21561
|
return "sdk";
|
|
18155
21562
|
}
|
|
@@ -18608,13 +22015,14 @@ function compressWithInfo(payload, options = {}) {
|
|
|
18608
22015
|
init_esm_shims();
|
|
18609
22016
|
init_factory();
|
|
18610
22017
|
init_logger();
|
|
22018
|
+
init_validation_limits();
|
|
18611
22019
|
var NATIVE_MODULE_ERROR_PATTERNS = [
|
|
18612
22020
|
"NODE_MODULE_VERSION",
|
|
18613
22021
|
"was compiled against a different Node.js version",
|
|
18614
22022
|
"better_sqlite3.node"
|
|
18615
22023
|
];
|
|
18616
22024
|
var DEFAULT_CONFIG3 = {
|
|
18617
|
-
dbPath:
|
|
22025
|
+
dbPath: `${AX_PATHS.TASKS}/tasks.db`,
|
|
18618
22026
|
maxPayloadBytes: 1024 * 1024,
|
|
18619
22027
|
// 1MB
|
|
18620
22028
|
compressionEnabled: true,
|
|
@@ -18622,7 +22030,7 @@ var DEFAULT_CONFIG3 = {
|
|
|
18622
22030
|
defaultTtlHours: 24,
|
|
18623
22031
|
maxTtlHours: 168,
|
|
18624
22032
|
// 7 days
|
|
18625
|
-
busyTimeout:
|
|
22033
|
+
busyTimeout: DATABASE.BUSY_TIMEOUT
|
|
18626
22034
|
};
|
|
18627
22035
|
var SQL = {
|
|
18628
22036
|
CREATE_TABLE: `
|
|
@@ -20141,7 +23549,6 @@ function createRunTaskHandler(deps) {
|
|
|
20141
23549
|
});
|
|
20142
23550
|
try {
|
|
20143
23551
|
const taskEngine = getTaskEngine();
|
|
20144
|
-
const session = deps.getSession();
|
|
20145
23552
|
const options = {
|
|
20146
23553
|
engineOverride: input.engine_override,
|
|
20147
23554
|
timeoutMs: input.timeout_ms,
|
|
@@ -22398,7 +25805,6 @@ var CodexEventNormalizer = class extends BaseEventNormalizer {
|
|
|
22398
25805
|
const inputTokens = event.inputTokens ?? 0;
|
|
22399
25806
|
const outputTokens = event.outputTokens ?? 0;
|
|
22400
25807
|
this.tokenCount = inputTokens + outputTokens;
|
|
22401
|
-
Date.now() - this.startTime;
|
|
22402
25808
|
return this.createEvent("execution.progress", {
|
|
22403
25809
|
agent: "codex",
|
|
22404
25810
|
progress: 100,
|
|
@@ -22783,36 +26189,61 @@ Use this tool first to understand what AutomatosX offers.`,
|
|
|
22783
26189
|
const projectDir = process.cwd();
|
|
22784
26190
|
const config = await loadConfig(projectDir);
|
|
22785
26191
|
const teamManager = new TeamManager(
|
|
22786
|
-
join(projectDir,
|
|
26192
|
+
join(projectDir, AX_PATHS.TEAMS)
|
|
22787
26193
|
);
|
|
22788
26194
|
this.profileLoader = new ProfileLoader(
|
|
22789
|
-
join(projectDir,
|
|
26195
|
+
join(projectDir, AX_PATHS.AGENTS),
|
|
22790
26196
|
void 0,
|
|
22791
26197
|
teamManager
|
|
22792
26198
|
);
|
|
22793
26199
|
const abilitiesManager = new AbilitiesManager(
|
|
22794
|
-
join(projectDir,
|
|
26200
|
+
join(projectDir, AX_PATHS.ABILITIES)
|
|
22795
26201
|
);
|
|
22796
26202
|
this.memoryManager = new LazyMemoryManager({
|
|
22797
|
-
dbPath: join(projectDir,
|
|
26203
|
+
dbPath: join(projectDir, AX_PATHS.MEMORY, "memory.db")
|
|
22798
26204
|
});
|
|
22799
26205
|
this.pathResolver = new PathResolver({
|
|
22800
26206
|
projectDir,
|
|
22801
26207
|
workingDir: process.cwd(),
|
|
22802
|
-
agentWorkspace: join(projectDir,
|
|
26208
|
+
agentWorkspace: join(projectDir, AX_PATHS.WORKSPACES)
|
|
22803
26209
|
});
|
|
22804
26210
|
const providers = [];
|
|
22805
26211
|
if (config.providers["claude-code"]?.enabled) {
|
|
22806
26212
|
const { ClaudeProvider: ClaudeProvider2 } = await Promise.resolve().then(() => (init_claude_provider(), claude_provider_exports));
|
|
22807
|
-
|
|
26213
|
+
const claudeConfig = config.providers["claude-code"];
|
|
26214
|
+
providers.push(new ClaudeProvider2({ ...claudeConfig, name: "claude-code", command: claudeConfig.command || "claude" }));
|
|
22808
26215
|
}
|
|
22809
26216
|
if (config.providers["gemini-cli"]?.enabled) {
|
|
22810
26217
|
const { GeminiProvider: GeminiProvider2 } = await Promise.resolve().then(() => (init_gemini_provider(), gemini_provider_exports));
|
|
22811
|
-
|
|
26218
|
+
const geminiConfig = config.providers["gemini-cli"];
|
|
26219
|
+
providers.push(new GeminiProvider2({ ...geminiConfig, name: "gemini-cli", command: geminiConfig.command || "gemini" }));
|
|
22812
26220
|
}
|
|
22813
26221
|
if (config.providers["openai"]?.enabled) {
|
|
22814
26222
|
const { createOpenAIProviderSync: createOpenAIProviderSync2 } = await Promise.resolve().then(() => (init_openai_provider_factory(), openai_provider_factory_exports));
|
|
22815
|
-
|
|
26223
|
+
const openaiConfig = config.providers["openai"];
|
|
26224
|
+
providers.push(createOpenAIProviderSync2({ ...openaiConfig, name: "openai", command: openaiConfig.command || "codex" }, openaiConfig.integration));
|
|
26225
|
+
}
|
|
26226
|
+
if (config.providers["glm"]?.enabled) {
|
|
26227
|
+
const { GLMProvider: GLMProvider2 } = await Promise.resolve().then(() => (init_glm_provider(), glm_provider_exports));
|
|
26228
|
+
const glmConfig = config.providers["glm"];
|
|
26229
|
+
providers.push(new GLMProvider2({
|
|
26230
|
+
name: "glm",
|
|
26231
|
+
enabled: true,
|
|
26232
|
+
priority: glmConfig.priority,
|
|
26233
|
+
timeout: glmConfig.timeout,
|
|
26234
|
+
mode: "sdk"
|
|
26235
|
+
}));
|
|
26236
|
+
}
|
|
26237
|
+
if (config.providers["grok"]?.enabled) {
|
|
26238
|
+
const { GrokProvider: GrokProvider2 } = await Promise.resolve().then(() => (init_grok_provider(), grok_provider_exports));
|
|
26239
|
+
const grokConfig = config.providers["grok"];
|
|
26240
|
+
providers.push(new GrokProvider2({
|
|
26241
|
+
name: "grok",
|
|
26242
|
+
enabled: true,
|
|
26243
|
+
priority: grokConfig.priority,
|
|
26244
|
+
timeout: grokConfig.timeout,
|
|
26245
|
+
mode: "sdk"
|
|
26246
|
+
}));
|
|
22816
26247
|
}
|
|
22817
26248
|
const healthCheckInterval = config.router?.healthCheckInterval;
|
|
22818
26249
|
this.router = new Router({
|
|
@@ -22966,9 +26397,7 @@ Use this tool first to understand what AutomatosX offers.`,
|
|
|
22966
26397
|
register("create_task", createCreateTaskHandler({
|
|
22967
26398
|
getSession: () => this.session
|
|
22968
26399
|
}));
|
|
22969
|
-
register("run_task", createRunTaskHandler(
|
|
22970
|
-
getSession: () => this.session
|
|
22971
|
-
}));
|
|
26400
|
+
register("run_task", createRunTaskHandler());
|
|
22972
26401
|
register("get_task_result", createGetTaskResultHandler());
|
|
22973
26402
|
register("list_tasks", createListTasksHandler());
|
|
22974
26403
|
register("delete_task", createDeleteTaskHandler());
|
|
@@ -23050,7 +26479,7 @@ Use this tool first to understand what AutomatosX offers.`,
|
|
|
23050
26479
|
normalizedProvider: this.session.normalizedProvider
|
|
23051
26480
|
});
|
|
23052
26481
|
const requestedProtocol = request.params?.protocolVersion;
|
|
23053
|
-
const negotiated = MCP_SUPPORTED_VERSIONS.find((version) => version === requestedProtocol) ??
|
|
26482
|
+
const negotiated = MCP_SUPPORTED_VERSIONS.find((version) => version === requestedProtocol) ?? "2024-11-05";
|
|
23054
26483
|
this.negotiatedProtocolVersion = negotiated;
|
|
23055
26484
|
const response = {
|
|
23056
26485
|
protocolVersion: negotiated,
|
|
@@ -23363,20 +26792,21 @@ Streaming: ${summary.streamingNotifications}` },
|
|
|
23363
26792
|
logger.debug("[MCP Server] Response sent", { id: response.id, length: json.length });
|
|
23364
26793
|
}
|
|
23365
26794
|
/**
|
|
23366
|
-
* Start stdio server with
|
|
26795
|
+
* Start stdio server with hybrid framing support
|
|
23367
26796
|
*
|
|
23368
|
-
* v12.
|
|
23369
|
-
*
|
|
26797
|
+
* v12.3.1: Supports BOTH framing formats for maximum compatibility:
|
|
26798
|
+
* 1. Newline-delimited (official MCP spec, used by Claude Code, @modelcontextprotocol/sdk)
|
|
26799
|
+
* 2. Content-Length (LSP-style, used by ax-glm, ax-grok, some older clients)
|
|
23370
26800
|
*
|
|
23371
|
-
*
|
|
23372
|
-
*
|
|
23373
|
-
* Reference: @modelcontextprotocol/sdk ReadBuffer uses: buffer.indexOf('\n')
|
|
26801
|
+
* Detection: If first line starts with "Content-Length:", use LSP framing.
|
|
26802
|
+
* Otherwise, use newline-delimited framing.
|
|
23374
26803
|
*
|
|
23375
26804
|
* BUG FIX (v9.0.1): Added iteration limit and buffer size checks to prevent infinite loops
|
|
23376
26805
|
*/
|
|
23377
26806
|
async start() {
|
|
23378
26807
|
logger.info("[MCP Server] Starting stdio JSON-RPC server...");
|
|
23379
26808
|
let buffer = "";
|
|
26809
|
+
let detectedFraming = null;
|
|
23380
26810
|
process.stdin.on("data", (chunk) => {
|
|
23381
26811
|
void this.stdinMutex.runExclusive(async () => {
|
|
23382
26812
|
buffer += chunk.toString("utf-8");
|
|
@@ -23388,17 +26818,49 @@ Streaming: ${summary.streamingNotifications}` },
|
|
|
23388
26818
|
buffer = "";
|
|
23389
26819
|
return;
|
|
23390
26820
|
}
|
|
26821
|
+
if (detectedFraming === null) {
|
|
26822
|
+
if (buffer.startsWith("Content-Length:")) {
|
|
26823
|
+
detectedFraming = "content-length";
|
|
26824
|
+
logger.info("[MCP Server] Detected Content-Length framing (LSP-style)");
|
|
26825
|
+
} else if (buffer.includes("{")) {
|
|
26826
|
+
detectedFraming = "newline";
|
|
26827
|
+
logger.info("[MCP Server] Detected newline-delimited framing (MCP spec)");
|
|
26828
|
+
}
|
|
26829
|
+
}
|
|
23391
26830
|
let iterations = 0;
|
|
23392
26831
|
while (iterations < STDIO_MAX_ITERATIONS) {
|
|
23393
26832
|
iterations++;
|
|
23394
|
-
|
|
23395
|
-
if (
|
|
23396
|
-
|
|
23397
|
-
|
|
23398
|
-
|
|
26833
|
+
let jsonMessage = null;
|
|
26834
|
+
if (detectedFraming === "content-length") {
|
|
26835
|
+
const headerEnd = buffer.indexOf("\r\n\r\n");
|
|
26836
|
+
if (headerEnd === -1) break;
|
|
26837
|
+
const headers = buffer.slice(0, headerEnd);
|
|
26838
|
+
const contentLengthMatch = headers.match(/Content-Length:\s*(\d+)/i);
|
|
26839
|
+
if (!contentLengthMatch) {
|
|
26840
|
+
const lineEnd = buffer.indexOf("\n");
|
|
26841
|
+
if (lineEnd !== -1) {
|
|
26842
|
+
buffer = buffer.slice(lineEnd + 1);
|
|
26843
|
+
continue;
|
|
26844
|
+
}
|
|
26845
|
+
break;
|
|
26846
|
+
}
|
|
26847
|
+
const contentLength = parseInt(contentLengthMatch[1], 10);
|
|
26848
|
+
const bodyStart = headerEnd + 4;
|
|
26849
|
+
const bodyEnd = bodyStart + contentLength;
|
|
26850
|
+
if (buffer.length < bodyEnd) break;
|
|
26851
|
+
jsonMessage = buffer.slice(bodyStart, bodyEnd);
|
|
26852
|
+
buffer = buffer.slice(bodyEnd);
|
|
26853
|
+
} else {
|
|
26854
|
+
const newlineIndex = buffer.indexOf("\n");
|
|
26855
|
+
if (newlineIndex === -1) break;
|
|
26856
|
+
jsonMessage = buffer.slice(0, newlineIndex);
|
|
26857
|
+
if (jsonMessage.endsWith("\r")) {
|
|
26858
|
+
jsonMessage = jsonMessage.slice(0, -1);
|
|
26859
|
+
}
|
|
26860
|
+
buffer = buffer.slice(newlineIndex + 1);
|
|
26861
|
+
if (jsonMessage.trim() === "") continue;
|
|
23399
26862
|
}
|
|
23400
|
-
|
|
23401
|
-
if (jsonMessage.trim() === "") continue;
|
|
26863
|
+
if (!jsonMessage) break;
|
|
23402
26864
|
if (jsonMessage.length > STDIO_MAX_MESSAGE_SIZE) {
|
|
23403
26865
|
logger.error("[MCP Server] Message size exceeded maximum", {
|
|
23404
26866
|
messageSize: jsonMessage.length,
|
|
@@ -23441,7 +26903,7 @@ Streaming: ${summary.streamingNotifications}` },
|
|
|
23441
26903
|
process.stdin.on("end", () => shutdown("Server stopped (stdin closed)"));
|
|
23442
26904
|
process.on("SIGINT", () => shutdown("Received SIGINT, shutting down..."));
|
|
23443
26905
|
process.on("SIGTERM", () => shutdown("Received SIGTERM, shutting down..."));
|
|
23444
|
-
logger.info("[MCP Server] Server started successfully (newline-
|
|
26906
|
+
logger.info("[MCP Server] Server started successfully (hybrid framing: newline + Content-Length)");
|
|
23445
26907
|
}
|
|
23446
26908
|
};
|
|
23447
26909
|
|