@defai.digital/automatosx 12.6.2 → 12.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.js +1694 -1137
- package/dist/mcp/index.js +1100 -208
- package/package.json +1 -1
package/dist/mcp/index.js
CHANGED
|
@@ -22,7 +22,6 @@ import { EventEmitter } from 'events';
|
|
|
22
22
|
import { findUp } from 'find-up';
|
|
23
23
|
import * as sqliteVec from 'sqlite-vec';
|
|
24
24
|
import { randomUUID, randomBytes, createHash } from 'crypto';
|
|
25
|
-
import yaml from 'yaml';
|
|
26
25
|
import { gzipSync, gunzipSync } from 'zlib';
|
|
27
26
|
|
|
28
27
|
var __defProp = Object.defineProperty;
|
|
@@ -2492,6 +2491,7 @@ function getRetryableErrors(provider) {
|
|
|
2492
2491
|
return [...baseErrors, ...CODEX_RETRYABLE_ERRORS];
|
|
2493
2492
|
case "glm":
|
|
2494
2493
|
case "grok":
|
|
2494
|
+
case "qwen":
|
|
2495
2495
|
return [...baseErrors, ...OPENAI_RETRYABLE_ERRORS];
|
|
2496
2496
|
case "base":
|
|
2497
2497
|
default:
|
|
@@ -2594,6 +2594,10 @@ var init_base_provider = __esm({
|
|
|
2594
2594
|
// v12.0.0: Native Grok provider (xAI)
|
|
2595
2595
|
"ax-grok",
|
|
2596
2596
|
// v12.0.0: Alias for grok
|
|
2597
|
+
"qwen",
|
|
2598
|
+
// v12.7.0: Qwen Code provider (Alibaba Cloud)
|
|
2599
|
+
"qwen-code",
|
|
2600
|
+
// v12.7.0: Alias for qwen
|
|
2597
2601
|
"test-provider"
|
|
2598
2602
|
// For unit tests
|
|
2599
2603
|
];
|
|
@@ -2956,7 +2960,7 @@ var init_base_provider = __esm({
|
|
|
2956
2960
|
if (readlineInterface) {
|
|
2957
2961
|
try {
|
|
2958
2962
|
readlineInterface.close();
|
|
2959
|
-
} catch
|
|
2963
|
+
} catch {
|
|
2960
2964
|
} finally {
|
|
2961
2965
|
readlineInterface = null;
|
|
2962
2966
|
}
|
|
@@ -2964,7 +2968,7 @@ var init_base_provider = __esm({
|
|
|
2964
2968
|
if (stderrInterface) {
|
|
2965
2969
|
try {
|
|
2966
2970
|
stderrInterface.close();
|
|
2967
|
-
} catch
|
|
2971
|
+
} catch {
|
|
2968
2972
|
} finally {
|
|
2969
2973
|
stderrInterface = null;
|
|
2970
2974
|
}
|
|
@@ -3158,7 +3162,7 @@ ${fullPrompt}
|
|
|
3158
3162
|
this.health.consecutiveSuccesses = 0;
|
|
3159
3163
|
}
|
|
3160
3164
|
return available;
|
|
3161
|
-
} catch
|
|
3165
|
+
} catch {
|
|
3162
3166
|
this.health.available = false;
|
|
3163
3167
|
this.health.errorRate = 1;
|
|
3164
3168
|
this.health.lastCheck = Date.now();
|
|
@@ -3334,6 +3338,8 @@ ${fullPrompt}
|
|
|
3334
3338
|
retryableProvider = "glm";
|
|
3335
3339
|
} else if (providerName === "grok" || providerName === "ax-grok") {
|
|
3336
3340
|
retryableProvider = "grok";
|
|
3341
|
+
} else if (providerName === "qwen" || providerName === "qwen-code") {
|
|
3342
|
+
retryableProvider = "qwen";
|
|
3337
3343
|
}
|
|
3338
3344
|
return shouldRetryError(error, retryableProvider);
|
|
3339
3345
|
}
|
|
@@ -5788,13 +5794,21 @@ var init_types2 = __esm({
|
|
|
5788
5794
|
"src/integrations/ax-glm/types.ts"() {
|
|
5789
5795
|
init_esm_shims();
|
|
5790
5796
|
GLM_MODEL_MAPPING = {
|
|
5797
|
+
// Convenience aliases (ax-cli v4.3.15)
|
|
5798
|
+
"glm-latest": "glm-4.6",
|
|
5799
|
+
"glm-vision": "glm-4.6v",
|
|
5800
|
+
"glm-fast": "glm-4-flash",
|
|
5801
|
+
"glm-image": "cogview-4",
|
|
5802
|
+
// Legacy aliases (deprecated)
|
|
5791
5803
|
"glm-4-plus": "glm-4.6",
|
|
5792
|
-
"glm-
|
|
5804
|
+
"glm-4.5v": "glm-4.6v",
|
|
5805
|
+
"glm-4v": "glm-4.6v",
|
|
5806
|
+
"glm-4": "glm-4.6",
|
|
5793
5807
|
"glm-4-air": "glm-4-flash",
|
|
5794
5808
|
"glm-4-airx": "glm-4-flash"
|
|
5795
5809
|
};
|
|
5796
5810
|
GLM_DEFAULT_BASE_URL = "https://open.bigmodel.cn/api/paas/v4";
|
|
5797
|
-
GLM_DEFAULT_MODEL = "glm-4";
|
|
5811
|
+
GLM_DEFAULT_MODEL = "glm-4.6";
|
|
5798
5812
|
GLM_DEFAULT_COMMAND = "ax-glm";
|
|
5799
5813
|
}
|
|
5800
5814
|
});
|
|
@@ -6743,10 +6757,18 @@ var init_types3 = __esm({
|
|
|
6743
6757
|
"src/integrations/ax-grok/types.ts"() {
|
|
6744
6758
|
init_esm_shims();
|
|
6745
6759
|
GROK_MODEL_MAPPING = {
|
|
6746
|
-
|
|
6760
|
+
// Convenience aliases (ax-cli v4.3.15)
|
|
6761
|
+
"grok-latest": "grok-4-0709",
|
|
6762
|
+
"grok-fast": "grok-4.1-fast",
|
|
6763
|
+
"grok-mini": "grok-3-mini",
|
|
6764
|
+
"grok-vision": "grok-2-vision-1212",
|
|
6765
|
+
"grok-image": "grok-2-image-1212",
|
|
6766
|
+
// Legacy aliases (deprecated)
|
|
6767
|
+
"grok-beta": "grok-3",
|
|
6768
|
+
"grok-2-vision": "grok-2-vision-1212"
|
|
6747
6769
|
};
|
|
6748
6770
|
GROK_DEFAULT_BASE_URL = "https://api.x.ai/v1";
|
|
6749
|
-
GROK_DEFAULT_MODEL = "grok-
|
|
6771
|
+
GROK_DEFAULT_MODEL = "grok-4-0709";
|
|
6750
6772
|
GROK_DEFAULT_COMMAND = "ax-grok";
|
|
6751
6773
|
}
|
|
6752
6774
|
});
|
|
@@ -7678,6 +7700,902 @@ Mode: ${this.grokConfig.mode || "auto"}`;
|
|
|
7678
7700
|
}
|
|
7679
7701
|
});
|
|
7680
7702
|
|
|
7703
|
+
// src/integrations/qwen-code/types.ts
|
|
7704
|
+
function normalizeQwenModel(model) {
|
|
7705
|
+
return QWEN_MODEL_MAPPING[model] || model;
|
|
7706
|
+
}
|
|
7707
|
+
function isVisionModel(model) {
|
|
7708
|
+
return model.includes("qwen3-coder") || model.includes("qwen-max");
|
|
7709
|
+
}
|
|
7710
|
+
function getModelContextWindow(model) {
|
|
7711
|
+
const normalizedModel = normalizeQwenModel(model);
|
|
7712
|
+
if (normalizedModel.includes("480b")) return 128e3;
|
|
7713
|
+
if (normalizedModel.includes("30b")) return 64e3;
|
|
7714
|
+
if (normalizedModel.includes("qwen-max")) return 128e3;
|
|
7715
|
+
if (normalizedModel.includes("qwen-plus")) return 128e3;
|
|
7716
|
+
if (normalizedModel.includes("qwen-turbo")) return 128e3;
|
|
7717
|
+
return 64e3;
|
|
7718
|
+
}
|
|
7719
|
+
var QWEN_DEFAULT_BASE_URL, QWEN_DEFAULT_MODEL, QWEN_DEFAULT_COMMAND, QWEN_MODEL_MAPPING;
|
|
7720
|
+
var init_types4 = __esm({
|
|
7721
|
+
"src/integrations/qwen-code/types.ts"() {
|
|
7722
|
+
init_esm_shims();
|
|
7723
|
+
QWEN_DEFAULT_BASE_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1";
|
|
7724
|
+
QWEN_DEFAULT_MODEL = "qwen-turbo";
|
|
7725
|
+
QWEN_DEFAULT_COMMAND = "qwen";
|
|
7726
|
+
QWEN_MODEL_MAPPING = {
|
|
7727
|
+
// Normalize common aliases
|
|
7728
|
+
"qwen-coder": "qwen3-coder-480b-a35b-instruct",
|
|
7729
|
+
"qwen-coder-480b": "qwen3-coder-480b-a35b-instruct",
|
|
7730
|
+
"qwen-coder-30b": "qwen3-coder-30b-a3b-instruct",
|
|
7731
|
+
"qwen2.5-coder": "qwen2.5-coder-32b-instruct"
|
|
7732
|
+
};
|
|
7733
|
+
}
|
|
7734
|
+
});
|
|
7735
|
+
|
|
7736
|
+
// src/integrations/qwen-code/sdk-adapter.ts
|
|
7737
|
+
var QwenSdkAdapter;
|
|
7738
|
+
var init_sdk_adapter4 = __esm({
|
|
7739
|
+
"src/integrations/qwen-code/sdk-adapter.ts"() {
|
|
7740
|
+
init_esm_shims();
|
|
7741
|
+
init_logger();
|
|
7742
|
+
init_validation_limits();
|
|
7743
|
+
init_types4();
|
|
7744
|
+
QwenSdkAdapter = class {
|
|
7745
|
+
client = null;
|
|
7746
|
+
config;
|
|
7747
|
+
initialized = false;
|
|
7748
|
+
constructor(config = {}) {
|
|
7749
|
+
const apiKey = config.apiKey || process.env.DASHSCOPE_API_KEY || process.env.QWEN_API_KEY || "";
|
|
7750
|
+
this.config = {
|
|
7751
|
+
apiKey,
|
|
7752
|
+
baseUrl: config.baseUrl || QWEN_DEFAULT_BASE_URL,
|
|
7753
|
+
model: config.model || QWEN_DEFAULT_MODEL,
|
|
7754
|
+
timeout: config.timeout || TIMEOUTS.PROVIDER_DEFAULT
|
|
7755
|
+
};
|
|
7756
|
+
logger.debug("[Qwen SDK] Adapter created", {
|
|
7757
|
+
model: this.config.model,
|
|
7758
|
+
baseUrl: this.config.baseUrl,
|
|
7759
|
+
hasApiKey: !!this.config.apiKey
|
|
7760
|
+
});
|
|
7761
|
+
}
|
|
7762
|
+
/**
|
|
7763
|
+
* Check if SDK is available (OpenAI package installed and API key configured)
|
|
7764
|
+
*/
|
|
7765
|
+
async isAvailable() {
|
|
7766
|
+
try {
|
|
7767
|
+
if (!this.config.apiKey) {
|
|
7768
|
+
logger.debug("[Qwen SDK] No API key configured (set DASHSCOPE_API_KEY or QWEN_API_KEY)");
|
|
7769
|
+
return false;
|
|
7770
|
+
}
|
|
7771
|
+
await import('openai');
|
|
7772
|
+
return true;
|
|
7773
|
+
} catch (error) {
|
|
7774
|
+
logger.debug("[Qwen SDK] OpenAI SDK not available", {
|
|
7775
|
+
error: error instanceof Error ? error.message : String(error)
|
|
7776
|
+
});
|
|
7777
|
+
return false;
|
|
7778
|
+
}
|
|
7779
|
+
}
|
|
7780
|
+
/**
|
|
7781
|
+
* Initialize the SDK client
|
|
7782
|
+
*/
|
|
7783
|
+
async initialize() {
|
|
7784
|
+
if (this.initialized) {
|
|
7785
|
+
return;
|
|
7786
|
+
}
|
|
7787
|
+
try {
|
|
7788
|
+
const OpenAI = (await import('openai')).default;
|
|
7789
|
+
this.client = new OpenAI({
|
|
7790
|
+
apiKey: this.config.apiKey,
|
|
7791
|
+
baseURL: this.config.baseUrl,
|
|
7792
|
+
timeout: this.config.timeout
|
|
7793
|
+
});
|
|
7794
|
+
this.initialized = true;
|
|
7795
|
+
logger.debug("[Qwen SDK] Client initialized", {
|
|
7796
|
+
model: this.config.model
|
|
7797
|
+
});
|
|
7798
|
+
} catch (error) {
|
|
7799
|
+
throw new Error(
|
|
7800
|
+
`Failed to initialize Qwen SDK: ${error instanceof Error ? error.message : String(error)}`
|
|
7801
|
+
);
|
|
7802
|
+
}
|
|
7803
|
+
}
|
|
7804
|
+
/**
|
|
7805
|
+
* Execute a request using the Qwen SDK
|
|
7806
|
+
*/
|
|
7807
|
+
async execute(request) {
|
|
7808
|
+
if (!this.initialized) {
|
|
7809
|
+
await this.initialize();
|
|
7810
|
+
}
|
|
7811
|
+
const startTime = Date.now();
|
|
7812
|
+
try {
|
|
7813
|
+
const messages = [];
|
|
7814
|
+
if (request.systemPrompt) {
|
|
7815
|
+
messages.push({
|
|
7816
|
+
role: "system",
|
|
7817
|
+
content: request.systemPrompt
|
|
7818
|
+
});
|
|
7819
|
+
}
|
|
7820
|
+
messages.push({
|
|
7821
|
+
role: "user",
|
|
7822
|
+
content: request.prompt
|
|
7823
|
+
});
|
|
7824
|
+
const model = normalizeQwenModel(request.model || this.config.model);
|
|
7825
|
+
logger.debug("[Qwen SDK] Executing request", {
|
|
7826
|
+
model,
|
|
7827
|
+
messageCount: messages.length,
|
|
7828
|
+
promptLength: request.prompt.length
|
|
7829
|
+
});
|
|
7830
|
+
const openaiClient = this.client;
|
|
7831
|
+
const response = await openaiClient.chat.completions.create({
|
|
7832
|
+
model,
|
|
7833
|
+
messages,
|
|
7834
|
+
max_tokens: request.maxTokens,
|
|
7835
|
+
temperature: request.temperature,
|
|
7836
|
+
stream: false
|
|
7837
|
+
});
|
|
7838
|
+
const latencyMs = Date.now() - startTime;
|
|
7839
|
+
if (!response.choices || response.choices.length === 0) {
|
|
7840
|
+
throw new Error("Qwen API returned empty choices array");
|
|
7841
|
+
}
|
|
7842
|
+
const choice = response.choices[0];
|
|
7843
|
+
const content = choice?.message?.content || "";
|
|
7844
|
+
const finishReason = choice?.finish_reason || "unknown";
|
|
7845
|
+
logger.debug("[Qwen SDK] Request completed", {
|
|
7846
|
+
model: response.model,
|
|
7847
|
+
latencyMs,
|
|
7848
|
+
tokensUsed: response.usage?.total_tokens
|
|
7849
|
+
});
|
|
7850
|
+
return {
|
|
7851
|
+
content,
|
|
7852
|
+
model: response.model,
|
|
7853
|
+
tokensUsed: response.usage ? {
|
|
7854
|
+
prompt: response.usage.prompt_tokens,
|
|
7855
|
+
completion: response.usage.completion_tokens,
|
|
7856
|
+
total: response.usage.total_tokens
|
|
7857
|
+
} : { prompt: 0, completion: 0, total: 0 },
|
|
7858
|
+
latencyMs,
|
|
7859
|
+
finishReason,
|
|
7860
|
+
cached: false
|
|
7861
|
+
};
|
|
7862
|
+
} catch (error) {
|
|
7863
|
+
const latencyMs = Date.now() - startTime;
|
|
7864
|
+
logger.error("[Qwen SDK] Request failed", {
|
|
7865
|
+
error: error instanceof Error ? error.message : String(error),
|
|
7866
|
+
latencyMs
|
|
7867
|
+
});
|
|
7868
|
+
throw error;
|
|
7869
|
+
}
|
|
7870
|
+
}
|
|
7871
|
+
/**
|
|
7872
|
+
* Get the configured model
|
|
7873
|
+
*/
|
|
7874
|
+
getModel() {
|
|
7875
|
+
return this.config.model;
|
|
7876
|
+
}
|
|
7877
|
+
/**
|
|
7878
|
+
* Clean up resources
|
|
7879
|
+
*/
|
|
7880
|
+
async destroy() {
|
|
7881
|
+
this.client = null;
|
|
7882
|
+
this.initialized = false;
|
|
7883
|
+
logger.debug("[Qwen SDK] Adapter destroyed");
|
|
7884
|
+
}
|
|
7885
|
+
};
|
|
7886
|
+
}
|
|
7887
|
+
});
|
|
7888
|
+
var NON_INTERACTIVE_ENV, SIGKILL_ESCALATION_MS, QwenCliWrapper;
|
|
7889
|
+
var init_cli_wrapper4 = __esm({
|
|
7890
|
+
"src/integrations/qwen-code/cli-wrapper.ts"() {
|
|
7891
|
+
init_esm_shims();
|
|
7892
|
+
init_logger();
|
|
7893
|
+
init_validation_limits();
|
|
7894
|
+
init_cli_provider_detector();
|
|
7895
|
+
init_types4();
|
|
7896
|
+
NON_INTERACTIVE_ENV = {
|
|
7897
|
+
TERM: "dumb",
|
|
7898
|
+
NO_COLOR: "1",
|
|
7899
|
+
FORCE_COLOR: "0",
|
|
7900
|
+
CI: "true",
|
|
7901
|
+
NO_UPDATE_NOTIFIER: "1",
|
|
7902
|
+
DEBIAN_FRONTEND: "noninteractive"
|
|
7903
|
+
};
|
|
7904
|
+
SIGKILL_ESCALATION_MS = 5e3;
|
|
7905
|
+
QwenCliWrapper = class {
|
|
7906
|
+
config;
|
|
7907
|
+
constructor(config = {}) {
|
|
7908
|
+
this.config = {
|
|
7909
|
+
command: config.command || QWEN_DEFAULT_COMMAND,
|
|
7910
|
+
vlmSwitchMode: config.vlmSwitchMode || "once",
|
|
7911
|
+
yolo: config.yolo || false,
|
|
7912
|
+
timeout: config.timeout || TIMEOUTS.PROVIDER_DEFAULT
|
|
7913
|
+
};
|
|
7914
|
+
logger.debug("[Qwen CLI] Wrapper created", {
|
|
7915
|
+
command: this.config.command,
|
|
7916
|
+
timeout: this.config.timeout
|
|
7917
|
+
});
|
|
7918
|
+
}
|
|
7919
|
+
/**
|
|
7920
|
+
* Check if Qwen CLI is available on PATH
|
|
7921
|
+
*/
|
|
7922
|
+
async isAvailable() {
|
|
7923
|
+
if (process.env.AX_MOCK_PROVIDERS === "true") {
|
|
7924
|
+
return true;
|
|
7925
|
+
}
|
|
7926
|
+
try {
|
|
7927
|
+
const result = findOnPath(this.config.command);
|
|
7928
|
+
logger.debug("[Qwen CLI] Availability check", {
|
|
7929
|
+
available: result.found,
|
|
7930
|
+
path: result.path
|
|
7931
|
+
});
|
|
7932
|
+
return result.found;
|
|
7933
|
+
} catch (error) {
|
|
7934
|
+
logger.debug("[Qwen CLI] Availability check failed", {
|
|
7935
|
+
error: error instanceof Error ? error.message : String(error)
|
|
7936
|
+
});
|
|
7937
|
+
return false;
|
|
7938
|
+
}
|
|
7939
|
+
}
|
|
7940
|
+
/**
|
|
7941
|
+
* Execute a prompt using the Qwen CLI
|
|
7942
|
+
*
|
|
7943
|
+
* Since Qwen CLI is interactive, we:
|
|
7944
|
+
* 1. Spawn the process
|
|
7945
|
+
* 2. Send the prompt via stdin
|
|
7946
|
+
* 3. Capture output until we detect completion
|
|
7947
|
+
* 4. Return the response
|
|
7948
|
+
*/
|
|
7949
|
+
async execute(request) {
|
|
7950
|
+
if (process.env.AX_MOCK_PROVIDERS === "true") {
|
|
7951
|
+
return this.createMockResponse(request.prompt);
|
|
7952
|
+
}
|
|
7953
|
+
const startTime = Date.now();
|
|
7954
|
+
return new Promise((resolve5, reject) => {
|
|
7955
|
+
const args2 = [];
|
|
7956
|
+
if (this.config.vlmSwitchMode !== "once") {
|
|
7957
|
+
args2.push("--vlm-switch-mode", this.config.vlmSwitchMode);
|
|
7958
|
+
}
|
|
7959
|
+
if (this.config.yolo) {
|
|
7960
|
+
args2.push("--yolo");
|
|
7961
|
+
}
|
|
7962
|
+
logger.debug("[Qwen CLI] Spawning process", {
|
|
7963
|
+
command: this.config.command,
|
|
7964
|
+
args: args2,
|
|
7965
|
+
promptLength: request.prompt.length
|
|
7966
|
+
});
|
|
7967
|
+
const child = spawn(this.config.command, args2, {
|
|
7968
|
+
env: { ...process.env, ...NON_INTERACTIVE_ENV }
|
|
7969
|
+
});
|
|
7970
|
+
let stdout = "";
|
|
7971
|
+
let stderr = "";
|
|
7972
|
+
let timeoutId = null;
|
|
7973
|
+
let forceKillTimer = null;
|
|
7974
|
+
let readlineInterface = null;
|
|
7975
|
+
let responseStarted = false;
|
|
7976
|
+
const cleanup = () => {
|
|
7977
|
+
if (timeoutId) {
|
|
7978
|
+
clearTimeout(timeoutId);
|
|
7979
|
+
timeoutId = null;
|
|
7980
|
+
}
|
|
7981
|
+
if (forceKillTimer) {
|
|
7982
|
+
clearTimeout(forceKillTimer);
|
|
7983
|
+
forceKillTimer = null;
|
|
7984
|
+
}
|
|
7985
|
+
if (readlineInterface) {
|
|
7986
|
+
try {
|
|
7987
|
+
readlineInterface.close();
|
|
7988
|
+
} catch {
|
|
7989
|
+
}
|
|
7990
|
+
readlineInterface = null;
|
|
7991
|
+
}
|
|
7992
|
+
};
|
|
7993
|
+
if (child.stdout) {
|
|
7994
|
+
readlineInterface = readline.createInterface({
|
|
7995
|
+
input: child.stdout,
|
|
7996
|
+
crlfDelay: Infinity
|
|
7997
|
+
});
|
|
7998
|
+
readlineInterface.on("line", (line) => {
|
|
7999
|
+
if (line.startsWith(">") && !responseStarted) {
|
|
8000
|
+
responseStarted = true;
|
|
8001
|
+
return;
|
|
8002
|
+
}
|
|
8003
|
+
if (responseStarted) {
|
|
8004
|
+
stdout += line + "\n";
|
|
8005
|
+
}
|
|
8006
|
+
});
|
|
8007
|
+
readlineInterface.on("error", (error) => {
|
|
8008
|
+
logger.debug("[Qwen CLI] Readline error", {
|
|
8009
|
+
error: error.message
|
|
8010
|
+
});
|
|
8011
|
+
});
|
|
8012
|
+
}
|
|
8013
|
+
if (child.stderr) {
|
|
8014
|
+
child.stderr.on("data", (data) => {
|
|
8015
|
+
stderr += data.toString();
|
|
8016
|
+
});
|
|
8017
|
+
}
|
|
8018
|
+
if (child.stdin) {
|
|
8019
|
+
try {
|
|
8020
|
+
let fullPrompt = request.prompt;
|
|
8021
|
+
if (request.systemPrompt) {
|
|
8022
|
+
fullPrompt = `${request.systemPrompt}
|
|
8023
|
+
|
|
8024
|
+
${request.prompt}`;
|
|
8025
|
+
}
|
|
8026
|
+
child.stdin.write(fullPrompt);
|
|
8027
|
+
child.stdin.write("\n");
|
|
8028
|
+
child.stdin.end();
|
|
8029
|
+
} catch (error) {
|
|
8030
|
+
cleanup();
|
|
8031
|
+
child.kill("SIGTERM");
|
|
8032
|
+
reject(new Error(`Failed to write to Qwen stdin: ${error instanceof Error ? error.message : String(error)}`));
|
|
8033
|
+
return;
|
|
8034
|
+
}
|
|
8035
|
+
} else {
|
|
8036
|
+
cleanup();
|
|
8037
|
+
reject(new Error("Qwen CLI stdin not available"));
|
|
8038
|
+
return;
|
|
8039
|
+
}
|
|
8040
|
+
child.on("close", (code, signal) => {
|
|
8041
|
+
cleanup();
|
|
8042
|
+
const latencyMs = Date.now() - startTime;
|
|
8043
|
+
if (stderr) {
|
|
8044
|
+
logger.debug("[Qwen CLI] stderr output", { stderr: stderr.trim() });
|
|
8045
|
+
}
|
|
8046
|
+
if ((code === 0 || code === null) && !signal) {
|
|
8047
|
+
const content = this.parseResponse(stdout);
|
|
8048
|
+
resolve5({
|
|
8049
|
+
content: content.trim(),
|
|
8050
|
+
model: "qwen-code-cli",
|
|
8051
|
+
tokensUsed: {
|
|
8052
|
+
prompt: this.estimateTokens(request.prompt),
|
|
8053
|
+
completion: this.estimateTokens(content),
|
|
8054
|
+
total: this.estimateTokens(request.prompt) + this.estimateTokens(content)
|
|
8055
|
+
},
|
|
8056
|
+
latencyMs,
|
|
8057
|
+
finishReason: "stop",
|
|
8058
|
+
cached: false
|
|
8059
|
+
});
|
|
8060
|
+
} else if (signal) {
|
|
8061
|
+
reject(new Error(`Qwen CLI killed by signal ${signal}. stderr: ${stderr || "none"}`));
|
|
8062
|
+
} else {
|
|
8063
|
+
reject(new Error(`Qwen CLI exited with code ${code}. stderr: ${stderr || "none"}`));
|
|
8064
|
+
}
|
|
8065
|
+
});
|
|
8066
|
+
child.on("error", (error) => {
|
|
8067
|
+
cleanup();
|
|
8068
|
+
logger.error("[Qwen CLI] Process error", {
|
|
8069
|
+
error: error.message
|
|
8070
|
+
});
|
|
8071
|
+
reject(new Error(`Failed to spawn Qwen CLI: ${error.message}`));
|
|
8072
|
+
});
|
|
8073
|
+
timeoutId = setTimeout(() => {
|
|
8074
|
+
if (child.pid && !child.killed) {
|
|
8075
|
+
logger.warn("[Qwen CLI] Killing process due to timeout", {
|
|
8076
|
+
pid: child.pid,
|
|
8077
|
+
timeout: this.config.timeout
|
|
8078
|
+
});
|
|
8079
|
+
child.kill("SIGTERM");
|
|
8080
|
+
forceKillTimer = setTimeout(() => {
|
|
8081
|
+
if (child.pid) {
|
|
8082
|
+
try {
|
|
8083
|
+
process.kill(child.pid, 0);
|
|
8084
|
+
logger.warn("[Qwen CLI] Force killing process", { pid: child.pid });
|
|
8085
|
+
child.kill("SIGKILL");
|
|
8086
|
+
} catch {
|
|
8087
|
+
}
|
|
8088
|
+
}
|
|
8089
|
+
}, SIGKILL_ESCALATION_MS);
|
|
8090
|
+
}
|
|
8091
|
+
}, this.config.timeout);
|
|
8092
|
+
});
|
|
8093
|
+
}
|
|
8094
|
+
/**
|
|
8095
|
+
* Parse response from CLI output
|
|
8096
|
+
*
|
|
8097
|
+
* Qwen CLI outputs include prompts and formatting that we need to strip.
|
|
8098
|
+
*/
|
|
8099
|
+
parseResponse(output) {
|
|
8100
|
+
let content = output.trim();
|
|
8101
|
+
content = content.replace(/\x1B\[[0-9;]*[mGKH]/g, "");
|
|
8102
|
+
content = content.replace(/^>\s*/gm, "").replace(/\n{3,}/g, "\n\n").trim();
|
|
8103
|
+
return content;
|
|
8104
|
+
}
|
|
8105
|
+
/**
|
|
8106
|
+
* Estimate token count
|
|
8107
|
+
*/
|
|
8108
|
+
estimateTokens(text) {
|
|
8109
|
+
return Math.ceil(text.length / 4);
|
|
8110
|
+
}
|
|
8111
|
+
/**
|
|
8112
|
+
* Create mock response for testing
|
|
8113
|
+
*/
|
|
8114
|
+
createMockResponse(prompt) {
|
|
8115
|
+
return {
|
|
8116
|
+
content: `[Mock Qwen Response]
|
|
8117
|
+
|
|
8118
|
+
This is a mock response from Qwen Code CLI.
|
|
8119
|
+
Prompt length: ${prompt.length} characters.`,
|
|
8120
|
+
model: "qwen-code-cli-mock",
|
|
8121
|
+
tokensUsed: {
|
|
8122
|
+
prompt: this.estimateTokens(prompt),
|
|
8123
|
+
completion: 50,
|
|
8124
|
+
total: this.estimateTokens(prompt) + 50
|
|
8125
|
+
},
|
|
8126
|
+
latencyMs: 10,
|
|
8127
|
+
finishReason: "stop",
|
|
8128
|
+
cached: false
|
|
8129
|
+
};
|
|
8130
|
+
}
|
|
8131
|
+
/**
|
|
8132
|
+
* Get CLI command
|
|
8133
|
+
*/
|
|
8134
|
+
getCommand() {
|
|
8135
|
+
return this.config.command;
|
|
8136
|
+
}
|
|
8137
|
+
};
|
|
8138
|
+
}
|
|
8139
|
+
});
|
|
8140
|
+
|
|
8141
|
+
// src/integrations/qwen-code/hybrid-adapter.ts
|
|
8142
|
+
var QwenHybridAdapter;
|
|
8143
|
+
var init_hybrid_adapter4 = __esm({
|
|
8144
|
+
"src/integrations/qwen-code/hybrid-adapter.ts"() {
|
|
8145
|
+
init_esm_shims();
|
|
8146
|
+
init_logger();
|
|
8147
|
+
init_sdk_adapter4();
|
|
8148
|
+
init_cli_wrapper4();
|
|
8149
|
+
QwenHybridAdapter = class {
|
|
8150
|
+
sdkAdapter = null;
|
|
8151
|
+
cliWrapper = null;
|
|
8152
|
+
options;
|
|
8153
|
+
activeMode = null;
|
|
8154
|
+
// Circuit breakers for each mode
|
|
8155
|
+
sdkCircuitBreaker = {
|
|
8156
|
+
failures: 0,
|
|
8157
|
+
lastFailure: 0,
|
|
8158
|
+
isOpen: false
|
|
8159
|
+
};
|
|
8160
|
+
cliCircuitBreaker = {
|
|
8161
|
+
failures: 0,
|
|
8162
|
+
lastFailure: 0,
|
|
8163
|
+
isOpen: false
|
|
8164
|
+
};
|
|
8165
|
+
// Circuit breaker configuration
|
|
8166
|
+
FAILURE_THRESHOLD = 3;
|
|
8167
|
+
RECOVERY_TIMEOUT_MS = 6e4;
|
|
8168
|
+
// 1 minute
|
|
8169
|
+
constructor(options = {}) {
|
|
8170
|
+
this.options = {
|
|
8171
|
+
mode: options.mode || "auto",
|
|
8172
|
+
model: options.model || "qwen-turbo",
|
|
8173
|
+
apiKey: options.apiKey,
|
|
8174
|
+
baseUrl: options.baseUrl,
|
|
8175
|
+
command: options.command || "qwen",
|
|
8176
|
+
timeout: options.timeout
|
|
8177
|
+
};
|
|
8178
|
+
logger.debug("[Qwen Hybrid] Adapter created", {
|
|
8179
|
+
mode: this.options.mode,
|
|
8180
|
+
model: this.options.model
|
|
8181
|
+
});
|
|
8182
|
+
}
|
|
8183
|
+
/**
|
|
8184
|
+
* Get or create SDK adapter
|
|
8185
|
+
*/
|
|
8186
|
+
getSdkAdapter() {
|
|
8187
|
+
if (!this.sdkAdapter) {
|
|
8188
|
+
const config = {
|
|
8189
|
+
apiKey: this.options.apiKey,
|
|
8190
|
+
baseUrl: this.options.baseUrl,
|
|
8191
|
+
model: this.options.model,
|
|
8192
|
+
timeout: this.options.timeout
|
|
8193
|
+
};
|
|
8194
|
+
this.sdkAdapter = new QwenSdkAdapter(config);
|
|
8195
|
+
}
|
|
8196
|
+
return this.sdkAdapter;
|
|
8197
|
+
}
|
|
8198
|
+
/**
|
|
8199
|
+
* Get or create CLI wrapper
|
|
8200
|
+
*/
|
|
8201
|
+
getCliWrapper() {
|
|
8202
|
+
if (!this.cliWrapper) {
|
|
8203
|
+
const config = {
|
|
8204
|
+
command: this.options.command,
|
|
8205
|
+
timeout: this.options.timeout
|
|
8206
|
+
};
|
|
8207
|
+
this.cliWrapper = new QwenCliWrapper(config);
|
|
8208
|
+
}
|
|
8209
|
+
return this.cliWrapper;
|
|
8210
|
+
}
|
|
8211
|
+
/**
|
|
8212
|
+
* Check if a circuit breaker should allow requests
|
|
8213
|
+
*/
|
|
8214
|
+
isCircuitClosed(breaker) {
|
|
8215
|
+
if (!breaker.isOpen) {
|
|
8216
|
+
return true;
|
|
8217
|
+
}
|
|
8218
|
+
if (Date.now() - breaker.lastFailure > this.RECOVERY_TIMEOUT_MS) {
|
|
8219
|
+
breaker.isOpen = false;
|
|
8220
|
+
breaker.failures = 0;
|
|
8221
|
+
return true;
|
|
8222
|
+
}
|
|
8223
|
+
return false;
|
|
8224
|
+
}
|
|
8225
|
+
/**
|
|
8226
|
+
* Record a failure on a circuit breaker
|
|
8227
|
+
*/
|
|
8228
|
+
recordFailure(breaker) {
|
|
8229
|
+
breaker.failures++;
|
|
8230
|
+
breaker.lastFailure = Date.now();
|
|
8231
|
+
if (breaker.failures >= this.FAILURE_THRESHOLD) {
|
|
8232
|
+
breaker.isOpen = true;
|
|
8233
|
+
logger.warn("[Qwen Hybrid] Circuit breaker opened", {
|
|
8234
|
+
failures: breaker.failures
|
|
8235
|
+
});
|
|
8236
|
+
}
|
|
8237
|
+
}
|
|
8238
|
+
/**
|
|
8239
|
+
* Record a success on a circuit breaker
|
|
8240
|
+
*/
|
|
8241
|
+
recordSuccess(breaker) {
|
|
8242
|
+
breaker.failures = 0;
|
|
8243
|
+
breaker.isOpen = false;
|
|
8244
|
+
}
|
|
8245
|
+
/**
|
|
8246
|
+
* Execute a request using the appropriate mode
|
|
8247
|
+
*/
|
|
8248
|
+
async execute(request) {
|
|
8249
|
+
const mode = this.options.mode;
|
|
8250
|
+
if (mode === "sdk") {
|
|
8251
|
+
return this.executeWithSdk(request);
|
|
8252
|
+
}
|
|
8253
|
+
if (mode === "cli") {
|
|
8254
|
+
return this.executeWithCli(request);
|
|
8255
|
+
}
|
|
8256
|
+
return this.executeWithAuto(request);
|
|
8257
|
+
}
|
|
8258
|
+
/**
|
|
8259
|
+
* Execute with SDK only
|
|
8260
|
+
*/
|
|
8261
|
+
async executeWithSdk(request) {
|
|
8262
|
+
const adapter = this.getSdkAdapter();
|
|
8263
|
+
if (!await adapter.isAvailable()) {
|
|
8264
|
+
throw new Error("Qwen SDK is not available. Check DASHSCOPE_API_KEY or QWEN_API_KEY.");
|
|
8265
|
+
}
|
|
8266
|
+
this.activeMode = "sdk";
|
|
8267
|
+
return adapter.execute(request);
|
|
8268
|
+
}
|
|
8269
|
+
/**
|
|
8270
|
+
* Execute with CLI only
|
|
8271
|
+
*/
|
|
8272
|
+
async executeWithCli(request) {
|
|
8273
|
+
const wrapper = this.getCliWrapper();
|
|
8274
|
+
if (!await wrapper.isAvailable()) {
|
|
8275
|
+
throw new Error("Qwen CLI is not available. Run: npm install -g @qwen-code/qwen-code@latest");
|
|
8276
|
+
}
|
|
8277
|
+
this.activeMode = "cli";
|
|
8278
|
+
return wrapper.execute(request);
|
|
8279
|
+
}
|
|
8280
|
+
/**
|
|
8281
|
+
* Execute with automatic mode selection
|
|
8282
|
+
*/
|
|
8283
|
+
async executeWithAuto(request) {
|
|
8284
|
+
if (this.isCircuitClosed(this.sdkCircuitBreaker)) {
|
|
8285
|
+
const adapter = this.getSdkAdapter();
|
|
8286
|
+
try {
|
|
8287
|
+
if (await adapter.isAvailable()) {
|
|
8288
|
+
logger.debug("[Qwen Hybrid] Using SDK mode");
|
|
8289
|
+
this.activeMode = "sdk";
|
|
8290
|
+
const response = await adapter.execute(request);
|
|
8291
|
+
this.recordSuccess(this.sdkCircuitBreaker);
|
|
8292
|
+
return response;
|
|
8293
|
+
}
|
|
8294
|
+
} catch (error) {
|
|
8295
|
+
logger.warn("[Qwen Hybrid] SDK execution failed, trying CLI", {
|
|
8296
|
+
error: error instanceof Error ? error.message : String(error)
|
|
8297
|
+
});
|
|
8298
|
+
this.recordFailure(this.sdkCircuitBreaker);
|
|
8299
|
+
}
|
|
8300
|
+
}
|
|
8301
|
+
if (this.isCircuitClosed(this.cliCircuitBreaker)) {
|
|
8302
|
+
const wrapper = this.getCliWrapper();
|
|
8303
|
+
try {
|
|
8304
|
+
if (await wrapper.isAvailable()) {
|
|
8305
|
+
logger.debug("[Qwen Hybrid] Using CLI mode (fallback)");
|
|
8306
|
+
this.activeMode = "cli";
|
|
8307
|
+
const response = await wrapper.execute(request);
|
|
8308
|
+
this.recordSuccess(this.cliCircuitBreaker);
|
|
8309
|
+
return response;
|
|
8310
|
+
}
|
|
8311
|
+
} catch (error) {
|
|
8312
|
+
logger.error("[Qwen Hybrid] CLI execution also failed", {
|
|
8313
|
+
error: error instanceof Error ? error.message : String(error)
|
|
8314
|
+
});
|
|
8315
|
+
this.recordFailure(this.cliCircuitBreaker);
|
|
8316
|
+
throw error;
|
|
8317
|
+
}
|
|
8318
|
+
}
|
|
8319
|
+
throw new Error(
|
|
8320
|
+
"Qwen execution failed: Both SDK and CLI modes are unavailable or have failed too many times. Ensure DASHSCOPE_API_KEY is set or Qwen CLI is installed."
|
|
8321
|
+
);
|
|
8322
|
+
}
|
|
8323
|
+
/**
|
|
8324
|
+
* Get the currently active execution mode
|
|
8325
|
+
*/
|
|
8326
|
+
getActiveMode() {
|
|
8327
|
+
return this.activeMode;
|
|
8328
|
+
}
|
|
8329
|
+
/**
|
|
8330
|
+
* Reset circuit breakers
|
|
8331
|
+
*/
|
|
8332
|
+
resetCircuitBreakers() {
|
|
8333
|
+
this.sdkCircuitBreaker = { failures: 0, lastFailure: 0, isOpen: false };
|
|
8334
|
+
this.cliCircuitBreaker = { failures: 0, lastFailure: 0, isOpen: false };
|
|
8335
|
+
logger.debug("[Qwen Hybrid] Circuit breakers reset");
|
|
8336
|
+
}
|
|
8337
|
+
/**
|
|
8338
|
+
* Clean up resources
|
|
8339
|
+
*/
|
|
8340
|
+
async destroy() {
|
|
8341
|
+
if (this.sdkAdapter) {
|
|
8342
|
+
await this.sdkAdapter.destroy();
|
|
8343
|
+
this.sdkAdapter = null;
|
|
8344
|
+
}
|
|
8345
|
+
this.cliWrapper = null;
|
|
8346
|
+
this.activeMode = null;
|
|
8347
|
+
logger.debug("[Qwen Hybrid] Adapter destroyed");
|
|
8348
|
+
}
|
|
8349
|
+
};
|
|
8350
|
+
}
|
|
8351
|
+
});
|
|
8352
|
+
|
|
8353
|
+
// src/integrations/qwen-code/index.ts
|
|
8354
|
+
var init_qwen_code = __esm({
|
|
8355
|
+
"src/integrations/qwen-code/index.ts"() {
|
|
8356
|
+
init_esm_shims();
|
|
8357
|
+
init_hybrid_adapter4();
|
|
8358
|
+
init_sdk_adapter4();
|
|
8359
|
+
init_cli_wrapper4();
|
|
8360
|
+
init_types4();
|
|
8361
|
+
}
|
|
8362
|
+
});
|
|
8363
|
+
|
|
8364
|
+
// src/providers/qwen-provider.ts
|
|
8365
|
+
var qwen_provider_exports = {};
|
|
8366
|
+
__export(qwen_provider_exports, {
|
|
8367
|
+
QwenProvider: () => QwenProvider,
|
|
8368
|
+
default: () => QwenProvider
|
|
8369
|
+
});
|
|
8370
|
+
var SUPPORTED_MODELS, QwenProvider;
|
|
8371
|
+
var init_qwen_provider = __esm({
|
|
8372
|
+
"src/providers/qwen-provider.ts"() {
|
|
8373
|
+
init_esm_shims();
|
|
8374
|
+
init_base_provider();
|
|
8375
|
+
init_logger();
|
|
8376
|
+
init_qwen_code();
|
|
8377
|
+
SUPPORTED_MODELS = [
|
|
8378
|
+
"qwen3-coder-480b-a35b-instruct",
|
|
8379
|
+
"qwen3-coder-30b-a3b-instruct",
|
|
8380
|
+
"qwen2.5-coder-32b-instruct",
|
|
8381
|
+
"qwen-max",
|
|
8382
|
+
"qwen-plus",
|
|
8383
|
+
"qwen-turbo"
|
|
8384
|
+
];
|
|
8385
|
+
QwenProvider = class extends BaseProvider {
|
|
8386
|
+
/** Selected model */
|
|
8387
|
+
model;
|
|
8388
|
+
/** SDK adapter for direct execution */
|
|
8389
|
+
sdkAdapter = null;
|
|
8390
|
+
/** Hybrid adapter for 'auto' mode */
|
|
8391
|
+
hybridAdapter = null;
|
|
8392
|
+
/** Provider configuration */
|
|
8393
|
+
qwenConfig;
|
|
8394
|
+
/** Supported models */
|
|
8395
|
+
static SUPPORTED_MODELS = SUPPORTED_MODELS;
|
|
8396
|
+
constructor(config) {
|
|
8397
|
+
super({
|
|
8398
|
+
...config,
|
|
8399
|
+
command: "qwen"
|
|
8400
|
+
});
|
|
8401
|
+
this.qwenConfig = config;
|
|
8402
|
+
const requestedModel = config.model || QWEN_DEFAULT_MODEL;
|
|
8403
|
+
if (!SUPPORTED_MODELS.includes(requestedModel)) {
|
|
8404
|
+
logger.warn(`[Qwen] Unknown model: ${requestedModel}. Using ${QWEN_DEFAULT_MODEL}.`);
|
|
8405
|
+
this.model = QWEN_DEFAULT_MODEL;
|
|
8406
|
+
} else {
|
|
8407
|
+
this.model = requestedModel;
|
|
8408
|
+
}
|
|
8409
|
+
logger.debug("[Qwen Provider] Initialized", {
|
|
8410
|
+
model: this.model,
|
|
8411
|
+
mode: config.mode || "sdk"
|
|
8412
|
+
});
|
|
8413
|
+
}
|
|
8414
|
+
/**
|
|
8415
|
+
* Get the normalized model name
|
|
8416
|
+
*/
|
|
8417
|
+
getNormalizedModel() {
|
|
8418
|
+
return normalizeQwenModel(this.model);
|
|
8419
|
+
}
|
|
8420
|
+
/**
|
|
8421
|
+
* Get or create SDK adapter
|
|
8422
|
+
*/
|
|
8423
|
+
getSdkAdapter() {
|
|
8424
|
+
if (!this.sdkAdapter) {
|
|
8425
|
+
this.sdkAdapter = new QwenSdkAdapter({
|
|
8426
|
+
model: this.model,
|
|
8427
|
+
apiKey: this.qwenConfig.apiKey,
|
|
8428
|
+
baseUrl: this.qwenConfig.baseUrl,
|
|
8429
|
+
timeout: this.qwenConfig.timeout
|
|
8430
|
+
});
|
|
8431
|
+
}
|
|
8432
|
+
return this.sdkAdapter;
|
|
8433
|
+
}
|
|
8434
|
+
/**
|
|
8435
|
+
* Get or create hybrid adapter
|
|
8436
|
+
*/
|
|
8437
|
+
getHybridAdapter() {
|
|
8438
|
+
if (!this.hybridAdapter) {
|
|
8439
|
+
const options = {
|
|
8440
|
+
mode: this.qwenConfig.mode || "auto",
|
|
8441
|
+
model: this.model,
|
|
8442
|
+
apiKey: this.qwenConfig.apiKey,
|
|
8443
|
+
baseUrl: this.qwenConfig.baseUrl,
|
|
8444
|
+
command: "qwen",
|
|
8445
|
+
timeout: this.qwenConfig.timeout
|
|
8446
|
+
};
|
|
8447
|
+
this.hybridAdapter = new QwenHybridAdapter(options);
|
|
8448
|
+
}
|
|
8449
|
+
return this.hybridAdapter;
|
|
8450
|
+
}
|
|
8451
|
+
/**
|
|
8452
|
+
* Execute a task using Qwen
|
|
8453
|
+
*
|
|
8454
|
+
* Execution flow:
|
|
8455
|
+
* 1. Mock mode → return mock response
|
|
8456
|
+
* 2. mode='sdk' (default) → use SDK adapter
|
|
8457
|
+
* 3. mode='auto' → use hybrid adapter (SDK with CLI fallback)
|
|
8458
|
+
* 4. mode='cli' → use CLI via BaseProvider
|
|
8459
|
+
*/
|
|
8460
|
+
async execute(request) {
|
|
8461
|
+
if (process.env.AX_MOCK_PROVIDERS === "true") {
|
|
8462
|
+
return this.createMockResponse(request.prompt);
|
|
8463
|
+
}
|
|
8464
|
+
const effectiveMode = this.qwenConfig.mode || "sdk";
|
|
8465
|
+
if (effectiveMode === "cli") {
|
|
8466
|
+
logger.debug("[Qwen Provider] Executing via CLI", {
|
|
8467
|
+
model: this.model
|
|
8468
|
+
});
|
|
8469
|
+
return super.execute(request);
|
|
8470
|
+
}
|
|
8471
|
+
if (effectiveMode === "auto") {
|
|
8472
|
+
logger.debug("[Qwen Provider] Executing via hybrid adapter", {
|
|
8473
|
+
promptLength: request.prompt.length,
|
|
8474
|
+
model: this.model
|
|
8475
|
+
});
|
|
8476
|
+
const adapter2 = this.getHybridAdapter();
|
|
8477
|
+
return adapter2.execute(request);
|
|
8478
|
+
}
|
|
8479
|
+
logger.debug("[Qwen Provider] Executing via SDK adapter", {
|
|
8480
|
+
promptLength: request.prompt.length,
|
|
8481
|
+
model: this.model
|
|
8482
|
+
});
|
|
8483
|
+
const adapter = this.getSdkAdapter();
|
|
8484
|
+
if (!await adapter.isAvailable()) {
|
|
8485
|
+
throw new Error(
|
|
8486
|
+
'Qwen SDK is not available. Set DASHSCOPE_API_KEY or QWEN_API_KEY environment variable, or use mode: "cli" to use Qwen Code CLI.'
|
|
8487
|
+
);
|
|
8488
|
+
}
|
|
8489
|
+
return adapter.execute(request);
|
|
8490
|
+
}
|
|
8491
|
+
/**
|
|
8492
|
+
* Get CLI command
|
|
8493
|
+
*/
|
|
8494
|
+
getCLICommand() {
|
|
8495
|
+
const activeMode = this.hybridAdapter?.getActiveMode();
|
|
8496
|
+
if (activeMode === "sdk") {
|
|
8497
|
+
return "qwen-sdk";
|
|
8498
|
+
}
|
|
8499
|
+
return "qwen";
|
|
8500
|
+
}
|
|
8501
|
+
/**
|
|
8502
|
+
* Get CLI arguments for Qwen Code CLI
|
|
8503
|
+
*
|
|
8504
|
+
* Note: Qwen Code CLI is interactive by default.
|
|
8505
|
+
* We pass the prompt via stdin instead of command-line args.
|
|
8506
|
+
*/
|
|
8507
|
+
getCLIArgs() {
|
|
8508
|
+
return [];
|
|
8509
|
+
}
|
|
8510
|
+
/**
|
|
8511
|
+
* Create mock response for testing
|
|
8512
|
+
*/
|
|
8513
|
+
createMockResponse(prompt) {
|
|
8514
|
+
return {
|
|
8515
|
+
content: this.getMockResponse(),
|
|
8516
|
+
model: this.getNormalizedModel(),
|
|
8517
|
+
tokensUsed: {
|
|
8518
|
+
prompt: this.estimateTokens(prompt),
|
|
8519
|
+
completion: 50,
|
|
8520
|
+
total: this.estimateTokens(prompt) + 50
|
|
8521
|
+
},
|
|
8522
|
+
latencyMs: 10,
|
|
8523
|
+
finishReason: "stop",
|
|
8524
|
+
cached: false
|
|
8525
|
+
};
|
|
8526
|
+
}
|
|
8527
|
+
/**
|
|
8528
|
+
* Estimate token count
|
|
8529
|
+
*/
|
|
8530
|
+
estimateTokens(text) {
|
|
8531
|
+
return Math.ceil(text.length / 4);
|
|
8532
|
+
}
|
|
8533
|
+
/**
|
|
8534
|
+
* Get mock response for testing
|
|
8535
|
+
*/
|
|
8536
|
+
getMockResponse() {
|
|
8537
|
+
return `[Mock Qwen Response]
|
|
8538
|
+
|
|
8539
|
+
This is a mock response from the Qwen provider (${this.getNormalizedModel()}).
|
|
8540
|
+
In production, this would be a response from ${this.qwenConfig.mode === "sdk" ? "Qwen SDK" : "Qwen Code CLI"}.
|
|
8541
|
+
|
|
8542
|
+
Model: ${this.getNormalizedModel()}
|
|
8543
|
+
Provider: Qwen (Alibaba Cloud)
|
|
8544
|
+
Mode: ${this.qwenConfig.mode || "sdk"}`;
|
|
8545
|
+
}
|
|
8546
|
+
/**
|
|
8547
|
+
* Get provider capabilities
|
|
8548
|
+
*/
|
|
8549
|
+
get capabilities() {
|
|
8550
|
+
const model = this.getNormalizedModel();
|
|
8551
|
+
const hasVision = isVisionModel(model);
|
|
8552
|
+
const maxContextTokens = getModelContextWindow(model);
|
|
8553
|
+
const activeMode = this.hybridAdapter?.getActiveMode();
|
|
8554
|
+
const integrationMode = activeMode === "sdk" ? "sdk" : "cli";
|
|
8555
|
+
return {
|
|
8556
|
+
...super.capabilities,
|
|
8557
|
+
supportsStreaming: true,
|
|
8558
|
+
supportsVision: hasVision,
|
|
8559
|
+
maxContextTokens,
|
|
8560
|
+
supportedModels: SUPPORTED_MODELS,
|
|
8561
|
+
integrationMode
|
|
8562
|
+
};
|
|
8563
|
+
}
|
|
8564
|
+
/**
|
|
8565
|
+
* Get the active execution mode
|
|
8566
|
+
*/
|
|
8567
|
+
getActiveMode() {
|
|
8568
|
+
return this.hybridAdapter?.getActiveMode() || null;
|
|
8569
|
+
}
|
|
8570
|
+
/**
|
|
8571
|
+
* Reset circuit breakers
|
|
8572
|
+
*/
|
|
8573
|
+
resetCircuitBreakers() {
|
|
8574
|
+
this.hybridAdapter?.resetCircuitBreakers();
|
|
8575
|
+
}
|
|
8576
|
+
/**
|
|
8577
|
+
* Clean up resources
|
|
8578
|
+
*/
|
|
8579
|
+
async destroy() {
|
|
8580
|
+
if (this.sdkAdapter) {
|
|
8581
|
+
await this.sdkAdapter.destroy();
|
|
8582
|
+
this.sdkAdapter = null;
|
|
8583
|
+
}
|
|
8584
|
+
if (this.hybridAdapter) {
|
|
8585
|
+
await this.hybridAdapter.destroy();
|
|
8586
|
+
this.hybridAdapter = null;
|
|
8587
|
+
}
|
|
8588
|
+
}
|
|
8589
|
+
/**
|
|
8590
|
+
* Get the list of supported models
|
|
8591
|
+
*/
|
|
8592
|
+
static getSupportedModels() {
|
|
8593
|
+
return [...SUPPORTED_MODELS];
|
|
8594
|
+
}
|
|
8595
|
+
};
|
|
8596
|
+
}
|
|
8597
|
+
});
|
|
8598
|
+
|
|
7681
8599
|
// src/mcp/index.ts
|
|
7682
8600
|
init_esm_shims();
|
|
7683
8601
|
|
|
@@ -8130,12 +9048,16 @@ init_logger();
|
|
|
8130
9048
|
|
|
8131
9049
|
// src/shared/helpers/deep-merge.ts
|
|
8132
9050
|
init_esm_shims();
|
|
9051
|
+
function isPlainObject(value) {
|
|
9052
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && Object.prototype.toString.call(value) === "[object Object]";
|
|
9053
|
+
}
|
|
8133
9054
|
function deepMerge(defaults, user) {
|
|
8134
9055
|
if (user === null || user === void 0) {
|
|
8135
|
-
return defaults;
|
|
9056
|
+
return { ...defaults };
|
|
8136
9057
|
}
|
|
8137
|
-
const result = {
|
|
8138
|
-
|
|
9058
|
+
const result = Object.assign({}, defaults);
|
|
9059
|
+
const userKeys = Object.keys(user);
|
|
9060
|
+
for (const key of userKeys) {
|
|
8139
9061
|
const userValue = user[key];
|
|
8140
9062
|
if (userValue === null) {
|
|
8141
9063
|
result[key] = void 0;
|
|
@@ -8145,8 +9067,11 @@ function deepMerge(defaults, user) {
|
|
|
8145
9067
|
continue;
|
|
8146
9068
|
}
|
|
8147
9069
|
const defaultValue = defaults[key];
|
|
8148
|
-
if (
|
|
8149
|
-
result[key] = deepMerge(
|
|
9070
|
+
if (isPlainObject(userValue) && isPlainObject(defaultValue)) {
|
|
9071
|
+
result[key] = deepMerge(
|
|
9072
|
+
defaultValue,
|
|
9073
|
+
userValue
|
|
9074
|
+
);
|
|
8150
9075
|
} else {
|
|
8151
9076
|
result[key] = userValue;
|
|
8152
9077
|
}
|
|
@@ -8788,7 +9713,7 @@ var PRECOMPILED_CONFIG = {
|
|
|
8788
9713
|
"enableFreeTierPrioritization": true,
|
|
8789
9714
|
"enableWorkloadAwareRouting": true
|
|
8790
9715
|
},
|
|
8791
|
-
"version": "12.6.
|
|
9716
|
+
"version": "12.6.3"
|
|
8792
9717
|
};
|
|
8793
9718
|
|
|
8794
9719
|
// src/core/config/schemas.ts
|
|
@@ -13211,7 +14136,7 @@ var MemoryManager = class _MemoryManager {
|
|
|
13211
14136
|
if (this.db) {
|
|
13212
14137
|
try {
|
|
13213
14138
|
this.db.close();
|
|
13214
|
-
} catch
|
|
14139
|
+
} catch {
|
|
13215
14140
|
}
|
|
13216
14141
|
this.initialized = false;
|
|
13217
14142
|
this.entryCount = 0;
|
|
@@ -14052,9 +14977,9 @@ var MemoryManager = class _MemoryManager {
|
|
|
14052
14977
|
}
|
|
14053
14978
|
}
|
|
14054
14979
|
const {
|
|
14055
|
-
includeEmbeddings = false,
|
|
14980
|
+
includeEmbeddings: _includeEmbeddings = false,
|
|
14056
14981
|
filters = {},
|
|
14057
|
-
batchSize = 1e3,
|
|
14982
|
+
batchSize: _batchSize = 1e3,
|
|
14058
14983
|
pretty = false
|
|
14059
14984
|
} = options || {};
|
|
14060
14985
|
try {
|
|
@@ -15211,7 +16136,7 @@ var SessionManager = class _SessionManager {
|
|
|
15211
16136
|
if (this.pendingSave) {
|
|
15212
16137
|
try {
|
|
15213
16138
|
await this.pendingSave;
|
|
15214
|
-
} catch
|
|
16139
|
+
} catch {
|
|
15215
16140
|
}
|
|
15216
16141
|
}
|
|
15217
16142
|
this.pendingSave = this.doSave().finally(() => {
|
|
@@ -15279,7 +16204,7 @@ var SessionManager = class _SessionManager {
|
|
|
15279
16204
|
}
|
|
15280
16205
|
try {
|
|
15281
16206
|
await unlink(tempPath);
|
|
15282
|
-
} catch
|
|
16207
|
+
} catch {
|
|
15283
16208
|
}
|
|
15284
16209
|
throw renameError;
|
|
15285
16210
|
}
|
|
@@ -15287,7 +16212,7 @@ var SessionManager = class _SessionManager {
|
|
|
15287
16212
|
const tempPath = `${this.persistencePath}.tmp`;
|
|
15288
16213
|
try {
|
|
15289
16214
|
await unlink(tempPath);
|
|
15290
|
-
} catch
|
|
16215
|
+
} catch {
|
|
15291
16216
|
}
|
|
15292
16217
|
logger.error("Failed to save sessions to persistence", {
|
|
15293
16218
|
path: normalizePath(this.persistencePath),
|
|
@@ -15825,6 +16750,7 @@ init_esm_shims();
|
|
|
15825
16750
|
init_logger();
|
|
15826
16751
|
var MAX_CONTEXT_SIZE = 100 * 1024;
|
|
15827
16752
|
var DEFAULT_CACHE_TTL = 3e5;
|
|
16753
|
+
var STALE_THRESHOLD_MS = 24 * 60 * 60 * 1e3;
|
|
15828
16754
|
var ProjectContextLoader = class {
|
|
15829
16755
|
constructor(projectRoot, options) {
|
|
15830
16756
|
this.projectRoot = projectRoot;
|
|
@@ -15848,76 +16774,79 @@ var ProjectContextLoader = class {
|
|
|
15848
16774
|
});
|
|
15849
16775
|
const context = {};
|
|
15850
16776
|
try {
|
|
15851
|
-
const
|
|
15852
|
-
const resolvedPath = await realpath(
|
|
16777
|
+
const indexPath = path4__default.join(this.projectRoot, "ax.index.json");
|
|
16778
|
+
const resolvedPath = await realpath(indexPath).catch(() => null);
|
|
15853
16779
|
if (resolvedPath) {
|
|
15854
16780
|
const rel = path4__default.relative(this.projectRoot, resolvedPath);
|
|
15855
16781
|
if (!rel.startsWith("..") && !path4__default.isAbsolute(rel)) {
|
|
15856
16782
|
const stats = await stat(resolvedPath);
|
|
15857
16783
|
if (stats.size > MAX_CONTEXT_SIZE) {
|
|
15858
|
-
logger.warn("
|
|
16784
|
+
logger.warn("ax.index.json too large, ignoring", {
|
|
15859
16785
|
size: stats.size,
|
|
15860
16786
|
limit: MAX_CONTEXT_SIZE
|
|
15861
16787
|
});
|
|
15862
16788
|
} else {
|
|
15863
|
-
|
|
15864
|
-
|
|
15865
|
-
|
|
15866
|
-
|
|
16789
|
+
const indexContent = await readFile(resolvedPath, "utf-8");
|
|
16790
|
+
context.index = JSON.parse(indexContent);
|
|
16791
|
+
context.lastUpdated = stats.mtime;
|
|
16792
|
+
const age = Date.now() - stats.mtime.getTime();
|
|
16793
|
+
context.isStale = age > STALE_THRESHOLD_MS;
|
|
16794
|
+
logger.info("Loaded ax.index.json", {
|
|
16795
|
+
projectName: context.index.projectName,
|
|
16796
|
+
projectType: context.index.projectType,
|
|
16797
|
+
isStale: context.isStale,
|
|
16798
|
+
ageHours: Math.floor(age / (1e3 * 60 * 60))
|
|
15867
16799
|
});
|
|
15868
|
-
|
|
15869
|
-
|
|
15870
|
-
|
|
15871
|
-
|
|
16800
|
+
if (context.index.commands) {
|
|
16801
|
+
context.commands = {};
|
|
16802
|
+
for (const [name, cmd] of Object.entries(context.index.commands)) {
|
|
16803
|
+
context.commands[name] = cmd.script;
|
|
16804
|
+
}
|
|
16805
|
+
}
|
|
15872
16806
|
}
|
|
15873
16807
|
}
|
|
15874
16808
|
}
|
|
15875
16809
|
} catch (error) {
|
|
15876
16810
|
if (error && typeof error === "object" && "code" in error && error.code !== "ENOENT") {
|
|
15877
|
-
logger.warn("Error loading
|
|
16811
|
+
logger.warn("Error loading ax.index.json", { error });
|
|
15878
16812
|
}
|
|
15879
16813
|
}
|
|
15880
16814
|
try {
|
|
15881
|
-
const
|
|
15882
|
-
const resolvedPath = await realpath(
|
|
16815
|
+
const customMdPath = path4__default.join(this.projectRoot, ".automatosx", "CUSTOM.md");
|
|
16816
|
+
const resolvedPath = await realpath(customMdPath).catch(() => null);
|
|
15883
16817
|
if (resolvedPath) {
|
|
15884
16818
|
const rel = path4__default.relative(this.projectRoot, resolvedPath);
|
|
15885
16819
|
if (!rel.startsWith("..") && !path4__default.isAbsolute(rel)) {
|
|
15886
16820
|
const stats = await stat(resolvedPath);
|
|
15887
16821
|
if (stats.size > MAX_CONTEXT_SIZE) {
|
|
15888
|
-
logger.warn("
|
|
16822
|
+
logger.warn("CUSTOM.md too large, ignoring", {
|
|
15889
16823
|
size: stats.size,
|
|
15890
16824
|
limit: MAX_CONTEXT_SIZE
|
|
15891
16825
|
});
|
|
15892
16826
|
} else {
|
|
15893
|
-
|
|
15894
|
-
|
|
15895
|
-
|
|
15896
|
-
|
|
16827
|
+
context.customInstructions = await readFile(resolvedPath, "utf-8");
|
|
16828
|
+
logger.info("Loaded CUSTOM.md", {
|
|
16829
|
+
size: stats.size,
|
|
16830
|
+
lines: context.customInstructions.split("\n").length
|
|
15897
16831
|
});
|
|
15898
|
-
|
|
15899
|
-
context.commands = { ...context.commands, ...context.config.commands };
|
|
15900
|
-
}
|
|
15901
|
-
if (context.config.project) {
|
|
15902
|
-
context.metadata = { ...context.metadata, ...context.config.project };
|
|
15903
|
-
}
|
|
16832
|
+
context.guardrails = this.parseGuardrails(context.customInstructions);
|
|
15904
16833
|
}
|
|
15905
16834
|
}
|
|
15906
16835
|
}
|
|
15907
16836
|
} catch (error) {
|
|
15908
16837
|
if (error && typeof error === "object" && "code" in error && error.code !== "ENOENT") {
|
|
15909
|
-
logger.warn("Error loading
|
|
16838
|
+
logger.warn("Error loading CUSTOM.md", { error });
|
|
15910
16839
|
}
|
|
15911
16840
|
}
|
|
15912
16841
|
context.contextPrompt = this.buildContextPrompt(context);
|
|
15913
16842
|
this.cache = context;
|
|
15914
16843
|
this.cacheExpiry = Date.now() + this.cacheTTL;
|
|
15915
16844
|
logger.info("Project context loaded", {
|
|
15916
|
-
|
|
15917
|
-
|
|
15918
|
-
agentRules: context.agentRules?.length ?? 0,
|
|
16845
|
+
hasIndex: !!context.index,
|
|
16846
|
+
hasCustomInstructions: !!context.customInstructions,
|
|
15919
16847
|
guardrails: context.guardrails?.length ?? 0,
|
|
15920
|
-
commands: Object.keys(context.commands ?? {}).length
|
|
16848
|
+
commands: Object.keys(context.commands ?? {}).length,
|
|
16849
|
+
isStale: context.isStale
|
|
15921
16850
|
});
|
|
15922
16851
|
return context;
|
|
15923
16852
|
}
|
|
@@ -15933,8 +16862,8 @@ var ProjectContextLoader = class {
|
|
|
15933
16862
|
* Check if context exists (without loading)
|
|
15934
16863
|
*/
|
|
15935
16864
|
async exists() {
|
|
15936
|
-
const
|
|
15937
|
-
const
|
|
16865
|
+
const indexPath = path4__default.join(this.projectRoot, "ax.index.json");
|
|
16866
|
+
const customMdPath = path4__default.join(this.projectRoot, ".automatosx", "CUSTOM.md");
|
|
15938
16867
|
const checkExists = async (filePath) => {
|
|
15939
16868
|
try {
|
|
15940
16869
|
await access(filePath, constants.F_OK);
|
|
@@ -15943,57 +16872,48 @@ var ProjectContextLoader = class {
|
|
|
15943
16872
|
return false;
|
|
15944
16873
|
}
|
|
15945
16874
|
};
|
|
15946
|
-
const [
|
|
15947
|
-
checkExists(
|
|
15948
|
-
checkExists(
|
|
16875
|
+
const [indexExists, customMdExists] = await Promise.all([
|
|
16876
|
+
checkExists(indexPath),
|
|
16877
|
+
checkExists(customMdPath)
|
|
15949
16878
|
]);
|
|
15950
|
-
return
|
|
16879
|
+
return indexExists || customMdExists;
|
|
15951
16880
|
}
|
|
15952
16881
|
/**
|
|
15953
|
-
*
|
|
15954
|
-
|
|
15955
|
-
|
|
15956
|
-
|
|
15957
|
-
|
|
15958
|
-
|
|
15959
|
-
|
|
15960
|
-
|
|
15961
|
-
|
|
15962
|
-
|
|
15963
|
-
if (!match || !match[1]) {
|
|
15964
|
-
return rules;
|
|
15965
|
-
}
|
|
15966
|
-
const section = match[1];
|
|
15967
|
-
const lineRegex = /^[-*]\s+(.+?)\s+(?:→|->)+\s+(.+?)$/gm;
|
|
15968
|
-
let lineMatch;
|
|
15969
|
-
while ((lineMatch = lineRegex.exec(section)) !== null) {
|
|
15970
|
-
const taskType = lineMatch[1]?.trim() ?? "";
|
|
15971
|
-
const agentsText = lineMatch[2]?.trim() ?? "";
|
|
15972
|
-
const agents = agentsText.split(",").map((a) => a.trim()).filter(Boolean);
|
|
15973
|
-
const defaultAgent = agents[0];
|
|
15974
|
-
if (defaultAgent) {
|
|
15975
|
-
rules.push({
|
|
15976
|
-
taskType,
|
|
15977
|
-
patterns: [],
|
|
15978
|
-
// TODO: Parse file patterns if specified
|
|
15979
|
-
defaultAgent,
|
|
15980
|
-
autoReview: agents.length > 1
|
|
15981
|
-
});
|
|
15982
|
-
}
|
|
16882
|
+
* Check if ax.index.json is stale (older than 24 hours)
|
|
16883
|
+
*/
|
|
16884
|
+
async isStale() {
|
|
16885
|
+
try {
|
|
16886
|
+
const indexPath = path4__default.join(this.projectRoot, "ax.index.json");
|
|
16887
|
+
const stats = await stat(indexPath);
|
|
16888
|
+
const age = Date.now() - stats.mtime.getTime();
|
|
16889
|
+
return age > STALE_THRESHOLD_MS;
|
|
16890
|
+
} catch {
|
|
16891
|
+
return true;
|
|
15983
16892
|
}
|
|
15984
|
-
return rules;
|
|
15985
16893
|
}
|
|
15986
16894
|
/**
|
|
15987
|
-
* Parse guardrails
|
|
16895
|
+
* Parse guardrails from CUSTOM.md
|
|
15988
16896
|
*
|
|
15989
|
-
* Looks for
|
|
15990
|
-
* Extracts items marked with ⚠️ or under NEVER headings
|
|
16897
|
+
* Looks for DO/DON'T sections in ax-cli format
|
|
15991
16898
|
*/
|
|
15992
16899
|
parseGuardrails(markdown) {
|
|
15993
16900
|
const guardrails = [];
|
|
15994
|
-
const
|
|
16901
|
+
const dontRegex = /###\s+DON'T[^\n]*\n([\s\S]*?)(?=\n###|$)/i;
|
|
16902
|
+
const dontMatch = markdown.match(dontRegex);
|
|
16903
|
+
if (dontMatch && dontMatch[1]) {
|
|
16904
|
+
const section = dontMatch[1];
|
|
16905
|
+
const bulletRegex = /^[-*]\s+(.+?)$/gm;
|
|
16906
|
+
let bulletMatch;
|
|
16907
|
+
while ((bulletMatch = bulletRegex.exec(section)) !== null) {
|
|
16908
|
+
const rule = bulletMatch[1]?.trim();
|
|
16909
|
+
if (rule) {
|
|
16910
|
+
guardrails.push(rule);
|
|
16911
|
+
}
|
|
16912
|
+
}
|
|
16913
|
+
}
|
|
16914
|
+
const criticalRegex = /##\s+(Critical\s+Guardrails|Critical\s+Rules|Never)[^\n]*\n([\s\S]*?)(?=\n##|$)/gi;
|
|
15995
16915
|
let match;
|
|
15996
|
-
while ((match =
|
|
16916
|
+
while ((match = criticalRegex.exec(markdown)) !== null) {
|
|
15997
16917
|
const section = match[2];
|
|
15998
16918
|
if (!section) continue;
|
|
15999
16919
|
const bulletRegex = /^[-*]\s+(.+?)$/gm;
|
|
@@ -16003,106 +16923,58 @@ var ProjectContextLoader = class {
|
|
|
16003
16923
|
rule = rule.replace(/^[⚠️❌✅✓⚡🔒]+\s*/, "");
|
|
16004
16924
|
rule = rule.replace(/\*\*(.+?)\*\*/g, "$1");
|
|
16005
16925
|
rule = rule.replace(/`(.+?)`/g, "$1");
|
|
16006
|
-
if (rule.length > 0) {
|
|
16926
|
+
if (rule.length > 0 && !guardrails.includes(rule)) {
|
|
16007
16927
|
guardrails.push(rule);
|
|
16008
16928
|
}
|
|
16009
16929
|
}
|
|
16010
16930
|
}
|
|
16011
16931
|
return guardrails;
|
|
16012
16932
|
}
|
|
16013
|
-
/**
|
|
16014
|
-
* Parse commands from markdown code blocks
|
|
16015
|
-
*
|
|
16016
|
-
* Looks for "## Commands" or "## Canonical Commands" section
|
|
16017
|
-
*/
|
|
16018
|
-
parseCommands(markdown) {
|
|
16019
|
-
const commands = {};
|
|
16020
|
-
const sectionRegex = /##\s+(Canonical\s+)?Commands[^\n]*\n([\s\S]*?)(?=\n##|$)/i;
|
|
16021
|
-
const match = markdown.match(sectionRegex);
|
|
16022
|
-
if (!match) {
|
|
16023
|
-
return commands;
|
|
16024
|
-
}
|
|
16025
|
-
const section = match[2];
|
|
16026
|
-
if (!section) {
|
|
16027
|
-
return commands;
|
|
16028
|
-
}
|
|
16029
|
-
const codeBlockRegex = /```(?:bash|sh|shell)?\n([\s\S]*?)```/;
|
|
16030
|
-
const codeMatch = section.match(codeBlockRegex);
|
|
16031
|
-
if (codeMatch && codeMatch[1]) {
|
|
16032
|
-
const lines = codeMatch[1].split("\n");
|
|
16033
|
-
for (const line of lines) {
|
|
16034
|
-
const cmdMatch = line.match(/^([^\s#]+(?:\s+[^\s#]+)*)\s*#?\s*(.*)$/);
|
|
16035
|
-
if (cmdMatch && cmdMatch[1]) {
|
|
16036
|
-
const cmd = cmdMatch[1].trim();
|
|
16037
|
-
const desc = cmdMatch[2]?.trim() ?? "";
|
|
16038
|
-
const key = desc || cmd;
|
|
16039
|
-
commands[key] = cmd;
|
|
16040
|
-
}
|
|
16041
|
-
}
|
|
16042
|
-
}
|
|
16043
|
-
return commands;
|
|
16044
|
-
}
|
|
16045
|
-
/**
|
|
16046
|
-
* Parse project metadata from markdown frontmatter or first section
|
|
16047
|
-
*/
|
|
16048
|
-
parseMetadata(markdown) {
|
|
16049
|
-
const metadata = {};
|
|
16050
|
-
const lastUpdatedMatch = markdown.match(/>\s*Last\s+updated:\s*(.+?)$/im);
|
|
16051
|
-
if (lastUpdatedMatch && lastUpdatedMatch[1]) {
|
|
16052
|
-
metadata.lastUpdated = lastUpdatedMatch[1].trim();
|
|
16053
|
-
}
|
|
16054
|
-
const projectMatch = markdown.match(/>\s*Project:\s*(.+?)$/im);
|
|
16055
|
-
if (projectMatch && projectMatch[1]) {
|
|
16056
|
-
const parts = projectMatch[1].trim().split(/\s+v/);
|
|
16057
|
-
metadata.name = parts[0] || "";
|
|
16058
|
-
if (parts.length > 1 && parts[1]) {
|
|
16059
|
-
metadata.version = parts[1];
|
|
16060
|
-
}
|
|
16061
|
-
}
|
|
16062
|
-
const h1Match = markdown.match(/^#\s+(.+?)$/m);
|
|
16063
|
-
if (h1Match && h1Match[1] && !metadata.name) {
|
|
16064
|
-
metadata.name = h1Match[1].replace(/Project Context for AutomatosX/i, "").trim();
|
|
16065
|
-
}
|
|
16066
|
-
return metadata;
|
|
16067
|
-
}
|
|
16068
16933
|
/**
|
|
16069
16934
|
* Build formatted context prompt for agent injection
|
|
16070
16935
|
*/
|
|
16071
16936
|
buildContextPrompt(context) {
|
|
16072
|
-
if (!context.
|
|
16937
|
+
if (!context.index && !context.customInstructions) {
|
|
16073
16938
|
return "";
|
|
16074
16939
|
}
|
|
16075
16940
|
let prompt = "\n# PROJECT CONTEXT\n\n";
|
|
16076
|
-
if (context.
|
|
16077
|
-
prompt +=
|
|
16078
|
-
context.guardrails.forEach((rule) => {
|
|
16079
|
-
prompt += `- ${rule}
|
|
16941
|
+
if (context.index) {
|
|
16942
|
+
prompt += `**Project:** ${context.index.projectName} v${context.index.version}
|
|
16080
16943
|
`;
|
|
16081
|
-
}
|
|
16082
|
-
prompt += "\n";
|
|
16083
|
-
}
|
|
16084
|
-
if (context.agentRules && context.agentRules.length > 0) {
|
|
16085
|
-
prompt += "## Agent Delegation Rules:\n\n";
|
|
16086
|
-
context.agentRules.forEach((rule) => {
|
|
16087
|
-
prompt += `- ${rule.taskType} \u2192 ${rule.defaultAgent}
|
|
16944
|
+
prompt += `**Type:** ${context.index.projectType}
|
|
16088
16945
|
`;
|
|
16089
|
-
}
|
|
16090
|
-
|
|
16946
|
+
prompt += `**Language:** ${context.index.language}`;
|
|
16947
|
+
if (context.index.framework) {
|
|
16948
|
+
prompt += ` + ${context.index.framework}`;
|
|
16949
|
+
}
|
|
16950
|
+
prompt += "\n\n";
|
|
16951
|
+
if (context.index.modules.length > 0) {
|
|
16952
|
+
prompt += "## Project Structure:\n\n";
|
|
16953
|
+
for (const mod of context.index.modules.slice(0, 10)) {
|
|
16954
|
+
prompt += `- \`${mod.path}/\` - ${mod.purpose}
|
|
16955
|
+
`;
|
|
16956
|
+
}
|
|
16957
|
+
prompt += "\n";
|
|
16958
|
+
}
|
|
16959
|
+
if (context.commands && Object.keys(context.commands).length > 0) {
|
|
16960
|
+
prompt += "## Available Commands:\n\n";
|
|
16961
|
+
for (const [name, script] of Object.entries(context.commands).slice(0, 10)) {
|
|
16962
|
+
prompt += `- ${name}: \`${script}\`
|
|
16963
|
+
`;
|
|
16964
|
+
}
|
|
16965
|
+
prompt += "\n";
|
|
16966
|
+
}
|
|
16091
16967
|
}
|
|
16092
|
-
if (context.
|
|
16093
|
-
prompt += "##
|
|
16094
|
-
|
|
16095
|
-
prompt += `- ${
|
|
16968
|
+
if (context.guardrails && context.guardrails.length > 0) {
|
|
16969
|
+
prompt += "## CRITICAL RULES (NEVER VIOLATE):\n\n";
|
|
16970
|
+
for (const rule of context.guardrails) {
|
|
16971
|
+
prompt += `- ${rule}
|
|
16096
16972
|
`;
|
|
16097
|
-
}
|
|
16973
|
+
}
|
|
16098
16974
|
prompt += "\n";
|
|
16099
16975
|
}
|
|
16100
|
-
if (context.
|
|
16101
|
-
|
|
16102
|
-
const contextToAdd = context.markdown.length > MAX_PROMPT_LENGTH ? context.markdown.substring(0, MAX_PROMPT_LENGTH) + "\n\n[... context truncated for length ...]" : context.markdown;
|
|
16103
|
-
prompt += "## Full Project Context:\n\n";
|
|
16104
|
-
prompt += contextToAdd;
|
|
16105
|
-
prompt += "\n";
|
|
16976
|
+
if (context.isStale) {
|
|
16977
|
+
prompt += "\u26A0\uFE0F **Note:** Project index is stale (>24h old). Run `ax init` to update.\n\n";
|
|
16106
16978
|
}
|
|
16107
16979
|
return prompt;
|
|
16108
16980
|
}
|
|
@@ -16526,10 +17398,10 @@ var ContextManager = class {
|
|
|
16526
17398
|
return provider;
|
|
16527
17399
|
}
|
|
16528
17400
|
/**
|
|
16529
|
-
* Load project context from
|
|
17401
|
+
* Load project context from ax.index.json (v12.9.0+)
|
|
16530
17402
|
*
|
|
16531
|
-
* Loads project-specific
|
|
16532
|
-
* Falls back gracefully if
|
|
17403
|
+
* Loads project-specific context from ax.index.json and CUSTOM.md.
|
|
17404
|
+
* Falls back gracefully if files don't exist.
|
|
16533
17405
|
*
|
|
16534
17406
|
* @param projectDir - Project root directory
|
|
16535
17407
|
* @returns Project context or null if not found
|
|
@@ -16542,15 +17414,15 @@ var ContextManager = class {
|
|
|
16542
17414
|
const loader = this.projectContextLoader;
|
|
16543
17415
|
const exists = await loader.exists();
|
|
16544
17416
|
if (!exists) {
|
|
16545
|
-
logger.debug("No
|
|
17417
|
+
logger.debug("No ax.index.json or CUSTOM.md found, skipping project context");
|
|
16546
17418
|
return null;
|
|
16547
17419
|
}
|
|
16548
17420
|
const context = await loader.load();
|
|
16549
|
-
logger.debug("Project context loaded
|
|
16550
|
-
|
|
16551
|
-
|
|
16552
|
-
|
|
16553
|
-
|
|
17421
|
+
logger.debug("Project context loaded", {
|
|
17422
|
+
hasIndex: !!context.index,
|
|
17423
|
+
hasCustomInstructions: !!context.customInstructions,
|
|
17424
|
+
guardrails: context.guardrails?.length ?? 0,
|
|
17425
|
+
isStale: context.isStale
|
|
16554
17426
|
});
|
|
16555
17427
|
return context;
|
|
16556
17428
|
} catch (error) {
|
|
@@ -20582,6 +21454,24 @@ ${context.task}`;
|
|
|
20582
21454
|
// src/agents/agent-selector.ts
|
|
20583
21455
|
init_esm_shims();
|
|
20584
21456
|
init_logger();
|
|
21457
|
+
var regexCache = /* @__PURE__ */ new Map();
|
|
21458
|
+
function getCachedRegex(pattern) {
|
|
21459
|
+
if (regexCache.has(pattern)) {
|
|
21460
|
+
return regexCache.get(pattern) ?? null;
|
|
21461
|
+
}
|
|
21462
|
+
try {
|
|
21463
|
+
const regex = new RegExp(pattern, "i");
|
|
21464
|
+
regexCache.set(pattern, regex);
|
|
21465
|
+
return regex;
|
|
21466
|
+
} catch (error) {
|
|
21467
|
+
logger.debug("Invalid regex pattern cached as null", {
|
|
21468
|
+
pattern,
|
|
21469
|
+
error: error instanceof Error ? error.message : String(error)
|
|
21470
|
+
});
|
|
21471
|
+
regexCache.set(pattern, null);
|
|
21472
|
+
return null;
|
|
21473
|
+
}
|
|
21474
|
+
}
|
|
20585
21475
|
function scoreAgent(task, profile) {
|
|
20586
21476
|
let score = 0;
|
|
20587
21477
|
const taskLower = task.toLowerCase();
|
|
@@ -20619,16 +21509,9 @@ function scoreAgent(task, profile) {
|
|
|
20619
21509
|
}
|
|
20620
21510
|
if (profile.selectionMetadata?.redirectWhen) {
|
|
20621
21511
|
for (const rule of profile.selectionMetadata.redirectWhen) {
|
|
20622
|
-
|
|
20623
|
-
|
|
20624
|
-
|
|
20625
|
-
score -= 15;
|
|
20626
|
-
}
|
|
20627
|
-
} catch (error) {
|
|
20628
|
-
logger.debug("Invalid regex pattern in redirectWhen rule", {
|
|
20629
|
-
pattern: rule.phrase,
|
|
20630
|
-
error: error instanceof Error ? error.message : String(error)
|
|
20631
|
-
});
|
|
21512
|
+
const regex = getCachedRegex(rule.phrase);
|
|
21513
|
+
if (regex && regex.test(task)) {
|
|
21514
|
+
score -= 15;
|
|
20632
21515
|
}
|
|
20633
21516
|
}
|
|
20634
21517
|
}
|
|
@@ -23218,8 +24101,8 @@ var BugDetector = class {
|
|
|
23218
24101
|
async loadRulesFromFile(filePath) {
|
|
23219
24102
|
try {
|
|
23220
24103
|
const content = await readFile(filePath, "utf-8");
|
|
23221
|
-
const
|
|
23222
|
-
const parsed =
|
|
24104
|
+
const yaml = await import('js-yaml');
|
|
24105
|
+
const parsed = yaml.load(content);
|
|
23223
24106
|
if (parsed.rules && Array.isArray(parsed.rules)) {
|
|
23224
24107
|
for (const rule of parsed.rules) {
|
|
23225
24108
|
this.addRule(rule);
|
|
@@ -23651,7 +24534,6 @@ var BugFixer = class {
|
|
|
23651
24534
|
}
|
|
23652
24535
|
const directSetIntervalPattern = /setInterval\s*\(/;
|
|
23653
24536
|
if (directSetIntervalPattern.test(line)) {
|
|
23654
|
-
line.match(/^(\s*)/)?.[1] || "";
|
|
23655
24537
|
const newLine = line.replace(
|
|
23656
24538
|
/(setInterval\s*\([^)]+\))/,
|
|
23657
24539
|
"const _interval = $1; if (_interval.unref) _interval.unref()"
|
|
@@ -23678,7 +24560,6 @@ var BugFixer = class {
|
|
|
23678
24560
|
const match = currentLine.match(classPattern);
|
|
23679
24561
|
if (match && match[1]) {
|
|
23680
24562
|
classStartLine = i;
|
|
23681
|
-
match[1];
|
|
23682
24563
|
break;
|
|
23683
24564
|
}
|
|
23684
24565
|
}
|
|
@@ -23730,7 +24611,7 @@ var BugFixer = class {
|
|
|
23730
24611
|
/**
|
|
23731
24612
|
* Apply use DisposableEventEmitter fix
|
|
23732
24613
|
*/
|
|
23733
|
-
applyUseDisposableEventEmitterFix(finding, originalContent,
|
|
24614
|
+
applyUseDisposableEventEmitterFix(finding, originalContent, _lines) {
|
|
23734
24615
|
let fixedContent = originalContent.replace(
|
|
23735
24616
|
/extends\s+EventEmitter\b/g,
|
|
23736
24617
|
"extends DisposableEventEmitter"
|
|
@@ -24652,7 +25533,7 @@ var DUPLICATION_RULES = [
|
|
|
24652
25533
|
suggestion: "Extract to a named constant"
|
|
24653
25534
|
}
|
|
24654
25535
|
];
|
|
24655
|
-
function detectDuplication(filePath, content, lines, ignoreState,
|
|
25536
|
+
function detectDuplication(filePath, content, lines, ignoreState, _config) {
|
|
24656
25537
|
const findings = [];
|
|
24657
25538
|
findings.push(...detectDuplicateBlocks(filePath, content, lines, ignoreState));
|
|
24658
25539
|
findings.push(...detectRepeatedConditionals(filePath, content, lines, ignoreState));
|
|
@@ -24679,7 +25560,7 @@ function detectDuplicateBlocks(filePath, content, lines, ignoreState) {
|
|
|
24679
25560
|
}
|
|
24680
25561
|
blockHashes.get(blockHash).push({ start: i + 1, end: i + MIN_BLOCK_SIZE });
|
|
24681
25562
|
}
|
|
24682
|
-
for (const [
|
|
25563
|
+
for (const [_hash, locations] of blockHashes) {
|
|
24683
25564
|
if (locations.length > 1) {
|
|
24684
25565
|
for (let i = 1; i < locations.length; i++) {
|
|
24685
25566
|
const loc = locations[i];
|
|
@@ -24728,7 +25609,7 @@ function detectRepeatedConditionals(filePath, content, lines, ignoreState) {
|
|
|
24728
25609
|
}
|
|
24729
25610
|
conditionPattern.lastIndex = 0;
|
|
24730
25611
|
}
|
|
24731
|
-
for (const [
|
|
25612
|
+
for (const [_condition, lineNums] of conditionalCounts) {
|
|
24732
25613
|
if (lineNums.length >= 2) {
|
|
24733
25614
|
const firstLine = lineNums[0];
|
|
24734
25615
|
const secondLine = lineNums[1];
|
|
@@ -24891,7 +25772,7 @@ var THRESHOLDS = {
|
|
|
24891
25772
|
maxCyclomaticComplexity: 10,
|
|
24892
25773
|
maxChainedCalls: 4
|
|
24893
25774
|
};
|
|
24894
|
-
function detectReadability(filePath, content, lines, ignoreState,
|
|
25775
|
+
function detectReadability(filePath, content, lines, ignoreState, _config) {
|
|
24895
25776
|
const findings = [];
|
|
24896
25777
|
findings.push(...detectLongFunctions(filePath, content, lines, ignoreState));
|
|
24897
25778
|
findings.push(...detectDeepNesting(filePath, content, lines, ignoreState));
|
|
@@ -25267,7 +26148,7 @@ var PERFORMANCE_RULES = [
|
|
|
25267
26148
|
suggestion: "Remove await - async functions automatically wrap return values in promises"
|
|
25268
26149
|
}
|
|
25269
26150
|
];
|
|
25270
|
-
function detectPerformance(filePath, content, lines, ignoreState,
|
|
26151
|
+
function detectPerformance(filePath, content, lines, ignoreState, _config) {
|
|
25271
26152
|
const findings = [];
|
|
25272
26153
|
findings.push(...detectSyncInAsync(filePath, content, lines, ignoreState));
|
|
25273
26154
|
findings.push(...detectNPlusOne(filePath, content, lines, ignoreState));
|
|
@@ -25682,7 +26563,7 @@ var HARDCODE_RULES = [
|
|
|
25682
26563
|
suggestion: "Extract timeout to a named constant or config"
|
|
25683
26564
|
}
|
|
25684
26565
|
];
|
|
25685
|
-
function detectHardcode(filePath, content, lines, ignoreState,
|
|
26566
|
+
function detectHardcode(filePath, content, lines, ignoreState, _config) {
|
|
25686
26567
|
const findings = [];
|
|
25687
26568
|
findings.push(...detectMagicNumbers(filePath, content, lines, ignoreState));
|
|
25688
26569
|
findings.push(...detectHardcodedUrls(filePath, content, lines, ignoreState));
|
|
@@ -26038,7 +26919,7 @@ var NAMING_RULES = [
|
|
|
26038
26919
|
suggestion: "Use is/has/can/should prefix for boolean variables"
|
|
26039
26920
|
}
|
|
26040
26921
|
];
|
|
26041
|
-
function detectNaming(filePath, content, lines, ignoreState,
|
|
26922
|
+
function detectNaming(filePath, content, lines, ignoreState, _config) {
|
|
26042
26923
|
const findings = [];
|
|
26043
26924
|
findings.push(...detectSingleLetterVariables(filePath, content, lines, ignoreState));
|
|
26044
26925
|
findings.push(...detectHungarianNotation(filePath, content, lines, ignoreState));
|
|
@@ -26321,7 +27202,7 @@ var CONDITIONAL_RULES = [
|
|
|
26321
27202
|
suggestion: "Use the condition directly (or negate it)"
|
|
26322
27203
|
}
|
|
26323
27204
|
];
|
|
26324
|
-
function detectConditionals(filePath, content, lines, ignoreState,
|
|
27205
|
+
function detectConditionals(filePath, content, lines, ignoreState, _config) {
|
|
26325
27206
|
const findings = [];
|
|
26326
27207
|
findings.push(...detectDeeplyNestedIf(filePath, content, lines, ignoreState));
|
|
26327
27208
|
findings.push(...detectComplexConditions(filePath, content, lines, ignoreState));
|
|
@@ -26634,7 +27515,7 @@ var DEAD_CODE_RULES = [
|
|
|
26634
27515
|
suggestion: "Implement function or add TODO comment"
|
|
26635
27516
|
}
|
|
26636
27517
|
];
|
|
26637
|
-
function detectDeadCode(filePath, content, lines, ignoreState,
|
|
27518
|
+
function detectDeadCode(filePath, content, lines, ignoreState, _config) {
|
|
26638
27519
|
const findings = [];
|
|
26639
27520
|
findings.push(...detectUnusedImports(filePath, content, lines, ignoreState));
|
|
26640
27521
|
findings.push(...detectUnusedVariables(filePath, content, lines, ignoreState));
|
|
@@ -26928,7 +27809,7 @@ var TYPE_SAFETY_RULES = [
|
|
|
26928
27809
|
fileExtensions: [".ts", ".tsx"]
|
|
26929
27810
|
}
|
|
26930
27811
|
];
|
|
26931
|
-
function detectTypeSafety(filePath, content, lines, ignoreState,
|
|
27812
|
+
function detectTypeSafety(filePath, content, lines, ignoreState, _config) {
|
|
26932
27813
|
const findings = [];
|
|
26933
27814
|
if (!filePath.endsWith(".ts") && !filePath.endsWith(".tsx")) {
|
|
26934
27815
|
return findings;
|
|
@@ -31137,7 +32018,7 @@ var createTaskSchema = {
|
|
|
31137
32018
|
// src/mcp/tools/task/run-task.ts
|
|
31138
32019
|
init_esm_shims();
|
|
31139
32020
|
init_logger();
|
|
31140
|
-
function createRunTaskHandler(
|
|
32021
|
+
function createRunTaskHandler(_deps) {
|
|
31141
32022
|
return async (input, context) => {
|
|
31142
32023
|
const startTime = Date.now();
|
|
31143
32024
|
if (context?.signal?.aborted) {
|
|
@@ -33687,6 +34568,17 @@ Use this tool first to understand what AutomatosX offers.`,
|
|
|
33687
34568
|
mode: "sdk"
|
|
33688
34569
|
}));
|
|
33689
34570
|
}
|
|
34571
|
+
if (config.providers["qwen"]?.enabled) {
|
|
34572
|
+
const { QwenProvider: QwenProvider2 } = await Promise.resolve().then(() => (init_qwen_provider(), qwen_provider_exports));
|
|
34573
|
+
const qwenConfig = config.providers["qwen"];
|
|
34574
|
+
providers.push(new QwenProvider2({
|
|
34575
|
+
name: "qwen",
|
|
34576
|
+
enabled: true,
|
|
34577
|
+
priority: qwenConfig.priority,
|
|
34578
|
+
timeout: qwenConfig.timeout,
|
|
34579
|
+
mode: qwenConfig.mode || "sdk"
|
|
34580
|
+
}));
|
|
34581
|
+
}
|
|
33690
34582
|
const healthCheckInterval = config.router?.healthCheckInterval;
|
|
33691
34583
|
this.router = new Router({
|
|
33692
34584
|
providers,
|