jinzd-ai-cli 0.4.113 → 0.4.115

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.
@@ -36,7 +36,7 @@ import {
36
36
  VERSION,
37
37
  buildUserIdentityPrompt,
38
38
  runTestsTool
39
- } from "./chunk-2WAEM5B6.js";
39
+ } from "./chunk-KQZU2VS5.js";
40
40
  import {
41
41
  hasSemanticIndex,
42
42
  semanticSearch
@@ -604,10 +604,20 @@ var ClaudeProvider = class extends BaseProvider {
604
604
  ]
605
605
  };
606
606
  async initialize(apiKey, options) {
607
- this.client = new Anthropic({
607
+ const clientOptions = {
608
608
  apiKey,
609
609
  baseURL: options?.baseUrl
610
- });
610
+ };
611
+ const proxyUrl = options?.proxy;
612
+ try {
613
+ const { Agent, ProxyAgent, fetch: undiciFetch } = await import("undici");
614
+ const STREAM_BODY_TIMEOUT = 30 * 60 * 1e3;
615
+ const STREAM_HEADERS_TIMEOUT = 5 * 60 * 1e3;
616
+ const dispatcher = proxyUrl ? new ProxyAgent({ uri: proxyUrl, bodyTimeout: STREAM_BODY_TIMEOUT, headersTimeout: STREAM_HEADERS_TIMEOUT }) : new Agent({ bodyTimeout: STREAM_BODY_TIMEOUT, headersTimeout: STREAM_HEADERS_TIMEOUT });
617
+ clientOptions.fetch = ((url, init) => undiciFetch(url, { ...init, dispatcher }));
618
+ } catch {
619
+ }
620
+ this.client = new Anthropic(clientOptions);
611
621
  }
