jinzd-ai-cli 0.4.218 → 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 +102 -99
- package/README.zh-CN.md +40 -38
- package/dist/{batch-7V7OTMUP.js → batch-QBSKTLTO.js} +3 -3
- package/dist/{chunk-TZQHYZKT.js → chunk-5ULLIOVC.js} +1 -0
- package/dist/{chunk-P4VBLXKS.js → chunk-5WBNXYDJ.js} +2 -2
- package/dist/chunk-ALQN7VUJ.js +604 -0
- package/dist/{chunk-C2Z42DI5.js → chunk-CAUHLHDR.js} +1 -1
- package/dist/{chunk-GX3HSGJX.js → chunk-IZW5LMI6.js} +166 -638
- package/dist/chunk-P3LKFA54.js +639 -0
- package/dist/{chunk-5CA2TJ5F.js → chunk-P4DYAGUI.js} +1 -1
- package/dist/{chunk-L4UREAID.js → chunk-P4PTXCHY.js} +6 -5
- package/dist/{chunk-QMXC327F.js → chunk-PUBCJF7E.js} +213 -175
- package/dist/{chunk-NTCB7CMT.js → chunk-QZYNRCPX.js} +1 -1
- package/dist/{chunk-H2UIHGHH.js → chunk-VHX7KZDY.js} +29 -189
- package/dist/{chunk-VGFTM3XT.js → chunk-Y25MSSUP.js} +1 -1
- package/dist/{ci-L6GH2WVC.js → ci-5SNXGM3M.js} +6 -6
- package/dist/{ci-format-WW7454AY.js → ci-format-57QQTHQE.js} +2 -2
- package/dist/{constants-NCTFSHDU.js → constants-ML5KFEOH.js} +1 -1
- package/dist/doctor-cli-ZRH3XZ3R.js +17 -0
- package/dist/electron-server.js +836 -470
- package/dist/{hub-CDL6T7CP.js → hub-IPLMJ6TJ.js} +1 -1
- package/dist/index.js +125 -194
- package/dist/{pr-D6PEKEGK.js → pr-GJYDAPK4.js} +8 -7
- package/dist/{run-tests-NXVVKAK2.js → run-tests-GK5AIAV2.js} +1 -1
- package/dist/{run-tests-SWU2XEV7.js → run-tests-KLQAI5CX.js} +2 -2
- package/dist/{server-WUT7VYTD.js → server-5FZDALHH.js} +10 -10
- package/dist/{server-LHYSS6CK.js → server-RU36PRSH.js} +74 -83
- package/dist/{task-orchestrator-C5AA2BI5.js → task-orchestrator-JMHPVNUO.js} +10 -10
- package/dist/{usage-6ZUUJBI2.js → usage-IRMRCCKN.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-MWKE2TNS.js +0 -129
- package/dist/chunk-OUC75QCF.js +0 -166
- package/dist/doctor-cli-EWMFBP5Q.js +0 -226
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,17 +6658,32 @@ 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;
|
|
6629
6665
|
} catch {
|
|
6630
6666
|
}
|
|
6631
6667
|
}
|
|
6668
|
+
function getStatsSnapshot() {
|
|
6669
|
+
const s = ensureLoaded();
|
|
6670
|
+
return Object.values(s.entries).map((e) => ({ ...e }));
|
|
6671
|
+
}
|
|
6672
|
+
function getTopFailingTools(limit = 5) {
|
|
6673
|
+
return getStatsSnapshot().filter((e) => e.failures > 0).sort((a, b) => {
|
|
6674
|
+
const rateA = a.failures / Math.max(1, a.calls);
|
|
6675
|
+
const rateB = b.failures / Math.max(1, b.calls);
|
|
6676
|
+
if (rateB !== rateA) return rateB - rateA;
|
|
6677
|
+
return b.failures - a.failures;
|
|
6678
|
+
}).slice(0, limit);
|
|
6679
|
+
}
|
|
6680
|
+
function getTopUsedTools(limit = 5) {
|
|
6681
|
+
return getStatsSnapshot().sort((a, b) => b.calls - a.calls).slice(0, limit);
|
|
6682
|
+
}
|
|
6632
6683
|
|
|
6633
6684
|
// src/tools/action-classifier.ts
|
|
6634
|
-
import { existsSync as
|
|
6635
|
-
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";
|
|
6636
6687
|
var RECENT_DENIED_LIMIT = 50;
|
|
6637
6688
|
var recentlyDenied = [];
|
|
6638
6689
|
function recordRecentlyDeniedAutoAction(call, classification) {
|
|
@@ -6673,10 +6724,10 @@ function splitShellWords(command) {
|
|
|
6673
6724
|
return out.filter(Boolean);
|
|
6674
6725
|
}
|
|
6675
6726
|
function readPackageManifest(root) {
|
|
6676
|
-
const p =
|
|
6677
|
-
if (!
|
|
6727
|
+
const p = join6(root, "package.json");
|
|
6728
|
+
if (!existsSync9(p)) return null;
|
|
6678
6729
|
try {
|
|
6679
|
-
return JSON.parse(
|
|
6730
|
+
return JSON.parse(readFileSync8(p, "utf-8"));
|
|
6680
6731
|
} catch {
|
|
6681
6732
|
return null;
|
|
6682
6733
|
}
|
|
@@ -6692,10 +6743,10 @@ function manifestHasDependency(root, name) {
|
|
|
6692
6743
|
}
|
|
6693
6744
|
function lockfileMentions(root, name) {
|
|
6694
6745
|
for (const file of ["package-lock.json", "pnpm-lock.yaml", "yarn.lock"]) {
|
|
6695
|
-
const p =
|
|
6696
|
-
if (!
|
|
6746
|
+
const p = join6(root, file);
|
|
6747
|
+
if (!existsSync9(p)) continue;
|
|
6697
6748
|
try {
|
|
6698
|
-
if (
|
|
6749
|
+
if (readFileSync8(p, "utf-8").includes(name)) return true;
|
|
6699
6750
|
} catch {
|
|
6700
6751
|
}
|
|
6701
6752
|
}
|
|
@@ -7259,10 +7310,10 @@ var ToolExecutor = class {
|
|
|
7259
7310
|
const filePath = String(call.arguments["path"] ?? "");
|
|
7260
7311
|
const newContent = String(call.arguments["content"] ?? "");
|
|
7261
7312
|
if (!filePath) return;
|
|
7262
|
-
if (
|
|
7313
|
+
if (existsSync10(filePath)) {
|
|
7263
7314
|
let oldContent;
|
|
7264
7315
|
try {
|
|
7265
|
-
oldContent =
|
|
7316
|
+
oldContent = readFileSync9(filePath, "utf-8");
|
|
7266
7317
|
} catch {
|
|
7267
7318
|
return;
|
|
7268
7319
|
}
|
|
@@ -7285,7 +7336,7 @@ var ToolExecutor = class {
|
|
|
7285
7336
|
}
|
|
7286
7337
|
} else if (call.name === "edit_file") {
|
|
7287
7338
|
const filePath = String(call.arguments["path"] ?? "");
|
|
7288
|
-
if (!filePath || !
|
|
7339
|
+
if (!filePath || !existsSync10(filePath)) return;
|
|
7289
7340
|
const oldStr = call.arguments["old_str"];
|
|
7290
7341
|
const newStr = call.arguments["new_str"];
|
|
7291
7342
|
if (oldStr !== void 0) {
|
|
@@ -7312,7 +7363,7 @@ var ToolExecutor = class {
|
|
|
7312
7363
|
const to = Number(call.arguments["delete_to_line"] ?? from);
|
|
7313
7364
|
let fileContent;
|
|
7314
7365
|
try {
|
|
7315
|
-
fileContent =
|
|
7366
|
+
fileContent = readFileSync9(filePath, "utf-8");
|
|
7316
7367
|
} catch {
|
|
7317
7368
|
return;
|
|
7318
7369
|
}
|
|
@@ -7547,7 +7598,7 @@ Do NOT split a long document into many write_file(append=true) calls. That patte
|
|
|
7547
7598
|
}
|
|
7548
7599
|
undoStack.push(filePath, `write_file${appendMode ? " (append)" : ""}: ${filePath}`);
|
|
7549
7600
|
fileCheckpoints.snapshot(filePath, ToolExecutor.currentMessageIndex);
|
|
7550
|
-
|
|
7601
|
+
mkdirSync6(dirname4(filePath), { recursive: true });
|
|
7551
7602
|
if (appendMode) {
|
|
7552
7603
|
appendFileSync(filePath, content, encoding);
|
|
7553
7604
|
} else {
|
|
@@ -7567,7 +7618,7 @@ Do NOT split a long document into many write_file(append=true) calls. That patte
|
|
|
7567
7618
|
};
|
|
7568
7619
|
|
|
7569
7620
|
// src/tools/builtin/edit-file.ts
|
|
7570
|
-
import { readFileSync as
|
|
7621
|
+
import { readFileSync as readFileSync10, existsSync as existsSync11 } from "fs";
|
|
7571
7622
|
|
|
7572
7623
|
// src/tools/builtin/patch-apply.ts
|
|
7573
7624
|
function parseUnifiedDiff(patch) {
|
|
@@ -7928,7 +7979,7 @@ Note: Path can be absolute or relative to cwd.`,
|
|
|
7928
7979
|
const filePath = String(args["path"] ?? "");
|
|
7929
7980
|
const encoding = args["encoding"] ?? "utf-8";
|
|
7930
7981
|
if (!filePath) throw new ToolError("edit_file", "path is required");
|
|
7931
|
-
if (!
|
|
7982
|
+
if (!existsSync11(filePath)) throw new ToolError("edit_file", `File not found: ${filePath}`);
|
|
7932
7983
|
const verdict = classifyWritePath(filePath);
|
|
7933
7984
|
if (verdict.sensitive && subAgentGuard.active) {
|
|
7934
7985
|
throw new ToolError(
|
|
@@ -7936,7 +7987,7 @@ Note: Path can be absolute or relative to cwd.`,
|
|
|
7936
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.`
|
|
7937
7988
|
);
|
|
7938
7989
|
}
|
|
7939
|
-
const original =
|
|
7990
|
+
const original = readFileSync10(filePath, encoding);
|
|
7940
7991
|
if (args["patch"] !== void 0) {
|
|
7941
7992
|
const patchText = String(args["patch"] ?? "");
|
|
7942
7993
|
const stopOnError = args["stop_on_error"] !== false;
|
|
@@ -8094,8 +8145,8 @@ function truncatePreview(str, maxLen = 80) {
|
|
|
8094
8145
|
}
|
|
8095
8146
|
|
|
8096
8147
|
// src/tools/builtin/list-dir.ts
|
|
8097
|
-
import { readdirSync as readdirSync4, statSync as statSync3, existsSync as
|
|
8098
|
-
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";
|
|
8099
8150
|
var listDirTool = {
|
|
8100
8151
|
definition: {
|
|
8101
8152
|
name: "list_dir",
|
|
@@ -8117,7 +8168,7 @@ var listDirTool = {
|
|
|
8117
8168
|
async execute(args) {
|
|
8118
8169
|
const dirPath = String(args["path"] ?? process.cwd());
|
|
8119
8170
|
const recursive = Boolean(args["recursive"] ?? false);
|
|
8120
|
-
if (!
|
|
8171
|
+
if (!existsSync12(dirPath)) {
|
|
8121
8172
|
const targetName = basename3(dirPath).toLowerCase();
|
|
8122
8173
|
const cwd = process.cwd();
|
|
8123
8174
|
const suggestions = [];
|
|
@@ -8175,11 +8226,11 @@ function listRecursive(basePath, indent, recursive, lines) {
|
|
|
8175
8226
|
if (entry.isDirectory()) {
|
|
8176
8227
|
lines.push(`${indent}\u{1F4C1} ${entry.name}/`);
|
|
8177
8228
|
if (recursive) {
|
|
8178
|
-
listRecursive(
|
|
8229
|
+
listRecursive(join7(basePath, entry.name), indent + " ", true, lines);
|
|
8179
8230
|
}
|
|
8180
8231
|
} else {
|
|
8181
8232
|
try {
|
|
8182
|
-
const stat = statSync3(
|
|
8233
|
+
const stat = statSync3(join7(basePath, entry.name));
|
|
8183
8234
|
const size = formatSize(stat.size);
|
|
8184
8235
|
lines.push(`${indent}\u{1F4C4} ${entry.name} (${size})`);
|
|
8185
8236
|
} catch {
|
|
@@ -8195,9 +8246,9 @@ function formatSize(bytes) {
|
|
|
8195
8246
|
}
|
|
8196
8247
|
|
|
8197
8248
|
// src/tools/builtin/grep-files.ts
|
|
8198
|
-
import { readdirSync as readdirSync5, readFileSync as
|
|
8249
|
+
import { readdirSync as readdirSync5, readFileSync as readFileSync11, statSync as statSync4, existsSync as existsSync13 } from "fs";
|
|
8199
8250
|
import { readFile } from "fs/promises";
|
|
8200
|
-
import { join as
|
|
8251
|
+
import { join as join8, relative } from "path";
|
|
8201
8252
|
var grepFilesTool = {
|
|
8202
8253
|
definition: {
|
|
8203
8254
|
name: "grep_files",
|
|
@@ -8248,7 +8299,7 @@ Supports regex. Automatically skips node_modules, dist, .git directories.`,
|
|
|
8248
8299
|
const contextLines = Math.max(0, Number(args["context_lines"] ?? 0));
|
|
8249
8300
|
const maxResults = Math.max(1, Number(args["max_results"] ?? 50));
|
|
8250
8301
|
if (!pattern) throw new ToolError("grep_files", "pattern is required");
|
|
8251
|
-
if (!
|
|
8302
|
+
if (!existsSync13(rootPath)) throw new ToolError("grep_files", `Path not found: ${rootPath}`);
|
|
8252
8303
|
const MAX_PATTERN_LENGTH = 1e3;
|
|
8253
8304
|
if (pattern.length > MAX_PATTERN_LENGTH) {
|
|
8254
8305
|
throw new ToolError("grep_files", `Pattern too long (${pattern.length} chars, max ${MAX_PATTERN_LENGTH}). Use a shorter pattern.`);
|
|
@@ -8342,11 +8393,11 @@ function collectFilePaths(rootPath, filePattern, maxFiles) {
|
|
|
8342
8393
|
if (paths.length >= maxFiles) return;
|
|
8343
8394
|
if (entry.isDirectory()) {
|
|
8344
8395
|
if (SKIP_DIRS.has(entry.name) || entry.name.startsWith(".")) continue;
|
|
8345
|
-
walk(
|
|
8396
|
+
walk(join8(dirPath, entry.name));
|
|
8346
8397
|
} else if (entry.isFile()) {
|
|
8347
8398
|
if (isBinary(entry.name)) continue;
|
|
8348
8399
|
if (filePattern && !matchesFilePattern(entry.name, filePattern)) continue;
|
|
8349
|
-
const fullPath =
|
|
8400
|
+
const fullPath = join8(dirPath, entry.name);
|
|
8350
8401
|
try {
|
|
8351
8402
|
if (statSync4(fullPath).size > 1e6) continue;
|
|
8352
8403
|
} catch {
|
|
@@ -8401,7 +8452,7 @@ function searchInFile(fullPath, displayPath2, regex, contextLines, maxResults, r
|
|
|
8401
8452
|
}
|
|
8402
8453
|
let content;
|
|
8403
8454
|
try {
|
|
8404
|
-
content =
|
|
8455
|
+
content = readFileSync11(fullPath, "utf-8");
|
|
8405
8456
|
} catch {
|
|
8406
8457
|
return;
|
|
8407
8458
|
}
|
|
@@ -8432,8 +8483,8 @@ function searchInFile(fullPath, displayPath2, regex, contextLines, maxResults, r
|
|
|
8432
8483
|
}
|
|
8433
8484
|
|
|
8434
8485
|
// src/tools/builtin/glob-files.ts
|
|
8435
|
-
import { readdirSync as readdirSync6, statSync as statSync5, existsSync as
|
|
8436
|
-
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";
|
|
8437
8488
|
var globFilesTool = {
|
|
8438
8489
|
definition: {
|
|
8439
8490
|
name: "glob_files",
|
|
@@ -8466,7 +8517,7 @@ Results sorted by most recent modification time. Automatically skips node_module
|
|
|
8466
8517
|
const rootPath = String(args["path"] ?? process.cwd());
|
|
8467
8518
|
const maxResults = Math.max(1, Number(args["max_results"] ?? 100));
|
|
8468
8519
|
if (!pattern) throw new ToolError("glob_files", "pattern is required");
|
|
8469
|
-
if (!
|
|
8520
|
+
if (!existsSync14(rootPath)) throw new ToolError("glob_files", `Path not found: ${rootPath}`);
|
|
8470
8521
|
const regex = globToRegex(pattern);
|
|
8471
8522
|
const matches = [];
|
|
8472
8523
|
collectMatchingFiles(rootPath, rootPath, regex, matches, maxResults);
|
|
@@ -8543,7 +8594,7 @@ function collectMatchingFiles(dirPath, rootPath, regex, results, maxResults) {
|
|
|
8543
8594
|
}
|
|
8544
8595
|
for (const entry of entries) {
|
|
8545
8596
|
if (results.length >= maxResults) break;
|
|
8546
|
-
const fullPath =
|
|
8597
|
+
const fullPath = join9(dirPath, entry.name);
|
|
8547
8598
|
if (entry.isDirectory()) {
|
|
8548
8599
|
if (SKIP_DIRS2.has(entry.name) || entry.name.startsWith(".")) continue;
|
|
8549
8600
|
collectMatchingFiles(fullPath, rootPath, regex, results, maxResults);
|
|
@@ -9283,16 +9334,16 @@ ${preamble}`;
|
|
|
9283
9334
|
}
|
|
9284
9335
|
|
|
9285
9336
|
// src/tools/builtin/save-last-response.ts
|
|
9286
|
-
import { mkdirSync as
|
|
9287
|
-
import { dirname as
|
|
9337
|
+
import { mkdirSync as mkdirSync7, unlinkSync as unlinkSync4, rmdirSync as rmdirSync2 } from "fs";
|
|
9338
|
+
import { dirname as dirname5 } from "path";
|
|
9288
9339
|
var lastResponseStore = { content: "" };
|
|
9289
9340
|
function cleanupRejectedTeeFile(filePath) {
|
|
9290
9341
|
try {
|
|
9291
|
-
|
|
9342
|
+
unlinkSync4(filePath);
|
|
9292
9343
|
} catch {
|
|
9293
9344
|
}
|
|
9294
9345
|
try {
|
|
9295
|
-
rmdirSync2(
|
|
9346
|
+
rmdirSync2(dirname5(filePath));
|
|
9296
9347
|
} catch {
|
|
9297
9348
|
}
|
|
9298
9349
|
}
|
|
@@ -9333,7 +9384,7 @@ Any of these triggers means use save_last_response, NOT write_file:
|
|
|
9333
9384
|
throw new ToolError("save_last_response", "No content to save: AI has not produced any response yet, or the last response was empty.");
|
|
9334
9385
|
}
|
|
9335
9386
|
undoStack.push(filePath, `save_last_response: ${filePath}`);
|
|
9336
|
-
|
|
9387
|
+
mkdirSync7(dirname5(filePath), { recursive: true });
|
|
9337
9388
|
atomicWriteFileSync(filePath, content);
|
|
9338
9389
|
const lines = content.split("\n").length;
|
|
9339
9390
|
return `File saved: ${filePath} (${lines} lines, ${content.length} bytes)`;
|
|
@@ -9341,18 +9392,18 @@ Any of these triggers means use save_last_response, NOT write_file:
|
|
|
9341
9392
|
};
|
|
9342
9393
|
|
|
9343
9394
|
// src/tools/builtin/save-memory.ts
|
|
9344
|
-
import { join as
|
|
9395
|
+
import { join as join12 } from "path";
|
|
9345
9396
|
import { homedir as homedir5 } from "os";
|
|
9346
9397
|
|
|
9347
9398
|
// src/memory/persistent-memory.ts
|
|
9348
|
-
import { existsSync as
|
|
9399
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync8, readFileSync as readFileSync12 } from "fs";
|
|
9349
9400
|
import { randomUUID, createHash as createHash2 } from "crypto";
|
|
9350
|
-
import { dirname as
|
|
9401
|
+
import { dirname as dirname6, join as join11, resolve as resolve6 } from "path";
|
|
9351
9402
|
|
|
9352
9403
|
// src/tools/git-context.ts
|
|
9353
9404
|
import { execSync as execSync2 } from "child_process";
|
|
9354
|
-
import { existsSync as
|
|
9355
|
-
import { join as
|
|
9405
|
+
import { existsSync as existsSync15 } from "fs";
|
|
9406
|
+
import { join as join10 } from "path";
|
|
9356
9407
|
function runGit(cmd, cwd) {
|
|
9357
9408
|
try {
|
|
9358
9409
|
return execSync2(`git ${cmd}`, {
|
|
@@ -9369,7 +9420,7 @@ function getGitRoot(cwd = process.cwd()) {
|
|
|
9369
9420
|
return runGit("rev-parse --show-toplevel", cwd);
|
|
9370
9421
|
}
|
|
9371
9422
|
function getGitContext(cwd = process.cwd()) {
|
|
9372
|
-
if (!
|
|
9423
|
+
if (!existsSync15(join10(cwd, ".git"))) {
|
|
9373
9424
|
const result = runGit("rev-parse --git-dir", cwd);
|
|
9374
9425
|
if (!result) return null;
|
|
9375
9426
|
}
|
|
@@ -9441,10 +9492,10 @@ function formatGitContextForPrompt(ctx) {
|
|
|
9441
9492
|
|
|
9442
9493
|
// src/memory/persistent-memory.ts
|
|
9443
9494
|
function memoryStorePath(configDir) {
|
|
9444
|
-
return
|
|
9495
|
+
return join11(configDir, MEMORY_STORE_FILE_NAME);
|
|
9445
9496
|
}
|
|
9446
9497
|
function memoryMarkdownPath(configDir) {
|
|
9447
|
-
return
|
|
9498
|
+
return join11(configDir, MEMORY_FILE_NAME);
|
|
9448
9499
|
}
|
|
9449
9500
|
function getProjectKey(cwd = process.cwd()) {
|
|
9450
9501
|
return resolve6(getGitRoot(cwd) ?? cwd);
|
|
@@ -9480,8 +9531,8 @@ function normalizeEntry(value) {
|
|
|
9480
9531
|
}
|
|
9481
9532
|
function legacyEntriesFromMarkdown(configDir) {
|
|
9482
9533
|
const file = memoryMarkdownPath(configDir);
|
|
9483
|
-
if (!
|
|
9484
|
-
const content =
|
|
9534
|
+
if (!existsSync16(file)) return [];
|
|
9535
|
+
const content = readFileSync12(file, "utf-8").trim();
|
|
9485
9536
|
if (!content) return [];
|
|
9486
9537
|
const parts = content.split(/\n(?=##\s+\d{4}-\d{2}-\d{2})/g);
|
|
9487
9538
|
const chunks = parts.length > 1 ? parts : [content];
|
|
@@ -9497,8 +9548,8 @@ ${chunk}`).digest("hex").slice(0, 12);
|
|
|
9497
9548
|
}
|
|
9498
9549
|
function loadMemoryEntries(configDir) {
|
|
9499
9550
|
const file = memoryStorePath(configDir);
|
|
9500
|
-
if (!
|
|
9501
|
-
const text =
|
|
9551
|
+
if (!existsSync16(file)) return legacyEntriesFromMarkdown(configDir);
|
|
9552
|
+
const text = readFileSync12(file, "utf-8").trim();
|
|
9502
9553
|
if (!text) return [];
|
|
9503
9554
|
const entries = [];
|
|
9504
9555
|
for (const line of text.split("\n")) {
|
|
@@ -9513,7 +9564,7 @@ function loadMemoryEntries(configDir) {
|
|
|
9513
9564
|
return entries;
|
|
9514
9565
|
}
|
|
9515
9566
|
function saveMemoryEntries(configDir, entries) {
|
|
9516
|
-
|
|
9567
|
+
mkdirSync8(configDir, { recursive: true });
|
|
9517
9568
|
const jsonl = entries.map((entry) => JSON.stringify(entry)).join("\n");
|
|
9518
9569
|
atomicWriteFileSync(memoryStorePath(configDir), jsonl ? jsonl + "\n" : "");
|
|
9519
9570
|
syncLegacyMarkdown(configDir, entries);
|
|
@@ -9588,7 +9639,7 @@ function findUnique(entries, idPrefix) {
|
|
|
9588
9639
|
return matches[0];
|
|
9589
9640
|
}
|
|
9590
9641
|
function syncLegacyMarkdown(configDir, entries = loadMemoryEntries(configDir)) {
|
|
9591
|
-
|
|
9642
|
+
mkdirSync8(dirname6(memoryMarkdownPath(configDir)), { recursive: true });
|
|
9592
9643
|
const active = entries.filter((entry) => entry.approved && !isMemoryExpired(entry));
|
|
9593
9644
|
const markdown = active.map((entry) => {
|
|
9594
9645
|
const date = entry.createdAt.replace("T", " ").slice(0, 19);
|
|
@@ -9631,7 +9682,7 @@ ${entry.content}`;
|
|
|
9631
9682
|
|
|
9632
9683
|
// src/tools/builtin/save-memory.ts
|
|
9633
9684
|
function getConfigDir() {
|
|
9634
|
-
return
|
|
9685
|
+
return join12(homedir5(), CONFIG_DIR_NAME);
|
|
9635
9686
|
}
|
|
9636
9687
|
function parseScope(value) {
|
|
9637
9688
|
return value === "personal" || value === "project" || value === "session" || value === "team" ? value : void 0;
|
|
@@ -9965,13 +10016,13 @@ function formatResults2(query, data, _requested) {
|
|
|
9965
10016
|
}
|
|
9966
10017
|
|
|
9967
10018
|
// src/agents/agent-config.ts
|
|
9968
|
-
import { existsSync as
|
|
9969
|
-
import { join as
|
|
10019
|
+
import { existsSync as existsSync18, readdirSync as readdirSync8, readFileSync as readFileSync14 } from "fs";
|
|
10020
|
+
import { join as join14 } from "path";
|
|
9970
10021
|
import { homedir as homedir6 } from "os";
|
|
9971
10022
|
|
|
9972
10023
|
// src/plugins/plugin-manager.ts
|
|
9973
|
-
import { existsSync as
|
|
9974
|
-
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";
|
|
9975
10026
|
import { createHash as createHash3 } from "crypto";
|
|
9976
10027
|
import { z as z2 } from "zod";
|
|
9977
10028
|
var MANIFEST_RELATIVE = ".aicli-plugin/plugin.json";
|
|
@@ -10031,48 +10082,48 @@ var PluginManifestSchema = z2.object({
|
|
|
10031
10082
|
permissionHints: z2.array(z2.string()).default([])
|
|
10032
10083
|
}).strict();
|
|
10033
10084
|
function pluginRoot(configDir) {
|
|
10034
|
-
return
|
|
10085
|
+
return join13(configDir, PLUGINS_DIR_NAME);
|
|
10035
10086
|
}
|
|
10036
10087
|
function pluginManifestPath(pluginDir) {
|
|
10037
|
-
return
|
|
10088
|
+
return join13(pluginDir, MANIFEST_RELATIVE);
|
|
10038
10089
|
}
|
|
10039
10090
|
function statePath(configDir) {
|
|
10040
|
-
return
|
|
10091
|
+
return join13(pluginRoot(configDir), STATE_FILE);
|
|
10041
10092
|
}
|
|
10042
10093
|
function loadState(configDir) {
|
|
10043
10094
|
const file = statePath(configDir);
|
|
10044
|
-
if (!
|
|
10095
|
+
if (!existsSync17(file)) return { version: 1, plugins: [] };
|
|
10045
10096
|
try {
|
|
10046
|
-
const parsed = JSON.parse(
|
|
10097
|
+
const parsed = JSON.parse(readFileSync13(file, "utf-8"));
|
|
10047
10098
|
return { version: 1, plugins: Array.isArray(parsed.plugins) ? parsed.plugins : [] };
|
|
10048
10099
|
} catch {
|
|
10049
10100
|
return { version: 1, plugins: [] };
|
|
10050
10101
|
}
|
|
10051
10102
|
}
|
|
10052
10103
|
function saveState(configDir, state2) {
|
|
10053
|
-
|
|
10104
|
+
mkdirSync9(pluginRoot(configDir), { recursive: true });
|
|
10054
10105
|
atomicWriteFileSync(statePath(configDir), JSON.stringify(state2, null, 2));
|
|
10055
10106
|
}
|
|
10056
10107
|
function hashPluginManifest(manifestPath) {
|
|
10057
|
-
return createHash3("sha256").update(
|
|
10108
|
+
return createHash3("sha256").update(readFileSync13(manifestPath)).digest("hex");
|
|
10058
10109
|
}
|
|
10059
10110
|
function readPluginManifest(manifestPath) {
|
|
10060
|
-
return PluginManifestSchema.parse(JSON.parse(
|
|
10111
|
+
return PluginManifestSchema.parse(JSON.parse(readFileSync13(manifestPath, "utf-8")));
|
|
10061
10112
|
}
|
|
10062
10113
|
function findPluginSourceDir(inputPath) {
|
|
10063
10114
|
const abs = resolve7(inputPath);
|
|
10064
|
-
const direct = statSync6(abs).isDirectory() ? abs :
|
|
10065
|
-
if (
|
|
10066
|
-
const nested =
|
|
10067
|
-
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;
|
|
10068
10119
|
throw new Error(`plugin manifest not found: ${MANIFEST_RELATIVE}`);
|
|
10069
10120
|
}
|
|
10070
10121
|
function installPlugin(configDir, inputPath) {
|
|
10071
10122
|
const sourceDir = findPluginSourceDir(inputPath);
|
|
10072
10123
|
const manifest = readPluginManifest(pluginManifestPath(sourceDir));
|
|
10073
|
-
const targetDir =
|
|
10074
|
-
|
|
10075
|
-
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 });
|
|
10076
10127
|
cpSync(sourceDir, targetDir, { recursive: true });
|
|
10077
10128
|
const manifestPath = pluginManifestPath(targetDir);
|
|
10078
10129
|
const hash = hashPluginManifest(manifestPath);
|
|
@@ -10130,18 +10181,18 @@ function getInstalledPlugin(configDir, name) {
|
|
|
10130
10181
|
}
|
|
10131
10182
|
function listInstalledPlugins(configDir) {
|
|
10132
10183
|
const root = pluginRoot(configDir);
|
|
10133
|
-
if (!
|
|
10184
|
+
if (!existsSync17(root)) return [];
|
|
10134
10185
|
const state2 = loadState(configDir);
|
|
10135
10186
|
const out = [];
|
|
10136
10187
|
for (const entry of readdirSync7(root)) {
|
|
10137
|
-
const dir =
|
|
10188
|
+
const dir = join13(root, entry);
|
|
10138
10189
|
try {
|
|
10139
10190
|
if (!statSync6(dir).isDirectory()) continue;
|
|
10140
10191
|
} catch {
|
|
10141
10192
|
continue;
|
|
10142
10193
|
}
|
|
10143
10194
|
const manifestPath = pluginManifestPath(dir);
|
|
10144
|
-
if (!
|
|
10195
|
+
if (!existsSync17(manifestPath)) continue;
|
|
10145
10196
|
try {
|
|
10146
10197
|
const manifest = readPluginManifest(manifestPath);
|
|
10147
10198
|
const hash = hashPluginManifest(manifestPath);
|
|
@@ -10181,7 +10232,7 @@ function uniqueDirs(plugin, rels) {
|
|
|
10181
10232
|
for (const rel of rels) {
|
|
10182
10233
|
const abs = resolve7(plugin.dir, rel);
|
|
10183
10234
|
if (!abs.startsWith(resolve7(plugin.dir))) continue;
|
|
10184
|
-
if (
|
|
10235
|
+
if (existsSync17(abs)) dirs.add(statSync6(abs).isDirectory() ? abs : dirname7(abs));
|
|
10185
10236
|
}
|
|
10186
10237
|
return [...dirs];
|
|
10187
10238
|
}
|
|
@@ -10344,13 +10395,13 @@ function parseAgentConfig(raw, source, path3) {
|
|
|
10344
10395
|
return cfg;
|
|
10345
10396
|
}
|
|
10346
10397
|
function loadAgentDir(dir, source) {
|
|
10347
|
-
if (!
|
|
10398
|
+
if (!existsSync18(dir)) return [];
|
|
10348
10399
|
const out = [];
|
|
10349
10400
|
for (const file of readdirSync8(dir)) {
|
|
10350
10401
|
if (!file.endsWith(".json")) continue;
|
|
10351
|
-
const path3 =
|
|
10402
|
+
const path3 = join14(dir, file);
|
|
10352
10403
|
try {
|
|
10353
|
-
const parsed = JSON.parse(
|
|
10404
|
+
const parsed = JSON.parse(readFileSync14(path3, "utf-8"));
|
|
10354
10405
|
const cfg = parseAgentConfig(parsed, source, path3);
|
|
10355
10406
|
if (cfg) out.push(cfg);
|
|
10356
10407
|
} catch {
|
|
@@ -10360,8 +10411,8 @@ function loadAgentDir(dir, source) {
|
|
|
10360
10411
|
}
|
|
10361
10412
|
function getAgentDirs(configDir, cwd = process.cwd()) {
|
|
10362
10413
|
return {
|
|
10363
|
-
user:
|
|
10364
|
-
project:
|
|
10414
|
+
user: join14(configDir ?? join14(homedir6(), CONFIG_DIR_NAME), "agents"),
|
|
10415
|
+
project: join14(cwd, CONFIG_DIR_NAME, "agents")
|
|
10365
10416
|
};
|
|
10366
10417
|
}
|
|
10367
10418
|
function listAgentConfigs(configDir, cwd = process.cwd()) {
|
|
@@ -11076,14 +11127,14 @@ var taskStopTool = {
|
|
|
11076
11127
|
|
|
11077
11128
|
// src/tools/builtin/git-tools.ts
|
|
11078
11129
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
11079
|
-
import { existsSync as
|
|
11080
|
-
import { join as
|
|
11130
|
+
import { existsSync as existsSync19 } from "fs";
|
|
11131
|
+
import { join as join15 } from "path";
|
|
11081
11132
|
function assertGitRepo(cwd) {
|
|
11082
11133
|
let dir = cwd;
|
|
11083
11134
|
const root = dir.split(/[\\/]/)[0] + (dir.includes("\\") ? "\\" : "/");
|
|
11084
11135
|
while (dir && dir !== root) {
|
|
11085
|
-
if (
|
|
11086
|
-
const parent =
|
|
11136
|
+
if (existsSync19(join15(dir, ".git"))) return;
|
|
11137
|
+
const parent = join15(dir, "..");
|
|
11087
11138
|
if (parent === dir) break;
|
|
11088
11139
|
dir = parent;
|
|
11089
11140
|
}
|
|
@@ -11348,7 +11399,7 @@ ${commitOutput.trim()}`;
|
|
|
11348
11399
|
};
|
|
11349
11400
|
|
|
11350
11401
|
// src/tools/builtin/notebook-edit.ts
|
|
11351
|
-
import { readFileSync as
|
|
11402
|
+
import { readFileSync as readFileSync15, existsSync as existsSync20 } from "fs";
|
|
11352
11403
|
import { writeFile } from "fs/promises";
|
|
11353
11404
|
import { resolve as resolve8, extname as extname2 } from "path";
|
|
11354
11405
|
var notebookEditTool = {
|
|
@@ -11403,10 +11454,10 @@ var notebookEditTool = {
|
|
|
11403
11454
|
if (extname2(absPath).toLowerCase() !== ".ipynb") {
|
|
11404
11455
|
throw new ToolError("notebook_edit", "path must point to a .ipynb file");
|
|
11405
11456
|
}
|
|
11406
|
-
if (!
|
|
11457
|
+
if (!existsSync20(absPath)) {
|
|
11407
11458
|
throw new ToolError("notebook_edit", `Notebook not found: ${filePath}`);
|
|
11408
11459
|
}
|
|
11409
|
-
const raw =
|
|
11460
|
+
const raw = readFileSync15(absPath, "utf-8");
|
|
11410
11461
|
const nb = parseNotebook(raw);
|
|
11411
11462
|
const cellIdx0 = cellIndexRaw - 1;
|
|
11412
11463
|
undoStack.push(absPath, `notebook_edit (${action}): ${filePath}`);
|
|
@@ -11823,8 +11874,8 @@ function estimateToolDefinitionTokens(def) {
|
|
|
11823
11874
|
|
|
11824
11875
|
// src/tools/registry.ts
|
|
11825
11876
|
import { pathToFileURL } from "url";
|
|
11826
|
-
import { existsSync as
|
|
11827
|
-
import { join as
|
|
11877
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync10, readdirSync as readdirSync9 } from "fs";
|
|
11878
|
+
import { join as join16 } from "path";
|
|
11828
11879
|
var ToolRegistry = class {
|
|
11829
11880
|
tools = /* @__PURE__ */ new Map();
|
|
11830
11881
|
pluginToolNames = /* @__PURE__ */ new Set();
|
|
@@ -11985,9 +12036,9 @@ var ToolRegistry = class {
|
|
|
11985
12036
|
* Returns the number of successfully loaded plugins.
|
|
11986
12037
|
*/
|
|
11987
12038
|
async loadPlugins(pluginsDir, allowPlugins = false) {
|
|
11988
|
-
if (!
|
|
12039
|
+
if (!existsSync21(pluginsDir)) {
|
|
11989
12040
|
try {
|
|
11990
|
-
|
|
12041
|
+
mkdirSync10(pluginsDir, { recursive: true });
|
|
11991
12042
|
} catch {
|
|
11992
12043
|
}
|
|
11993
12044
|
return 0;
|
|
@@ -12011,12 +12062,12 @@ var ToolRegistry = class {
|
|
|
12011
12062
|
process.stderr.write(
|
|
12012
12063
|
`
|
|
12013
12064
|
[plugins] \u26A0 Loading ${files.length} plugin(s) with FULL system privileges:
|
|
12014
|
-
` + files.map((f) => ` + ${
|
|
12065
|
+
` + files.map((f) => ` + ${join16(pluginsDir, f)}`).join("\n") + "\n\n"
|
|
12015
12066
|
);
|
|
12016
12067
|
let loaded = 0;
|
|
12017
12068
|
for (const file of files) {
|
|
12018
12069
|
try {
|
|
12019
|
-
const fileUrl = pathToFileURL(
|
|
12070
|
+
const fileUrl = pathToFileURL(join16(pluginsDir, file)).href;
|
|
12020
12071
|
const mod = await import(fileUrl);
|
|
12021
12072
|
const tool = mod.tool ?? mod.default?.tool ?? mod.default;
|
|
12022
12073
|
if (!tool || typeof tool.execute !== "function" || !tool.definition?.name) {
|
|
@@ -12562,11 +12613,11 @@ var McpManager = class {
|
|
|
12562
12613
|
};
|
|
12563
12614
|
|
|
12564
12615
|
// src/skills/manager.ts
|
|
12565
|
-
import { existsSync as
|
|
12566
|
-
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";
|
|
12567
12618
|
|
|
12568
12619
|
// src/skills/types.ts
|
|
12569
|
-
import { readFileSync as
|
|
12620
|
+
import { readFileSync as readFileSync16 } from "fs";
|
|
12570
12621
|
import { basename as basename6 } from "path";
|
|
12571
12622
|
function parseSimpleYaml(yaml) {
|
|
12572
12623
|
const result = {};
|
|
@@ -12588,7 +12639,7 @@ function parseYamlArray(value) {
|
|
|
12588
12639
|
function parseSkillFile(filePath) {
|
|
12589
12640
|
let raw;
|
|
12590
12641
|
try {
|
|
12591
|
-
raw =
|
|
12642
|
+
raw = readFileSync16(filePath, "utf-8");
|
|
12592
12643
|
} catch {
|
|
12593
12644
|
return null;
|
|
12594
12645
|
}
|
|
@@ -12636,10 +12687,10 @@ var SkillManager = class {
|
|
|
12636
12687
|
return this.skills.size;
|
|
12637
12688
|
}
|
|
12638
12689
|
loadSkillDir(dir, createIfMissing) {
|
|
12639
|
-
if (!
|
|
12690
|
+
if (!existsSync22(dir)) {
|
|
12640
12691
|
if (createIfMissing) {
|
|
12641
12692
|
try {
|
|
12642
|
-
|
|
12693
|
+
mkdirSync11(dir, { recursive: true });
|
|
12643
12694
|
} catch {
|
|
12644
12695
|
}
|
|
12645
12696
|
}
|
|
@@ -12653,14 +12704,14 @@ var SkillManager = class {
|
|
|
12653
12704
|
}
|
|
12654
12705
|
for (const entry of entries) {
|
|
12655
12706
|
let filePath;
|
|
12656
|
-
const fullPath =
|
|
12707
|
+
const fullPath = join17(dir, entry);
|
|
12657
12708
|
if (entry.endsWith(".md")) {
|
|
12658
12709
|
filePath = fullPath;
|
|
12659
12710
|
} else {
|
|
12660
12711
|
try {
|
|
12661
12712
|
if (statSync7(fullPath).isDirectory()) {
|
|
12662
|
-
const skillMd =
|
|
12663
|
-
if (
|
|
12713
|
+
const skillMd = join17(fullPath, "SKILL.md");
|
|
12714
|
+
if (existsSync22(skillMd)) filePath = skillMd;
|
|
12664
12715
|
else continue;
|
|
12665
12716
|
} else {
|
|
12666
12717
|
continue;
|
|
@@ -12719,7 +12770,7 @@ var SkillManager = class {
|
|
|
12719
12770
|
// src/web/tool-executor-web.ts
|
|
12720
12771
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
12721
12772
|
import { tmpdir as tmpdir2 } from "os";
|
|
12722
|
-
import { existsSync as
|
|
12773
|
+
import { existsSync as existsSync23, readFileSync as readFileSync17 } from "fs";
|
|
12723
12774
|
var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
12724
12775
|
constructor(registry, ws) {
|
|
12725
12776
|
this.registry = registry;
|
|
@@ -12858,9 +12909,9 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
12858
12909
|
if (call.name === "write_file") {
|
|
12859
12910
|
const filePath = String(call.arguments["path"] ?? "");
|
|
12860
12911
|
const newContent = String(call.arguments["content"] ?? "");
|
|
12861
|
-
if (filePath &&
|
|
12912
|
+
if (filePath && existsSync23(filePath)) {
|
|
12862
12913
|
try {
|
|
12863
|
-
const old =
|
|
12914
|
+
const old = readFileSync17(filePath, "utf-8");
|
|
12864
12915
|
return renderDiff(old, newContent, { filePath });
|
|
12865
12916
|
} catch {
|
|
12866
12917
|
}
|
|
@@ -13163,16 +13214,16 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
13163
13214
|
};
|
|
13164
13215
|
|
|
13165
13216
|
// src/repl/dev-state.ts
|
|
13166
|
-
import { existsSync as
|
|
13167
|
-
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";
|
|
13168
13219
|
import { homedir as homedir7 } from "os";
|
|
13169
13220
|
function getDevStatePath() {
|
|
13170
|
-
return
|
|
13221
|
+
return join18(homedir7(), CONFIG_DIR_NAME, DEV_STATE_FILE_NAME);
|
|
13171
13222
|
}
|
|
13172
13223
|
function loadDevState() {
|
|
13173
13224
|
const path3 = getDevStatePath();
|
|
13174
|
-
if (!
|
|
13175
|
-
const content =
|
|
13225
|
+
if (!existsSync24(path3)) return null;
|
|
13226
|
+
const content = readFileSync18(path3, "utf-8").trim();
|
|
13176
13227
|
return content || null;
|
|
13177
13228
|
}
|
|
13178
13229
|
|
|
@@ -13429,8 +13480,8 @@ function autoTrimSessionIfNeeded(session, sizeLimit = SESSION_SIZE_LIMIT) {
|
|
|
13429
13480
|
}
|
|
13430
13481
|
|
|
13431
13482
|
// src/core/context-files.ts
|
|
13432
|
-
import { existsSync as
|
|
13433
|
-
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";
|
|
13434
13485
|
function uniqueNonEmpty(values) {
|
|
13435
13486
|
const seen = /* @__PURE__ */ new Set();
|
|
13436
13487
|
const out = [];
|
|
@@ -13460,7 +13511,7 @@ function readContextFile(level, filePath, cwd, maxBytes) {
|
|
|
13460
13511
|
const name = filePath.split(/[\\/]/).pop() ?? filePath;
|
|
13461
13512
|
const shown = displayPath(filePath, cwd);
|
|
13462
13513
|
try {
|
|
13463
|
-
const raw =
|
|
13514
|
+
const raw = readFileSync19(filePath);
|
|
13464
13515
|
const byteCount = raw.byteLength;
|
|
13465
13516
|
if (byteCount === 0) {
|
|
13466
13517
|
return {
|
|
@@ -13507,8 +13558,8 @@ function readContextFile(level, filePath, cwd, maxBytes) {
|
|
|
13507
13558
|
function findFirstContextFile(level, dir, cwd, candidates, maxBytes) {
|
|
13508
13559
|
const skipped = [];
|
|
13509
13560
|
for (const candidate of candidates) {
|
|
13510
|
-
const filePath =
|
|
13511
|
-
if (!
|
|
13561
|
+
const filePath = join19(dir, candidate);
|
|
13562
|
+
if (!existsSync25(filePath)) continue;
|
|
13512
13563
|
const result = readContextFile(level, filePath, cwd, maxBytes);
|
|
13513
13564
|
if (result.skipped) skipped.push(result.skipped);
|
|
13514
13565
|
if (result.layer) return { layer: result.layer, skipped };
|
|
@@ -13535,7 +13586,7 @@ function loadContextFiles(options) {
|
|
|
13535
13586
|
});
|
|
13536
13587
|
return { layers: [], mergedContent: "", skipped, totalChars: 0, totalBytes: 0, maxBytes, candidates };
|
|
13537
13588
|
}
|
|
13538
|
-
if (!
|
|
13589
|
+
if (!existsSync25(filePath)) {
|
|
13539
13590
|
skipped.push({
|
|
13540
13591
|
level: "single",
|
|
13541
13592
|
filePath,
|
|
@@ -13586,8 +13637,8 @@ function loadContextFiles(options) {
|
|
|
13586
13637
|
}
|
|
13587
13638
|
|
|
13588
13639
|
// src/web/session-handler.ts
|
|
13589
|
-
import { existsSync as
|
|
13590
|
-
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";
|
|
13591
13642
|
|
|
13592
13643
|
// src/cli/review-prompts.ts
|
|
13593
13644
|
function buildReviewPrompt(diff, gitContextStr, detailed) {
|
|
@@ -13648,8 +13699,8 @@ If no security issues found, state "\u2705 No security vulnerabilities detected"
|
|
|
13648
13699
|
}
|
|
13649
13700
|
|
|
13650
13701
|
// src/repl/commands/project-init.ts
|
|
13651
|
-
import { existsSync as
|
|
13652
|
-
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";
|
|
13653
13704
|
var SCAN_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
13654
13705
|
"node_modules",
|
|
13655
13706
|
".git",
|
|
@@ -13693,11 +13744,11 @@ function scanDirTree(dir, maxDepth = 2, maxEntries = 80) {
|
|
|
13693
13744
|
const sorted = filtered.sort((a, b) => {
|
|
13694
13745
|
let aIsDir = false, bIsDir = false;
|
|
13695
13746
|
try {
|
|
13696
|
-
aIsDir = statSync8(
|
|
13747
|
+
aIsDir = statSync8(join20(d, a)).isDirectory();
|
|
13697
13748
|
} catch {
|
|
13698
13749
|
}
|
|
13699
13750
|
try {
|
|
13700
|
-
bIsDir = statSync8(
|
|
13751
|
+
bIsDir = statSync8(join20(d, b)).isDirectory();
|
|
13701
13752
|
} catch {
|
|
13702
13753
|
}
|
|
13703
13754
|
if (aIsDir !== bIsDir) return aIsDir ? -1 : 1;
|
|
@@ -13705,7 +13756,7 @@ function scanDirTree(dir, maxDepth = 2, maxEntries = 80) {
|
|
|
13705
13756
|
});
|
|
13706
13757
|
for (let i = 0; i < sorted.length && count < maxEntries; i++) {
|
|
13707
13758
|
const name = sorted[i];
|
|
13708
|
-
const fullPath =
|
|
13759
|
+
const fullPath = join20(d, name);
|
|
13709
13760
|
const isLast = i === sorted.length - 1;
|
|
13710
13761
|
const connector = isLast ? "+-- " : "|-- ";
|
|
13711
13762
|
let isDir;
|
|
@@ -13732,7 +13783,7 @@ function scanProject(cwd) {
|
|
|
13732
13783
|
configFiles: [],
|
|
13733
13784
|
directoryStructure: ""
|
|
13734
13785
|
};
|
|
13735
|
-
const check = (file) =>
|
|
13786
|
+
const check = (file) => existsSync26(join20(cwd, file));
|
|
13736
13787
|
const configCandidates = [
|
|
13737
13788
|
"package.json",
|
|
13738
13789
|
"tsconfig.json",
|
|
@@ -13759,7 +13810,7 @@ function scanProject(cwd) {
|
|
|
13759
13810
|
info.type = "node";
|
|
13760
13811
|
info.language = check("tsconfig.json") ? "TypeScript" : "JavaScript";
|
|
13761
13812
|
try {
|
|
13762
|
-
const pkg = JSON.parse(
|
|
13813
|
+
const pkg = JSON.parse(readFileSync20(join20(cwd, "package.json"), "utf-8"));
|
|
13763
13814
|
const scripts = pkg.scripts ?? {};
|
|
13764
13815
|
info.buildCommand = scripts.build ? "npm run build" : void 0;
|
|
13765
13816
|
info.testCommand = scripts.test ? "npm test" : void 0;
|
|
@@ -14232,6 +14283,334 @@ var DiscussionOrchestrator = class {
|
|
|
14232
14283
|
}
|
|
14233
14284
|
};
|
|
14234
14285
|
|
|
14286
|
+
// src/diagnostics/doctor-report.ts
|
|
14287
|
+
import { existsSync as existsSync28, statSync as statSync10 } from "fs";
|
|
14288
|
+
import { join as join22 } from "path";
|
|
14289
|
+
import { arch as arch2, platform as platform6, release as release2 } from "os";
|
|
14290
|
+
|
|
14291
|
+
// src/diagnostics/crash-log.ts
|
|
14292
|
+
import {
|
|
14293
|
+
existsSync as existsSync27,
|
|
14294
|
+
mkdirSync as mkdirSync13,
|
|
14295
|
+
readdirSync as readdirSync12,
|
|
14296
|
+
readFileSync as readFileSync21,
|
|
14297
|
+
statSync as statSync9,
|
|
14298
|
+
unlinkSync as unlinkSync6,
|
|
14299
|
+
writeFileSync as writeFileSync2
|
|
14300
|
+
} from "fs";
|
|
14301
|
+
import { join as join21 } from "path";
|
|
14302
|
+
import { homedir as homedir8, platform as platform5, release, arch } from "os";
|
|
14303
|
+
var LOGS_DIR_NAME = "logs";
|
|
14304
|
+
var CRASH_LOG_PREFIX = "crash-";
|
|
14305
|
+
function getLogsDir(configDir) {
|
|
14306
|
+
const base = configDir ?? join21(homedir8(), CONFIG_DIR_NAME);
|
|
14307
|
+
return join21(base, LOGS_DIR_NAME);
|
|
14308
|
+
}
|
|
14309
|
+
function truncate(s, n) {
|
|
14310
|
+
return s.length <= n ? s : s.slice(0, n) + "\u2026";
|
|
14311
|
+
}
|
|
14312
|
+
function listRecentCrashes(limit = 10, configDir) {
|
|
14313
|
+
const dir = getLogsDir(configDir);
|
|
14314
|
+
if (!existsSync27(dir)) return [];
|
|
14315
|
+
const files = [];
|
|
14316
|
+
let names;
|
|
14317
|
+
try {
|
|
14318
|
+
names = readdirSync12(dir);
|
|
14319
|
+
} catch {
|
|
14320
|
+
return [];
|
|
14321
|
+
}
|
|
14322
|
+
for (const name of names) {
|
|
14323
|
+
if (!name.startsWith(CRASH_LOG_PREFIX) || !name.endsWith(".log")) continue;
|
|
14324
|
+
const full = join21(dir, name);
|
|
14325
|
+
try {
|
|
14326
|
+
const st = statSync9(full);
|
|
14327
|
+
const head = readFileSync21(full, "utf-8").split("\n").slice(0, 20);
|
|
14328
|
+
const tsLine = head.find((l) => l.startsWith("timestamp:"));
|
|
14329
|
+
const kindLine = head.find((l) => l.startsWith("kind:"));
|
|
14330
|
+
const msgIdx = head.findIndex((l) => l.trim() === "## message");
|
|
14331
|
+
const message = msgIdx >= 0 && head[msgIdx + 1] ? head[msgIdx + 1] : "";
|
|
14332
|
+
files.push({
|
|
14333
|
+
file: full,
|
|
14334
|
+
timestamp: tsLine ? tsLine.replace("timestamp:", "").trim() : st.mtime.toISOString(),
|
|
14335
|
+
kind: kindLine ? kindLine.replace("kind:", "").trim() : "unknown",
|
|
14336
|
+
message: truncate(message, 200),
|
|
14337
|
+
sizeBytes: st.size
|
|
14338
|
+
});
|
|
14339
|
+
} catch {
|
|
14340
|
+
}
|
|
14341
|
+
}
|
|
14342
|
+
files.sort((a, b) => a.timestamp < b.timestamp ? 1 : -1);
|
|
14343
|
+
return files.slice(0, limit);
|
|
14344
|
+
}
|
|
14345
|
+
function getConfigDirUsage(configDir) {
|
|
14346
|
+
const base = configDir ?? join21(homedir8(), CONFIG_DIR_NAME);
|
|
14347
|
+
if (!existsSync27(base)) return { totalBytes: 0, entries: [] };
|
|
14348
|
+
const entries = [];
|
|
14349
|
+
let total = 0;
|
|
14350
|
+
let names;
|
|
14351
|
+
try {
|
|
14352
|
+
names = readdirSync12(base);
|
|
14353
|
+
} catch {
|
|
14354
|
+
return { totalBytes: 0, entries: [] };
|
|
14355
|
+
}
|
|
14356
|
+
for (const name of names) {
|
|
14357
|
+
const full = join21(base, name);
|
|
14358
|
+
const bytes = dirSize(full);
|
|
14359
|
+
entries.push({ name, bytes });
|
|
14360
|
+
total += bytes;
|
|
14361
|
+
}
|
|
14362
|
+
entries.sort((a, b) => b.bytes - a.bytes);
|
|
14363
|
+
return { totalBytes: total, entries };
|
|
14364
|
+
}
|
|
14365
|
+
function dirSize(path3) {
|
|
14366
|
+
try {
|
|
14367
|
+
const st = statSync9(path3);
|
|
14368
|
+
if (st.isFile()) return st.size;
|
|
14369
|
+
if (!st.isDirectory()) return 0;
|
|
14370
|
+
let sum = 0;
|
|
14371
|
+
for (const name of readdirSync12(path3)) {
|
|
14372
|
+
sum += dirSize(join21(path3, name));
|
|
14373
|
+
}
|
|
14374
|
+
return sum;
|
|
14375
|
+
} catch {
|
|
14376
|
+
return 0;
|
|
14377
|
+
}
|
|
14378
|
+
}
|
|
14379
|
+
|
|
14380
|
+
// src/diagnostics/doctor-report.ts
|
|
14381
|
+
function checkFile(label, path3) {
|
|
14382
|
+
const exists = existsSync28(path3);
|
|
14383
|
+
let sizeBytes = null;
|
|
14384
|
+
if (exists) {
|
|
14385
|
+
try {
|
|
14386
|
+
sizeBytes = statSync10(path3).size;
|
|
14387
|
+
} catch {
|
|
14388
|
+
}
|
|
14389
|
+
}
|
|
14390
|
+
return { label, path: path3, exists, sizeBytes };
|
|
14391
|
+
}
|
|
14392
|
+
function buildDoctorReport(options) {
|
|
14393
|
+
const config = options.config;
|
|
14394
|
+
const cwd = options.cwd ?? process.cwd();
|
|
14395
|
+
const configDir = config.getConfigDir();
|
|
14396
|
+
const projectRoot = getGitRoot(cwd) ?? cwd;
|
|
14397
|
+
const contextCfg = config.get("context");
|
|
14398
|
+
const contextSetting = config.get("contextFile");
|
|
14399
|
+
const context = loadContextFiles({
|
|
14400
|
+
cwd,
|
|
14401
|
+
configDir,
|
|
14402
|
+
projectRoot,
|
|
14403
|
+
setting: contextSetting,
|
|
14404
|
+
maxBytes: contextCfg.projectDocMaxBytes,
|
|
14405
|
+
fallbackFilenames: contextCfg.projectDocFallbackFilenames
|
|
14406
|
+
});
|
|
14407
|
+
const hooksConfig = config.get("hooks");
|
|
14408
|
+
const hooks = listHooks(hooksConfig, configDir);
|
|
14409
|
+
const pendingHooks = getPendingHookTrust(hooksConfig, configDir);
|
|
14410
|
+
const plugins = listInstalledPlugins(configDir);
|
|
14411
|
+
const activeAssets = getActivePluginAssets(configDir);
|
|
14412
|
+
const network = config.get("networkPolicy");
|
|
14413
|
+
const mcpServers = config.get("mcpServers");
|
|
14414
|
+
const allStats = getStatsSnapshot();
|
|
14415
|
+
return {
|
|
14416
|
+
version: VERSION,
|
|
14417
|
+
npmCheck: options.npmCheck,
|
|
14418
|
+
node: process.version,
|
|
14419
|
+
platform: `${platform6()} ${release2()} (${arch2()})`,
|
|
14420
|
+
cwd,
|
|
14421
|
+
projectRoot,
|
|
14422
|
+
configDir,
|
|
14423
|
+
providers: options.providers.listAll().map((p) => ({ id: p.id, displayName: p.displayName, configured: p.configured })),
|
|
14424
|
+
files: [
|
|
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"))
|
|
14431
|
+
],
|
|
14432
|
+
context: {
|
|
14433
|
+
enabled: contextSetting !== false,
|
|
14434
|
+
setting: contextSetting,
|
|
14435
|
+
candidates: context.candidates,
|
|
14436
|
+
maxBytes: context.maxBytes,
|
|
14437
|
+
layers: context.layers.map((l) => ({ level: l.level, fileName: l.fileName, displayPath: l.displayPath, bytes: l.byteCount, chars: l.charCount, truncated: l.truncated })),
|
|
14438
|
+
skipped: context.skipped.map((s) => ({ level: s.level, fileName: s.fileName, displayPath: s.displayPath, reason: s.reason, message: s.message }))
|
|
14439
|
+
},
|
|
14440
|
+
permissions: {
|
|
14441
|
+
defaultProfile: config.get("defaultPermissionProfile"),
|
|
14442
|
+
allowedProfiles: config.get("allowedPermissionProfiles"),
|
|
14443
|
+
customProfileCount: Object.keys(config.get("permissionProfiles")).length,
|
|
14444
|
+
legacyDefaultAction: config.get("defaultPermission"),
|
|
14445
|
+
ruleCount: config.get("permissionRules").length
|
|
14446
|
+
},
|
|
14447
|
+
networkPolicy: {
|
|
14448
|
+
enabled: network.enabled,
|
|
14449
|
+
defaultAction: network.defaultAction,
|
|
14450
|
+
allowDomains: network.allowDomains.length,
|
|
14451
|
+
denyDomains: network.denyDomains.length,
|
|
14452
|
+
allowPorts: network.allowPorts.length,
|
|
14453
|
+
denyPorts: network.denyPorts.length,
|
|
14454
|
+
allowPrivateNetwork: network.allowPrivateNetwork,
|
|
14455
|
+
toolOverrides: Object.fromEntries(Object.entries(network.tools).filter(([, v]) => v !== void 0))
|
|
14456
|
+
},
|
|
14457
|
+
hooks: {
|
|
14458
|
+
enabled: hooksConfig?.enabled !== false,
|
|
14459
|
+
total: hooks.length,
|
|
14460
|
+
project: hooks.filter((h) => h.source === "project").length,
|
|
14461
|
+
pendingTrust: pendingHooks.length,
|
|
14462
|
+
disabled: hooks.filter((h) => h.disabled).length
|
|
14463
|
+
},
|
|
14464
|
+
plugins: {
|
|
14465
|
+
root: join22(configDir, "plugins"),
|
|
14466
|
+
installed: plugins.length,
|
|
14467
|
+
active: activeAssets.plugins.length,
|
|
14468
|
+
invalid: plugins.filter((p) => !p.valid).length,
|
|
14469
|
+
untrusted: plugins.filter((p) => p.valid && !p.trusted).length,
|
|
14470
|
+
disabled: plugins.filter((p) => p.valid && !p.enabled).length,
|
|
14471
|
+
mcpServers: Object.keys(activeAssets.mcpServers).length,
|
|
14472
|
+
skillDirs: activeAssets.skillDirs.length,
|
|
14473
|
+
commandDirs: activeAssets.commandDirs.length,
|
|
14474
|
+
agentDirs: activeAssets.agentDirs.length,
|
|
14475
|
+
entries: plugins.map((p) => ({ name: p.name, version: p.manifest.version, enabled: p.enabled, trusted: p.trusted, valid: p.valid, error: p.error }))
|
|
14476
|
+
},
|
|
14477
|
+
mcp: {
|
|
14478
|
+
enabled: config.get("mcpEnabled"),
|
|
14479
|
+
configured: Object.keys(mcpServers).length,
|
|
14480
|
+
projectConfigExists: existsSync28(join22(projectRoot, MCP_PROJECT_CONFIG_NAME)),
|
|
14481
|
+
statuses: options.mcpStatuses ?? []
|
|
14482
|
+
},
|
|
14483
|
+
recentCrashes: listRecentCrashes(5, configDir),
|
|
14484
|
+
diskUsage: getConfigDirUsage(configDir),
|
|
14485
|
+
toolStats: {
|
|
14486
|
+
totalCalls: allStats.reduce((a, b) => a + b.calls, 0),
|
|
14487
|
+
totalFailures: allStats.reduce((a, b) => a + b.failures, 0),
|
|
14488
|
+
topUsed: getTopUsedTools(5),
|
|
14489
|
+
topFailing: getTopFailingTools(5)
|
|
14490
|
+
}
|
|
14491
|
+
};
|
|
14492
|
+
}
|
|
14493
|
+
|
|
14494
|
+
// src/diagnostics/doctor-cli.ts
|
|
14495
|
+
function formatBytes(n) {
|
|
14496
|
+
if (n < 1024) return `${n} B`;
|
|
14497
|
+
if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
|
|
14498
|
+
if (n < 1024 * 1024 * 1024) return `${(n / 1024 / 1024).toFixed(1)} MB`;
|
|
14499
|
+
return `${(n / 1024 / 1024 / 1024).toFixed(2)} GB`;
|
|
14500
|
+
}
|
|
14501
|
+
function formatDoctorReport(report, ansi = true) {
|
|
14502
|
+
const wrap = (code, s) => ansi ? `${code}${s}\x1B[0m` : s;
|
|
14503
|
+
const B = (s) => wrap("\x1B[1m", s);
|
|
14504
|
+
const D = (s) => wrap("\x1B[2m", s);
|
|
14505
|
+
const G = (s) => wrap("\x1B[32m", s);
|
|
14506
|
+
const Y = (s) => wrap("\x1B[33m", s);
|
|
14507
|
+
const R = (s) => wrap("\x1B[31m", s);
|
|
14508
|
+
const out = [];
|
|
14509
|
+
out.push("");
|
|
14510
|
+
out.push(B("\u{1FA7A} AI-CLI Health Check"));
|
|
14511
|
+
out.push("");
|
|
14512
|
+
const nc = report.npmCheck;
|
|
14513
|
+
let versionLine = ` version: ${report.version}`;
|
|
14514
|
+
if (nc?.status === "up-to-date") versionLine += ` ${D("(latest on npm)")}`;
|
|
14515
|
+
else if (nc?.status === "outdated" && nc.latest) versionLine += ` ${Y(`\u2192 ${nc.latest} available`)} ${D("(npm i -g jinzd-ai-cli@latest)")}`;
|
|
14516
|
+
else if (nc?.status === "skipped") versionLine += ` ${D(`(npm check skipped: ${nc.reason ?? "unknown"})`)}`;
|
|
14517
|
+
out.push(versionLine);
|
|
14518
|
+
out.push(` node: ${report.node}`);
|
|
14519
|
+
out.push(` platform: ${report.platform}`);
|
|
14520
|
+
out.push(` cwd: ${report.cwd}`);
|
|
14521
|
+
out.push(` project: ${report.projectRoot}`);
|
|
14522
|
+
out.push(` config: ${report.configDir}`);
|
|
14523
|
+
out.push("");
|
|
14524
|
+
out.push(B("API Keys:"));
|
|
14525
|
+
for (const p of report.providers) {
|
|
14526
|
+
out.push(` ${p.configured ? G("\u2713") : D("\u25CB")} ${p.id.padEnd(14)} ${p.configured ? G("configured") : D("not configured")}`);
|
|
14527
|
+
}
|
|
14528
|
+
out.push("");
|
|
14529
|
+
out.push(B("Context Files:"));
|
|
14530
|
+
out.push(` status: ${report.context.enabled ? G("enabled") : D("disabled")} \xB7 max ${formatBytes(report.context.maxBytes)}`);
|
|
14531
|
+
if (report.context.layers.length === 0) out.push(` ${D("(no context files loaded)")}`);
|
|
14532
|
+
for (const l of report.context.layers) {
|
|
14533
|
+
out.push(` ${G("\u2713")} ${l.level.padEnd(7)} ${l.displayPath} ${D(`(${formatBytes(l.bytes)}${l.truncated ? ", truncated" : ""})`)}`);
|
|
14534
|
+
}
|
|
14535
|
+
if (report.context.skipped.length > 0) {
|
|
14536
|
+
out.push(` ${D("skipped:")}`);
|
|
14537
|
+
for (const s of report.context.skipped.slice(0, 5)) {
|
|
14538
|
+
out.push(` ${Y("!")} ${s.level}:${s.fileName} ${s.reason}${s.message ? ` \xB7 ${s.message}` : ""}`);
|
|
14539
|
+
}
|
|
14540
|
+
}
|
|
14541
|
+
out.push("");
|
|
14542
|
+
out.push(B("Permissions / Network:"));
|
|
14543
|
+
out.push(` profile: ${report.permissions.defaultProfile} \xB7 allowed ${report.permissions.allowedProfiles.join(", ")} \xB7 rules ${report.permissions.ruleCount} \xB7 custom profiles ${report.permissions.customProfileCount}`);
|
|
14544
|
+
const np = report.networkPolicy;
|
|
14545
|
+
out.push(` networkPolicy: ${np.enabled ? G("enabled") : D("disabled")} \xB7 default ${np.defaultAction} \xB7 allow ${np.allowDomains} domain(s)/${np.allowPorts} port(s) \xB7 deny ${np.denyDomains} domain(s)/${np.denyPorts} port(s) \xB7 private ${np.allowPrivateNetwork ? "allow" : "block/confirm"}`);
|
|
14546
|
+
const overrides = Object.entries(np.toolOverrides).map(([k, v]) => `${k}=${v}`).join(", ");
|
|
14547
|
+
if (overrides) out.push(` tool overrides: ${overrides}`);
|
|
14548
|
+
out.push("");
|
|
14549
|
+
out.push(B("Hooks / Plugins:"));
|
|
14550
|
+
out.push(` hooks: ${report.hooks.enabled ? G("enabled") : D("disabled")} \xB7 total ${report.hooks.total} \xB7 project ${report.hooks.project} \xB7 pending trust ${report.hooks.pendingTrust} \xB7 disabled ${report.hooks.disabled}`);
|
|
14551
|
+
out.push(` plugins: installed ${report.plugins.installed} \xB7 active ${report.plugins.active} \xB7 disabled ${report.plugins.disabled} \xB7 untrusted ${report.plugins.untrusted} \xB7 invalid ${report.plugins.invalid}`);
|
|
14552
|
+
out.push(` plugin assets: ${report.plugins.skillDirs} skill dir(s), ${report.plugins.commandDirs} command dir(s), ${report.plugins.agentDirs} agent dir(s), ${report.plugins.mcpServers} MCP server(s)`);
|
|
14553
|
+
if (report.plugins.entries.length > 0) {
|
|
14554
|
+
for (const p of report.plugins.entries.slice(0, 5)) {
|
|
14555
|
+
const state2 = p.valid ? `${p.enabled ? "enabled" : "disabled"} \xB7 ${p.trusted ? "trusted" : "untrusted"}` : `invalid \xB7 ${p.error ?? ""}`;
|
|
14556
|
+
out.push(` ${p.valid ? G("\u2713") : R("\u2717")} ${p.name}@${p.version} ${D(state2)}`);
|
|
14557
|
+
}
|
|
14558
|
+
}
|
|
14559
|
+
out.push("");
|
|
14560
|
+
out.push(B("MCP Servers:"));
|
|
14561
|
+
out.push(` global configured: ${report.mcp.configured} \xB7 project .mcp.json: ${report.mcp.projectConfigExists ? "yes" : "no"} \xB7 mcpEnabled: ${report.mcp.enabled ? "true" : "false"}`);
|
|
14562
|
+
if (report.mcp.statuses.length === 0) out.push(` ${D("(no connected manager status in this mode)")}`);
|
|
14563
|
+
for (const s of report.mcp.statuses) {
|
|
14564
|
+
const state2 = s.connected ? `${G("connected")} \xB7 ${s.serverName} \xB7 ${s.toolCount} tools` : `${R("disconnected")}${s.error ? D(` \xB7 ${s.error}`) : ""}`;
|
|
14565
|
+
out.push(` ${s.connected ? G("\u2713") : R("\u2717")} ${s.serverId.padEnd(16)} ${state2}`);
|
|
14566
|
+
}
|
|
14567
|
+
out.push("");
|
|
14568
|
+
out.push(B("Config Files:"));
|
|
14569
|
+
for (const f of report.files) {
|
|
14570
|
+
const extra = f.exists && f.sizeBytes !== null ? ` ${D(`(${formatBytes(f.sizeBytes)})`)}` : "";
|
|
14571
|
+
out.push(` ${f.exists ? G("\u2713") : D("\u2013")} ${f.label.padEnd(16)} ${f.exists ? f.path : D("(not found)")}${extra}`);
|
|
14572
|
+
}
|
|
14573
|
+
out.push("");
|
|
14574
|
+
out.push(B("Recent Crashes (last 5):"));
|
|
14575
|
+
if (report.recentCrashes.length === 0) out.push(` ${D("(none - clean!)")}`);
|
|
14576
|
+
for (const c of report.recentCrashes) {
|
|
14577
|
+
out.push(` ${R("\u2717")} ${c.timestamp} ${Y(c.kind)}`);
|
|
14578
|
+
out.push(` ${D(c.message)}`);
|
|
14579
|
+
out.push(` ${D(c.file)}`);
|
|
14580
|
+
}
|
|
14581
|
+
out.push("");
|
|
14582
|
+
const ts = report.toolStats;
|
|
14583
|
+
out.push(B("Tool Usage:"));
|
|
14584
|
+
out.push(` total: ${ts.totalCalls} calls \xB7 ${ts.totalFailures} failures (${ts.totalCalls ? (ts.totalFailures * 100 / ts.totalCalls).toFixed(1) : "0.0"}%)`);
|
|
14585
|
+
if (ts.topUsed.length > 0) {
|
|
14586
|
+
out.push(` ${D("top used:")}`);
|
|
14587
|
+
for (const t of ts.topUsed) out.push(` ${t.name.padEnd(20)} ${String(t.calls).padStart(6)} calls \xB7 avg ${t.calls ? (t.totalDurationMs / t.calls).toFixed(0) : "0"}ms`);
|
|
14588
|
+
}
|
|
14589
|
+
if (ts.topFailing.length > 0) {
|
|
14590
|
+
out.push(` ${D("top failing:")}`);
|
|
14591
|
+
for (const t of ts.topFailing) out.push(` ${R(t.name.padEnd(20))} ${t.failures}/${t.calls} (${(t.failures * 100 / Math.max(1, t.calls)).toFixed(1)}%) ${D(t.lastFailureMessage ?? "")}`.slice(0, 200));
|
|
14592
|
+
}
|
|
14593
|
+
out.push("");
|
|
14594
|
+
const dirAnnotations = {
|
|
14595
|
+
models: "semantic search model -> /index semantic-clear",
|
|
14596
|
+
index: "symbol index -> /index clear",
|
|
14597
|
+
history: "conversation history",
|
|
14598
|
+
logs: "crash logs",
|
|
14599
|
+
"memory-index": "chat memory index -> /memory index-clear",
|
|
14600
|
+
plugins: "installed package plugins"
|
|
14601
|
+
};
|
|
14602
|
+
out.push(B("Disk Usage (~/.aicli):"));
|
|
14603
|
+
out.push(` total: ${formatBytes(report.diskUsage.totalBytes)}`);
|
|
14604
|
+
for (const e of report.diskUsage.entries.slice(0, 10)) {
|
|
14605
|
+
const note = dirAnnotations[e.name];
|
|
14606
|
+
out.push(` ${e.name.padEnd(18)} ${formatBytes(e.bytes)}${note ? ` ${D(`\xB7 ${note}`)}` : ""}`);
|
|
14607
|
+
}
|
|
14608
|
+
out.push("");
|
|
14609
|
+
out.push(G("\u2713 Health check complete"));
|
|
14610
|
+
out.push("");
|
|
14611
|
+
return out.join("\n");
|
|
14612
|
+
}
|
|
14613
|
+
|
|
14235
14614
|
// src/hub/presets.ts
|
|
14236
14615
|
var PRESETS = [
|
|
14237
14616
|
{
|
|
@@ -14419,7 +14798,7 @@ function resolveRoleProviders(roles, lookup, defaultProvider, defaultModel, avai
|
|
|
14419
14798
|
}
|
|
14420
14799
|
|
|
14421
14800
|
// src/hub/persist.ts
|
|
14422
|
-
import { join as
|
|
14801
|
+
import { join as join23 } from "path";
|
|
14423
14802
|
function discussionToMessages(state2) {
|
|
14424
14803
|
const out = [];
|
|
14425
14804
|
const t0 = state2.messages[0]?.timestamp ?? /* @__PURE__ */ new Date();
|
|
@@ -14457,7 +14836,7 @@ async function persistDiscussion(state2, config, defaultProvider, defaultModel)
|
|
|
14457
14836
|
session.title = `[Hub] ${state2.topic.slice(0, 48)}`.replace(/\n/g, " ");
|
|
14458
14837
|
session.titleAiGenerated = true;
|
|
14459
14838
|
await sm.save();
|
|
14460
|
-
return { id: session.id, path:
|
|
14839
|
+
return { id: session.id, path: join23(config.getHistoryDir(), `${session.id}.json`) };
|
|
14461
14840
|
}
|
|
14462
14841
|
|
|
14463
14842
|
// src/web/session-handler.ts
|
|
@@ -15174,7 +15553,7 @@ Try: /compact to reduce context, /clear to reset, or switch to a larger-context
|
|
|
15174
15553
|
this.send({ type: "response_done", content, usage });
|
|
15175
15554
|
if (pendingTeeSave && isCleanDocumentBody(content)) {
|
|
15176
15555
|
try {
|
|
15177
|
-
|
|
15556
|
+
mkdirSync14(dirname8(pendingTeeSave), { recursive: true });
|
|
15178
15557
|
const bodyToSave = stripOuterCodeFence(content);
|
|
15179
15558
|
atomicWriteFileSync(pendingTeeSave, bodyToSave);
|
|
15180
15559
|
undoStack.push(pendingTeeSave, `save_last_response (deferred): ${pendingTeeSave}`);
|
|
@@ -15350,7 +15729,7 @@ ${summaryContent}`,
|
|
|
15350
15729
|
let isError = false;
|
|
15351
15730
|
let summary;
|
|
15352
15731
|
try {
|
|
15353
|
-
|
|
15732
|
+
mkdirSync14(dirname8(saveToFile), { recursive: true });
|
|
15354
15733
|
fileStream = createWriteStream(saveToFile);
|
|
15355
15734
|
const teeSystemPrompt = stripToolCallReminder(systemPrompt ?? "") + CONTENT_ONLY_STREAM_REMINDER;
|
|
15356
15735
|
const teeExtraMessages = extraMessages.length > 0 ? [...extraMessages, { role: "user", content: TEE_FINAL_USER_NUDGE }] : [{ role: "user", content: TEE_FINAL_USER_NUDGE }];
|
|
@@ -15474,13 +15853,39 @@ ${summaryContent}`,
|
|
|
15474
15853
|
break;
|
|
15475
15854
|
}
|
|
15476
15855
|
case "model": {
|
|
15477
|
-
const
|
|
15478
|
-
if (!
|
|
15479
|
-
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]]" });
|
|
15480
15859
|
return;
|
|
15481
15860
|
}
|
|
15482
|
-
|
|
15483
|
-
|
|
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
|
+
});
|
|
15884
|
+
return;
|
|
15885
|
+
}
|
|
15886
|
+
const modelId = sub;
|
|
15887
|
+
const models = await this.providers.listModels(this.currentProvider);
|
|
15888
|
+
const found = models.find((m) => m.id === modelId);
|
|
15484
15889
|
if (!found) {
|
|
15485
15890
|
this.send({ type: "error", message: `Model "${modelId}" not found` });
|
|
15486
15891
|
return;
|
|
@@ -16039,9 +16444,9 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
16039
16444
|
let modifiedFiles = 0;
|
|
16040
16445
|
const diffLines2 = [];
|
|
16041
16446
|
for (const [filePath, { earliest }] of fileMap) {
|
|
16042
|
-
const currentContent =
|
|
16447
|
+
const currentContent = existsSync29(filePath) ? (() => {
|
|
16043
16448
|
try {
|
|
16044
|
-
return
|
|
16449
|
+
return readFileSync22(filePath, "utf-8");
|
|
16045
16450
|
} catch {
|
|
16046
16451
|
return null;
|
|
16047
16452
|
}
|
|
@@ -16557,7 +16962,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
16557
16962
|
case "test": {
|
|
16558
16963
|
this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
|
|
16559
16964
|
try {
|
|
16560
|
-
const { executeTests } = await import("./run-tests-
|
|
16965
|
+
const { executeTests } = await import("./run-tests-GK5AIAV2.js");
|
|
16561
16966
|
const argStr = args.join(" ").trim();
|
|
16562
16967
|
let testArgs = {};
|
|
16563
16968
|
if (argStr) {
|
|
@@ -16574,9 +16979,9 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
16574
16979
|
// ── /init ───────────────────────────────────────────────────────
|
|
16575
16980
|
case "init": {
|
|
16576
16981
|
const cwd = process.cwd();
|
|
16577
|
-
const targetPath =
|
|
16982
|
+
const targetPath = join24(cwd, "AICLI.md");
|
|
16578
16983
|
const force = args.includes("--force");
|
|
16579
|
-
if (
|
|
16984
|
+
if (existsSync29(targetPath) && !force) {
|
|
16580
16985
|
this.send({ type: "info", message: `AICLI.md already exists at ${targetPath}
|
|
16581
16986
|
Use /init --force to overwrite.` });
|
|
16582
16987
|
break;
|
|
@@ -16586,7 +16991,7 @@ Use /init --force to overwrite.` });
|
|
|
16586
16991
|
const projectInfo = scanProject(cwd);
|
|
16587
16992
|
const prompt = buildInitPrompt(projectInfo, cwd);
|
|
16588
16993
|
const content = await this.chatOnce(prompt, { temperature: 0.3, maxTokens: 4096 });
|
|
16589
|
-
|
|
16994
|
+
writeFileSync3(targetPath, content, "utf-8");
|
|
16590
16995
|
this.send({ type: "info", message: `\u2713 Generated: ${targetPath} (${content.length} chars)
|
|
16591
16996
|
Use /context reload to load it.` });
|
|
16592
16997
|
} catch (err) {
|
|
@@ -16596,53 +17001,13 @@ Use /context reload to load it.` });
|
|
|
16596
17001
|
}
|
|
16597
17002
|
// ── /doctor ─────────────────────────────────────────────────────
|
|
16598
17003
|
case "doctor": {
|
|
16599
|
-
const
|
|
16600
|
-
|
|
16601
|
-
|
|
16602
|
-
|
|
16603
|
-
|
|
16604
|
-
|
|
16605
|
-
|
|
16606
|
-
}
|
|
16607
|
-
lines.push("");
|
|
16608
|
-
const configDir = this.config.getConfigDir();
|
|
16609
|
-
lines.push("**Config Files:**");
|
|
16610
|
-
lines.push(` Dir: ${configDir}`);
|
|
16611
|
-
const checkFile = (label, filePath) => {
|
|
16612
|
-
const exists = existsSync26(filePath);
|
|
16613
|
-
let extra = "";
|
|
16614
|
-
if (exists) {
|
|
16615
|
-
try {
|
|
16616
|
-
extra = ` (${statSync9(filePath).size} bytes)`;
|
|
16617
|
-
} catch {
|
|
16618
|
-
}
|
|
16619
|
-
}
|
|
16620
|
-
lines.push(` ${exists ? "\u2713" : "\u2013"} ${label.padEnd(14)} ${exists ? filePath + extra : "(not found)"}`);
|
|
16621
|
-
};
|
|
16622
|
-
checkFile("config.json", join21(configDir, "config.json"));
|
|
16623
|
-
checkFile("memory.md", join21(configDir, MEMORY_FILE_NAME));
|
|
16624
|
-
checkFile("dev-state.md", join21(configDir, "dev-state.md"));
|
|
16625
|
-
lines.push("");
|
|
16626
|
-
if (this.mcpManager) {
|
|
16627
|
-
lines.push("**MCP Servers:**");
|
|
16628
|
-
const statuses = this.mcpManager.getStatus();
|
|
16629
|
-
if (statuses.length === 0) {
|
|
16630
|
-
lines.push(" (no servers configured)");
|
|
16631
|
-
} else {
|
|
16632
|
-
for (const s of statuses) {
|
|
16633
|
-
const state2 = s.connected ? `connected \xB7 ${s.serverName} \xB7 ${s.toolCount} tools` : `disconnected${s.error ? ` \xB7 ${s.error}` : ""}`;
|
|
16634
|
-
lines.push(` ${s.connected ? "\u2713" : "\u2717"} ${s.serverId.padEnd(16)} ${state2}`);
|
|
16635
|
-
}
|
|
16636
|
-
}
|
|
16637
|
-
lines.push("");
|
|
16638
|
-
}
|
|
16639
|
-
lines.push("**Current Session:**");
|
|
16640
|
-
lines.push(` Provider: ${this.currentProvider}`);
|
|
16641
|
-
lines.push(` Model: ${this.currentModel}`);
|
|
16642
|
-
lines.push(` Version: ${VERSION}`);
|
|
16643
|
-
lines.push("");
|
|
16644
|
-
lines.push("\u2713 Health check complete");
|
|
16645
|
-
this.send({ type: "info", message: lines.join("\n") });
|
|
17004
|
+
const report = buildDoctorReport({
|
|
17005
|
+
config: this.config,
|
|
17006
|
+
providers: this.providers,
|
|
17007
|
+
cwd: process.cwd(),
|
|
17008
|
+
mcpStatuses: this.mcpManager?.getStatus() ?? []
|
|
17009
|
+
});
|
|
17010
|
+
this.send({ type: "info", message: formatDoctorReport(report, false) });
|
|
16646
17011
|
break;
|
|
16647
17012
|
}
|
|
16648
17013
|
// ── /about ──────────────────────────────────────────────────────
|
|
@@ -16809,11 +17174,11 @@ Use /add-dir remove to clear.` : "No directories added.\nUsage: /add-dir <path>
|
|
|
16809
17174
|
break;
|
|
16810
17175
|
}
|
|
16811
17176
|
const dirPath = resolve10(sub);
|
|
16812
|
-
if (!
|
|
17177
|
+
if (!existsSync29(dirPath)) {
|
|
16813
17178
|
this.send({ type: "error", message: `Directory not found: ${dirPath}` });
|
|
16814
17179
|
break;
|
|
16815
17180
|
}
|
|
16816
|
-
if (!
|
|
17181
|
+
if (!statSync11(dirPath).isDirectory()) {
|
|
16817
17182
|
this.send({ type: "error", message: `Not a directory: ${dirPath}` });
|
|
16818
17183
|
break;
|
|
16819
17184
|
}
|
|
@@ -16825,14 +17190,14 @@ It will be included in AI context for subsequent messages.` });
|
|
|
16825
17190
|
// ── /commands ───────────────────────────────────────────────────
|
|
16826
17191
|
case "commands": {
|
|
16827
17192
|
const configDir = this.config.getConfigDir();
|
|
16828
|
-
const commandsDir =
|
|
16829
|
-
if (!
|
|
17193
|
+
const commandsDir = join24(configDir, CUSTOM_COMMANDS_DIR_NAME);
|
|
17194
|
+
if (!existsSync29(commandsDir)) {
|
|
16830
17195
|
this.send({ type: "info", message: `No custom commands directory.
|
|
16831
17196
|
Create: ${commandsDir}/ with .md files.` });
|
|
16832
17197
|
break;
|
|
16833
17198
|
}
|
|
16834
17199
|
try {
|
|
16835
|
-
const files =
|
|
17200
|
+
const files = readdirSync13(commandsDir).filter((f) => f.endsWith(".md"));
|
|
16836
17201
|
if (files.length === 0) {
|
|
16837
17202
|
this.send({ type: "info", message: `No custom commands found in ${commandsDir}
|
|
16838
17203
|
Add .md files to create commands.` });
|
|
@@ -16852,7 +17217,7 @@ Add .md files to create commands.` });
|
|
|
16852
17217
|
// ── /plugins / /plugin ──────────────────────────────────────────
|
|
16853
17218
|
case "plugins": {
|
|
16854
17219
|
const configDir = this.config.getConfigDir();
|
|
16855
|
-
const pluginsDir =
|
|
17220
|
+
const pluginsDir = join24(configDir, PLUGINS_DIR_NAME);
|
|
16856
17221
|
const pluginTools = this.toolRegistry.listPluginTools();
|
|
16857
17222
|
const pluginPackages = listInstalledPlugins(configDir);
|
|
16858
17223
|
const lines = [`\u{1F50C} **Plugins:**`, `Dir: ${pluginsDir}`, ""];
|
|
@@ -17134,7 +17499,7 @@ ${entry.content}`;
|
|
|
17134
17499
|
const configDir = this.config.getConfigDir();
|
|
17135
17500
|
try {
|
|
17136
17501
|
atomicWriteFileSync(memoryStorePath(configDir), "");
|
|
17137
|
-
atomicWriteFileSync(
|
|
17502
|
+
atomicWriteFileSync(join24(configDir, MEMORY_FILE_NAME), "");
|
|
17138
17503
|
this.send({ type: "info", message: "\u{1F5D1}\uFE0F Persistent memory cleared." });
|
|
17139
17504
|
this.sendMemoryEntries();
|
|
17140
17505
|
} catch (err) {
|
|
@@ -17416,8 +17781,8 @@ async function setupProxy(configProxy) {
|
|
|
17416
17781
|
}
|
|
17417
17782
|
|
|
17418
17783
|
// src/web/auth.ts
|
|
17419
|
-
import { existsSync as
|
|
17420
|
-
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";
|
|
17421
17786
|
import { createHmac, randomBytes, timingSafeEqual, pbkdf2Sync } from "crypto";
|
|
17422
17787
|
var USERS_FILE = "users.json";
|
|
17423
17788
|
var TOKEN_EXPIRY_HOURS = 24;
|
|
@@ -17432,7 +17797,7 @@ var AuthManager = class {
|
|
|
17432
17797
|
db;
|
|
17433
17798
|
constructor(baseDir) {
|
|
17434
17799
|
this.baseDir = baseDir;
|
|
17435
|
-
this.usersFile =
|
|
17800
|
+
this.usersFile = join25(baseDir, USERS_FILE);
|
|
17436
17801
|
this.db = this.loadOrCreate();
|
|
17437
17802
|
}
|
|
17438
17803
|
// ── Public API ─────────────────────────────────────────────────
|
|
@@ -17461,9 +17826,9 @@ var AuthManager = class {
|
|
|
17461
17826
|
}
|
|
17462
17827
|
const salt = randomBytes(16).toString("hex");
|
|
17463
17828
|
const passwordHash = this.hashPassword(password, salt);
|
|
17464
|
-
const dataDir =
|
|
17465
|
-
const fullDataDir =
|
|
17466
|
-
|
|
17829
|
+
const dataDir = join25(USERS_DIR, username);
|
|
17830
|
+
const fullDataDir = join25(this.baseDir, dataDir);
|
|
17831
|
+
mkdirSync15(join25(fullDataDir, "history"), { recursive: true });
|
|
17467
17832
|
const user = {
|
|
17468
17833
|
username,
|
|
17469
17834
|
passwordHash,
|
|
@@ -17578,7 +17943,7 @@ var AuthManager = class {
|
|
|
17578
17943
|
getUserDataDir(username) {
|
|
17579
17944
|
const user = this.db.users.find((u) => u.username === username);
|
|
17580
17945
|
if (!user) throw new Error(`User not found: ${username}`);
|
|
17581
|
-
return
|
|
17946
|
+
return join25(this.baseDir, user.dataDir);
|
|
17582
17947
|
}
|
|
17583
17948
|
/** List all usernames */
|
|
17584
17949
|
listUsers() {
|
|
@@ -17614,30 +17979,30 @@ var AuthManager = class {
|
|
|
17614
17979
|
const err = this.register(username, password);
|
|
17615
17980
|
if (err) return err;
|
|
17616
17981
|
const userDir = this.getUserDataDir(username);
|
|
17617
|
-
const globalConfig =
|
|
17618
|
-
if (
|
|
17982
|
+
const globalConfig = join25(this.baseDir, "config.json");
|
|
17983
|
+
if (existsSync30(globalConfig)) {
|
|
17619
17984
|
try {
|
|
17620
|
-
const content =
|
|
17621
|
-
|
|
17985
|
+
const content = readFileSync23(globalConfig, "utf-8");
|
|
17986
|
+
writeFileSync4(join25(userDir, "config.json"), content, "utf-8");
|
|
17622
17987
|
} catch {
|
|
17623
17988
|
}
|
|
17624
17989
|
}
|
|
17625
|
-
const globalMemory =
|
|
17626
|
-
if (
|
|
17990
|
+
const globalMemory = join25(this.baseDir, "memory.md");
|
|
17991
|
+
if (existsSync30(globalMemory)) {
|
|
17627
17992
|
try {
|
|
17628
|
-
const content =
|
|
17629
|
-
|
|
17993
|
+
const content = readFileSync23(globalMemory, "utf-8");
|
|
17994
|
+
writeFileSync4(join25(userDir, "memory.md"), content, "utf-8");
|
|
17630
17995
|
} catch {
|
|
17631
17996
|
}
|
|
17632
17997
|
}
|
|
17633
|
-
const globalHistory =
|
|
17634
|
-
if (
|
|
17998
|
+
const globalHistory = join25(this.baseDir, "history");
|
|
17999
|
+
if (existsSync30(globalHistory)) {
|
|
17635
18000
|
try {
|
|
17636
|
-
const files =
|
|
17637
|
-
const userHistory =
|
|
18001
|
+
const files = readdirSync14(globalHistory).filter((f) => f.endsWith(".json"));
|
|
18002
|
+
const userHistory = join25(userDir, "history");
|
|
17638
18003
|
for (const f of files) {
|
|
17639
18004
|
try {
|
|
17640
|
-
copyFileSync(
|
|
18005
|
+
copyFileSync(join25(globalHistory, f), join25(userHistory, f));
|
|
17641
18006
|
} catch {
|
|
17642
18007
|
}
|
|
17643
18008
|
}
|
|
@@ -17648,9 +18013,9 @@ var AuthManager = class {
|
|
|
17648
18013
|
}
|
|
17649
18014
|
// ── Private methods ────────────────────────────────────────────
|
|
17650
18015
|
loadOrCreate() {
|
|
17651
|
-
if (
|
|
18016
|
+
if (existsSync30(this.usersFile)) {
|
|
17652
18017
|
try {
|
|
17653
|
-
return JSON.parse(
|
|
18018
|
+
return JSON.parse(readFileSync23(this.usersFile, "utf-8"));
|
|
17654
18019
|
} catch {
|
|
17655
18020
|
}
|
|
17656
18021
|
}
|
|
@@ -17666,7 +18031,7 @@ var AuthManager = class {
|
|
|
17666
18031
|
this.saveDB(this.db);
|
|
17667
18032
|
}
|
|
17668
18033
|
saveDB(db) {
|
|
17669
|
-
|
|
18034
|
+
mkdirSync15(this.baseDir, { recursive: true });
|
|
17670
18035
|
atomicWriteFileSync(this.usersFile, JSON.stringify(db, null, 2));
|
|
17671
18036
|
}
|
|
17672
18037
|
/** Legacy hash — kept only for migrating old users (v0.2.x) */
|
|
@@ -17698,7 +18063,7 @@ function getModuleDir() {
|
|
|
17698
18063
|
if (typeof import.meta?.url === "string") {
|
|
17699
18064
|
const url = new URL(import.meta.url);
|
|
17700
18065
|
const filePath = url.pathname.replace(/^\/([A-Z]:)/i, "$1");
|
|
17701
|
-
return
|
|
18066
|
+
return dirname9(filePath);
|
|
17702
18067
|
}
|
|
17703
18068
|
} catch {
|
|
17704
18069
|
}
|
|
@@ -17721,7 +18086,8 @@ async function startWebServer(options = {}) {
|
|
|
17721
18086
|
timeout: config.get("timeouts")[id],
|
|
17722
18087
|
proxy: config.get("proxy")
|
|
17723
18088
|
}),
|
|
17724
|
-
config.get("customProviders")
|
|
18089
|
+
config.get("customProviders"),
|
|
18090
|
+
config.getConfigDir()
|
|
17725
18091
|
);
|
|
17726
18092
|
if (options._extraProviders) {
|
|
17727
18093
|
for (const p of options._extraProviders) {
|
|
@@ -17769,8 +18135,8 @@ async function startWebServer(options = {}) {
|
|
|
17769
18135
|
}
|
|
17770
18136
|
}
|
|
17771
18137
|
let skillManager = null;
|
|
17772
|
-
const skillsDir =
|
|
17773
|
-
if (
|
|
18138
|
+
const skillsDir = join26(config.getConfigDir(), SKILLS_DIR_NAME);
|
|
18139
|
+
if (existsSync31(skillsDir)) {
|
|
17774
18140
|
skillManager = new SkillManager(skillsDir, config.get("ui").skillSizeWarn);
|
|
17775
18141
|
skillManager.loadSkills(pluginAssets.skillDirs);
|
|
17776
18142
|
const count = skillManager.listSkills().length;
|
|
@@ -17841,18 +18207,18 @@ async function startWebServer(options = {}) {
|
|
|
17841
18207
|
next();
|
|
17842
18208
|
};
|
|
17843
18209
|
const moduleDir = getModuleDir();
|
|
17844
|
-
let clientDir =
|
|
17845
|
-
if (!
|
|
17846
|
-
clientDir =
|
|
18210
|
+
let clientDir = join26(moduleDir, "web", "client");
|
|
18211
|
+
if (!existsSync31(clientDir)) {
|
|
18212
|
+
clientDir = join26(moduleDir, "client");
|
|
17847
18213
|
}
|
|
17848
|
-
if (!
|
|
17849
|
-
clientDir =
|
|
18214
|
+
if (!existsSync31(clientDir)) {
|
|
18215
|
+
clientDir = join26(moduleDir, "..", "..", "src", "web", "client");
|
|
17850
18216
|
}
|
|
17851
|
-
if (!
|
|
17852
|
-
clientDir =
|
|
18217
|
+
if (!existsSync31(clientDir)) {
|
|
18218
|
+
clientDir = join26(process.cwd(), "src", "web", "client");
|
|
17853
18219
|
}
|
|
17854
18220
|
console.log(` Static files: ${clientDir}`);
|
|
17855
|
-
app.use("/vendor", express.static(
|
|
18221
|
+
app.use("/vendor", express.static(join26(clientDir, "vendor"), { maxAge: "1y", immutable: true }));
|
|
17856
18222
|
app.use(express.static(clientDir));
|
|
17857
18223
|
app.get("/api/status", (_req, res) => {
|
|
17858
18224
|
res.json({
|
|
@@ -17919,7 +18285,7 @@ async function startWebServer(options = {}) {
|
|
|
17919
18285
|
app.get("/api/files", requireAuth, (req, res) => {
|
|
17920
18286
|
const cwd = process.cwd();
|
|
17921
18287
|
const prefix = req.query.prefix || "";
|
|
17922
|
-
const targetDir =
|
|
18288
|
+
const targetDir = join26(cwd, prefix);
|
|
17923
18289
|
try {
|
|
17924
18290
|
const canonicalTarget = realpathSync(resolve11(targetDir));
|
|
17925
18291
|
const canonicalCwd = realpathSync(resolve11(cwd));
|
|
@@ -17933,10 +18299,10 @@ async function startWebServer(options = {}) {
|
|
|
17933
18299
|
}
|
|
17934
18300
|
try {
|
|
17935
18301
|
const SKIP = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "dist-cjs", "release", "__pycache__", ".next", ".nuxt", "coverage", ".cache"]);
|
|
17936
|
-
const entries =
|
|
18302
|
+
const entries = readdirSync15(targetDir, { withFileTypes: true });
|
|
17937
18303
|
const files = entries.filter((e) => !SKIP.has(e.name) && !e.name.startsWith(".")).slice(0, 50).map((e) => ({
|
|
17938
18304
|
name: e.name,
|
|
17939
|
-
path: relative4(cwd,
|
|
18305
|
+
path: relative4(cwd, join26(targetDir, e.name)).replace(/\\/g, "/"),
|
|
17940
18306
|
isDir: e.isDirectory()
|
|
17941
18307
|
}));
|
|
17942
18308
|
res.json({ files });
|
|
@@ -17972,8 +18338,8 @@ async function startWebServer(options = {}) {
|
|
|
17972
18338
|
try {
|
|
17973
18339
|
const authUser = req._authUser;
|
|
17974
18340
|
const histDir = authUser ? getUserShared(authUser).config.getHistoryDir() : config.getHistoryDir();
|
|
17975
|
-
const filePath =
|
|
17976
|
-
if (!
|
|
18341
|
+
const filePath = join26(histDir, `${id}.json`);
|
|
18342
|
+
if (!existsSync31(filePath)) {
|
|
17977
18343
|
res.status(404).json({ error: "Session not found" });
|
|
17978
18344
|
return;
|
|
17979
18345
|
}
|
|
@@ -17988,7 +18354,7 @@ async function startWebServer(options = {}) {
|
|
|
17988
18354
|
res.status(404).json({ error: "Session not found" });
|
|
17989
18355
|
return;
|
|
17990
18356
|
}
|
|
17991
|
-
const data = JSON.parse(
|
|
18357
|
+
const data = JSON.parse(readFileSync24(filePath, "utf-8"));
|
|
17992
18358
|
res.json({ session: data });
|
|
17993
18359
|
} catch (err) {
|
|
17994
18360
|
res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
|
|
@@ -18001,7 +18367,7 @@ async function startWebServer(options = {}) {
|
|
|
18001
18367
|
return;
|
|
18002
18368
|
}
|
|
18003
18369
|
const cwd = process.cwd();
|
|
18004
|
-
const fullPath = resolve11(
|
|
18370
|
+
const fullPath = resolve11(join26(cwd, filePath));
|
|
18005
18371
|
try {
|
|
18006
18372
|
const canonicalFull = realpathSync(fullPath);
|
|
18007
18373
|
const canonicalCwd = realpathSync(resolve11(cwd));
|
|
@@ -18014,12 +18380,12 @@ async function startWebServer(options = {}) {
|
|
|
18014
18380
|
return;
|
|
18015
18381
|
}
|
|
18016
18382
|
try {
|
|
18017
|
-
const stat =
|
|
18383
|
+
const stat = statSync12(fullPath);
|
|
18018
18384
|
if (stat.size > 512 * 1024) {
|
|
18019
18385
|
res.json({ error: `File too large (${(stat.size / 1024).toFixed(0)} KB, max 512 KB)` });
|
|
18020
18386
|
return;
|
|
18021
18387
|
}
|
|
18022
|
-
const content =
|
|
18388
|
+
const content = readFileSync24(fullPath, "utf-8");
|
|
18023
18389
|
res.json({ content, size: stat.size });
|
|
18024
18390
|
} catch {
|
|
18025
18391
|
res.json({ error: "Cannot read file" });
|
|
@@ -18274,17 +18640,17 @@ function resolveProjectMcpPath() {
|
|
|
18274
18640
|
const cwd = process.cwd();
|
|
18275
18641
|
const gitRoot = getGitRoot(cwd);
|
|
18276
18642
|
const projectRoot = gitRoot ?? cwd;
|
|
18277
|
-
const configPath =
|
|
18278
|
-
return
|
|
18643
|
+
const configPath = join26(projectRoot, MCP_PROJECT_CONFIG_NAME);
|
|
18644
|
+
return existsSync31(configPath) ? configPath : null;
|
|
18279
18645
|
}
|
|
18280
18646
|
function loadProjectMcpConfig() {
|
|
18281
18647
|
const cwd = process.cwd();
|
|
18282
18648
|
const gitRoot = getGitRoot(cwd);
|
|
18283
18649
|
const projectRoot = gitRoot ?? cwd;
|
|
18284
|
-
const configPath =
|
|
18285
|
-
if (!
|
|
18650
|
+
const configPath = join26(projectRoot, MCP_PROJECT_CONFIG_NAME);
|
|
18651
|
+
if (!existsSync31(configPath)) return null;
|
|
18286
18652
|
try {
|
|
18287
|
-
const raw = JSON.parse(
|
|
18653
|
+
const raw = JSON.parse(readFileSync24(configPath, "utf-8"));
|
|
18288
18654
|
return raw.mcpServers ?? raw;
|
|
18289
18655
|
} catch {
|
|
18290
18656
|
return null;
|