jinzd-ai-cli 0.4.219 → 0.4.221
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 +101 -99
- package/README.zh-CN.md +40 -38
- package/dist/{batch-CHAPYRVM.js → batch-LSWWSINO.js} +3 -3
- package/dist/{chunk-GEN4O5QH.js → chunk-4AX2MEJA.js} +4 -4
- package/dist/{chunk-64NUW3WL.js → chunk-4MOBTDS6.js} +1 -1
- package/dist/{chunk-4ZN6L6K5.js → chunk-5BY4XICU.js} +6 -5
- package/dist/{chunk-MC34ISJU.js → chunk-5GTWZ4QF.js} +30 -6
- package/dist/{chunk-TZQHYZKT.js → chunk-5ULLIOVC.js} +1 -0
- package/dist/{chunk-3I4RZYYO.js → chunk-6SKMGONY.js} +6 -5
- package/dist/{chunk-QUFYBR6Q.js → chunk-BYQEWWA4.js} +2 -2
- package/dist/{chunk-VADHBW7T.js → chunk-OULVIHCT.js} +1 -1
- package/dist/{chunk-KIEZBTVD.js → chunk-PMTEZTXP.js} +2 -2
- package/dist/{chunk-QMXC327F.js → chunk-PUBCJF7E.js} +213 -175
- package/dist/{chunk-U7KVU25H.js → chunk-QDN5G7JN.js} +1 -1
- package/dist/{chunk-GGKQHPB3.js → chunk-QKJ3MAP7.js} +1 -1
- package/dist/{chunk-A4GUGWEY.js → chunk-QT5FA2ZY.js} +1 -1
- package/dist/{chunk-EGMORH5W.js → chunk-S5EEKS4P.js} +1 -1
- package/dist/{ci-QPP66T53.js → ci-OIFTAJKR.js} +6 -6
- package/dist/{ci-format-5S3EEYRK.js → ci-format-H6BLMWQ5.js} +2 -2
- package/dist/{constants-FJOLBABC.js → constants-EJRLT7DM.js} +1 -1
- package/dist/{doctor-cli-KB2LUDF3.js → doctor-cli-EXC7YU5D.js} +6 -6
- package/dist/electron-server.js +558 -466
- package/dist/{hub-DIM7SKKY.js → hub-HQDRQBNM.js} +2 -2
- package/dist/index.js +376 -45
- package/dist/{persist-L54DPLI7.js → persist-GTBEKVFL.js} +2 -2
- package/dist/{pr-YQGH72N6.js → pr-VSZNVKBS.js} +8 -7
- package/dist/{run-tests-O76JIBCW.js → run-tests-55JGEZYL.js} +1 -1
- package/dist/{run-tests-GEZSSNJM.js → run-tests-OYXK27XH.js} +2 -2
- package/dist/{server-5E2AIXVX.js → server-JKNIZ5CB.js} +54 -22
- package/dist/{server-Z6O3G2LY.js → server-XX6UM4ZQ.js} +5 -5
- package/dist/{task-orchestrator-5HBW4O64.js → task-orchestrator-F3DUIACA.js} +5 -5
- package/dist/{usage-BMK6M5U3.js → usage-4KFX3HTK.js} +3 -3
- package/package.json +1 -1
package/dist/electron-server.js
CHANGED
|
@@ -38,7 +38,7 @@ import {
|
|
|
38
38
|
VERSION,
|
|
39
39
|
buildUserIdentityPrompt,
|
|
40
40
|
runTestsTool
|
|
41
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-S5EEKS4P.js";
|
|
42
42
|
import {
|
|
43
43
|
hasSemanticIndex,
|
|
44
44
|
semanticSearch
|
|
@@ -64,8 +64,8 @@ import {
|
|
|
64
64
|
import express from "express";
|
|
65
65
|
import { createServer } from "http";
|
|
66
66
|
import { WebSocketServer } from "ws";
|
|
67
|
-
import { join as
|
|
68
|
-
import { existsSync as
|
|
67
|
+
import { join as join26, dirname as dirname9, resolve as resolve11, relative as relative4, sep as sep3 } from "path";
|
|
68
|
+
import { existsSync as existsSync31, readFileSync as readFileSync24, readdirSync as readdirSync15, statSync as statSync13, realpathSync } from "fs";
|
|
69
69
|
import { networkInterfaces } from "os";
|
|
70
70
|
|
|
71
71
|
// src/config/config-manager.ts
|
|
@@ -386,6 +386,7 @@ var ENV_KEY_MAP = {
|
|
|
386
386
|
zhipu: "AICLI_API_KEY_ZHIPU",
|
|
387
387
|
kimi: "AICLI_API_KEY_KIMI",
|
|
388
388
|
minimax: "AICLI_API_KEY_MINIMAX",
|
|
389
|
+
qwen: "AICLI_API_KEY_QWEN",
|
|
389
390
|
openai: "AICLI_API_KEY_OPENAI",
|
|
390
391
|
openrouter: "AICLI_API_KEY_OPENROUTER",
|
|
391
392
|
"google-search": "AICLI_API_KEY_GOOGLESEARCH",
|
|
@@ -1297,6 +1298,23 @@ var ClaudeProvider = class extends BaseProvider {
|
|
|
1297
1298
|
async listModels() {
|
|
1298
1299
|
return this.info.models;
|
|
1299
1300
|
}
|
|
1301
|
+
async refreshModels() {
|
|
1302
|
+
try {
|
|
1303
|
+
const response = await this.client.models.list();
|
|
1304
|
+
const data = response.data ?? [];
|
|
1305
|
+
return data.map((model) => ({
|
|
1306
|
+
id: model.id,
|
|
1307
|
+
displayName: model.id,
|
|
1308
|
+
contextWindow: 2e5,
|
|
1309
|
+
supportsStreaming: true,
|
|
1310
|
+
source: "live",
|
|
1311
|
+
available: true,
|
|
1312
|
+
metadataUnknown: true
|
|
1313
|
+
}));
|
|
1314
|
+
} catch (err) {
|
|
1315
|
+
throw this.wrapError(err);
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1300
1318
|
wrapError(err) {
|
|
1301
1319
|
if (err instanceof Anthropic.AuthenticationError) {
|
|
1302
1320
|
return new AuthError("claude");
|
|
@@ -3088,6 +3106,22 @@ var OpenAICompatibleProvider = class extends BaseProvider {
|
|
|
3088
3106
|
async listModels() {
|
|
3089
3107
|
return this.info.models;
|
|
3090
3108
|
}
|
|
3109
|
+
async refreshModels() {
|
|
3110
|
+
try {
|
|
3111
|
+
const response = await this.client.models.list();
|
|
3112
|
+
return response.data.map((model) => ({
|
|
3113
|
+
id: model.id,
|
|
3114
|
+
displayName: model.id,
|
|
3115
|
+
contextWindow: 128e3,
|
|
3116
|
+
supportsStreaming: true,
|
|
3117
|
+
source: "live",
|
|
3118
|
+
available: true,
|
|
3119
|
+
metadataUnknown: true
|
|
3120
|
+
}));
|
|
3121
|
+
} catch (err) {
|
|
3122
|
+
throw this.wrapError(err);
|
|
3123
|
+
}
|
|
3124
|
+
}
|
|
3091
3125
|
wrapError(err) {
|
|
3092
3126
|
if (err instanceof OpenAI.AuthenticationError) {
|
|
3093
3127
|
return new AuthError(this.info.id);
|
|
@@ -3278,54 +3312,6 @@ var ZhipuProvider = class extends OpenAICompatibleProvider {
|
|
|
3278
3312
|
displayName: "GLM-4.6 (200K, \u4E2D\u6587\u5199\u4F5C\u63A8\u8350)",
|
|
3279
3313
|
contextWindow: 204800,
|
|
3280
3314
|
supportsStreaming: true
|
|
3281
|
-
},
|
|
3282
|
-
{
|
|
3283
|
-
id: "glm-4.6v",
|
|
3284
|
-
displayName: "GLM-4.6V (Vision + Thinking)",
|
|
3285
|
-
contextWindow: 131072,
|
|
3286
|
-
supportsStreaming: true,
|
|
3287
|
-
supportsThinking: true
|
|
3288
|
-
},
|
|
3289
|
-
// ── GLM-Z1 推理系列 ──
|
|
3290
|
-
{
|
|
3291
|
-
id: "glm-z1",
|
|
3292
|
-
displayName: "GLM-Z1 (Reasoning Flagship)",
|
|
3293
|
-
contextWindow: 131072,
|
|
3294
|
-
supportsStreaming: true,
|
|
3295
|
-
supportsThinking: true
|
|
3296
|
-
},
|
|
3297
|
-
{
|
|
3298
|
-
id: "glm-z1-air",
|
|
3299
|
-
displayName: "GLM-Z1 Air (Lightweight Reasoning)",
|
|
3300
|
-
contextWindow: 131072,
|
|
3301
|
-
supportsStreaming: true,
|
|
3302
|
-
supportsThinking: true
|
|
3303
|
-
},
|
|
3304
|
-
{
|
|
3305
|
-
id: "glm-z1-flash",
|
|
3306
|
-
displayName: "GLM-Z1 Flash (Free Reasoning)",
|
|
3307
|
-
contextWindow: 128e3,
|
|
3308
|
-
supportsStreaming: true,
|
|
3309
|
-
supportsThinking: true
|
|
3310
|
-
},
|
|
3311
|
-
// ── GLM-4 系列(稳定,价格低) ──
|
|
3312
|
-
{
|
|
3313
|
-
id: "glm-4-plus",
|
|
3314
|
-
displayName: "GLM-4 Plus",
|
|
3315
|
-
contextWindow: 128e3,
|
|
3316
|
-
supportsStreaming: true
|
|
3317
|
-
},
|
|
3318
|
-
{
|
|
3319
|
-
id: "glm-4-air",
|
|
3320
|
-
displayName: "GLM-4 Air",
|
|
3321
|
-
contextWindow: 128e3,
|
|
3322
|
-
supportsStreaming: true
|
|
3323
|
-
},
|
|
3324
|
-
{
|
|
3325
|
-
id: "glm-4-flash",
|
|
3326
|
-
displayName: "GLM-4 Flash (Free)",
|
|
3327
|
-
contextWindow: 128e3,
|
|
3328
|
-
supportsStreaming: true
|
|
3329
3315
|
}
|
|
3330
3316
|
]
|
|
3331
3317
|
};
|
|
@@ -3362,8 +3348,7 @@ var KimiProvider = class _KimiProvider extends OpenAICompatibleProvider {
|
|
|
3362
3348
|
"kimi-k2.6",
|
|
3363
3349
|
"kimi-k2.5",
|
|
3364
3350
|
"kimi-k2-thinking",
|
|
3365
|
-
"kimi-k2-thinking-turbo"
|
|
3366
|
-
"kimi-k2-0905-preview"
|
|
3351
|
+
"kimi-k2-thinking-turbo"
|
|
3367
3352
|
]);
|
|
3368
3353
|
/** 对需要固定 temperature=1 的模型自动修正请求。 */
|
|
3369
3354
|
fixTemp(req) {
|
|
@@ -3411,51 +3396,6 @@ var KimiProvider = class _KimiProvider extends OpenAICompatibleProvider {
|
|
|
3411
3396
|
displayName: "Kimi K2 Thinking Turbo",
|
|
3412
3397
|
contextWindow: 262144,
|
|
3413
3398
|
supportsStreaming: true
|
|
3414
|
-
},
|
|
3415
|
-
// Kimi K2 Preview 系列
|
|
3416
|
-
{
|
|
3417
|
-
id: "kimi-k2-0905-preview",
|
|
3418
|
-
displayName: "Kimi K2 0905 Preview",
|
|
3419
|
-
contextWindow: 262144,
|
|
3420
|
-
supportsStreaming: true
|
|
3421
|
-
},
|
|
3422
|
-
// moonshot-v1 系列(老一代,稳定)
|
|
3423
|
-
{
|
|
3424
|
-
id: "moonshot-v1-8k",
|
|
3425
|
-
displayName: "Moonshot V1 8K",
|
|
3426
|
-
contextWindow: 8192,
|
|
3427
|
-
supportsStreaming: true
|
|
3428
|
-
},
|
|
3429
|
-
{
|
|
3430
|
-
id: "moonshot-v1-32k",
|
|
3431
|
-
displayName: "Moonshot V1 32K",
|
|
3432
|
-
contextWindow: 32768,
|
|
3433
|
-
supportsStreaming: true
|
|
3434
|
-
},
|
|
3435
|
-
{
|
|
3436
|
-
id: "moonshot-v1-128k",
|
|
3437
|
-
displayName: "Moonshot V1 128K",
|
|
3438
|
-
contextWindow: 131072,
|
|
3439
|
-
supportsStreaming: true
|
|
3440
|
-
},
|
|
3441
|
-
// moonshot-v1 Vision 系列(支持图片输入)
|
|
3442
|
-
{
|
|
3443
|
-
id: "moonshot-v1-8k-vision-preview",
|
|
3444
|
-
displayName: "Moonshot V1 8K Vision",
|
|
3445
|
-
contextWindow: 8192,
|
|
3446
|
-
supportsStreaming: true
|
|
3447
|
-
},
|
|
3448
|
-
{
|
|
3449
|
-
id: "moonshot-v1-32k-vision-preview",
|
|
3450
|
-
displayName: "Moonshot V1 32K Vision",
|
|
3451
|
-
contextWindow: 32768,
|
|
3452
|
-
supportsStreaming: true
|
|
3453
|
-
},
|
|
3454
|
-
{
|
|
3455
|
-
id: "moonshot-v1-128k-vision-preview",
|
|
3456
|
-
displayName: "Moonshot V1 128K Vision",
|
|
3457
|
-
contextWindow: 131072,
|
|
3458
|
-
supportsStreaming: true
|
|
3459
3399
|
}
|
|
3460
3400
|
]
|
|
3461
3401
|
};
|
|
@@ -3581,23 +3521,44 @@ var MiniMaxProvider = class extends OpenAICompatibleProvider {
|
|
|
3581
3521
|
displayName: "MiniMax M2.5 Highspeed",
|
|
3582
3522
|
contextWindow: 1e6,
|
|
3583
3523
|
supportsStreaming: true
|
|
3524
|
+
}
|
|
3525
|
+
]
|
|
3526
|
+
};
|
|
3527
|
+
};
|
|
3528
|
+
|
|
3529
|
+
// src/providers/qwen.ts
|
|
3530
|
+
var QwenProvider = class extends OpenAICompatibleProvider {
|
|
3531
|
+
defaultBaseUrl = "https://dashscope.aliyuncs.com/compatible-mode/v1";
|
|
3532
|
+
info = {
|
|
3533
|
+
id: "qwen",
|
|
3534
|
+
displayName: "Qwen (Alibaba Cloud)",
|
|
3535
|
+
defaultModel: "qwen-plus",
|
|
3536
|
+
apiKeyEnvVar: "AICLI_API_KEY_QWEN",
|
|
3537
|
+
requiresApiKey: true,
|
|
3538
|
+
baseUrl: this.defaultBaseUrl,
|
|
3539
|
+
models: [
|
|
3540
|
+
{
|
|
3541
|
+
id: "qwen-plus",
|
|
3542
|
+
displayName: "Qwen Plus",
|
|
3543
|
+
contextWindow: 131072,
|
|
3544
|
+
supportsStreaming: true
|
|
3584
3545
|
},
|
|
3585
3546
|
{
|
|
3586
|
-
id: "
|
|
3587
|
-
displayName: "
|
|
3588
|
-
contextWindow:
|
|
3547
|
+
id: "qwen-max",
|
|
3548
|
+
displayName: "Qwen Max",
|
|
3549
|
+
contextWindow: 131072,
|
|
3589
3550
|
supportsStreaming: true
|
|
3590
3551
|
},
|
|
3591
3552
|
{
|
|
3592
|
-
id: "
|
|
3593
|
-
displayName: "
|
|
3594
|
-
contextWindow:
|
|
3553
|
+
id: "qwen-turbo",
|
|
3554
|
+
displayName: "Qwen Turbo",
|
|
3555
|
+
contextWindow: 131072,
|
|
3595
3556
|
supportsStreaming: true
|
|
3596
3557
|
},
|
|
3597
3558
|
{
|
|
3598
|
-
id: "
|
|
3599
|
-
displayName: "
|
|
3600
|
-
contextWindow:
|
|
3559
|
+
id: "qwen-coder-plus",
|
|
3560
|
+
displayName: "Qwen Coder Plus",
|
|
3561
|
+
contextWindow: 131072,
|
|
3601
3562
|
supportsStreaming: true
|
|
3602
3563
|
}
|
|
3603
3564
|
]
|
|
@@ -3610,7 +3571,7 @@ var OpenAIProvider = class extends OpenAICompatibleProvider {
|
|
|
3610
3571
|
info = {
|
|
3611
3572
|
id: "openai",
|
|
3612
3573
|
displayName: "OpenAI",
|
|
3613
|
-
defaultModel: "gpt-
|
|
3574
|
+
defaultModel: "gpt-5.4",
|
|
3614
3575
|
apiKeyEnvVar: "AICLI_API_KEY_OPENAI",
|
|
3615
3576
|
requiresApiKey: true,
|
|
3616
3577
|
baseUrl: this.defaultBaseUrl,
|
|
@@ -3666,19 +3627,6 @@ var OpenAIProvider = class extends OpenAICompatibleProvider {
|
|
|
3666
3627
|
contextWindow: 1048576,
|
|
3667
3628
|
supportsStreaming: true
|
|
3668
3629
|
},
|
|
3669
|
-
// GPT-4o 系列(128K 上下文)
|
|
3670
|
-
{
|
|
3671
|
-
id: "gpt-4o",
|
|
3672
|
-
displayName: "GPT-4o",
|
|
3673
|
-
contextWindow: 128e3,
|
|
3674
|
-
supportsStreaming: true
|
|
3675
|
-
},
|
|
3676
|
-
{
|
|
3677
|
-
id: "gpt-4o-mini",
|
|
3678
|
-
displayName: "GPT-4o Mini",
|
|
3679
|
-
contextWindow: 128e3,
|
|
3680
|
-
supportsStreaming: true
|
|
3681
|
-
},
|
|
3682
3630
|
// 推理模型(200K 上下文)
|
|
3683
3631
|
{
|
|
3684
3632
|
id: "o3",
|
|
@@ -3686,12 +3634,6 @@ var OpenAIProvider = class extends OpenAICompatibleProvider {
|
|
|
3686
3634
|
contextWindow: 2e5,
|
|
3687
3635
|
supportsStreaming: true
|
|
3688
3636
|
},
|
|
3689
|
-
{
|
|
3690
|
-
id: "o3-mini",
|
|
3691
|
-
displayName: "o3 Mini",
|
|
3692
|
-
contextWindow: 2e5,
|
|
3693
|
-
supportsStreaming: true
|
|
3694
|
-
},
|
|
3695
3637
|
{
|
|
3696
3638
|
id: "o4-mini",
|
|
3697
3639
|
displayName: "o4 Mini",
|
|
@@ -3713,7 +3655,6 @@ var OpenRouterProvider = class extends OpenAICompatibleProvider {
|
|
|
3713
3655
|
requiresApiKey: true,
|
|
3714
3656
|
baseUrl: this.defaultBaseUrl,
|
|
3715
3657
|
models: [
|
|
3716
|
-
// Anthropic Claude 系列
|
|
3717
3658
|
{
|
|
3718
3659
|
id: "anthropic/claude-opus-4",
|
|
3719
3660
|
displayName: "Claude Opus 4",
|
|
@@ -3732,26 +3673,12 @@ var OpenRouterProvider = class extends OpenAICompatibleProvider {
|
|
|
3732
3673
|
contextWindow: 2e5,
|
|
3733
3674
|
supportsStreaming: true
|
|
3734
3675
|
},
|
|
3735
|
-
// OpenAI 系列
|
|
3736
3676
|
{
|
|
3737
3677
|
id: "openai/gpt-5.4",
|
|
3738
3678
|
displayName: "GPT-5.4",
|
|
3739
3679
|
contextWindow: 105e4,
|
|
3740
3680
|
supportsStreaming: true
|
|
3741
3681
|
},
|
|
3742
|
-
{
|
|
3743
|
-
id: "openai/gpt-4o",
|
|
3744
|
-
displayName: "GPT-4o",
|
|
3745
|
-
contextWindow: 128e3,
|
|
3746
|
-
supportsStreaming: true
|
|
3747
|
-
},
|
|
3748
|
-
{
|
|
3749
|
-
id: "openai/gpt-4o-mini",
|
|
3750
|
-
displayName: "GPT-4o Mini",
|
|
3751
|
-
contextWindow: 128e3,
|
|
3752
|
-
supportsStreaming: true
|
|
3753
|
-
},
|
|
3754
|
-
// Google Gemini 系列
|
|
3755
3682
|
{
|
|
3756
3683
|
id: "google/gemini-2.5-pro",
|
|
3757
3684
|
displayName: "Gemini 2.5 Pro",
|
|
@@ -3764,7 +3691,6 @@ var OpenRouterProvider = class extends OpenAICompatibleProvider {
|
|
|
3764
3691
|
contextWindow: 1048576,
|
|
3765
3692
|
supportsStreaming: true
|
|
3766
3693
|
},
|
|
3767
|
-
// Meta Llama 系列
|
|
3768
3694
|
{
|
|
3769
3695
|
id: "meta-llama/llama-4-maverick",
|
|
3770
3696
|
displayName: "Llama 4 Maverick",
|
|
@@ -3777,44 +3703,17 @@ var OpenRouterProvider = class extends OpenAICompatibleProvider {
|
|
|
3777
3703
|
contextWindow: 512e3,
|
|
3778
3704
|
supportsStreaming: true
|
|
3779
3705
|
},
|
|
3780
|
-
// DeepSeek 系列
|
|
3781
|
-
{
|
|
3782
|
-
id: "deepseek/deepseek-chat-v3-0324",
|
|
3783
|
-
displayName: "DeepSeek V3 0324",
|
|
3784
|
-
contextWindow: 131072,
|
|
3785
|
-
supportsStreaming: true
|
|
3786
|
-
},
|
|
3787
|
-
{
|
|
3788
|
-
id: "deepseek/deepseek-r1",
|
|
3789
|
-
displayName: "DeepSeek R1",
|
|
3790
|
-
contextWindow: 163840,
|
|
3791
|
-
supportsStreaming: true
|
|
3792
|
-
},
|
|
3793
|
-
// Qwen 系列
|
|
3794
3706
|
{
|
|
3795
3707
|
id: "qwen/qwen3-coder",
|
|
3796
3708
|
displayName: "Qwen3 Coder",
|
|
3797
3709
|
contextWindow: 262144,
|
|
3798
3710
|
supportsStreaming: true
|
|
3799
3711
|
},
|
|
3800
|
-
{
|
|
3801
|
-
id: "qwen/qwen-2.5-72b-instruct",
|
|
3802
|
-
displayName: "Qwen 2.5 72B",
|
|
3803
|
-
contextWindow: 131072,
|
|
3804
|
-
supportsStreaming: true
|
|
3805
|
-
},
|
|
3806
|
-
// Mistral 系列
|
|
3807
3712
|
{
|
|
3808
3713
|
id: "mistralai/mistral-large",
|
|
3809
3714
|
displayName: "Mistral Large",
|
|
3810
3715
|
contextWindow: 131072,
|
|
3811
3716
|
supportsStreaming: true
|
|
3812
|
-
},
|
|
3813
|
-
{
|
|
3814
|
-
id: "mistralai/mistral-small-3.1-24b-instruct",
|
|
3815
|
-
displayName: "Mistral Small 3.1 24B",
|
|
3816
|
-
contextWindow: 128e3,
|
|
3817
|
-
supportsStreaming: true
|
|
3818
3717
|
}
|
|
3819
3718
|
]
|
|
3820
3719
|
};
|
|
@@ -3899,6 +3798,10 @@ var OllamaProvider = class extends OpenAICompatibleProvider {
|
|
|
3899
3798
|
await this.tryConnect();
|
|
3900
3799
|
return this.dynamicModels.length > 0 ? this.dynamicModels : this.info.models;
|
|
3901
3800
|
}
|
|
3801
|
+
async refreshModels() {
|
|
3802
|
+
await this.tryConnect(false);
|
|
3803
|
+
return this.dynamicModels.length > 0 ? this.dynamicModels : this.info.models;
|
|
3804
|
+
}
|
|
3902
3805
|
/** Ollama 无需验证 API Key,只检查服务是否可达 */
|
|
3903
3806
|
async validateApiKey() {
|
|
3904
3807
|
return this.tryConnect();
|
|
@@ -3939,6 +3842,111 @@ var CustomProvider = class extends OpenAICompatibleProvider {
|
|
|
3939
3842
|
}
|
|
3940
3843
|
};
|
|
3941
3844
|
|
|
3845
|
+
// src/providers/model-cache.ts
|
|
3846
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, unlinkSync } from "fs";
|
|
3847
|
+
import { dirname, join as join2 } from "path";
|
|
3848
|
+
var CACHE_FILE_NAME = "model-cache.json";
|
|
3849
|
+
var LIVE_DEFAULT_CONTEXT = 128e3;
|
|
3850
|
+
function modelCachePath(configDir) {
|
|
3851
|
+
return join2(configDir, CACHE_FILE_NAME);
|
|
3852
|
+
}
|
|
3853
|
+
function loadModelCache(configDir) {
|
|
3854
|
+
const path3 = modelCachePath(configDir);
|
|
3855
|
+
if (!existsSync2(path3)) return { version: 1, providers: {} };
|
|
3856
|
+
try {
|
|
3857
|
+
const parsed = JSON.parse(readFileSync2(path3, "utf-8"));
|
|
3858
|
+
if (parsed.version !== 1 || !parsed.providers || typeof parsed.providers !== "object") {
|
|
3859
|
+
return { version: 1, providers: {} };
|
|
3860
|
+
}
|
|
3861
|
+
return { version: 1, providers: parsed.providers };
|
|
3862
|
+
} catch {
|
|
3863
|
+
return { version: 1, providers: {} };
|
|
3864
|
+
}
|
|
3865
|
+
}
|
|
3866
|
+
function saveModelCache(configDir, cache) {
|
|
3867
|
+
const path3 = modelCachePath(configDir);
|
|
3868
|
+
mkdirSync2(dirname(path3), { recursive: true });
|
|
3869
|
+
atomicWriteFileSync(path3, `${JSON.stringify(cache, null, 2)}
|
|
3870
|
+
`);
|
|
3871
|
+
}
|
|
3872
|
+
function getCachedModels(configDir, providerId) {
|
|
3873
|
+
const entry = loadModelCache(configDir).providers[providerId];
|
|
3874
|
+
if (!entry) return [];
|
|
3875
|
+
return normalizeLiveModels(entry.models, entry.fetchedAt, "cache");
|
|
3876
|
+
}
|
|
3877
|
+
function getModelCacheSummary(configDir) {
|
|
3878
|
+
const cache = loadModelCache(configDir);
|
|
3879
|
+
return Object.entries(cache.providers).map(([providerId, entry]) => ({
|
|
3880
|
+
providerId,
|
|
3881
|
+
count: entry.models.length,
|
|
3882
|
+
fetchedAt: entry.fetchedAt,
|
|
3883
|
+
baseUrl: entry.baseUrl
|
|
3884
|
+
}));
|
|
3885
|
+
}
|
|
3886
|
+
function setCachedModels(configDir, providerId, models, baseUrl) {
|
|
3887
|
+
const cache = loadModelCache(configDir);
|
|
3888
|
+
const fetchedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3889
|
+
const entry = {
|
|
3890
|
+
fetchedAt,
|
|
3891
|
+
...baseUrl ? { baseUrl } : {},
|
|
3892
|
+
models: normalizeLiveModels(models, fetchedAt, "live"),
|
|
3893
|
+
lastError: null
|
|
3894
|
+
};
|
|
3895
|
+
cache.providers[providerId] = entry;
|
|
3896
|
+
saveModelCache(configDir, cache);
|
|
3897
|
+
return entry;
|
|
3898
|
+
}
|
|
3899
|
+
function clearModelCache(configDir, providerId) {
|
|
3900
|
+
const path3 = modelCachePath(configDir);
|
|
3901
|
+
if (!providerId) {
|
|
3902
|
+
if (existsSync2(path3)) unlinkSync(path3);
|
|
3903
|
+
return;
|
|
3904
|
+
}
|
|
3905
|
+
const cache = loadModelCache(configDir);
|
|
3906
|
+
delete cache.providers[providerId];
|
|
3907
|
+
saveModelCache(configDir, cache);
|
|
3908
|
+
}
|
|
3909
|
+
function normalizeLiveModels(models, fetchedAt, source = "live") {
|
|
3910
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3911
|
+
const normalized = [];
|
|
3912
|
+
for (const model of models) {
|
|
3913
|
+
if (!model.id || seen.has(model.id)) continue;
|
|
3914
|
+
seen.add(model.id);
|
|
3915
|
+
normalized.push({
|
|
3916
|
+
id: model.id,
|
|
3917
|
+
displayName: model.displayName || model.id,
|
|
3918
|
+
contextWindow: model.contextWindow || LIVE_DEFAULT_CONTEXT,
|
|
3919
|
+
supportsStreaming: model.supportsStreaming ?? true,
|
|
3920
|
+
...model.supportsThinking !== void 0 ? { supportsThinking: model.supportsThinking } : {},
|
|
3921
|
+
source,
|
|
3922
|
+
available: true,
|
|
3923
|
+
metadataUnknown: model.metadataUnknown ?? !model.contextWindow,
|
|
3924
|
+
...fetchedAt ? { fetchedAt } : {}
|
|
3925
|
+
});
|
|
3926
|
+
}
|
|
3927
|
+
return normalized;
|
|
3928
|
+
}
|
|
3929
|
+
function mergeModelLists(curatedModels, liveModels) {
|
|
3930
|
+
const liveById = new Map(liveModels.map((model) => [model.id, model]));
|
|
3931
|
+
const merged = curatedModels.map((model) => ({
|
|
3932
|
+
...model,
|
|
3933
|
+
source: liveById.has(model.id) ? "curated+live" : model.source ?? "curated",
|
|
3934
|
+
available: liveById.has(model.id) ? true : model.available ?? "unknown",
|
|
3935
|
+
fetchedAt: liveById.get(model.id)?.fetchedAt
|
|
3936
|
+
}));
|
|
3937
|
+
const curatedIds = new Set(curatedModels.map((model) => model.id));
|
|
3938
|
+
for (const model of liveModels) {
|
|
3939
|
+
if (curatedIds.has(model.id)) continue;
|
|
3940
|
+
merged.push({
|
|
3941
|
+
...model,
|
|
3942
|
+
source: model.source ?? "live",
|
|
3943
|
+
available: true,
|
|
3944
|
+
metadataUnknown: model.metadataUnknown ?? true
|
|
3945
|
+
});
|
|
3946
|
+
}
|
|
3947
|
+
return merged;
|
|
3948
|
+
}
|
|
3949
|
+
|
|
3942
3950
|
// src/providers/registry.ts
|
|
3943
3951
|
var BUILT_IN_PROVIDERS = [
|
|
3944
3952
|
ClaudeProvider,
|
|
@@ -3947,15 +3955,17 @@ var BUILT_IN_PROVIDERS = [
|
|
|
3947
3955
|
ZhipuProvider,
|
|
3948
3956
|
KimiProvider,
|
|
3949
3957
|
MiniMaxProvider,
|
|
3958
|
+
QwenProvider,
|
|
3950
3959
|
OpenAIProvider,
|
|
3951
3960
|
OpenRouterProvider,
|
|
3952
3961
|
OllamaProvider
|
|
3953
3962
|
];
|
|
3954
3963
|
var ProviderRegistry = class {
|
|
3955
3964
|
providers = /* @__PURE__ */ new Map();
|
|
3956
|
-
// 记录自定义 provider 的状态(用于 listAll 展示)
|
|
3957
3965
|
customConfigs = [];
|
|
3958
|
-
|
|
3966
|
+
configDir;
|
|
3967
|
+
async initialize(getApiKey, getOptions, customProviderConfigs, configDir) {
|
|
3968
|
+
this.configDir = configDir;
|
|
3959
3969
|
for (const ProviderClass of BUILT_IN_PROVIDERS) {
|
|
3960
3970
|
const provider = new ProviderClass();
|
|
3961
3971
|
const apiKey = getApiKey(provider.info.id);
|
|
@@ -3982,11 +3992,6 @@ var ProviderRegistry = class {
|
|
|
3982
3992
|
this.providers.set(cfg.id, provider);
|
|
3983
3993
|
}
|
|
3984
3994
|
}
|
|
3985
|
-
/**
|
|
3986
|
-
* Register a provider instance under the given id (overwrites any existing).
|
|
3987
|
-
* Primary uses: integration tests injecting MockProvider, and future hot-swap
|
|
3988
|
-
* scenarios. The provider is assumed already initialized.
|
|
3989
|
-
*/
|
|
3990
3995
|
register(id, provider) {
|
|
3991
3996
|
this.providers.set(id, provider);
|
|
3992
3997
|
}
|
|
@@ -4000,6 +4005,37 @@ var ProviderRegistry = class {
|
|
|
4000
4005
|
has(id) {
|
|
4001
4006
|
return this.providers.has(id);
|
|
4002
4007
|
}
|
|
4008
|
+
async listModels(providerId) {
|
|
4009
|
+
const provider = this.get(providerId);
|
|
4010
|
+
const curated = await provider.listModels();
|
|
4011
|
+
if (!this.configDir) return curated;
|
|
4012
|
+
const cached = getCachedModels(this.configDir, providerId);
|
|
4013
|
+
return cached.length > 0 ? mergeModelLists(curated, cached) : curated;
|
|
4014
|
+
}
|
|
4015
|
+
async refreshModels(providerId) {
|
|
4016
|
+
const provider = this.get(providerId);
|
|
4017
|
+
if (!provider.refreshModels) {
|
|
4018
|
+
throw new Error(`Provider ${providerId} does not support model refresh`);
|
|
4019
|
+
}
|
|
4020
|
+
const liveModels = await provider.refreshModels();
|
|
4021
|
+
if (!this.configDir) {
|
|
4022
|
+
const merged2 = mergeModelLists(await provider.listModels(), liveModels);
|
|
4023
|
+
provider.info.models = merged2;
|
|
4024
|
+
return merged2;
|
|
4025
|
+
}
|
|
4026
|
+
setCachedModels(this.configDir, providerId, liveModels, provider.info.baseUrl);
|
|
4027
|
+
const merged = await this.listModels(providerId);
|
|
4028
|
+
provider.info.models = merged;
|
|
4029
|
+
return merged;
|
|
4030
|
+
}
|
|
4031
|
+
getModelCacheStatus() {
|
|
4032
|
+
if (!this.configDir) return [];
|
|
4033
|
+
return getModelCacheSummary(this.configDir);
|
|
4034
|
+
}
|
|
4035
|
+
clearModelCache(providerId) {
|
|
4036
|
+
if (!this.configDir) return;
|
|
4037
|
+
clearModelCache(this.configDir, providerId);
|
|
4038
|
+
}
|
|
4003
4039
|
listAvailable() {
|
|
4004
4040
|
return [...this.providers.values()];
|
|
4005
4041
|
}
|
|
@@ -4029,8 +4065,8 @@ var ProviderRegistry = class {
|
|
|
4029
4065
|
};
|
|
4030
4066
|
|
|
4031
4067
|
// src/session/session-manager.ts
|
|
4032
|
-
import { readFileSync as
|
|
4033
|
-
import { join as
|
|
4068
|
+
import { readFileSync as readFileSync3, existsSync as existsSync3, mkdirSync as mkdirSync3, readdirSync, unlinkSync as unlinkSync2, openSync, readSync, closeSync, statSync } from "fs";
|
|
4069
|
+
import { join as join3 } from "path";
|
|
4034
4070
|
import { v4 as uuidv4 } from "uuid";
|
|
4035
4071
|
|
|
4036
4072
|
// src/core/types.ts
|
|
@@ -4609,8 +4645,8 @@ var SessionManager = class {
|
|
|
4609
4645
|
}
|
|
4610
4646
|
async save() {
|
|
4611
4647
|
if (!this._current) return;
|
|
4612
|
-
|
|
4613
|
-
const filePath =
|
|
4648
|
+
mkdirSync3(this.historyDir, { recursive: true });
|
|
4649
|
+
const filePath = join3(this.historyDir, `${this._current.id}.json`);
|
|
4614
4650
|
const raw = this._current.toJSON();
|
|
4615
4651
|
const opts = this.redactOptionsForSave();
|
|
4616
4652
|
const { value: payload, hits } = redactJson(raw, opts);
|
|
@@ -4618,13 +4654,13 @@ var SessionManager = class {
|
|
|
4618
4654
|
atomicWriteFileSync(filePath, JSON.stringify(payload, null, 2));
|
|
4619
4655
|
}
|
|
4620
4656
|
loadSession(id) {
|
|
4621
|
-
const filePath =
|
|
4622
|
-
if (!
|
|
4657
|
+
const filePath = join3(this.historyDir, `${id}.json`);
|
|
4658
|
+
if (!existsSync3(filePath)) {
|
|
4623
4659
|
throw new Error(`Session ${id} not found`);
|
|
4624
4660
|
}
|
|
4625
4661
|
let data;
|
|
4626
4662
|
try {
|
|
4627
|
-
data = JSON.parse(
|
|
4663
|
+
data = JSON.parse(readFileSync3(filePath, "utf-8"));
|
|
4628
4664
|
} catch (err) {
|
|
4629
4665
|
throw new Error(`Session ${id} is corrupted: ${err instanceof Error ? err.message : String(err)}`);
|
|
4630
4666
|
}
|
|
@@ -4632,14 +4668,22 @@ var SessionManager = class {
|
|
|
4632
4668
|
this._current = session;
|
|
4633
4669
|
return session;
|
|
4634
4670
|
}
|
|
4635
|
-
listSessions() {
|
|
4636
|
-
if (!
|
|
4671
|
+
listSessions(opts) {
|
|
4672
|
+
if (!existsSync3(this.historyDir)) return [];
|
|
4637
4673
|
const files = readdirSync(this.historyDir).filter((f) => f.endsWith(".json"));
|
|
4638
4674
|
const metas = [];
|
|
4639
4675
|
for (const file of files) {
|
|
4640
4676
|
try {
|
|
4641
|
-
const
|
|
4642
|
-
|
|
4677
|
+
const filePath = join3(this.historyDir, file);
|
|
4678
|
+
const meta = this.readSessionMeta(filePath);
|
|
4679
|
+
if (!meta) continue;
|
|
4680
|
+
if (opts?.includeFileSize) {
|
|
4681
|
+
try {
|
|
4682
|
+
meta.fileSize = statSync(filePath).size;
|
|
4683
|
+
} catch {
|
|
4684
|
+
}
|
|
4685
|
+
}
|
|
4686
|
+
metas.push(meta);
|
|
4643
4687
|
} catch (err) {
|
|
4644
4688
|
process.stderr.write(
|
|
4645
4689
|
`[Warning] Skipping corrupted session file "${file}": ${err instanceof Error ? err.message : String(err)}
|
|
@@ -4678,12 +4722,24 @@ var SessionManager = class {
|
|
|
4678
4722
|
if (messageCount === void 0) {
|
|
4679
4723
|
messageCount = 0;
|
|
4680
4724
|
try {
|
|
4681
|
-
const full =
|
|
4725
|
+
const full = readFileSync3(filePath, "utf-8");
|
|
4682
4726
|
const matches = full.match(/"role"\s*:/g);
|
|
4683
4727
|
messageCount = matches ? matches.length : 0;
|
|
4684
4728
|
} catch {
|
|
4685
4729
|
}
|
|
4686
4730
|
}
|
|
4731
|
+
let branchCount;
|
|
4732
|
+
let activeBranch;
|
|
4733
|
+
try {
|
|
4734
|
+
const brMatch = header.match(/"branches"\s*:\s*\[/);
|
|
4735
|
+
if (brMatch) {
|
|
4736
|
+
const brIds = header.match(/"id"\s*:\s*"[^"]*"/g);
|
|
4737
|
+
const parentMatches = header.match(/"parentBranchId"/g);
|
|
4738
|
+
branchCount = parentMatches ? parentMatches.length + 1 : 1;
|
|
4739
|
+
}
|
|
4740
|
+
activeBranch = extractJsonField(header, "activeBranchId");
|
|
4741
|
+
} catch {
|
|
4742
|
+
}
|
|
4687
4743
|
return {
|
|
4688
4744
|
id,
|
|
4689
4745
|
provider,
|
|
@@ -4691,10 +4747,12 @@ var SessionManager = class {
|
|
|
4691
4747
|
messageCount,
|
|
4692
4748
|
created: safeDate(created),
|
|
4693
4749
|
updated: safeDate(updated),
|
|
4694
|
-
title: title || void 0
|
|
4750
|
+
title: title || void 0,
|
|
4751
|
+
branchCount,
|
|
4752
|
+
activeBranch
|
|
4695
4753
|
};
|
|
4696
4754
|
}
|
|
4697
|
-
const data = JSON.parse(
|
|
4755
|
+
const data = JSON.parse(readFileSync3(filePath, "utf-8"));
|
|
4698
4756
|
return {
|
|
4699
4757
|
id: data.id,
|
|
4700
4758
|
provider: data.provider,
|
|
@@ -4702,14 +4760,16 @@ var SessionManager = class {
|
|
|
4702
4760
|
messageCount: data.messages?.length ?? 0,
|
|
4703
4761
|
created: safeDate(data.created),
|
|
4704
4762
|
updated: safeDate(data.updated),
|
|
4705
|
-
title: data.title
|
|
4763
|
+
title: data.title,
|
|
4764
|
+
branchCount: data.branches?.length ?? 1,
|
|
4765
|
+
activeBranch: data.activeBranchId
|
|
4706
4766
|
};
|
|
4707
4767
|
}
|
|
4708
4768
|
deleteSession(id) {
|
|
4709
|
-
const filePath =
|
|
4710
|
-
if (!
|
|
4769
|
+
const filePath = join3(this.historyDir, `${id}.json`);
|
|
4770
|
+
if (!existsSync3(filePath)) return false;
|
|
4711
4771
|
try {
|
|
4712
|
-
|
|
4772
|
+
unlinkSync2(filePath);
|
|
4713
4773
|
if (this._current && this._current.id === id) {
|
|
4714
4774
|
this._current = null;
|
|
4715
4775
|
}
|
|
@@ -4744,14 +4804,14 @@ var SessionManager = class {
|
|
|
4744
4804
|
* 每个 session 最多返回 3 条匹配片段,全局最多 maxResults 个 session。
|
|
4745
4805
|
*/
|
|
4746
4806
|
searchMessages(query, maxResults = 20) {
|
|
4747
|
-
if (!
|
|
4807
|
+
if (!existsSync3(this.historyDir)) return [];
|
|
4748
4808
|
const q = query.toLowerCase();
|
|
4749
|
-
const files = readdirSync(this.historyDir).filter((f) => f.endsWith(".json")).map((f) =>
|
|
4809
|
+
const files = readdirSync(this.historyDir).filter((f) => f.endsWith(".json")).map((f) => join3(this.historyDir, f));
|
|
4750
4810
|
const results = [];
|
|
4751
4811
|
for (const filePath of files) {
|
|
4752
4812
|
if (results.length >= maxResults) break;
|
|
4753
4813
|
try {
|
|
4754
|
-
const data = JSON.parse(
|
|
4814
|
+
const data = JSON.parse(readFileSync3(filePath, "utf-8"));
|
|
4755
4815
|
const messages = data.messages ?? [];
|
|
4756
4816
|
const matches = [];
|
|
4757
4817
|
for (const msg of messages) {
|
|
@@ -4798,12 +4858,12 @@ var SessionManager = class {
|
|
|
4798
4858
|
|
|
4799
4859
|
// src/tools/builtin/bash.ts
|
|
4800
4860
|
import { spawn } from "child_process";
|
|
4801
|
-
import { existsSync as
|
|
4861
|
+
import { existsSync as existsSync5, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
4802
4862
|
import { platform as platform2 } from "os";
|
|
4803
4863
|
import { resolve } from "path";
|
|
4804
4864
|
|
|
4805
4865
|
// src/tools/undo-stack.ts
|
|
4806
|
-
import { readFileSync as
|
|
4866
|
+
import { readFileSync as readFileSync4, writeFileSync, unlinkSync as unlinkSync3, rmdirSync, existsSync as existsSync4 } from "fs";
|
|
4807
4867
|
var MAX_UNDO_DEPTH = 20;
|
|
4808
4868
|
var UndoStack = class {
|
|
4809
4869
|
stack = [];
|
|
@@ -4814,9 +4874,9 @@ var UndoStack = class {
|
|
|
4814
4874
|
*/
|
|
4815
4875
|
push(filePath, description) {
|
|
4816
4876
|
let previousContent = null;
|
|
4817
|
-
if (
|
|
4877
|
+
if (existsSync4(filePath)) {
|
|
4818
4878
|
try {
|
|
4819
|
-
previousContent =
|
|
4879
|
+
previousContent = readFileSync4(filePath, "utf-8");
|
|
4820
4880
|
} catch {
|
|
4821
4881
|
return;
|
|
4822
4882
|
}
|
|
@@ -4870,7 +4930,7 @@ var UndoStack = class {
|
|
|
4870
4930
|
try {
|
|
4871
4931
|
if (entry.previousContent === null) {
|
|
4872
4932
|
if (entry.isDirectory) {
|
|
4873
|
-
if (
|
|
4933
|
+
if (existsSync4(entry.filePath)) {
|
|
4874
4934
|
try {
|
|
4875
4935
|
rmdirSync(entry.filePath);
|
|
4876
4936
|
return { entry, result: `Removed newly created directory: ${entry.filePath}` };
|
|
@@ -4880,8 +4940,8 @@ var UndoStack = class {
|
|
|
4880
4940
|
}
|
|
4881
4941
|
return { entry, result: `Directory already removed: ${entry.filePath}` };
|
|
4882
4942
|
} else {
|
|
4883
|
-
if (
|
|
4884
|
-
|
|
4943
|
+
if (existsSync4(entry.filePath)) {
|
|
4944
|
+
unlinkSync3(entry.filePath);
|
|
4885
4945
|
}
|
|
4886
4946
|
return { entry, result: `Deleted newly created file: ${entry.filePath}` };
|
|
4887
4947
|
}
|
|
@@ -5025,7 +5085,7 @@ Important rules:
|
|
|
5025
5085
|
throw new ToolError("bash", blockingHint);
|
|
5026
5086
|
}
|
|
5027
5087
|
let currentCwd = getCwd();
|
|
5028
|
-
if (!
|
|
5088
|
+
if (!existsSync5(currentCwd)) {
|
|
5029
5089
|
const fallback = process.cwd();
|
|
5030
5090
|
process.stderr.write(
|
|
5031
5091
|
`[bash] Previous cwd "${currentCwd}" no longer exists, reset to "${fallback}"
|
|
@@ -5037,7 +5097,7 @@ Important rules:
|
|
|
5037
5097
|
let effectiveCwd = currentCwd;
|
|
5038
5098
|
if (cwdArg) {
|
|
5039
5099
|
const resolved = resolve(currentCwd, cwdArg);
|
|
5040
|
-
if (!
|
|
5100
|
+
if (!existsSync5(resolved)) {
|
|
5041
5101
|
throw new ToolError(
|
|
5042
5102
|
"bash",
|
|
5043
5103
|
`cwd directory does not exist: "${resolved}". Create it first (e.g. mkdir -p "${resolved}") before specifying it as cwd.`
|
|
@@ -5057,7 +5117,7 @@ Important rules:
|
|
|
5057
5117
|
const parsedTargets = parseCreationTargets(command, effectiveCwd);
|
|
5058
5118
|
const parsedTargetsBefore = /* @__PURE__ */ new Map();
|
|
5059
5119
|
for (const t of parsedTargets) {
|
|
5060
|
-
parsedTargetsBefore.set(t,
|
|
5120
|
+
parsedTargetsBefore.set(t, existsSync5(t));
|
|
5061
5121
|
}
|
|
5062
5122
|
try {
|
|
5063
5123
|
const { stdout, stderr, status, signal, timedOut, aborted } = await runShell(
|
|
@@ -5273,7 +5333,7 @@ function pushBashUndoEntries(beforeSnapshot, parsedTargetsBefore, cwd) {
|
|
|
5273
5333
|
for (const absPath of afterSnapshot) {
|
|
5274
5334
|
if (!beforeSnapshot.has(absPath)) {
|
|
5275
5335
|
try {
|
|
5276
|
-
const st =
|
|
5336
|
+
const st = statSync2(absPath);
|
|
5277
5337
|
if (st.isDirectory()) {
|
|
5278
5338
|
undoStack.pushNewDir(absPath, `bash (new dir): ${absPath}`);
|
|
5279
5339
|
} else {
|
|
@@ -5285,9 +5345,9 @@ function pushBashUndoEntries(beforeSnapshot, parsedTargetsBefore, cwd) {
|
|
|
5285
5345
|
}
|
|
5286
5346
|
}
|
|
5287
5347
|
for (const [target, existedBefore] of parsedTargetsBefore) {
|
|
5288
|
-
if (!existedBefore && !tracked.has(target) &&
|
|
5348
|
+
if (!existedBefore && !tracked.has(target) && existsSync5(target)) {
|
|
5289
5349
|
try {
|
|
5290
|
-
const st =
|
|
5350
|
+
const st = statSync2(target);
|
|
5291
5351
|
if (st.isDirectory()) {
|
|
5292
5352
|
undoStack.pushNewDir(target, `bash (new dir): ${target}`);
|
|
5293
5353
|
} else {
|
|
@@ -5398,7 +5458,7 @@ function updateCwdFromCommand(command, baseCwd) {
|
|
|
5398
5458
|
if (!target || target.startsWith("$") || target === "~") return;
|
|
5399
5459
|
try {
|
|
5400
5460
|
const newDir = resolve(baseCwd, target);
|
|
5401
|
-
if (
|
|
5461
|
+
if (existsSync5(newDir)) {
|
|
5402
5462
|
setCwd(newDir);
|
|
5403
5463
|
}
|
|
5404
5464
|
} catch {
|
|
@@ -5406,9 +5466,9 @@ function updateCwdFromCommand(command, baseCwd) {
|
|
|
5406
5466
|
}
|
|
5407
5467
|
|
|
5408
5468
|
// src/tools/builtin/read-file.ts
|
|
5409
|
-
import { readFileSync as
|
|
5469
|
+
import { readFileSync as readFileSync5, existsSync as existsSync6, statSync as statSync3, readdirSync as readdirSync3 } from "fs";
|
|
5410
5470
|
import { execFileSync } from "child_process";
|
|
5411
|
-
import { extname, resolve as resolve2, basename, sep, dirname } from "path";
|
|
5471
|
+
import { extname, resolve as resolve2, basename, sep, dirname as dirname2 } from "path";
|
|
5412
5472
|
import { homedir as homedir2 } from "os";
|
|
5413
5473
|
|
|
5414
5474
|
// src/tools/builtin/notebook-utils.ts
|
|
@@ -5683,7 +5743,7 @@ var readFileTool = {
|
|
|
5683
5743
|
const normalizedPath = resolve2(filePath);
|
|
5684
5744
|
const refusal = getHardRefusal(normalizedPath);
|
|
5685
5745
|
if (refusal) return refusal;
|
|
5686
|
-
if (!
|
|
5746
|
+
if (!existsSync6(normalizedPath)) {
|
|
5687
5747
|
const suggestions = findSimilarFiles(filePath);
|
|
5688
5748
|
if (suggestions.length > 0) {
|
|
5689
5749
|
throw new ToolError(
|
|
@@ -5702,7 +5762,7 @@ Current working directory: ${process.cwd()}
|
|
|
5702
5762
|
Please use list_dir to verify the file path and retry.`
|
|
5703
5763
|
);
|
|
5704
5764
|
}
|
|
5705
|
-
const { size } =
|
|
5765
|
+
const { size } = statSync3(normalizedPath);
|
|
5706
5766
|
if (size > MAX_FILE_BYTES) {
|
|
5707
5767
|
const mb = (size / 1024 / 1024).toFixed(1);
|
|
5708
5768
|
return `[File too large: ${filePath} (${mb} MB)]
|
|
@@ -5722,9 +5782,9 @@ Use the bash tool to read in segments, e.g.:
|
|
|
5722
5782
|
|
|
5723
5783
|
${pdfText}`;
|
|
5724
5784
|
}
|
|
5725
|
-
const dir =
|
|
5785
|
+
const dir = dirname2(normalizedPath);
|
|
5726
5786
|
const nameNoExt = basename(normalizedPath, ext);
|
|
5727
|
-
const textAlts = [".md", ".txt", ".html"].map((e) => resolve2(dir, nameNoExt + e)).filter(
|
|
5787
|
+
const textAlts = [".md", ".txt", ".html"].map((e) => resolve2(dir, nameNoExt + e)).filter(existsSync6);
|
|
5728
5788
|
if (textAlts.length > 0) {
|
|
5729
5789
|
return `[PDF file: ${filePath}]
|
|
5730
5790
|
Cannot extract text from this PDF, but found alternative text versions:
|
|
@@ -5737,7 +5797,7 @@ Suggestion: read existing text versions (.md / .txt) in the project, or install
|
|
|
5737
5797
|
}
|
|
5738
5798
|
if (ext === ".ipynb") {
|
|
5739
5799
|
try {
|
|
5740
|
-
const raw =
|
|
5800
|
+
const raw = readFileSync5(normalizedPath, "utf-8");
|
|
5741
5801
|
const nb = parseNotebook(raw);
|
|
5742
5802
|
return renderNotebookAsText(nb, filePath);
|
|
5743
5803
|
} catch (err) {
|
|
@@ -5750,7 +5810,7 @@ ${err.message}`;
|
|
|
5750
5810
|
This is a binary file and cannot be read as text. If there is a text version (.md / .txt) in the project, please read that instead.`;
|
|
5751
5811
|
}
|
|
5752
5812
|
const { readFile: readFile2 } = await import("fs/promises");
|
|
5753
|
-
const buf = size > 1048576 ? await readFile2(normalizedPath) :
|
|
5813
|
+
const buf = size > 1048576 ? await readFile2(normalizedPath) : readFileSync5(normalizedPath);
|
|
5754
5814
|
if (encoding === "base64") {
|
|
5755
5815
|
return `[File: ${filePath} | base64]
|
|
5756
5816
|
|
|
@@ -5788,12 +5848,12 @@ ${content}`;
|
|
|
5788
5848
|
};
|
|
5789
5849
|
|
|
5790
5850
|
// src/tools/builtin/write-file.ts
|
|
5791
|
-
import { appendFileSync, mkdirSync as
|
|
5792
|
-
import { dirname as
|
|
5851
|
+
import { appendFileSync, mkdirSync as mkdirSync6 } from "fs";
|
|
5852
|
+
import { dirname as dirname4 } from "path";
|
|
5793
5853
|
|
|
5794
5854
|
// src/tools/executor.ts
|
|
5795
5855
|
import chalk3 from "chalk";
|
|
5796
|
-
import { existsSync as
|
|
5856
|
+
import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
|
|
5797
5857
|
import { tmpdir } from "os";
|
|
5798
5858
|
|
|
5799
5859
|
// src/core/readline-internal.ts
|
|
@@ -6002,8 +6062,8 @@ function simpleDiff(oldLines, newLines) {
|
|
|
6002
6062
|
// src/tools/hooks.ts
|
|
6003
6063
|
import { execSync } from "child_process";
|
|
6004
6064
|
import { createHash } from "crypto";
|
|
6005
|
-
import { existsSync as
|
|
6006
|
-
import { join as
|
|
6065
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync6 } from "fs";
|
|
6066
|
+
import { join as join4 } from "path";
|
|
6007
6067
|
var TRUST_FILE = "hooks-trust.json";
|
|
6008
6068
|
var DEFAULT_TIMEOUT_MS = 5e3;
|
|
6009
6069
|
function asArray(entry) {
|
|
@@ -6012,13 +6072,13 @@ function asArray(entry) {
|
|
|
6012
6072
|
return raw.map((item) => typeof item === "string" ? { command: item } : item);
|
|
6013
6073
|
}
|
|
6014
6074
|
function hookTrustPath(configDir) {
|
|
6015
|
-
return
|
|
6075
|
+
return join4(configDir, TRUST_FILE);
|
|
6016
6076
|
}
|
|
6017
6077
|
function loadHookTrustStore(configDir) {
|
|
6018
6078
|
const file = hookTrustPath(configDir);
|
|
6019
|
-
if (!
|
|
6079
|
+
if (!existsSync7(file)) return { records: [] };
|
|
6020
6080
|
try {
|
|
6021
|
-
const parsed = JSON.parse(
|
|
6081
|
+
const parsed = JSON.parse(readFileSync6(file, "utf-8"));
|
|
6022
6082
|
return { records: Array.isArray(parsed.records) ? parsed.records : [] };
|
|
6023
6083
|
} catch {
|
|
6024
6084
|
return { records: [] };
|
|
@@ -6544,8 +6604,8 @@ var theme = new Proxy(DARK_THEME, {
|
|
|
6544
6604
|
});
|
|
6545
6605
|
|
|
6546
6606
|
// src/diagnostics/tool-stats.ts
|
|
6547
|
-
import { existsSync as
|
|
6548
|
-
import { join as
|
|
6607
|
+
import { existsSync as existsSync8, readFileSync as readFileSync7, mkdirSync as mkdirSync5 } from "fs";
|
|
6608
|
+
import { join as join5, dirname as dirname3 } from "path";
|
|
6549
6609
|
import { homedir as homedir3 } from "os";
|
|
6550
6610
|
var STATS_FILE_NAME = "tool-stats.json";
|
|
6551
6611
|
var STATS_VERSION = 1;
|
|
@@ -6555,16 +6615,16 @@ var dirty = false;
|
|
|
6555
6615
|
var pendingWrites = 0;
|
|
6556
6616
|
var configDirOverride;
|
|
6557
6617
|
function statsFilePath() {
|
|
6558
|
-
const base = configDirOverride ??
|
|
6559
|
-
return
|
|
6618
|
+
const base = configDirOverride ?? join5(homedir3(), CONFIG_DIR_NAME);
|
|
6619
|
+
return join5(base, STATS_FILE_NAME);
|
|
6560
6620
|
}
|
|
6561
6621
|
function load() {
|
|
6562
6622
|
const path3 = statsFilePath();
|
|
6563
|
-
if (!
|
|
6623
|
+
if (!existsSync8(path3)) {
|
|
6564
6624
|
return { version: STATS_VERSION, startedAt: (/* @__PURE__ */ new Date()).toISOString(), entries: {} };
|
|
6565
6625
|
}
|
|
6566
6626
|
try {
|
|
6567
|
-
const raw = JSON.parse(
|
|
6627
|
+
const raw = JSON.parse(readFileSync7(path3, "utf-8"));
|
|
6568
6628
|
if (raw.version !== STATS_VERSION || !raw.entries) {
|
|
6569
6629
|
return { version: STATS_VERSION, startedAt: (/* @__PURE__ */ new Date()).toISOString(), entries: {} };
|
|
6570
6630
|
}
|
|
@@ -6622,7 +6682,7 @@ function flush() {
|
|
|
6622
6682
|
if (!dirty || state === null) return;
|
|
6623
6683
|
const path3 = statsFilePath();
|
|
6624
6684
|
try {
|
|
6625
|
-
|
|
6685
|
+
mkdirSync5(dirname3(path3), { recursive: true });
|
|
6626
6686
|
atomicWriteFileSync(path3, JSON.stringify(state, null, 2));
|
|
6627
6687
|
dirty = false;
|
|
6628
6688
|
pendingWrites = 0;
|
|
@@ -6646,8 +6706,8 @@ function getTopUsedTools(limit = 5) {
|
|
|
6646
6706
|
}
|
|
6647
6707
|
|
|
6648
6708
|
// src/tools/action-classifier.ts
|
|
6649
|
-
import { existsSync as
|
|
6650
|
-
import { isAbsolute as isAbsolute2, join as
|
|
6709
|
+
import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
|
|
6710
|
+
import { isAbsolute as isAbsolute2, join as join6, resolve as resolve4 } from "path";
|
|
6651
6711
|
var RECENT_DENIED_LIMIT = 50;
|
|
6652
6712
|
var recentlyDenied = [];
|
|
6653
6713
|
function recordRecentlyDeniedAutoAction(call, classification) {
|
|
@@ -6688,10 +6748,10 @@ function splitShellWords(command) {
|
|
|
6688
6748
|
return out.filter(Boolean);
|
|
6689
6749
|
}
|
|
6690
6750
|
function readPackageManifest(root) {
|
|
6691
|
-
const p =
|
|
6692
|
-
if (!
|
|
6751
|
+
const p = join6(root, "package.json");
|
|
6752
|
+
if (!existsSync9(p)) return null;
|
|
6693
6753
|
try {
|
|
6694
|
-
return JSON.parse(
|
|
6754
|
+
return JSON.parse(readFileSync8(p, "utf-8"));
|
|
6695
6755
|
} catch {
|
|
6696
6756
|
return null;
|
|
6697
6757
|
}
|
|
@@ -6707,10 +6767,10 @@ function manifestHasDependency(root, name) {
|
|
|
6707
6767
|
}
|
|
6708
6768
|
function lockfileMentions(root, name) {
|
|
6709
6769
|
for (const file of ["package-lock.json", "pnpm-lock.yaml", "yarn.lock"]) {
|
|
6710
|
-
const p =
|
|
6711
|
-
if (!
|
|
6770
|
+
const p = join6(root, file);
|
|
6771
|
+
if (!existsSync9(p)) continue;
|
|
6712
6772
|
try {
|
|
6713
|
-
if (
|
|
6773
|
+
if (readFileSync8(p, "utf-8").includes(name)) return true;
|
|
6714
6774
|
} catch {
|
|
6715
6775
|
}
|
|
6716
6776
|
}
|
|
@@ -7274,10 +7334,10 @@ var ToolExecutor = class {
|
|
|
7274
7334
|
const filePath = String(call.arguments["path"] ?? "");
|
|
7275
7335
|
const newContent = String(call.arguments["content"] ?? "");
|
|
7276
7336
|
if (!filePath) return;
|
|
7277
|
-
if (
|
|
7337
|
+
if (existsSync10(filePath)) {
|
|
7278
7338
|
let oldContent;
|
|
7279
7339
|
try {
|
|
7280
|
-
oldContent =
|
|
7340
|
+
oldContent = readFileSync9(filePath, "utf-8");
|
|
7281
7341
|
} catch {
|
|
7282
7342
|
return;
|
|
7283
7343
|
}
|
|
@@ -7300,7 +7360,7 @@ var ToolExecutor = class {
|
|
|
7300
7360
|
}
|
|
7301
7361
|
} else if (call.name === "edit_file") {
|
|
7302
7362
|
const filePath = String(call.arguments["path"] ?? "");
|
|
7303
|
-
if (!filePath || !
|
|
7363
|
+
if (!filePath || !existsSync10(filePath)) return;
|
|
7304
7364
|
const oldStr = call.arguments["old_str"];
|
|
7305
7365
|
const newStr = call.arguments["new_str"];
|
|
7306
7366
|
if (oldStr !== void 0) {
|
|
@@ -7327,7 +7387,7 @@ var ToolExecutor = class {
|
|
|
7327
7387
|
const to = Number(call.arguments["delete_to_line"] ?? from);
|
|
7328
7388
|
let fileContent;
|
|
7329
7389
|
try {
|
|
7330
|
-
fileContent =
|
|
7390
|
+
fileContent = readFileSync9(filePath, "utf-8");
|
|
7331
7391
|
} catch {
|
|
7332
7392
|
return;
|
|
7333
7393
|
}
|
|
@@ -7562,7 +7622,7 @@ Do NOT split a long document into many write_file(append=true) calls. That patte
|
|
|
7562
7622
|
}
|
|
7563
7623
|
undoStack.push(filePath, `write_file${appendMode ? " (append)" : ""}: ${filePath}`);
|
|
7564
7624
|
fileCheckpoints.snapshot(filePath, ToolExecutor.currentMessageIndex);
|
|
7565
|
-
|
|
7625
|
+
mkdirSync6(dirname4(filePath), { recursive: true });
|
|
7566
7626
|
if (appendMode) {
|
|
7567
7627
|
appendFileSync(filePath, content, encoding);
|
|
7568
7628
|
} else {
|
|
@@ -7582,7 +7642,7 @@ Do NOT split a long document into many write_file(append=true) calls. That patte
|
|
|
7582
7642
|
};
|
|
7583
7643
|
|
|
7584
7644
|
// src/tools/builtin/edit-file.ts
|
|
7585
|
-
import { readFileSync as
|
|
7645
|
+
import { readFileSync as readFileSync10, existsSync as existsSync11 } from "fs";
|
|
7586
7646
|
|
|
7587
7647
|
// src/tools/builtin/patch-apply.ts
|
|
7588
7648
|
function parseUnifiedDiff(patch) {
|
|
@@ -7943,7 +8003,7 @@ Note: Path can be absolute or relative to cwd.`,
|
|
|
7943
8003
|
const filePath = String(args["path"] ?? "");
|
|
7944
8004
|
const encoding = args["encoding"] ?? "utf-8";
|
|
7945
8005
|
if (!filePath) throw new ToolError("edit_file", "path is required");
|
|
7946
|
-
if (!
|
|
8006
|
+
if (!existsSync11(filePath)) throw new ToolError("edit_file", `File not found: ${filePath}`);
|
|
7947
8007
|
const verdict = classifyWritePath(filePath);
|
|
7948
8008
|
if (verdict.sensitive && subAgentGuard.active) {
|
|
7949
8009
|
throw new ToolError(
|
|
@@ -7951,7 +8011,7 @@ Note: Path can be absolute or relative to cwd.`,
|
|
|
7951
8011
|
`Refused: sub-agents cannot edit sensitive paths \u2014 ${verdict.reason}. If this is genuinely needed, ask the parent agent to perform the edit so the user can approve it.`
|
|
7952
8012
|
);
|
|
7953
8013
|
}
|
|
7954
|
-
const original =
|
|
8014
|
+
const original = readFileSync10(filePath, encoding);
|
|
7955
8015
|
if (args["patch"] !== void 0) {
|
|
7956
8016
|
const patchText = String(args["patch"] ?? "");
|
|
7957
8017
|
const stopOnError = args["stop_on_error"] !== false;
|
|
@@ -8109,8 +8169,8 @@ function truncatePreview(str, maxLen = 80) {
|
|
|
8109
8169
|
}
|
|
8110
8170
|
|
|
8111
8171
|
// src/tools/builtin/list-dir.ts
|
|
8112
|
-
import { readdirSync as readdirSync4, statSync as
|
|
8113
|
-
import { join as
|
|
8172
|
+
import { readdirSync as readdirSync4, statSync as statSync4, existsSync as existsSync12 } from "fs";
|
|
8173
|
+
import { join as join7, basename as basename3 } from "path";
|
|
8114
8174
|
var listDirTool = {
|
|
8115
8175
|
definition: {
|
|
8116
8176
|
name: "list_dir",
|
|
@@ -8132,7 +8192,7 @@ var listDirTool = {
|
|
|
8132
8192
|
async execute(args) {
|
|
8133
8193
|
const dirPath = String(args["path"] ?? process.cwd());
|
|
8134
8194
|
const recursive = Boolean(args["recursive"] ?? false);
|
|
8135
|
-
if (!
|
|
8195
|
+
if (!existsSync12(dirPath)) {
|
|
8136
8196
|
const targetName = basename3(dirPath).toLowerCase();
|
|
8137
8197
|
const cwd = process.cwd();
|
|
8138
8198
|
const suggestions = [];
|
|
@@ -8190,11 +8250,11 @@ function listRecursive(basePath, indent, recursive, lines) {
|
|
|
8190
8250
|
if (entry.isDirectory()) {
|
|
8191
8251
|
lines.push(`${indent}\u{1F4C1} ${entry.name}/`);
|
|
8192
8252
|
if (recursive) {
|
|
8193
|
-
listRecursive(
|
|
8253
|
+
listRecursive(join7(basePath, entry.name), indent + " ", true, lines);
|
|
8194
8254
|
}
|
|
8195
8255
|
} else {
|
|
8196
8256
|
try {
|
|
8197
|
-
const stat =
|
|
8257
|
+
const stat = statSync4(join7(basePath, entry.name));
|
|
8198
8258
|
const size = formatSize(stat.size);
|
|
8199
8259
|
lines.push(`${indent}\u{1F4C4} ${entry.name} (${size})`);
|
|
8200
8260
|
} catch {
|
|
@@ -8210,9 +8270,9 @@ function formatSize(bytes) {
|
|
|
8210
8270
|
}
|
|
8211
8271
|
|
|
8212
8272
|
// src/tools/builtin/grep-files.ts
|
|
8213
|
-
import { readdirSync as readdirSync5, readFileSync as
|
|
8273
|
+
import { readdirSync as readdirSync5, readFileSync as readFileSync11, statSync as statSync5, existsSync as existsSync13 } from "fs";
|
|
8214
8274
|
import { readFile } from "fs/promises";
|
|
8215
|
-
import { join as
|
|
8275
|
+
import { join as join8, relative } from "path";
|
|
8216
8276
|
var grepFilesTool = {
|
|
8217
8277
|
definition: {
|
|
8218
8278
|
name: "grep_files",
|
|
@@ -8263,7 +8323,7 @@ Supports regex. Automatically skips node_modules, dist, .git directories.`,
|
|
|
8263
8323
|
const contextLines = Math.max(0, Number(args["context_lines"] ?? 0));
|
|
8264
8324
|
const maxResults = Math.max(1, Number(args["max_results"] ?? 50));
|
|
8265
8325
|
if (!pattern) throw new ToolError("grep_files", "pattern is required");
|
|
8266
|
-
if (!
|
|
8326
|
+
if (!existsSync13(rootPath)) throw new ToolError("grep_files", `Path not found: ${rootPath}`);
|
|
8267
8327
|
const MAX_PATTERN_LENGTH = 1e3;
|
|
8268
8328
|
if (pattern.length > MAX_PATTERN_LENGTH) {
|
|
8269
8329
|
throw new ToolError("grep_files", `Pattern too long (${pattern.length} chars, max ${MAX_PATTERN_LENGTH}). Use a shorter pattern.`);
|
|
@@ -8276,7 +8336,7 @@ Supports regex. Automatically skips node_modules, dist, .git directories.`,
|
|
|
8276
8336
|
regex = new RegExp(escaped, ignoreCase ? "gi" : "g");
|
|
8277
8337
|
}
|
|
8278
8338
|
const results = [];
|
|
8279
|
-
const stat =
|
|
8339
|
+
const stat = statSync5(rootPath);
|
|
8280
8340
|
if (stat.isFile()) {
|
|
8281
8341
|
searchInFile(rootPath, rootPath, regex, contextLines, maxResults, results);
|
|
8282
8342
|
} else {
|
|
@@ -8357,13 +8417,13 @@ function collectFilePaths(rootPath, filePattern, maxFiles) {
|
|
|
8357
8417
|
if (paths.length >= maxFiles) return;
|
|
8358
8418
|
if (entry.isDirectory()) {
|
|
8359
8419
|
if (SKIP_DIRS.has(entry.name) || entry.name.startsWith(".")) continue;
|
|
8360
|
-
walk(
|
|
8420
|
+
walk(join8(dirPath, entry.name));
|
|
8361
8421
|
} else if (entry.isFile()) {
|
|
8362
8422
|
if (isBinary(entry.name)) continue;
|
|
8363
8423
|
if (filePattern && !matchesFilePattern(entry.name, filePattern)) continue;
|
|
8364
|
-
const fullPath =
|
|
8424
|
+
const fullPath = join8(dirPath, entry.name);
|
|
8365
8425
|
try {
|
|
8366
|
-
if (
|
|
8426
|
+
if (statSync5(fullPath).size > 1e6) continue;
|
|
8367
8427
|
} catch {
|
|
8368
8428
|
continue;
|
|
8369
8429
|
}
|
|
@@ -8409,14 +8469,14 @@ async function searchInFileAsync(fullPath, displayPath2, regex, contextLines, ma
|
|
|
8409
8469
|
}
|
|
8410
8470
|
function searchInFile(fullPath, displayPath2, regex, contextLines, maxResults, results) {
|
|
8411
8471
|
try {
|
|
8412
|
-
const stat =
|
|
8472
|
+
const stat = statSync5(fullPath);
|
|
8413
8473
|
if (stat.size > 1e6) return;
|
|
8414
8474
|
} catch {
|
|
8415
8475
|
return;
|
|
8416
8476
|
}
|
|
8417
8477
|
let content;
|
|
8418
8478
|
try {
|
|
8419
|
-
content =
|
|
8479
|
+
content = readFileSync11(fullPath, "utf-8");
|
|
8420
8480
|
} catch {
|
|
8421
8481
|
return;
|
|
8422
8482
|
}
|
|
@@ -8447,8 +8507,8 @@ function searchInFile(fullPath, displayPath2, regex, contextLines, maxResults, r
|
|
|
8447
8507
|
}
|
|
8448
8508
|
|
|
8449
8509
|
// src/tools/builtin/glob-files.ts
|
|
8450
|
-
import { readdirSync as readdirSync6, statSync as
|
|
8451
|
-
import { join as
|
|
8510
|
+
import { readdirSync as readdirSync6, statSync as statSync6, existsSync as existsSync14 } from "fs";
|
|
8511
|
+
import { join as join9, relative as relative2, basename as basename4 } from "path";
|
|
8452
8512
|
var globFilesTool = {
|
|
8453
8513
|
definition: {
|
|
8454
8514
|
name: "glob_files",
|
|
@@ -8481,7 +8541,7 @@ Results sorted by most recent modification time. Automatically skips node_module
|
|
|
8481
8541
|
const rootPath = String(args["path"] ?? process.cwd());
|
|
8482
8542
|
const maxResults = Math.max(1, Number(args["max_results"] ?? 100));
|
|
8483
8543
|
if (!pattern) throw new ToolError("glob_files", "pattern is required");
|
|
8484
|
-
if (!
|
|
8544
|
+
if (!existsSync14(rootPath)) throw new ToolError("glob_files", `Path not found: ${rootPath}`);
|
|
8485
8545
|
const regex = globToRegex(pattern);
|
|
8486
8546
|
const matches = [];
|
|
8487
8547
|
collectMatchingFiles(rootPath, rootPath, regex, matches, maxResults);
|
|
@@ -8558,7 +8618,7 @@ function collectMatchingFiles(dirPath, rootPath, regex, results, maxResults) {
|
|
|
8558
8618
|
}
|
|
8559
8619
|
for (const entry of entries) {
|
|
8560
8620
|
if (results.length >= maxResults) break;
|
|
8561
|
-
const fullPath =
|
|
8621
|
+
const fullPath = join9(dirPath, entry.name);
|
|
8562
8622
|
if (entry.isDirectory()) {
|
|
8563
8623
|
if (SKIP_DIRS2.has(entry.name) || entry.name.startsWith(".")) continue;
|
|
8564
8624
|
collectMatchingFiles(fullPath, rootPath, regex, results, maxResults);
|
|
@@ -8566,7 +8626,7 @@ function collectMatchingFiles(dirPath, rootPath, regex, results, maxResults) {
|
|
|
8566
8626
|
const relPath = relative2(rootPath, fullPath).replace(/\\/g, "/");
|
|
8567
8627
|
if (regex.test(relPath) || regex.test(basename4(relPath))) {
|
|
8568
8628
|
try {
|
|
8569
|
-
const stat =
|
|
8629
|
+
const stat = statSync6(fullPath);
|
|
8570
8630
|
results.push({ relPath, absPath: fullPath, mtime: stat.mtimeMs });
|
|
8571
8631
|
} catch {
|
|
8572
8632
|
results.push({ relPath, absPath: fullPath, mtime: 0 });
|
|
@@ -9298,16 +9358,16 @@ ${preamble}`;
|
|
|
9298
9358
|
}
|
|
9299
9359
|
|
|
9300
9360
|
// src/tools/builtin/save-last-response.ts
|
|
9301
|
-
import { mkdirSync as
|
|
9302
|
-
import { dirname as
|
|
9361
|
+
import { mkdirSync as mkdirSync7, unlinkSync as unlinkSync4, rmdirSync as rmdirSync2 } from "fs";
|
|
9362
|
+
import { dirname as dirname5 } from "path";
|
|
9303
9363
|
var lastResponseStore = { content: "" };
|
|
9304
9364
|
function cleanupRejectedTeeFile(filePath) {
|
|
9305
9365
|
try {
|
|
9306
|
-
|
|
9366
|
+
unlinkSync4(filePath);
|
|
9307
9367
|
} catch {
|
|
9308
9368
|
}
|
|
9309
9369
|
try {
|
|
9310
|
-
rmdirSync2(
|
|
9370
|
+
rmdirSync2(dirname5(filePath));
|
|
9311
9371
|
} catch {
|
|
9312
9372
|
}
|
|
9313
9373
|
}
|
|
@@ -9348,7 +9408,7 @@ Any of these triggers means use save_last_response, NOT write_file:
|
|
|
9348
9408
|
throw new ToolError("save_last_response", "No content to save: AI has not produced any response yet, or the last response was empty.");
|
|
9349
9409
|
}
|
|
9350
9410
|
undoStack.push(filePath, `save_last_response: ${filePath}`);
|
|
9351
|
-
|
|
9411
|
+
mkdirSync7(dirname5(filePath), { recursive: true });
|
|
9352
9412
|
atomicWriteFileSync(filePath, content);
|
|
9353
9413
|
const lines = content.split("\n").length;
|
|
9354
9414
|
return `File saved: ${filePath} (${lines} lines, ${content.length} bytes)`;
|
|
@@ -9356,18 +9416,18 @@ Any of these triggers means use save_last_response, NOT write_file:
|
|
|
9356
9416
|
};
|
|
9357
9417
|
|
|
9358
9418
|
// src/tools/builtin/save-memory.ts
|
|
9359
|
-
import { join as
|
|
9419
|
+
import { join as join12 } from "path";
|
|
9360
9420
|
import { homedir as homedir5 } from "os";
|
|
9361
9421
|
|
|
9362
9422
|
// src/memory/persistent-memory.ts
|
|
9363
|
-
import { existsSync as
|
|
9423
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync8, readFileSync as readFileSync12 } from "fs";
|
|
9364
9424
|
import { randomUUID, createHash as createHash2 } from "crypto";
|
|
9365
|
-
import { dirname as
|
|
9425
|
+
import { dirname as dirname6, join as join11, resolve as resolve6 } from "path";
|
|
9366
9426
|
|
|
9367
9427
|
// src/tools/git-context.ts
|
|
9368
9428
|
import { execSync as execSync2 } from "child_process";
|
|
9369
|
-
import { existsSync as
|
|
9370
|
-
import { join as
|
|
9429
|
+
import { existsSync as existsSync15 } from "fs";
|
|
9430
|
+
import { join as join10 } from "path";
|
|
9371
9431
|
function runGit(cmd, cwd) {
|
|
9372
9432
|
try {
|
|
9373
9433
|
return execSync2(`git ${cmd}`, {
|
|
@@ -9384,7 +9444,7 @@ function getGitRoot(cwd = process.cwd()) {
|
|
|
9384
9444
|
return runGit("rev-parse --show-toplevel", cwd);
|
|
9385
9445
|
}
|
|
9386
9446
|
function getGitContext(cwd = process.cwd()) {
|
|
9387
|
-
if (!
|
|
9447
|
+
if (!existsSync15(join10(cwd, ".git"))) {
|
|
9388
9448
|
const result = runGit("rev-parse --git-dir", cwd);
|
|
9389
9449
|
if (!result) return null;
|
|
9390
9450
|
}
|
|
@@ -9456,10 +9516,10 @@ function formatGitContextForPrompt(ctx) {
|
|
|
9456
9516
|
|
|
9457
9517
|
// src/memory/persistent-memory.ts
|
|
9458
9518
|
function memoryStorePath(configDir) {
|
|
9459
|
-
return
|
|
9519
|
+
return join11(configDir, MEMORY_STORE_FILE_NAME);
|
|
9460
9520
|
}
|
|
9461
9521
|
function memoryMarkdownPath(configDir) {
|
|
9462
|
-
return
|
|
9522
|
+
return join11(configDir, MEMORY_FILE_NAME);
|
|
9463
9523
|
}
|
|
9464
9524
|
function getProjectKey(cwd = process.cwd()) {
|
|
9465
9525
|
return resolve6(getGitRoot(cwd) ?? cwd);
|
|
@@ -9495,8 +9555,8 @@ function normalizeEntry(value) {
|
|
|
9495
9555
|
}
|
|
9496
9556
|
function legacyEntriesFromMarkdown(configDir) {
|
|
9497
9557
|
const file = memoryMarkdownPath(configDir);
|
|
9498
|
-
if (!
|
|
9499
|
-
const content =
|
|
9558
|
+
if (!existsSync16(file)) return [];
|
|
9559
|
+
const content = readFileSync12(file, "utf-8").trim();
|
|
9500
9560
|
if (!content) return [];
|
|
9501
9561
|
const parts = content.split(/\n(?=##\s+\d{4}-\d{2}-\d{2})/g);
|
|
9502
9562
|
const chunks = parts.length > 1 ? parts : [content];
|
|
@@ -9512,8 +9572,8 @@ ${chunk}`).digest("hex").slice(0, 12);
|
|
|
9512
9572
|
}
|
|
9513
9573
|
function loadMemoryEntries(configDir) {
|
|
9514
9574
|
const file = memoryStorePath(configDir);
|
|
9515
|
-
if (!
|
|
9516
|
-
const text =
|
|
9575
|
+
if (!existsSync16(file)) return legacyEntriesFromMarkdown(configDir);
|
|
9576
|
+
const text = readFileSync12(file, "utf-8").trim();
|
|
9517
9577
|
if (!text) return [];
|
|
9518
9578
|
const entries = [];
|
|
9519
9579
|
for (const line of text.split("\n")) {
|
|
@@ -9528,7 +9588,7 @@ function loadMemoryEntries(configDir) {
|
|
|
9528
9588
|
return entries;
|
|
9529
9589
|
}
|
|
9530
9590
|
function saveMemoryEntries(configDir, entries) {
|
|
9531
|
-
|
|
9591
|
+
mkdirSync8(configDir, { recursive: true });
|
|
9532
9592
|
const jsonl = entries.map((entry) => JSON.stringify(entry)).join("\n");
|
|
9533
9593
|
atomicWriteFileSync(memoryStorePath(configDir), jsonl ? jsonl + "\n" : "");
|
|
9534
9594
|
syncLegacyMarkdown(configDir, entries);
|
|
@@ -9603,7 +9663,7 @@ function findUnique(entries, idPrefix) {
|
|
|
9603
9663
|
return matches[0];
|
|
9604
9664
|
}
|
|
9605
9665
|
function syncLegacyMarkdown(configDir, entries = loadMemoryEntries(configDir)) {
|
|
9606
|
-
|
|
9666
|
+
mkdirSync8(dirname6(memoryMarkdownPath(configDir)), { recursive: true });
|
|
9607
9667
|
const active = entries.filter((entry) => entry.approved && !isMemoryExpired(entry));
|
|
9608
9668
|
const markdown = active.map((entry) => {
|
|
9609
9669
|
const date = entry.createdAt.replace("T", " ").slice(0, 19);
|
|
@@ -9646,7 +9706,7 @@ ${entry.content}`;
|
|
|
9646
9706
|
|
|
9647
9707
|
// src/tools/builtin/save-memory.ts
|
|
9648
9708
|
function getConfigDir() {
|
|
9649
|
-
return
|
|
9709
|
+
return join12(homedir5(), CONFIG_DIR_NAME);
|
|
9650
9710
|
}
|
|
9651
9711
|
function parseScope(value) {
|
|
9652
9712
|
return value === "personal" || value === "project" || value === "session" || value === "team" ? value : void 0;
|
|
@@ -9980,13 +10040,13 @@ function formatResults2(query, data, _requested) {
|
|
|
9980
10040
|
}
|
|
9981
10041
|
|
|
9982
10042
|
// src/agents/agent-config.ts
|
|
9983
|
-
import { existsSync as
|
|
9984
|
-
import { join as
|
|
10043
|
+
import { existsSync as existsSync18, readdirSync as readdirSync8, readFileSync as readFileSync14 } from "fs";
|
|
10044
|
+
import { join as join14 } from "path";
|
|
9985
10045
|
import { homedir as homedir6 } from "os";
|
|
9986
10046
|
|
|
9987
10047
|
// src/plugins/plugin-manager.ts
|
|
9988
|
-
import { existsSync as
|
|
9989
|
-
import { basename as basename5, dirname as
|
|
10048
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync9, readdirSync as readdirSync7, readFileSync as readFileSync13, cpSync, rmSync, statSync as statSync7 } from "fs";
|
|
10049
|
+
import { basename as basename5, dirname as dirname7, join as join13, resolve as resolve7 } from "path";
|
|
9990
10050
|
import { createHash as createHash3 } from "crypto";
|
|
9991
10051
|
import { z as z2 } from "zod";
|
|
9992
10052
|
var MANIFEST_RELATIVE = ".aicli-plugin/plugin.json";
|
|
@@ -10046,48 +10106,48 @@ var PluginManifestSchema = z2.object({
|
|
|
10046
10106
|
permissionHints: z2.array(z2.string()).default([])
|
|
10047
10107
|
}).strict();
|
|
10048
10108
|
function pluginRoot(configDir) {
|
|
10049
|
-
return
|
|
10109
|
+
return join13(configDir, PLUGINS_DIR_NAME);
|
|
10050
10110
|
}
|
|
10051
10111
|
function pluginManifestPath(pluginDir) {
|
|
10052
|
-
return
|
|
10112
|
+
return join13(pluginDir, MANIFEST_RELATIVE);
|
|
10053
10113
|
}
|
|
10054
10114
|
function statePath(configDir) {
|
|
10055
|
-
return
|
|
10115
|
+
return join13(pluginRoot(configDir), STATE_FILE);
|
|
10056
10116
|
}
|
|
10057
10117
|
function loadState(configDir) {
|
|
10058
10118
|
const file = statePath(configDir);
|
|
10059
|
-
if (!
|
|
10119
|
+
if (!existsSync17(file)) return { version: 1, plugins: [] };
|
|
10060
10120
|
try {
|
|
10061
|
-
const parsed = JSON.parse(
|
|
10121
|
+
const parsed = JSON.parse(readFileSync13(file, "utf-8"));
|
|
10062
10122
|
return { version: 1, plugins: Array.isArray(parsed.plugins) ? parsed.plugins : [] };
|
|
10063
10123
|
} catch {
|
|
10064
10124
|
return { version: 1, plugins: [] };
|
|
10065
10125
|
}
|
|
10066
10126
|
}
|
|
10067
10127
|
function saveState(configDir, state2) {
|
|
10068
|
-
|
|
10128
|
+
mkdirSync9(pluginRoot(configDir), { recursive: true });
|
|
10069
10129
|
atomicWriteFileSync(statePath(configDir), JSON.stringify(state2, null, 2));
|
|
10070
10130
|
}
|
|
10071
10131
|
function hashPluginManifest(manifestPath) {
|
|
10072
|
-
return createHash3("sha256").update(
|
|
10132
|
+
return createHash3("sha256").update(readFileSync13(manifestPath)).digest("hex");
|
|
10073
10133
|
}
|
|
10074
10134
|
function readPluginManifest(manifestPath) {
|
|
10075
|
-
return PluginManifestSchema.parse(JSON.parse(
|
|
10135
|
+
return PluginManifestSchema.parse(JSON.parse(readFileSync13(manifestPath, "utf-8")));
|
|
10076
10136
|
}
|
|
10077
10137
|
function findPluginSourceDir(inputPath) {
|
|
10078
10138
|
const abs = resolve7(inputPath);
|
|
10079
|
-
const direct =
|
|
10080
|
-
if (
|
|
10081
|
-
const nested =
|
|
10082
|
-
if (
|
|
10139
|
+
const direct = statSync7(abs).isDirectory() ? abs : dirname7(abs);
|
|
10140
|
+
if (existsSync17(pluginManifestPath(direct))) return direct;
|
|
10141
|
+
const nested = join13(direct, ".aicli-plugin");
|
|
10142
|
+
if (existsSync17(join13(nested, "plugin.json"))) return direct;
|
|
10083
10143
|
throw new Error(`plugin manifest not found: ${MANIFEST_RELATIVE}`);
|
|
10084
10144
|
}
|
|
10085
10145
|
function installPlugin(configDir, inputPath) {
|
|
10086
10146
|
const sourceDir = findPluginSourceDir(inputPath);
|
|
10087
10147
|
const manifest = readPluginManifest(pluginManifestPath(sourceDir));
|
|
10088
|
-
const targetDir =
|
|
10089
|
-
|
|
10090
|
-
if (
|
|
10148
|
+
const targetDir = join13(pluginRoot(configDir), manifest.name);
|
|
10149
|
+
mkdirSync9(pluginRoot(configDir), { recursive: true });
|
|
10150
|
+
if (existsSync17(targetDir)) rmSync(targetDir, { recursive: true, force: true });
|
|
10091
10151
|
cpSync(sourceDir, targetDir, { recursive: true });
|
|
10092
10152
|
const manifestPath = pluginManifestPath(targetDir);
|
|
10093
10153
|
const hash = hashPluginManifest(manifestPath);
|
|
@@ -10145,18 +10205,18 @@ function getInstalledPlugin(configDir, name) {
|
|
|
10145
10205
|
}
|
|
10146
10206
|
function listInstalledPlugins(configDir) {
|
|
10147
10207
|
const root = pluginRoot(configDir);
|
|
10148
|
-
if (!
|
|
10208
|
+
if (!existsSync17(root)) return [];
|
|
10149
10209
|
const state2 = loadState(configDir);
|
|
10150
10210
|
const out = [];
|
|
10151
10211
|
for (const entry of readdirSync7(root)) {
|
|
10152
|
-
const dir =
|
|
10212
|
+
const dir = join13(root, entry);
|
|
10153
10213
|
try {
|
|
10154
|
-
if (!
|
|
10214
|
+
if (!statSync7(dir).isDirectory()) continue;
|
|
10155
10215
|
} catch {
|
|
10156
10216
|
continue;
|
|
10157
10217
|
}
|
|
10158
10218
|
const manifestPath = pluginManifestPath(dir);
|
|
10159
|
-
if (!
|
|
10219
|
+
if (!existsSync17(manifestPath)) continue;
|
|
10160
10220
|
try {
|
|
10161
10221
|
const manifest = readPluginManifest(manifestPath);
|
|
10162
10222
|
const hash = hashPluginManifest(manifestPath);
|
|
@@ -10196,7 +10256,7 @@ function uniqueDirs(plugin, rels) {
|
|
|
10196
10256
|
for (const rel of rels) {
|
|
10197
10257
|
const abs = resolve7(plugin.dir, rel);
|
|
10198
10258
|
if (!abs.startsWith(resolve7(plugin.dir))) continue;
|
|
10199
|
-
if (
|
|
10259
|
+
if (existsSync17(abs)) dirs.add(statSync7(abs).isDirectory() ? abs : dirname7(abs));
|
|
10200
10260
|
}
|
|
10201
10261
|
return [...dirs];
|
|
10202
10262
|
}
|
|
@@ -10359,13 +10419,13 @@ function parseAgentConfig(raw, source, path3) {
|
|
|
10359
10419
|
return cfg;
|
|
10360
10420
|
}
|
|
10361
10421
|
function loadAgentDir(dir, source) {
|
|
10362
|
-
if (!
|
|
10422
|
+
if (!existsSync18(dir)) return [];
|
|
10363
10423
|
const out = [];
|
|
10364
10424
|
for (const file of readdirSync8(dir)) {
|
|
10365
10425
|
if (!file.endsWith(".json")) continue;
|
|
10366
|
-
const path3 =
|
|
10426
|
+
const path3 = join14(dir, file);
|
|
10367
10427
|
try {
|
|
10368
|
-
const parsed = JSON.parse(
|
|
10428
|
+
const parsed = JSON.parse(readFileSync14(path3, "utf-8"));
|
|
10369
10429
|
const cfg = parseAgentConfig(parsed, source, path3);
|
|
10370
10430
|
if (cfg) out.push(cfg);
|
|
10371
10431
|
} catch {
|
|
@@ -10375,8 +10435,8 @@ function loadAgentDir(dir, source) {
|
|
|
10375
10435
|
}
|
|
10376
10436
|
function getAgentDirs(configDir, cwd = process.cwd()) {
|
|
10377
10437
|
return {
|
|
10378
|
-
user:
|
|
10379
|
-
project:
|
|
10438
|
+
user: join14(configDir ?? join14(homedir6(), CONFIG_DIR_NAME), "agents"),
|
|
10439
|
+
project: join14(cwd, CONFIG_DIR_NAME, "agents")
|
|
10380
10440
|
};
|
|
10381
10441
|
}
|
|
10382
10442
|
function listAgentConfigs(configDir, cwd = process.cwd()) {
|
|
@@ -11091,14 +11151,14 @@ var taskStopTool = {
|
|
|
11091
11151
|
|
|
11092
11152
|
// src/tools/builtin/git-tools.ts
|
|
11093
11153
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
11094
|
-
import { existsSync as
|
|
11095
|
-
import { join as
|
|
11154
|
+
import { existsSync as existsSync19 } from "fs";
|
|
11155
|
+
import { join as join15 } from "path";
|
|
11096
11156
|
function assertGitRepo(cwd) {
|
|
11097
11157
|
let dir = cwd;
|
|
11098
11158
|
const root = dir.split(/[\\/]/)[0] + (dir.includes("\\") ? "\\" : "/");
|
|
11099
11159
|
while (dir && dir !== root) {
|
|
11100
|
-
if (
|
|
11101
|
-
const parent =
|
|
11160
|
+
if (existsSync19(join15(dir, ".git"))) return;
|
|
11161
|
+
const parent = join15(dir, "..");
|
|
11102
11162
|
if (parent === dir) break;
|
|
11103
11163
|
dir = parent;
|
|
11104
11164
|
}
|
|
@@ -11363,7 +11423,7 @@ ${commitOutput.trim()}`;
|
|
|
11363
11423
|
};
|
|
11364
11424
|
|
|
11365
11425
|
// src/tools/builtin/notebook-edit.ts
|
|
11366
|
-
import { readFileSync as
|
|
11426
|
+
import { readFileSync as readFileSync15, existsSync as existsSync20 } from "fs";
|
|
11367
11427
|
import { writeFile } from "fs/promises";
|
|
11368
11428
|
import { resolve as resolve8, extname as extname2 } from "path";
|
|
11369
11429
|
var notebookEditTool = {
|
|
@@ -11418,10 +11478,10 @@ var notebookEditTool = {
|
|
|
11418
11478
|
if (extname2(absPath).toLowerCase() !== ".ipynb") {
|
|
11419
11479
|
throw new ToolError("notebook_edit", "path must point to a .ipynb file");
|
|
11420
11480
|
}
|
|
11421
|
-
if (!
|
|
11481
|
+
if (!existsSync20(absPath)) {
|
|
11422
11482
|
throw new ToolError("notebook_edit", `Notebook not found: ${filePath}`);
|
|
11423
11483
|
}
|
|
11424
|
-
const raw =
|
|
11484
|
+
const raw = readFileSync15(absPath, "utf-8");
|
|
11425
11485
|
const nb = parseNotebook(raw);
|
|
11426
11486
|
const cellIdx0 = cellIndexRaw - 1;
|
|
11427
11487
|
undoStack.push(absPath, `notebook_edit (${action}): ${filePath}`);
|
|
@@ -11838,8 +11898,8 @@ function estimateToolDefinitionTokens(def) {
|
|
|
11838
11898
|
|
|
11839
11899
|
// src/tools/registry.ts
|
|
11840
11900
|
import { pathToFileURL } from "url";
|
|
11841
|
-
import { existsSync as
|
|
11842
|
-
import { join as
|
|
11901
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync10, readdirSync as readdirSync9 } from "fs";
|
|
11902
|
+
import { join as join16 } from "path";
|
|
11843
11903
|
var ToolRegistry = class {
|
|
11844
11904
|
tools = /* @__PURE__ */ new Map();
|
|
11845
11905
|
pluginToolNames = /* @__PURE__ */ new Set();
|
|
@@ -12000,9 +12060,9 @@ var ToolRegistry = class {
|
|
|
12000
12060
|
* Returns the number of successfully loaded plugins.
|
|
12001
12061
|
*/
|
|
12002
12062
|
async loadPlugins(pluginsDir, allowPlugins = false) {
|
|
12003
|
-
if (!
|
|
12063
|
+
if (!existsSync21(pluginsDir)) {
|
|
12004
12064
|
try {
|
|
12005
|
-
|
|
12065
|
+
mkdirSync10(pluginsDir, { recursive: true });
|
|
12006
12066
|
} catch {
|
|
12007
12067
|
}
|
|
12008
12068
|
return 0;
|
|
@@ -12026,12 +12086,12 @@ var ToolRegistry = class {
|
|
|
12026
12086
|
process.stderr.write(
|
|
12027
12087
|
`
|
|
12028
12088
|
[plugins] \u26A0 Loading ${files.length} plugin(s) with FULL system privileges:
|
|
12029
|
-
` + files.map((f) => ` + ${
|
|
12089
|
+
` + files.map((f) => ` + ${join16(pluginsDir, f)}`).join("\n") + "\n\n"
|
|
12030
12090
|
);
|
|
12031
12091
|
let loaded = 0;
|
|
12032
12092
|
for (const file of files) {
|
|
12033
12093
|
try {
|
|
12034
|
-
const fileUrl = pathToFileURL(
|
|
12094
|
+
const fileUrl = pathToFileURL(join16(pluginsDir, file)).href;
|
|
12035
12095
|
const mod = await import(fileUrl);
|
|
12036
12096
|
const tool = mod.tool ?? mod.default?.tool ?? mod.default;
|
|
12037
12097
|
if (!tool || typeof tool.execute !== "function" || !tool.definition?.name) {
|
|
@@ -12577,11 +12637,11 @@ var McpManager = class {
|
|
|
12577
12637
|
};
|
|
12578
12638
|
|
|
12579
12639
|
// src/skills/manager.ts
|
|
12580
|
-
import { existsSync as
|
|
12581
|
-
import { join as
|
|
12640
|
+
import { existsSync as existsSync22, readdirSync as readdirSync10, mkdirSync as mkdirSync11, statSync as statSync8 } from "fs";
|
|
12641
|
+
import { join as join17 } from "path";
|
|
12582
12642
|
|
|
12583
12643
|
// src/skills/types.ts
|
|
12584
|
-
import { readFileSync as
|
|
12644
|
+
import { readFileSync as readFileSync16 } from "fs";
|
|
12585
12645
|
import { basename as basename6 } from "path";
|
|
12586
12646
|
function parseSimpleYaml(yaml) {
|
|
12587
12647
|
const result = {};
|
|
@@ -12603,7 +12663,7 @@ function parseYamlArray(value) {
|
|
|
12603
12663
|
function parseSkillFile(filePath) {
|
|
12604
12664
|
let raw;
|
|
12605
12665
|
try {
|
|
12606
|
-
raw =
|
|
12666
|
+
raw = readFileSync16(filePath, "utf-8");
|
|
12607
12667
|
} catch {
|
|
12608
12668
|
return null;
|
|
12609
12669
|
}
|
|
@@ -12651,10 +12711,10 @@ var SkillManager = class {
|
|
|
12651
12711
|
return this.skills.size;
|
|
12652
12712
|
}
|
|
12653
12713
|
loadSkillDir(dir, createIfMissing) {
|
|
12654
|
-
if (!
|
|
12714
|
+
if (!existsSync22(dir)) {
|
|
12655
12715
|
if (createIfMissing) {
|
|
12656
12716
|
try {
|
|
12657
|
-
|
|
12717
|
+
mkdirSync11(dir, { recursive: true });
|
|
12658
12718
|
} catch {
|
|
12659
12719
|
}
|
|
12660
12720
|
}
|
|
@@ -12668,14 +12728,14 @@ var SkillManager = class {
|
|
|
12668
12728
|
}
|
|
12669
12729
|
for (const entry of entries) {
|
|
12670
12730
|
let filePath;
|
|
12671
|
-
const fullPath =
|
|
12731
|
+
const fullPath = join17(dir, entry);
|
|
12672
12732
|
if (entry.endsWith(".md")) {
|
|
12673
12733
|
filePath = fullPath;
|
|
12674
12734
|
} else {
|
|
12675
12735
|
try {
|
|
12676
|
-
if (
|
|
12677
|
-
const skillMd =
|
|
12678
|
-
if (
|
|
12736
|
+
if (statSync8(fullPath).isDirectory()) {
|
|
12737
|
+
const skillMd = join17(fullPath, "SKILL.md");
|
|
12738
|
+
if (existsSync22(skillMd)) filePath = skillMd;
|
|
12679
12739
|
else continue;
|
|
12680
12740
|
} else {
|
|
12681
12741
|
continue;
|
|
@@ -12734,7 +12794,7 @@ var SkillManager = class {
|
|
|
12734
12794
|
// src/web/tool-executor-web.ts
|
|
12735
12795
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
12736
12796
|
import { tmpdir as tmpdir2 } from "os";
|
|
12737
|
-
import { existsSync as
|
|
12797
|
+
import { existsSync as existsSync23, readFileSync as readFileSync17 } from "fs";
|
|
12738
12798
|
var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
12739
12799
|
constructor(registry, ws) {
|
|
12740
12800
|
this.registry = registry;
|
|
@@ -12873,9 +12933,9 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
12873
12933
|
if (call.name === "write_file") {
|
|
12874
12934
|
const filePath = String(call.arguments["path"] ?? "");
|
|
12875
12935
|
const newContent = String(call.arguments["content"] ?? "");
|
|
12876
|
-
if (filePath &&
|
|
12936
|
+
if (filePath && existsSync23(filePath)) {
|
|
12877
12937
|
try {
|
|
12878
|
-
const old =
|
|
12938
|
+
const old = readFileSync17(filePath, "utf-8");
|
|
12879
12939
|
return renderDiff(old, newContent, { filePath });
|
|
12880
12940
|
} catch {
|
|
12881
12941
|
}
|
|
@@ -13178,16 +13238,16 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
13178
13238
|
};
|
|
13179
13239
|
|
|
13180
13240
|
// src/repl/dev-state.ts
|
|
13181
|
-
import { existsSync as
|
|
13182
|
-
import { join as
|
|
13241
|
+
import { existsSync as existsSync24, readFileSync as readFileSync18, unlinkSync as unlinkSync5, mkdirSync as mkdirSync12 } from "fs";
|
|
13242
|
+
import { join as join18 } from "path";
|
|
13183
13243
|
import { homedir as homedir7 } from "os";
|
|
13184
13244
|
function getDevStatePath() {
|
|
13185
|
-
return
|
|
13245
|
+
return join18(homedir7(), CONFIG_DIR_NAME, DEV_STATE_FILE_NAME);
|
|
13186
13246
|
}
|
|
13187
13247
|
function loadDevState() {
|
|
13188
13248
|
const path3 = getDevStatePath();
|
|
13189
|
-
if (!
|
|
13190
|
-
const content =
|
|
13249
|
+
if (!existsSync24(path3)) return null;
|
|
13250
|
+
const content = readFileSync18(path3, "utf-8").trim();
|
|
13191
13251
|
return content || null;
|
|
13192
13252
|
}
|
|
13193
13253
|
|
|
@@ -13444,8 +13504,8 @@ function autoTrimSessionIfNeeded(session, sizeLimit = SESSION_SIZE_LIMIT) {
|
|
|
13444
13504
|
}
|
|
13445
13505
|
|
|
13446
13506
|
// src/core/context-files.ts
|
|
13447
|
-
import { existsSync as
|
|
13448
|
-
import { join as
|
|
13507
|
+
import { existsSync as existsSync25, readFileSync as readFileSync19 } from "fs";
|
|
13508
|
+
import { join as join19, relative as relative3, resolve as resolve9 } from "path";
|
|
13449
13509
|
function uniqueNonEmpty(values) {
|
|
13450
13510
|
const seen = /* @__PURE__ */ new Set();
|
|
13451
13511
|
const out = [];
|
|
@@ -13475,7 +13535,7 @@ function readContextFile(level, filePath, cwd, maxBytes) {
|
|
|
13475
13535
|
const name = filePath.split(/[\\/]/).pop() ?? filePath;
|
|
13476
13536
|
const shown = displayPath(filePath, cwd);
|
|
13477
13537
|
try {
|
|
13478
|
-
const raw =
|
|
13538
|
+
const raw = readFileSync19(filePath);
|
|
13479
13539
|
const byteCount = raw.byteLength;
|
|
13480
13540
|
if (byteCount === 0) {
|
|
13481
13541
|
return {
|
|
@@ -13522,8 +13582,8 @@ function readContextFile(level, filePath, cwd, maxBytes) {
|
|
|
13522
13582
|
function findFirstContextFile(level, dir, cwd, candidates, maxBytes) {
|
|
13523
13583
|
const skipped = [];
|
|
13524
13584
|
for (const candidate of candidates) {
|
|
13525
|
-
const filePath =
|
|
13526
|
-
if (!
|
|
13585
|
+
const filePath = join19(dir, candidate);
|
|
13586
|
+
if (!existsSync25(filePath)) continue;
|
|
13527
13587
|
const result = readContextFile(level, filePath, cwd, maxBytes);
|
|
13528
13588
|
if (result.skipped) skipped.push(result.skipped);
|
|
13529
13589
|
if (result.layer) return { layer: result.layer, skipped };
|
|
@@ -13550,7 +13610,7 @@ function loadContextFiles(options) {
|
|
|
13550
13610
|
});
|
|
13551
13611
|
return { layers: [], mergedContent: "", skipped, totalChars: 0, totalBytes: 0, maxBytes, candidates };
|
|
13552
13612
|
}
|
|
13553
|
-
if (!
|
|
13613
|
+
if (!existsSync25(filePath)) {
|
|
13554
13614
|
skipped.push({
|
|
13555
13615
|
level: "single",
|
|
13556
13616
|
filePath,
|
|
@@ -13601,8 +13661,8 @@ function loadContextFiles(options) {
|
|
|
13601
13661
|
}
|
|
13602
13662
|
|
|
13603
13663
|
// src/web/session-handler.ts
|
|
13604
|
-
import { existsSync as
|
|
13605
|
-
import { join as
|
|
13664
|
+
import { existsSync as existsSync29, readFileSync as readFileSync22, writeFileSync as writeFileSync3, mkdirSync as mkdirSync14, readdirSync as readdirSync13, statSync as statSync12, createWriteStream } from "fs";
|
|
13665
|
+
import { join as join24, resolve as resolve10, dirname as dirname8 } from "path";
|
|
13606
13666
|
|
|
13607
13667
|
// src/cli/review-prompts.ts
|
|
13608
13668
|
function buildReviewPrompt(diff, gitContextStr, detailed) {
|
|
@@ -13663,8 +13723,8 @@ If no security issues found, state "\u2705 No security vulnerabilities detected"
|
|
|
13663
13723
|
}
|
|
13664
13724
|
|
|
13665
13725
|
// src/repl/commands/project-init.ts
|
|
13666
|
-
import { existsSync as
|
|
13667
|
-
import { join as
|
|
13726
|
+
import { existsSync as existsSync26, readFileSync as readFileSync20, readdirSync as readdirSync11, statSync as statSync9 } from "fs";
|
|
13727
|
+
import { join as join20 } from "path";
|
|
13668
13728
|
var SCAN_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
13669
13729
|
"node_modules",
|
|
13670
13730
|
".git",
|
|
@@ -13708,11 +13768,11 @@ function scanDirTree(dir, maxDepth = 2, maxEntries = 80) {
|
|
|
13708
13768
|
const sorted = filtered.sort((a, b) => {
|
|
13709
13769
|
let aIsDir = false, bIsDir = false;
|
|
13710
13770
|
try {
|
|
13711
|
-
aIsDir =
|
|
13771
|
+
aIsDir = statSync9(join20(d, a)).isDirectory();
|
|
13712
13772
|
} catch {
|
|
13713
13773
|
}
|
|
13714
13774
|
try {
|
|
13715
|
-
bIsDir =
|
|
13775
|
+
bIsDir = statSync9(join20(d, b)).isDirectory();
|
|
13716
13776
|
} catch {
|
|
13717
13777
|
}
|
|
13718
13778
|
if (aIsDir !== bIsDir) return aIsDir ? -1 : 1;
|
|
@@ -13720,12 +13780,12 @@ function scanDirTree(dir, maxDepth = 2, maxEntries = 80) {
|
|
|
13720
13780
|
});
|
|
13721
13781
|
for (let i = 0; i < sorted.length && count < maxEntries; i++) {
|
|
13722
13782
|
const name = sorted[i];
|
|
13723
|
-
const fullPath =
|
|
13783
|
+
const fullPath = join20(d, name);
|
|
13724
13784
|
const isLast = i === sorted.length - 1;
|
|
13725
13785
|
const connector = isLast ? "+-- " : "|-- ";
|
|
13726
13786
|
let isDir;
|
|
13727
13787
|
try {
|
|
13728
|
-
isDir =
|
|
13788
|
+
isDir = statSync9(fullPath).isDirectory();
|
|
13729
13789
|
} catch {
|
|
13730
13790
|
continue;
|
|
13731
13791
|
}
|
|
@@ -13747,7 +13807,7 @@ function scanProject(cwd) {
|
|
|
13747
13807
|
configFiles: [],
|
|
13748
13808
|
directoryStructure: ""
|
|
13749
13809
|
};
|
|
13750
|
-
const check = (file) =>
|
|
13810
|
+
const check = (file) => existsSync26(join20(cwd, file));
|
|
13751
13811
|
const configCandidates = [
|
|
13752
13812
|
"package.json",
|
|
13753
13813
|
"tsconfig.json",
|
|
@@ -13774,7 +13834,7 @@ function scanProject(cwd) {
|
|
|
13774
13834
|
info.type = "node";
|
|
13775
13835
|
info.language = check("tsconfig.json") ? "TypeScript" : "JavaScript";
|
|
13776
13836
|
try {
|
|
13777
|
-
const pkg = JSON.parse(
|
|
13837
|
+
const pkg = JSON.parse(readFileSync20(join20(cwd, "package.json"), "utf-8"));
|
|
13778
13838
|
const scripts = pkg.scripts ?? {};
|
|
13779
13839
|
info.buildCommand = scripts.build ? "npm run build" : void 0;
|
|
13780
13840
|
info.testCommand = scripts.test ? "npm test" : void 0;
|
|
@@ -14248,34 +14308,34 @@ var DiscussionOrchestrator = class {
|
|
|
14248
14308
|
};
|
|
14249
14309
|
|
|
14250
14310
|
// src/diagnostics/doctor-report.ts
|
|
14251
|
-
import { existsSync as
|
|
14252
|
-
import { join as
|
|
14311
|
+
import { existsSync as existsSync28, statSync as statSync11 } from "fs";
|
|
14312
|
+
import { join as join22 } from "path";
|
|
14253
14313
|
import { arch as arch2, platform as platform6, release as release2 } from "os";
|
|
14254
14314
|
|
|
14255
14315
|
// src/diagnostics/crash-log.ts
|
|
14256
14316
|
import {
|
|
14257
|
-
existsSync as
|
|
14258
|
-
mkdirSync as
|
|
14317
|
+
existsSync as existsSync27,
|
|
14318
|
+
mkdirSync as mkdirSync13,
|
|
14259
14319
|
readdirSync as readdirSync12,
|
|
14260
|
-
readFileSync as
|
|
14261
|
-
statSync as
|
|
14262
|
-
unlinkSync as
|
|
14320
|
+
readFileSync as readFileSync21,
|
|
14321
|
+
statSync as statSync10,
|
|
14322
|
+
unlinkSync as unlinkSync6,
|
|
14263
14323
|
writeFileSync as writeFileSync2
|
|
14264
14324
|
} from "fs";
|
|
14265
|
-
import { join as
|
|
14325
|
+
import { join as join21 } from "path";
|
|
14266
14326
|
import { homedir as homedir8, platform as platform5, release, arch } from "os";
|
|
14267
14327
|
var LOGS_DIR_NAME = "logs";
|
|
14268
14328
|
var CRASH_LOG_PREFIX = "crash-";
|
|
14269
14329
|
function getLogsDir(configDir) {
|
|
14270
|
-
const base = configDir ??
|
|
14271
|
-
return
|
|
14330
|
+
const base = configDir ?? join21(homedir8(), CONFIG_DIR_NAME);
|
|
14331
|
+
return join21(base, LOGS_DIR_NAME);
|
|
14272
14332
|
}
|
|
14273
14333
|
function truncate(s, n) {
|
|
14274
14334
|
return s.length <= n ? s : s.slice(0, n) + "\u2026";
|
|
14275
14335
|
}
|
|
14276
14336
|
function listRecentCrashes(limit = 10, configDir) {
|
|
14277
14337
|
const dir = getLogsDir(configDir);
|
|
14278
|
-
if (!
|
|
14338
|
+
if (!existsSync27(dir)) return [];
|
|
14279
14339
|
const files = [];
|
|
14280
14340
|
let names;
|
|
14281
14341
|
try {
|
|
@@ -14285,10 +14345,10 @@ function listRecentCrashes(limit = 10, configDir) {
|
|
|
14285
14345
|
}
|
|
14286
14346
|
for (const name of names) {
|
|
14287
14347
|
if (!name.startsWith(CRASH_LOG_PREFIX) || !name.endsWith(".log")) continue;
|
|
14288
|
-
const full =
|
|
14348
|
+
const full = join21(dir, name);
|
|
14289
14349
|
try {
|
|
14290
|
-
const st =
|
|
14291
|
-
const head =
|
|
14350
|
+
const st = statSync10(full);
|
|
14351
|
+
const head = readFileSync21(full, "utf-8").split("\n").slice(0, 20);
|
|
14292
14352
|
const tsLine = head.find((l) => l.startsWith("timestamp:"));
|
|
14293
14353
|
const kindLine = head.find((l) => l.startsWith("kind:"));
|
|
14294
14354
|
const msgIdx = head.findIndex((l) => l.trim() === "## message");
|
|
@@ -14307,8 +14367,8 @@ function listRecentCrashes(limit = 10, configDir) {
|
|
|
14307
14367
|
return files.slice(0, limit);
|
|
14308
14368
|
}
|
|
14309
14369
|
function getConfigDirUsage(configDir) {
|
|
14310
|
-
const base = configDir ??
|
|
14311
|
-
if (!
|
|
14370
|
+
const base = configDir ?? join21(homedir8(), CONFIG_DIR_NAME);
|
|
14371
|
+
if (!existsSync27(base)) return { totalBytes: 0, entries: [] };
|
|
14312
14372
|
const entries = [];
|
|
14313
14373
|
let total = 0;
|
|
14314
14374
|
let names;
|
|
@@ -14318,7 +14378,7 @@ function getConfigDirUsage(configDir) {
|
|
|
14318
14378
|
return { totalBytes: 0, entries: [] };
|
|
14319
14379
|
}
|
|
14320
14380
|
for (const name of names) {
|
|
14321
|
-
const full =
|
|
14381
|
+
const full = join21(base, name);
|
|
14322
14382
|
const bytes = dirSize(full);
|
|
14323
14383
|
entries.push({ name, bytes });
|
|
14324
14384
|
total += bytes;
|
|
@@ -14328,12 +14388,12 @@ function getConfigDirUsage(configDir) {
|
|
|
14328
14388
|
}
|
|
14329
14389
|
function dirSize(path3) {
|
|
14330
14390
|
try {
|
|
14331
|
-
const st =
|
|
14391
|
+
const st = statSync10(path3);
|
|
14332
14392
|
if (st.isFile()) return st.size;
|
|
14333
14393
|
if (!st.isDirectory()) return 0;
|
|
14334
14394
|
let sum = 0;
|
|
14335
14395
|
for (const name of readdirSync12(path3)) {
|
|
14336
|
-
sum += dirSize(
|
|
14396
|
+
sum += dirSize(join21(path3, name));
|
|
14337
14397
|
}
|
|
14338
14398
|
return sum;
|
|
14339
14399
|
} catch {
|
|
@@ -14343,11 +14403,11 @@ function dirSize(path3) {
|
|
|
14343
14403
|
|
|
14344
14404
|
// src/diagnostics/doctor-report.ts
|
|
14345
14405
|
function checkFile(label, path3) {
|
|
14346
|
-
const exists =
|
|
14406
|
+
const exists = existsSync28(path3);
|
|
14347
14407
|
let sizeBytes = null;
|
|
14348
14408
|
if (exists) {
|
|
14349
14409
|
try {
|
|
14350
|
-
sizeBytes =
|
|
14410
|
+
sizeBytes = statSync11(path3).size;
|
|
14351
14411
|
} catch {
|
|
14352
14412
|
}
|
|
14353
14413
|
}
|
|
@@ -14386,12 +14446,12 @@ function buildDoctorReport(options) {
|
|
|
14386
14446
|
configDir,
|
|
14387
14447
|
providers: options.providers.listAll().map((p) => ({ id: p.id, displayName: p.displayName, configured: p.configured })),
|
|
14388
14448
|
files: [
|
|
14389
|
-
checkFile("config.json",
|
|
14390
|
-
checkFile("memory.md",
|
|
14391
|
-
checkFile("memory.jsonl",
|
|
14392
|
-
checkFile("dev-state.md",
|
|
14393
|
-
checkFile("hooks-trust.json",
|
|
14394
|
-
checkFile("plugin-state.json",
|
|
14449
|
+
checkFile("config.json", join22(configDir, "config.json")),
|
|
14450
|
+
checkFile("memory.md", join22(configDir, MEMORY_FILE_NAME)),
|
|
14451
|
+
checkFile("memory.jsonl", join22(configDir, "memory.jsonl")),
|
|
14452
|
+
checkFile("dev-state.md", join22(configDir, DEV_STATE_FILE_NAME)),
|
|
14453
|
+
checkFile("hooks-trust.json", join22(configDir, "hooks-trust.json")),
|
|
14454
|
+
checkFile("plugin-state.json", join22(configDir, "plugins", "plugin-state.json"))
|
|
14395
14455
|
],
|
|
14396
14456
|
context: {
|
|
14397
14457
|
enabled: contextSetting !== false,
|
|
@@ -14426,7 +14486,7 @@ function buildDoctorReport(options) {
|
|
|
14426
14486
|
disabled: hooks.filter((h) => h.disabled).length
|
|
14427
14487
|
},
|
|
14428
14488
|
plugins: {
|
|
14429
|
-
root:
|
|
14489
|
+
root: join22(configDir, "plugins"),
|
|
14430
14490
|
installed: plugins.length,
|
|
14431
14491
|
active: activeAssets.plugins.length,
|
|
14432
14492
|
invalid: plugins.filter((p) => !p.valid).length,
|
|
@@ -14441,7 +14501,7 @@ function buildDoctorReport(options) {
|
|
|
14441
14501
|
mcp: {
|
|
14442
14502
|
enabled: config.get("mcpEnabled"),
|
|
14443
14503
|
configured: Object.keys(mcpServers).length,
|
|
14444
|
-
projectConfigExists:
|
|
14504
|
+
projectConfigExists: existsSync28(join22(projectRoot, MCP_PROJECT_CONFIG_NAME)),
|
|
14445
14505
|
statuses: options.mcpStatuses ?? []
|
|
14446
14506
|
},
|
|
14447
14507
|
recentCrashes: listRecentCrashes(5, configDir),
|
|
@@ -14762,7 +14822,7 @@ function resolveRoleProviders(roles, lookup, defaultProvider, defaultModel, avai
|
|
|
14762
14822
|
}
|
|
14763
14823
|
|
|
14764
14824
|
// src/hub/persist.ts
|
|
14765
|
-
import { join as
|
|
14825
|
+
import { join as join23 } from "path";
|
|
14766
14826
|
function discussionToMessages(state2) {
|
|
14767
14827
|
const out = [];
|
|
14768
14828
|
const t0 = state2.messages[0]?.timestamp ?? /* @__PURE__ */ new Date();
|
|
@@ -14800,7 +14860,7 @@ async function persistDiscussion(state2, config, defaultProvider, defaultModel)
|
|
|
14800
14860
|
session.title = `[Hub] ${state2.topic.slice(0, 48)}`.replace(/\n/g, " ");
|
|
14801
14861
|
session.titleAiGenerated = true;
|
|
14802
14862
|
await sm.save();
|
|
14803
|
-
return { id: session.id, path:
|
|
14863
|
+
return { id: session.id, path: join23(config.getHistoryDir(), `${session.id}.json`) };
|
|
14804
14864
|
}
|
|
14805
14865
|
|
|
14806
14866
|
// src/web/session-handler.ts
|
|
@@ -15517,7 +15577,7 @@ Try: /compact to reduce context, /clear to reset, or switch to a larger-context
|
|
|
15517
15577
|
this.send({ type: "response_done", content, usage });
|
|
15518
15578
|
if (pendingTeeSave && isCleanDocumentBody(content)) {
|
|
15519
15579
|
try {
|
|
15520
|
-
|
|
15580
|
+
mkdirSync14(dirname8(pendingTeeSave), { recursive: true });
|
|
15521
15581
|
const bodyToSave = stripOuterCodeFence(content);
|
|
15522
15582
|
atomicWriteFileSync(pendingTeeSave, bodyToSave);
|
|
15523
15583
|
undoStack.push(pendingTeeSave, `save_last_response (deferred): ${pendingTeeSave}`);
|
|
@@ -15693,7 +15753,7 @@ ${summaryContent}`,
|
|
|
15693
15753
|
let isError = false;
|
|
15694
15754
|
let summary;
|
|
15695
15755
|
try {
|
|
15696
|
-
|
|
15756
|
+
mkdirSync14(dirname8(saveToFile), { recursive: true });
|
|
15697
15757
|
fileStream = createWriteStream(saveToFile);
|
|
15698
15758
|
const teeSystemPrompt = stripToolCallReminder(systemPrompt ?? "") + CONTENT_ONLY_STREAM_REMINDER;
|
|
15699
15759
|
const teeExtraMessages = extraMessages.length > 0 ? [...extraMessages, { role: "user", content: TEE_FINAL_USER_NUDGE }] : [{ role: "user", content: TEE_FINAL_USER_NUDGE }];
|
|
@@ -15817,13 +15877,39 @@ ${summaryContent}`,
|
|
|
15817
15877
|
break;
|
|
15818
15878
|
}
|
|
15819
15879
|
case "model": {
|
|
15820
|
-
const
|
|
15821
|
-
if (!
|
|
15822
|
-
this.send({ type: "error", message: "Usage: /model <id
|
|
15880
|
+
const sub = args[0];
|
|
15881
|
+
if (!sub) {
|
|
15882
|
+
this.send({ type: "error", message: "Usage: /model <id>|refresh [provider]|cache [clear [provider]]" });
|
|
15883
|
+
return;
|
|
15884
|
+
}
|
|
15885
|
+
if (sub === "refresh") {
|
|
15886
|
+
const providerId = args[1] ?? this.currentProvider;
|
|
15887
|
+
try {
|
|
15888
|
+
const models2 = await this.providers.refreshModels(providerId);
|
|
15889
|
+
this.send({ type: "info", message: `Refreshed ${models2.length} model(s) for ${providerId}.` });
|
|
15890
|
+
this.sendStatus();
|
|
15891
|
+
} catch (err) {
|
|
15892
|
+
this.send({ type: "error", message: `Model refresh failed: ${err instanceof Error ? err.message : String(err)}` });
|
|
15893
|
+
}
|
|
15823
15894
|
return;
|
|
15824
15895
|
}
|
|
15825
|
-
|
|
15826
|
-
|
|
15896
|
+
if (sub === "cache") {
|
|
15897
|
+
if ((args[1] ?? "").toLowerCase() === "clear") {
|
|
15898
|
+
const providerId = args[2];
|
|
15899
|
+
this.providers.clearModelCache(providerId);
|
|
15900
|
+
this.send({ type: "info", message: providerId ? `Cleared model cache for ${providerId}.` : "Cleared model cache." });
|
|
15901
|
+
return;
|
|
15902
|
+
}
|
|
15903
|
+
const rows = this.providers.getModelCacheStatus();
|
|
15904
|
+
this.send({
|
|
15905
|
+
type: "info",
|
|
15906
|
+
message: rows.length === 0 ? "Model cache is empty. Use /model refresh to populate it." : rows.map((row) => `${row.providerId}: ${row.count} model(s), fetched ${row.fetchedAt}`).join("\n")
|
|
15907
|
+
});
|
|
15908
|
+
return;
|
|
15909
|
+
}
|
|
15910
|
+
const modelId = sub;
|
|
15911
|
+
const models = await this.providers.listModels(this.currentProvider);
|
|
15912
|
+
const found = models.find((m) => m.id === modelId);
|
|
15827
15913
|
if (!found) {
|
|
15828
15914
|
this.send({ type: "error", message: `Model "${modelId}" not found` });
|
|
15829
15915
|
return;
|
|
@@ -16382,9 +16468,9 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
16382
16468
|
let modifiedFiles = 0;
|
|
16383
16469
|
const diffLines2 = [];
|
|
16384
16470
|
for (const [filePath, { earliest }] of fileMap) {
|
|
16385
|
-
const currentContent =
|
|
16471
|
+
const currentContent = existsSync29(filePath) ? (() => {
|
|
16386
16472
|
try {
|
|
16387
|
-
return
|
|
16473
|
+
return readFileSync22(filePath, "utf-8");
|
|
16388
16474
|
} catch {
|
|
16389
16475
|
return null;
|
|
16390
16476
|
}
|
|
@@ -16900,7 +16986,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
16900
16986
|
case "test": {
|
|
16901
16987
|
this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
|
|
16902
16988
|
try {
|
|
16903
|
-
const { executeTests } = await import("./run-tests-
|
|
16989
|
+
const { executeTests } = await import("./run-tests-55JGEZYL.js");
|
|
16904
16990
|
const argStr = args.join(" ").trim();
|
|
16905
16991
|
let testArgs = {};
|
|
16906
16992
|
if (argStr) {
|
|
@@ -16917,9 +17003,9 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
16917
17003
|
// ── /init ───────────────────────────────────────────────────────
|
|
16918
17004
|
case "init": {
|
|
16919
17005
|
const cwd = process.cwd();
|
|
16920
|
-
const targetPath =
|
|
17006
|
+
const targetPath = join24(cwd, "AICLI.md");
|
|
16921
17007
|
const force = args.includes("--force");
|
|
16922
|
-
if (
|
|
17008
|
+
if (existsSync29(targetPath) && !force) {
|
|
16923
17009
|
this.send({ type: "info", message: `AICLI.md already exists at ${targetPath}
|
|
16924
17010
|
Use /init --force to overwrite.` });
|
|
16925
17011
|
break;
|
|
@@ -17112,11 +17198,11 @@ Use /add-dir remove to clear.` : "No directories added.\nUsage: /add-dir <path>
|
|
|
17112
17198
|
break;
|
|
17113
17199
|
}
|
|
17114
17200
|
const dirPath = resolve10(sub);
|
|
17115
|
-
if (!
|
|
17201
|
+
if (!existsSync29(dirPath)) {
|
|
17116
17202
|
this.send({ type: "error", message: `Directory not found: ${dirPath}` });
|
|
17117
17203
|
break;
|
|
17118
17204
|
}
|
|
17119
|
-
if (!
|
|
17205
|
+
if (!statSync12(dirPath).isDirectory()) {
|
|
17120
17206
|
this.send({ type: "error", message: `Not a directory: ${dirPath}` });
|
|
17121
17207
|
break;
|
|
17122
17208
|
}
|
|
@@ -17128,8 +17214,8 @@ It will be included in AI context for subsequent messages.` });
|
|
|
17128
17214
|
// ── /commands ───────────────────────────────────────────────────
|
|
17129
17215
|
case "commands": {
|
|
17130
17216
|
const configDir = this.config.getConfigDir();
|
|
17131
|
-
const commandsDir =
|
|
17132
|
-
if (!
|
|
17217
|
+
const commandsDir = join24(configDir, CUSTOM_COMMANDS_DIR_NAME);
|
|
17218
|
+
if (!existsSync29(commandsDir)) {
|
|
17133
17219
|
this.send({ type: "info", message: `No custom commands directory.
|
|
17134
17220
|
Create: ${commandsDir}/ with .md files.` });
|
|
17135
17221
|
break;
|
|
@@ -17155,7 +17241,7 @@ Add .md files to create commands.` });
|
|
|
17155
17241
|
// ── /plugins / /plugin ──────────────────────────────────────────
|
|
17156
17242
|
case "plugins": {
|
|
17157
17243
|
const configDir = this.config.getConfigDir();
|
|
17158
|
-
const pluginsDir =
|
|
17244
|
+
const pluginsDir = join24(configDir, PLUGINS_DIR_NAME);
|
|
17159
17245
|
const pluginTools = this.toolRegistry.listPluginTools();
|
|
17160
17246
|
const pluginPackages = listInstalledPlugins(configDir);
|
|
17161
17247
|
const lines = [`\u{1F50C} **Plugins:**`, `Dir: ${pluginsDir}`, ""];
|
|
@@ -17323,7 +17409,8 @@ Add .md files to create commands.` });
|
|
|
17323
17409
|
messageCount: s.messageCount,
|
|
17324
17410
|
updated: s.updated instanceof Date ? s.updated.toISOString() : String(s.updated),
|
|
17325
17411
|
isCurrent: s.id === this.sessions.current?.id
|
|
17326
|
-
}))
|
|
17412
|
+
})),
|
|
17413
|
+
totalCount: list.length
|
|
17327
17414
|
});
|
|
17328
17415
|
}
|
|
17329
17416
|
sendToolsList() {
|
|
@@ -17437,7 +17524,7 @@ ${entry.content}`;
|
|
|
17437
17524
|
const configDir = this.config.getConfigDir();
|
|
17438
17525
|
try {
|
|
17439
17526
|
atomicWriteFileSync(memoryStorePath(configDir), "");
|
|
17440
|
-
atomicWriteFileSync(
|
|
17527
|
+
atomicWriteFileSync(join24(configDir, MEMORY_FILE_NAME), "");
|
|
17441
17528
|
this.send({ type: "info", message: "\u{1F5D1}\uFE0F Persistent memory cleared." });
|
|
17442
17529
|
this.sendMemoryEntries();
|
|
17443
17530
|
} catch (err) {
|
|
@@ -17719,8 +17806,8 @@ async function setupProxy(configProxy) {
|
|
|
17719
17806
|
}
|
|
17720
17807
|
|
|
17721
17808
|
// src/web/auth.ts
|
|
17722
|
-
import { existsSync as
|
|
17723
|
-
import { join as
|
|
17809
|
+
import { existsSync as existsSync30, readFileSync as readFileSync23, writeFileSync as writeFileSync4, mkdirSync as mkdirSync15, readdirSync as readdirSync14, copyFileSync } from "fs";
|
|
17810
|
+
import { join as join25 } from "path";
|
|
17724
17811
|
import { createHmac, randomBytes, timingSafeEqual, pbkdf2Sync } from "crypto";
|
|
17725
17812
|
var USERS_FILE = "users.json";
|
|
17726
17813
|
var TOKEN_EXPIRY_HOURS = 24;
|
|
@@ -17735,7 +17822,7 @@ var AuthManager = class {
|
|
|
17735
17822
|
db;
|
|
17736
17823
|
constructor(baseDir) {
|
|
17737
17824
|
this.baseDir = baseDir;
|
|
17738
|
-
this.usersFile =
|
|
17825
|
+
this.usersFile = join25(baseDir, USERS_FILE);
|
|
17739
17826
|
this.db = this.loadOrCreate();
|
|
17740
17827
|
}
|
|
17741
17828
|
// ── Public API ─────────────────────────────────────────────────
|
|
@@ -17764,9 +17851,9 @@ var AuthManager = class {
|
|
|
17764
17851
|
}
|
|
17765
17852
|
const salt = randomBytes(16).toString("hex");
|
|
17766
17853
|
const passwordHash = this.hashPassword(password, salt);
|
|
17767
|
-
const dataDir =
|
|
17768
|
-
const fullDataDir =
|
|
17769
|
-
|
|
17854
|
+
const dataDir = join25(USERS_DIR, username);
|
|
17855
|
+
const fullDataDir = join25(this.baseDir, dataDir);
|
|
17856
|
+
mkdirSync15(join25(fullDataDir, "history"), { recursive: true });
|
|
17770
17857
|
const user = {
|
|
17771
17858
|
username,
|
|
17772
17859
|
passwordHash,
|
|
@@ -17881,7 +17968,7 @@ var AuthManager = class {
|
|
|
17881
17968
|
getUserDataDir(username) {
|
|
17882
17969
|
const user = this.db.users.find((u) => u.username === username);
|
|
17883
17970
|
if (!user) throw new Error(`User not found: ${username}`);
|
|
17884
|
-
return
|
|
17971
|
+
return join25(this.baseDir, user.dataDir);
|
|
17885
17972
|
}
|
|
17886
17973
|
/** List all usernames */
|
|
17887
17974
|
listUsers() {
|
|
@@ -17917,30 +18004,30 @@ var AuthManager = class {
|
|
|
17917
18004
|
const err = this.register(username, password);
|
|
17918
18005
|
if (err) return err;
|
|
17919
18006
|
const userDir = this.getUserDataDir(username);
|
|
17920
|
-
const globalConfig =
|
|
17921
|
-
if (
|
|
18007
|
+
const globalConfig = join25(this.baseDir, "config.json");
|
|
18008
|
+
if (existsSync30(globalConfig)) {
|
|
17922
18009
|
try {
|
|
17923
|
-
const content =
|
|
17924
|
-
writeFileSync4(
|
|
18010
|
+
const content = readFileSync23(globalConfig, "utf-8");
|
|
18011
|
+
writeFileSync4(join25(userDir, "config.json"), content, "utf-8");
|
|
17925
18012
|
} catch {
|
|
17926
18013
|
}
|
|
17927
18014
|
}
|
|
17928
|
-
const globalMemory =
|
|
17929
|
-
if (
|
|
18015
|
+
const globalMemory = join25(this.baseDir, "memory.md");
|
|
18016
|
+
if (existsSync30(globalMemory)) {
|
|
17930
18017
|
try {
|
|
17931
|
-
const content =
|
|
17932
|
-
writeFileSync4(
|
|
18018
|
+
const content = readFileSync23(globalMemory, "utf-8");
|
|
18019
|
+
writeFileSync4(join25(userDir, "memory.md"), content, "utf-8");
|
|
17933
18020
|
} catch {
|
|
17934
18021
|
}
|
|
17935
18022
|
}
|
|
17936
|
-
const globalHistory =
|
|
17937
|
-
if (
|
|
18023
|
+
const globalHistory = join25(this.baseDir, "history");
|
|
18024
|
+
if (existsSync30(globalHistory)) {
|
|
17938
18025
|
try {
|
|
17939
18026
|
const files = readdirSync14(globalHistory).filter((f) => f.endsWith(".json"));
|
|
17940
|
-
const userHistory =
|
|
18027
|
+
const userHistory = join25(userDir, "history");
|
|
17941
18028
|
for (const f of files) {
|
|
17942
18029
|
try {
|
|
17943
|
-
copyFileSync(
|
|
18030
|
+
copyFileSync(join25(globalHistory, f), join25(userHistory, f));
|
|
17944
18031
|
} catch {
|
|
17945
18032
|
}
|
|
17946
18033
|
}
|
|
@@ -17951,9 +18038,9 @@ var AuthManager = class {
|
|
|
17951
18038
|
}
|
|
17952
18039
|
// ── Private methods ────────────────────────────────────────────
|
|
17953
18040
|
loadOrCreate() {
|
|
17954
|
-
if (
|
|
18041
|
+
if (existsSync30(this.usersFile)) {
|
|
17955
18042
|
try {
|
|
17956
|
-
return JSON.parse(
|
|
18043
|
+
return JSON.parse(readFileSync23(this.usersFile, "utf-8"));
|
|
17957
18044
|
} catch {
|
|
17958
18045
|
}
|
|
17959
18046
|
}
|
|
@@ -17969,7 +18056,7 @@ var AuthManager = class {
|
|
|
17969
18056
|
this.saveDB(this.db);
|
|
17970
18057
|
}
|
|
17971
18058
|
saveDB(db) {
|
|
17972
|
-
|
|
18059
|
+
mkdirSync15(this.baseDir, { recursive: true });
|
|
17973
18060
|
atomicWriteFileSync(this.usersFile, JSON.stringify(db, null, 2));
|
|
17974
18061
|
}
|
|
17975
18062
|
/** Legacy hash — kept only for migrating old users (v0.2.x) */
|
|
@@ -18001,7 +18088,7 @@ function getModuleDir() {
|
|
|
18001
18088
|
if (typeof import.meta?.url === "string") {
|
|
18002
18089
|
const url = new URL(import.meta.url);
|
|
18003
18090
|
const filePath = url.pathname.replace(/^\/([A-Z]:)/i, "$1");
|
|
18004
|
-
return
|
|
18091
|
+
return dirname9(filePath);
|
|
18005
18092
|
}
|
|
18006
18093
|
} catch {
|
|
18007
18094
|
}
|
|
@@ -18024,7 +18111,8 @@ async function startWebServer(options = {}) {
|
|
|
18024
18111
|
timeout: config.get("timeouts")[id],
|
|
18025
18112
|
proxy: config.get("proxy")
|
|
18026
18113
|
}),
|
|
18027
|
-
config.get("customProviders")
|
|
18114
|
+
config.get("customProviders"),
|
|
18115
|
+
config.getConfigDir()
|
|
18028
18116
|
);
|
|
18029
18117
|
if (options._extraProviders) {
|
|
18030
18118
|
for (const p of options._extraProviders) {
|
|
@@ -18072,8 +18160,8 @@ async function startWebServer(options = {}) {
|
|
|
18072
18160
|
}
|
|
18073
18161
|
}
|
|
18074
18162
|
let skillManager = null;
|
|
18075
|
-
const skillsDir =
|
|
18076
|
-
if (
|
|
18163
|
+
const skillsDir = join26(config.getConfigDir(), SKILLS_DIR_NAME);
|
|
18164
|
+
if (existsSync31(skillsDir)) {
|
|
18077
18165
|
skillManager = new SkillManager(skillsDir, config.get("ui").skillSizeWarn);
|
|
18078
18166
|
skillManager.loadSkills(pluginAssets.skillDirs);
|
|
18079
18167
|
const count = skillManager.listSkills().length;
|
|
@@ -18144,18 +18232,18 @@ async function startWebServer(options = {}) {
|
|
|
18144
18232
|
next();
|
|
18145
18233
|
};
|
|
18146
18234
|
const moduleDir = getModuleDir();
|
|
18147
|
-
let clientDir =
|
|
18148
|
-
if (!
|
|
18149
|
-
clientDir =
|
|
18235
|
+
let clientDir = join26(moduleDir, "web", "client");
|
|
18236
|
+
if (!existsSync31(clientDir)) {
|
|
18237
|
+
clientDir = join26(moduleDir, "client");
|
|
18150
18238
|
}
|
|
18151
|
-
if (!
|
|
18152
|
-
clientDir =
|
|
18239
|
+
if (!existsSync31(clientDir)) {
|
|
18240
|
+
clientDir = join26(moduleDir, "..", "..", "src", "web", "client");
|
|
18153
18241
|
}
|
|
18154
|
-
if (!
|
|
18155
|
-
clientDir =
|
|
18242
|
+
if (!existsSync31(clientDir)) {
|
|
18243
|
+
clientDir = join26(process.cwd(), "src", "web", "client");
|
|
18156
18244
|
}
|
|
18157
18245
|
console.log(` Static files: ${clientDir}`);
|
|
18158
|
-
app.use("/vendor", express.static(
|
|
18246
|
+
app.use("/vendor", express.static(join26(clientDir, "vendor"), { maxAge: "1y", immutable: true }));
|
|
18159
18247
|
app.use(express.static(clientDir));
|
|
18160
18248
|
app.get("/api/status", (_req, res) => {
|
|
18161
18249
|
res.json({
|
|
@@ -18222,7 +18310,7 @@ async function startWebServer(options = {}) {
|
|
|
18222
18310
|
app.get("/api/files", requireAuth, (req, res) => {
|
|
18223
18311
|
const cwd = process.cwd();
|
|
18224
18312
|
const prefix = req.query.prefix || "";
|
|
18225
|
-
const targetDir =
|
|
18313
|
+
const targetDir = join26(cwd, prefix);
|
|
18226
18314
|
try {
|
|
18227
18315
|
const canonicalTarget = realpathSync(resolve11(targetDir));
|
|
18228
18316
|
const canonicalCwd = realpathSync(resolve11(cwd));
|
|
@@ -18239,7 +18327,7 @@ async function startWebServer(options = {}) {
|
|
|
18239
18327
|
const entries = readdirSync15(targetDir, { withFileTypes: true });
|
|
18240
18328
|
const files = entries.filter((e) => !SKIP.has(e.name) && !e.name.startsWith(".")).slice(0, 50).map((e) => ({
|
|
18241
18329
|
name: e.name,
|
|
18242
|
-
path: relative4(cwd,
|
|
18330
|
+
path: relative4(cwd, join26(targetDir, e.name)).replace(/\\/g, "/"),
|
|
18243
18331
|
isDir: e.isDirectory()
|
|
18244
18332
|
}));
|
|
18245
18333
|
res.json({ files });
|
|
@@ -18252,18 +18340,22 @@ async function startWebServer(options = {}) {
|
|
|
18252
18340
|
const authUser = req._authUser;
|
|
18253
18341
|
const sm = authUser ? getUserShared(authUser).sessions : sessions;
|
|
18254
18342
|
const list = sm.listSessions();
|
|
18343
|
+
const all = req.query.all === "true";
|
|
18344
|
+
const limit = all ? -1 : Number(req.query.limit) || 50;
|
|
18345
|
+
const sliced = limit > 0 ? list.slice(0, limit) : list;
|
|
18255
18346
|
res.json({
|
|
18256
|
-
sessions:
|
|
18347
|
+
sessions: sliced.map((s) => ({
|
|
18257
18348
|
id: s.id,
|
|
18258
18349
|
title: s.title,
|
|
18259
18350
|
provider: s.provider,
|
|
18260
18351
|
model: s.model,
|
|
18261
18352
|
messageCount: s.messageCount,
|
|
18262
18353
|
updated: s.updated
|
|
18263
|
-
}))
|
|
18354
|
+
})),
|
|
18355
|
+
totalCount: list.length
|
|
18264
18356
|
});
|
|
18265
18357
|
} catch {
|
|
18266
|
-
res.json({ sessions: [] });
|
|
18358
|
+
res.json({ sessions: [], totalCount: 0 });
|
|
18267
18359
|
}
|
|
18268
18360
|
});
|
|
18269
18361
|
app.get("/api/sessions/:id/replay", requireAuth, (req, res) => {
|
|
@@ -18275,8 +18367,8 @@ async function startWebServer(options = {}) {
|
|
|
18275
18367
|
try {
|
|
18276
18368
|
const authUser = req._authUser;
|
|
18277
18369
|
const histDir = authUser ? getUserShared(authUser).config.getHistoryDir() : config.getHistoryDir();
|
|
18278
|
-
const filePath =
|
|
18279
|
-
if (!
|
|
18370
|
+
const filePath = join26(histDir, `${id}.json`);
|
|
18371
|
+
if (!existsSync31(filePath)) {
|
|
18280
18372
|
res.status(404).json({ error: "Session not found" });
|
|
18281
18373
|
return;
|
|
18282
18374
|
}
|
|
@@ -18291,7 +18383,7 @@ async function startWebServer(options = {}) {
|
|
|
18291
18383
|
res.status(404).json({ error: "Session not found" });
|
|
18292
18384
|
return;
|
|
18293
18385
|
}
|
|
18294
|
-
const data = JSON.parse(
|
|
18386
|
+
const data = JSON.parse(readFileSync24(filePath, "utf-8"));
|
|
18295
18387
|
res.json({ session: data });
|
|
18296
18388
|
} catch (err) {
|
|
18297
18389
|
res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
|
|
@@ -18304,7 +18396,7 @@ async function startWebServer(options = {}) {
|
|
|
18304
18396
|
return;
|
|
18305
18397
|
}
|
|
18306
18398
|
const cwd = process.cwd();
|
|
18307
|
-
const fullPath = resolve11(
|
|
18399
|
+
const fullPath = resolve11(join26(cwd, filePath));
|
|
18308
18400
|
try {
|
|
18309
18401
|
const canonicalFull = realpathSync(fullPath);
|
|
18310
18402
|
const canonicalCwd = realpathSync(resolve11(cwd));
|
|
@@ -18317,12 +18409,12 @@ async function startWebServer(options = {}) {
|
|
|
18317
18409
|
return;
|
|
18318
18410
|
}
|
|
18319
18411
|
try {
|
|
18320
|
-
const stat =
|
|
18412
|
+
const stat = statSync13(fullPath);
|
|
18321
18413
|
if (stat.size > 512 * 1024) {
|
|
18322
18414
|
res.json({ error: `File too large (${(stat.size / 1024).toFixed(0)} KB, max 512 KB)` });
|
|
18323
18415
|
return;
|
|
18324
18416
|
}
|
|
18325
|
-
const content =
|
|
18417
|
+
const content = readFileSync24(fullPath, "utf-8");
|
|
18326
18418
|
res.json({ content, size: stat.size });
|
|
18327
18419
|
} catch {
|
|
18328
18420
|
res.json({ error: "Cannot read file" });
|
|
@@ -18577,17 +18669,17 @@ function resolveProjectMcpPath() {
|
|
|
18577
18669
|
const cwd = process.cwd();
|
|
18578
18670
|
const gitRoot = getGitRoot(cwd);
|
|
18579
18671
|
const projectRoot = gitRoot ?? cwd;
|
|
18580
|
-
const configPath =
|
|
18581
|
-
return
|
|
18672
|
+
const configPath = join26(projectRoot, MCP_PROJECT_CONFIG_NAME);
|
|
18673
|
+
return existsSync31(configPath) ? configPath : null;
|
|
18582
18674
|
}
|
|
18583
18675
|
function loadProjectMcpConfig() {
|
|
18584
18676
|
const cwd = process.cwd();
|
|
18585
18677
|
const gitRoot = getGitRoot(cwd);
|
|
18586
18678
|
const projectRoot = gitRoot ?? cwd;
|
|
18587
|
-
const configPath =
|
|
18588
|
-
if (!
|
|
18679
|
+
const configPath = join26(projectRoot, MCP_PROJECT_CONFIG_NAME);
|
|
18680
|
+
if (!existsSync31(configPath)) return null;
|
|
18589
18681
|
try {
|
|
18590
|
-
const raw = JSON.parse(
|
|
18682
|
+
const raw = JSON.parse(readFileSync24(configPath, "utf-8"));
|
|
18591
18683
|
return raw.mcpServers ?? raw;
|
|
18592
18684
|
} catch {
|
|
18593
18685
|
return null;
|