jinzd-ai-cli 0.4.219 → 0.4.220
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-QBSKTLTO.js} +3 -3
- package/dist/{chunk-TZQHYZKT.js → chunk-5ULLIOVC.js} +1 -0
- package/dist/{chunk-KIEZBTVD.js → chunk-5WBNXYDJ.js} +2 -2
- package/dist/{chunk-GGKQHPB3.js → chunk-ALQN7VUJ.js} +1 -1
- package/dist/{chunk-EGMORH5W.js → chunk-CAUHLHDR.js} +1 -1
- package/dist/{chunk-GEN4O5QH.js → chunk-IZW5LMI6.js} +4 -4
- package/dist/{chunk-3I4RZYYO.js → chunk-P3LKFA54.js} +6 -5
- package/dist/{chunk-VADHBW7T.js → chunk-P4DYAGUI.js} +1 -1
- package/dist/{chunk-4ZN6L6K5.js → chunk-P4PTXCHY.js} +6 -5
- package/dist/{chunk-QMXC327F.js → chunk-PUBCJF7E.js} +213 -175
- package/dist/{chunk-U7KVU25H.js → chunk-QZYNRCPX.js} +1 -1
- package/dist/{chunk-QUFYBR6Q.js → chunk-VHX7KZDY.js} +2 -2
- package/dist/{chunk-64NUW3WL.js → chunk-Y25MSSUP.js} +1 -1
- package/dist/{ci-QPP66T53.js → ci-5SNXGM3M.js} +6 -6
- package/dist/{ci-format-5S3EEYRK.js → ci-format-57QQTHQE.js} +2 -2
- package/dist/{constants-FJOLBABC.js → constants-ML5KFEOH.js} +1 -1
- package/dist/{doctor-cli-KB2LUDF3.js → doctor-cli-ZRH3XZ3R.js} +6 -6
- package/dist/electron-server.js +505 -442
- package/dist/{hub-DIM7SKKY.js → hub-IPLMJ6TJ.js} +1 -1
- package/dist/index.js +72 -31
- package/dist/{pr-YQGH72N6.js → pr-GJYDAPK4.js} +8 -7
- package/dist/{run-tests-O76JIBCW.js → run-tests-GK5AIAV2.js} +1 -1
- package/dist/{run-tests-GEZSSNJM.js → run-tests-KLQAI5CX.js} +2 -2
- package/dist/{server-Z6O3G2LY.js → server-5FZDALHH.js} +5 -5
- package/dist/{server-5E2AIXVX.js → server-RU36PRSH.js} +43 -16
- package/dist/{task-orchestrator-5HBW4O64.js → task-orchestrator-JMHPVNUO.js} +5 -5
- package/dist/{usage-BMK6M5U3.js → usage-IRMRCCKN.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-CAUHLHDR.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 statSync12, 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 } 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
|
}
|
|
@@ -4633,12 +4669,12 @@ var SessionManager = class {
|
|
|
4633
4669
|
return session;
|
|
4634
4670
|
}
|
|
4635
4671
|
listSessions() {
|
|
4636
|
-
if (!
|
|
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 meta = this.readSessionMeta(
|
|
4677
|
+
const meta = this.readSessionMeta(join3(this.historyDir, file));
|
|
4642
4678
|
if (meta) metas.push(meta);
|
|
4643
4679
|
} catch (err) {
|
|
4644
4680
|
process.stderr.write(
|
|
@@ -4678,7 +4714,7 @@ var SessionManager = class {
|
|
|
4678
4714
|
if (messageCount === void 0) {
|
|
4679
4715
|
messageCount = 0;
|
|
4680
4716
|
try {
|
|
4681
|
-
const full =
|
|
4717
|
+
const full = readFileSync3(filePath, "utf-8");
|
|
4682
4718
|
const matches = full.match(/"role"\s*:/g);
|
|
4683
4719
|
messageCount = matches ? matches.length : 0;
|
|
4684
4720
|
} catch {
|
|
@@ -4694,7 +4730,7 @@ var SessionManager = class {
|
|
|
4694
4730
|
title: title || void 0
|
|
4695
4731
|
};
|
|
4696
4732
|
}
|
|
4697
|
-
const data = JSON.parse(
|
|
4733
|
+
const data = JSON.parse(readFileSync3(filePath, "utf-8"));
|
|
4698
4734
|
return {
|
|
4699
4735
|
id: data.id,
|
|
4700
4736
|
provider: data.provider,
|
|
@@ -4706,10 +4742,10 @@ var SessionManager = class {
|
|
|
4706
4742
|
};
|
|
4707
4743
|
}
|
|
4708
4744
|
deleteSession(id) {
|
|
4709
|
-
const filePath =
|
|
4710
|
-
if (!
|
|
4745
|
+
const filePath = join3(this.historyDir, `${id}.json`);
|
|
4746
|
+
if (!existsSync3(filePath)) return false;
|
|
4711
4747
|
try {
|
|
4712
|
-
|
|
4748
|
+
unlinkSync2(filePath);
|
|
4713
4749
|
if (this._current && this._current.id === id) {
|
|
4714
4750
|
this._current = null;
|
|
4715
4751
|
}
|
|
@@ -4744,14 +4780,14 @@ var SessionManager = class {
|
|
|
4744
4780
|
* 每个 session 最多返回 3 条匹配片段,全局最多 maxResults 个 session。
|
|
4745
4781
|
*/
|
|
4746
4782
|
searchMessages(query, maxResults = 20) {
|
|
4747
|
-
if (!
|
|
4783
|
+
if (!existsSync3(this.historyDir)) return [];
|
|
4748
4784
|
const q = query.toLowerCase();
|
|
4749
|
-
const files = readdirSync(this.historyDir).filter((f) => f.endsWith(".json")).map((f) =>
|
|
4785
|
+
const files = readdirSync(this.historyDir).filter((f) => f.endsWith(".json")).map((f) => join3(this.historyDir, f));
|
|
4750
4786
|
const results = [];
|
|
4751
4787
|
for (const filePath of files) {
|
|
4752
4788
|
if (results.length >= maxResults) break;
|
|
4753
4789
|
try {
|
|
4754
|
-
const data = JSON.parse(
|
|
4790
|
+
const data = JSON.parse(readFileSync3(filePath, "utf-8"));
|
|
4755
4791
|
const messages = data.messages ?? [];
|
|
4756
4792
|
const matches = [];
|
|
4757
4793
|
for (const msg of messages) {
|
|
@@ -4798,12 +4834,12 @@ var SessionManager = class {
|
|
|
4798
4834
|
|
|
4799
4835
|
// src/tools/builtin/bash.ts
|
|
4800
4836
|
import { spawn } from "child_process";
|
|
4801
|
-
import { existsSync as
|
|
4837
|
+
import { existsSync as existsSync5, readdirSync as readdirSync2, statSync } from "fs";
|
|
4802
4838
|
import { platform as platform2 } from "os";
|
|
4803
4839
|
import { resolve } from "path";
|
|
4804
4840
|
|
|
4805
4841
|
// src/tools/undo-stack.ts
|
|
4806
|
-
import { readFileSync as
|
|
4842
|
+
import { readFileSync as readFileSync4, writeFileSync, unlinkSync as unlinkSync3, rmdirSync, existsSync as existsSync4 } from "fs";
|
|
4807
4843
|
var MAX_UNDO_DEPTH = 20;
|
|
4808
4844
|
var UndoStack = class {
|
|
4809
4845
|
stack = [];
|
|
@@ -4814,9 +4850,9 @@ var UndoStack = class {
|
|
|
4814
4850
|
*/
|
|
4815
4851
|
push(filePath, description) {
|
|
4816
4852
|
let previousContent = null;
|
|
4817
|
-
if (
|
|
4853
|
+
if (existsSync4(filePath)) {
|
|
4818
4854
|
try {
|
|
4819
|
-
previousContent =
|
|
4855
|
+
previousContent = readFileSync4(filePath, "utf-8");
|
|
4820
4856
|
} catch {
|
|
4821
4857
|
return;
|
|
4822
4858
|
}
|
|
@@ -4870,7 +4906,7 @@ var UndoStack = class {
|
|
|
4870
4906
|
try {
|
|
4871
4907
|
if (entry.previousContent === null) {
|
|
4872
4908
|
if (entry.isDirectory) {
|
|
4873
|
-
if (
|
|
4909
|
+
if (existsSync4(entry.filePath)) {
|
|
4874
4910
|
try {
|
|
4875
4911
|
rmdirSync(entry.filePath);
|
|
4876
4912
|
return { entry, result: `Removed newly created directory: ${entry.filePath}` };
|
|
@@ -4880,8 +4916,8 @@ var UndoStack = class {
|
|
|
4880
4916
|
}
|
|
4881
4917
|
return { entry, result: `Directory already removed: ${entry.filePath}` };
|
|
4882
4918
|
} else {
|
|
4883
|
-
if (
|
|
4884
|
-
|
|
4919
|
+
if (existsSync4(entry.filePath)) {
|
|
4920
|
+
unlinkSync3(entry.filePath);
|
|
4885
4921
|
}
|
|
4886
4922
|
return { entry, result: `Deleted newly created file: ${entry.filePath}` };
|
|
4887
4923
|
}
|
|
@@ -5025,7 +5061,7 @@ Important rules:
|
|
|
5025
5061
|
throw new ToolError("bash", blockingHint);
|
|
5026
5062
|
}
|
|
5027
5063
|
let currentCwd = getCwd();
|
|
5028
|
-
if (!
|
|
5064
|
+
if (!existsSync5(currentCwd)) {
|
|
5029
5065
|
const fallback = process.cwd();
|
|
5030
5066
|
process.stderr.write(
|
|
5031
5067
|
`[bash] Previous cwd "${currentCwd}" no longer exists, reset to "${fallback}"
|
|
@@ -5037,7 +5073,7 @@ Important rules:
|
|
|
5037
5073
|
let effectiveCwd = currentCwd;
|
|
5038
5074
|
if (cwdArg) {
|
|
5039
5075
|
const resolved = resolve(currentCwd, cwdArg);
|
|
5040
|
-
if (!
|
|
5076
|
+
if (!existsSync5(resolved)) {
|
|
5041
5077
|
throw new ToolError(
|
|
5042
5078
|
"bash",
|
|
5043
5079
|
`cwd directory does not exist: "${resolved}". Create it first (e.g. mkdir -p "${resolved}") before specifying it as cwd.`
|
|
@@ -5057,7 +5093,7 @@ Important rules:
|
|
|
5057
5093
|
const parsedTargets = parseCreationTargets(command, effectiveCwd);
|
|
5058
5094
|
const parsedTargetsBefore = /* @__PURE__ */ new Map();
|
|
5059
5095
|
for (const t of parsedTargets) {
|
|
5060
|
-
parsedTargetsBefore.set(t,
|
|
5096
|
+
parsedTargetsBefore.set(t, existsSync5(t));
|
|
5061
5097
|
}
|
|
5062
5098
|
try {
|
|
5063
5099
|
const { stdout, stderr, status, signal, timedOut, aborted } = await runShell(
|
|
@@ -5285,7 +5321,7 @@ function pushBashUndoEntries(beforeSnapshot, parsedTargetsBefore, cwd) {
|
|
|
5285
5321
|
}
|
|
5286
5322
|
}
|
|
5287
5323
|
for (const [target, existedBefore] of parsedTargetsBefore) {
|
|
5288
|
-
if (!existedBefore && !tracked.has(target) &&
|
|
5324
|
+
if (!existedBefore && !tracked.has(target) && existsSync5(target)) {
|
|
5289
5325
|
try {
|
|
5290
5326
|
const st = statSync(target);
|
|
5291
5327
|
if (st.isDirectory()) {
|
|
@@ -5398,7 +5434,7 @@ function updateCwdFromCommand(command, baseCwd) {
|
|
|
5398
5434
|
if (!target || target.startsWith("$") || target === "~") return;
|
|
5399
5435
|
try {
|
|
5400
5436
|
const newDir = resolve(baseCwd, target);
|
|
5401
|
-
if (
|
|
5437
|
+
if (existsSync5(newDir)) {
|
|
5402
5438
|
setCwd(newDir);
|
|
5403
5439
|
}
|
|
5404
5440
|
} catch {
|
|
@@ -5406,9 +5442,9 @@ function updateCwdFromCommand(command, baseCwd) {
|
|
|
5406
5442
|
}
|
|
5407
5443
|
|
|
5408
5444
|
// src/tools/builtin/read-file.ts
|
|
5409
|
-
import { readFileSync as
|
|
5445
|
+
import { readFileSync as readFileSync5, existsSync as existsSync6, statSync as statSync2, readdirSync as readdirSync3 } from "fs";
|
|
5410
5446
|
import { execFileSync } from "child_process";
|
|
5411
|
-
import { extname, resolve as resolve2, basename, sep, dirname } from "path";
|
|
5447
|
+
import { extname, resolve as resolve2, basename, sep, dirname as dirname2 } from "path";
|
|
5412
5448
|
import { homedir as homedir2 } from "os";
|
|
5413
5449
|
|
|
5414
5450
|
// src/tools/builtin/notebook-utils.ts
|
|
@@ -5683,7 +5719,7 @@ var readFileTool = {
|
|
|
5683
5719
|
const normalizedPath = resolve2(filePath);
|
|
5684
5720
|
const refusal = getHardRefusal(normalizedPath);
|
|
5685
5721
|
if (refusal) return refusal;
|
|
5686
|
-
if (!
|
|
5722
|
+
if (!existsSync6(normalizedPath)) {
|
|
5687
5723
|
const suggestions = findSimilarFiles(filePath);
|
|
5688
5724
|
if (suggestions.length > 0) {
|
|
5689
5725
|
throw new ToolError(
|
|
@@ -5722,9 +5758,9 @@ Use the bash tool to read in segments, e.g.:
|
|
|
5722
5758
|
|
|
5723
5759
|
${pdfText}`;
|
|
5724
5760
|
}
|
|
5725
|
-
const dir =
|
|
5761
|
+
const dir = dirname2(normalizedPath);
|
|
5726
5762
|
const nameNoExt = basename(normalizedPath, ext);
|
|
5727
|
-
const textAlts = [".md", ".txt", ".html"].map((e) => resolve2(dir, nameNoExt + e)).filter(
|
|
5763
|
+
const textAlts = [".md", ".txt", ".html"].map((e) => resolve2(dir, nameNoExt + e)).filter(existsSync6);
|
|
5728
5764
|
if (textAlts.length > 0) {
|
|
5729
5765
|
return `[PDF file: ${filePath}]
|
|
5730
5766
|
Cannot extract text from this PDF, but found alternative text versions:
|
|
@@ -5737,7 +5773,7 @@ Suggestion: read existing text versions (.md / .txt) in the project, or install
|
|
|
5737
5773
|
}
|
|
5738
5774
|
if (ext === ".ipynb") {
|
|
5739
5775
|
try {
|
|
5740
|
-
const raw =
|
|
5776
|
+
const raw = readFileSync5(normalizedPath, "utf-8");
|
|
5741
5777
|
const nb = parseNotebook(raw);
|
|
5742
5778
|
return renderNotebookAsText(nb, filePath);
|
|
5743
5779
|
} catch (err) {
|
|
@@ -5750,7 +5786,7 @@ ${err.message}`;
|
|
|
5750
5786
|
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
5787
|
}
|
|
5752
5788
|
const { readFile: readFile2 } = await import("fs/promises");
|
|
5753
|
-
const buf = size > 1048576 ? await readFile2(normalizedPath) :
|
|
5789
|
+
const buf = size > 1048576 ? await readFile2(normalizedPath) : readFileSync5(normalizedPath);
|
|
5754
5790
|
if (encoding === "base64") {
|
|
5755
5791
|
return `[File: ${filePath} | base64]
|
|
5756
5792
|
|
|
@@ -5788,12 +5824,12 @@ ${content}`;
|
|
|
5788
5824
|
};
|
|
5789
5825
|
|
|
5790
5826
|
// src/tools/builtin/write-file.ts
|
|
5791
|
-
import { appendFileSync, mkdirSync as
|
|
5792
|
-
import { dirname as
|
|
5827
|
+
import { appendFileSync, mkdirSync as mkdirSync6 } from "fs";
|
|
5828
|
+
import { dirname as dirname4 } from "path";
|
|
5793
5829
|
|
|
5794
5830
|
// src/tools/executor.ts
|
|
5795
5831
|
import chalk3 from "chalk";
|
|
5796
|
-
import { existsSync as
|
|
5832
|
+
import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
|
|
5797
5833
|
import { tmpdir } from "os";
|
|
5798
5834
|
|
|
5799
5835
|
// src/core/readline-internal.ts
|
|
@@ -6002,8 +6038,8 @@ function simpleDiff(oldLines, newLines) {
|
|
|
6002
6038
|
// src/tools/hooks.ts
|
|
6003
6039
|
import { execSync } from "child_process";
|
|
6004
6040
|
import { createHash } from "crypto";
|
|
6005
|
-
import { existsSync as
|
|
6006
|
-
import { join as
|
|
6041
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync6 } from "fs";
|
|
6042
|
+
import { join as join4 } from "path";
|
|
6007
6043
|
var TRUST_FILE = "hooks-trust.json";
|
|
6008
6044
|
var DEFAULT_TIMEOUT_MS = 5e3;
|
|
6009
6045
|
function asArray(entry) {
|
|
@@ -6012,13 +6048,13 @@ function asArray(entry) {
|
|
|
6012
6048
|
return raw.map((item) => typeof item === "string" ? { command: item } : item);
|
|
6013
6049
|
}
|
|
6014
6050
|
function hookTrustPath(configDir) {
|
|
6015
|
-
return
|
|
6051
|
+
return join4(configDir, TRUST_FILE);
|
|
6016
6052
|
}
|
|
6017
6053
|
function loadHookTrustStore(configDir) {
|
|
6018
6054
|
const file = hookTrustPath(configDir);
|
|
6019
|
-
if (!
|
|
6055
|
+
if (!existsSync7(file)) return { records: [] };
|
|
6020
6056
|
try {
|
|
6021
|
-
const parsed = JSON.parse(
|
|
6057
|
+
const parsed = JSON.parse(readFileSync6(file, "utf-8"));
|
|
6022
6058
|
return { records: Array.isArray(parsed.records) ? parsed.records : [] };
|
|
6023
6059
|
} catch {
|
|
6024
6060
|
return { records: [] };
|
|
@@ -6544,8 +6580,8 @@ var theme = new Proxy(DARK_THEME, {
|
|
|
6544
6580
|
});
|
|
6545
6581
|
|
|
6546
6582
|
// src/diagnostics/tool-stats.ts
|
|
6547
|
-
import { existsSync as
|
|
6548
|
-
import { join as
|
|
6583
|
+
import { existsSync as existsSync8, readFileSync as readFileSync7, mkdirSync as mkdirSync5 } from "fs";
|
|
6584
|
+
import { join as join5, dirname as dirname3 } from "path";
|
|
6549
6585
|
import { homedir as homedir3 } from "os";
|
|
6550
6586
|
var STATS_FILE_NAME = "tool-stats.json";
|
|
6551
6587
|
var STATS_VERSION = 1;
|
|
@@ -6555,16 +6591,16 @@ var dirty = false;
|
|
|
6555
6591
|
var pendingWrites = 0;
|
|
6556
6592
|
var configDirOverride;
|
|
6557
6593
|
function statsFilePath() {
|
|
6558
|
-
const base = configDirOverride ??
|
|
6559
|
-
return
|
|
6594
|
+
const base = configDirOverride ?? join5(homedir3(), CONFIG_DIR_NAME);
|
|
6595
|
+
return join5(base, STATS_FILE_NAME);
|
|
6560
6596
|
}
|
|
6561
6597
|
function load() {
|
|
6562
6598
|
const path3 = statsFilePath();
|
|
6563
|
-
if (!
|
|
6599
|
+
if (!existsSync8(path3)) {
|
|
6564
6600
|
return { version: STATS_VERSION, startedAt: (/* @__PURE__ */ new Date()).toISOString(), entries: {} };
|
|
6565
6601
|
}
|
|
6566
6602
|
try {
|
|
6567
|
-
const raw = JSON.parse(
|
|
6603
|
+
const raw = JSON.parse(readFileSync7(path3, "utf-8"));
|
|
6568
6604
|
if (raw.version !== STATS_VERSION || !raw.entries) {
|
|
6569
6605
|
return { version: STATS_VERSION, startedAt: (/* @__PURE__ */ new Date()).toISOString(), entries: {} };
|
|
6570
6606
|
}
|
|
@@ -6622,7 +6658,7 @@ function flush() {
|
|
|
6622
6658
|
if (!dirty || state === null) return;
|
|
6623
6659
|
const path3 = statsFilePath();
|
|
6624
6660
|
try {
|
|
6625
|
-
|
|
6661
|
+
mkdirSync5(dirname3(path3), { recursive: true });
|
|
6626
6662
|
atomicWriteFileSync(path3, JSON.stringify(state, null, 2));
|
|
6627
6663
|
dirty = false;
|
|
6628
6664
|
pendingWrites = 0;
|
|
@@ -6646,8 +6682,8 @@ function getTopUsedTools(limit = 5) {
|
|
|
6646
6682
|
}
|
|
6647
6683
|
|
|
6648
6684
|
// src/tools/action-classifier.ts
|
|
6649
|
-
import { existsSync as
|
|
6650
|
-
import { isAbsolute as isAbsolute2, join as
|
|
6685
|
+
import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
|
|
6686
|
+
import { isAbsolute as isAbsolute2, join as join6, resolve as resolve4 } from "path";
|
|
6651
6687
|
var RECENT_DENIED_LIMIT = 50;
|
|
6652
6688
|
var recentlyDenied = [];
|
|
6653
6689
|
function recordRecentlyDeniedAutoAction(call, classification) {
|
|
@@ -6688,10 +6724,10 @@ function splitShellWords(command) {
|
|
|
6688
6724
|
return out.filter(Boolean);
|
|
6689
6725
|
}
|
|
6690
6726
|
function readPackageManifest(root) {
|
|
6691
|
-
const p =
|
|
6692
|
-
if (!
|
|
6727
|
+
const p = join6(root, "package.json");
|
|
6728
|
+
if (!existsSync9(p)) return null;
|
|
6693
6729
|
try {
|
|
6694
|
-
return JSON.parse(
|
|
6730
|
+
return JSON.parse(readFileSync8(p, "utf-8"));
|
|
6695
6731
|
} catch {
|
|
6696
6732
|
return null;
|
|
6697
6733
|
}
|
|
@@ -6707,10 +6743,10 @@ function manifestHasDependency(root, name) {
|
|
|
6707
6743
|
}
|
|
6708
6744
|
function lockfileMentions(root, name) {
|
|
6709
6745
|
for (const file of ["package-lock.json", "pnpm-lock.yaml", "yarn.lock"]) {
|
|
6710
|
-
const p =
|
|
6711
|
-
if (!
|
|
6746
|
+
const p = join6(root, file);
|
|
6747
|
+
if (!existsSync9(p)) continue;
|
|
6712
6748
|
try {
|
|
6713
|
-
if (
|
|
6749
|
+
if (readFileSync8(p, "utf-8").includes(name)) return true;
|
|
6714
6750
|
} catch {
|
|
6715
6751
|
}
|
|
6716
6752
|
}
|
|
@@ -7274,10 +7310,10 @@ var ToolExecutor = class {
|
|
|
7274
7310
|
const filePath = String(call.arguments["path"] ?? "");
|
|
7275
7311
|
const newContent = String(call.arguments["content"] ?? "");
|
|
7276
7312
|
if (!filePath) return;
|
|
7277
|
-
if (
|
|
7313
|
+
if (existsSync10(filePath)) {
|
|
7278
7314
|
let oldContent;
|
|
7279
7315
|
try {
|
|
7280
|
-
oldContent =
|
|
7316
|
+
oldContent = readFileSync9(filePath, "utf-8");
|
|
7281
7317
|
} catch {
|
|
7282
7318
|
return;
|
|
7283
7319
|
}
|
|
@@ -7300,7 +7336,7 @@ var ToolExecutor = class {
|
|
|
7300
7336
|
}
|
|
7301
7337
|
} else if (call.name === "edit_file") {
|
|
7302
7338
|
const filePath = String(call.arguments["path"] ?? "");
|
|
7303
|
-
if (!filePath || !
|
|
7339
|
+
if (!filePath || !existsSync10(filePath)) return;
|
|
7304
7340
|
const oldStr = call.arguments["old_str"];
|
|
7305
7341
|
const newStr = call.arguments["new_str"];
|
|
7306
7342
|
if (oldStr !== void 0) {
|
|
@@ -7327,7 +7363,7 @@ var ToolExecutor = class {
|
|
|
7327
7363
|
const to = Number(call.arguments["delete_to_line"] ?? from);
|
|
7328
7364
|
let fileContent;
|
|
7329
7365
|
try {
|
|
7330
|
-
fileContent =
|
|
7366
|
+
fileContent = readFileSync9(filePath, "utf-8");
|
|
7331
7367
|
} catch {
|
|
7332
7368
|
return;
|
|
7333
7369
|
}
|
|
@@ -7562,7 +7598,7 @@ Do NOT split a long document into many write_file(append=true) calls. That patte
|
|
|
7562
7598
|
}
|
|
7563
7599
|
undoStack.push(filePath, `write_file${appendMode ? " (append)" : ""}: ${filePath}`);
|
|
7564
7600
|
fileCheckpoints.snapshot(filePath, ToolExecutor.currentMessageIndex);
|
|
7565
|
-
|
|
7601
|
+
mkdirSync6(dirname4(filePath), { recursive: true });
|
|
7566
7602
|
if (appendMode) {
|
|
7567
7603
|
appendFileSync(filePath, content, encoding);
|
|
7568
7604
|
} else {
|
|
@@ -7582,7 +7618,7 @@ Do NOT split a long document into many write_file(append=true) calls. That patte
|
|
|
7582
7618
|
};
|
|
7583
7619
|
|
|
7584
7620
|
// src/tools/builtin/edit-file.ts
|
|
7585
|
-
import { readFileSync as
|
|
7621
|
+
import { readFileSync as readFileSync10, existsSync as existsSync11 } from "fs";
|
|
7586
7622
|
|
|
7587
7623
|
// src/tools/builtin/patch-apply.ts
|
|
7588
7624
|
function parseUnifiedDiff(patch) {
|
|
@@ -7943,7 +7979,7 @@ Note: Path can be absolute or relative to cwd.`,
|
|
|
7943
7979
|
const filePath = String(args["path"] ?? "");
|
|
7944
7980
|
const encoding = args["encoding"] ?? "utf-8";
|
|
7945
7981
|
if (!filePath) throw new ToolError("edit_file", "path is required");
|
|
7946
|
-
if (!
|
|
7982
|
+
if (!existsSync11(filePath)) throw new ToolError("edit_file", `File not found: ${filePath}`);
|
|
7947
7983
|
const verdict = classifyWritePath(filePath);
|
|
7948
7984
|
if (verdict.sensitive && subAgentGuard.active) {
|
|
7949
7985
|
throw new ToolError(
|
|
@@ -7951,7 +7987,7 @@ Note: Path can be absolute or relative to cwd.`,
|
|
|
7951
7987
|
`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
7988
|
);
|
|
7953
7989
|
}
|
|
7954
|
-
const original =
|
|
7990
|
+
const original = readFileSync10(filePath, encoding);
|
|
7955
7991
|
if (args["patch"] !== void 0) {
|
|
7956
7992
|
const patchText = String(args["patch"] ?? "");
|
|
7957
7993
|
const stopOnError = args["stop_on_error"] !== false;
|
|
@@ -8109,8 +8145,8 @@ function truncatePreview(str, maxLen = 80) {
|
|
|
8109
8145
|
}
|
|
8110
8146
|
|
|
8111
8147
|
// src/tools/builtin/list-dir.ts
|
|
8112
|
-
import { readdirSync as readdirSync4, statSync as statSync3, existsSync as
|
|
8113
|
-
import { join as
|
|
8148
|
+
import { readdirSync as readdirSync4, statSync as statSync3, existsSync as existsSync12 } from "fs";
|
|
8149
|
+
import { join as join7, basename as basename3 } from "path";
|
|
8114
8150
|
var listDirTool = {
|
|
8115
8151
|
definition: {
|
|
8116
8152
|
name: "list_dir",
|
|
@@ -8132,7 +8168,7 @@ var listDirTool = {
|
|
|
8132
8168
|
async execute(args) {
|
|
8133
8169
|
const dirPath = String(args["path"] ?? process.cwd());
|
|
8134
8170
|
const recursive = Boolean(args["recursive"] ?? false);
|
|
8135
|
-
if (!
|
|
8171
|
+
if (!existsSync12(dirPath)) {
|
|
8136
8172
|
const targetName = basename3(dirPath).toLowerCase();
|
|
8137
8173
|
const cwd = process.cwd();
|
|
8138
8174
|
const suggestions = [];
|
|
@@ -8190,11 +8226,11 @@ function listRecursive(basePath, indent, recursive, lines) {
|
|
|
8190
8226
|
if (entry.isDirectory()) {
|
|
8191
8227
|
lines.push(`${indent}\u{1F4C1} ${entry.name}/`);
|
|
8192
8228
|
if (recursive) {
|
|
8193
|
-
listRecursive(
|
|
8229
|
+
listRecursive(join7(basePath, entry.name), indent + " ", true, lines);
|
|
8194
8230
|
}
|
|
8195
8231
|
} else {
|
|
8196
8232
|
try {
|
|
8197
|
-
const stat = statSync3(
|
|
8233
|
+
const stat = statSync3(join7(basePath, entry.name));
|
|
8198
8234
|
const size = formatSize(stat.size);
|
|
8199
8235
|
lines.push(`${indent}\u{1F4C4} ${entry.name} (${size})`);
|
|
8200
8236
|
} catch {
|
|
@@ -8210,9 +8246,9 @@ function formatSize(bytes) {
|
|
|
8210
8246
|
}
|
|
8211
8247
|
|
|
8212
8248
|
// src/tools/builtin/grep-files.ts
|
|
8213
|
-
import { readdirSync as readdirSync5, readFileSync as
|
|
8249
|
+
import { readdirSync as readdirSync5, readFileSync as readFileSync11, statSync as statSync4, existsSync as existsSync13 } from "fs";
|
|
8214
8250
|
import { readFile } from "fs/promises";
|
|
8215
|
-
import { join as
|
|
8251
|
+
import { join as join8, relative } from "path";
|
|
8216
8252
|
var grepFilesTool = {
|
|
8217
8253
|
definition: {
|
|
8218
8254
|
name: "grep_files",
|
|
@@ -8263,7 +8299,7 @@ Supports regex. Automatically skips node_modules, dist, .git directories.`,
|
|
|
8263
8299
|
const contextLines = Math.max(0, Number(args["context_lines"] ?? 0));
|
|
8264
8300
|
const maxResults = Math.max(1, Number(args["max_results"] ?? 50));
|
|
8265
8301
|
if (!pattern) throw new ToolError("grep_files", "pattern is required");
|
|
8266
|
-
if (!
|
|
8302
|
+
if (!existsSync13(rootPath)) throw new ToolError("grep_files", `Path not found: ${rootPath}`);
|
|
8267
8303
|
const MAX_PATTERN_LENGTH = 1e3;
|
|
8268
8304
|
if (pattern.length > MAX_PATTERN_LENGTH) {
|
|
8269
8305
|
throw new ToolError("grep_files", `Pattern too long (${pattern.length} chars, max ${MAX_PATTERN_LENGTH}). Use a shorter pattern.`);
|
|
@@ -8357,11 +8393,11 @@ function collectFilePaths(rootPath, filePattern, maxFiles) {
|
|
|
8357
8393
|
if (paths.length >= maxFiles) return;
|
|
8358
8394
|
if (entry.isDirectory()) {
|
|
8359
8395
|
if (SKIP_DIRS.has(entry.name) || entry.name.startsWith(".")) continue;
|
|
8360
|
-
walk(
|
|
8396
|
+
walk(join8(dirPath, entry.name));
|
|
8361
8397
|
} else if (entry.isFile()) {
|
|
8362
8398
|
if (isBinary(entry.name)) continue;
|
|
8363
8399
|
if (filePattern && !matchesFilePattern(entry.name, filePattern)) continue;
|
|
8364
|
-
const fullPath =
|
|
8400
|
+
const fullPath = join8(dirPath, entry.name);
|
|
8365
8401
|
try {
|
|
8366
8402
|
if (statSync4(fullPath).size > 1e6) continue;
|
|
8367
8403
|
} catch {
|
|
@@ -8416,7 +8452,7 @@ function searchInFile(fullPath, displayPath2, regex, contextLines, maxResults, r
|
|
|
8416
8452
|
}
|
|
8417
8453
|
let content;
|
|
8418
8454
|
try {
|
|
8419
|
-
content =
|
|
8455
|
+
content = readFileSync11(fullPath, "utf-8");
|
|
8420
8456
|
} catch {
|
|
8421
8457
|
return;
|
|
8422
8458
|
}
|
|
@@ -8447,8 +8483,8 @@ function searchInFile(fullPath, displayPath2, regex, contextLines, maxResults, r
|
|
|
8447
8483
|
}
|
|
8448
8484
|
|
|
8449
8485
|
// src/tools/builtin/glob-files.ts
|
|
8450
|
-
import { readdirSync as readdirSync6, statSync as statSync5, existsSync as
|
|
8451
|
-
import { join as
|
|
8486
|
+
import { readdirSync as readdirSync6, statSync as statSync5, existsSync as existsSync14 } from "fs";
|
|
8487
|
+
import { join as join9, relative as relative2, basename as basename4 } from "path";
|
|
8452
8488
|
var globFilesTool = {
|
|
8453
8489
|
definition: {
|
|
8454
8490
|
name: "glob_files",
|
|
@@ -8481,7 +8517,7 @@ Results sorted by most recent modification time. Automatically skips node_module
|
|
|
8481
8517
|
const rootPath = String(args["path"] ?? process.cwd());
|
|
8482
8518
|
const maxResults = Math.max(1, Number(args["max_results"] ?? 100));
|
|
8483
8519
|
if (!pattern) throw new ToolError("glob_files", "pattern is required");
|
|
8484
|
-
if (!
|
|
8520
|
+
if (!existsSync14(rootPath)) throw new ToolError("glob_files", `Path not found: ${rootPath}`);
|
|
8485
8521
|
const regex = globToRegex(pattern);
|
|
8486
8522
|
const matches = [];
|
|
8487
8523
|
collectMatchingFiles(rootPath, rootPath, regex, matches, maxResults);
|
|
@@ -8558,7 +8594,7 @@ function collectMatchingFiles(dirPath, rootPath, regex, results, maxResults) {
|
|
|
8558
8594
|
}
|
|
8559
8595
|
for (const entry of entries) {
|
|
8560
8596
|
if (results.length >= maxResults) break;
|
|
8561
|
-
const fullPath =
|
|
8597
|
+
const fullPath = join9(dirPath, entry.name);
|
|
8562
8598
|
if (entry.isDirectory()) {
|
|
8563
8599
|
if (SKIP_DIRS2.has(entry.name) || entry.name.startsWith(".")) continue;
|
|
8564
8600
|
collectMatchingFiles(fullPath, rootPath, regex, results, maxResults);
|
|
@@ -9298,16 +9334,16 @@ ${preamble}`;
|
|
|
9298
9334
|
}
|
|
9299
9335
|
|
|
9300
9336
|
// src/tools/builtin/save-last-response.ts
|
|
9301
|
-
import { mkdirSync as
|
|
9302
|
-
import { dirname as
|
|
9337
|
+
import { mkdirSync as mkdirSync7, unlinkSync as unlinkSync4, rmdirSync as rmdirSync2 } from "fs";
|
|
9338
|
+
import { dirname as dirname5 } from "path";
|
|
9303
9339
|
var lastResponseStore = { content: "" };
|
|
9304
9340
|
function cleanupRejectedTeeFile(filePath) {
|
|
9305
9341
|
try {
|
|
9306
|
-
|
|
9342
|
+
unlinkSync4(filePath);
|
|
9307
9343
|
} catch {
|
|
9308
9344
|
}
|
|
9309
9345
|
try {
|
|
9310
|
-
rmdirSync2(
|
|
9346
|
+
rmdirSync2(dirname5(filePath));
|
|
9311
9347
|
} catch {
|
|
9312
9348
|
}
|
|
9313
9349
|
}
|
|
@@ -9348,7 +9384,7 @@ Any of these triggers means use save_last_response, NOT write_file:
|
|
|
9348
9384
|
throw new ToolError("save_last_response", "No content to save: AI has not produced any response yet, or the last response was empty.");
|
|
9349
9385
|
}
|
|
9350
9386
|
undoStack.push(filePath, `save_last_response: ${filePath}`);
|
|
9351
|
-
|
|
9387
|
+
mkdirSync7(dirname5(filePath), { recursive: true });
|
|
9352
9388
|
atomicWriteFileSync(filePath, content);
|
|
9353
9389
|
const lines = content.split("\n").length;
|
|
9354
9390
|
return `File saved: ${filePath} (${lines} lines, ${content.length} bytes)`;
|
|
@@ -9356,18 +9392,18 @@ Any of these triggers means use save_last_response, NOT write_file:
|
|
|
9356
9392
|
};
|
|
9357
9393
|
|
|
9358
9394
|
// src/tools/builtin/save-memory.ts
|
|
9359
|
-
import { join as
|
|
9395
|
+
import { join as join12 } from "path";
|
|
9360
9396
|
import { homedir as homedir5 } from "os";
|
|
9361
9397
|
|
|
9362
9398
|
// src/memory/persistent-memory.ts
|
|
9363
|
-
import { existsSync as
|
|
9399
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync8, readFileSync as readFileSync12 } from "fs";
|
|
9364
9400
|
import { randomUUID, createHash as createHash2 } from "crypto";
|
|
9365
|
-
import { dirname as
|
|
9401
|
+
import { dirname as dirname6, join as join11, resolve as resolve6 } from "path";
|
|
9366
9402
|
|
|
9367
9403
|
// src/tools/git-context.ts
|
|
9368
9404
|
import { execSync as execSync2 } from "child_process";
|
|
9369
|
-
import { existsSync as
|
|
9370
|
-
import { join as
|
|
9405
|
+
import { existsSync as existsSync15 } from "fs";
|
|
9406
|
+
import { join as join10 } from "path";
|
|
9371
9407
|
function runGit(cmd, cwd) {
|
|
9372
9408
|
try {
|
|
9373
9409
|
return execSync2(`git ${cmd}`, {
|
|
@@ -9384,7 +9420,7 @@ function getGitRoot(cwd = process.cwd()) {
|
|
|
9384
9420
|
return runGit("rev-parse --show-toplevel", cwd);
|
|
9385
9421
|
}
|
|
9386
9422
|
function getGitContext(cwd = process.cwd()) {
|
|
9387
|
-
if (!
|
|
9423
|
+
if (!existsSync15(join10(cwd, ".git"))) {
|
|
9388
9424
|
const result = runGit("rev-parse --git-dir", cwd);
|
|
9389
9425
|
if (!result) return null;
|
|
9390
9426
|
}
|
|
@@ -9456,10 +9492,10 @@ function formatGitContextForPrompt(ctx) {
|
|
|
9456
9492
|
|
|
9457
9493
|
// src/memory/persistent-memory.ts
|
|
9458
9494
|
function memoryStorePath(configDir) {
|
|
9459
|
-
return
|
|
9495
|
+
return join11(configDir, MEMORY_STORE_FILE_NAME);
|
|
9460
9496
|
}
|
|
9461
9497
|
function memoryMarkdownPath(configDir) {
|
|
9462
|
-
return
|
|
9498
|
+
return join11(configDir, MEMORY_FILE_NAME);
|
|
9463
9499
|
}
|
|
9464
9500
|
function getProjectKey(cwd = process.cwd()) {
|
|
9465
9501
|
return resolve6(getGitRoot(cwd) ?? cwd);
|
|
@@ -9495,8 +9531,8 @@ function normalizeEntry(value) {
|
|
|
9495
9531
|
}
|
|
9496
9532
|
function legacyEntriesFromMarkdown(configDir) {
|
|
9497
9533
|
const file = memoryMarkdownPath(configDir);
|
|
9498
|
-
if (!
|
|
9499
|
-
const content =
|
|
9534
|
+
if (!existsSync16(file)) return [];
|
|
9535
|
+
const content = readFileSync12(file, "utf-8").trim();
|
|
9500
9536
|
if (!content) return [];
|
|
9501
9537
|
const parts = content.split(/\n(?=##\s+\d{4}-\d{2}-\d{2})/g);
|
|
9502
9538
|
const chunks = parts.length > 1 ? parts : [content];
|
|
@@ -9512,8 +9548,8 @@ ${chunk}`).digest("hex").slice(0, 12);
|
|
|
9512
9548
|
}
|
|
9513
9549
|
function loadMemoryEntries(configDir) {
|
|
9514
9550
|
const file = memoryStorePath(configDir);
|
|
9515
|
-
if (!
|
|
9516
|
-
const text =
|
|
9551
|
+
if (!existsSync16(file)) return legacyEntriesFromMarkdown(configDir);
|
|
9552
|
+
const text = readFileSync12(file, "utf-8").trim();
|
|
9517
9553
|
if (!text) return [];
|
|
9518
9554
|
const entries = [];
|
|
9519
9555
|
for (const line of text.split("\n")) {
|
|
@@ -9528,7 +9564,7 @@ function loadMemoryEntries(configDir) {
|
|
|
9528
9564
|
return entries;
|
|
9529
9565
|
}
|
|
9530
9566
|
function saveMemoryEntries(configDir, entries) {
|
|
9531
|
-
|
|
9567
|
+
mkdirSync8(configDir, { recursive: true });
|
|
9532
9568
|
const jsonl = entries.map((entry) => JSON.stringify(entry)).join("\n");
|
|
9533
9569
|
atomicWriteFileSync(memoryStorePath(configDir), jsonl ? jsonl + "\n" : "");
|
|
9534
9570
|
syncLegacyMarkdown(configDir, entries);
|
|
@@ -9603,7 +9639,7 @@ function findUnique(entries, idPrefix) {
|
|
|
9603
9639
|
return matches[0];
|
|
9604
9640
|
}
|
|
9605
9641
|
function syncLegacyMarkdown(configDir, entries = loadMemoryEntries(configDir)) {
|
|
9606
|
-
|
|
9642
|
+
mkdirSync8(dirname6(memoryMarkdownPath(configDir)), { recursive: true });
|
|
9607
9643
|
const active = entries.filter((entry) => entry.approved && !isMemoryExpired(entry));
|
|
9608
9644
|
const markdown = active.map((entry) => {
|
|
9609
9645
|
const date = entry.createdAt.replace("T", " ").slice(0, 19);
|
|
@@ -9646,7 +9682,7 @@ ${entry.content}`;
|
|
|
9646
9682
|
|
|
9647
9683
|
// src/tools/builtin/save-memory.ts
|
|
9648
9684
|
function getConfigDir() {
|
|
9649
|
-
return
|
|
9685
|
+
return join12(homedir5(), CONFIG_DIR_NAME);
|
|
9650
9686
|
}
|
|
9651
9687
|
function parseScope(value) {
|
|
9652
9688
|
return value === "personal" || value === "project" || value === "session" || value === "team" ? value : void 0;
|
|
@@ -9980,13 +10016,13 @@ function formatResults2(query, data, _requested) {
|
|
|
9980
10016
|
}
|
|
9981
10017
|
|
|
9982
10018
|
// src/agents/agent-config.ts
|
|
9983
|
-
import { existsSync as
|
|
9984
|
-
import { join as
|
|
10019
|
+
import { existsSync as existsSync18, readdirSync as readdirSync8, readFileSync as readFileSync14 } from "fs";
|
|
10020
|
+
import { join as join14 } from "path";
|
|
9985
10021
|
import { homedir as homedir6 } from "os";
|
|
9986
10022
|
|
|
9987
10023
|
// src/plugins/plugin-manager.ts
|
|
9988
|
-
import { existsSync as
|
|
9989
|
-
import { basename as basename5, dirname as
|
|
10024
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync9, readdirSync as readdirSync7, readFileSync as readFileSync13, cpSync, rmSync, statSync as statSync6 } from "fs";
|
|
10025
|
+
import { basename as basename5, dirname as dirname7, join as join13, resolve as resolve7 } from "path";
|
|
9990
10026
|
import { createHash as createHash3 } from "crypto";
|
|
9991
10027
|
import { z as z2 } from "zod";
|
|
9992
10028
|
var MANIFEST_RELATIVE = ".aicli-plugin/plugin.json";
|
|
@@ -10046,48 +10082,48 @@ var PluginManifestSchema = z2.object({
|
|
|
10046
10082
|
permissionHints: z2.array(z2.string()).default([])
|
|
10047
10083
|
}).strict();
|
|
10048
10084
|
function pluginRoot(configDir) {
|
|
10049
|
-
return
|
|
10085
|
+
return join13(configDir, PLUGINS_DIR_NAME);
|
|
10050
10086
|
}
|
|
10051
10087
|
function pluginManifestPath(pluginDir) {
|
|
10052
|
-
return
|
|
10088
|
+
return join13(pluginDir, MANIFEST_RELATIVE);
|
|
10053
10089
|
}
|
|
10054
10090
|
function statePath(configDir) {
|
|
10055
|
-
return
|
|
10091
|
+
return join13(pluginRoot(configDir), STATE_FILE);
|
|
10056
10092
|
}
|
|
10057
10093
|
function loadState(configDir) {
|
|
10058
10094
|
const file = statePath(configDir);
|
|
10059
|
-
if (!
|
|
10095
|
+
if (!existsSync17(file)) return { version: 1, plugins: [] };
|
|
10060
10096
|
try {
|
|
10061
|
-
const parsed = JSON.parse(
|
|
10097
|
+
const parsed = JSON.parse(readFileSync13(file, "utf-8"));
|
|
10062
10098
|
return { version: 1, plugins: Array.isArray(parsed.plugins) ? parsed.plugins : [] };
|
|
10063
10099
|
} catch {
|
|
10064
10100
|
return { version: 1, plugins: [] };
|
|
10065
10101
|
}
|
|
10066
10102
|
}
|
|
10067
10103
|
function saveState(configDir, state2) {
|
|
10068
|
-
|
|
10104
|
+
mkdirSync9(pluginRoot(configDir), { recursive: true });
|
|
10069
10105
|
atomicWriteFileSync(statePath(configDir), JSON.stringify(state2, null, 2));
|
|
10070
10106
|
}
|
|
10071
10107
|
function hashPluginManifest(manifestPath) {
|
|
10072
|
-
return createHash3("sha256").update(
|
|
10108
|
+
return createHash3("sha256").update(readFileSync13(manifestPath)).digest("hex");
|
|
10073
10109
|
}
|
|
10074
10110
|
function readPluginManifest(manifestPath) {
|
|
10075
|
-
return PluginManifestSchema.parse(JSON.parse(
|
|
10111
|
+
return PluginManifestSchema.parse(JSON.parse(readFileSync13(manifestPath, "utf-8")));
|
|
10076
10112
|
}
|
|
10077
10113
|
function findPluginSourceDir(inputPath) {
|
|
10078
10114
|
const abs = resolve7(inputPath);
|
|
10079
|
-
const direct = statSync6(abs).isDirectory() ? abs :
|
|
10080
|
-
if (
|
|
10081
|
-
const nested =
|
|
10082
|
-
if (
|
|
10115
|
+
const direct = statSync6(abs).isDirectory() ? abs : dirname7(abs);
|
|
10116
|
+
if (existsSync17(pluginManifestPath(direct))) return direct;
|
|
10117
|
+
const nested = join13(direct, ".aicli-plugin");
|
|
10118
|
+
if (existsSync17(join13(nested, "plugin.json"))) return direct;
|
|
10083
10119
|
throw new Error(`plugin manifest not found: ${MANIFEST_RELATIVE}`);
|
|
10084
10120
|
}
|
|
10085
10121
|
function installPlugin(configDir, inputPath) {
|
|
10086
10122
|
const sourceDir = findPluginSourceDir(inputPath);
|
|
10087
10123
|
const manifest = readPluginManifest(pluginManifestPath(sourceDir));
|
|
10088
|
-
const targetDir =
|
|
10089
|
-
|
|
10090
|
-
if (
|
|
10124
|
+
const targetDir = join13(pluginRoot(configDir), manifest.name);
|
|
10125
|
+
mkdirSync9(pluginRoot(configDir), { recursive: true });
|
|
10126
|
+
if (existsSync17(targetDir)) rmSync(targetDir, { recursive: true, force: true });
|
|
10091
10127
|
cpSync(sourceDir, targetDir, { recursive: true });
|
|
10092
10128
|
const manifestPath = pluginManifestPath(targetDir);
|
|
10093
10129
|
const hash = hashPluginManifest(manifestPath);
|
|
@@ -10145,18 +10181,18 @@ function getInstalledPlugin(configDir, name) {
|
|
|
10145
10181
|
}
|
|
10146
10182
|
function listInstalledPlugins(configDir) {
|
|
10147
10183
|
const root = pluginRoot(configDir);
|
|
10148
|
-
if (!
|
|
10184
|
+
if (!existsSync17(root)) return [];
|
|
10149
10185
|
const state2 = loadState(configDir);
|
|
10150
10186
|
const out = [];
|
|
10151
10187
|
for (const entry of readdirSync7(root)) {
|
|
10152
|
-
const dir =
|
|
10188
|
+
const dir = join13(root, entry);
|
|
10153
10189
|
try {
|
|
10154
10190
|
if (!statSync6(dir).isDirectory()) continue;
|
|
10155
10191
|
} catch {
|
|
10156
10192
|
continue;
|
|
10157
10193
|
}
|
|
10158
10194
|
const manifestPath = pluginManifestPath(dir);
|
|
10159
|
-
if (!
|
|
10195
|
+
if (!existsSync17(manifestPath)) continue;
|
|
10160
10196
|
try {
|
|
10161
10197
|
const manifest = readPluginManifest(manifestPath);
|
|
10162
10198
|
const hash = hashPluginManifest(manifestPath);
|
|
@@ -10196,7 +10232,7 @@ function uniqueDirs(plugin, rels) {
|
|
|
10196
10232
|
for (const rel of rels) {
|
|
10197
10233
|
const abs = resolve7(plugin.dir, rel);
|
|
10198
10234
|
if (!abs.startsWith(resolve7(plugin.dir))) continue;
|
|
10199
|
-
if (
|
|
10235
|
+
if (existsSync17(abs)) dirs.add(statSync6(abs).isDirectory() ? abs : dirname7(abs));
|
|
10200
10236
|
}
|
|
10201
10237
|
return [...dirs];
|
|
10202
10238
|
}
|
|
@@ -10359,13 +10395,13 @@ function parseAgentConfig(raw, source, path3) {
|
|
|
10359
10395
|
return cfg;
|
|
10360
10396
|
}
|
|
10361
10397
|
function loadAgentDir(dir, source) {
|
|
10362
|
-
if (!
|
|
10398
|
+
if (!existsSync18(dir)) return [];
|
|
10363
10399
|
const out = [];
|
|
10364
10400
|
for (const file of readdirSync8(dir)) {
|
|
10365
10401
|
if (!file.endsWith(".json")) continue;
|
|
10366
|
-
const path3 =
|
|
10402
|
+
const path3 = join14(dir, file);
|
|
10367
10403
|
try {
|
|
10368
|
-
const parsed = JSON.parse(
|
|
10404
|
+
const parsed = JSON.parse(readFileSync14(path3, "utf-8"));
|
|
10369
10405
|
const cfg = parseAgentConfig(parsed, source, path3);
|
|
10370
10406
|
if (cfg) out.push(cfg);
|
|
10371
10407
|
} catch {
|
|
@@ -10375,8 +10411,8 @@ function loadAgentDir(dir, source) {
|
|
|
10375
10411
|
}
|
|
10376
10412
|
function getAgentDirs(configDir, cwd = process.cwd()) {
|
|
10377
10413
|
return {
|
|
10378
|
-
user:
|
|
10379
|
-
project:
|
|
10414
|
+
user: join14(configDir ?? join14(homedir6(), CONFIG_DIR_NAME), "agents"),
|
|
10415
|
+
project: join14(cwd, CONFIG_DIR_NAME, "agents")
|
|
10380
10416
|
};
|
|
10381
10417
|
}
|
|
10382
10418
|
function listAgentConfigs(configDir, cwd = process.cwd()) {
|
|
@@ -11091,14 +11127,14 @@ var taskStopTool = {
|
|
|
11091
11127
|
|
|
11092
11128
|
// src/tools/builtin/git-tools.ts
|
|
11093
11129
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
11094
|
-
import { existsSync as
|
|
11095
|
-
import { join as
|
|
11130
|
+
import { existsSync as existsSync19 } from "fs";
|
|
11131
|
+
import { join as join15 } from "path";
|
|
11096
11132
|
function assertGitRepo(cwd) {
|
|
11097
11133
|
let dir = cwd;
|
|
11098
11134
|
const root = dir.split(/[\\/]/)[0] + (dir.includes("\\") ? "\\" : "/");
|
|
11099
11135
|
while (dir && dir !== root) {
|
|
11100
|
-
if (
|
|
11101
|
-
const parent =
|
|
11136
|
+
if (existsSync19(join15(dir, ".git"))) return;
|
|
11137
|
+
const parent = join15(dir, "..");
|
|
11102
11138
|
if (parent === dir) break;
|
|
11103
11139
|
dir = parent;
|
|
11104
11140
|
}
|
|
@@ -11363,7 +11399,7 @@ ${commitOutput.trim()}`;
|
|
|
11363
11399
|
};
|
|
11364
11400
|
|
|
11365
11401
|
// src/tools/builtin/notebook-edit.ts
|
|
11366
|
-
import { readFileSync as
|
|
11402
|
+
import { readFileSync as readFileSync15, existsSync as existsSync20 } from "fs";
|
|
11367
11403
|
import { writeFile } from "fs/promises";
|
|
11368
11404
|
import { resolve as resolve8, extname as extname2 } from "path";
|
|
11369
11405
|
var notebookEditTool = {
|
|
@@ -11418,10 +11454,10 @@ var notebookEditTool = {
|
|
|
11418
11454
|
if (extname2(absPath).toLowerCase() !== ".ipynb") {
|
|
11419
11455
|
throw new ToolError("notebook_edit", "path must point to a .ipynb file");
|
|
11420
11456
|
}
|
|
11421
|
-
if (!
|
|
11457
|
+
if (!existsSync20(absPath)) {
|
|
11422
11458
|
throw new ToolError("notebook_edit", `Notebook not found: ${filePath}`);
|
|
11423
11459
|
}
|
|
11424
|
-
const raw =
|
|
11460
|
+
const raw = readFileSync15(absPath, "utf-8");
|
|
11425
11461
|
const nb = parseNotebook(raw);
|
|
11426
11462
|
const cellIdx0 = cellIndexRaw - 1;
|
|
11427
11463
|
undoStack.push(absPath, `notebook_edit (${action}): ${filePath}`);
|
|
@@ -11838,8 +11874,8 @@ function estimateToolDefinitionTokens(def) {
|
|
|
11838
11874
|
|
|
11839
11875
|
// src/tools/registry.ts
|
|
11840
11876
|
import { pathToFileURL } from "url";
|
|
11841
|
-
import { existsSync as
|
|
11842
|
-
import { join as
|
|
11877
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync10, readdirSync as readdirSync9 } from "fs";
|
|
11878
|
+
import { join as join16 } from "path";
|
|
11843
11879
|
var ToolRegistry = class {
|
|
11844
11880
|
tools = /* @__PURE__ */ new Map();
|
|
11845
11881
|
pluginToolNames = /* @__PURE__ */ new Set();
|
|
@@ -12000,9 +12036,9 @@ var ToolRegistry = class {
|
|
|
12000
12036
|
* Returns the number of successfully loaded plugins.
|
|
12001
12037
|
*/
|
|
12002
12038
|
async loadPlugins(pluginsDir, allowPlugins = false) {
|
|
12003
|
-
if (!
|
|
12039
|
+
if (!existsSync21(pluginsDir)) {
|
|
12004
12040
|
try {
|
|
12005
|
-
|
|
12041
|
+
mkdirSync10(pluginsDir, { recursive: true });
|
|
12006
12042
|
} catch {
|
|
12007
12043
|
}
|
|
12008
12044
|
return 0;
|
|
@@ -12026,12 +12062,12 @@ var ToolRegistry = class {
|
|
|
12026
12062
|
process.stderr.write(
|
|
12027
12063
|
`
|
|
12028
12064
|
[plugins] \u26A0 Loading ${files.length} plugin(s) with FULL system privileges:
|
|
12029
|
-
` + files.map((f) => ` + ${
|
|
12065
|
+
` + files.map((f) => ` + ${join16(pluginsDir, f)}`).join("\n") + "\n\n"
|
|
12030
12066
|
);
|
|
12031
12067
|
let loaded = 0;
|
|
12032
12068
|
for (const file of files) {
|
|
12033
12069
|
try {
|
|
12034
|
-
const fileUrl = pathToFileURL(
|
|
12070
|
+
const fileUrl = pathToFileURL(join16(pluginsDir, file)).href;
|
|
12035
12071
|
const mod = await import(fileUrl);
|
|
12036
12072
|
const tool = mod.tool ?? mod.default?.tool ?? mod.default;
|
|
12037
12073
|
if (!tool || typeof tool.execute !== "function" || !tool.definition?.name) {
|
|
@@ -12577,11 +12613,11 @@ var McpManager = class {
|
|
|
12577
12613
|
};
|
|
12578
12614
|
|
|
12579
12615
|
// src/skills/manager.ts
|
|
12580
|
-
import { existsSync as
|
|
12581
|
-
import { join as
|
|
12616
|
+
import { existsSync as existsSync22, readdirSync as readdirSync10, mkdirSync as mkdirSync11, statSync as statSync7 } from "fs";
|
|
12617
|
+
import { join as join17 } from "path";
|
|
12582
12618
|
|
|
12583
12619
|
// src/skills/types.ts
|
|
12584
|
-
import { readFileSync as
|
|
12620
|
+
import { readFileSync as readFileSync16 } from "fs";
|
|
12585
12621
|
import { basename as basename6 } from "path";
|
|
12586
12622
|
function parseSimpleYaml(yaml) {
|
|
12587
12623
|
const result = {};
|
|
@@ -12603,7 +12639,7 @@ function parseYamlArray(value) {
|
|
|
12603
12639
|
function parseSkillFile(filePath) {
|
|
12604
12640
|
let raw;
|
|
12605
12641
|
try {
|
|
12606
|
-
raw =
|
|
12642
|
+
raw = readFileSync16(filePath, "utf-8");
|
|
12607
12643
|
} catch {
|
|
12608
12644
|
return null;
|
|
12609
12645
|
}
|
|
@@ -12651,10 +12687,10 @@ var SkillManager = class {
|
|
|
12651
12687
|
return this.skills.size;
|
|
12652
12688
|
}
|
|
12653
12689
|
loadSkillDir(dir, createIfMissing) {
|
|
12654
|
-
if (!
|
|
12690
|
+
if (!existsSync22(dir)) {
|
|
12655
12691
|
if (createIfMissing) {
|
|
12656
12692
|
try {
|
|
12657
|
-
|
|
12693
|
+
mkdirSync11(dir, { recursive: true });
|
|
12658
12694
|
} catch {
|
|
12659
12695
|
}
|
|
12660
12696
|
}
|
|
@@ -12668,14 +12704,14 @@ var SkillManager = class {
|
|
|
12668
12704
|
}
|
|
12669
12705
|
for (const entry of entries) {
|
|
12670
12706
|
let filePath;
|
|
12671
|
-
const fullPath =
|
|
12707
|
+
const fullPath = join17(dir, entry);
|
|
12672
12708
|
if (entry.endsWith(".md")) {
|
|
12673
12709
|
filePath = fullPath;
|
|
12674
12710
|
} else {
|
|
12675
12711
|
try {
|
|
12676
12712
|
if (statSync7(fullPath).isDirectory()) {
|
|
12677
|
-
const skillMd =
|
|
12678
|
-
if (
|
|
12713
|
+
const skillMd = join17(fullPath, "SKILL.md");
|
|
12714
|
+
if (existsSync22(skillMd)) filePath = skillMd;
|
|
12679
12715
|
else continue;
|
|
12680
12716
|
} else {
|
|
12681
12717
|
continue;
|
|
@@ -12734,7 +12770,7 @@ var SkillManager = class {
|
|
|
12734
12770
|
// src/web/tool-executor-web.ts
|
|
12735
12771
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
12736
12772
|
import { tmpdir as tmpdir2 } from "os";
|
|
12737
|
-
import { existsSync as
|
|
12773
|
+
import { existsSync as existsSync23, readFileSync as readFileSync17 } from "fs";
|
|
12738
12774
|
var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
12739
12775
|
constructor(registry, ws) {
|
|
12740
12776
|
this.registry = registry;
|
|
@@ -12873,9 +12909,9 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
12873
12909
|
if (call.name === "write_file") {
|
|
12874
12910
|
const filePath = String(call.arguments["path"] ?? "");
|
|
12875
12911
|
const newContent = String(call.arguments["content"] ?? "");
|
|
12876
|
-
if (filePath &&
|
|
12912
|
+
if (filePath && existsSync23(filePath)) {
|
|
12877
12913
|
try {
|
|
12878
|
-
const old =
|
|
12914
|
+
const old = readFileSync17(filePath, "utf-8");
|
|
12879
12915
|
return renderDiff(old, newContent, { filePath });
|
|
12880
12916
|
} catch {
|
|
12881
12917
|
}
|
|
@@ -13178,16 +13214,16 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
13178
13214
|
};
|
|
13179
13215
|
|
|
13180
13216
|
// src/repl/dev-state.ts
|
|
13181
|
-
import { existsSync as
|
|
13182
|
-
import { join as
|
|
13217
|
+
import { existsSync as existsSync24, readFileSync as readFileSync18, unlinkSync as unlinkSync5, mkdirSync as mkdirSync12 } from "fs";
|
|
13218
|
+
import { join as join18 } from "path";
|
|
13183
13219
|
import { homedir as homedir7 } from "os";
|
|
13184
13220
|
function getDevStatePath() {
|
|
13185
|
-
return
|
|
13221
|
+
return join18(homedir7(), CONFIG_DIR_NAME, DEV_STATE_FILE_NAME);
|
|
13186
13222
|
}
|
|
13187
13223
|
function loadDevState() {
|
|
13188
13224
|
const path3 = getDevStatePath();
|
|
13189
|
-
if (!
|
|
13190
|
-
const content =
|
|
13225
|
+
if (!existsSync24(path3)) return null;
|
|
13226
|
+
const content = readFileSync18(path3, "utf-8").trim();
|
|
13191
13227
|
return content || null;
|
|
13192
13228
|
}
|
|
13193
13229
|
|
|
@@ -13444,8 +13480,8 @@ function autoTrimSessionIfNeeded(session, sizeLimit = SESSION_SIZE_LIMIT) {
|
|
|
13444
13480
|
}
|
|
13445
13481
|
|
|
13446
13482
|
// src/core/context-files.ts
|
|
13447
|
-
import { existsSync as
|
|
13448
|
-
import { join as
|
|
13483
|
+
import { existsSync as existsSync25, readFileSync as readFileSync19 } from "fs";
|
|
13484
|
+
import { join as join19, relative as relative3, resolve as resolve9 } from "path";
|
|
13449
13485
|
function uniqueNonEmpty(values) {
|
|
13450
13486
|
const seen = /* @__PURE__ */ new Set();
|
|
13451
13487
|
const out = [];
|
|
@@ -13475,7 +13511,7 @@ function readContextFile(level, filePath, cwd, maxBytes) {
|
|
|
13475
13511
|
const name = filePath.split(/[\\/]/).pop() ?? filePath;
|
|
13476
13512
|
const shown = displayPath(filePath, cwd);
|
|
13477
13513
|
try {
|
|
13478
|
-
const raw =
|
|
13514
|
+
const raw = readFileSync19(filePath);
|
|
13479
13515
|
const byteCount = raw.byteLength;
|
|
13480
13516
|
if (byteCount === 0) {
|
|
13481
13517
|
return {
|
|
@@ -13522,8 +13558,8 @@ function readContextFile(level, filePath, cwd, maxBytes) {
|
|
|
13522
13558
|
function findFirstContextFile(level, dir, cwd, candidates, maxBytes) {
|
|
13523
13559
|
const skipped = [];
|
|
13524
13560
|
for (const candidate of candidates) {
|
|
13525
|
-
const filePath =
|
|
13526
|
-
if (!
|
|
13561
|
+
const filePath = join19(dir, candidate);
|
|
13562
|
+
if (!existsSync25(filePath)) continue;
|
|
13527
13563
|
const result = readContextFile(level, filePath, cwd, maxBytes);
|
|
13528
13564
|
if (result.skipped) skipped.push(result.skipped);
|
|
13529
13565
|
if (result.layer) return { layer: result.layer, skipped };
|
|
@@ -13550,7 +13586,7 @@ function loadContextFiles(options) {
|
|
|
13550
13586
|
});
|
|
13551
13587
|
return { layers: [], mergedContent: "", skipped, totalChars: 0, totalBytes: 0, maxBytes, candidates };
|
|
13552
13588
|
}
|
|
13553
|
-
if (!
|
|
13589
|
+
if (!existsSync25(filePath)) {
|
|
13554
13590
|
skipped.push({
|
|
13555
13591
|
level: "single",
|
|
13556
13592
|
filePath,
|
|
@@ -13601,8 +13637,8 @@ function loadContextFiles(options) {
|
|
|
13601
13637
|
}
|
|
13602
13638
|
|
|
13603
13639
|
// src/web/session-handler.ts
|
|
13604
|
-
import { existsSync as
|
|
13605
|
-
import { join as
|
|
13640
|
+
import { existsSync as existsSync29, readFileSync as readFileSync22, writeFileSync as writeFileSync3, mkdirSync as mkdirSync14, readdirSync as readdirSync13, statSync as statSync11, createWriteStream } from "fs";
|
|
13641
|
+
import { join as join24, resolve as resolve10, dirname as dirname8 } from "path";
|
|
13606
13642
|
|
|
13607
13643
|
// src/cli/review-prompts.ts
|
|
13608
13644
|
function buildReviewPrompt(diff, gitContextStr, detailed) {
|
|
@@ -13663,8 +13699,8 @@ If no security issues found, state "\u2705 No security vulnerabilities detected"
|
|
|
13663
13699
|
}
|
|
13664
13700
|
|
|
13665
13701
|
// src/repl/commands/project-init.ts
|
|
13666
|
-
import { existsSync as
|
|
13667
|
-
import { join as
|
|
13702
|
+
import { existsSync as existsSync26, readFileSync as readFileSync20, readdirSync as readdirSync11, statSync as statSync8 } from "fs";
|
|
13703
|
+
import { join as join20 } from "path";
|
|
13668
13704
|
var SCAN_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
13669
13705
|
"node_modules",
|
|
13670
13706
|
".git",
|
|
@@ -13708,11 +13744,11 @@ function scanDirTree(dir, maxDepth = 2, maxEntries = 80) {
|
|
|
13708
13744
|
const sorted = filtered.sort((a, b) => {
|
|
13709
13745
|
let aIsDir = false, bIsDir = false;
|
|
13710
13746
|
try {
|
|
13711
|
-
aIsDir = statSync8(
|
|
13747
|
+
aIsDir = statSync8(join20(d, a)).isDirectory();
|
|
13712
13748
|
} catch {
|
|
13713
13749
|
}
|
|
13714
13750
|
try {
|
|
13715
|
-
bIsDir = statSync8(
|
|
13751
|
+
bIsDir = statSync8(join20(d, b)).isDirectory();
|
|
13716
13752
|
} catch {
|
|
13717
13753
|
}
|
|
13718
13754
|
if (aIsDir !== bIsDir) return aIsDir ? -1 : 1;
|
|
@@ -13720,7 +13756,7 @@ function scanDirTree(dir, maxDepth = 2, maxEntries = 80) {
|
|
|
13720
13756
|
});
|
|
13721
13757
|
for (let i = 0; i < sorted.length && count < maxEntries; i++) {
|
|
13722
13758
|
const name = sorted[i];
|
|
13723
|
-
const fullPath =
|
|
13759
|
+
const fullPath = join20(d, name);
|
|
13724
13760
|
const isLast = i === sorted.length - 1;
|
|
13725
13761
|
const connector = isLast ? "+-- " : "|-- ";
|
|
13726
13762
|
let isDir;
|
|
@@ -13747,7 +13783,7 @@ function scanProject(cwd) {
|
|
|
13747
13783
|
configFiles: [],
|
|
13748
13784
|
directoryStructure: ""
|
|
13749
13785
|
};
|
|
13750
|
-
const check = (file) =>
|
|
13786
|
+
const check = (file) => existsSync26(join20(cwd, file));
|
|
13751
13787
|
const configCandidates = [
|
|
13752
13788
|
"package.json",
|
|
13753
13789
|
"tsconfig.json",
|
|
@@ -13774,7 +13810,7 @@ function scanProject(cwd) {
|
|
|
13774
13810
|
info.type = "node";
|
|
13775
13811
|
info.language = check("tsconfig.json") ? "TypeScript" : "JavaScript";
|
|
13776
13812
|
try {
|
|
13777
|
-
const pkg = JSON.parse(
|
|
13813
|
+
const pkg = JSON.parse(readFileSync20(join20(cwd, "package.json"), "utf-8"));
|
|
13778
13814
|
const scripts = pkg.scripts ?? {};
|
|
13779
13815
|
info.buildCommand = scripts.build ? "npm run build" : void 0;
|
|
13780
13816
|
info.testCommand = scripts.test ? "npm test" : void 0;
|
|
@@ -14248,34 +14284,34 @@ var DiscussionOrchestrator = class {
|
|
|
14248
14284
|
};
|
|
14249
14285
|
|
|
14250
14286
|
// src/diagnostics/doctor-report.ts
|
|
14251
|
-
import { existsSync as
|
|
14252
|
-
import { join as
|
|
14287
|
+
import { existsSync as existsSync28, statSync as statSync10 } from "fs";
|
|
14288
|
+
import { join as join22 } from "path";
|
|
14253
14289
|
import { arch as arch2, platform as platform6, release as release2 } from "os";
|
|
14254
14290
|
|
|
14255
14291
|
// src/diagnostics/crash-log.ts
|
|
14256
14292
|
import {
|
|
14257
|
-
existsSync as
|
|
14258
|
-
mkdirSync as
|
|
14293
|
+
existsSync as existsSync27,
|
|
14294
|
+
mkdirSync as mkdirSync13,
|
|
14259
14295
|
readdirSync as readdirSync12,
|
|
14260
|
-
readFileSync as
|
|
14296
|
+
readFileSync as readFileSync21,
|
|
14261
14297
|
statSync as statSync9,
|
|
14262
|
-
unlinkSync as
|
|
14298
|
+
unlinkSync as unlinkSync6,
|
|
14263
14299
|
writeFileSync as writeFileSync2
|
|
14264
14300
|
} from "fs";
|
|
14265
|
-
import { join as
|
|
14301
|
+
import { join as join21 } from "path";
|
|
14266
14302
|
import { homedir as homedir8, platform as platform5, release, arch } from "os";
|
|
14267
14303
|
var LOGS_DIR_NAME = "logs";
|
|
14268
14304
|
var CRASH_LOG_PREFIX = "crash-";
|
|
14269
14305
|
function getLogsDir(configDir) {
|
|
14270
|
-
const base = configDir ??
|
|
14271
|
-
return
|
|
14306
|
+
const base = configDir ?? join21(homedir8(), CONFIG_DIR_NAME);
|
|
14307
|
+
return join21(base, LOGS_DIR_NAME);
|
|
14272
14308
|
}
|
|
14273
14309
|
function truncate(s, n) {
|
|
14274
14310
|
return s.length <= n ? s : s.slice(0, n) + "\u2026";
|
|
14275
14311
|
}
|
|
14276
14312
|
function listRecentCrashes(limit = 10, configDir) {
|
|
14277
14313
|
const dir = getLogsDir(configDir);
|
|
14278
|
-
if (!
|
|
14314
|
+
if (!existsSync27(dir)) return [];
|
|
14279
14315
|
const files = [];
|
|
14280
14316
|
let names;
|
|
14281
14317
|
try {
|
|
@@ -14285,10 +14321,10 @@ function listRecentCrashes(limit = 10, configDir) {
|
|
|
14285
14321
|
}
|
|
14286
14322
|
for (const name of names) {
|
|
14287
14323
|
if (!name.startsWith(CRASH_LOG_PREFIX) || !name.endsWith(".log")) continue;
|
|
14288
|
-
const full =
|
|
14324
|
+
const full = join21(dir, name);
|
|
14289
14325
|
try {
|
|
14290
14326
|
const st = statSync9(full);
|
|
14291
|
-
const head =
|
|
14327
|
+
const head = readFileSync21(full, "utf-8").split("\n").slice(0, 20);
|
|
14292
14328
|
const tsLine = head.find((l) => l.startsWith("timestamp:"));
|
|
14293
14329
|
const kindLine = head.find((l) => l.startsWith("kind:"));
|
|
14294
14330
|
const msgIdx = head.findIndex((l) => l.trim() === "## message");
|
|
@@ -14307,8 +14343,8 @@ function listRecentCrashes(limit = 10, configDir) {
|
|
|
14307
14343
|
return files.slice(0, limit);
|
|
14308
14344
|
}
|
|
14309
14345
|
function getConfigDirUsage(configDir) {
|
|
14310
|
-
const base = configDir ??
|
|
14311
|
-
if (!
|
|
14346
|
+
const base = configDir ?? join21(homedir8(), CONFIG_DIR_NAME);
|
|
14347
|
+
if (!existsSync27(base)) return { totalBytes: 0, entries: [] };
|
|
14312
14348
|
const entries = [];
|
|
14313
14349
|
let total = 0;
|
|
14314
14350
|
let names;
|
|
@@ -14318,7 +14354,7 @@ function getConfigDirUsage(configDir) {
|
|
|
14318
14354
|
return { totalBytes: 0, entries: [] };
|
|
14319
14355
|
}
|
|
14320
14356
|
for (const name of names) {
|
|
14321
|
-
const full =
|
|
14357
|
+
const full = join21(base, name);
|
|
14322
14358
|
const bytes = dirSize(full);
|
|
14323
14359
|
entries.push({ name, bytes });
|
|
14324
14360
|
total += bytes;
|
|
@@ -14333,7 +14369,7 @@ function dirSize(path3) {
|
|
|
14333
14369
|
if (!st.isDirectory()) return 0;
|
|
14334
14370
|
let sum = 0;
|
|
14335
14371
|
for (const name of readdirSync12(path3)) {
|
|
14336
|
-
sum += dirSize(
|
|
14372
|
+
sum += dirSize(join21(path3, name));
|
|
14337
14373
|
}
|
|
14338
14374
|
return sum;
|
|
14339
14375
|
} catch {
|
|
@@ -14343,7 +14379,7 @@ function dirSize(path3) {
|
|
|
14343
14379
|
|
|
14344
14380
|
// src/diagnostics/doctor-report.ts
|
|
14345
14381
|
function checkFile(label, path3) {
|
|
14346
|
-
const exists =
|
|
14382
|
+
const exists = existsSync28(path3);
|
|
14347
14383
|
let sizeBytes = null;
|
|
14348
14384
|
if (exists) {
|
|
14349
14385
|
try {
|
|
@@ -14386,12 +14422,12 @@ function buildDoctorReport(options) {
|
|
|
14386
14422
|
configDir,
|
|
14387
14423
|
providers: options.providers.listAll().map((p) => ({ id: p.id, displayName: p.displayName, configured: p.configured })),
|
|
14388
14424
|
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",
|
|
14425
|
+
checkFile("config.json", join22(configDir, "config.json")),
|
|
14426
|
+
checkFile("memory.md", join22(configDir, MEMORY_FILE_NAME)),
|
|
14427
|
+
checkFile("memory.jsonl", join22(configDir, "memory.jsonl")),
|
|
14428
|
+
checkFile("dev-state.md", join22(configDir, DEV_STATE_FILE_NAME)),
|
|
14429
|
+
checkFile("hooks-trust.json", join22(configDir, "hooks-trust.json")),
|
|
14430
|
+
checkFile("plugin-state.json", join22(configDir, "plugins", "plugin-state.json"))
|
|
14395
14431
|
],
|
|
14396
14432
|
context: {
|
|
14397
14433
|
enabled: contextSetting !== false,
|
|
@@ -14426,7 +14462,7 @@ function buildDoctorReport(options) {
|
|
|
14426
14462
|
disabled: hooks.filter((h) => h.disabled).length
|
|
14427
14463
|
},
|
|
14428
14464
|
plugins: {
|
|
14429
|
-
root:
|
|
14465
|
+
root: join22(configDir, "plugins"),
|
|
14430
14466
|
installed: plugins.length,
|
|
14431
14467
|
active: activeAssets.plugins.length,
|
|
14432
14468
|
invalid: plugins.filter((p) => !p.valid).length,
|
|
@@ -14441,7 +14477,7 @@ function buildDoctorReport(options) {
|
|
|
14441
14477
|
mcp: {
|
|
14442
14478
|
enabled: config.get("mcpEnabled"),
|
|
14443
14479
|
configured: Object.keys(mcpServers).length,
|
|
14444
|
-
projectConfigExists:
|
|
14480
|
+
projectConfigExists: existsSync28(join22(projectRoot, MCP_PROJECT_CONFIG_NAME)),
|
|
14445
14481
|
statuses: options.mcpStatuses ?? []
|
|
14446
14482
|
},
|
|
14447
14483
|
recentCrashes: listRecentCrashes(5, configDir),
|
|
@@ -14762,7 +14798,7 @@ function resolveRoleProviders(roles, lookup, defaultProvider, defaultModel, avai
|
|
|
14762
14798
|
}
|
|
14763
14799
|
|
|
14764
14800
|
// src/hub/persist.ts
|
|
14765
|
-
import { join as
|
|
14801
|
+
import { join as join23 } from "path";
|
|
14766
14802
|
function discussionToMessages(state2) {
|
|
14767
14803
|
const out = [];
|
|
14768
14804
|
const t0 = state2.messages[0]?.timestamp ?? /* @__PURE__ */ new Date();
|
|
@@ -14800,7 +14836,7 @@ async function persistDiscussion(state2, config, defaultProvider, defaultModel)
|
|
|
14800
14836
|
session.title = `[Hub] ${state2.topic.slice(0, 48)}`.replace(/\n/g, " ");
|
|
14801
14837
|
session.titleAiGenerated = true;
|
|
14802
14838
|
await sm.save();
|
|
14803
|
-
return { id: session.id, path:
|
|
14839
|
+
return { id: session.id, path: join23(config.getHistoryDir(), `${session.id}.json`) };
|
|
14804
14840
|
}
|
|
14805
14841
|
|
|
14806
14842
|
// src/web/session-handler.ts
|
|
@@ -15517,7 +15553,7 @@ Try: /compact to reduce context, /clear to reset, or switch to a larger-context
|
|
|
15517
15553
|
this.send({ type: "response_done", content, usage });
|
|
15518
15554
|
if (pendingTeeSave && isCleanDocumentBody(content)) {
|
|
15519
15555
|
try {
|
|
15520
|
-
|
|
15556
|
+
mkdirSync14(dirname8(pendingTeeSave), { recursive: true });
|
|
15521
15557
|
const bodyToSave = stripOuterCodeFence(content);
|
|
15522
15558
|
atomicWriteFileSync(pendingTeeSave, bodyToSave);
|
|
15523
15559
|
undoStack.push(pendingTeeSave, `save_last_response (deferred): ${pendingTeeSave}`);
|
|
@@ -15693,7 +15729,7 @@ ${summaryContent}`,
|
|
|
15693
15729
|
let isError = false;
|
|
15694
15730
|
let summary;
|
|
15695
15731
|
try {
|
|
15696
|
-
|
|
15732
|
+
mkdirSync14(dirname8(saveToFile), { recursive: true });
|
|
15697
15733
|
fileStream = createWriteStream(saveToFile);
|
|
15698
15734
|
const teeSystemPrompt = stripToolCallReminder(systemPrompt ?? "") + CONTENT_ONLY_STREAM_REMINDER;
|
|
15699
15735
|
const teeExtraMessages = extraMessages.length > 0 ? [...extraMessages, { role: "user", content: TEE_FINAL_USER_NUDGE }] : [{ role: "user", content: TEE_FINAL_USER_NUDGE }];
|
|
@@ -15817,13 +15853,39 @@ ${summaryContent}`,
|
|
|
15817
15853
|
break;
|
|
15818
15854
|
}
|
|
15819
15855
|
case "model": {
|
|
15820
|
-
const
|
|
15821
|
-
if (!
|
|
15822
|
-
this.send({ type: "error", message: "Usage: /model <id
|
|
15856
|
+
const sub = args[0];
|
|
15857
|
+
if (!sub) {
|
|
15858
|
+
this.send({ type: "error", message: "Usage: /model <id>|refresh [provider]|cache [clear [provider]]" });
|
|
15859
|
+
return;
|
|
15860
|
+
}
|
|
15861
|
+
if (sub === "refresh") {
|
|
15862
|
+
const providerId = args[1] ?? this.currentProvider;
|
|
15863
|
+
try {
|
|
15864
|
+
const models2 = await this.providers.refreshModels(providerId);
|
|
15865
|
+
this.send({ type: "info", message: `Refreshed ${models2.length} model(s) for ${providerId}.` });
|
|
15866
|
+
this.sendStatus();
|
|
15867
|
+
} catch (err) {
|
|
15868
|
+
this.send({ type: "error", message: `Model refresh failed: ${err instanceof Error ? err.message : String(err)}` });
|
|
15869
|
+
}
|
|
15870
|
+
return;
|
|
15871
|
+
}
|
|
15872
|
+
if (sub === "cache") {
|
|
15873
|
+
if ((args[1] ?? "").toLowerCase() === "clear") {
|
|
15874
|
+
const providerId = args[2];
|
|
15875
|
+
this.providers.clearModelCache(providerId);
|
|
15876
|
+
this.send({ type: "info", message: providerId ? `Cleared model cache for ${providerId}.` : "Cleared model cache." });
|
|
15877
|
+
return;
|
|
15878
|
+
}
|
|
15879
|
+
const rows = this.providers.getModelCacheStatus();
|
|
15880
|
+
this.send({
|
|
15881
|
+
type: "info",
|
|
15882
|
+
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")
|
|
15883
|
+
});
|
|
15823
15884
|
return;
|
|
15824
15885
|
}
|
|
15825
|
-
const
|
|
15826
|
-
const
|
|
15886
|
+
const modelId = sub;
|
|
15887
|
+
const models = await this.providers.listModels(this.currentProvider);
|
|
15888
|
+
const found = models.find((m) => m.id === modelId);
|
|
15827
15889
|
if (!found) {
|
|
15828
15890
|
this.send({ type: "error", message: `Model "${modelId}" not found` });
|
|
15829
15891
|
return;
|
|
@@ -16382,9 +16444,9 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
16382
16444
|
let modifiedFiles = 0;
|
|
16383
16445
|
const diffLines2 = [];
|
|
16384
16446
|
for (const [filePath, { earliest }] of fileMap) {
|
|
16385
|
-
const currentContent =
|
|
16447
|
+
const currentContent = existsSync29(filePath) ? (() => {
|
|
16386
16448
|
try {
|
|
16387
|
-
return
|
|
16449
|
+
return readFileSync22(filePath, "utf-8");
|
|
16388
16450
|
} catch {
|
|
16389
16451
|
return null;
|
|
16390
16452
|
}
|
|
@@ -16900,7 +16962,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
16900
16962
|
case "test": {
|
|
16901
16963
|
this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
|
|
16902
16964
|
try {
|
|
16903
|
-
const { executeTests } = await import("./run-tests-
|
|
16965
|
+
const { executeTests } = await import("./run-tests-GK5AIAV2.js");
|
|
16904
16966
|
const argStr = args.join(" ").trim();
|
|
16905
16967
|
let testArgs = {};
|
|
16906
16968
|
if (argStr) {
|
|
@@ -16917,9 +16979,9 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
16917
16979
|
// ── /init ───────────────────────────────────────────────────────
|
|
16918
16980
|
case "init": {
|
|
16919
16981
|
const cwd = process.cwd();
|
|
16920
|
-
const targetPath =
|
|
16982
|
+
const targetPath = join24(cwd, "AICLI.md");
|
|
16921
16983
|
const force = args.includes("--force");
|
|
16922
|
-
if (
|
|
16984
|
+
if (existsSync29(targetPath) && !force) {
|
|
16923
16985
|
this.send({ type: "info", message: `AICLI.md already exists at ${targetPath}
|
|
16924
16986
|
Use /init --force to overwrite.` });
|
|
16925
16987
|
break;
|
|
@@ -17112,7 +17174,7 @@ Use /add-dir remove to clear.` : "No directories added.\nUsage: /add-dir <path>
|
|
|
17112
17174
|
break;
|
|
17113
17175
|
}
|
|
17114
17176
|
const dirPath = resolve10(sub);
|
|
17115
|
-
if (!
|
|
17177
|
+
if (!existsSync29(dirPath)) {
|
|
17116
17178
|
this.send({ type: "error", message: `Directory not found: ${dirPath}` });
|
|
17117
17179
|
break;
|
|
17118
17180
|
}
|
|
@@ -17128,8 +17190,8 @@ It will be included in AI context for subsequent messages.` });
|
|
|
17128
17190
|
// ── /commands ───────────────────────────────────────────────────
|
|
17129
17191
|
case "commands": {
|
|
17130
17192
|
const configDir = this.config.getConfigDir();
|
|
17131
|
-
const commandsDir =
|
|
17132
|
-
if (!
|
|
17193
|
+
const commandsDir = join24(configDir, CUSTOM_COMMANDS_DIR_NAME);
|
|
17194
|
+
if (!existsSync29(commandsDir)) {
|
|
17133
17195
|
this.send({ type: "info", message: `No custom commands directory.
|
|
17134
17196
|
Create: ${commandsDir}/ with .md files.` });
|
|
17135
17197
|
break;
|
|
@@ -17155,7 +17217,7 @@ Add .md files to create commands.` });
|
|
|
17155
17217
|
// ── /plugins / /plugin ──────────────────────────────────────────
|
|
17156
17218
|
case "plugins": {
|
|
17157
17219
|
const configDir = this.config.getConfigDir();
|
|
17158
|
-
const pluginsDir =
|
|
17220
|
+
const pluginsDir = join24(configDir, PLUGINS_DIR_NAME);
|
|
17159
17221
|
const pluginTools = this.toolRegistry.listPluginTools();
|
|
17160
17222
|
const pluginPackages = listInstalledPlugins(configDir);
|
|
17161
17223
|
const lines = [`\u{1F50C} **Plugins:**`, `Dir: ${pluginsDir}`, ""];
|
|
@@ -17437,7 +17499,7 @@ ${entry.content}`;
|
|
|
17437
17499
|
const configDir = this.config.getConfigDir();
|
|
17438
17500
|
try {
|
|
17439
17501
|
atomicWriteFileSync(memoryStorePath(configDir), "");
|
|
17440
|
-
atomicWriteFileSync(
|
|
17502
|
+
atomicWriteFileSync(join24(configDir, MEMORY_FILE_NAME), "");
|
|
17441
17503
|
this.send({ type: "info", message: "\u{1F5D1}\uFE0F Persistent memory cleared." });
|
|
17442
17504
|
this.sendMemoryEntries();
|
|
17443
17505
|
} catch (err) {
|
|
@@ -17719,8 +17781,8 @@ async function setupProxy(configProxy) {
|
|
|
17719
17781
|
}
|
|
17720
17782
|
|
|
17721
17783
|
// src/web/auth.ts
|
|
17722
|
-
import { existsSync as
|
|
17723
|
-
import { join as
|
|
17784
|
+
import { existsSync as existsSync30, readFileSync as readFileSync23, writeFileSync as writeFileSync4, mkdirSync as mkdirSync15, readdirSync as readdirSync14, copyFileSync } from "fs";
|
|
17785
|
+
import { join as join25 } from "path";
|
|
17724
17786
|
import { createHmac, randomBytes, timingSafeEqual, pbkdf2Sync } from "crypto";
|
|
17725
17787
|
var USERS_FILE = "users.json";
|
|
17726
17788
|
var TOKEN_EXPIRY_HOURS = 24;
|
|
@@ -17735,7 +17797,7 @@ var AuthManager = class {
|
|
|
17735
17797
|
db;
|
|
17736
17798
|
constructor(baseDir) {
|
|
17737
17799
|
this.baseDir = baseDir;
|
|
17738
|
-
this.usersFile =
|
|
17800
|
+
this.usersFile = join25(baseDir, USERS_FILE);
|
|
17739
17801
|
this.db = this.loadOrCreate();
|
|
17740
17802
|
}
|
|
17741
17803
|
// ── Public API ─────────────────────────────────────────────────
|
|
@@ -17764,9 +17826,9 @@ var AuthManager = class {
|
|
|
17764
17826
|
}
|
|
17765
17827
|
const salt = randomBytes(16).toString("hex");
|
|
17766
17828
|
const passwordHash = this.hashPassword(password, salt);
|
|
17767
|
-
const dataDir =
|
|
17768
|
-
const fullDataDir =
|
|
17769
|
-
|
|
17829
|
+
const dataDir = join25(USERS_DIR, username);
|
|
17830
|
+
const fullDataDir = join25(this.baseDir, dataDir);
|
|
17831
|
+
mkdirSync15(join25(fullDataDir, "history"), { recursive: true });
|
|
17770
17832
|
const user = {
|
|
17771
17833
|
username,
|
|
17772
17834
|
passwordHash,
|
|
@@ -17881,7 +17943,7 @@ var AuthManager = class {
|
|
|
17881
17943
|
getUserDataDir(username) {
|
|
17882
17944
|
const user = this.db.users.find((u) => u.username === username);
|
|
17883
17945
|
if (!user) throw new Error(`User not found: ${username}`);
|
|
17884
|
-
return
|
|
17946
|
+
return join25(this.baseDir, user.dataDir);
|
|
17885
17947
|
}
|
|
17886
17948
|
/** List all usernames */
|
|
17887
17949
|
listUsers() {
|
|
@@ -17917,30 +17979,30 @@ var AuthManager = class {
|
|
|
17917
17979
|
const err = this.register(username, password);
|
|
17918
17980
|
if (err) return err;
|
|
17919
17981
|
const userDir = this.getUserDataDir(username);
|
|
17920
|
-
const globalConfig =
|
|
17921
|
-
if (
|
|
17982
|
+
const globalConfig = join25(this.baseDir, "config.json");
|
|
17983
|
+
if (existsSync30(globalConfig)) {
|
|
17922
17984
|
try {
|
|
17923
|
-
const content =
|
|
17924
|
-
writeFileSync4(
|
|
17985
|
+
const content = readFileSync23(globalConfig, "utf-8");
|
|
17986
|
+
writeFileSync4(join25(userDir, "config.json"), content, "utf-8");
|
|
17925
17987
|
} catch {
|
|
17926
17988
|
}
|
|
17927
17989
|
}
|
|
17928
|
-
const globalMemory =
|
|
17929
|
-
if (
|
|
17990
|
+
const globalMemory = join25(this.baseDir, "memory.md");
|
|
17991
|
+
if (existsSync30(globalMemory)) {
|
|
17930
17992
|
try {
|
|
17931
|
-
const content =
|
|
17932
|
-
writeFileSync4(
|
|
17993
|
+
const content = readFileSync23(globalMemory, "utf-8");
|
|
17994
|
+
writeFileSync4(join25(userDir, "memory.md"), content, "utf-8");
|
|
17933
17995
|
} catch {
|
|
17934
17996
|
}
|
|
17935
17997
|
}
|
|
17936
|
-
const globalHistory =
|
|
17937
|
-
if (
|
|
17998
|
+
const globalHistory = join25(this.baseDir, "history");
|
|
17999
|
+
if (existsSync30(globalHistory)) {
|
|
17938
18000
|
try {
|
|
17939
18001
|
const files = readdirSync14(globalHistory).filter((f) => f.endsWith(".json"));
|
|
17940
|
-
const userHistory =
|
|
18002
|
+
const userHistory = join25(userDir, "history");
|
|
17941
18003
|
for (const f of files) {
|
|
17942
18004
|
try {
|
|
17943
|
-
copyFileSync(
|
|
18005
|
+
copyFileSync(join25(globalHistory, f), join25(userHistory, f));
|
|
17944
18006
|
} catch {
|
|
17945
18007
|
}
|
|
17946
18008
|
}
|
|
@@ -17951,9 +18013,9 @@ var AuthManager = class {
|
|
|
17951
18013
|
}
|
|
17952
18014
|
// ── Private methods ────────────────────────────────────────────
|
|
17953
18015
|
loadOrCreate() {
|
|
17954
|
-
if (
|
|
18016
|
+
if (existsSync30(this.usersFile)) {
|
|
17955
18017
|
try {
|
|
17956
|
-
return JSON.parse(
|
|
18018
|
+
return JSON.parse(readFileSync23(this.usersFile, "utf-8"));
|
|
17957
18019
|
} catch {
|
|
17958
18020
|
}
|
|
17959
18021
|
}
|
|
@@ -17969,7 +18031,7 @@ var AuthManager = class {
|
|
|
17969
18031
|
this.saveDB(this.db);
|
|
17970
18032
|
}
|
|
17971
18033
|
saveDB(db) {
|
|
17972
|
-
|
|
18034
|
+
mkdirSync15(this.baseDir, { recursive: true });
|
|
17973
18035
|
atomicWriteFileSync(this.usersFile, JSON.stringify(db, null, 2));
|
|
17974
18036
|
}
|
|
17975
18037
|
/** Legacy hash — kept only for migrating old users (v0.2.x) */
|
|
@@ -18001,7 +18063,7 @@ function getModuleDir() {
|
|
|
18001
18063
|
if (typeof import.meta?.url === "string") {
|
|
18002
18064
|
const url = new URL(import.meta.url);
|
|
18003
18065
|
const filePath = url.pathname.replace(/^\/([A-Z]:)/i, "$1");
|
|
18004
|
-
return
|
|
18066
|
+
return dirname9(filePath);
|
|
18005
18067
|
}
|
|
18006
18068
|
} catch {
|
|
18007
18069
|
}
|
|
@@ -18024,7 +18086,8 @@ async function startWebServer(options = {}) {
|
|
|
18024
18086
|
timeout: config.get("timeouts")[id],
|
|
18025
18087
|
proxy: config.get("proxy")
|
|
18026
18088
|
}),
|
|
18027
|
-
config.get("customProviders")
|
|
18089
|
+
config.get("customProviders"),
|
|
18090
|
+
config.getConfigDir()
|
|
18028
18091
|
);
|
|
18029
18092
|
if (options._extraProviders) {
|
|
18030
18093
|
for (const p of options._extraProviders) {
|
|
@@ -18072,8 +18135,8 @@ async function startWebServer(options = {}) {
|
|
|
18072
18135
|
}
|
|
18073
18136
|
}
|
|
18074
18137
|
let skillManager = null;
|
|
18075
|
-
const skillsDir =
|
|
18076
|
-
if (
|
|
18138
|
+
const skillsDir = join26(config.getConfigDir(), SKILLS_DIR_NAME);
|
|
18139
|
+
if (existsSync31(skillsDir)) {
|
|
18077
18140
|
skillManager = new SkillManager(skillsDir, config.get("ui").skillSizeWarn);
|
|
18078
18141
|
skillManager.loadSkills(pluginAssets.skillDirs);
|
|
18079
18142
|
const count = skillManager.listSkills().length;
|
|
@@ -18144,18 +18207,18 @@ async function startWebServer(options = {}) {
|
|
|
18144
18207
|
next();
|
|
18145
18208
|
};
|
|
18146
18209
|
const moduleDir = getModuleDir();
|
|
18147
|
-
let clientDir =
|
|
18148
|
-
if (!
|
|
18149
|
-
clientDir =
|
|
18210
|
+
let clientDir = join26(moduleDir, "web", "client");
|
|
18211
|
+
if (!existsSync31(clientDir)) {
|
|
18212
|
+
clientDir = join26(moduleDir, "client");
|
|
18150
18213
|
}
|
|
18151
|
-
if (!
|
|
18152
|
-
clientDir =
|
|
18214
|
+
if (!existsSync31(clientDir)) {
|
|
18215
|
+
clientDir = join26(moduleDir, "..", "..", "src", "web", "client");
|
|
18153
18216
|
}
|
|
18154
|
-
if (!
|
|
18155
|
-
clientDir =
|
|
18217
|
+
if (!existsSync31(clientDir)) {
|
|
18218
|
+
clientDir = join26(process.cwd(), "src", "web", "client");
|
|
18156
18219
|
}
|
|
18157
18220
|
console.log(` Static files: ${clientDir}`);
|
|
18158
|
-
app.use("/vendor", express.static(
|
|
18221
|
+
app.use("/vendor", express.static(join26(clientDir, "vendor"), { maxAge: "1y", immutable: true }));
|
|
18159
18222
|
app.use(express.static(clientDir));
|
|
18160
18223
|
app.get("/api/status", (_req, res) => {
|
|
18161
18224
|
res.json({
|
|
@@ -18222,7 +18285,7 @@ async function startWebServer(options = {}) {
|
|
|
18222
18285
|
app.get("/api/files", requireAuth, (req, res) => {
|
|
18223
18286
|
const cwd = process.cwd();
|
|
18224
18287
|
const prefix = req.query.prefix || "";
|
|
18225
|
-
const targetDir =
|
|
18288
|
+
const targetDir = join26(cwd, prefix);
|
|
18226
18289
|
try {
|
|
18227
18290
|
const canonicalTarget = realpathSync(resolve11(targetDir));
|
|
18228
18291
|
const canonicalCwd = realpathSync(resolve11(cwd));
|
|
@@ -18239,7 +18302,7 @@ async function startWebServer(options = {}) {
|
|
|
18239
18302
|
const entries = readdirSync15(targetDir, { withFileTypes: true });
|
|
18240
18303
|
const files = entries.filter((e) => !SKIP.has(e.name) && !e.name.startsWith(".")).slice(0, 50).map((e) => ({
|
|
18241
18304
|
name: e.name,
|
|
18242
|
-
path: relative4(cwd,
|
|
18305
|
+
path: relative4(cwd, join26(targetDir, e.name)).replace(/\\/g, "/"),
|
|
18243
18306
|
isDir: e.isDirectory()
|
|
18244
18307
|
}));
|
|
18245
18308
|
res.json({ files });
|
|
@@ -18275,8 +18338,8 @@ async function startWebServer(options = {}) {
|
|
|
18275
18338
|
try {
|
|
18276
18339
|
const authUser = req._authUser;
|
|
18277
18340
|
const histDir = authUser ? getUserShared(authUser).config.getHistoryDir() : config.getHistoryDir();
|
|
18278
|
-
const filePath =
|
|
18279
|
-
if (!
|
|
18341
|
+
const filePath = join26(histDir, `${id}.json`);
|
|
18342
|
+
if (!existsSync31(filePath)) {
|
|
18280
18343
|
res.status(404).json({ error: "Session not found" });
|
|
18281
18344
|
return;
|
|
18282
18345
|
}
|
|
@@ -18291,7 +18354,7 @@ async function startWebServer(options = {}) {
|
|
|
18291
18354
|
res.status(404).json({ error: "Session not found" });
|
|
18292
18355
|
return;
|
|
18293
18356
|
}
|
|
18294
|
-
const data = JSON.parse(
|
|
18357
|
+
const data = JSON.parse(readFileSync24(filePath, "utf-8"));
|
|
18295
18358
|
res.json({ session: data });
|
|
18296
18359
|
} catch (err) {
|
|
18297
18360
|
res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
|
|
@@ -18304,7 +18367,7 @@ async function startWebServer(options = {}) {
|
|
|
18304
18367
|
return;
|
|
18305
18368
|
}
|
|
18306
18369
|
const cwd = process.cwd();
|
|
18307
|
-
const fullPath = resolve11(
|
|
18370
|
+
const fullPath = resolve11(join26(cwd, filePath));
|
|
18308
18371
|
try {
|
|
18309
18372
|
const canonicalFull = realpathSync(fullPath);
|
|
18310
18373
|
const canonicalCwd = realpathSync(resolve11(cwd));
|
|
@@ -18322,7 +18385,7 @@ async function startWebServer(options = {}) {
|
|
|
18322
18385
|
res.json({ error: `File too large (${(stat.size / 1024).toFixed(0)} KB, max 512 KB)` });
|
|
18323
18386
|
return;
|
|
18324
18387
|
}
|
|
18325
|
-
const content =
|
|
18388
|
+
const content = readFileSync24(fullPath, "utf-8");
|
|
18326
18389
|
res.json({ content, size: stat.size });
|
|
18327
18390
|
} catch {
|
|
18328
18391
|
res.json({ error: "Cannot read file" });
|
|
@@ -18577,17 +18640,17 @@ function resolveProjectMcpPath() {
|
|
|
18577
18640
|
const cwd = process.cwd();
|
|
18578
18641
|
const gitRoot = getGitRoot(cwd);
|
|
18579
18642
|
const projectRoot = gitRoot ?? cwd;
|
|
18580
|
-
const configPath =
|
|
18581
|
-
return
|
|
18643
|
+
const configPath = join26(projectRoot, MCP_PROJECT_CONFIG_NAME);
|
|
18644
|
+
return existsSync31(configPath) ? configPath : null;
|
|
18582
18645
|
}
|
|
18583
18646
|
function loadProjectMcpConfig() {
|
|
18584
18647
|
const cwd = process.cwd();
|
|
18585
18648
|
const gitRoot = getGitRoot(cwd);
|
|
18586
18649
|
const projectRoot = gitRoot ?? cwd;
|
|
18587
|
-
const configPath =
|
|
18588
|
-
if (!
|
|
18650
|
+
const configPath = join26(projectRoot, MCP_PROJECT_CONFIG_NAME);
|
|
18651
|
+
if (!existsSync31(configPath)) return null;
|
|
18589
18652
|
try {
|
|
18590
|
-
const raw = JSON.parse(
|
|
18653
|
+
const raw = JSON.parse(readFileSync24(configPath, "utf-8"));
|
|
18591
18654
|
return raw.mcpServers ?? raw;
|
|
18592
18655
|
} catch {
|
|
18593
18656
|
return null;
|