jinzd-ai-cli 0.4.85 → 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.
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ConfigManager
4
- } from "./chunk-APU36NHI.js";
4
+ } from "./chunk-YRWBFUYH.js";
5
5
  import "./chunk-2ZD3YTVM.js";
6
- import "./chunk-XEXVG3CQ.js";
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
- writeFileSync(getBatchesPath(config), JSON.stringify({ batches }, null, 2), "utf-8");
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");
@@ -6,7 +6,7 @@ import { platform } from "os";
6
6
  import chalk from "chalk";
7
7
 
8
8
  // src/core/constants.ts
9
- var VERSION = "0.4.85";
9
+ var VERSION = "0.4.86";
10
10
  var APP_NAME = "ai-cli";
11
11
  var CONFIG_DIR_NAME = ".aicli";
12
12
  var CONFIG_FILE_NAME = "config.json";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/core/constants.ts
4
- var VERSION = "0.4.85";
4
+ var VERSION = "0.4.86";
5
5
  var APP_NAME = "ai-cli";
6
6
  var CONFIG_DIR_NAME = ".aicli";
7
7
  var CONFIG_FILE_NAME = "config.json";
@@ -19,7 +19,7 @@ import {
19
19
  } from "./chunk-6VRJGH25.js";
20
20
  import {
21
21
  runTestsTool
22
- } from "./chunk-AX7T3J7G.js";
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-XEXVG3CQ.js";
30
+ } from "./chunk-CKCGAHEF.js";
31
31
 
32
32
  // src/tools/types.ts
33
33
  function isFileWriteTool(name) {
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  schemaToJsonSchema,
4
4
  truncateForPersist
5
- } from "./chunk-YBL4QDLD.js";
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-XEXVG3CQ.js";
21
+ } from "./chunk-CKCGAHEF.js";
22
22
 
23
23
  // src/providers/claude.ts
24
24
  import Anthropic from "@anthropic-ai/sdk";
@@ -8,7 +8,7 @@ import {
8
8
  CONFIG_FILE_NAME,
9
9
  HISTORY_DIR_NAME,
10
10
  PLUGINS_DIR_NAME
11
- } from "./chunk-XEXVG3CQ.js";
11
+ } from "./chunk-CKCGAHEF.js";
12
12
 
13
13
  // src/config/config-manager.ts
14
14
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  TEST_TIMEOUT
4
- } from "./chunk-XEXVG3CQ.js";
4
+ } from "./chunk-CKCGAHEF.js";
5
5
 
6
6
  // src/tools/builtin/run-tests.ts
7
7
  import { execSync } from "child_process";
@@ -36,7 +36,7 @@ import {
36
36
  VERSION,
37
37
  buildUserIdentityPrompt,
38
38
  runTestsTool
39
- } from "./chunk-4GMAVGPX.js";
39
+ } from "./chunk-CHJFRDUB.js";
40
40
  import {
41
41
  hasSemanticIndex,
42
42
  semanticSearch
@@ -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-PIISCS6B.js");
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 wss = new WebSocketServer({ server });
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, (_req, res) => {
12344
+ app.get("/api/sessions", requireAuth, (req, res) => {
12341
12345
  try {
12342
- const list = sessions.listSessions();
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-H73NIC6T.js");
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-6IOQPMMT.js";
33
+ } from "./chunk-P6XKBTP4.js";
34
34
  import {
35
35
  ConfigManager
36
- } from "./chunk-APU36NHI.js";
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-YBL4QDLD.js";
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-AX7T3J7G.js";
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-XEXVG3CQ.js";
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-KWVQL2PO.js");
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-WNL7UU5Q.js");
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-YBWWF5BJ.js");
6611
+ const batch = await import("./batch-KN3ESR3U.js");
6612
6612
  switch (action) {
6613
6613
  case "submit":
6614
6614
  if (!arg) {
@@ -6651,7 +6651,7 @@ program.command("batch <action> [arg] [arg2]").description("Anthropic Message Ba
6651
6651
  }
6652
6652
  });
6653
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-GAKP7VZ7.js");
6654
+ const { startMcpServer } = await import("./server-73EENVYE.js");
6655
6655
  await startMcpServer({
6656
6656
  allowDestructive: !!options.allowDestructive,
6657
6657
  allowOutsideCwd: !!options.allowOutsideCwd,
@@ -6778,7 +6778,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
6778
6778
  }),
6779
6779
  config.get("customProviders")
6780
6780
  );
6781
- const { startHub } = await import("./hub-RCX7G5T6.js");
6781
+ const { startHub } = await import("./hub-6GS4DYZO.js");
6782
6782
  await startHub(
6783
6783
  {
6784
6784
  topic: topic ?? "",
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  executeTests,
4
4
  runTestsTool
5
- } from "./chunk-AX7T3J7G.js";
6
- import "./chunk-XEXVG3CQ.js";
5
+ } from "./chunk-ZC4DN6C7.js";
6
+ import "./chunk-CKCGAHEF.js";
7
7
  export {
8
8
  executeTests,
9
9
  runTestsTool
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  executeTests,
3
3
  runTestsTool
4
- } from "./chunk-4GMAVGPX.js";
4
+ } from "./chunk-CHJFRDUB.js";
5
5
  export {
6
6
  executeTests,
7
7
  runTestsTool
@@ -3,17 +3,17 @@ import {
3
3
  ToolRegistry,
4
4
  getDangerLevel,
5
5
  schemaToJsonSchema
6
- } from "./chunk-YBL4QDLD.js";
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-AX7T3J7G.js";
13
+ import "./chunk-ZC4DN6C7.js";
14
14
  import {
15
15
  VERSION
16
- } from "./chunk-XEXVG3CQ.js";
16
+ } from "./chunk-CKCGAHEF.js";
17
17
 
18
18
  // src/mcp/server.ts
19
19
  import { createInterface } from "readline";
@@ -20,10 +20,10 @@ import {
20
20
  persistToolRound,
21
21
  rebuildExtraMessages,
22
22
  setupProxy
23
- } from "./chunk-6IOQPMMT.js";
23
+ } from "./chunk-P6XKBTP4.js";
24
24
  import {
25
25
  ConfigManager
26
- } from "./chunk-APU36NHI.js";
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-YBL4QDLD.js";
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-AX7T3J7G.js";
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-XEXVG3CQ.js";
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-KWVQL2PO.js");
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 wss = new WebSocketServer({ server });
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, (_req, res) => {
3281
+ app.get("/api/sessions", requireAuth, (req, res) => {
3278
3282
  try {
3279
- const list = sessions.listSessions();
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-YBL4QDLD.js";
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-AX7T3J7G.js";
14
+ import "./chunk-ZC4DN6C7.js";
15
15
  import {
16
16
  SUBAGENT_ALLOWED_TOOLS
17
- } from "./chunk-XEXVG3CQ.js";
17
+ } from "./chunk-CKCGAHEF.js";
18
18
 
19
19
  // src/hub/task-orchestrator.ts
20
20
  import { createInterface } from "readline";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jinzd-ai-cli",
3
- "version": "0.4.85",
3
+ "version": "0.4.86",
4
4
  "description": "Cross-platform REPL-style AI CLI with multi-provider support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",