jinzd-ai-cli 0.4.112 → 0.4.114

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.
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ AuthManager,
4
+ __resetLoginAttemptsForTests
5
+ } from "./chunk-5UPFMM2A.js";
6
+ import "./chunk-PDX44BCA.js";
7
+ export {
8
+ AuthManager,
9
+ __resetLoginAttemptsForTests
10
+ };
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ConfigManager
4
- } from "./chunk-FM5VYCXA.js";
4
+ } from "./chunk-CP6PALA4.js";
5
5
  import "./chunk-2ZD3YTVM.js";
6
- import "./chunk-AWZ63EVH.js";
6
+ import "./chunk-UF62SHR7.js";
7
7
  import "./chunk-PDX44BCA.js";
8
8
 
9
9
  // src/cli/batch.ts
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  TEST_TIMEOUT
4
- } from "./chunk-AWZ63EVH.js";
4
+ } from "./chunk-UF62SHR7.js";
5
5
 
6
6
  // src/tools/builtin/run-tests.ts
7
7
  import { execSync, spawnSync } from "child_process";
@@ -5,8 +5,14 @@ import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, copyFi
5
5
  import { join } from "path";
6
6
  import { createHmac, randomBytes, timingSafeEqual, pbkdf2Sync } from "crypto";
7
7
  var USERS_FILE = "users.json";
8
- var TOKEN_EXPIRY_HOURS = 24 * 7;
8
+ var TOKEN_EXPIRY_HOURS = 24;
9
9
  var USERS_DIR = "users";
10
+ var LOGIN_MAX_FAILS = 5;
11
+ var LOGIN_LOCKOUT_MS = 15 * 60 * 1e3;
12
+ var loginAttempts = /* @__PURE__ */ new Map();
13
+ function __resetLoginAttemptsForTests() {
14
+ loginAttempts.clear();
15
+ }
10
16
  var AuthManager = class {
11
17
  usersFile;
12
18
  baseDir;
@@ -57,16 +63,30 @@ var AuthManager = class {
57
63
  this.save();
58
64
  return null;
59
65
  }
60
- /** Authenticate user. Returns JWT token or null. */
66
+ /**
67
+ * Authenticate user. Returns JWT token or null on failure.
68
+ *
69
+ * Audit closure (5th audit, v0.4.114): integrates failed-login lockout
70
+ * (CWE-307). After {@link LOGIN_MAX_FAILS} consecutive failures within a
71
+ * lockout window, further attempts return null without checking the
72
+ * password (avoiding pbkdf2 work + leaking timing). Successful login
73
+ * resets the counter.
74
+ */
61
75
  login(username, password) {
62
76
  username = username.trim().toLowerCase();
77
+ const lockState = this.getLockState(username);
78
+ if (lockState.locked) return null;
63
79
  const user = this.db.users.find((u) => u.username === username);
64
- if (!user) return null;
80
+ if (!user) {
81
+ this.recordFailedLogin(username);
82
+ return null;
83
+ }
65
84
  const isLegacy = !user.hashVersion || user.hashVersion < 2;
66
85
  const hash = isLegacy ? this.hashPasswordLegacy(password, user.salt) : this.hashPassword(password, user.salt);
67
86
  const a = Buffer.from(hash, "utf-8");
68
87
  const b = Buffer.from(user.passwordHash, "utf-8");
69
88
  if (a.length !== b.length || !timingSafeEqual(a, b)) {
89
+ this.recordFailedLogin(username);
70
90
  return null;
71
91
  }
72
92
  if (isLegacy) {
@@ -76,8 +96,34 @@ var AuthManager = class {
76
96
  user.hashVersion = 2;
77
97
  this.save();
78
98
  }
99
+ loginAttempts.delete(username);
79
100
  return this.createToken(username);
80
101
  }
102
+ /**
103
+ * Returns current lockout state for a username and lazily expires it.
104
+ * Exposed (read-only) for tests and the `aicli user` CLI status output.
105
+ */
106
+ getLockState(username) {
107
+ username = username.trim().toLowerCase();
108
+ const state = loginAttempts.get(username);
109
+ if (!state) return { locked: false, remainingMs: 0, fails: 0 };
110
+ if (state.lockedUntil > 0 && Date.now() >= state.lockedUntil) {
111
+ loginAttempts.delete(username);
112
+ return { locked: false, remainingMs: 0, fails: 0 };
113
+ }
114
+ if (state.lockedUntil > 0) {
115
+ return { locked: true, remainingMs: state.lockedUntil - Date.now(), fails: state.fails };
116
+ }
117
+ return { locked: false, remainingMs: 0, fails: state.fails };
118
+ }
119
+ recordFailedLogin(username) {
120
+ const state = loginAttempts.get(username) ?? { fails: 0, lockedUntil: 0 };
121
+ state.fails += 1;
122
+ if (state.fails >= LOGIN_MAX_FAILS) {
123
+ state.lockedUntil = Date.now() + LOGIN_LOCKOUT_MS;
124
+ }
125
+ loginAttempts.set(username, state);
126
+ }
81
127
  /** Verify a token. Returns username or null. */
82
128
  verifyToken(token) {
83
129
  try {
@@ -90,12 +136,29 @@ var AuthManager = class {
90
136
  Buffer.from(payloadB64, "base64url").toString("utf-8")
91
137
  );
92
138
  if (Date.now() > payload.exp) return null;
93
- if (!this.db.users.find((u) => u.username === payload.username)) return null;
139
+ const user = this.db.users.find((u) => u.username === payload.username);
140
+ if (!user) return null;
141
+ if (user.tokensRevokedBefore && (!payload.iat || payload.iat < user.tokensRevokedBefore)) {
142
+ return null;
143
+ }
94
144
  return payload.username;
95
145
  } catch {
96
146
  return null;
97
147
  }
98
148
  }
149
+ /**
150
+ * Revoke every outstanding token for the given user by bumping the
151
+ * `tokensRevokedBefore` watermark to now. Audit closure (5th audit,
152
+ * v0.4.114). Returns true if the user existed.
153
+ */
154
+ logoutAll(username) {
155
+ username = username.trim().toLowerCase();
156
+ const user = this.db.users.find((u) => u.username === username);
157
+ if (!user) return false;
158
+ user.tokensRevokedBefore = Date.now() + 1;
159
+ this.save();
160
+ return true;
161
+ }
99
162
  /** Get user's data directory (absolute path) */
100
163
  getUserDataDir(username) {
101
164
  const user = this.db.users.find((u) => u.username === username);
@@ -127,6 +190,7 @@ var AuthManager = class {
127
190
  user.passwordHash = this.hashPassword(newPassword, salt);
128
191
  user.salt = salt;
129
192
  user.hashVersion = 2;
193
+ user.tokensRevokedBefore = Date.now() + 1;
130
194
  this.save();
131
195
  return null;
132
196
  }
@@ -198,9 +262,11 @@ var AuthManager = class {
198
262
  return pbkdf2Sync(password, salt, 1e5, 64, "sha512").toString("hex");
199
263
  }
200
264
  createToken(username) {
265
+ const now = Date.now();
201
266
  const payload = {
202
267
  username,
203
- exp: Date.now() + TOKEN_EXPIRY_HOURS * 3600 * 1e3
268
+ iat: now,
269
+ exp: now + TOKEN_EXPIRY_HOURS * 3600 * 1e3
204
270
  };
205
271
  const payloadB64 = Buffer.from(JSON.stringify(payload), "utf-8").toString("base64url");
206
272
  const signature = this.sign(payloadB64);
@@ -212,5 +278,6 @@ var AuthManager = class {
212
278
  };
213
279
 
214
280
  export {
281
+ __resetLoginAttemptsForTests,
215
282
  AuthManager
216
283
  };
@@ -8,7 +8,7 @@ import {
8
8
  CONFIG_FILE_NAME,
9
9
  HISTORY_DIR_NAME,
10
10
  PLUGINS_DIR_NAME
11
- } from "./chunk-AWZ63EVH.js";
11
+ } from "./chunk-UF62SHR7.js";
12
12
 
13
13
  // src/config/config-manager.ts
14
14
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  schemaToJsonSchema,
4
4
  truncateForPersist
5
- } from "./chunk-7XXZWPTN.js";
5
+ } from "./chunk-XXKWSBRC.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-AWZ63EVH.js";
21
+ } from "./chunk-UF62SHR7.js";
22
22
  import {
23
23
  redactJson
24
24
  } from "./chunk-7ZJN4KLV.js";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/core/constants.ts