612
622
  /**
613
623
  * 将内部 MessageContentPart[] 格式转换为 Anthropic SDK 期望的 ContentBlockParam[]。
@@ -1464,13 +1474,20 @@ var OpenAICompatibleProvider = class extends BaseProvider {
1464
1474
  timeout: this.defaultTimeout
1465
1475
  };
1466
1476
  const proxyUrl = options?.proxy;
1467
- if (proxyUrl) {
1468
- try {
1469
- const { ProxyAgent, fetch: undiciFetch } = await import("undici");
1470
- const agent = new ProxyAgent({ uri: proxyUrl });
1471
- clientOptions.fetch = ((url, init) => undiciFetch(url, { ...init, dispatcher: agent }));
1472
- } catch {
1473
- }
1477
+ try {
1478
+ const { Agent, ProxyAgent, fetch: undiciFetch } = await import("undici");
1479
+ const STREAM_BODY_TIMEOUT = 30 * 60 * 1e3;
1480
+ const STREAM_HEADERS_TIMEOUT = 5 * 60 * 1e3;
1481
+ const dispatcher = proxyUrl ? new ProxyAgent({
1482
+ uri: proxyUrl,
1483
+ bodyTimeout: STREAM_BODY_TIMEOUT,
1484
+ headersTimeout: STREAM_HEADERS_TIMEOUT
1485
+ }) : new Agent({
1486
+ bodyTimeout: STREAM_BODY_TIMEOUT,
1487
+ headersTimeout: STREAM_HEADERS_TIMEOUT
1488
+ });
1489
+ clientOptions.fetch = ((url, init) => undiciFetch(url, { ...init, dispatcher }));
1490
+ } catch {
1474
1491
  }
1475
1492
  this.client = new OpenAI(clientOptions);
1476
1493
  }
@@ -2268,6 +2285,40 @@ function peelMetaNarration(content) {
2268
2285
  }
2269
2286
  return out.trim();
2270
2287
  }
2288
+ var META_NARRATION_HARD_MARKERS = [
2289
+ /\[⚠️\s*CONTENT GENERATION MODE\]/,
2290
+ /CONTENT_ONLY_STREAM_REMINDER\b/,
2291
+ /<system-reminder>/i
2292
+ ];
2293
+ var META_NARRATION_HEURISTICS = [
2294
+ /\bthe user (?:is asking me|wants me|is requesting|expects me)\b/i,
2295
+ /\blet me (?:re-?read|re-?consider|reconsider|think about|carefully (?:re-?read|consider))\b/i,
2296
+ /\bI'?m (?:in (?:a )?content-only|in CONTENT-ONLY|currently in)\b/i,
2297
+ /\bI think (?:there might be|I should|I cannot|the (?:user|best)|maybe)\b/i,
2298
+ /\bWait,?\s+let me\b/i,
2299
+ /\bActually,?\s+I\b/i,
2300
+ /\bI need to be honest with the user\b/i,
2301
+ /\bI(?:'m| am) in a special mode\b/i,
2302
+ /\bGiven that I cannot\b/i
2303
+ ];
2304
+ function detectMetaNarration(content) {
2305
+ if (!content) return null;
2306
+ const head = content.slice(0, 2e3);
2307
+ for (const re of META_NARRATION_HARD_MARKERS) {
2308
+ if (re.test(head)) return re.source;
2309
+ }
2310
+ if (/^#{1,3}\s+\S/m.test(head)) return null;
2311
+ let hits = 0;
2312
+ let firstMatch = "";
2313
+ for (const re of META_NARRATION_HEURISTICS) {
2314
+ if (re.test(head)) {
2315
+ hits++;
2316
+ if (!firstMatch) firstMatch = re.source;
2317
+ if (hits >= 2) return `meta-narration:${firstMatch}`;
2318
+ }
2319
+ }
2320
+ return null;
2321
+ }
2271
2322
  function looksLikeDocumentBody(content) {
2272
2323
  if (!content || content.length < 200) return false;
2273
2324
  if (/^#{1,6}\s+\S/m.test(content)) return true;
@@ -3742,10 +3793,36 @@ function currentAbortSignal() {
3742
3793
  return controller.signal;
3743
3794
  }
3744
3795
 
3796
+ // src/tools/session-context.ts
3797
+ import { AsyncLocalStorage } from "async_hooks";
3798
+ var als = new AsyncLocalStorage();
3799
+ var DEFAULT_SESSION_KEY = "__default__";
3800
+ function getCurrentSessionKey() {
3801
+ const ctx = als.getStore();
3802
+ if (!ctx || !ctx.sessionKey) return DEFAULT_SESSION_KEY;
3803
+ return ctx.sessionKey;
3804
+ }
3805
+ function runWithSessionKey(sessionKey, fn) {
3806
+ const key = sessionKey && sessionKey.length > 0 ? sessionKey : DEFAULT_SESSION_KEY;
3807
+ return als.run({ sessionKey: key }, fn);
3808
+ }
3809
+
3745
3810
  // src/tools/builtin/bash.ts
3746
3811
  var IS_WINDOWS = platform() === "win32";
3747
3812
  var SHELL = IS_WINDOWS ? "powershell.exe" : process.env["SHELL"] ?? "/bin/bash";
3748
- var persistentCwd = process.cwd();
3813
+ var cwdBySession = /* @__PURE__ */ new Map();
3814
+ function getCwd() {
3815
+ const key = getCurrentSessionKey();
3816
+ let cwd = cwdBySession.get(key);
3817
+ if (!cwd) {
3818
+ cwd = process.cwd();
3819
+ cwdBySession.set(key, cwd);
3820
+ }
3821
+ return cwd;
3822
+ }
3823
+ function setCwd(next) {
3824
+ cwdBySession.set(getCurrentSessionKey(), next);
3825
+ }
3749
3826
  var bashTool = {
3750
3827
  definition: {
3751
3828
  name: "bash",
@@ -3790,17 +3867,19 @@ Important rules:
3790
3867
  if (!command.trim()) {
3791
3868
  throw new ToolError("bash", "command is required");
3792
3869
  }
3793
- if (!existsSync4(persistentCwd)) {
3870
+ let currentCwd = getCwd();
3871
+ if (!existsSync4(currentCwd)) {
3794
3872
  const fallback = process.cwd();
3795
3873
  process.stderr.write(
3796
- `[bash] Previous cwd "${persistentCwd}" no longer exists, reset to "${fallback}"
3874
+ `[bash] Previous cwd "${currentCwd}" no longer exists, reset to "${fallback}"
3797
3875
  `
3798
3876
  );
3799
- persistentCwd = fallback;
3877
+ currentCwd = fallback;
3878
+ setCwd(fallback);
3800
3879
  }
3801
- let effectiveCwd = persistentCwd;
3880
+ let effectiveCwd = currentCwd;
3802
3881
  if (cwdArg) {
3803
- const resolved = resolve(persistentCwd, cwdArg);
3882
+ const resolved = resolve(currentCwd, cwdArg);
3804
3883
  if (!existsSync4(resolved)) {
3805
3884
  throw new ToolError(
3806
3885
  "bash",
@@ -3808,7 +3887,7 @@ Important rules:
3808
3887
  );
3809
3888
  }
3810
3889
  effectiveCwd = resolved;
3811
- persistentCwd = resolved;
3890
+ setCwd(resolved);
3812
3891
  }
3813
3892
  let actualCommand;
3814
3893
  if (IS_WINDOWS) {
@@ -3857,7 +3936,7 @@ Important rules:
3857
3936
  }
3858
3937
  updateCwdFromCommand(command, effectiveCwd);
3859
3938
  pushBashUndoEntries(beforeSnapshot, parsedTargetsBefore, effectiveCwd);
3860
- const result = IS_WINDOWS && Buffer.isBuffer(stdout) ? stdout.toString("utf-8") : stdout;
3939
+ const result = Buffer.isBuffer(stdout) ? stdout.toString("utf-8") : String(stdout ?? "");
3861
3940
  return result || "(command completed with no output)";
3862
3941
  } catch (err) {
3863
3942
  pushBashUndoEntries(beforeSnapshot, parsedTargetsBefore, effectiveCwd);
@@ -4064,7 +4143,7 @@ function updateCwdFromCommand(command, baseCwd) {
4064
4143
  try {
4065
4144
  const newDir = resolve(baseCwd, target);
4066
4145
  if (existsSync4(newDir)) {
4067
- persistentCwd = newDir;
4146
+ setCwd(newDir);
4068
4147
  }
4069
4148
  } catch {
4070
4149
  }
@@ -4885,6 +4964,16 @@ var ToolExecutor = class {
4885
4964
  * 通过 /yolo 命令切换。destructive 操作仍会显示警告但不阻塞。
4886
4965
  */
4887
4966
  sessionAutoApprove = false;
4967
+ /**
4968
+ * Logical session key used to scope per-session state in stateful tools
4969
+ * (currently only `bash`'s persistent cwd). Web mode sets this per-tab so
4970
+ * concurrent tabs don't share cwd. CLI/REPL leaves it undefined (the bash
4971
+ * tool falls back to a default key).
4972
+ */
4973
+ sessionKey;
4974
+ setSessionKey(key) {
4975
+ this.sessionKey = key;
4976
+ }
4888
4977
  /**
4889
4978
  * 由外部(repl.ts SIGINT handler)调用,将当前 confirm() 等待视为用户按 N 取消。
4890
4979
  * 若当前没有 confirm() 进行中,无操作。
@@ -4917,6 +5006,9 @@ var ToolExecutor = class {
4917
5006
  if (opts.defaultPermission) this.defaultPermission = opts.defaultPermission;
4918
5007
  }
4919
5008
  async execute(call) {
5009
+ return runWithSessionKey(this.sessionKey, () => this.executeInner(call));
5010
+ }
5011
+ async executeInner(call) {
4920
5012
  const tool = this.registry.get(call.name);
4921
5013
  if (!tool) {
4922
5014
  return {
@@ -10046,6 +10138,7 @@ var SessionHandler = class _SessionHandler {
10046
10138
  this.mcpManager = shared.mcpManager;
10047
10139
  this.skillManager = shared.skillManager;
10048
10140
  this.toolExecutor = new ToolExecutorWeb(shared.toolRegistry, ws);
10141
+ this.toolExecutor.setSessionKey(`web-${Math.random().toString(36).slice(2)}-${Date.now()}`);
10049
10142
  this.currentProvider = this.config.get("defaultProvider");
10050
10143
  const allDefaultModels = this.config.get("defaultModels");
10051
10144
  try {
@@ -10800,6 +10893,31 @@ ${summaryResult.content}`,
10800
10893
  await new Promise((resolve7, reject) => {
10801
10894
  fileStream.end((err) => err ? reject(err) : resolve7());
10802
10895
  });
10896
+ const metaMatch = detectMetaNarration(fullContent);
10897
+ if (metaMatch) {
10898
+ try {
10899
+ unlinkSync4(saveToFile);
10900
+ } catch {
10901
+ }
10902
+ isError = true;
10903
+ summary = `[save_last_response REJECTED] Your output was internal reasoning / meta-narration (e.g. "Let me re-read\u2026", "the user is asking me to\u2026") instead of the requested document body (matched: ${metaMatch}). ${saveToFile} was NOT saved.
10904
+
10905
+ This fresh stream has NO tools. Produce ONLY the document body: start with a markdown heading and write the full content. Do NOT narrate that you will produce the document \u2014 produce it.`;
10906
+ if (teeUsage) {
10907
+ roundUsage.inputTokens += teeUsage.inputTokens;
10908
+ roundUsage.outputTokens += teeUsage.outputTokens;
10909
+ roundUsage.cacheCreationTokens += teeUsage.cacheCreationTokens ?? 0;
10910
+ roundUsage.cacheReadTokens += teeUsage.cacheReadTokens ?? 0;
10911
+ }
10912
+ this.send({
10913
+ type: "tool_call_result",
10914
+ callId: call.id,
10915
+ result: summary,
10916
+ isError: true,
10917
+ endTime: Date.now()
10918
+ });
10919
+ return { content: "", summary, isError: true };
10920
+ }
10803
10921
  const pseudoMatch = detectPseudoToolCalls(fullContent);
10804
10922
  if (pseudoMatch) {
10805
10923
  const cleaned = stripPseudoToolCalls(fullContent);
@@ -10837,9 +10955,13 @@ ${summaryResult.content}`,
10837
10955
  } catch {
10838
10956
  }
10839
10957
  }
10958
+ try {
10959
+ unlinkSync4(saveToFile);
10960
+ } catch {
10961
+ }
10840
10962
  isError = true;
10841
10963
  const msg = err instanceof Error ? err.message : String(err);
10842
- summary = `[save_last_response failed] ${msg}`;
10964
+ summary = `[save_last_response failed] streaming was interrupted: ${msg}. ${saveToFile} (partial) was deleted. Retry \u2014 and consider producing a more compact output (split very large reports across multiple save_last_response calls if the previous attempt timed out).`;
10843
10965
  }
10844
10966
  this.send({
10845
10967
  type: "tool_call_result",
@@ -11918,7 +12040,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
11918
12040
  case "test": {
11919
12041
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
11920
12042
  try {
11921
- const { executeTests } = await import("./run-tests-YOBAV24V.js");
12043
+ const { executeTests } = await import("./run-tests-7VYL7OVA.js");
11922
12044
  const argStr = args.join(" ").trim();
11923
12045
  let testArgs = {};
11924
12046
  if (argStr) {
@@ -12902,12 +13024,16 @@ async function setupProxy(configProxy) {
12902
13024
  }
12903
13025
 
12904
13026
  // src/web/auth.ts
12905
- import { existsSync as existsSync21, readFileSync as readFileSync14, writeFileSync as writeFileSync9, mkdirSync as mkdirSync10, readdirSync as readdirSync10, copyFileSync } from "fs";
13027
+ import { existsSync as existsSync21, readFileSync as readFileSync14, writeFileSync as writeFileSync9, mkdirSync as mkdirSync10, readdirSync as readdirSync10, copyFileSync, renameSync as renameSync2, unlinkSync as unlinkSync5 } from "fs";
12906
13028
  import { join as join14 } from "path";
12907
13029
  import { createHmac, randomBytes, timingSafeEqual, pbkdf2Sync } from "crypto";
12908
13030
  var USERS_FILE = "users.json";
12909
- var TOKEN_EXPIRY_HOURS = 24 * 7;
13031
+ var TOKEN_EXPIRY_HOURS = 24;
13032
+ var TOKEN_EXPIRY_MS = TOKEN_EXPIRY_HOURS * 3600 * 1e3;
12910
13033
  var USERS_DIR = "users";
13034
+ var LOGIN_MAX_FAILS = 5;
13035
+ var LOGIN_LOCKOUT_MS = 15 * 60 * 1e3;
13036
+ var loginAttempts = /* @__PURE__ */ new Map();
12911
13037
  var AuthManager = class {
12912
13038
  usersFile;
12913
13039
  baseDir;
@@ -12958,16 +13084,30 @@ var AuthManager = class {
12958
13084
  this.save();
12959
13085
  return null;
12960
13086
  }
12961
- /** Authenticate user. Returns JWT token or null. */
13087
+ /**
13088
+ * Authenticate user. Returns JWT token or null on failure.
13089
+ *
13090
+ * Audit closure (5th audit, v0.4.114): integrates failed-login lockout
13091
+ * (CWE-307). After {@link LOGIN_MAX_FAILS} consecutive failures within a
13092
+ * lockout window, further attempts return null without checking the
13093
+ * password (avoiding pbkdf2 work + leaking timing). Successful login
13094
+ * resets the counter.
13095
+ */
12962
13096
  login(username, password) {
12963
13097
  username = username.trim().toLowerCase();
13098
+ const lockState = this.getLockState(username);
13099
+ if (lockState.locked) return null;
12964
13100
  const user = this.db.users.find((u) => u.username === username);
12965
- if (!user) return null;
13101
+ if (!user) {
13102
+ this.recordFailedLogin(username);
13103
+ return null;
13104
+ }
12966
13105
  const isLegacy = !user.hashVersion || user.hashVersion < 2;
12967
13106
  const hash = isLegacy ? this.hashPasswordLegacy(password, user.salt) : this.hashPassword(password, user.salt);
12968
13107
  const a = Buffer.from(hash, "utf-8");
12969
13108
  const b = Buffer.from(user.passwordHash, "utf-8");
12970
13109
  if (a.length !== b.length || !timingSafeEqual(a, b)) {
13110
+ this.recordFailedLogin(username);
12971
13111
  return null;
12972
13112
  }
12973
13113
  if (isLegacy) {
@@ -12977,8 +13117,34 @@ var AuthManager = class {
12977
13117
  user.hashVersion = 2;
12978
13118
  this.save();
12979
13119
  }
13120
+ loginAttempts.delete(username);
12980
13121
  return this.createToken(username);
12981
13122
  }
13123
+ /**
13124
+ * Returns current lockout state for a username and lazily expires it.
13125
+ * Exposed (read-only) for tests and the `aicli user` CLI status output.
13126
+ */
13127
+ getLockState(username) {
13128
+ username = username.trim().toLowerCase();
13129
+ const state = loginAttempts.get(username);
13130
+ if (!state) return { locked: false, remainingMs: 0, fails: 0 };
13131
+ if (state.lockedUntil > 0 && Date.now() >= state.lockedUntil) {
13132
+ loginAttempts.delete(username);
13133
+ return { locked: false, remainingMs: 0, fails: 0 };
13134
+ }
13135
+ if (state.lockedUntil > 0) {
13136
+ return { locked: true, remainingMs: state.lockedUntil - Date.now(), fails: state.fails };
13137
+ }
13138
+ return { locked: false, remainingMs: 0, fails: state.fails };
13139
+ }
13140
+ recordFailedLogin(username) {
13141
+ const state = loginAttempts.get(username) ?? { fails: 0, lockedUntil: 0 };
13142
+ state.fails += 1;
13143
+ if (state.fails >= LOGIN_MAX_FAILS) {
13144
+ state.lockedUntil = Date.now() + LOGIN_LOCKOUT_MS;
13145
+ }
13146
+ loginAttempts.set(username, state);
13147
+ }
12982
13148
  /** Verify a token. Returns username or null. */
12983
13149
  verifyToken(token) {
12984
13150
  try {
@@ -12991,12 +13157,29 @@ var AuthManager = class {
12991
13157
  Buffer.from(payloadB64, "base64url").toString("utf-8")
12992
13158
  );
12993
13159
  if (Date.now() > payload.exp) return null;
12994
- if (!this.db.users.find((u) => u.username === payload.username)) return null;
13160
+ const user = this.db.users.find((u) => u.username === payload.username);
13161
+ if (!user) return null;
13162
+ if (user.tokensRevokedBefore && (!payload.iat || payload.iat < user.tokensRevokedBefore)) {
13163
+ return null;
13164
+ }
12995
13165
  return payload.username;
12996
13166
  } catch {
12997
13167
  return null;
12998
13168
  }
12999
13169
  }
13170
+ /**
13171
+ * Revoke every outstanding token for the given user by bumping the
13172
+ * `tokensRevokedBefore` watermark to now. Audit closure (5th audit,
13173
+ * v0.4.114). Returns true if the user existed.
13174
+ */
13175
+ logoutAll(username) {
13176
+ username = username.trim().toLowerCase();
13177
+ const user = this.db.users.find((u) => u.username === username);
13178
+ if (!user) return false;
13179
+ user.tokensRevokedBefore = Date.now() + 1;
13180
+ this.save();
13181
+ return true;
13182
+ }
13000
13183
  /** Get user's data directory (absolute path) */
13001
13184
  getUserDataDir(username) {
13002
13185
  const user = this.db.users.find((u) => u.username === username);
@@ -13028,6 +13211,7 @@ var AuthManager = class {
13028
13211
  user.passwordHash = this.hashPassword(newPassword, salt);
13029
13212
  user.salt = salt;
13030
13213
  user.hashVersion = 2;
13214
+ user.tokensRevokedBefore = Date.now() + 1;
13031
13215
  this.save();
13032
13216
  return null;
13033
13217
  }
@@ -13089,7 +13273,17 @@ var AuthManager = class {
13089
13273
  }
13090
13274
  saveDB(db) {
13091
13275
  mkdirSync10(this.baseDir, { recursive: true });
13092
- writeFileSync9(this.usersFile, JSON.stringify(db, null, 2), "utf-8");
13276
+ const tmp = `${this.usersFile}.tmp`;
13277
+ try {
13278
+ writeFileSync9(tmp, JSON.stringify(db, null, 2), "utf-8");
13279
+ renameSync2(tmp, this.usersFile);
13280
+ } catch (err) {
13281
+ try {
13282
+ unlinkSync5(tmp);
13283
+ } catch {
13284
+ }
13285
+ throw err;
13286
+ }
13093
13287
  }
13094
13288
  /** Legacy hash — kept only for migrating old users (v0.2.x) */
13095
13289
  hashPasswordLegacy(password, salt) {
@@ -13099,9 +13293,11 @@ var AuthManager = class {
13099
13293
  return pbkdf2Sync(password, salt, 1e5, 64, "sha512").toString("hex");
13100
13294
  }
13101
13295
  createToken(username) {
13296
+ const now = Date.now();
13102
13297
  const payload = {
13103
13298
  username,
13104
- exp: Date.now() + TOKEN_EXPIRY_HOURS * 3600 * 1e3
13299
+ iat: now,
13300
+ exp: now + TOKEN_EXPIRY_HOURS * 3600 * 1e3
13105
13301
  };
13106
13302
  const payloadB64 = Buffer.from(JSON.stringify(payload), "utf-8").toString("base64url");
13107
13303
  const signature = this.sign(payloadB64);
@@ -13196,6 +13392,27 @@ async function startWebServer(options = {}) {
13196
13392
  skillManager
13197
13393
  };
13198
13394
  const app = express();
13395
+ app.use((_req, res, next) => {
13396
+ res.setHeader(
13397
+ "Content-Security-Policy",
13398
+ [
13399
+ "default-src 'self'",
13400
+ "script-src 'self'",
13401
+ "style-src 'self' 'unsafe-inline'",
13402
+ "img-src 'self' data: blob:",
13403
+ "font-src 'self' data:",
13404
+ "connect-src 'self' ws: wss:",
13405
+ "frame-ancestors 'none'",
13406
+ "base-uri 'self'",
13407
+ "form-action 'self'"
13408
+ ].join("; ")
13409
+ );
13410
+ res.setHeader("X-Content-Type-Options", "nosniff");
13411
+ res.setHeader("Referrer-Policy", "no-referrer");
13412
+ res.setHeader("X-Frame-Options", "DENY");
13413
+ res.setHeader("Permissions-Policy", "geolocation=(), microphone=(), camera=()");
13414
+ next();
13415
+ });
13199
13416
  const server = createServer(app);
13200
13417
  const WS_MAX_PAYLOAD = 1 * 1024 * 1024;
13201
13418
  const WS_MSG_RATE_PER_SEC = 30;
@@ -13280,7 +13497,7 @@ async function startWebServer(options = {}) {
13280
13497
  }
13281
13498
  const token = authManager.login(username, password);
13282
13499
  console.log(` \u2713 User registered via API: ${username}${firstRun ? " (first-run)" : ""}`);
13283
- res.cookie("aicli_token", token, { httpOnly: true, sameSite: "strict", maxAge: 7 * 24 * 3600 * 1e3 });
13500
+ res.cookie("aicli_token", token, { httpOnly: true, sameSite: "strict", maxAge: TOKEN_EXPIRY_MS });
13284
13501
  res.json({ success: true, username });
13285
13502
  });
13286
13503
  app.post("/api/auth/login", (req, res) => {
@@ -13294,7 +13511,7 @@ async function startWebServer(options = {}) {
13294
13511
  res.status(401).json({ error: "Invalid username or password" });
13295
13512
  return;
13296
13513
  }
13297
- res.cookie("aicli_token", token, { httpOnly: true, sameSite: "strict", maxAge: 7 * 24 * 3600 * 1e3 });
13514
+ res.cookie("aicli_token", token, { httpOnly: true, sameSite: "strict", maxAge: TOKEN_EXPIRY_MS });
13298
13515
  res.json({ success: true, username });
13299
13516
  });
13300
13517
  app.post("/api/auth/logout", (_req, res) => {
@@ -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-KZISH5DT.js");
389
+ const { TaskOrchestrator } = await import("./task-orchestrator-24IGVXYP.js");
390
390
  const orchestrator = new TaskOrchestrator(config, providers, configManager);
391
391
  let interrupted = false;
392
392
  const onSigint = () => {