jinzd-ai-cli 0.4.233 → 0.4.235
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/dist/{auth-FSTAKSYF.js → auth-PE3Z5OHS.js} +1 -1
- package/dist/{batch-WWNPYQ6G.js → batch-Y7MHXVR7.js} +2 -2
- package/dist/{chat-index-O56HDGFI.js → chat-index-7HXBWQFH.js} +2 -2
- package/dist/{chat-index-WSI7ULRQ.js → chat-index-WYXYD7YP.js} +1 -1
- package/dist/{chunk-4KM53GLG.js → chunk-2QQKS56X.js} +1 -1
- package/dist/{chunk-NVUCDUXE.js → chunk-5MYPIQ3Y.js} +11 -5
- package/dist/{chunk-NXYXFRPA.js → chunk-7D5EJBHO.js} +3 -3
- package/dist/{chunk-5PKNKJYP.js → chunk-BFTTW5LS.js} +1 -1
- package/dist/{chunk-FQSAK75P.js → chunk-CDOQNIVV.js} +3 -3
- package/dist/{chunk-7KS3RGZ7.js → chunk-DHNYGJYB.js} +2 -2
- package/dist/{chunk-HX67JAK6.js → chunk-FYJGVFXL.js} +87 -54
- package/dist/{chunk-ACTO5C6K.js → chunk-GDXKFWFL.js} +1 -1
- package/dist/{chunk-EZGZOK6E.js → chunk-GFDRC2JZ.js} +1 -1
- package/dist/{chunk-4OAV7QMB.js → chunk-HHYMSEWS.js} +1 -1
- package/dist/{chunk-ZWU7FWHO.js → chunk-JMP3LJC2.js} +38 -91
- package/dist/chunk-KCEO2XJ4.js +420 -0
- package/dist/{chunk-M4FW2NPU.js → chunk-Q5GVANMH.js} +1 -1
- package/dist/{chunk-JVKAL5Q3.js → chunk-RYQBBEMM.js} +33 -3
- package/dist/{chunk-LF3OUJHK.js → chunk-UO3XWZFE.js} +302 -4
- package/dist/{chunk-TJK7AU65.js → chunk-WXFQ6LYN.js} +1 -1
- package/dist/{ci-V5V3JSJC.js → ci-OAFNH3MA.js} +4 -4
- package/dist/{ci-format-XS7LKBO5.js → ci-format-4LSXJNND.js} +2 -2
- package/dist/{constants-UROQJXLY.js → constants-ERIAS7GV.js} +1 -1
- package/dist/{doctor-cli-BY3FIV2B.js → doctor-cli-VFSVWZU6.js} +4 -4
- package/dist/electron-server.js +235 -224
- package/dist/{hub-L2QCRI5M.js → hub-X4EUYRIN.js} +2 -2
- package/dist/index.js +23 -23
- package/dist/{persist-I3JI27OK.js → persist-AMH5SQ4W.js} +3 -3
- package/dist/{pr-IJRUPR5C.js → pr-2CZN5JLE.js} +4 -4
- package/dist/{run-tests-FLCVJ6TK.js → run-tests-DXRJDPFF.js} +2 -2
- package/dist/{run-tests-NNXBS4EL.js → run-tests-YSRET52H.js} +1 -1
- package/dist/{server-W2X3C6GJ.js → server-5ZAXMKRI.js} +21 -20
- package/dist/{server-E6LZB72O.js → server-FGJGXHZD.js} +6 -6
- package/dist/{task-orchestrator-W55VKYTC.js → task-orchestrator-QMERCD77.js} +6 -6
- package/dist/{usage-MZCDTJWB.js → usage-633DUDNT.js} +2 -2
- package/package.json +2 -4
- package/dist/chunk-B5TYJO7V.js +0 -123
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-HHYMSEWS.js";
|
|
42
42
|
import {
|
|
43
43
|
hasSemanticIndex,
|
|
44
44
|
semanticSearch
|
|
@@ -49,12 +49,18 @@ import {
|
|
|
49
49
|
import "./chunk-IEQAE3QG.js";
|
|
50
50
|
import {
|
|
51
51
|
DEFAULT_PATTERNS,
|
|
52
|
+
listFromIndex,
|
|
52
53
|
loadChatIndex,
|
|
54
|
+
readSessionMeta,
|
|
53
55
|
redactJson,
|
|
54
56
|
redactString,
|
|
57
|
+
removeSessionFromIndex,
|
|
58
|
+
safeDate,
|
|
55
59
|
scanString,
|
|
56
|
-
|
|
57
|
-
|
|
60
|
+
searchCandidates,
|
|
61
|
+
searchChatMemory,
|
|
62
|
+
updateSessionInIndex
|
|
63
|
+
} from "./chunk-UO3XWZFE.js";
|
|
58
64
|
import "./chunk-JV5N65KN.js";
|
|
59
65
|
import {
|
|
60
66
|
atomicWriteFileSync
|
|
@@ -64,8 +70,8 @@ import {
|
|
|
64
70
|
import express from "express";
|
|
65
71
|
import { createServer } from "http";
|
|
66
72
|
import { WebSocketServer } from "ws";
|
|
67
|
-
import { join as join28, dirname as
|
|
68
|
-
import { existsSync as
|
|
73
|
+
import { join as join28, dirname as dirname12, resolve as resolve13, relative as relative5, sep as sep3 } from "path";
|
|
74
|
+
import { existsSync as existsSync34, readFileSync as readFileSync26, readdirSync as readdirSync17, statSync as statSync15, realpathSync as realpathSync2 } from "fs";
|
|
69
75
|
import { networkInterfaces } from "os";
|
|
70
76
|
|
|
71
77
|
// src/config/config-manager.ts
|
|
@@ -1902,12 +1908,12 @@ function findPhantomClaims(content, extraMessages) {
|
|
|
1902
1908
|
const claimed = extractClaimedFilePaths(content);
|
|
1903
1909
|
if (claimed.length === 0) return [];
|
|
1904
1910
|
const normalize = (p) => p.replace(/\\/g, "/").toLowerCase().replace(/^\.\//, "");
|
|
1905
|
-
const
|
|
1911
|
+
const basename8 = (p) => {
|
|
1906
1912
|
const parts = normalize(p).split("/");
|
|
1907
1913
|
return parts[parts.length - 1] ?? "";
|
|
1908
1914
|
};
|
|
1909
1915
|
const written = extractWrittenFilePaths(extraMessages).map(normalize);
|
|
1910
|
-
const writtenBases = new Set(written.map(
|
|
1916
|
+
const writtenBases = new Set(written.map(basename8));
|
|
1911
1917
|
const writtenFull = new Set(written);
|
|
1912
1918
|
const bashText = extractBashCommands(extraMessages).map((c) => c.replace(/\\/g, "/").toLowerCase()).join("\n");
|
|
1913
1919
|
return claimed.filter((raw) => {
|
|
@@ -1916,8 +1922,8 @@ function findPhantomClaims(content, extraMessages) {
|
|
|
1916
1922
|
for (const w of writtenFull) {
|
|
1917
1923
|
if (w.endsWith("/" + norm2) || norm2.endsWith("/" + w)) return false;
|
|
1918
1924
|
}
|
|
1919
|
-
if (writtenBases.has(
|
|
1920
|
-
const base =
|
|
1925
|
+
if (writtenBases.has(basename8(norm2))) return false;
|
|
1926
|
+
const base = basename8(norm2);
|
|
1921
1927
|
if (base && bashText.includes(base)) return false;
|
|
1922
1928
|
return true;
|
|
1923
1929
|
});
|
|
@@ -3947,7 +3953,7 @@ var ProviderRegistry = class {
|
|
|
3947
3953
|
};
|
|
3948
3954
|
|
|
3949
3955
|
// src/session/session-manager.ts
|
|
3950
|
-
import { readFileSync as readFileSync3, existsSync as existsSync3, mkdirSync as mkdirSync3, readdirSync, unlinkSync as unlinkSync2,
|
|
3956
|
+
import { readFileSync as readFileSync3, existsSync as existsSync3, mkdirSync as mkdirSync3, readdirSync, unlinkSync as unlinkSync2, statSync } from "fs";
|
|
3951
3957
|
import { join as join3 } from "path";
|
|
3952
3958
|
import { v4 as uuidv4 } from "uuid";
|
|
3953
3959
|
|
|
@@ -4483,19 +4489,6 @@ var Session = class _Session {
|
|
|
4483
4489
|
};
|
|
4484
4490
|
|
|
4485
4491
|
// src/session/session-manager.ts
|
|
4486
|
-
function safeDate(value) {
|
|
4487
|
-
const d = new Date(value);
|
|
4488
|
-
return isNaN(d.getTime()) ? /* @__PURE__ */ new Date(0) : d;
|
|
4489
|
-
}
|
|
4490
|
-
function extractJsonField(header, field) {
|
|
4491
|
-
const re = new RegExp(`"${field}"\\s*:\\s*"([^"]*)"`, "i");
|
|
4492
|
-
const m = header.match(re);
|
|
4493
|
-
return m ? m[1] : void 0;
|
|
4494
|
-
}
|
|
4495
|
-
function extractJsonNumberField(header, field) {
|
|
4496
|
-
const m = header.match(new RegExp(`"${field}"\\s*:\\s*(\\d+)`));
|
|
4497
|
-
return m ? Number(m[1]) : void 0;
|
|
4498
|
-
}
|
|
4499
4492
|
var SessionManager = class {
|
|
4500
4493
|
_current = null;
|
|
4501
4494
|
historyDir;
|
|
@@ -4553,6 +4546,18 @@ var SessionManager = class {
|
|
|
4553
4546
|
this.lastRedactionHits = hits.length;
|
|
4554
4547
|
atomicWriteFileSync(filePath, JSON.stringify(payload, null, 2));
|
|
4555
4548
|
this._current.dirty = false;
|
|
4549
|
+
try {
|
|
4550
|
+
updateSessionInIndex(this.historyDir, {
|
|
4551
|
+
id: this._current.id,
|
|
4552
|
+
provider: this._current.provider,
|
|
4553
|
+
model: this._current.model,
|
|
4554
|
+
title: this._current.title,
|
|
4555
|
+
messageCount: this._current.messages.length,
|
|
4556
|
+
created: this._current.created,
|
|
4557
|
+
updated: this._current.updated
|
|
4558
|
+
}, filePath);
|
|
4559
|
+
} catch {
|
|
4560
|
+
}
|
|
4556
4561
|
}
|
|
4557
4562
|
loadSession(id) {
|
|
4558
4563
|
const filePath = join3(this.historyDir, `${id}.json`);
|
|
@@ -4571,12 +4576,16 @@ var SessionManager = class {
|
|
|
4571
4576
|
}
|
|
4572
4577
|
listSessions(opts) {
|
|
4573
4578
|
if (!existsSync3(this.historyDir)) return [];
|
|
4579
|
+
try {
|
|
4580
|
+
return listFromIndex(this.historyDir, opts);
|
|
4581
|
+
} catch {
|
|
4582
|
+
}
|
|
4574
4583
|
const files = readdirSync(this.historyDir).filter((f) => f.endsWith(".json"));
|
|
4575
4584
|
const metas = [];
|
|
4576
4585
|
for (const file of files) {
|
|
4577
4586
|
try {
|
|
4578
4587
|
const filePath = join3(this.historyDir, file);
|
|
4579
|
-
const meta =
|
|
4588
|
+
const meta = readSessionMeta(filePath);
|
|
4580
4589
|
if (!meta) continue;
|
|
4581
4590
|
if (opts?.includeFileSize) {
|
|
4582
4591
|
try {
|
|
@@ -4594,78 +4603,6 @@ var SessionManager = class {
|
|
|
4594
4603
|
}
|
|
4595
4604
|
return metas.sort((a, b) => b.updated.getTime() - a.updated.getTime());
|
|
4596
4605
|
}
|
|
4597
|
-
/**
|
|
4598
|
-
* P1-B: Read only the first ~1KB of a session file to extract metadata fields.
|
|
4599
|
-
* Session JSON format puts id/provider/model/created/updated/title before the
|
|
4600
|
-
* large "messages" array, so a small header read suffices for metadata extraction.
|
|
4601
|
-
* Falls back to full file read if header parsing fails.
|
|
4602
|
-
*/
|
|
4603
|
-
readSessionMeta(filePath) {
|
|
4604
|
-
const HEADER_SIZE = 1024;
|
|
4605
|
-
let header;
|
|
4606
|
-
try {
|
|
4607
|
-
const fd = openSync(filePath, "r");
|
|
4608
|
-
const buf = Buffer.alloc(HEADER_SIZE);
|
|
4609
|
-
const bytesRead = readSync(fd, buf, 0, HEADER_SIZE, 0);
|
|
4610
|
-
closeSync(fd);
|
|
4611
|
-
header = buf.toString("utf-8", 0, bytesRead);
|
|
4612
|
-
} catch {
|
|
4613
|
-
return null;
|
|
4614
|
-
}
|
|
4615
|
-
const id = extractJsonField(header, "id");
|
|
4616
|
-
const provider = extractJsonField(header, "provider");
|
|
4617
|
-
const model = extractJsonField(header, "model");
|
|
4618
|
-
const created = extractJsonField(header, "created");
|
|
4619
|
-
const updated = extractJsonField(header, "updated");
|
|
4620
|
-
const title = extractJsonField(header, "title");
|
|
4621
|
-
if (id && provider && model) {
|
|
4622
|
-
let messageCount = extractJsonNumberField(header, "messageCount");
|
|
4623
|
-
if (messageCount === void 0) {
|
|
4624
|
-
messageCount = 0;
|
|
4625
|
-
try {
|
|
4626
|
-
const full = readFileSync3(filePath, "utf-8");
|
|
4627
|
-
const matches = full.match(/"role"\s*:/g);
|
|
4628
|
-
messageCount = matches ? matches.length : 0;
|
|
4629
|
-
} catch {
|
|
4630
|
-
}
|
|
4631
|
-
}
|
|
4632
|
-
let branchCount;
|
|
4633
|
-
let activeBranch;
|
|
4634
|
-
try {
|
|
4635
|
-
const brMatch = header.match(/"branches"\s*:\s*\[/);
|
|
4636
|
-
if (brMatch) {
|
|
4637
|
-
const brIds = header.match(/"id"\s*:\s*"[^"]*"/g);
|
|
4638
|
-
const parentMatches = header.match(/"parentBranchId"/g);
|
|
4639
|
-
branchCount = parentMatches ? parentMatches.length + 1 : 1;
|
|
4640
|
-
}
|
|
4641
|
-
activeBranch = extractJsonField(header, "activeBranchId");
|
|
4642
|
-
} catch {
|
|
4643
|
-
}
|
|
4644
|
-
return {
|
|
4645
|
-
id,
|
|
4646
|
-
provider,
|
|
4647
|
-
model,
|
|
4648
|
-
messageCount,
|
|
4649
|
-
created: safeDate(created),
|
|
4650
|
-
updated: safeDate(updated),
|
|
4651
|
-
title: title || void 0,
|
|
4652
|
-
branchCount,
|
|
4653
|
-
activeBranch
|
|
4654
|
-
};
|
|
4655
|
-
}
|
|
4656
|
-
const data = JSON.parse(readFileSync3(filePath, "utf-8"));
|
|
4657
|
-
return {
|
|
4658
|
-
id: data.id,
|
|
4659
|
-
provider: data.provider,
|
|
4660
|
-
model: data.model,
|
|
4661
|
-
messageCount: data.messages?.length ?? 0,
|
|
4662
|
-
created: safeDate(data.created),
|
|
4663
|
-
updated: safeDate(data.updated),
|
|
4664
|
-
title: data.title,
|
|
4665
|
-
branchCount: data.branches?.length ?? 1,
|
|
4666
|
-
activeBranch: data.activeBranchId
|
|
4667
|
-
};
|
|
4668
|
-
}
|
|
4669
4606
|
deleteSession(id) {
|
|
4670
4607
|
const filePath = join3(this.historyDir, `${id}.json`);
|
|
4671
4608
|
if (!existsSync3(filePath)) return false;
|
|
@@ -4674,6 +4611,10 @@ var SessionManager = class {
|
|
|
4674
4611
|
if (this._current && this._current.id === id) {
|
|
4675
4612
|
this._current = null;
|
|
4676
4613
|
}
|
|
4614
|
+
try {
|
|
4615
|
+
removeSessionFromIndex(this.historyDir, id);
|
|
4616
|
+
} catch {
|
|
4617
|
+
}
|
|
4677
4618
|
return true;
|
|
4678
4619
|
} catch {
|
|
4679
4620
|
return false;
|
|
@@ -4707,9 +4648,15 @@ var SessionManager = class {
|
|
|
4707
4648
|
searchMessages(query, maxResults = 20) {
|
|
4708
4649
|
if (!existsSync3(this.historyDir)) return [];
|
|
4709
4650
|
const q = query.toLowerCase();
|
|
4710
|
-
const
|
|
4651
|
+
const candidateIds = searchCandidates(this.historyDir, q);
|
|
4652
|
+
let filesToSearch;
|
|
4653
|
+
if (candidateIds !== null) {
|
|
4654
|
+
filesToSearch = candidateIds.map((id) => join3(this.historyDir, `${id}.json`)).filter((fp) => existsSync3(fp));
|
|
4655
|
+
} else {
|
|
4656
|
+
filesToSearch = readdirSync(this.historyDir).filter((f) => f.endsWith(".json")).map((f) => join3(this.historyDir, f));
|
|
4657
|
+
}
|
|
4711
4658
|
const results = [];
|
|
4712
|
-
for (const filePath of
|
|
4659
|
+
for (const filePath of filesToSearch) {
|
|
4713
4660
|
if (results.length >= maxResults) break;
|
|
4714
4661
|
try {
|
|
4715
4662
|
const data = JSON.parse(readFileSync3(filePath, "utf-8"));
|
|
@@ -5990,11 +5937,11 @@ ${content}`;
|
|
|
5990
5937
|
|
|
5991
5938
|
// src/tools/builtin/write-file.ts
|
|
5992
5939
|
import { appendFileSync, mkdirSync as mkdirSync6 } from "fs";
|
|
5993
|
-
import { dirname as
|
|
5940
|
+
import { dirname as dirname5 } from "path";
|
|
5994
5941
|
|
|
5995
5942
|
// src/tools/executor.ts
|
|
5996
5943
|
import chalk3 from "chalk";
|
|
5997
|
-
import { existsSync as
|
|
5944
|
+
import { existsSync as existsSync11, readFileSync as readFileSync9 } from "fs";
|
|
5998
5945
|
import { tmpdir } from "os";
|
|
5999
5946
|
|
|
6000
5947
|
// src/core/readline-internal.ts
|
|
@@ -6363,7 +6310,8 @@ function runHook(template, vars) {
|
|
|
6363
6310
|
}
|
|
6364
6311
|
|
|
6365
6312
|
// src/tools/permissions.ts
|
|
6366
|
-
import {
|
|
6313
|
+
import { existsSync as existsSync8, realpathSync } from "fs";
|
|
6314
|
+
import { basename as basename2, dirname as dirname3, isAbsolute, resolve as resolve3 } from "path";
|
|
6367
6315
|
function checkPermission(toolName, args, dangerLevel, rules, defaultAction = "confirm") {
|
|
6368
6316
|
for (const rule of rules) {
|
|
6369
6317
|
if (rule.tool !== "*" && rule.tool !== toolName) continue;
|
|
@@ -6408,10 +6356,28 @@ var READ_ONLY_TOOLS = /* @__PURE__ */ new Set([
|
|
|
6408
6356
|
function normalizePathForPermission(value) {
|
|
6409
6357
|
return value.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
6410
6358
|
}
|
|
6359
|
+
function canonicalizeWithNearestParent(value) {
|
|
6360
|
+
const missing = [];
|
|
6361
|
+
let cursor = resolve3(value);
|
|
6362
|
+
while (!existsSync8(cursor)) {
|
|
6363
|
+
const parent = dirname3(cursor);
|
|
6364
|
+
if (parent === cursor) return null;
|
|
6365
|
+
missing.unshift(basename2(cursor));
|
|
6366
|
+
cursor = parent;
|
|
6367
|
+
}
|
|
6368
|
+
try {
|
|
6369
|
+
return resolve3(realpathSync(cursor), ...missing);
|
|
6370
|
+
} catch {
|
|
6371
|
+
return null;
|
|
6372
|
+
}
|
|
6373
|
+
}
|
|
6411
6374
|
function isInsidePath(root, target) {
|
|
6412
6375
|
const absoluteTarget = isAbsolute(target) ? target : resolve3(root, target);
|
|
6413
|
-
const
|
|
6414
|
-
const
|
|
6376
|
+
const canonicalRoot = canonicalizeWithNearestParent(root);
|
|
6377
|
+
const canonicalTarget = canonicalizeWithNearestParent(absoluteTarget);
|
|
6378
|
+
if (!canonicalRoot || !canonicalTarget) return false;
|
|
6379
|
+
const r = normalizePathForPermission(canonicalRoot);
|
|
6380
|
+
const t = normalizePathForPermission(canonicalTarget);
|
|
6415
6381
|
return t === r || t.startsWith(`${r}/`);
|
|
6416
6382
|
}
|
|
6417
6383
|
function extractPathArg(toolName, args) {
|
|
@@ -6463,19 +6429,31 @@ function profileHardDecision(profileName, toolName, args, dangerLevel, ctx) {
|
|
|
6463
6429
|
return null;
|
|
6464
6430
|
}
|
|
6465
6431
|
if (profileName === "workspace-write") {
|
|
6466
|
-
if (dangerLevel === "destructive")
|
|
6432
|
+
if (dangerLevel === "destructive") {
|
|
6433
|
+
return { action: "confirm", reason: "destructive tools always require confirmation", profileName, hardBoundary: true };
|
|
6434
|
+
}
|
|
6467
6435
|
if (dangerLevel === "write" && isExplicitFileWriteTool(toolName)) {
|
|
6468
6436
|
const target = extractPathArg(toolName, args);
|
|
6469
|
-
if (!target)
|
|
6437
|
+
if (!target) {
|
|
6438
|
+
return { action: "confirm", reason: "file write target is not explicit", profileName, hardBoundary: true };
|
|
6439
|
+
}
|
|
6470
6440
|
const allowedRoots = getAllowedWriteRoots(ctx);
|
|
6471
6441
|
if (allowedRoots.length === 0 || !allowedRoots.some((root) => isInsidePath(root, target))) {
|
|
6472
|
-
return { action: "deny", reason: "file write target is outside workspace/temp roots", profileName };
|
|
6442
|
+
return { action: "deny", reason: "file write target is outside workspace/temp roots", profileName, hardBoundary: true };
|
|
6473
6443
|
}
|
|
6474
6444
|
}
|
|
6445
|
+
if (toolName === "bash" || toolName === "run_interactive" || toolName === "task_create") {
|
|
6446
|
+
return {
|
|
6447
|
+
action: "confirm",
|
|
6448
|
+
reason: "shell/process writes cannot be proven to stay inside workspace/temp roots",
|
|
6449
|
+
profileName,
|
|
6450
|
+
hardBoundary: true
|
|
6451
|
+
};
|
|
6452
|
+
}
|
|
6475
6453
|
return null;
|
|
6476
6454
|
}
|
|
6477
6455
|
if (profileName === "danger-full-access") {
|
|
6478
|
-
if (dangerLevel === "destructive") return { action: "confirm", reason: "destructive tools still require confirmation", profileName };
|
|
6456
|
+
if (dangerLevel === "destructive") return { action: "confirm", reason: "destructive tools still require confirmation", profileName, hardBoundary: true };
|
|
6479
6457
|
return null;
|
|
6480
6458
|
}
|
|
6481
6459
|
return null;
|
|
@@ -6487,7 +6465,8 @@ function checkPermissionWithProfile(toolName, args, dangerLevel, rules, defaultA
|
|
|
6487
6465
|
const combinedRules = [...getProfileRules(profileContext), ...rules];
|
|
6488
6466
|
const profileDefault = profileName === "legacy" ? defaultAction : getProfileDefaultAction(profileName, profileContext);
|
|
6489
6467
|
const action = checkPermission(toolName, args, dangerLevel, combinedRules, profileDefault);
|
|
6490
|
-
if (
|
|
6468
|
+
if (action === "deny") return { action, profileName };
|
|
6469
|
+
if (hard?.action === "confirm") return hard;
|
|
6491
6470
|
return { action, profileName };
|
|
6492
6471
|
}
|
|
6493
6472
|
function formatPermissionProfileWarning(profileName) {
|
|
@@ -6745,8 +6724,8 @@ var theme = new Proxy(DARK_THEME, {
|
|
|
6745
6724
|
});
|
|
6746
6725
|
|
|
6747
6726
|
// src/diagnostics/tool-stats.ts
|
|
6748
|
-
import { existsSync as
|
|
6749
|
-
import { join as join5, dirname as
|
|
6727
|
+
import { existsSync as existsSync9, readFileSync as readFileSync7, mkdirSync as mkdirSync5 } from "fs";
|
|
6728
|
+
import { join as join5, dirname as dirname4 } from "path";
|
|
6750
6729
|
import { homedir as homedir3 } from "os";
|
|
6751
6730
|
var STATS_FILE_NAME = "tool-stats.json";
|
|
6752
6731
|
var STATS_VERSION = 1;
|
|
@@ -6761,7 +6740,7 @@ function statsFilePath() {
|
|
|
6761
6740
|
}
|
|
6762
6741
|
function load() {
|
|
6763
6742
|
const path3 = statsFilePath();
|
|
6764
|
-
if (!
|
|
6743
|
+
if (!existsSync9(path3)) {
|
|
6765
6744
|
return { version: STATS_VERSION, startedAt: (/* @__PURE__ */ new Date()).toISOString(), entries: {} };
|
|
6766
6745
|
}
|
|
6767
6746
|
try {
|
|
@@ -6823,7 +6802,7 @@ function flush() {
|
|
|
6823
6802
|
if (!dirty || state === null) return;
|
|
6824
6803
|
const path3 = statsFilePath();
|
|
6825
6804
|
try {
|
|
6826
|
-
mkdirSync5(
|
|
6805
|
+
mkdirSync5(dirname4(path3), { recursive: true });
|
|
6827
6806
|
atomicWriteFileSync(path3, JSON.stringify(state, null, 2));
|
|
6828
6807
|
dirty = false;
|
|
6829
6808
|
pendingWrites = 0;
|
|
@@ -6847,7 +6826,7 @@ function getTopUsedTools(limit = 5) {
|
|
|
6847
6826
|
}
|
|
6848
6827
|
|
|
6849
6828
|
// src/tools/action-classifier.ts
|
|
6850
|
-
import { existsSync as
|
|
6829
|
+
import { existsSync as existsSync10, readFileSync as readFileSync8 } from "fs";
|
|
6851
6830
|
import { isAbsolute as isAbsolute2, join as join6, resolve as resolve4 } from "path";
|
|
6852
6831
|
var RECENT_DENIED_LIMIT = 50;
|
|
6853
6832
|
var recentlyDenied = [];
|
|
@@ -6890,7 +6869,7 @@ function splitShellWords(command) {
|
|
|
6890
6869
|
}
|
|
6891
6870
|
function readPackageManifest(root) {
|
|
6892
6871
|
const p = join6(root, "package.json");
|
|
6893
|
-
if (!
|
|
6872
|
+
if (!existsSync10(p)) return null;
|
|
6894
6873
|
try {
|
|
6895
6874
|
return JSON.parse(readFileSync8(p, "utf-8"));
|
|
6896
6875
|
} catch {
|
|
@@ -6909,7 +6888,7 @@ function manifestHasDependency(root, name) {
|
|
|
6909
6888
|
function lockfileMentions(root, name) {
|
|
6910
6889
|
for (const file of ["package-lock.json", "pnpm-lock.yaml", "yarn.lock"]) {
|
|
6911
6890
|
const p = join6(root, file);
|
|
6912
|
-
if (!
|
|
6891
|
+
if (!existsSync10(p)) continue;
|
|
6913
6892
|
try {
|
|
6914
6893
|
if (readFileSync8(p, "utf-8").includes(name)) return true;
|
|
6915
6894
|
} catch {
|
|
@@ -7185,14 +7164,15 @@ var ToolExecutor = class {
|
|
|
7185
7164
|
return { callId: call.id, content: message, isError: true };
|
|
7186
7165
|
}
|
|
7187
7166
|
}
|
|
7188
|
-
|
|
7167
|
+
const requiresHardConfirmation = permission.action === "confirm" && permission.hardBoundary === true;
|
|
7168
|
+
if ((networkPermission?.action === "confirm" || requiresHardConfirmation) && dangerLevel === "safe") {
|
|
7189
7169
|
this.printToolCall(call);
|
|
7190
7170
|
toolCallAlreadyPrinted = true;
|
|
7191
7171
|
const confirmed = await this.confirm(call, "write");
|
|
7192
7172
|
if (!confirmed) {
|
|
7193
7173
|
return {
|
|
7194
7174
|
callId: call.id,
|
|
7195
|
-
content: `[User cancelled] The user declined
|
|
7175
|
+
content: `[User cancelled] The user declined permission for ${call.name}. Do not retry without asking.`,
|
|
7196
7176
|
isError: true
|
|
7197
7177
|
};
|
|
7198
7178
|
}
|
|
@@ -7210,7 +7190,7 @@ var ToolExecutor = class {
|
|
|
7210
7190
|
isError: true
|
|
7211
7191
|
};
|
|
7212
7192
|
}
|
|
7213
|
-
if (classification.decision === "auto-approve") {
|
|
7193
|
+
if (classification.decision === "auto-approve" && !requiresHardConfirmation) {
|
|
7214
7194
|
this.printToolCall(call);
|
|
7215
7195
|
if (dangerLevel === "write") this.printDiffPreview(call);
|
|
7216
7196
|
console.log(theme.warning(` \u26A1 Auto-approved (/auto: ${classification.ruleId})`));
|
|
@@ -7231,7 +7211,7 @@ var ToolExecutor = class {
|
|
|
7231
7211
|
}
|
|
7232
7212
|
}
|
|
7233
7213
|
}
|
|
7234
|
-
if (this.sessionAutoApprove && dangerLevel === "write") {
|
|
7214
|
+
if (this.sessionAutoApprove && dangerLevel === "write" && !requiresHardConfirmation) {
|
|
7235
7215
|
this.printToolCall(call);
|
|
7236
7216
|
if (dangerLevel === "write") this.printDiffPreview(call);
|
|
7237
7217
|
console.log(theme.warning(" \u26A1 Auto-approved (session /yolo mode)"));
|
|
@@ -7475,7 +7455,7 @@ var ToolExecutor = class {
|
|
|
7475
7455
|
const filePath = String(call.arguments["path"] ?? "");
|
|
7476
7456
|
const newContent = String(call.arguments["content"] ?? "");
|
|
7477
7457
|
if (!filePath) return;
|
|
7478
|
-
if (
|
|
7458
|
+
if (existsSync11(filePath)) {
|
|
7479
7459
|
let oldContent;
|
|
7480
7460
|
try {
|
|
7481
7461
|
oldContent = readFileSync9(filePath, "utf-8");
|
|
@@ -7501,7 +7481,7 @@ var ToolExecutor = class {
|
|
|
7501
7481
|
}
|
|
7502
7482
|
} else if (call.name === "edit_file") {
|
|
7503
7483
|
const filePath = String(call.arguments["path"] ?? "");
|
|
7504
|
-
if (!filePath || !
|
|
7484
|
+
if (!filePath || !existsSync11(filePath)) return;
|
|
7505
7485
|
const oldStr = call.arguments["old_str"];
|
|
7506
7486
|
const newStr = call.arguments["new_str"];
|
|
7507
7487
|
if (oldStr !== void 0) {
|
|
@@ -7620,7 +7600,7 @@ var ToolExecutor = class {
|
|
|
7620
7600
|
};
|
|
7621
7601
|
|
|
7622
7602
|
// src/tools/sensitive-paths.ts
|
|
7623
|
-
import { resolve as resolve5, sep as sep2, basename as
|
|
7603
|
+
import { resolve as resolve5, sep as sep2, basename as basename3 } from "path";
|
|
7624
7604
|
import { homedir as homedir4 } from "os";
|
|
7625
7605
|
var home = homedir4();
|
|
7626
7606
|
function norm(p) {
|
|
@@ -7639,7 +7619,7 @@ function homeRel(p) {
|
|
|
7639
7619
|
function classifyWritePath(path3) {
|
|
7640
7620
|
if (!path3) return { sensitive: false };
|
|
7641
7621
|
const abs = norm(path3);
|
|
7642
|
-
const base =
|
|
7622
|
+
const base = basename3(abs);
|
|
7643
7623
|
const rel = homeRel(path3);
|
|
7644
7624
|
if (rel) {
|
|
7645
7625
|
const shellRc = /* @__PURE__ */ new Set([
|
|
@@ -7763,7 +7743,7 @@ Do NOT split a long document into many write_file(append=true) calls. That patte
|
|
|
7763
7743
|
}
|
|
7764
7744
|
undoStack.push(filePath, `write_file${appendMode ? " (append)" : ""}: ${filePath}`);
|
|
7765
7745
|
fileCheckpoints.snapshot(filePath, ToolExecutor.currentMessageIndex);
|
|
7766
|
-
mkdirSync6(
|
|
7746
|
+
mkdirSync6(dirname5(filePath), { recursive: true });
|
|
7767
7747
|
if (appendMode) {
|
|
7768
7748
|
appendFileSync(filePath, content, encoding);
|
|
7769
7749
|
} else {
|
|
@@ -7783,7 +7763,7 @@ Do NOT split a long document into many write_file(append=true) calls. That patte
|
|
|
7783
7763
|
};
|
|
7784
7764
|
|
|
7785
7765
|
// src/tools/builtin/edit-file.ts
|
|
7786
|
-
import { readFileSync as readFileSync10, existsSync as
|
|
7766
|
+
import { readFileSync as readFileSync10, existsSync as existsSync12 } from "fs";
|
|
7787
7767
|
|
|
7788
7768
|
// src/tools/builtin/patch-apply.ts
|
|
7789
7769
|
function parseUnifiedDiff(patch) {
|
|
@@ -8144,7 +8124,7 @@ Note: Path can be absolute or relative to cwd.`,
|
|
|
8144
8124
|
const filePath = String(args["path"] ?? args["file"] ?? "");
|
|
8145
8125
|
const encoding = args["encoding"] ?? "utf-8";
|
|
8146
8126
|
if (!filePath) throw new ToolError("edit_file", "path is required");
|
|
8147
|
-
if (!
|
|
8127
|
+
if (!existsSync12(filePath)) throw new ToolError("edit_file", `File not found: ${filePath}`);
|
|
8148
8128
|
const verdict = classifyWritePath(filePath);
|
|
8149
8129
|
if (verdict.sensitive && subAgentGuard.active) {
|
|
8150
8130
|
throw new ToolError(
|
|
@@ -8310,8 +8290,8 @@ function truncatePreview(str, maxLen = 80) {
|
|
|
8310
8290
|
}
|
|
8311
8291
|
|
|
8312
8292
|
// src/tools/builtin/list-dir.ts
|
|
8313
|
-
import { readdirSync as readdirSync4, statSync as statSync4, existsSync as
|
|
8314
|
-
import { join as join7, basename as
|
|
8293
|
+
import { readdirSync as readdirSync4, statSync as statSync4, existsSync as existsSync13 } from "fs";
|
|
8294
|
+
import { join as join7, basename as basename4 } from "path";
|
|
8315
8295
|
var listDirTool = {
|
|
8316
8296
|
definition: {
|
|
8317
8297
|
name: "list_dir",
|
|
@@ -8333,8 +8313,8 @@ var listDirTool = {
|
|
|
8333
8313
|
async execute(args) {
|
|
8334
8314
|
const dirPath = String(args["path"] ?? process.cwd());
|
|
8335
8315
|
const recursive = Boolean(args["recursive"] ?? false);
|
|
8336
|
-
if (!
|
|
8337
|
-
const targetName =
|
|
8316
|
+
if (!existsSync13(dirPath)) {
|
|
8317
|
+
const targetName = basename4(dirPath).toLowerCase();
|
|
8338
8318
|
const cwd = process.cwd();
|
|
8339
8319
|
const suggestions = [];
|
|
8340
8320
|
try {
|
|
@@ -8411,7 +8391,7 @@ function formatSize(bytes) {
|
|
|
8411
8391
|
}
|
|
8412
8392
|
|
|
8413
8393
|
// src/tools/builtin/grep-files.ts
|
|
8414
|
-
import { readdirSync as readdirSync5, readFileSync as readFileSync11, statSync as statSync5, existsSync as
|
|
8394
|
+
import { readdirSync as readdirSync5, readFileSync as readFileSync11, statSync as statSync5, existsSync as existsSync14 } from "fs";
|
|
8415
8395
|
import { readFile } from "fs/promises";
|
|
8416
8396
|
import { join as join8, relative } from "path";
|
|
8417
8397
|
var grepFilesTool = {
|
|
@@ -8464,7 +8444,7 @@ Supports regex. Automatically skips node_modules, dist, .git directories.`,
|
|
|
8464
8444
|
const contextLines = Math.max(0, Number(args["context_lines"] ?? 0));
|
|
8465
8445
|
const maxResults = Math.max(1, Number(args["max_results"] ?? 50));
|
|
8466
8446
|
if (!pattern) throw new ToolError("grep_files", "pattern is required");
|
|
8467
|
-
if (!
|
|
8447
|
+
if (!existsSync14(rootPath)) throw new ToolError("grep_files", `Path not found: ${rootPath}`);
|
|
8468
8448
|
const MAX_PATTERN_LENGTH = 1e3;
|
|
8469
8449
|
if (pattern.length > MAX_PATTERN_LENGTH) {
|
|
8470
8450
|
throw new ToolError("grep_files", `Pattern too long (${pattern.length} chars, max ${MAX_PATTERN_LENGTH}). Use a shorter pattern.`);
|
|
@@ -8648,8 +8628,8 @@ function searchInFile(fullPath, displayPath2, regex, contextLines, maxResults, r
|
|
|
8648
8628
|
}
|
|
8649
8629
|
|
|
8650
8630
|
// src/tools/builtin/glob-files.ts
|
|
8651
|
-
import { readdirSync as readdirSync6, statSync as statSync6, existsSync as
|
|
8652
|
-
import { join as join9, relative as relative2, basename as
|
|
8631
|
+
import { readdirSync as readdirSync6, statSync as statSync6, existsSync as existsSync15 } from "fs";
|
|
8632
|
+
import { join as join9, relative as relative2, basename as basename5 } from "path";
|
|
8653
8633
|
var globFilesTool = {
|
|
8654
8634
|
definition: {
|
|
8655
8635
|
name: "glob_files",
|
|
@@ -8682,7 +8662,7 @@ Results sorted by most recent modification time. Automatically skips node_module
|
|
|
8682
8662
|
const rootPath = String(args["path"] ?? process.cwd());
|
|
8683
8663
|
const maxResults = Math.max(1, Number(args["max_results"] ?? 100));
|
|
8684
8664
|
if (!pattern) throw new ToolError("glob_files", "pattern is required");
|
|
8685
|
-
if (!
|
|
8665
|
+
if (!existsSync15(rootPath)) throw new ToolError("glob_files", `Path not found: ${rootPath}`);
|
|
8686
8666
|
const regex = globToRegex(pattern);
|
|
8687
8667
|
const matches = [];
|
|
8688
8668
|
collectMatchingFiles(rootPath, rootPath, regex, matches, maxResults);
|
|
@@ -8765,7 +8745,7 @@ function collectMatchingFiles(dirPath, rootPath, regex, results, maxResults) {
|
|
|
8765
8745
|
collectMatchingFiles(fullPath, rootPath, regex, results, maxResults);
|
|
8766
8746
|
} else if (entry.isFile()) {
|
|
8767
8747
|
const relPath = relative2(rootPath, fullPath).replace(/\\/g, "/");
|
|
8768
|
-
if (regex.test(relPath) || regex.test(
|
|
8748
|
+
if (regex.test(relPath) || regex.test(basename5(relPath))) {
|
|
8769
8749
|
try {
|
|
8770
8750
|
const stat = statSync6(fullPath);
|
|
8771
8751
|
results.push({ relPath, absPath: fullPath, mtime: stat.mtimeMs });
|
|
@@ -9500,7 +9480,7 @@ ${preamble}`;
|
|
|
9500
9480
|
|
|
9501
9481
|
// src/tools/builtin/save-last-response.ts
|
|
9502
9482
|
import { mkdirSync as mkdirSync7, unlinkSync as unlinkSync4, rmdirSync as rmdirSync2 } from "fs";
|
|
9503
|
-
import { dirname as
|
|
9483
|
+
import { dirname as dirname6 } from "path";
|
|
9504
9484
|
var lastResponseStore = { content: "" };
|
|
9505
9485
|
function cleanupRejectedTeeFile(filePath) {
|
|
9506
9486
|
try {
|
|
@@ -9508,7 +9488,7 @@ function cleanupRejectedTeeFile(filePath) {
|
|
|
9508
9488
|
} catch {
|
|
9509
9489
|
}
|
|
9510
9490
|
try {
|
|
9511
|
-
rmdirSync2(
|
|
9491
|
+
rmdirSync2(dirname6(filePath));
|
|
9512
9492
|
} catch {
|
|
9513
9493
|
}
|
|
9514
9494
|
}
|
|
@@ -9549,7 +9529,7 @@ Any of these triggers means use save_last_response, NOT write_file:
|
|
|
9549
9529
|
throw new ToolError("save_last_response", "No content to save: AI has not produced any response yet, or the last response was empty.");
|
|
9550
9530
|
}
|
|
9551
9531
|
undoStack.push(filePath, `save_last_response: ${filePath}`);
|
|
9552
|
-
mkdirSync7(
|
|
9532
|
+
mkdirSync7(dirname6(filePath), { recursive: true });
|
|
9553
9533
|
atomicWriteFileSync(filePath, content);
|
|
9554
9534
|
const lines = content.split("\n").length;
|
|
9555
9535
|
return `File saved: ${filePath} (${lines} lines, ${content.length} bytes)`;
|
|
@@ -9561,13 +9541,13 @@ import { join as join12 } from "path";
|
|
|
9561
9541
|
import { homedir as homedir5 } from "os";
|
|
9562
9542
|
|
|
9563
9543
|
// src/memory/persistent-memory.ts
|
|
9564
|
-
import { existsSync as
|
|
9544
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync8, readFileSync as readFileSync12 } from "fs";
|
|
9565
9545
|
import { randomUUID, createHash as createHash2 } from "crypto";
|
|
9566
|
-
import { dirname as
|
|
9546
|
+
import { dirname as dirname7, join as join11, resolve as resolve6 } from "path";
|
|
9567
9547
|
|
|
9568
9548
|
// src/tools/git-context.ts
|
|
9569
9549
|
import { execSync as execSync2 } from "child_process";
|
|
9570
|
-
import { existsSync as
|
|
9550
|
+
import { existsSync as existsSync16 } from "fs";
|
|
9571
9551
|
import { join as join10 } from "path";
|
|
9572
9552
|
function runGit(cmd, cwd) {
|
|
9573
9553
|
try {
|
|
@@ -9585,7 +9565,7 @@ function getGitRoot(cwd = process.cwd()) {
|
|
|
9585
9565
|
return runGit("rev-parse --show-toplevel", cwd);
|
|
9586
9566
|
}
|
|
9587
9567
|
function getGitContext(cwd = process.cwd()) {
|
|
9588
|
-
if (!
|
|
9568
|
+
if (!existsSync16(join10(cwd, ".git"))) {
|
|
9589
9569
|
const result = runGit("rev-parse --git-dir", cwd);
|
|
9590
9570
|
if (!result) return null;
|
|
9591
9571
|
}
|
|
@@ -9696,7 +9676,7 @@ function normalizeEntry(value) {
|
|
|
9696
9676
|
}
|
|
9697
9677
|
function legacyEntriesFromMarkdown(configDir) {
|
|
9698
9678
|
const file = memoryMarkdownPath(configDir);
|
|
9699
|
-
if (!
|
|
9679
|
+
if (!existsSync17(file)) return [];
|
|
9700
9680
|
const content = readFileSync12(file, "utf-8").trim();
|
|
9701
9681
|
if (!content) return [];
|
|
9702
9682
|
const parts = content.split(/\n(?=##\s+\d{4}-\d{2}-\d{2})/g);
|
|
@@ -9713,7 +9693,7 @@ ${chunk}`).digest("hex").slice(0, 12);
|
|
|
9713
9693
|
}
|
|
9714
9694
|
function loadMemoryEntries(configDir) {
|
|
9715
9695
|
const file = memoryStorePath(configDir);
|
|
9716
|
-
if (!
|
|
9696
|
+
if (!existsSync17(file)) return legacyEntriesFromMarkdown(configDir);
|
|
9717
9697
|
const text = readFileSync12(file, "utf-8").trim();
|
|
9718
9698
|
if (!text) return [];
|
|
9719
9699
|
const entries = [];
|
|
@@ -10161,13 +10141,13 @@ function formatResults2(query, data, _requested) {
|
|
|
10161
10141
|
}
|
|
10162
10142
|
|
|
10163
10143
|
// src/agents/agent-config.ts
|
|
10164
|
-
import { existsSync as
|
|
10144
|
+
import { existsSync as existsSync19, readdirSync as readdirSync8, readFileSync as readFileSync14 } from "fs";
|
|
10165
10145
|
import { join as join14 } from "path";
|
|
10166
10146
|
import { homedir as homedir6 } from "os";
|
|
10167
10147
|
|
|
10168
10148
|
// src/plugins/plugin-manager.ts
|
|
10169
|
-
import { existsSync as
|
|
10170
|
-
import { basename as
|
|
10149
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync9, readdirSync as readdirSync7, readFileSync as readFileSync13, cpSync, rmSync, statSync as statSync7 } from "fs";
|
|
10150
|
+
import { basename as basename6, dirname as dirname8, join as join13, resolve as resolve7 } from "path";
|
|
10171
10151
|
import { createHash as createHash3 } from "crypto";
|
|
10172
10152
|
import { z as z2 } from "zod";
|
|
10173
10153
|
var MANIFEST_RELATIVE = ".aicli-plugin/plugin.json";
|
|
@@ -10237,7 +10217,7 @@ function statePath(configDir) {
|
|
|
10237
10217
|
}
|
|
10238
10218
|
function loadState(configDir) {
|
|
10239
10219
|
const file = statePath(configDir);
|
|
10240
|
-
if (!
|
|
10220
|
+
if (!existsSync18(file)) return { version: 1, plugins: [] };
|
|
10241
10221
|
try {
|
|
10242
10222
|
const parsed = JSON.parse(readFileSync13(file, "utf-8"));
|
|
10243
10223
|
return { version: 1, plugins: Array.isArray(parsed.plugins) ? parsed.plugins : [] };
|
|
@@ -10257,10 +10237,10 @@ function readPluginManifest(manifestPath) {
|
|
|
10257
10237
|
}
|
|
10258
10238
|
function findPluginSourceDir(inputPath) {
|
|
10259
10239
|
const abs = resolve7(inputPath);
|
|
10260
|
-
const direct = statSync7(abs).isDirectory() ? abs :
|
|
10261
|
-
if (
|
|
10240
|
+
const direct = statSync7(abs).isDirectory() ? abs : dirname8(abs);
|
|
10241
|
+
if (existsSync18(pluginManifestPath(direct))) return direct;
|
|
10262
10242
|
const nested = join13(direct, ".aicli-plugin");
|
|
10263
|
-
if (
|
|
10243
|
+
if (existsSync18(join13(nested, "plugin.json"))) return direct;
|
|
10264
10244
|
throw new Error(`plugin manifest not found: ${MANIFEST_RELATIVE}`);
|
|
10265
10245
|
}
|
|
10266
10246
|
function installPlugin(configDir, inputPath) {
|
|
@@ -10268,7 +10248,7 @@ function installPlugin(configDir, inputPath) {
|
|
|
10268
10248
|
const manifest = readPluginManifest(pluginManifestPath(sourceDir));
|
|
10269
10249
|
const targetDir = join13(pluginRoot(configDir), manifest.name);
|
|
10270
10250
|
mkdirSync9(pluginRoot(configDir), { recursive: true });
|
|
10271
|
-
if (
|
|
10251
|
+
if (existsSync18(targetDir)) rmSync(targetDir, { recursive: true, force: true });
|
|
10272
10252
|
cpSync(sourceDir, targetDir, { recursive: true });
|
|
10273
10253
|
const manifestPath = pluginManifestPath(targetDir);
|
|
10274
10254
|
const hash = hashPluginManifest(manifestPath);
|
|
@@ -10326,7 +10306,7 @@ function getInstalledPlugin(configDir, name) {
|
|
|
10326
10306
|
}
|
|
10327
10307
|
function listInstalledPlugins(configDir) {
|
|
10328
10308
|
const root = pluginRoot(configDir);
|
|
10329
|
-
if (!
|
|
10309
|
+
if (!existsSync18(root)) return [];
|
|
10330
10310
|
const state2 = loadState(configDir);
|
|
10331
10311
|
const out = [];
|
|
10332
10312
|
for (const entry of readdirSync7(root)) {
|
|
@@ -10337,7 +10317,7 @@ function listInstalledPlugins(configDir) {
|
|
|
10337
10317
|
continue;
|
|
10338
10318
|
}
|
|
10339
10319
|
const manifestPath = pluginManifestPath(dir);
|
|
10340
|
-
if (!
|
|
10320
|
+
if (!existsSync18(manifestPath)) continue;
|
|
10341
10321
|
try {
|
|
10342
10322
|
const manifest = readPluginManifest(manifestPath);
|
|
10343
10323
|
const hash = hashPluginManifest(manifestPath);
|
|
@@ -10377,7 +10357,7 @@ function uniqueDirs(plugin, rels) {
|
|
|
10377
10357
|
for (const rel of rels) {
|
|
10378
10358
|
const abs = resolve7(plugin.dir, rel);
|
|
10379
10359
|
if (!abs.startsWith(resolve7(plugin.dir))) continue;
|
|
10380
|
-
if (
|
|
10360
|
+
if (existsSync18(abs)) dirs.add(statSync7(abs).isDirectory() ? abs : dirname8(abs));
|
|
10381
10361
|
}
|
|
10382
10362
|
return [...dirs];
|
|
10383
10363
|
}
|
|
@@ -10540,7 +10520,7 @@ function parseAgentConfig(raw, source, path3) {
|
|
|
10540
10520
|
return cfg;
|
|
10541
10521
|
}
|
|
10542
10522
|
function loadAgentDir(dir, source) {
|
|
10543
|
-
if (!
|
|
10523
|
+
if (!existsSync19(dir)) return [];
|
|
10544
10524
|
const out = [];
|
|
10545
10525
|
for (const file of readdirSync8(dir)) {
|
|
10546
10526
|
if (!file.endsWith(".json")) continue;
|
|
@@ -11272,13 +11252,13 @@ var taskStopTool = {
|
|
|
11272
11252
|
|
|
11273
11253
|
// src/tools/builtin/git-tools.ts
|
|
11274
11254
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
11275
|
-
import { existsSync as
|
|
11255
|
+
import { existsSync as existsSync20 } from "fs";
|
|
11276
11256
|
import { join as join15 } from "path";
|
|
11277
11257
|
function assertGitRepo(cwd) {
|
|
11278
11258
|
let dir = cwd;
|
|
11279
11259
|
const root = dir.split(/[\\/]/)[0] + (dir.includes("\\") ? "\\" : "/");
|
|
11280
11260
|
while (dir && dir !== root) {
|
|
11281
|
-
if (
|
|
11261
|
+
if (existsSync20(join15(dir, ".git"))) return;
|
|
11282
11262
|
const parent = join15(dir, "..");
|
|
11283
11263
|
if (parent === dir) break;
|
|
11284
11264
|
dir = parent;
|
|
@@ -11544,7 +11524,7 @@ ${commitOutput.trim()}`;
|
|
|
11544
11524
|
};
|
|
11545
11525
|
|
|
11546
11526
|
// src/tools/builtin/notebook-edit.ts
|
|
11547
|
-
import { readFileSync as readFileSync15, existsSync as
|
|
11527
|
+
import { readFileSync as readFileSync15, existsSync as existsSync21 } from "fs";
|
|
11548
11528
|
import { writeFile } from "fs/promises";
|
|
11549
11529
|
import { resolve as resolve8, extname as extname2 } from "path";
|
|
11550
11530
|
var notebookEditTool = {
|
|
@@ -11599,7 +11579,7 @@ var notebookEditTool = {
|
|
|
11599
11579
|
if (extname2(absPath).toLowerCase() !== ".ipynb") {
|
|
11600
11580
|
throw new ToolError("notebook_edit", "path must point to a .ipynb file");
|
|
11601
11581
|
}
|
|
11602
|
-
if (!
|
|
11582
|
+
if (!existsSync21(absPath)) {
|
|
11603
11583
|
throw new ToolError("notebook_edit", `Notebook not found: ${filePath}`);
|
|
11604
11584
|
}
|
|
11605
11585
|
const raw = readFileSync15(absPath, "utf-8");
|
|
@@ -12019,7 +11999,7 @@ function estimateToolDefinitionTokens(def) {
|
|
|
12019
11999
|
|
|
12020
12000
|
// src/tools/registry.ts
|
|
12021
12001
|
import { pathToFileURL } from "url";
|
|
12022
|
-
import { existsSync as
|
|
12002
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync10, readdirSync as readdirSync9 } from "fs";
|
|
12023
12003
|
import { join as join16 } from "path";
|
|
12024
12004
|
var ToolRegistry = class {
|
|
12025
12005
|
tools = /* @__PURE__ */ new Map();
|
|
@@ -12181,7 +12161,7 @@ var ToolRegistry = class {
|
|
|
12181
12161
|
* Returns the number of successfully loaded plugins.
|
|
12182
12162
|
*/
|
|
12183
12163
|
async loadPlugins(pluginsDir, allowPlugins = false) {
|
|
12184
|
-
if (!
|
|
12164
|
+
if (!existsSync22(pluginsDir)) {
|
|
12185
12165
|
try {
|
|
12186
12166
|
mkdirSync10(pluginsDir, { recursive: true });
|
|
12187
12167
|
} catch {
|
|
@@ -12758,12 +12738,12 @@ var McpManager = class {
|
|
|
12758
12738
|
};
|
|
12759
12739
|
|
|
12760
12740
|
// src/skills/manager.ts
|
|
12761
|
-
import { existsSync as
|
|
12741
|
+
import { existsSync as existsSync23, readdirSync as readdirSync10, mkdirSync as mkdirSync11, statSync as statSync8 } from "fs";
|
|
12762
12742
|
import { join as join17 } from "path";
|
|
12763
12743
|
|
|
12764
12744
|
// src/skills/types.ts
|
|
12765
12745
|
import { readFileSync as readFileSync16 } from "fs";
|
|
12766
|
-
import { basename as
|
|
12746
|
+
import { basename as basename7 } from "path";
|
|
12767
12747
|
function parseSimpleYaml(yaml) {
|
|
12768
12748
|
const result = {};
|
|
12769
12749
|
for (const line of yaml.split("\n")) {
|
|
@@ -12792,7 +12772,7 @@ function parseSkillFile(filePath) {
|
|
|
12792
12772
|
if (!frontmatterMatch) {
|
|
12793
12773
|
return {
|
|
12794
12774
|
meta: {
|
|
12795
|
-
name:
|
|
12775
|
+
name: basename7(filePath, ".md"),
|
|
12796
12776
|
description: ""
|
|
12797
12777
|
},
|
|
12798
12778
|
content: raw.trim(),
|
|
@@ -12803,7 +12783,7 @@ function parseSkillFile(filePath) {
|
|
|
12803
12783
|
const parsed = parseSimpleYaml(yaml);
|
|
12804
12784
|
return {
|
|
12805
12785
|
meta: {
|
|
12806
|
-
name: parsed["name"] ??
|
|
12786
|
+
name: parsed["name"] ?? basename7(filePath, ".md"),
|
|
12807
12787
|
description: parsed["description"] ?? "",
|
|
12808
12788
|
tools: parsed["tools"] ? parseYamlArray(parsed["tools"]) : void 0
|
|
12809
12789
|
},
|
|
@@ -12832,7 +12812,7 @@ var SkillManager = class {
|
|
|
12832
12812
|
return this.skills.size;
|
|
12833
12813
|
}
|
|
12834
12814
|
loadSkillDir(dir, createIfMissing) {
|
|
12835
|
-
if (!
|
|
12815
|
+
if (!existsSync23(dir)) {
|
|
12836
12816
|
if (createIfMissing) {
|
|
12837
12817
|
try {
|
|
12838
12818
|
mkdirSync11(dir, { recursive: true });
|
|
@@ -12856,7 +12836,7 @@ var SkillManager = class {
|
|
|
12856
12836
|
try {
|
|
12857
12837
|
if (statSync8(fullPath).isDirectory()) {
|
|
12858
12838
|
const skillMd = join17(fullPath, "SKILL.md");
|
|
12859
|
-
if (
|
|
12839
|
+
if (existsSync23(skillMd)) filePath = skillMd;
|
|
12860
12840
|
else continue;
|
|
12861
12841
|
} else {
|
|
12862
12842
|
continue;
|
|
@@ -12915,7 +12895,7 @@ var SkillManager = class {
|
|
|
12915
12895
|
// src/web/tool-executor-web.ts
|
|
12916
12896
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
12917
12897
|
import { tmpdir as tmpdir2 } from "os";
|
|
12918
|
-
import { existsSync as
|
|
12898
|
+
import { existsSync as existsSync24, readFileSync as readFileSync17 } from "fs";
|
|
12919
12899
|
var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
12920
12900
|
constructor(registry, ws) {
|
|
12921
12901
|
this.registry = registry;
|
|
@@ -13054,7 +13034,7 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
13054
13034
|
if (call.name === "write_file") {
|
|
13055
13035
|
const filePath = String(call.arguments["path"] ?? "");
|
|
13056
13036
|
const newContent = String(call.arguments["content"] ?? "");
|
|
13057
|
-
if (filePath &&
|
|
13037
|
+
if (filePath && existsSync24(filePath)) {
|
|
13058
13038
|
try {
|
|
13059
13039
|
const old = readFileSync17(filePath, "utf-8");
|
|
13060
13040
|
return renderDiff(old, newContent, { filePath });
|
|
@@ -13212,12 +13192,13 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
13212
13192
|
return { callId: call.id, content: message, isError: true };
|
|
13213
13193
|
}
|
|
13214
13194
|
}
|
|
13215
|
-
|
|
13195
|
+
const requiresHardConfirmation = permission.action === "confirm" && permission.hardBoundary === true;
|
|
13196
|
+
if ((networkPermission?.action === "confirm" || requiresHardConfirmation) && dangerLevel === "safe") {
|
|
13216
13197
|
this.sendToolCallStart(call);
|
|
13217
13198
|
toolCallStarted = true;
|
|
13218
13199
|
const confirmed = await this.confirm(call, "write");
|
|
13219
13200
|
if (!confirmed) {
|
|
13220
|
-
const rejectionMsg = `[User cancelled] The user declined
|
|
13201
|
+
const rejectionMsg = `[User cancelled] The user declined permission for ${call.name}. Do not retry without asking.`;
|
|
13221
13202
|
this.sendToolCallResult(call, rejectionMsg, true);
|
|
13222
13203
|
return { callId: call.id, content: rejectionMsg, isError: true };
|
|
13223
13204
|
}
|
|
@@ -13234,7 +13215,7 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
13234
13215
|
this.sendToolCallResult(call, rejectionMsg, true);
|
|
13235
13216
|
return { callId: call.id, content: rejectionMsg, isError: true };
|
|
13236
13217
|
}
|
|
13237
|
-
if (classification.decision === "auto-approve") {
|
|
13218
|
+
if (classification.decision === "auto-approve" && !requiresHardConfirmation) {
|
|
13238
13219
|
this.send({ type: "info", message: `\u26A1 Auto-approved (/auto: ${classification.ruleId})` });
|
|
13239
13220
|
try {
|
|
13240
13221
|
const rawContent = await runTool(tool, call.arguments, call.name);
|
|
@@ -13252,7 +13233,7 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
13252
13233
|
}
|
|
13253
13234
|
}
|
|
13254
13235
|
}
|
|
13255
|
-
if (this.sessionAutoApprove && dangerLevel === "write") {
|
|
13236
|
+
if (this.sessionAutoApprove && dangerLevel === "write" && !requiresHardConfirmation) {
|
|
13256
13237
|
try {
|
|
13257
13238
|
const rawContent = await runTool(tool, call.arguments, call.name);
|
|
13258
13239
|
const content = truncateOutput(rawContent, call.name);
|
|
@@ -13359,7 +13340,7 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
13359
13340
|
};
|
|
13360
13341
|
|
|
13361
13342
|
// src/repl/dev-state.ts
|
|
13362
|
-
import { existsSync as
|
|
13343
|
+
import { existsSync as existsSync25, readFileSync as readFileSync18, unlinkSync as unlinkSync5, mkdirSync as mkdirSync12 } from "fs";
|
|
13363
13344
|
import { join as join18 } from "path";
|
|
13364
13345
|
import { homedir as homedir7 } from "os";
|
|
13365
13346
|
function getDevStatePath() {
|
|
@@ -13367,7 +13348,7 @@ function getDevStatePath() {
|
|
|
13367
13348
|
}
|
|
13368
13349
|
function loadDevState() {
|
|
13369
13350
|
const path3 = getDevStatePath();
|
|
13370
|
-
if (!
|
|
13351
|
+
if (!existsSync25(path3)) return null;
|
|
13371
13352
|
const content = readFileSync18(path3, "utf-8").trim();
|
|
13372
13353
|
return content || null;
|
|
13373
13354
|
}
|
|
@@ -13625,7 +13606,7 @@ function autoTrimSessionIfNeeded(session, sizeLimit = SESSION_SIZE_LIMIT) {
|
|
|
13625
13606
|
}
|
|
13626
13607
|
|
|
13627
13608
|
// src/core/context-files.ts
|
|
13628
|
-
import { existsSync as
|
|
13609
|
+
import { existsSync as existsSync26, readFileSync as readFileSync19 } from "fs";
|
|
13629
13610
|
import { join as join19, relative as relative3, resolve as resolve9 } from "path";
|
|
13630
13611
|
function uniqueNonEmpty(values) {
|
|
13631
13612
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -13704,7 +13685,7 @@ function findFirstContextFile(level, dir, cwd, candidates, maxBytes) {
|
|
|
13704
13685
|
const skipped = [];
|
|
13705
13686
|
for (const candidate of candidates) {
|
|
13706
13687
|
const filePath = join19(dir, candidate);
|
|
13707
|
-
if (!
|
|
13688
|
+
if (!existsSync26(filePath)) continue;
|
|
13708
13689
|
const result = readContextFile(level, filePath, cwd, maxBytes);
|
|
13709
13690
|
if (result.skipped) skipped.push(result.skipped);
|
|
13710
13691
|
if (result.layer) return { layer: result.layer, skipped };
|
|
@@ -13731,7 +13712,7 @@ function loadContextFiles(options) {
|
|
|
13731
13712
|
});
|
|
13732
13713
|
return { layers: [], mergedContent: "", skipped, totalChars: 0, totalBytes: 0, maxBytes, candidates };
|
|
13733
13714
|
}
|
|
13734
|
-
if (!
|
|
13715
|
+
if (!existsSync26(filePath)) {
|
|
13735
13716
|
skipped.push({
|
|
13736
13717
|
level: "single",
|
|
13737
13718
|
filePath,
|
|
@@ -13783,10 +13764,10 @@ function loadContextFiles(options) {
|
|
|
13783
13764
|
|
|
13784
13765
|
// src/web/session-handler.ts
|
|
13785
13766
|
import { mkdirSync as mkdirSync16, createWriteStream as createWriteStream3 } from "fs";
|
|
13786
|
-
import { join as join26, dirname as
|
|
13767
|
+
import { join as join26, dirname as dirname11 } from "path";
|
|
13787
13768
|
|
|
13788
13769
|
// src/repl/commands/project-init.ts
|
|
13789
|
-
import { existsSync as
|
|
13770
|
+
import { existsSync as existsSync27, readFileSync as readFileSync20, readdirSync as readdirSync11, statSync as statSync9 } from "fs";
|
|
13790
13771
|
import { join as join20 } from "path";
|
|
13791
13772
|
var SCAN_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
13792
13773
|
"node_modules",
|
|
@@ -13870,7 +13851,7 @@ function scanProject(cwd) {
|
|
|
13870
13851
|
configFiles: [],
|
|
13871
13852
|
directoryStructure: ""
|
|
13872
13853
|
};
|
|
13873
|
-
const check = (file) =>
|
|
13854
|
+
const check = (file) => existsSync27(join20(cwd, file));
|
|
13874
13855
|
const configCandidates = [
|
|
13875
13856
|
"package.json",
|
|
13876
13857
|
"tsconfig.json",
|
|
@@ -14672,8 +14653,8 @@ async function handlePlan(args, ctx) {
|
|
|
14672
14653
|
}
|
|
14673
14654
|
|
|
14674
14655
|
// src/web/commands/_shared.ts
|
|
14675
|
-
import { existsSync as
|
|
14676
|
-
import { join as join24, resolve as resolve10, dirname as
|
|
14656
|
+
import { existsSync as existsSync30, readFileSync as readFileSync22, writeFileSync as writeFileSync3, mkdirSync as mkdirSync14, readdirSync as readdirSync13, statSync as statSync12, createWriteStream } from "fs";
|
|
14657
|
+
import { join as join24, resolve as resolve10, dirname as dirname9 } from "path";
|
|
14677
14658
|
|
|
14678
14659
|
// src/cli/review-prompts.ts
|
|
14679
14660
|
function buildReviewPrompt(diff, gitContextStr, detailed) {
|
|
@@ -14745,13 +14726,13 @@ function readGitDiff(options = {}) {
|
|
|
14745
14726
|
}
|
|
14746
14727
|
|
|
14747
14728
|
// src/diagnostics/doctor-report.ts
|
|
14748
|
-
import { existsSync as
|
|
14729
|
+
import { existsSync as existsSync29, statSync as statSync11 } from "fs";
|
|
14749
14730
|
import { join as join23 } from "path";
|
|
14750
14731
|
import { arch as arch2, platform as platform7, release as release2 } from "os";
|
|
14751
14732
|
|
|
14752
14733
|
// src/diagnostics/crash-log.ts
|
|
14753
14734
|
import {
|
|
14754
|
-
existsSync as
|
|
14735
|
+
existsSync as existsSync28,
|
|
14755
14736
|
mkdirSync as mkdirSync13,
|
|
14756
14737
|
readdirSync as readdirSync12,
|
|
14757
14738
|
readFileSync as readFileSync21,
|
|
@@ -14772,7 +14753,7 @@ function truncate(s, n) {
|
|
|
14772
14753
|
}
|
|
14773
14754
|
function listRecentCrashes(limit = 10, configDir) {
|
|
14774
14755
|
const dir = getLogsDir(configDir);
|
|
14775
|
-
if (!
|
|
14756
|
+
if (!existsSync28(dir)) return [];
|
|
14776
14757
|
const files = [];
|
|
14777
14758
|
let names;
|
|
14778
14759
|
try {
|
|
@@ -14805,7 +14786,7 @@ function listRecentCrashes(limit = 10, configDir) {
|
|
|
14805
14786
|
}
|
|
14806
14787
|
function getConfigDirUsage(configDir) {
|
|
14807
14788
|
const base = configDir ?? join22(homedir8(), CONFIG_DIR_NAME);
|
|
14808
|
-
if (!
|
|
14789
|
+
if (!existsSync28(base)) return { totalBytes: 0, entries: [] };
|
|
14809
14790
|
const entries = [];
|
|
14810
14791
|
let total = 0;
|
|
14811
14792
|
let names;
|
|
@@ -14840,7 +14821,7 @@ function dirSize(path3) {
|
|
|
14840
14821
|
|
|
14841
14822
|
// src/diagnostics/doctor-report.ts
|
|
14842
14823
|
function checkFile(label, path3) {
|
|
14843
|
-
const exists =
|
|
14824
|
+
const exists = existsSync29(path3);
|
|
14844
14825
|
let sizeBytes = null;
|
|
14845
14826
|
if (exists) {
|
|
14846
14827
|
try {
|
|
@@ -14938,7 +14919,7 @@ function buildDoctorReport(options) {
|
|
|
14938
14919
|
mcp: {
|
|
14939
14920
|
enabled: config.get("mcpEnabled"),
|
|
14940
14921
|
configured: Object.keys(mcpServers).length,
|
|
14941
|
-
projectConfigExists:
|
|
14922
|
+
projectConfigExists: existsSync29(join23(projectRoot, MCP_PROJECT_CONFIG_NAME)),
|
|
14942
14923
|
statuses: options.mcpStatuses ?? []
|
|
14943
14924
|
},
|
|
14944
14925
|
recentCrashes: listRecentCrashes(5, configDir),
|
|
@@ -15808,7 +15789,7 @@ ${activated.meta.description || ""}` });
|
|
|
15808
15789
|
async function handleCommands(_args, ctx) {
|
|
15809
15790
|
const configDir = ctx.config.getConfigDir();
|
|
15810
15791
|
const commandsDir = join24(configDir, CUSTOM_COMMANDS_DIR_NAME);
|
|
15811
|
-
if (!
|
|
15792
|
+
if (!existsSync30(commandsDir)) {
|
|
15812
15793
|
ctx.send({ type: "info", message: `No custom commands directory.
|
|
15813
15794
|
Create: ${commandsDir}/ with .md files.` });
|
|
15814
15795
|
return;
|
|
@@ -15908,7 +15889,7 @@ async function handleSecurityReview(args, ctx) {
|
|
|
15908
15889
|
async function handleTest(args, ctx) {
|
|
15909
15890
|
ctx.send({ type: "info", message: "\u{1F9EA} Running tests..." });
|
|
15910
15891
|
try {
|
|
15911
|
-
const { executeTests } = await import("./run-tests-
|
|
15892
|
+
const { executeTests } = await import("./run-tests-YSRET52H.js");
|
|
15912
15893
|
const argStr = args.join(" ").trim();
|
|
15913
15894
|
let testArgs = {};
|
|
15914
15895
|
if (argStr) {
|
|
@@ -15938,7 +15919,7 @@ async function handleDiff(args, ctx) {
|
|
|
15938
15919
|
let modifiedFiles = 0;
|
|
15939
15920
|
const diffLines2 = [];
|
|
15940
15921
|
for (const [filePath, { earliest }] of fileMap) {
|
|
15941
|
-
const currentContent =
|
|
15922
|
+
const currentContent = existsSync30(filePath) ? (() => {
|
|
15942
15923
|
try {
|
|
15943
15924
|
return readFileSync22(filePath, "utf-8");
|
|
15944
15925
|
} catch {
|
|
@@ -15977,7 +15958,7 @@ async function handleInit(args, ctx) {
|
|
|
15977
15958
|
const cwd = process.cwd();
|
|
15978
15959
|
const targetPath = join24(cwd, "AICLI.md");
|
|
15979
15960
|
const force = args.includes("--force");
|
|
15980
|
-
if (
|
|
15961
|
+
if (existsSync30(targetPath) && !force) {
|
|
15981
15962
|
ctx.send({ type: "info", message: `AICLI.md already exists at ${targetPath}
|
|
15982
15963
|
Use /init --force to overwrite.` });
|
|
15983
15964
|
return;
|
|
@@ -16161,7 +16142,7 @@ Use /add-dir remove to clear.` : "No directories added.\nUsage: /add-dir <path>
|
|
|
16161
16142
|
return;
|
|
16162
16143
|
}
|
|
16163
16144
|
const dirPath = resolve10(sub);
|
|
16164
|
-
if (!
|
|
16145
|
+
if (!existsSync30(dirPath)) {
|
|
16165
16146
|
ctx.send({ type: "error", message: `Directory not found: ${dirPath}` });
|
|
16166
16147
|
return;
|
|
16167
16148
|
}
|
|
@@ -17236,7 +17217,7 @@ Try: /compact to reduce context, /clear to reset, or switch to a larger-context
|
|
|
17236
17217
|
this.send({ type: "response_done", content, usage });
|
|
17237
17218
|
if (pendingTeeSave && isCleanDocumentBody(content)) {
|
|
17238
17219
|
try {
|
|
17239
|
-
mkdirSync16(
|
|
17220
|
+
mkdirSync16(dirname11(pendingTeeSave), { recursive: true });
|
|
17240
17221
|
const bodyToSave = stripOuterCodeFence(content);
|
|
17241
17222
|
atomicWriteFileSync(pendingTeeSave, bodyToSave);
|
|
17242
17223
|
undoStack.push(pendingTeeSave, `save_last_response (deferred): ${pendingTeeSave}`);
|
|
@@ -17412,7 +17393,7 @@ ${summaryContent}`,
|
|
|
17412
17393
|
let isError = false;
|
|
17413
17394
|
let summary;
|
|
17414
17395
|
try {
|
|
17415
|
-
mkdirSync16(
|
|
17396
|
+
mkdirSync16(dirname11(saveToFile), { recursive: true });
|
|
17416
17397
|
fileStream = createWriteStream3(saveToFile);
|
|
17417
17398
|
const teeSystemPrompt = stripToolCallReminder(systemPrompt ?? "") + CONTENT_ONLY_STREAM_REMINDER;
|
|
17418
17399
|
const teeExtraMessages = extraMessages.length > 0 ? [...extraMessages, { role: "user", content: TEE_FINAL_USER_NUDGE }] : [{ role: "user", content: TEE_FINAL_USER_NUDGE }];
|
|
@@ -17771,7 +17752,7 @@ ${entry.content}`;
|
|
|
17771
17752
|
return;
|
|
17772
17753
|
}
|
|
17773
17754
|
try {
|
|
17774
|
-
const { searchChatMemory: searchChatMemory2, loadChatIndex: loadChatIndex2 } = await import("./chat-index-
|
|
17755
|
+
const { searchChatMemory: searchChatMemory2, loadChatIndex: loadChatIndex2 } = await import("./chat-index-WYXYD7YP.js");
|
|
17775
17756
|
const loaded = loadChatIndex2();
|
|
17776
17757
|
if (!loaded || loaded.idx.chunks.length === 0) {
|
|
17777
17758
|
this.send({ type: "memory_hits", query: q, hits: [], indexMissing: true });
|
|
@@ -17807,7 +17788,7 @@ ${entry.content}`;
|
|
|
17807
17788
|
}
|
|
17808
17789
|
async handleMemoryStatus() {
|
|
17809
17790
|
try {
|
|
17810
|
-
const { getChatIndexStatus } = await import("./chat-index-
|
|
17791
|
+
const { getChatIndexStatus } = await import("./chat-index-WYXYD7YP.js");
|
|
17811
17792
|
const s = getChatIndexStatus();
|
|
17812
17793
|
this.send({
|
|
17813
17794
|
type: "memory_status",
|
|
@@ -17832,7 +17813,7 @@ ${entry.content}`;
|
|
|
17832
17813
|
type: "info",
|
|
17833
17814
|
message: full ? "\u{1F9E0} Rebuilding chat memory index (this may take a while on first run \u2014 ~117 MB embedder)." : "\u{1F9E0} Refreshing chat memory index (incremental)\u2026"
|
|
17834
17815
|
});
|
|
17835
|
-
const { buildChatIndex } = await import("./chat-index-
|
|
17816
|
+
const { buildChatIndex } = await import("./chat-index-WYXYD7YP.js");
|
|
17836
17817
|
const stats = await buildChatIndex({
|
|
17837
17818
|
full,
|
|
17838
17819
|
onProgress: (p) => {
|
|
@@ -17987,8 +17968,8 @@ async function setupProxy(configProxy) {
|
|
|
17987
17968
|
}
|
|
17988
17969
|
|
|
17989
17970
|
// src/web/auth.ts
|
|
17990
|
-
import { existsSync as
|
|
17991
|
-
import { join as join27 } from "path";
|
|
17971
|
+
import { existsSync as existsSync33, readFileSync as readFileSync25, writeFileSync as writeFileSync6, mkdirSync as mkdirSync17, readdirSync as readdirSync16, copyFileSync } from "fs";
|
|
17972
|
+
import { isAbsolute as isAbsolute3, join as join27, relative as relative4 } from "path";
|
|
17992
17973
|
import { createHmac, randomBytes, timingSafeEqual, pbkdf2Sync } from "crypto";
|
|
17993
17974
|
var USERS_FILE = "users.json";
|
|
17994
17975
|
var TOKEN_EXPIRY_HOURS = 24;
|
|
@@ -18186,7 +18167,7 @@ var AuthManager = class {
|
|
|
18186
18167
|
if (err) return err;
|
|
18187
18168
|
const userDir = this.getUserDataDir(username);
|
|
18188
18169
|
const globalConfig = join27(this.baseDir, "config.json");
|
|
18189
|
-
if (
|
|
18170
|
+
if (existsSync33(globalConfig)) {
|
|
18190
18171
|
try {
|
|
18191
18172
|
const content = readFileSync25(globalConfig, "utf-8");
|
|
18192
18173
|
writeFileSync6(join27(userDir, "config.json"), content, "utf-8");
|
|
@@ -18194,7 +18175,7 @@ var AuthManager = class {
|
|
|
18194
18175
|
}
|
|
18195
18176
|
}
|
|
18196
18177
|
const globalMemory = join27(this.baseDir, "memory.md");
|
|
18197
|
-
if (
|
|
18178
|
+
if (existsSync33(globalMemory)) {
|
|
18198
18179
|
try {
|
|
18199
18180
|
const content = readFileSync25(globalMemory, "utf-8");
|
|
18200
18181
|
writeFileSync6(join27(userDir, "memory.md"), content, "utf-8");
|
|
@@ -18202,7 +18183,7 @@ var AuthManager = class {
|
|
|
18202
18183
|
}
|
|
18203
18184
|
}
|
|
18204
18185
|
const globalHistory = join27(this.baseDir, "history");
|
|
18205
|
-
if (
|
|
18186
|
+
if (existsSync33(globalHistory)) {
|
|
18206
18187
|
try {
|
|
18207
18188
|
const files = readdirSync16(globalHistory).filter((f) => f.endsWith(".json"));
|
|
18208
18189
|
const userHistory = join27(userDir, "history");
|
|
@@ -18219,10 +18200,19 @@ var AuthManager = class {
|
|
|
18219
18200
|
}
|
|
18220
18201
|
// ── Private methods ────────────────────────────────────────────
|
|
18221
18202
|
loadOrCreate() {
|
|
18222
|
-
if (
|
|
18203
|
+
if (existsSync33(this.usersFile)) {
|
|
18223
18204
|
try {
|
|
18224
|
-
|
|
18225
|
-
|
|
18205
|
+
const parsed = JSON.parse(readFileSync25(this.usersFile, "utf-8"));
|
|
18206
|
+
if (!this.isValidUsersDB(parsed)) {
|
|
18207
|
+
throw new Error("database structure is invalid");
|
|
18208
|
+
}
|
|
18209
|
+
return parsed;
|
|
18210
|
+
} catch (err) {
|
|
18211
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
18212
|
+
throw new Error(
|
|
18213
|
+
`Authentication database is unreadable or corrupt: ${this.usersFile} (${detail}). Refusing to start without authentication; restore or remove the file explicitly.`,
|
|
18214
|
+
{ cause: err }
|
|
18215
|
+
);
|
|
18226
18216
|
}
|
|
18227
18217
|
}
|
|
18228
18218
|
const db = {
|
|
@@ -18233,6 +18223,27 @@ var AuthManager = class {
|
|
|
18233
18223
|
this.saveDB(db);
|
|
18234
18224
|
return db;
|
|
18235
18225
|
}
|
|
18226
|
+
isValidUsersDB(value) {
|
|
18227
|
+
if (!value || typeof value !== "object") return false;
|
|
18228
|
+
const db = value;
|
|
18229
|
+
if (db.version !== 1 || typeof db.secret !== "string" || db.secret.length < 32 || !Array.isArray(db.users)) {
|
|
18230
|
+
return false;
|
|
18231
|
+
}
|
|
18232
|
+
const usernames = /* @__PURE__ */ new Set();
|
|
18233
|
+
for (const user of db.users) {
|
|
18234
|
+
if (!user || typeof user !== "object") return false;
|
|
18235
|
+
const record = user;
|
|
18236
|
+
if (typeof record.username !== "string" || !/^[a-z0-9_-]{2,32}$/.test(record.username) || usernames.has(record.username) || typeof record.passwordHash !== "string" || record.passwordHash.length === 0 || typeof record.salt !== "string" || record.salt.length === 0 || typeof record.createdAt !== "string" || typeof record.dataDir !== "string" || record.dataDir.length === 0 || isAbsolute3(record.dataDir)) {
|
|
18237
|
+
return false;
|
|
18238
|
+
}
|
|
18239
|
+
const dataDirFromBase = relative4(this.baseDir, join27(this.baseDir, record.dataDir));
|
|
18240
|
+
if (dataDirFromBase.startsWith("..") || isAbsolute3(dataDirFromBase)) return false;
|
|
18241
|
+
if (record.hashVersion !== void 0 && record.hashVersion !== 1 && record.hashVersion !== 2) return false;
|
|
18242
|
+
if (record.tokensRevokedBefore !== void 0 && !Number.isFinite(record.tokensRevokedBefore)) return false;
|
|
18243
|
+
usernames.add(record.username);
|
|
18244
|
+
}
|
|
18245
|
+
return true;
|
|
18246
|
+
}
|
|
18236
18247
|
save() {
|
|
18237
18248
|
this.saveDB(this.db);
|
|
18238
18249
|
}
|
|
@@ -18269,7 +18280,7 @@ function getModuleDir() {
|
|
|
18269
18280
|
if (typeof import.meta?.url === "string") {
|
|
18270
18281
|
const url = new URL(import.meta.url);
|
|
18271
18282
|
const filePath = url.pathname.replace(/^\/([A-Z]:)/i, "$1");
|
|
18272
|
-
return
|
|
18283
|
+
return dirname12(filePath);
|
|
18273
18284
|
}
|
|
18274
18285
|
} catch {
|
|
18275
18286
|
}
|
|
@@ -18342,7 +18353,7 @@ async function startWebServer(options = {}) {
|
|
|
18342
18353
|
}
|
|
18343
18354
|
let skillManager = null;
|
|
18344
18355
|
const skillsDir = join28(config.getConfigDir(), SKILLS_DIR_NAME);
|
|
18345
|
-
if (
|
|
18356
|
+
if (existsSync34(skillsDir)) {
|
|
18346
18357
|
skillManager = new SkillManager(skillsDir, config.get("ui").skillSizeWarn);
|
|
18347
18358
|
skillManager.loadSkills(pluginAssets.skillDirs);
|
|
18348
18359
|
const count = skillManager.listSkills().length;
|
|
@@ -18414,13 +18425,13 @@ async function startWebServer(options = {}) {
|
|
|
18414
18425
|
};
|
|
18415
18426
|
const moduleDir = getModuleDir();
|
|
18416
18427
|
let clientDir = join28(moduleDir, "web", "client");
|
|
18417
|
-
if (!
|
|
18428
|
+
if (!existsSync34(clientDir)) {
|
|
18418
18429
|
clientDir = join28(moduleDir, "client");
|
|
18419
18430
|
}
|
|
18420
|
-
if (!
|
|
18431
|
+
if (!existsSync34(clientDir)) {
|
|
18421
18432
|
clientDir = join28(moduleDir, "..", "..", "src", "web", "client");
|
|
18422
18433
|
}
|
|
18423
|
-
if (!
|
|
18434
|
+
if (!existsSync34(clientDir)) {
|
|
18424
18435
|
clientDir = join28(process.cwd(), "src", "web", "client");
|
|
18425
18436
|
}
|
|
18426
18437
|
console.log(` Static files: ${clientDir}`);
|
|
@@ -18493,8 +18504,8 @@ async function startWebServer(options = {}) {
|
|
|
18493
18504
|
const prefix = req.query.prefix || "";
|
|
18494
18505
|
const targetDir = join28(cwd, prefix);
|
|
18495
18506
|
try {
|
|
18496
|
-
const canonicalTarget =
|
|
18497
|
-
const canonicalCwd =
|
|
18507
|
+
const canonicalTarget = realpathSync2(resolve13(targetDir));
|
|
18508
|
+
const canonicalCwd = realpathSync2(resolve13(cwd));
|
|
18498
18509
|
if (!canonicalTarget.startsWith(canonicalCwd + sep3) && canonicalTarget !== canonicalCwd) {
|
|
18499
18510
|
res.json({ files: [] });
|
|
18500
18511
|
return;
|
|
@@ -18508,7 +18519,7 @@ async function startWebServer(options = {}) {
|
|
|
18508
18519
|
const entries = readdirSync17(targetDir, { withFileTypes: true });
|
|
18509
18520
|
const files = entries.filter((e) => !SKIP.has(e.name) && !e.name.startsWith(".")).slice(0, 50).map((e) => ({
|
|
18510
18521
|
name: e.name,
|
|
18511
|
-
path:
|
|
18522
|
+
path: relative5(cwd, join28(targetDir, e.name)).replace(/\\/g, "/"),
|
|
18512
18523
|
isDir: e.isDirectory()
|
|
18513
18524
|
}));
|
|
18514
18525
|
res.json({ files });
|
|
@@ -18549,13 +18560,13 @@ async function startWebServer(options = {}) {
|
|
|
18549
18560
|
const authUser = req._authUser;
|
|
18550
18561
|
const histDir = authUser ? getUserShared(authUser).config.getHistoryDir() : config.getHistoryDir();
|
|
18551
18562
|
const filePath = join28(histDir, `${id}.json`);
|
|
18552
|
-
if (!
|
|
18563
|
+
if (!existsSync34(filePath)) {
|
|
18553
18564
|
res.status(404).json({ error: "Session not found" });
|
|
18554
18565
|
return;
|
|
18555
18566
|
}
|
|
18556
18567
|
try {
|
|
18557
|
-
const canonicalFile =
|
|
18558
|
-
const canonicalDir =
|
|
18568
|
+
const canonicalFile = realpathSync2(filePath);
|
|
18569
|
+
const canonicalDir = realpathSync2(histDir);
|
|
18559
18570
|
if (!canonicalFile.startsWith(canonicalDir + sep3)) {
|
|
18560
18571
|
res.status(404).json({ error: "Session not found" });
|
|
18561
18572
|
return;
|
|
@@ -18579,8 +18590,8 @@ async function startWebServer(options = {}) {
|
|
|
18579
18590
|
const cwd = process.cwd();
|
|
18580
18591
|
const fullPath = resolve13(join28(cwd, filePath));
|
|
18581
18592
|
try {
|
|
18582
|
-
const canonicalFull =
|
|
18583
|
-
const canonicalCwd =
|
|
18593
|
+
const canonicalFull = realpathSync2(fullPath);
|
|
18594
|
+
const canonicalCwd = realpathSync2(resolve13(cwd));
|
|
18584
18595
|
if (!canonicalFull.startsWith(canonicalCwd + sep3) && canonicalFull !== canonicalCwd) {
|
|
18585
18596
|
res.json({ error: "Access denied" });
|
|
18586
18597
|
return;
|
|
@@ -18851,14 +18862,14 @@ function resolveProjectMcpPath() {
|
|
|
18851
18862
|
const gitRoot = getGitRoot(cwd);
|
|
18852
18863
|
const projectRoot = gitRoot ?? cwd;
|
|
18853
18864
|
const configPath = join28(projectRoot, MCP_PROJECT_CONFIG_NAME);
|
|
18854
|
-
return
|
|
18865
|
+
return existsSync34(configPath) ? configPath : null;
|
|
18855
18866
|
}
|
|
18856
18867
|
function loadProjectMcpConfig() {
|
|
18857
18868
|
const cwd = process.cwd();
|
|
18858
18869
|
const gitRoot = getGitRoot(cwd);
|
|
18859
18870
|
const projectRoot = gitRoot ?? cwd;
|
|
18860
18871
|
const configPath = join28(projectRoot, MCP_PROJECT_CONFIG_NAME);
|
|
18861
|
-
if (!
|
|
18872
|
+
if (!existsSync34(configPath)) return null;
|
|
18862
18873
|
try {
|
|
18863
18874
|
const raw = JSON.parse(readFileSync26(configPath, "utf-8"));
|
|
18864
18875
|
return raw.mcpServers ?? raw;
|