4
- var VERSION = "0.4.112";
4
+ var VERSION = "0.4.114";
5
5
  var APP_NAME = "ai-cli";
6
6
  var CONFIG_DIR_NAME = ".aicli";
7
7
  var CONFIG_FILE_NAME = "config.json";
@@ -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.112";
9
+ var VERSION = "0.4.114";
10
10
  var APP_NAME = "ai-cli";
11
11
  var CONFIG_DIR_NAME = ".aicli";
12
12
  var CONFIG_FILE_NAME = "config.json";
@@ -5,7 +5,7 @@ import {
5
5
  } from "./chunk-3BICTI5M.js";
6
6
  import {
7
7
  runTestsTool
8
- } from "./chunk-5XHNTLRS.js";
8
+ } from "./chunk-2WAF7FOX.js";
9
9
  import {
10
10
  EnvLoader,
11
11
  NetworkError,
@@ -18,7 +18,7 @@ import {
18
18
  SUBAGENT_ALLOWED_TOOLS,
19
19
  SUBAGENT_DEFAULT_MAX_ROUNDS,
20
20
  SUBAGENT_MAX_ROUNDS_LIMIT
21
- } from "./chunk-AWZ63EVH.js";
21
+ } from "./chunk-UF62SHR7.js";
22
22
  import {
23
23
  fileCheckpoints
24
24
  } from "./chunk-4BKXL7SM.js";
@@ -229,10 +229,36 @@ function resetInterrupt() {
229
229
  interrupted = false;
230
230
  }
231
231
 
232
+ // src/tools/session-context.ts
233
+ import { AsyncLocalStorage } from "async_hooks";
234
+ var als = new AsyncLocalStorage();
235
+ var DEFAULT_SESSION_KEY = "__default__";
236
+ function getCurrentSessionKey() {
237
+ const ctx = als.getStore();
238
+ if (!ctx || !ctx.sessionKey) return DEFAULT_SESSION_KEY;
239
+ return ctx.sessionKey;
240
+ }
241
+ function runWithSessionKey(sessionKey, fn) {
242
+ const key = sessionKey && sessionKey.length > 0 ? sessionKey : DEFAULT_SESSION_KEY;
243
+ return als.run({ sessionKey: key }, fn);
244
+ }
245
+
232
246
  // src/tools/builtin/bash.ts
233
247
  var IS_WINDOWS = platform() === "win32";
234
248
  var SHELL = IS_WINDOWS ? "powershell.exe" : process.env["SHELL"] ?? "/bin/bash";
