jinzd-ai-cli 0.4.84 → 0.4.86
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/{batch-QAIJGCG3.js → batch-KN3ESR3U.js} +15 -4
- package/dist/{chunk-E7G3QCMU.js → chunk-CHJFRDUB.js} +1 -1
- package/dist/{chunk-3BLU2654.js → chunk-CKCGAHEF.js} +1 -1
- package/dist/{chunk-UJPR6N6O.js → chunk-M7KYMJT5.js} +10 -10
- package/dist/{chunk-YY7EPHN4.js → chunk-P6XKBTP4.js} +2 -2
- package/dist/{chunk-V6ZENAVD.js → chunk-YRWBFUYH.js} +1 -1
- package/dist/{chunk-6WKHRV3J.js → chunk-ZC4DN6C7.js} +1 -1
- package/dist/electron-server.js +77 -18
- package/dist/{hub-MP66YGSM.js → hub-6GS4DYZO.js} +1 -1
- package/dist/index.js +12 -11
- package/dist/{run-tests-N4PJMRDQ.js → run-tests-BQMCZVI4.js} +2 -2
- package/dist/{run-tests-JFREA67C.js → run-tests-NWQ3YPDN.js} +1 -1
- package/dist/{server-EGK4ASC7.js → server-73EENVYE.js} +115 -19
- package/dist/{server-6QLOHAJJ.js → server-GD5OGIOF.js} +68 -9
- package/dist/{task-orchestrator-24TW64OU.js → task-orchestrator-YW33QQKO.js} +3 -3
- package/package.json +1 -1
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
ConfigManager
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-YRWBFUYH.js";
|
|
5
5
|
import "./chunk-2ZD3YTVM.js";
|
|
6
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-CKCGAHEF.js";
|
|
7
7
|
|
|
8
8
|
// src/cli/batch.ts
|
|
9
9
|
import Anthropic from "@anthropic-ai/sdk";
|
|
10
|
-
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
10
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, renameSync, unlinkSync } from "fs";
|
|
11
11
|
import { join } from "path";
|
|
12
12
|
function parseBatchInput(jsonl) {
|
|
13
13
|
const lines = jsonl.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
@@ -72,7 +72,18 @@ function loadTrackedBatches(config) {
|
|
|
72
72
|
}
|
|
73
73
|
function saveTrackedBatches(config, batches) {
|
|
74
74
|
mkdirSync(config.getConfigDir(), { recursive: true });
|
|
75
|
-
|
|
75
|
+
const finalPath = getBatchesPath(config);
|
|
76
|
+
const tmpPath = `${finalPath}.tmp.${process.pid}.${Date.now()}`;
|
|
77
|
+
try {
|
|
78
|
+
writeFileSync(tmpPath, JSON.stringify({ batches }, null, 2), "utf-8");
|
|
79
|
+
renameSync(tmpPath, finalPath);
|
|
80
|
+
} catch (err) {
|
|
81
|
+
try {
|
|
82
|
+
unlinkSync(tmpPath);
|
|
83
|
+
} catch {
|
|
84
|
+
}
|
|
85
|
+
throw err;
|
|
86
|
+
}
|
|
76
87
|
}
|
|
77
88
|
function getClaudeClient(config) {
|
|
78
89
|
const apiKey = config.getApiKey("claude");
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
} from "./chunk-6VRJGH25.js";
|
|
20
20
|
import {
|
|
21
21
|
runTestsTool
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-ZC4DN6C7.js";
|
|
23
23
|
import {
|
|
24
24
|
CONFIG_DIR_NAME,
|
|
25
25
|
DEFAULT_MAX_TOOL_OUTPUT_CHARS_CAP,
|
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
SUBAGENT_ALLOWED_TOOLS,
|
|
28
28
|
SUBAGENT_DEFAULT_MAX_ROUNDS,
|
|
29
29
|
SUBAGENT_MAX_ROUNDS_LIMIT
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-CKCGAHEF.js";
|
|
31
31
|
|
|
32
32
|
// src/tools/types.ts
|
|
33
33
|
function isFileWriteTool(name) {
|
|
@@ -432,7 +432,7 @@ function updateCwdFromCommand(command, baseCwd) {
|
|
|
432
432
|
|
|
433
433
|
// src/tools/builtin/read-file.ts
|
|
434
434
|
import { readFileSync as readFileSync2, existsSync as existsSync3, statSync as statSync2, readdirSync as readdirSync2 } from "fs";
|
|
435
|
-
import {
|
|
435
|
+
import { execFileSync } from "child_process";
|
|
436
436
|
import { extname, resolve as resolve2, basename, sep, dirname } from "path";
|
|
437
437
|
import { homedir } from "os";
|
|
438
438
|
|
|
@@ -633,7 +633,7 @@ function findSimilarFiles(filePath) {
|
|
|
633
633
|
}
|
|
634
634
|
function tryExtractPdfText(absPath) {
|
|
635
635
|
try {
|
|
636
|
-
const output =
|
|
636
|
+
const output = execFileSync("pdftotext", [absPath, "-"], {
|
|
637
637
|
timeout: 15e3,
|
|
638
638
|
encoding: "utf-8",
|
|
639
639
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -642,8 +642,8 @@ function tryExtractPdfText(absPath) {
|
|
|
642
642
|
} catch {
|
|
643
643
|
}
|
|
644
644
|
try {
|
|
645
|
-
const pyScript =
|
|
646
|
-
const output =
|
|
645
|
+
const pyScript = 'import sys; exec("try:\\n from pdfminer.high_level import extract_text\\n print(extract_text(sys.argv[1]))\\nexcept: pass")';
|
|
646
|
+
const output = execFileSync("python", ["-c", pyScript, absPath], {
|
|
647
647
|
timeout: 15e3,
|
|
648
648
|
encoding: "utf-8",
|
|
649
649
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -1008,7 +1008,7 @@ function simpleDiff(oldLines, newLines) {
|
|
|
1008
1008
|
}
|
|
1009
1009
|
|
|
1010
1010
|
// src/tools/hooks.ts
|
|
1011
|
-
import { execSync as
|
|
1011
|
+
import { execSync as execSync2 } from "child_process";
|
|
1012
1012
|
function shellEscape(value) {
|
|
1013
1013
|
return "'" + value.replace(/'/g, "'\\''") + "'";
|
|
1014
1014
|
}
|
|
@@ -1020,7 +1020,7 @@ function runHook(template, vars) {
|
|
|
1020
1020
|
cmd = cmd.replace(/\{args\}/g, shellEscape(vars.args ?? ""));
|
|
1021
1021
|
cmd = cmd.replace(/\{status\}/g, shellEscape(vars.status ?? ""));
|
|
1022
1022
|
try {
|
|
1023
|
-
|
|
1023
|
+
execSync2(cmd, {
|
|
1024
1024
|
timeout: 5e3,
|
|
1025
1025
|
stdio: ["pipe", "pipe", "pipe"],
|
|
1026
1026
|
encoding: "utf-8"
|
|
@@ -3922,7 +3922,7 @@ var taskStopTool = {
|
|
|
3922
3922
|
};
|
|
3923
3923
|
|
|
3924
3924
|
// src/tools/builtin/git-tools.ts
|
|
3925
|
-
import {
|
|
3925
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
3926
3926
|
import { existsSync as existsSync10 } from "fs";
|
|
3927
3927
|
import { join as join5 } from "path";
|
|
3928
3928
|
function assertGitRepo(cwd) {
|
|
@@ -3938,7 +3938,7 @@ function assertGitRepo(cwd) {
|
|
|
3938
3938
|
}
|
|
3939
3939
|
function runGit(args, cwd, maxBuffer = 10 * 1024 * 1024) {
|
|
3940
3940
|
try {
|
|
3941
|
-
const output =
|
|
3941
|
+
const output = execFileSync2("git", args, {
|
|
3942
3942
|
cwd,
|
|
3943
3943
|
encoding: "utf-8",
|
|
3944
3944
|
maxBuffer,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
schemaToJsonSchema,
|
|
4
4
|
truncateForPersist
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-M7KYMJT5.js";
|
|
6
6
|
import {
|
|
7
7
|
AuthError,
|
|
8
8
|
ProviderError,
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
MCP_PROTOCOL_VERSION,
|
|
19
19
|
MCP_TOOL_PREFIX,
|
|
20
20
|
VERSION
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-CKCGAHEF.js";
|
|
22
22
|
|
|
23
23
|
// src/providers/claude.ts
|
|
24
24
|
import Anthropic from "@anthropic-ai/sdk";
|
package/dist/electron-server.js
CHANGED
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
VERSION,
|
|
37
37
|
buildUserIdentityPrompt,
|
|
38
38
|
runTestsTool
|
|
39
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-CHJFRDUB.js";
|
|
40
40
|
import {
|
|
41
41
|
hasSemanticIndex,
|
|
42
42
|
semanticSearch
|
|
@@ -3581,7 +3581,7 @@ function updateCwdFromCommand(command, baseCwd) {
|
|
|
3581
3581
|
|
|
3582
3582
|
// src/tools/builtin/read-file.ts
|
|
3583
3583
|
import { readFileSync as readFileSync4, existsSync as existsSync5, statSync as statSync2, readdirSync as readdirSync3 } from "fs";
|
|
3584
|
-
import {
|
|
3584
|
+
import { execFileSync } from "child_process";
|
|
3585
3585
|
import { extname, resolve as resolve2, basename, sep, dirname } from "path";
|
|
3586
3586
|
import { homedir as homedir2 } from "os";
|
|
3587
3587
|
|
|
@@ -3782,7 +3782,7 @@ function findSimilarFiles(filePath) {
|
|
|
3782
3782
|
}
|
|
3783
3783
|
function tryExtractPdfText(absPath) {
|
|
3784
3784
|
try {
|
|
3785
|
-
const output =
|
|
3785
|
+
const output = execFileSync("pdftotext", [absPath, "-"], {
|
|
3786
3786
|
timeout: 15e3,
|
|
3787
3787
|
encoding: "utf-8",
|
|
3788
3788
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -3791,8 +3791,8 @@ function tryExtractPdfText(absPath) {
|
|
|
3791
3791
|
} catch {
|
|
3792
3792
|
}
|
|
3793
3793
|
try {
|
|
3794
|
-
const pyScript =
|
|
3795
|
-
const output =
|
|
3794
|
+
const pyScript = 'import sys; exec("try:\\n from pdfminer.high_level import extract_text\\n print(extract_text(sys.argv[1]))\\nexcept: pass")';
|
|
3795
|
+
const output = execFileSync("python", ["-c", pyScript, absPath], {
|
|
3796
3796
|
timeout: 15e3,
|
|
3797
3797
|
encoding: "utf-8",
|
|
3798
3798
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -4157,7 +4157,7 @@ function simpleDiff(oldLines, newLines) {
|
|
|
4157
4157
|
}
|
|
4158
4158
|
|
|
4159
4159
|
// src/tools/hooks.ts
|
|
4160
|
-
import { execSync as
|
|
4160
|
+
import { execSync as execSync2 } from "child_process";
|
|
4161
4161
|
function shellEscape(value) {
|
|
4162
4162
|
return "'" + value.replace(/'/g, "'\\''") + "'";
|
|
4163
4163
|
}
|
|
@@ -4169,7 +4169,7 @@ function runHook(template, vars) {
|
|
|
4169
4169
|
cmd = cmd.replace(/\{args\}/g, shellEscape(vars.args ?? ""));
|
|
4170
4170
|
cmd = cmd.replace(/\{status\}/g, shellEscape(vars.status ?? ""));
|
|
4171
4171
|
try {
|
|
4172
|
-
|
|
4172
|
+
execSync2(cmd, {
|
|
4173
4173
|
timeout: 5e3,
|
|
4174
4174
|
stdio: ["pipe", "pipe", "pipe"],
|
|
4175
4175
|
encoding: "utf-8"
|
|
@@ -7032,7 +7032,7 @@ var taskStopTool = {
|
|
|
7032
7032
|
};
|
|
7033
7033
|
|
|
7034
7034
|
// src/tools/builtin/git-tools.ts
|
|
7035
|
-
import {
|
|
7035
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
7036
7036
|
import { existsSync as existsSync12 } from "fs";
|
|
7037
7037
|
import { join as join7 } from "path";
|
|
7038
7038
|
function assertGitRepo(cwd) {
|
|
@@ -7048,7 +7048,7 @@ function assertGitRepo(cwd) {
|
|
|
7048
7048
|
}
|
|
7049
7049
|
function runGit(args, cwd, maxBuffer = 10 * 1024 * 1024) {
|
|
7050
7050
|
try {
|
|
7051
|
-
const output =
|
|
7051
|
+
const output = execFileSync2("git", args, {
|
|
7052
7052
|
cwd,
|
|
7053
7053
|
encoding: "utf-8",
|
|
7054
7054
|
maxBuffer,
|
|
@@ -9221,15 +9221,15 @@ function autoTrimSessionIfNeeded(session, sizeLimit = SESSION_SIZE_LIMIT) {
|
|
|
9221
9221
|
// src/web/session-handler.ts
|
|
9222
9222
|
import { existsSync as existsSync20, readFileSync as readFileSync13, appendFileSync as appendFileSync3, writeFileSync as writeFileSync8, mkdirSync as mkdirSync9, readdirSync as readdirSync9, statSync as statSync8 } from "fs";
|
|
9223
9223
|
import { join as join13, resolve as resolve4 } from "path";
|
|
9224
|
-
import { execSync as
|
|
9224
|
+
import { execSync as execSync4 } from "child_process";
|
|
9225
9225
|
|
|
9226
9226
|
// src/tools/git-context.ts
|
|
9227
|
-
import { execSync as
|
|
9227
|
+
import { execSync as execSync3 } from "child_process";
|
|
9228
9228
|
import { existsSync as existsSync19 } from "fs";
|
|
9229
9229
|
import { join as join12 } from "path";
|
|
9230
9230
|
function runGit2(cmd, cwd) {
|
|
9231
9231
|
try {
|
|
9232
|
-
return
|
|
9232
|
+
return execSync3(`git ${cmd}`, {
|
|
9233
9233
|
cwd,
|
|
9234
9234
|
encoding: "utf-8",
|
|
9235
9235
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -10953,7 +10953,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
10953
10953
|
let diff;
|
|
10954
10954
|
try {
|
|
10955
10955
|
const cmd = staged ? "git diff --staged" : "git diff";
|
|
10956
|
-
diff =
|
|
10956
|
+
diff = execSync4(cmd, { encoding: "utf-8", timeout: 1e4 }).trim();
|
|
10957
10957
|
} catch {
|
|
10958
10958
|
this.send({ type: "error", message: "Failed to run git diff." });
|
|
10959
10959
|
break;
|
|
@@ -10992,7 +10992,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
10992
10992
|
let secDiff;
|
|
10993
10993
|
try {
|
|
10994
10994
|
const cmd = secStaged ? "git diff --staged" : "git diff";
|
|
10995
|
-
secDiff =
|
|
10995
|
+
secDiff = execSync4(cmd, { encoding: "utf-8", timeout: 1e4 }).trim();
|
|
10996
10996
|
} catch {
|
|
10997
10997
|
this.send({ type: "error", message: "Failed to run git diff." });
|
|
10998
10998
|
break;
|
|
@@ -11070,7 +11070,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
11070
11070
|
case "test": {
|
|
11071
11071
|
this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
|
|
11072
11072
|
try {
|
|
11073
|
-
const { executeTests } = await import("./run-tests-
|
|
11073
|
+
const { executeTests } = await import("./run-tests-NWQ3YPDN.js");
|
|
11074
11074
|
const argStr = args.join(" ").trim();
|
|
11075
11075
|
let testArgs = {};
|
|
11076
11076
|
if (argStr) {
|
|
@@ -12238,7 +12238,11 @@ async function startWebServer(options = {}) {
|
|
|
12238
12238
|
};
|
|
12239
12239
|
const app = express();
|
|
12240
12240
|
const server = createServer(app);
|
|
12241
|
-
const
|
|
12241
|
+
const WS_MAX_PAYLOAD = 1 * 1024 * 1024;
|
|
12242
|
+
const WS_MSG_RATE_PER_SEC = 30;
|
|
12243
|
+
const WS_MSG_BURST = 60;
|
|
12244
|
+
const WS_PREAUTH_TIMEOUT_MS = 3e4;
|
|
12245
|
+
const wss = new WebSocketServer({ server, maxPayload: WS_MAX_PAYLOAD });
|
|
12242
12246
|
const authManager = new AuthManager(config.getConfigDir());
|
|
12243
12247
|
if (authManager.isEnabled()) {
|
|
12244
12248
|
console.log(` Auth: ${authManager.listUsers().length} user(s) registered`);
|
|
@@ -12337,9 +12341,11 @@ async function startWebServer(options = {}) {
|
|
|
12337
12341
|
res.json({ files: [] });
|
|
12338
12342
|
}
|
|
12339
12343
|
});
|
|
12340
|
-
app.get("/api/sessions", requireAuth, (
|
|
12344
|
+
app.get("/api/sessions", requireAuth, (req, res) => {
|
|
12341
12345
|
try {
|
|
12342
|
-
const
|
|
12346
|
+
const authUser = req._authUser;
|
|
12347
|
+
const sm = authUser ? getUserShared(authUser).sessions : sessions;
|
|
12348
|
+
const list = sm.listSessions();
|
|
12343
12349
|
res.json({
|
|
12344
12350
|
sessions: list.slice(0, 50).map((s) => ({
|
|
12345
12351
|
id: s.id,
|
|
@@ -12368,6 +12374,17 @@ async function startWebServer(options = {}) {
|
|
|
12368
12374
|
res.status(404).json({ error: "Session not found" });
|
|
12369
12375
|
return;
|
|
12370
12376
|
}
|
|
12377
|
+
try {
|
|
12378
|
+
const canonicalFile = realpathSync(filePath);
|
|
12379
|
+
const canonicalDir = realpathSync(histDir);
|
|
12380
|
+
if (!canonicalFile.startsWith(canonicalDir + sep2)) {
|
|
12381
|
+
res.status(404).json({ error: "Session not found" });
|
|
12382
|
+
return;
|
|
12383
|
+
}
|
|
12384
|
+
} catch {
|
|
12385
|
+
res.status(404).json({ error: "Session not found" });
|
|
12386
|
+
return;
|
|
12387
|
+
}
|
|
12371
12388
|
const data = JSON.parse(readFileSync15(filePath, "utf-8"));
|
|
12372
12389
|
res.json({ session: data });
|
|
12373
12390
|
} catch (err) {
|
|
@@ -12444,8 +12461,35 @@ async function startWebServer(options = {}) {
|
|
|
12444
12461
|
existing.onDisconnect();
|
|
12445
12462
|
handlers.delete(tabId);
|
|
12446
12463
|
}
|
|
12464
|
+
let tokens = WS_MSG_BURST;
|
|
12465
|
+
let lastRefill = Date.now();
|
|
12466
|
+
const takeToken = () => {
|
|
12467
|
+
const now = Date.now();
|
|
12468
|
+
const refill = (now - lastRefill) / 1e3 * WS_MSG_RATE_PER_SEC;
|
|
12469
|
+
if (refill > 0) {
|
|
12470
|
+
tokens = Math.min(WS_MSG_BURST, tokens + refill);
|
|
12471
|
+
lastRefill = now;
|
|
12472
|
+
}
|
|
12473
|
+
if (tokens < 1) return false;
|
|
12474
|
+
tokens -= 1;
|
|
12475
|
+
return true;
|
|
12476
|
+
};
|
|
12447
12477
|
let authenticatedUser = null;
|
|
12448
12478
|
let handler = null;
|
|
12479
|
+
let preAuthTimer = setTimeout(() => {
|
|
12480
|
+
if (!handler && ws.readyState === ws.OPEN) {
|
|
12481
|
+
try {
|
|
12482
|
+
ws.close(1008, "auth timeout");
|
|
12483
|
+
} catch {
|
|
12484
|
+
}
|
|
12485
|
+
}
|
|
12486
|
+
}, WS_PREAUTH_TIMEOUT_MS);
|
|
12487
|
+
const clearPreAuthTimer = () => {
|
|
12488
|
+
if (preAuthTimer) {
|
|
12489
|
+
clearTimeout(preAuthTimer);
|
|
12490
|
+
preAuthTimer = null;
|
|
12491
|
+
}
|
|
12492
|
+
};
|
|
12449
12493
|
if (token) {
|
|
12450
12494
|
authenticatedUser = authManager.verifyToken(token);
|
|
12451
12495
|
}
|
|
@@ -12454,11 +12498,13 @@ async function startWebServer(options = {}) {
|
|
|
12454
12498
|
console.log(` \u2713 Tab connected: ${tabId.slice(0, 12)} (no auth) (total: ${handlers.size + 1})`);
|
|
12455
12499
|
handler = new SessionHandler(ws, shared);
|
|
12456
12500
|
handlers.set(tabId, handler);
|
|
12501
|
+
clearPreAuthTimer();
|
|
12457
12502
|
} else if (authenticatedUser) {
|
|
12458
12503
|
console.log(` \u2713 Tab connected: ${tabId.slice(0, 12)} (user: ${authenticatedUser}) (total: ${handlers.size + 1})`);
|
|
12459
12504
|
const userShared = getUserShared(authenticatedUser);
|
|
12460
12505
|
handler = new SessionHandler(ws, userShared);
|
|
12461
12506
|
handlers.set(tabId, handler);
|
|
12507
|
+
clearPreAuthTimer();
|
|
12462
12508
|
} else {
|
|
12463
12509
|
console.log(` \u23F3 Tab waiting auth: ${tabId.slice(0, 12)}`);
|
|
12464
12510
|
if (ws.readyState === ws.OPEN) {
|
|
@@ -12469,6 +12515,15 @@ async function startWebServer(options = {}) {
|
|
|
12469
12515
|
}
|
|
12470
12516
|
}
|
|
12471
12517
|
ws.on("message", async (data) => {
|
|
12518
|
+
if (!takeToken()) {
|
|
12519
|
+
if (ws.readyState === ws.OPEN) {
|
|
12520
|
+
try {
|
|
12521
|
+
ws.close(1008, "rate limit exceeded");
|
|
12522
|
+
} catch {
|
|
12523
|
+
}
|
|
12524
|
+
}
|
|
12525
|
+
return;
|
|
12526
|
+
}
|
|
12472
12527
|
try {
|
|
12473
12528
|
const raw = data.toString();
|
|
12474
12529
|
const parsed = JSON.parse(raw);
|
|
@@ -12491,6 +12546,7 @@ async function startWebServer(options = {}) {
|
|
|
12491
12546
|
const userShared = getUserShared(username);
|
|
12492
12547
|
handler = new SessionHandler(ws, userShared);
|
|
12493
12548
|
handlers.set(tabId, handler);
|
|
12549
|
+
clearPreAuthTimer();
|
|
12494
12550
|
console.log(` \u2713 User registered & connected: ${username} (tab: ${tabId.slice(0, 12)})`);
|
|
12495
12551
|
ws.send(JSON.stringify({ type: "auth_result", success: true, token: newToken, username, setCookie: true }));
|
|
12496
12552
|
return;
|
|
@@ -12506,6 +12562,7 @@ async function startWebServer(options = {}) {
|
|
|
12506
12562
|
const userShared = getUserShared(verifiedUser);
|
|
12507
12563
|
handler = new SessionHandler(ws, userShared);
|
|
12508
12564
|
handlers.set(tabId, handler);
|
|
12565
|
+
clearPreAuthTimer();
|
|
12509
12566
|
console.log(` \u2713 Token auth: ${verifiedUser} (tab: ${tabId.slice(0, 12)})`);
|
|
12510
12567
|
ws.send(JSON.stringify({ type: "auth_result", success: true, token: clientToken, username: verifiedUser }));
|
|
12511
12568
|
return;
|
|
@@ -12520,6 +12577,7 @@ async function startWebServer(options = {}) {
|
|
|
12520
12577
|
const userShared = getUserShared(username);
|
|
12521
12578
|
handler = new SessionHandler(ws, userShared);
|
|
12522
12579
|
handlers.set(tabId, handler);
|
|
12580
|
+
clearPreAuthTimer();
|
|
12523
12581
|
console.log(` \u2713 User logged in: ${username} (tab: ${tabId.slice(0, 12)})`);
|
|
12524
12582
|
ws.send(JSON.stringify({ type: "auth_result", success: true, token: loginToken, username, setCookie: true }));
|
|
12525
12583
|
return;
|
|
@@ -12546,6 +12604,7 @@ async function startWebServer(options = {}) {
|
|
|
12546
12604
|
}
|
|
12547
12605
|
});
|
|
12548
12606
|
ws.on("close", () => {
|
|
12607
|
+
clearPreAuthTimer();
|
|
12549
12608
|
console.log(` \u2717 Tab disconnected: ${tabId.slice(0, 12)} (total: ${handlers.size - 1})`);
|
|
12550
12609
|
if (handler) handler.onDisconnect();
|
|
12551
12610
|
handlers.delete(tabId);
|
|
@@ -385,7 +385,7 @@ ${content}`);
|
|
|
385
385
|
}
|
|
386
386
|
}
|
|
387
387
|
async function runTaskMode(config, providers, configManager, topic) {
|
|
388
|
-
const { TaskOrchestrator } = await import("./task-orchestrator-
|
|
388
|
+
const { TaskOrchestrator } = await import("./task-orchestrator-YW33QQKO.js");
|
|
389
389
|
const orchestrator = new TaskOrchestrator(config, providers, configManager);
|
|
390
390
|
let interrupted = false;
|
|
391
391
|
const onSigint = () => {
|
package/dist/index.js
CHANGED
|
@@ -30,10 +30,10 @@ import {
|
|
|
30
30
|
saveDevState,
|
|
31
31
|
sessionHasMeaningfulContent,
|
|
32
32
|
setupProxy
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-P6XKBTP4.js";
|
|
34
34
|
import {
|
|
35
35
|
ConfigManager
|
|
36
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-YRWBFUYH.js";
|
|
37
37
|
import {
|
|
38
38
|
ToolExecutor,
|
|
39
39
|
ToolRegistry,
|
|
@@ -49,7 +49,7 @@ import {
|
|
|
49
49
|
spawnAgentContext,
|
|
50
50
|
theme,
|
|
51
51
|
undoStack
|
|
52
|
-
} from "./chunk-
|
|
52
|
+
} from "./chunk-M7KYMJT5.js";
|
|
53
53
|
import "./chunk-2ZD3YTVM.js";
|
|
54
54
|
import {
|
|
55
55
|
fileCheckpoints
|
|
@@ -58,7 +58,7 @@ import "./chunk-NHNWUBXB.js";
|
|
|
58
58
|
import "./chunk-CQQQFNND.js";
|
|
59
59
|
import "./chunk-6VRJGH25.js";
|
|
60
60
|
import "./chunk-PFYAAX2S.js";
|
|
61
|
-
import "./chunk-
|
|
61
|
+
import "./chunk-ZC4DN6C7.js";
|
|
62
62
|
import {
|
|
63
63
|
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
64
64
|
AUTHOR,
|
|
@@ -80,7 +80,7 @@ import {
|
|
|
80
80
|
SKILLS_DIR_NAME,
|
|
81
81
|
VERSION,
|
|
82
82
|
buildUserIdentityPrompt
|
|
83
|
-
} from "./chunk-
|
|
83
|
+
} from "./chunk-CKCGAHEF.js";
|
|
84
84
|
|
|
85
85
|
// src/index.ts
|
|
86
86
|
import { program } from "commander";
|
|
@@ -2592,7 +2592,7 @@ ${hint}` : "")
|
|
|
2592
2592
|
usage: "/test [command|filter]",
|
|
2593
2593
|
async execute(args, ctx) {
|
|
2594
2594
|
try {
|
|
2595
|
-
const { executeTests } = await import("./run-tests-
|
|
2595
|
+
const { executeTests } = await import("./run-tests-BQMCZVI4.js");
|
|
2596
2596
|
const argStr = args.join(" ").trim();
|
|
2597
2597
|
let testArgs = {};
|
|
2598
2598
|
if (argStr) {
|
|
@@ -6485,7 +6485,7 @@ program.command("web").description("Start Web UI server with browser-based chat
|
|
|
6485
6485
|
console.error("Error: Invalid port number. Must be between 1 and 65535.");
|
|
6486
6486
|
process.exit(1);
|
|
6487
6487
|
}
|
|
6488
|
-
const { startWebServer } = await import("./server-
|
|
6488
|
+
const { startWebServer } = await import("./server-GD5OGIOF.js");
|
|
6489
6489
|
await startWebServer({ port, host: options.host });
|
|
6490
6490
|
});
|
|
6491
6491
|
program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | migrate <name>)").action(async (action, username) => {
|
|
@@ -6608,7 +6608,7 @@ program.command("sessions").description("List recent conversation sessions").act
|
|
|
6608
6608
|
});
|
|
6609
6609
|
program.command("batch <action> [arg] [arg2]").description("Anthropic Message Batches: submit | list | status <id> | results <id> [out] | cancel <id>").option("--dry-run", "Parse and validate input without submitting (submit only)").action(async (action, arg, arg2, options) => {
|
|
6610
6610
|
try {
|
|
6611
|
-
const batch = await import("./batch-
|
|
6611
|
+
const batch = await import("./batch-KN3ESR3U.js");
|
|
6612
6612
|
switch (action) {
|
|
6613
6613
|
case "submit":
|
|
6614
6614
|
if (!arg) {
|
|
@@ -6650,10 +6650,11 @@ program.command("batch <action> [arg] [arg2]").description("Anthropic Message Ba
|
|
|
6650
6650
|
process.exit(1);
|
|
6651
6651
|
}
|
|
6652
6652
|
});
|
|
6653
|
-
program.command("mcp-serve").description("Start an MCP server over STDIO, exposing aicli's built-in tools to Claude Desktop / Cursor / other MCP clients").option("--allow-destructive", "Allow
|
|
6654
|
-
const { startMcpServer } = await import("./server-
|
|
6653
|
+
program.command("mcp-serve").description("Start an MCP server over STDIO, exposing aicli's built-in tools to Claude Desktop / Cursor / other MCP clients").option("--allow-destructive", "Allow bash / run_interactive / task_create (always destructive in MCP mode)").option("--allow-outside-cwd", "Allow tool path arguments to escape the sandbox root \u2014 disabled by default").option("--tools <list>", "Comma-separated whitelist of tools to expose (default: all eligible tools)").option("--cwd <path>", "Working directory AND sandbox root (default: current directory)").action(async (options) => {
|
|
6654
|
+
const { startMcpServer } = await import("./server-73EENVYE.js");
|
|
6655
6655
|
await startMcpServer({
|
|
6656
6656
|
allowDestructive: !!options.allowDestructive,
|
|
6657
|
+
allowOutsideCwd: !!options.allowOutsideCwd,
|
|
6657
6658
|
tools: options.tools ? options.tools.split(",").map((s) => s.trim()).filter(Boolean) : void 0,
|
|
6658
6659
|
cwd: options.cwd
|
|
6659
6660
|
});
|
|
@@ -6777,7 +6778,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
|
|
|
6777
6778
|
}),
|
|
6778
6779
|
config.get("customProviders")
|
|
6779
6780
|
);
|
|
6780
|
-
const { startHub } = await import("./hub-
|
|
6781
|
+
const { startHub } = await import("./hub-6GS4DYZO.js");
|
|
6781
6782
|
await startHub(
|
|
6782
6783
|
{
|
|
6783
6784
|
topic: topic ?? "",
|
|
@@ -3,28 +3,78 @@ import {
|
|
|
3
3
|
ToolRegistry,
|
|
4
4
|
getDangerLevel,
|
|
5
5
|
schemaToJsonSchema
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-M7KYMJT5.js";
|
|
7
7
|
import "./chunk-2ZD3YTVM.js";
|
|
8
8
|
import "./chunk-4BKXL7SM.js";
|
|
9
9
|
import "./chunk-NHNWUBXB.js";
|
|
10
10
|
import "./chunk-CQQQFNND.js";
|
|
11
11
|
import "./chunk-6VRJGH25.js";
|
|
12
12
|
import "./chunk-PFYAAX2S.js";
|
|
13
|
-
import "./chunk-
|
|
13
|
+
import "./chunk-ZC4DN6C7.js";
|
|
14
14
|
import {
|
|
15
15
|
VERSION
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-CKCGAHEF.js";
|
|
17
17
|
|
|
18
18
|
// src/mcp/server.ts
|
|
19
19
|
import { createInterface } from "readline";
|
|
20
|
+
import { resolve } from "path";
|
|
21
|
+
import { realpathSync } from "fs";
|
|
20
22
|
var STDIN_TOOLS = /* @__PURE__ */ new Set(["ask_user", "spawn_agent"]);
|
|
23
|
+
var MCP_ALWAYS_DESTRUCTIVE = /* @__PURE__ */ new Set([
|
|
24
|
+
"bash",
|
|
25
|
+
"run_interactive",
|
|
26
|
+
"task_create"
|
|
27
|
+
]);
|
|
28
|
+
var TOOL_PATH_ARGS = {
|
|
29
|
+
read_file: ["path"],
|
|
30
|
+
write_file: ["path"],
|
|
31
|
+
edit_file: ["path"],
|
|
32
|
+
list_dir: ["path"],
|
|
33
|
+
grep_files: ["path"],
|
|
34
|
+
glob_files: ["path"],
|
|
35
|
+
notebook_edit: ["path"],
|
|
36
|
+
save_last_response: ["path"],
|
|
37
|
+
git_status: ["path"],
|
|
38
|
+
git_diff: ["path", "file"],
|
|
39
|
+
git_log: ["path", "file"],
|
|
40
|
+
git_commit: ["path", "files"],
|
|
41
|
+
find_symbol: ["path"],
|
|
42
|
+
get_outline: ["file", "path"],
|
|
43
|
+
find_references: ["path"],
|
|
44
|
+
search_code: ["path"],
|
|
45
|
+
bash: ["cwd"],
|
|
46
|
+
run_interactive: ["cwd"]
|
|
47
|
+
};
|
|
21
48
|
var McpServer = class {
|
|
22
49
|
registry;
|
|
23
50
|
opts;
|
|
24
51
|
initialized = false;
|
|
52
|
+
sandboxRoot;
|
|
53
|
+
eligibleNames;
|
|
25
54
|
constructor(registry, opts = {}) {
|
|
26
55
|
this.registry = registry;
|
|
27
56
|
this.opts = opts;
|
|
57
|
+
this.sandboxRoot = this.resolveSandboxRoot(opts.cwd);
|
|
58
|
+
this.eligibleNames = this.computeEligibleNames();
|
|
59
|
+
}
|
|
60
|
+
resolveSandboxRoot(cwd) {
|
|
61
|
+
const base = cwd ? resolve(cwd) : process.cwd();
|
|
62
|
+
try {
|
|
63
|
+
return realpathSync(base);
|
|
64
|
+
} catch {
|
|
65
|
+
return base;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
computeEligibleNames() {
|
|
69
|
+
const names = /* @__PURE__ */ new Set();
|
|
70
|
+
const whitelist = this.opts.tools && this.opts.tools.length > 0 ? new Set(this.opts.tools) : null;
|
|
71
|
+
for (const tool of this.registry.listAll()) {
|
|
72
|
+
const name = tool.definition.name;
|
|
73
|
+
if (STDIN_TOOLS.has(name)) continue;
|
|
74
|
+
if (whitelist && !whitelist.has(name)) continue;
|
|
75
|
+
names.add(name);
|
|
76
|
+
}
|
|
77
|
+
return names;
|
|
28
78
|
}
|
|
29
79
|
async start() {
|
|
30
80
|
if (this.opts.cwd) {
|
|
@@ -48,7 +98,9 @@ var McpServer = class {
|
|
|
48
98
|
});
|
|
49
99
|
rl.on("close", () => process.exit(0));
|
|
50
100
|
process.stdin.resume();
|
|
51
|
-
this.log(`aicli MCP server v${VERSION} started.
|
|
101
|
+
this.log(`aicli MCP server v${VERSION} started. sandboxRoot=${this.sandboxRoot}`);
|
|
102
|
+
if (this.opts.allowDestructive) this.log("WARNING: --allow-destructive enabled");
|
|
103
|
+
if (this.opts.allowOutsideCwd) this.log("WARNING: --allow-outside-cwd enabled");
|
|
52
104
|
}
|
|
53
105
|
// ── dispatcher ─────────────────────────────────────────────────────────────
|
|
54
106
|
async dispatch(msg) {
|
|
@@ -69,9 +121,17 @@ var McpServer = class {
|
|
|
69
121
|
this.handleInitialize(id, params);
|
|
70
122
|
break;
|
|
71
123
|
case "tools/list":
|
|
124
|
+
if (!this.initialized) {
|
|
125
|
+
this.sendError(id, -32002, "Server not initialized");
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
72
128
|
this.handleToolsList(id);
|
|
73
129
|
break;
|
|
74
130
|
case "tools/call":
|
|
131
|
+
if (!this.initialized) {
|
|
132
|
+
this.sendError(id, -32002, "Server not initialized");
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
75
135
|
await this.handleToolCall(id, params);
|
|
76
136
|
break;
|
|
77
137
|
case "ping":
|
|
@@ -81,12 +141,13 @@ var McpServer = class {
|
|
|
81
141
|
this.sendError(id, -32601, `Method not found: ${method}`);
|
|
82
142
|
}
|
|
83
143
|
} catch (err) {
|
|
84
|
-
const
|
|
85
|
-
this.sendError(id, -32603, `Internal error: ${
|
|
144
|
+
const emsg = err instanceof Error ? err.message : String(err);
|
|
145
|
+
this.sendError(id, -32603, `Internal error: ${emsg}`);
|
|
86
146
|
}
|
|
87
147
|
}
|
|
88
148
|
// ── handlers ───────────────────────────────────────────────────────────────
|
|
89
149
|
handleInitialize(id, _params) {
|
|
150
|
+
this.initialized = true;
|
|
90
151
|
this.sendResult(id, {
|
|
91
152
|
protocolVersion: "2024-11-05",
|
|
92
153
|
capabilities: { tools: {} },
|
|
@@ -94,7 +155,7 @@ var McpServer = class {
|
|
|
94
155
|
});
|
|
95
156
|
}
|
|
96
157
|
handleToolsList(id) {
|
|
97
|
-
const tools = this.
|
|
158
|
+
const tools = [...this.eligibleNames].map((n) => this.registry.get(n)).filter((t) => !!t);
|
|
98
159
|
const list = tools.map((tool) => {
|
|
99
160
|
const params = tool.definition.parameters;
|
|
100
161
|
const properties = {};
|
|
@@ -122,15 +183,28 @@ var McpServer = class {
|
|
|
122
183
|
this.sendError(id, -32602, "Missing required param: name");
|
|
123
184
|
return;
|
|
124
185
|
}
|
|
186
|
+
if (!this.eligibleNames.has(name)) {
|
|
187
|
+
this.sendError(id, -32602, `Tool not exposed: ${name}`);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
125
190
|
const tool = this.registry.get(name);
|
|
126
191
|
if (!tool) {
|
|
127
192
|
this.sendError(id, -32602, `Unknown tool: ${name}`);
|
|
128
193
|
return;
|
|
129
194
|
}
|
|
130
|
-
const
|
|
195
|
+
const forcedDestructive = MCP_ALWAYS_DESTRUCTIVE.has(name);
|
|
196
|
+
const level = forcedDestructive ? "destructive" : getDangerLevel(name, args);
|
|
131
197
|
if (level === "destructive" && !this.opts.allowDestructive) {
|
|
132
198
|
this.sendResult(id, {
|
|
133
|
-
content: [{ type: "text", text: `Tool '${name}'
|
|
199
|
+
content: [{ type: "text", text: `Tool '${name}' requires --allow-destructive in MCP mode.` }],
|
|
200
|
+
isError: true
|
|
201
|
+
});
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
const pathErr = this.validatePathArgs(name, args);
|
|
205
|
+
if (pathErr) {
|
|
206
|
+
this.sendResult(id, {
|
|
207
|
+
content: [{ type: "text", text: pathErr }],
|
|
134
208
|
isError: true
|
|
135
209
|
});
|
|
136
210
|
return;
|
|
@@ -148,17 +222,39 @@ var McpServer = class {
|
|
|
148
222
|
});
|
|
149
223
|
}
|
|
150
224
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
225
|
+
/**
|
|
226
|
+
* Validate that every known path-bearing argument of `toolName` resolves
|
|
227
|
+
* inside the sandbox root. Returns an error message on violation, else undef.
|
|
228
|
+
*
|
|
229
|
+
* We do NOT realpath() per-call: write_file targets may not exist yet, and
|
|
230
|
+
* realpath on every call is a syscall per arg. The root itself is realpath'd
|
|
231
|
+
* once at construction, which stops the "sandbox root is a symlink pointing
|
|
232
|
+
* elsewhere" class of attack. Symlinks inside the tree are allowed.
|
|
233
|
+
*/
|
|
234
|
+
validatePathArgs(toolName, args) {
|
|
235
|
+
if (this.opts.allowOutsideCwd) return void 0;
|
|
236
|
+
const keys = TOOL_PATH_ARGS[toolName];
|
|
237
|
+
if (!keys) return void 0;
|
|
238
|
+
for (const key of keys) {
|
|
239
|
+
const value = args[key];
|
|
240
|
+
if (value === void 0 || value === null) continue;
|
|
241
|
+
const list = Array.isArray(value) ? value : [value];
|
|
242
|
+
for (const entry of list) {
|
|
243
|
+
if (typeof entry !== "string" || entry.length === 0) continue;
|
|
244
|
+
const abs = resolve(this.sandboxRoot, entry);
|
|
245
|
+
if (!this.isInsideSandbox(abs)) {
|
|
246
|
+
return `Path '${entry}' escapes sandbox root '${this.sandboxRoot}'. Pass --allow-outside-cwd to permit.`;
|
|
247
|
+
}
|
|
159
248
|
}
|
|
160
|
-
|
|
161
|
-
|
|
249
|
+
}
|
|
250
|
+
return void 0;
|
|
251
|
+
}
|
|
252
|
+
isInsideSandbox(abs) {
|
|
253
|
+
const norm = process.platform === "win32" ? abs.toLowerCase() : abs;
|
|
254
|
+
const root = process.platform === "win32" ? this.sandboxRoot.toLowerCase() : this.sandboxRoot;
|
|
255
|
+
if (norm === root) return true;
|
|
256
|
+
const sep = process.platform === "win32" ? "\\" : "/";
|
|
257
|
+
return norm.startsWith(root + sep);
|
|
162
258
|
}
|
|
163
259
|
// ── transport ──────────────────────────────────────────────────────────────
|
|
164
260
|
sendResult(id, result) {
|
|
@@ -20,10 +20,10 @@ import {
|
|
|
20
20
|
persistToolRound,
|
|
21
21
|
rebuildExtraMessages,
|
|
22
22
|
setupProxy
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-P6XKBTP4.js";
|
|
24
24
|
import {
|
|
25
25
|
ConfigManager
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-YRWBFUYH.js";
|
|
27
27
|
import {
|
|
28
28
|
ToolExecutor,
|
|
29
29
|
ToolRegistry,
|
|
@@ -41,14 +41,14 @@ import {
|
|
|
41
41
|
spawnAgentContext,
|
|
42
42
|
truncateOutput,
|
|
43
43
|
undoStack
|
|
44
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-M7KYMJT5.js";
|
|
45
45
|
import "./chunk-2ZD3YTVM.js";
|
|
46
46
|
import "./chunk-4BKXL7SM.js";
|
|
47
47
|
import "./chunk-NHNWUBXB.js";
|
|
48
48
|
import "./chunk-CQQQFNND.js";
|
|
49
49
|
import "./chunk-6VRJGH25.js";
|
|
50
50
|
import "./chunk-PFYAAX2S.js";
|
|
51
|
-
import "./chunk-
|
|
51
|
+
import "./chunk-ZC4DN6C7.js";
|
|
52
52
|
import {
|
|
53
53
|
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
54
54
|
AUTHOR,
|
|
@@ -67,7 +67,7 @@ import {
|
|
|
67
67
|
SKILLS_DIR_NAME,
|
|
68
68
|
VERSION,
|
|
69
69
|
buildUserIdentityPrompt
|
|
70
|
-
} from "./chunk-
|
|
70
|
+
} from "./chunk-CKCGAHEF.js";
|
|
71
71
|
import {
|
|
72
72
|
AuthManager
|
|
73
73
|
} from "./chunk-BYNY5JPB.js";
|
|
@@ -2229,7 +2229,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
2229
2229
|
case "test": {
|
|
2230
2230
|
this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
|
|
2231
2231
|
try {
|
|
2232
|
-
const { executeTests } = await import("./run-tests-
|
|
2232
|
+
const { executeTests } = await import("./run-tests-BQMCZVI4.js");
|
|
2233
2233
|
const argStr = args.join(" ").trim();
|
|
2234
2234
|
let testArgs = {};
|
|
2235
2235
|
if (argStr) {
|
|
@@ -3175,7 +3175,11 @@ async function startWebServer(options = {}) {
|
|
|
3175
3175
|
};
|
|
3176
3176
|
const app = express();
|
|
3177
3177
|
const server = createServer(app);
|
|
3178
|
-
const
|
|
3178
|
+
const WS_MAX_PAYLOAD = 1 * 1024 * 1024;
|
|
3179
|
+
const WS_MSG_RATE_PER_SEC = 30;
|
|
3180
|
+
const WS_MSG_BURST = 60;
|
|
3181
|
+
const WS_PREAUTH_TIMEOUT_MS = 3e4;
|
|
3182
|
+
const wss = new WebSocketServer({ server, maxPayload: WS_MAX_PAYLOAD });
|
|
3179
3183
|
const authManager = new AuthManager(config.getConfigDir());
|
|
3180
3184
|
if (authManager.isEnabled()) {
|
|
3181
3185
|
console.log(` Auth: ${authManager.listUsers().length} user(s) registered`);
|
|
@@ -3274,9 +3278,11 @@ async function startWebServer(options = {}) {
|
|
|
3274
3278
|
res.json({ files: [] });
|
|
3275
3279
|
}
|
|
3276
3280
|
});
|
|
3277
|
-
app.get("/api/sessions", requireAuth, (
|
|
3281
|
+
app.get("/api/sessions", requireAuth, (req, res) => {
|
|
3278
3282
|
try {
|
|
3279
|
-
const
|
|
3283
|
+
const authUser = req._authUser;
|
|
3284
|
+
const sm = authUser ? getUserShared(authUser).sessions : sessions;
|
|
3285
|
+
const list = sm.listSessions();
|
|
3280
3286
|
res.json({
|
|
3281
3287
|
sessions: list.slice(0, 50).map((s) => ({
|
|
3282
3288
|
id: s.id,
|
|
@@ -3305,6 +3311,17 @@ async function startWebServer(options = {}) {
|
|
|
3305
3311
|
res.status(404).json({ error: "Session not found" });
|
|
3306
3312
|
return;
|
|
3307
3313
|
}
|
|
3314
|
+
try {
|
|
3315
|
+
const canonicalFile = realpathSync(filePath);
|
|
3316
|
+
const canonicalDir = realpathSync(histDir);
|
|
3317
|
+
if (!canonicalFile.startsWith(canonicalDir + sep)) {
|
|
3318
|
+
res.status(404).json({ error: "Session not found" });
|
|
3319
|
+
return;
|
|
3320
|
+
}
|
|
3321
|
+
} catch {
|
|
3322
|
+
res.status(404).json({ error: "Session not found" });
|
|
3323
|
+
return;
|
|
3324
|
+
}
|
|
3308
3325
|
const data = JSON.parse(readFileSync4(filePath, "utf-8"));
|
|
3309
3326
|
res.json({ session: data });
|
|
3310
3327
|
} catch (err) {
|
|
@@ -3381,8 +3398,35 @@ async function startWebServer(options = {}) {
|
|
|
3381
3398
|
existing.onDisconnect();
|
|
3382
3399
|
handlers.delete(tabId);
|
|
3383
3400
|
}
|
|
3401
|
+
let tokens = WS_MSG_BURST;
|
|
3402
|
+
let lastRefill = Date.now();
|
|
3403
|
+
const takeToken = () => {
|
|
3404
|
+
const now = Date.now();
|
|
3405
|
+
const refill = (now - lastRefill) / 1e3 * WS_MSG_RATE_PER_SEC;
|
|
3406
|
+
if (refill > 0) {
|
|
3407
|
+
tokens = Math.min(WS_MSG_BURST, tokens + refill);
|
|
3408
|
+
lastRefill = now;
|
|
3409
|
+
}
|
|
3410
|
+
if (tokens < 1) return false;
|
|
3411
|
+
tokens -= 1;
|
|
3412
|
+
return true;
|
|
3413
|
+
};
|
|
3384
3414
|
let authenticatedUser = null;
|
|
3385
3415
|
let handler = null;
|
|
3416
|
+
let preAuthTimer = setTimeout(() => {
|
|
3417
|
+
if (!handler && ws.readyState === ws.OPEN) {
|
|
3418
|
+
try {
|
|
3419
|
+
ws.close(1008, "auth timeout");
|
|
3420
|
+
} catch {
|
|
3421
|
+
}
|
|
3422
|
+
}
|
|
3423
|
+
}, WS_PREAUTH_TIMEOUT_MS);
|
|
3424
|
+
const clearPreAuthTimer = () => {
|
|
3425
|
+
if (preAuthTimer) {
|
|
3426
|
+
clearTimeout(preAuthTimer);
|
|
3427
|
+
preAuthTimer = null;
|
|
3428
|
+
}
|
|
3429
|
+
};
|
|
3386
3430
|
if (token) {
|
|
3387
3431
|
authenticatedUser = authManager.verifyToken(token);
|
|
3388
3432
|
}
|
|
@@ -3391,11 +3435,13 @@ async function startWebServer(options = {}) {
|
|
|
3391
3435
|
console.log(` \u2713 Tab connected: ${tabId.slice(0, 12)} (no auth) (total: ${handlers.size + 1})`);
|
|
3392
3436
|
handler = new SessionHandler(ws, shared);
|
|
3393
3437
|
handlers.set(tabId, handler);
|
|
3438
|
+
clearPreAuthTimer();
|
|
3394
3439
|
} else if (authenticatedUser) {
|
|
3395
3440
|
console.log(` \u2713 Tab connected: ${tabId.slice(0, 12)} (user: ${authenticatedUser}) (total: ${handlers.size + 1})`);
|
|
3396
3441
|
const userShared = getUserShared(authenticatedUser);
|
|
3397
3442
|
handler = new SessionHandler(ws, userShared);
|
|
3398
3443
|
handlers.set(tabId, handler);
|
|
3444
|
+
clearPreAuthTimer();
|
|
3399
3445
|
} else {
|
|
3400
3446
|
console.log(` \u23F3 Tab waiting auth: ${tabId.slice(0, 12)}`);
|
|
3401
3447
|
if (ws.readyState === ws.OPEN) {
|
|
@@ -3406,6 +3452,15 @@ async function startWebServer(options = {}) {
|
|
|
3406
3452
|
}
|
|
3407
3453
|
}
|
|
3408
3454
|
ws.on("message", async (data) => {
|
|
3455
|
+
if (!takeToken()) {
|
|
3456
|
+
if (ws.readyState === ws.OPEN) {
|
|
3457
|
+
try {
|
|
3458
|
+
ws.close(1008, "rate limit exceeded");
|
|
3459
|
+
} catch {
|
|
3460
|
+
}
|
|
3461
|
+
}
|
|
3462
|
+
return;
|
|
3463
|
+
}
|
|
3409
3464
|
try {
|
|
3410
3465
|
const raw = data.toString();
|
|
3411
3466
|
const parsed = JSON.parse(raw);
|
|
@@ -3428,6 +3483,7 @@ async function startWebServer(options = {}) {
|
|
|
3428
3483
|
const userShared = getUserShared(username);
|
|
3429
3484
|
handler = new SessionHandler(ws, userShared);
|
|
3430
3485
|
handlers.set(tabId, handler);
|
|
3486
|
+
clearPreAuthTimer();
|
|
3431
3487
|
console.log(` \u2713 User registered & connected: ${username} (tab: ${tabId.slice(0, 12)})`);
|
|
3432
3488
|
ws.send(JSON.stringify({ type: "auth_result", success: true, token: newToken, username, setCookie: true }));
|
|
3433
3489
|
return;
|
|
@@ -3443,6 +3499,7 @@ async function startWebServer(options = {}) {
|
|
|
3443
3499
|
const userShared = getUserShared(verifiedUser);
|
|
3444
3500
|
handler = new SessionHandler(ws, userShared);
|
|
3445
3501
|
handlers.set(tabId, handler);
|
|
3502
|
+
clearPreAuthTimer();
|
|
3446
3503
|
console.log(` \u2713 Token auth: ${verifiedUser} (tab: ${tabId.slice(0, 12)})`);
|
|
3447
3504
|
ws.send(JSON.stringify({ type: "auth_result", success: true, token: clientToken, username: verifiedUser }));
|
|
3448
3505
|
return;
|
|
@@ -3457,6 +3514,7 @@ async function startWebServer(options = {}) {
|
|
|
3457
3514
|
const userShared = getUserShared(username);
|
|
3458
3515
|
handler = new SessionHandler(ws, userShared);
|
|
3459
3516
|
handlers.set(tabId, handler);
|
|
3517
|
+
clearPreAuthTimer();
|
|
3460
3518
|
console.log(` \u2713 User logged in: ${username} (tab: ${tabId.slice(0, 12)})`);
|
|
3461
3519
|
ws.send(JSON.stringify({ type: "auth_result", success: true, token: loginToken, username, setCookie: true }));
|
|
3462
3520
|
return;
|
|
@@ -3483,6 +3541,7 @@ async function startWebServer(options = {}) {
|
|
|
3483
3541
|
}
|
|
3484
3542
|
});
|
|
3485
3543
|
ws.on("close", () => {
|
|
3544
|
+
clearPreAuthTimer();
|
|
3486
3545
|
console.log(` \u2717 Tab disconnected: ${tabId.slice(0, 12)} (total: ${handlers.size - 1})`);
|
|
3487
3546
|
if (handler) handler.onDisconnect();
|
|
3488
3547
|
handlers.delete(tabId);
|
|
@@ -4,17 +4,17 @@ import {
|
|
|
4
4
|
getDangerLevel,
|
|
5
5
|
googleSearchContext,
|
|
6
6
|
truncateOutput
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-M7KYMJT5.js";
|
|
8
8
|
import "./chunk-2ZD3YTVM.js";
|
|
9
9
|
import "./chunk-4BKXL7SM.js";
|
|
10
10
|
import "./chunk-NHNWUBXB.js";
|
|
11
11
|
import "./chunk-CQQQFNND.js";
|
|
12
12
|
import "./chunk-6VRJGH25.js";
|
|
13
13
|
import "./chunk-PFYAAX2S.js";
|
|
14
|
-
import "./chunk-
|
|
14
|
+
import "./chunk-ZC4DN6C7.js";
|
|
15
15
|
import {
|
|
16
16
|
SUBAGENT_ALLOWED_TOOLS
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-CKCGAHEF.js";
|
|
18
18
|
|
|
19
19
|
// src/hub/task-orchestrator.ts
|
|
20
20
|
import { createInterface } from "readline";
|