235
- var persistentCwd = process.cwd();
249
+ var cwdBySession = /* @__PURE__ */ new Map();
250
+ function getCwd() {
251
+ const key = getCurrentSessionKey();
252
+ let cwd = cwdBySession.get(key);
253
+ if (!cwd) {
254
+ cwd = process.cwd();
255
+ cwdBySession.set(key, cwd);
256
+ }
257
+ return cwd;
258
+ }
259
+ function setCwd(next) {
260
+ cwdBySession.set(getCurrentSessionKey(), next);
261
+ }
236
262
  var bashTool = {
237
263
  definition: {
238
264
  name: "bash",
@@ -277,17 +303,19 @@ Important rules:
277
303
  if (!command.trim()) {
278
304
  throw new ToolError("bash", "command is required");
279
305
  }
280
- if (!existsSync2(persistentCwd)) {
306
+ let currentCwd = getCwd();
307
+ if (!existsSync2(currentCwd)) {
281
308
  const fallback = process.cwd();
282
309
  process.stderr.write(
283
- `[bash] Previous cwd "${persistentCwd}" no longer exists, reset to "${fallback}"
310
+ `[bash] Previous cwd "${currentCwd}" no longer exists, reset to "${fallback}"
284
311
  `
285
312
  );
286
- persistentCwd = fallback;
313
+ currentCwd = fallback;
314
+ setCwd(fallback);
287
315
  }
288
- let effectiveCwd = persistentCwd;
316
+ let effectiveCwd = currentCwd;
289
317
  if (cwdArg) {
290
- const resolved = resolve(persistentCwd, cwdArg);
318
+ const resolved = resolve(currentCwd, cwdArg);
291
319
  if (!existsSync2(resolved)) {
292
320
  throw new ToolError(
293
321
  "bash",
@@ -295,7 +323,7 @@ Important rules:
295
323
  );
296
324
  }
297
325
  effectiveCwd = resolved;
298
- persistentCwd = resolved;
326
+ setCwd(resolved);
299
327
  }
300
328
  let actualCommand;
301
329
  if (IS_WINDOWS) {
@@ -551,7 +579,7 @@ function updateCwdFromCommand(command, baseCwd) {
551
579
  try {
552
580
  const newDir = resolve(baseCwd, target);
553
581
  if (existsSync2(newDir)) {
554
- persistentCwd = newDir;
582
+ setCwd(newDir);
555
583
  }
556
584
  } catch {
557
585
  }
@@ -1411,6 +1439,16 @@ var ToolExecutor = class {
1411
1439
  * 通过 /yolo 命令切换。destructive 操作仍会显示警告但不阻塞。
1412
1440
  */
1413
1441
  sessionAutoApprove = false;
1442
+ /**
1443
+ * Logical session key used to scope per-session state in stateful tools
1444
+ * (currently only `bash`'s persistent cwd). Web mode sets this per-tab so
1445
+ * concurrent tabs don't share cwd. CLI/REPL leaves it undefined (the bash
1446
+ * tool falls back to a default key).
1447
+ */
1448
+ sessionKey;
1449
+ setSessionKey(key) {
1450
+ this.sessionKey = key;
1451
+ }
1414
1452
  /**
1415
1453
  * 由外部(repl.ts SIGINT handler)调用,将当前 confirm() 等待视为用户按 N 取消。
1416
1454
  * 若当前没有 confirm() 进行中,无操作。
@@ -1443,6 +1481,9 @@ var ToolExecutor = class {
1443
1481
  if (opts.defaultPermission) this.defaultPermission = opts.defaultPermission;
1444
1482
  }
1445
1483
  async execute(call) {
1484
+ return runWithSessionKey(this.sessionKey, () => this.executeInner(call));
1485
+ }
1486
+ async executeInner(call) {
1446
1487
  const tool = this.registry.get(call.name);
1447
1488
  if (!tool) {
1448
1489
  return {
@@ -36,7 +36,7 @@ import {
36
36
  TEST_TIMEOUT,
37
37
  VERSION,
38
38
  buildUserIdentityPrompt
39
- } from "./chunk-AWZ63EVH.js";
39
+ } from "./chunk-UF62SHR7.js";
40
40
  import "./chunk-PDX44BCA.js";
41
41
  export {
42
42
  AGENTIC_BEHAVIOR_GUIDELINE,
@@ -36,7 +36,7 @@ import {
36
36
  VERSION,
37
37
  buildUserIdentityPrompt,
38
38
  runTestsTool
39
- } from "./chunk-DEXCXFLP.js";
39
+ } from "./chunk-W45U3KQE.js";
40
40
  import {
41
41
  hasSemanticIndex,
42
42
  semanticSearch
@@ -3742,10 +3742,36 @@ function currentAbortSignal() {
3742
3742
  return controller.signal;
3743
3743
  }
3744
3744
 
3745
+ // src/tools/session-context.ts
3746
+ import { AsyncLocalStorage } from "async_hooks";
3747
+ var als = new AsyncLocalStorage();
3748
+ var DEFAULT_SESSION_KEY = "__default__";
3749
+ function getCurrentSessionKey() {
3750
+ const ctx = als.getStore();
3751
+ if (!ctx || !ctx.sessionKey) return DEFAULT_SESSION_KEY;
3752
+ return ctx.sessionKey;
3753
+ }
3754
+ function runWithSessionKey(sessionKey, fn) {
3755
+ const key = sessionKey && sessionKey.length > 0 ? sessionKey : DEFAULT_SESSION_KEY;
3756
+ return als.run({ sessionKey: key }, fn);
3757
+ }
3758
+
3745
3759
  // src/tools/builtin/bash.ts
3746
3760
  var IS_WINDOWS = platform() === "win32";
3747
3761
  var SHELL = IS_WINDOWS ? "powershell.exe" : process.env["SHELL"] ?? "/bin/bash";
3748
- var persistentCwd = process.cwd();
3762
+ var cwdBySession = /* @__PURE__ */ new Map();
3763
+ function getCwd() {
3764
+ const key = getCurrentSessionKey();
3765
+ let cwd = cwdBySession.get(key);
3766
+ if (!cwd) {
3767
+ cwd = process.cwd();
3768
+ cwdBySession.set(key, cwd);
3769
+ }
3770
+ return cwd;
3771
+ }
3772
+ function setCwd(next) {
3773
+ cwdBySession.set(getCurrentSessionKey(), next);
3774
+ }
3749
3775
  var bashTool = {
3750
3776
  definition: {
3751
3777
  name: "bash",
@@ -3790,17 +3816,19 @@ Important rules:
3790
3816
  if (!command.trim()) {
3791
3817
  throw new ToolError("bash", "command is required");
3792
3818
  }
3793
- if (!existsSync4(persistentCwd)) {
3819
+ let currentCwd = getCwd();
3820
+ if (!existsSync4(currentCwd)) {
3794
3821
  const fallback = process.cwd();
3795
3822
  process.stderr.write(
3796
- `[bash] Previous cwd "${persistentCwd}" no longer exists, reset to "${fallback}"
3823
+ `[bash] Previous cwd "${currentCwd}" no longer exists, reset to "${fallback}"
3797
3824
  `
3798
3825
  );
3799
- persistentCwd = fallback;
3826
+ currentCwd = fallback;
3827
+ setCwd(fallback);
3800
3828
  }
3801
- let effectiveCwd = persistentCwd;
3829
+ let effectiveCwd = currentCwd;
3802
3830
  if (cwdArg) {
3803
- const resolved = resolve(persistentCwd, cwdArg);
3831
+ const resolved = resolve(currentCwd, cwdArg);
3804
3832
  if (!existsSync4(resolved)) {
3805
3833
  throw new ToolError(
3806
3834
  "bash",
@@ -3808,7 +3836,7 @@ Important rules:
3808
3836
  );
3809
3837
  }
3810
3838
  effectiveCwd = resolved;
3811
- persistentCwd = resolved;
3839
+ setCwd(resolved);
3812
3840
  }
3813
3841
  let actualCommand;
3814
3842
  if (IS_WINDOWS) {
@@ -4064,7 +4092,7 @@ function updateCwdFromCommand(command, baseCwd) {
4064
4092
  try {
4065
4093
  const newDir = resolve(baseCwd, target);
4066
4094
  if (existsSync4(newDir)) {
4067
- persistentCwd = newDir;
4095
+ setCwd(newDir);
4068
4096
  }
4069
4097
  } catch {
4070
4098
  }
@@ -4885,6 +4913,16 @@ var ToolExecutor = class {
4885
4913
  * 通过 /yolo 命令切换。destructive 操作仍会显示警告但不阻塞。
4886
4914
  */
4887
4915
  sessionAutoApprove = false;
4916
+ /**
4917
+ * Logical session key used to scope per-session state in stateful tools
4918
+ * (currently only `bash`'s persistent cwd). Web mode sets this per-tab so
4919
+ * concurrent tabs don't share cwd. CLI/REPL leaves it undefined (the bash
4920
+ * tool falls back to a default key).
4921
+ */
4922
+ sessionKey;
4923
+ setSessionKey(key) {
4924
+ this.sessionKey = key;
4925
+ }
4888
4926
  /**
4889
4927
  * 由外部(repl.ts SIGINT handler)调用,将当前 confirm() 等待视为用户按 N 取消。
4890
4928
  * 若当前没有 confirm() 进行中,无操作。
@@ -4917,6 +4955,9 @@ var ToolExecutor = class {
4917
4955
  if (opts.defaultPermission) this.defaultPermission = opts.defaultPermission;
4918
4956
  }
4919
4957
  async execute(call) {
4958
+ return runWithSessionKey(this.sessionKey, () => this.executeInner(call));
4959
+ }
4960
+ async executeInner(call) {
4920
4961
  const tool = this.registry.get(call.name);
4921
4962
  if (!tool) {
4922
4963
  return {
@@ -10046,6 +10087,7 @@ var SessionHandler = class _SessionHandler {
10046
10087
  this.mcpManager = shared.mcpManager;
10047
10088
  this.skillManager = shared.skillManager;
10048
10089
  this.toolExecutor = new ToolExecutorWeb(shared.toolRegistry, ws);
10090
+ this.toolExecutor.setSessionKey(`web-${Math.random().toString(36).slice(2)}-${Date.now()}`);
10049
10091
  this.currentProvider = this.config.get("defaultProvider");
10050
10092
  const allDefaultModels = this.config.get("defaultModels");
10051
10093
  try {
@@ -11918,7 +11960,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
11918
11960
  case "test": {
11919
11961
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
11920
11962
  try {
11921
- const { executeTests } = await import("./run-tests-V73IZCNW.js");
11963
+ const { executeTests } = await import("./run-tests-OB4CWKKX.js");
11922
11964
  const argStr = args.join(" ").trim();
11923
11965
  let testArgs = {};
11924
11966
  if (argStr) {
@@ -12906,8 +12948,11 @@ import { existsSync as existsSync21, readFileSync as readFileSync14, writeFileSy
12906
12948
  import { join as join14 } from "path";
12907
12949
  import { createHmac, randomBytes, timingSafeEqual, pbkdf2Sync } from "crypto";
12908
12950
  var USERS_FILE = "users.json";
12909
- var TOKEN_EXPIRY_HOURS = 24 * 7;
12951
+ var TOKEN_EXPIRY_HOURS = 24;
12910
12952
  var USERS_DIR = "users";
12953
+ var LOGIN_MAX_FAILS = 5;
12954
+ var LOGIN_LOCKOUT_MS = 15 * 60 * 1e3;
12955
+ var loginAttempts = /* @__PURE__ */ new Map();
12911
12956
  var AuthManager = class {
12912
12957
  usersFile;
12913
12958
  baseDir;
@@ -12958,16 +13003,30 @@ var AuthManager = class {
12958
13003
  this.save();
12959
13004
  return null;
12960
13005
  }
12961
- /** Authenticate user. Returns JWT token or null. */
13006
+ /**
13007
+ * Authenticate user. Returns JWT token or null on failure.
13008
+ *
13009
+ * Audit closure (5th audit, v0.4.114): integrates failed-login lockout
13010
+ * (CWE-307). After {@link LOGIN_MAX_FAILS} consecutive failures within a
13011
+ * lockout window, further attempts return null without checking the
13012
+ * password (avoiding pbkdf2 work + leaking timing). Successful login
13013
+ * resets the counter.
13014
+ */
12962
13015
  login(username, password) {
12963
13016
  username = username.trim().toLowerCase();
13017
+ const lockState = this.getLockState(username);
13018
+ if (lockState.locked) return null;
12964
13019
  const user = this.db.users.find((u) => u.username === username);
12965
- if (!user) return null;
13020
+ if (!user) {
13021
+ this.recordFailedLogin(username);
13022
+ return null;
13023
+ }
12966
13024
  const isLegacy = !user.hashVersion || user.hashVersion < 2;
12967
13025
  const hash = isLegacy ? this.hashPasswordLegacy(password, user.salt) : this.hashPassword(password, user.salt);
12968
13026
  const a = Buffer.from(hash, "utf-8");
12969
13027
  const b = Buffer.from(user.passwordHash, "utf-8");
12970
13028
  if (a.length !== b.length || !timingSafeEqual(a, b)) {
13029
+ this.recordFailedLogin(username);
12971
13030
  return null;
12972
13031
  }
12973
13032
  if (isLegacy) {
@@ -12977,8 +13036,34 @@ var AuthManager = class {
12977
13036
  user.hashVersion = 2;
12978
13037
  this.save();
12979
13038
  }
13039
+ loginAttempts.delete(username);
12980
13040
  return this.createToken(username);
12981
13041
  }
13042
+ /**
13043
+ * Returns current lockout state for a username and lazily expires it.
13044
+ * Exposed (read-only) for tests and the `aicli user` CLI status output.
13045
+ */
13046
+ getLockState(username) {
13047
+ username = username.trim().toLowerCase();
13048
+ const state = loginAttempts.get(username);
13049
+ if (!state) return { locked: false, remainingMs: 0, fails: 0 };
13050
+ if (state.lockedUntil > 0 && Date.now() >= state.lockedUntil) {
13051
+ loginAttempts.delete(username);
13052
+ return { locked: false, remainingMs: 0, fails: 0 };
13053
+ }
13054
+ if (state.lockedUntil > 0) {
13055
+ return { locked: true, remainingMs: state.lockedUntil - Date.now(), fails: state.fails };
13056
+ }
13057
+ return { locked: false, remainingMs: 0, fails: state.fails };
13058
+ }
13059
+ recordFailedLogin(username) {
13060
+ const state = loginAttempts.get(username) ?? { fails: 0, lockedUntil: 0 };
13061
+ state.fails += 1;
13062
+ if (state.fails >= LOGIN_MAX_FAILS) {
13063
+ state.lockedUntil = Date.now() + LOGIN_LOCKOUT_MS;
13064
+ }
13065
+ loginAttempts.set(username, state);
13066
+ }
12982
13067
  /** Verify a token. Returns username or null. */
12983
13068
  verifyToken(token) {
12984
13069
  try {
@@ -12991,12 +13076,29 @@ var AuthManager = class {
12991
13076
  Buffer.from(payloadB64, "base64url").toString("utf-8")
12992
13077
  );
12993
13078
  if (Date.now() > payload.exp) return null;
12994
- if (!this.db.users.find((u) => u.username === payload.username)) return null;
13079
+ const user = this.db.users.find((u) => u.username === payload.username);
13080
+ if (!user) return null;
13081
+ if (user.tokensRevokedBefore && (!payload.iat || payload.iat < user.tokensRevokedBefore)) {
13082
+ return null;
13083
+ }
12995
13084
  return payload.username;
12996
13085
  } catch {
12997
13086
  return null;
12998
13087
  }
12999
13088
  }
13089
+ /**
13090
+ * Revoke every outstanding token for the given user by bumping the
13091
+ * `tokensRevokedBefore` watermark to now. Audit closure (5th audit,
13092
+ * v0.4.114). Returns true if the user existed.
13093
+ */
13094
+ logoutAll(username) {
13095
+ username = username.trim().toLowerCase();
13096
+ const user = this.db.users.find((u) => u.username === username);
13097
+ if (!user) return false;
13098
+ user.tokensRevokedBefore = Date.now() + 1;
13099
+ this.save();
13100
+ return true;
13101
+ }
13000
13102
  /** Get user's data directory (absolute path) */
13001
13103
  getUserDataDir(username) {
13002
13104
  const user = this.db.users.find((u) => u.username === username);
@@ -13028,6 +13130,7 @@ var AuthManager = class {
13028
13130
  user.passwordHash = this.hashPassword(newPassword, salt);
13029
13131
  user.salt = salt;
13030
13132
  user.hashVersion = 2;
13133
+ user.tokensRevokedBefore = Date.now() + 1;
13031
13134
  this.save();
13032
13135
  return null;
13033
13136
  }
@@ -13099,9 +13202,11 @@ var AuthManager = class {
13099
13202
  return pbkdf2Sync(password, salt, 1e5, 64, "sha512").toString("hex");
13100
13203
  }
13101
13204
  createToken(username) {
13205
+ const now = Date.now();
13102
13206
  const payload = {
13103
13207
  username,
13104
- exp: Date.now() + TOKEN_EXPIRY_HOURS * 3600 * 1e3
13208
+ iat: now,
13209
+ exp: now + TOKEN_EXPIRY_HOURS * 3600 * 1e3
13105
13210
  };
13106
13211
  const payloadB64 = Buffer.from(JSON.stringify(payload), "utf-8").toString("base64url");
13107
13212
  const signature = this.sign(payloadB64);
@@ -13196,6 +13301,27 @@ async function startWebServer(options = {}) {
13196
13301
  skillManager
13197
13302
  };
13198
13303
  const app = express();
13304
+ app.use((_req, res, next) => {
13305
+ res.setHeader(
13306
+ "Content-Security-Policy",
13307
+ [
13308
+ "default-src 'self'",
13309
+ "script-src 'self'",
13310
+ "style-src 'self' 'unsafe-inline'",
13311
+ "img-src 'self' data: blob:",
13312
+ "font-src 'self' data:",
13313
+ "connect-src 'self' ws: wss:",
13314
+ "frame-ancestors 'none'",
13315
+ "base-uri 'self'",
13316
+ "form-action 'self'"
13317
+ ].join("; ")
13318
+ );
13319
+ res.setHeader("X-Content-Type-Options", "nosniff");
13320
+ res.setHeader("Referrer-Policy", "no-referrer");
13321
+ res.setHeader("X-Frame-Options", "DENY");
13322
+ res.setHeader("Permissions-Policy", "geolocation=(), microphone=(), camera=()");
13323
+ next();
13324
+ });
13199
13325
  const server = createServer(app);
13200
13326
  const WS_MAX_PAYLOAD = 1 * 1024 * 1024;
13201
13327
  const WS_MSG_RATE_PER_SEC = 30;
@@ -386,7 +386,7 @@ ${content}`);
386
386
  }
387
387
  }
388
388
  async function runTaskMode(config, providers, configManager, topic) {
389
- const { TaskOrchestrator } = await import("./task-orchestrator-RPYZT2WL.js");
389
+ const { TaskOrchestrator } = await import("./task-orchestrator-32YQ6HB2.js");
390
390
  const orchestrator = new TaskOrchestrator(config, providers, configManager);
391
391
  let interrupted = false;
392
392
  const onSigint = () => {
package/dist/index.js CHANGED
@@ -31,10 +31,10 @@ import {
31
31
  setupProxy,
32
32
  stripPseudoToolCalls,
33
33
  stripToolCallReminder
34
- } from "./chunk-BWZJGCO6.js";
34
+ } from "./chunk-E24HT62E.js";
35
35
  import {
36
36
  ConfigManager
37
- } from "./chunk-FM5VYCXA.js";
37
+ } from "./chunk-CP6PALA4.js";
38
38
  import {
39
39
  ToolExecutor,
40
40
  ToolRegistry,
@@ -53,10 +53,10 @@ import {
53
53
  spawnAgentContext,
54
54
  theme,
55
55
  undoStack
56
- } from "./chunk-7XXZWPTN.js";
56
+ } from "./chunk-XXKWSBRC.js";
57
57
  import "./chunk-3BICTI5M.js";
58
58
  import "./chunk-2DXY7UGF.js";
59
- import "./chunk-5XHNTLRS.js";
59
+ import "./chunk-2WAF7FOX.js";
60
60
  import "./chunk-2ZD3YTVM.js";
61
61
  import {
62
62
  AGENTIC_BEHAVIOR_GUIDELINE,
@@ -79,7 +79,7 @@ import {
79
79
  SKILLS_DIR_NAME,
80
80
  VERSION,
81
81
  buildUserIdentityPrompt
82
- } from "./chunk-AWZ63EVH.js";
82
+ } from "./chunk-UF62SHR7.js";
83
83
  import {
84
84
  formatGitContextForPrompt,
85
85
  getGitContext,
@@ -1600,7 +1600,7 @@ ${text}
1600
1600
  const { join: join6 } = await import("path");
1601
1601
  const { existsSync: existsSync6 } = await import("fs");
1602
1602
  const { getGitRoot: getGitRoot2 } = await import("./git-context-7KIP4X2V.js");
1603
- const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-OCYK6U3N.js");
1603
+ const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-YEBRZDBP.js");
1604
1604
  const { approveProject, hashMcpFile } = await import("./project-trust-IFM7FXEV.js");
1605
1605
  const cwd = process.cwd();
1606
1606
  const projectRoot = getGitRoot2(cwd) ?? cwd;
@@ -2650,7 +2650,7 @@ ${hint}` : "")
2650
2650
  usage: "/test [command|filter]",
2651
2651
  async execute(args, ctx) {
2652
2652
  try {
2653
- const { executeTests } = await import("./run-tests-JMKGA7XU.js");
2653
+ const { executeTests } = await import("./run-tests-7WN5Q7YV.js");
2654
2654
  const argStr = args.join(" ").trim();
2655
2655
  let testArgs = {};
2656
2656
  if (argStr) {
@@ -6882,11 +6882,11 @@ program.command("web").description("Start Web UI server with browser-based chat
6882
6882
  console.error("Error: Invalid port number. Must be between 1 and 65535.");
6883
6883
  process.exit(1);
6884
6884
  }
6885
- const { startWebServer } = await import("./server-YY6CUP3K.js");
6885
+ const { startWebServer } = await import("./server-AKG7HG36.js");
6886
6886
  await startWebServer({ port, host: options.host });
6887
6887
  });
6888
- program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | migrate <name>)").action(async (action, username) => {
6889
- const { AuthManager } = await import("./auth-SC6KHHI3.js");
6888
+ program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | logout-all <name> | migrate <name>)").action(async (action, username) => {
6889
+ const { AuthManager } = await import("./auth-VBV7HTLQ.js");
6890
6890
  const config = new ConfigManager();
6891
6891
  const auth = new AuthManager(config.getConfigDir());
6892
6892
  if (!action || action === "list") {
@@ -6961,6 +6961,19 @@ ${users.length} user(s) registered (auth enabled):
6961
6961
  console.log(`\u2713 Password reset for '${username}'.`);
6962
6962
  return;
6963
6963
  }
6964
+ if (action === "logout-all") {
6965
+ if (!username) {
6966
+ console.error("Usage: aicli user logout-all <username>");
6967
+ process.exit(1);
6968
+ }
6969
+ const ok = auth.logoutAll(username);
6970
+ if (!ok) {
6971
+ console.error(`Error: User '${username}' not found.`);
6972
+ process.exit(1);
6973
+ }
6974
+ console.log(`\u2713 All outstanding sessions for '${username}' revoked.`);
6975
+ return;
6976
+ }
6964
6977
  if (action === "migrate") {
6965
6978
  if (!username) {
6966
6979
  console.error("Usage: aicli user migrate <username>");
@@ -6980,7 +6993,7 @@ ${users.length} user(s) registered (auth enabled):
6980
6993
  return;
6981
6994
  }
6982
6995
  console.error(`Unknown action: ${action}`);
6983
- console.error("Available: list, create, delete, reset-password, migrate");
6996
+ console.error("Available: list, create, delete, reset-password, logout-all, migrate");
6984
6997
  process.exit(1);
6985
6998
  });
6986
6999
  program.command("sessions").description("List recent conversation sessions").action(async () => {
@@ -7005,7 +7018,7 @@ program.command("sessions").description("List recent conversation sessions").act
7005
7018
  });
7006
7019
  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) => {
7007
7020
  try {
7008
- const batch = await import("./batch-QQGRQ45S.js");
7021
+ const batch = await import("./batch-Q5NQCXKN.js");
7009
7022
  switch (action) {
7010
7023
  case "submit":
7011
7024
  if (!arg) {
@@ -7048,7 +7061,7 @@ program.command("batch <action> [arg] [arg2]").description("Anthropic Message Ba
7048
7061
  }
7049
7062
  });
7050
7063
  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) => {
7051
- const { startMcpServer } = await import("./server-WWFOPWWJ.js");
7064
+ const { startMcpServer } = await import("./server-NQ5J6FAL.js");
7052
7065
  await startMcpServer({
7053
7066
  allowDestructive: !!options.allowDestructive,
7054
7067
  allowOutsideCwd: !!options.allowOutsideCwd,
@@ -7175,7 +7188,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
7175
7188
  }),
7176
7189
  config.get("customProviders")
7177
7190
  );
7178
- const { startHub } = await import("./hub-7KYFVLHM.js");
7191
+ const { startHub } = await import("./hub-DGJC2RRF.js");
7179
7192
  await startHub(
7180
7193
  {
7181
7194
  topic: topic ?? "",
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  executeTests,
4
4
  runTestsTool
5
- } from "./chunk-5XHNTLRS.js";
6
- import "./chunk-AWZ63EVH.js";
5
+ } from "./chunk-2WAF7FOX.js";
6
+ import "./chunk-UF62SHR7.js";
7
7
  import "./chunk-PDX44BCA.js";
8
8
  export {
9
9
  executeTests,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  executeTests,
3
3
  runTestsTool
4
- } from "./chunk-DEXCXFLP.js";
4
+ } from "./chunk-W45U3KQE.js";
5
5
  import "./chunk-3RG5ZIWI.js";
6
6
  export {
7
7
  executeTests,
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  AuthManager
4
- } from "./chunk-BYNY5JPB.js";
4
+ } from "./chunk-5UPFMM2A.js";
5
5
  import {
6
6
  CONTENT_ONLY_STREAM_REMINDER,
7
7
  HALLUCINATION_CORRECTION_MESSAGE,
@@ -24,10 +24,10 @@ import {
24
24
  setupProxy,
25
25
  stripPseudoToolCalls,
26
26
  stripToolCallReminder
27
- } from "./chunk-BWZJGCO6.js";
27
+ } from "./chunk-E24HT62E.js";
28
28
  import {
29
29
  ConfigManager
30
- } from "./chunk-FM5VYCXA.js";
30
+ } from "./chunk-CP6PALA4.js";
31
31
  import {
32
32
  ToolExecutor,
33
33
  ToolRegistry,
@@ -45,10 +45,10 @@ import {
45
45
  spawnAgentContext,
46
46
  truncateOutput,
47
47
  undoStack
48
- } from "./chunk-7XXZWPTN.js";
48
+ } from "./chunk-XXKWSBRC.js";
49
49
  import "./chunk-3BICTI5M.js";
50
50
  import "./chunk-2DXY7UGF.js";
51
- import "./chunk-5XHNTLRS.js";
51
+ import "./chunk-2WAF7FOX.js";
52
52
  import "./chunk-2ZD3YTVM.js";
53
53
  import {
54
54
  AGENTIC_BEHAVIOR_GUIDELINE,
@@ -68,7 +68,7 @@ import {
68
68
  SKILLS_DIR_NAME,
69
69
  VERSION,
70
70
  buildUserIdentityPrompt
71
- } from "./chunk-AWZ63EVH.js";
71
+ } from "./chunk-UF62SHR7.js";
72
72
  import {
73
73
  formatGitContextForPrompt,
74
74
  getGitContext,
@@ -539,6 +539,7 @@ var SessionHandler = class _SessionHandler {
539
539
  this.mcpManager = shared.mcpManager;
540
540
  this.skillManager = shared.skillManager;
541
541
  this.toolExecutor = new ToolExecutorWeb(shared.toolRegistry, ws);
542
+ this.toolExecutor.setSessionKey(`web-${Math.random().toString(36).slice(2)}-${Date.now()}`);
542
543
  this.currentProvider = this.config.get("defaultProvider");
543
544
  const allDefaultModels = this.config.get("defaultModels");
544
545
  try {
@@ -2411,7 +2412,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
2411
2412
  case "test": {
2412
2413
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
2413
2414
  try {
2414
- const { executeTests } = await import("./run-tests-JMKGA7XU.js");
2415
+ const { executeTests } = await import("./run-tests-7WN5Q7YV.js");
2415
2416
  const argStr = args.join(" ").trim();
2416
2417
  let testArgs = {};
2417
2418
  if (argStr) {
@@ -3467,6 +3468,27 @@ async function startWebServer(options = {}) {
3467
3468
  skillManager
3468
3469
  };
3469
3470
  const app = express();
3471
+ app.use((_req, res, next) => {
3472
+ res.setHeader(
3473
+ "Content-Security-Policy",
3474
+ [
3475
+ "default-src 'self'",
3476
+ "script-src 'self'",
3477
+ "style-src 'self' 'unsafe-inline'",
3478
+ "img-src 'self' data: blob:",
3479
+ "font-src 'self' data:",
3480
+ "connect-src 'self' ws: wss:",
3481
+ "frame-ancestors 'none'",
3482
+ "base-uri 'self'",
3483
+ "form-action 'self'"
3484
+ ].join("; ")
3485
+ );
3486
+ res.setHeader("X-Content-Type-Options", "nosniff");
3487
+ res.setHeader("Referrer-Policy", "no-referrer");
3488
+ res.setHeader("X-Frame-Options", "DENY");
3489
+ res.setHeader("Permissions-Policy", "geolocation=(), microphone=(), camera=()");
3490
+ next();
3491
+ });
3470
3492
  const server = createServer(app);
3471
3493
  const WS_MAX_PAYLOAD = 1 * 1024 * 1024;
3472
3494
  const WS_MSG_RATE_PER_SEC = 30;
@@ -3,14 +3,14 @@ import {
3
3
  ToolRegistry,
4
4
  getDangerLevel,
5
5
  schemaToJsonSchema
6
- } from "./chunk-7XXZWPTN.js";
6
+ } from "./chunk-XXKWSBRC.js";
7
7
  import "./chunk-3BICTI5M.js";
8
8
  import "./chunk-2DXY7UGF.js";
9
- import "./chunk-5XHNTLRS.js";
9
+ import "./chunk-2WAF7FOX.js";
10
10
  import "./chunk-2ZD3YTVM.js";
11
11
  import {
12
12
  VERSION
13
- } from "./chunk-AWZ63EVH.js";
13
+ } from "./chunk-UF62SHR7.js";
14
14
  import "./chunk-4BKXL7SM.js";
15
15
  import "./chunk-7ZJN4KLV.js";
16
16
  import "./chunk-KHYD3WXE.js";
@@ -23,6 +23,14 @@ import { createInterface } from "readline";
23
23
  import { resolve } from "path";
24
24
  import { realpathSync } from "fs";
25
25
  var STDIN_TOOLS = /* @__PURE__ */ new Set(["ask_user", "spawn_agent"]);
26
+ function looksLikePath(s) {
27
+ if (s.startsWith("/")) return true;
28
+ if (s.startsWith("\\\\")) return true;
29
+ if (/^[a-zA-Z]:[\\/]/.test(s)) return true;
30
+ if (s.startsWith("../") || s.startsWith("..\\")) return true;
31
+ if (s === "..") return true;
32
+ return false;
33
+ }
26
34
  var MCP_ALWAYS_DESTRUCTIVE = /* @__PURE__ */ new Set([
27
35
  "bash",
28
36
  "run_interactive",
@@ -237,19 +245,30 @@ var McpServer = class {
237
245
  validatePathArgs(toolName, args) {
238
246
  if (this.opts.allowOutsideCwd) return void 0;
239
247
  const keys = TOOL_PATH_ARGS[toolName];
240
- if (!keys) return void 0;
241
- for (const key of keys) {
242
- const value = args[key];
243
- if (value === void 0 || value === null) continue;
244
- const list = Array.isArray(value) ? value : [value];
245
- for (const entry of list) {
246
- if (typeof entry !== "string" || entry.length === 0) continue;
247
- const abs = resolve(this.sandboxRoot, entry);
248
- if (!this.isInsideSandbox(abs)) {
249
- return `Path '${entry}' escapes sandbox root '${this.sandboxRoot}'. Pass --allow-outside-cwd to permit.`;
248
+ if (keys) {
249
+ for (const key of keys) {
250
+ const value = args[key];
251
+ if (value === void 0 || value === null) continue;
252
+ const list = Array.isArray(value) ? value : [value];
253
+ for (const entry of list) {
254
+ if (typeof entry !== "string" || entry.length === 0) continue;
255
+ const abs = resolve(this.sandboxRoot, entry);
256
+ if (!this.isInsideSandbox(abs)) {
257
+ return `Path '${entry}' escapes sandbox root '${this.sandboxRoot}'. Pass --allow-outside-cwd to permit.`;
258
+ }
250
259
  }
251
260
  }
252
261
  }
262
+ const knownKeys = new Set(keys ?? []);
263
+ for (const [k, v] of Object.entries(args)) {
264
+ if (knownKeys.has(k)) continue;
265
+ if (typeof v !== "string" || v.length === 0) continue;
266
+ if (!looksLikePath(v)) continue;
267
+ const abs = resolve(this.sandboxRoot, v);
268
+ if (!this.isInsideSandbox(abs)) {
269
+ return `Path-like argument '${k}=${v}' escapes sandbox root '${this.sandboxRoot}'. Pass --allow-outside-cwd to permit.`;
270
+ }
271
+ }
253
272
  return void 0;
254
273
  }
255
274
  isInsideSandbox(abs) {
@@ -4,14 +4,14 @@ import {
4
4
  getDangerLevel,
5
5
  googleSearchContext,
6
6
  truncateOutput
7
- } from "./chunk-7XXZWPTN.js";
7
+ } from "./chunk-XXKWSBRC.js";
8
8
  import "./chunk-3BICTI5M.js";
9
9
  import "./chunk-2DXY7UGF.js";
10
- import "./chunk-5XHNTLRS.js";
10
+ import "./chunk-2WAF7FOX.js";
11
11
  import "./chunk-2ZD3YTVM.js";
12
12
  import {
13
13
  SUBAGENT_ALLOWED_TOOLS
14
- } from "./chunk-AWZ63EVH.js";
14
+ } from "./chunk-UF62SHR7.js";
15
15
  import "./chunk-4BKXL7SM.js";
16
16
  import "./chunk-7ZJN4KLV.js";
17
17
  import "./chunk-KHYD3WXE.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jinzd-ai-cli",
3
- "version": "0.4.112",
3
+ "version": "0.4.114",
4
4
  "description": "Cross-platform REPL-style AI CLI with multi-provider support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -21,6 +21,7 @@
21
21
  "dev": "tsx src/index.ts",
22
22
  "test": "vitest run",
23
23
  "test:watch": "vitest",
24
+ "test:coverage": "vitest run --coverage",
24
25
  "lint": "eslint src",
25
26
  "prepublishOnly": "npm run build",
26
27
  "patch": "node scripts/patch-sqlite.mjs",
@@ -89,8 +90,8 @@
89
90
  "tree-sitter-javascript": "^0.25.0",
90
91
  "tree-sitter-python": "^0.25.0",
91
92
  "tree-sitter-typescript": "^0.23.2",
92
- "undici": "^7.22.0",
93
- "uuid": "^11.0.5",
93
+ "undici": "^7.24.0",
94
+ "uuid": "^14.0.0",
94
95
  "web-tree-sitter": "^0.26.8",
95
96
  "ws": "^8.19.0",
96
97
  "zod": "^3.24.1"
@@ -101,12 +102,15 @@
101
102
  "@types/uuid": "^10.0.0",
102
103
  "@types/ws": "^8.18.1",
103
104
  "@yao-pkg/pkg": "^6.14.0",
104
- "electron": "^35.0.0",
105
+ "@typescript-eslint/eslint-plugin": "^8.13.0",
106
+ "@typescript-eslint/parser": "^8.13.0",
107
+ "electron": "^41.4.0",
105
108
  "electron-builder": "^26.0.0",
109
+ "eslint": "^9.13.0",
106
110
  "tsup": "^8.3.5",
107
111
  "tsx": "^4.19.2",
108
112
  "typescript": "^5.7.3",
109
- "vitest": "^2.1.8"
113
+ "vitest": "^3.2.4"
110
114
  },
111
115
  "author": "jinzd",
112
116
  "build": {
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- AuthManager
4
- } from "./chunk-BYNY5JPB.js";
5
- import "./chunk-PDX44BCA.js";
6
- export {
7
- AuthManager
8
- };