jinzd-ai-cli 0.4.7 → 0.4.9

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,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  AuthManager
4
- } from "./chunk-CPLT6CD3.js";
4
+ } from "./chunk-BYNY5JPB.js";
5
5
  export {
6
6
  AuthManager
7
7
  };
@@ -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.7";
9
+ var VERSION = "0.4.9";
10
10
  var APP_NAME = "ai-cli";
11
11
  var CONFIG_DIR_NAME = ".aicli";
12
12
  var CONFIG_FILE_NAME = "config.json";
@@ -3,7 +3,7 @@
3
3
  // src/web/auth.ts
4
4
  import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, copyFileSync } from "fs";
5
5
  import { join } from "path";
6
- import { createHmac, randomBytes, timingSafeEqual } from "crypto";
6
+ import { createHmac, randomBytes, timingSafeEqual, pbkdf2Sync } from "crypto";
7
7
  var USERS_FILE = "users.json";
8
8
  var TOKEN_EXPIRY_HOURS = 24 * 7;
9
9
  var USERS_DIR = "users";
@@ -50,7 +50,8 @@ var AuthManager = class {
50
50
  passwordHash,
51
51
  salt,
52
52
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
53
- dataDir
53
+ dataDir,
54
+ hashVersion: 2
54
55
  };
55
56
  this.db.users.push(user);
56
57
  this.save();
@@ -61,12 +62,20 @@ var AuthManager = class {
61
62
  username = username.trim().toLowerCase();
62
63
  const user = this.db.users.find((u) => u.username === username);
63
64
  if (!user) return null;
64
- const hash = this.hashPassword(password, user.salt);
65
+ const isLegacy = !user.hashVersion || user.hashVersion < 2;
66
+ const hash = isLegacy ? this.hashPasswordLegacy(password, user.salt) : this.hashPassword(password, user.salt);
65
67
  const a = Buffer.from(hash, "utf-8");
66
68
  const b = Buffer.from(user.passwordHash, "utf-8");
67
69
  if (a.length !== b.length || !timingSafeEqual(a, b)) {
68
70
  return null;
69
71
  }
72
+ if (isLegacy) {
73
+ const newSalt = randomBytes(16).toString("hex");
74
+ user.passwordHash = this.hashPassword(password, newSalt);
75
+ user.salt = newSalt;
76
+ user.hashVersion = 2;
77
+ this.save();
78
+ }
70
79
  return this.createToken(username);
71
80
  }
72
81
  /** Verify a token. Returns username or null. */
@@ -117,6 +126,7 @@ var AuthManager = class {
117
126
  const salt = randomBytes(16).toString("hex");
118
127
  user.passwordHash = this.hashPassword(newPassword, salt);
119
128
  user.salt = salt;
129
+ user.hashVersion = 2;
120
130
  this.save();
121
131
  return null;
122
132
  }
@@ -180,9 +190,13 @@ var AuthManager = class {
180
190
  mkdirSync(this.baseDir, { recursive: true });
181
191
  writeFileSync(this.usersFile, JSON.stringify(db, null, 2), "utf-8");
182
192
  }
183
- hashPassword(password, salt) {
193
+ /** Legacy hash — kept only for migrating old users (v0.2.x) */
194
+ hashPasswordLegacy(password, salt) {
184
195
  return createHmac("sha256", salt).update(password).digest("hex");
185
196
  }
197
+ hashPassword(password, salt) {
198
+ return pbkdf2Sync(password, salt, 1e5, 64, "sha512").toString("hex");
199
+ }
186
200
  createToken(username) {
187
201
  const payload = {
188
202
  username,
@@ -6,7 +6,7 @@ import {
6
6
  SUBAGENT_DEFAULT_MAX_ROUNDS,
7
7
  SUBAGENT_MAX_ROUNDS_LIMIT,
8
8
  runTestsTool
9
- } from "./chunk-UQH2IHE7.js";
9
+ } from "./chunk-U6TWB6UQ.js";
10
10
 
11
11
  // src/tools/builtin/bash.ts
12
12
  import { execSync } from "child_process";
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  EnvLoader,
4
4
  schemaToJsonSchema
5
- } from "./chunk-N3DIEZ4F.js";
5
+ } from "./chunk-DAKTIUEG.js";
6
6
  import {
7
7
  APP_NAME,
8
8
  CONFIG_DIR_NAME,
@@ -15,7 +15,7 @@ import {
15
15
  MCP_TOOL_PREFIX,
16
16
  PLUGINS_DIR_NAME,
17
17
  VERSION
18
- } from "./chunk-UQH2IHE7.js";
18
+ } from "./chunk-U6TWB6UQ.js";
19
19
 
20
20
  // src/config/config-manager.ts
21
21
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
@@ -8,7 +8,7 @@ import { platform } from "os";
8
8
  import chalk from "chalk";
9
9
 
10
10
  // src/core/constants.ts
11
- var VERSION = "0.4.7";
11
+ var VERSION = "0.4.9";
12
12
  var APP_NAME = "ai-cli";
13
13
  var CONFIG_DIR_NAME = ".aicli";
14
14
  var CONFIG_FILE_NAME = "config.json";
@@ -381,7 +381,7 @@ ${content}`);
381
381
  }
382
382
  }
383
383
  async function runTaskMode(config, providers, configManager, topic) {
384
- const { TaskOrchestrator } = await import("./task-orchestrator-RHLP2TS7.js");
384
+ const { TaskOrchestrator } = await import("./task-orchestrator-V4ZQPJYI.js");
385
385
  const orchestrator = new TaskOrchestrator(config, providers, configManager);
386
386
  let interrupted = false;
387
387
  const onSigint = () => {
package/dist/index.js CHANGED
@@ -23,7 +23,7 @@ import {
23
23
  saveDevState,
24
24
  sessionHasMeaningfulContent,
25
25
  setupProxy
26
- } from "./chunk-2DCIW6JN.js";
26
+ } from "./chunk-ETHEKIMV.js";
27
27
  import {
28
28
  ToolRegistry,
29
29
  askUserContext,
@@ -38,7 +38,7 @@ import {
38
38
  theme,
39
39
  truncateOutput,
40
40
  undoStack
41
- } from "./chunk-N3DIEZ4F.js";
41
+ } from "./chunk-DAKTIUEG.js";
42
42
  import {
43
43
  AGENTIC_BEHAVIOR_GUIDELINE,
44
44
  AUTHOR,
@@ -58,7 +58,7 @@ import {
58
58
  REPO_URL,
59
59
  SKILLS_DIR_NAME,
60
60
  VERSION
61
- } from "./chunk-UQH2IHE7.js";
61
+ } from "./chunk-U6TWB6UQ.js";
62
62
 
63
63
  // src/index.ts
64
64
  import { program } from "commander";
@@ -1914,7 +1914,7 @@ ${hint}` : "")
1914
1914
  description: "Run project tests and show structured report",
1915
1915
  usage: "/test [command|filter]",
1916
1916
  async execute(args, _ctx) {
1917
- const { executeTests } = await import("./run-tests-OVUM7ACD.js");
1917
+ const { executeTests } = await import("./run-tests-MUSKRAOS.js");
1918
1918
  const argStr = args.join(" ").trim();
1919
1919
  let testArgs = {};
1920
1920
  if (argStr) {
@@ -5524,11 +5524,11 @@ program.command("web").description("Start Web UI server with browser-based chat
5524
5524
  console.error("Error: Invalid port number. Must be between 1 and 65535.");
5525
5525
  process.exit(1);
5526
5526
  }
5527
- const { startWebServer } = await import("./server-RFDBGC3H.js");
5527
+ const { startWebServer } = await import("./server-FUXCHUIZ.js");
5528
5528
  await startWebServer({ port, host: options.host });
5529
5529
  });
5530
5530
  program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | migrate <name>)").action(async (action, username) => {
5531
- const { AuthManager } = await import("./auth-TWWF22YZ.js");
5531
+ const { AuthManager } = await import("./auth-MSUWO6SE.js");
5532
5532
  const config = new ConfigManager();
5533
5533
  const auth = new AuthManager(config.getConfigDir());
5534
5534
  if (!action || action === "list") {
@@ -5757,7 +5757,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
5757
5757
  }),
5758
5758
  config.get("customProviders")
5759
5759
  );
5760
- const { startHub } = await import("./hub-XNIWPTZE.js");
5760
+ const { startHub } = await import("./hub-JACBMIVV.js");
5761
5761
  await startHub(
5762
5762
  {
5763
5763
  topic: topic ?? "",
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  executeTests,
3
3
  runTestsTool
4
- } from "./chunk-EIBYVUCB.js";
4
+ } from "./chunk-75RK4GJP.js";
5
5
  export {
6
6
  executeTests,
7
7
  runTestsTool
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  executeTests,
4
4
  runTestsTool
5
- } from "./chunk-UQH2IHE7.js";
5
+ } from "./chunk-U6TWB6UQ.js";
6
6
  export {
7
7
  executeTests,
8
8
  runTestsTool
@@ -18,10 +18,10 @@ import {
18
18
  renderDiff,
19
19
  runHook,
20
20
  setupProxy
21
- } from "./chunk-2DCIW6JN.js";
21
+ } from "./chunk-ETHEKIMV.js";
22
22
  import {
23
23
  AuthManager
24
- } from "./chunk-CPLT6CD3.js";
24
+ } from "./chunk-BYNY5JPB.js";
25
25
  import {
26
26
  ToolRegistry,
27
27
  askUserContext,
@@ -32,7 +32,7 @@ import {
32
32
  spawnAgentContext,
33
33
  truncateOutput,
34
34
  undoStack
35
- } from "./chunk-N3DIEZ4F.js";
35
+ } from "./chunk-DAKTIUEG.js";
36
36
  import {
37
37
  AGENTIC_BEHAVIOR_GUIDELINE,
38
38
  CONTEXT_FILE_CANDIDATES,
@@ -44,14 +44,14 @@ import {
44
44
  PLAN_MODE_SYSTEM_ADDON,
45
45
  SKILLS_DIR_NAME,
46
46
  VERSION
47
- } from "./chunk-UQH2IHE7.js";
47
+ } from "./chunk-U6TWB6UQ.js";
48
48
 
49
49
  // src/web/server.ts
50
50
  import express from "express";
51
51
  import { createServer } from "http";
52
52
  import { WebSocketServer } from "ws";
53
- import { join as join3, dirname, resolve as resolve2, relative } from "path";
54
- import { existsSync as existsSync4, readFileSync as readFileSync4, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
53
+ import { join as join3, dirname, resolve as resolve2, relative, sep } from "path";
54
+ import { existsSync as existsSync4, readFileSync as readFileSync4, readdirSync as readdirSync2, statSync as statSync2, realpathSync } from "fs";
55
55
  import { networkInterfaces } from "os";
56
56
 
57
57
  // src/web/tool-executor-web.ts
@@ -1440,7 +1440,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
1440
1440
  case "test": {
1441
1441
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
1442
1442
  try {
1443
- const { executeTests } = await import("./run-tests-OVUM7ACD.js");
1443
+ const { executeTests } = await import("./run-tests-MUSKRAOS.js");
1444
1444
  const argStr = args.join(" ").trim();
1445
1445
  let testArgs = {};
1446
1446
  if (argStr) {
@@ -2062,6 +2062,30 @@ async function startWebServer(options = {}) {
2062
2062
  const app = express();
2063
2063
  const server = createServer(app);
2064
2064
  const wss = new WebSocketServer({ server });
2065
+ const authManager = new AuthManager(config.getConfigDir());
2066
+ if (authManager.isEnabled()) {
2067
+ console.log(` Auth: ${authManager.listUsers().length} user(s) registered`);
2068
+ } else {
2069
+ console.log(` Auth: disabled (no users registered)`);
2070
+ }
2071
+ const requireAuth = (req, res, next) => {
2072
+ if (!authManager.isEnabled()) return next();
2073
+ const authHeader = req.headers.authorization;
2074
+ const token = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : null;
2075
+ const cookieToken = parseCookie(req.headers.cookie ?? "")["aicli_token"];
2076
+ const effectiveToken = token ?? cookieToken;
2077
+ if (!effectiveToken) {
2078
+ res.status(401).json({ error: "Authentication required" });
2079
+ return;
2080
+ }
2081
+ const username = authManager.verifyToken(effectiveToken);
2082
+ if (!username) {
2083
+ res.status(401).json({ error: "Invalid or expired token" });
2084
+ return;
2085
+ }
2086
+ req._authUser = username;
2087
+ next();
2088
+ };
2065
2089
  const moduleDir = getModuleDir();
2066
2090
  let clientDir = join3(moduleDir, "web", "client");
2067
2091
  if (!existsSync4(clientDir)) {
@@ -2105,13 +2129,21 @@ async function startWebServer(options = {}) {
2105
2129
  }
2106
2130
  const token = authManager.login(username, password);
2107
2131
  console.log(` \u2713 User registered via API: ${username}`);
2132
+ res.cookie("aicli_token", token, { httpOnly: true, sameSite: "strict", maxAge: 7 * 24 * 3600 * 1e3 });
2108
2133
  res.json({ success: true, token, username });
2109
2134
  });
2110
- app.get("/api/files", (req, res) => {
2135
+ app.get("/api/files", requireAuth, (req, res) => {
2111
2136
  const cwd = process.cwd();
2112
2137
  const prefix = req.query.prefix || "";
2113
2138
  const targetDir = join3(cwd, prefix);
2114
- if (!resolve2(targetDir).startsWith(resolve2(cwd))) {
2139
+ try {
2140
+ const canonicalTarget = realpathSync(resolve2(targetDir));
2141
+ const canonicalCwd = realpathSync(resolve2(cwd));
2142
+ if (!canonicalTarget.startsWith(canonicalCwd + sep) && canonicalTarget !== canonicalCwd) {
2143
+ res.json({ files: [] });
2144
+ return;
2145
+ }
2146
+ } catch {
2115
2147
  res.json({ files: [] });
2116
2148
  return;
2117
2149
  }
@@ -2128,7 +2160,7 @@ async function startWebServer(options = {}) {
2128
2160
  res.json({ files: [] });
2129
2161
  }
2130
2162
  });
2131
- app.get("/api/sessions", (_req, res) => {
2163
+ app.get("/api/sessions", requireAuth, (_req, res) => {
2132
2164
  try {
2133
2165
  const list = sessions.listSessions();
2134
2166
  res.json({
@@ -2145,7 +2177,7 @@ async function startWebServer(options = {}) {
2145
2177
  res.json({ sessions: [] });
2146
2178
  }
2147
2179
  });
2148
- app.get("/api/file-content", (req, res) => {
2180
+ app.get("/api/file-content", requireAuth, (req, res) => {
2149
2181
  const filePath = req.query.path;
2150
2182
  if (!filePath) {
2151
2183
  res.json({ error: "Missing path" });
@@ -2153,8 +2185,15 @@ async function startWebServer(options = {}) {
2153
2185
  }
2154
2186
  const cwd = process.cwd();
2155
2187
  const fullPath = resolve2(join3(cwd, filePath));
2156
- if (!fullPath.startsWith(resolve2(cwd))) {
2157
- res.json({ error: "Access denied" });
2188
+ try {
2189
+ const canonicalFull = realpathSync(fullPath);
2190
+ const canonicalCwd = realpathSync(resolve2(cwd));
2191
+ if (!canonicalFull.startsWith(canonicalCwd + sep) && canonicalFull !== canonicalCwd) {
2192
+ res.json({ error: "Access denied" });
2193
+ return;
2194
+ }
2195
+ } catch {
2196
+ res.json({ error: "File not found" });
2158
2197
  return;
2159
2198
  }
2160
2199
  try {
@@ -2165,16 +2204,10 @@ async function startWebServer(options = {}) {
2165
2204
  }
2166
2205
  const content = readFileSync4(fullPath, "utf-8");
2167
2206
  res.json({ content, size: stat.size });
2168
- } catch (err) {
2169
- res.json({ error: `Cannot read: ${err.message}` });
2207
+ } catch {
2208
+ res.json({ error: "Cannot read file" });
2170
2209
  }
2171
2210
  });
2172
- const authManager = new AuthManager(config.getConfigDir());
2173
- if (authManager.isEnabled()) {
2174
- console.log(` Auth: ${authManager.listUsers().length} user(s) registered`);
2175
- } else {
2176
- console.log(` Auth: disabled (no users registered)`);
2177
- }
2178
2211
  const userResources = /* @__PURE__ */ new Map();
2179
2212
  function getUserShared(username) {
2180
2213
  const cached = userResources.get(username);
@@ -2203,7 +2236,8 @@ async function startWebServer(options = {}) {
2203
2236
  const qMark = urlStr.indexOf("?");
2204
2237
  const params = new URLSearchParams(qMark >= 0 ? urlStr.slice(qMark + 1) : "");
2205
2238
  const tabId = params.get("tabId") || `tab-${Date.now()}`;
2206
- const token = params.get("token") || "";
2239
+ const cookies = parseCookie(req.headers.cookie ?? "");
2240
+ const token = cookies["aicli_token"] || "";
2207
2241
  const existing = handlers.get(tabId);
2208
2242
  if (existing) {
2209
2243
  existing.onDisconnect();
@@ -2257,7 +2291,22 @@ async function startWebServer(options = {}) {
2257
2291
  handler = new SessionHandler(ws, userShared);
2258
2292
  handlers.set(tabId, handler);
2259
2293
  console.log(` \u2713 User registered & connected: ${username} (tab: ${tabId.slice(0, 12)})`);
2260
- ws.send(JSON.stringify({ type: "auth_result", success: true, token: newToken, username }));
2294
+ ws.send(JSON.stringify({ type: "auth_result", success: true, token: newToken, username, setCookie: true }));
2295
+ return;
2296
+ }
2297
+ if (action === "token") {
2298
+ const { token: clientToken } = parsed;
2299
+ const verifiedUser = authManager.verifyToken(clientToken);
2300
+ if (!verifiedUser) {
2301
+ ws.send(JSON.stringify({ type: "auth_result", success: false, error: "Invalid or expired token" }));
2302
+ return;
2303
+ }
2304
+ authenticatedUser = verifiedUser;
2305
+ const userShared = getUserShared(verifiedUser);
2306
+ handler = new SessionHandler(ws, userShared);
2307
+ handlers.set(tabId, handler);
2308
+ console.log(` \u2713 Token auth: ${verifiedUser} (tab: ${tabId.slice(0, 12)})`);
2309
+ ws.send(JSON.stringify({ type: "auth_result", success: true, token: clientToken, username: verifiedUser }));
2261
2310
  return;
2262
2311
  }
2263
2312
  if (action === "login") {
@@ -2271,7 +2320,7 @@ async function startWebServer(options = {}) {
2271
2320
  handler = new SessionHandler(ws, userShared);
2272
2321
  handlers.set(tabId, handler);
2273
2322
  console.log(` \u2713 User logged in: ${username} (tab: ${tabId.slice(0, 12)})`);
2274
- ws.send(JSON.stringify({ type: "auth_result", success: true, token: loginToken, username }));
2323
+ ws.send(JSON.stringify({ type: "auth_result", success: true, token: loginToken, username, setCookie: true }));
2275
2324
  return;
2276
2325
  }
2277
2326
  ws.send(JSON.stringify({ type: "auth_result", success: false, error: "Unknown auth action" }));
@@ -2366,6 +2415,14 @@ function loadProjectMcpConfig() {
2366
2415
  return null;
2367
2416
  }
2368
2417
  }
2418
+ function parseCookie(cookie) {
2419
+ const result = {};
2420
+ for (const part of cookie.split(";")) {
2421
+ const [key, ...rest] = part.trim().split("=");
2422
+ if (key) result[key.trim()] = rest.join("=").trim();
2423
+ }
2424
+ return result;
2425
+ }
2369
2426
  async function openBrowser(url) {
2370
2427
  try {
2371
2428
  const { exec } = await import("child_process");
@@ -4,10 +4,10 @@ import {
4
4
  getDangerLevel,
5
5
  googleSearchContext,
6
6
  truncateOutput
7
- } from "./chunk-N3DIEZ4F.js";
7
+ } from "./chunk-DAKTIUEG.js";
8
8
  import {
9
9
  SUBAGENT_ALLOWED_TOOLS
10
- } from "./chunk-UQH2IHE7.js";
10
+ } from "./chunk-U6TWB6UQ.js";
11
11
 
12
12
  // src/hub/task-orchestrator.ts
13
13
  import { createInterface } from "readline";
@@ -48,6 +48,9 @@ ${role.persona}
48
48
  ## Your Task
49
49
  ${task}
50
50
 
51
+ ## Working Directory
52
+ You MUST work in the current directory (${process.cwd()}). Do NOT create sub-directories like "my-project" or "todo-app". Place all files directly in the current directory or in logical sub-folders (e.g. public/, src/, db/).
53
+
51
54
  ## Rules
52
55
  1. Focus exclusively on completing the task described above.
53
56
  2. Use tools to read, create, and modify files as needed.
@@ -55,6 +58,7 @@ ${task}
55
58
  4. Destructive operations (rm -rf, etc.) will be automatically blocked.
56
59
  5. You cannot interact with users \u2014 work independently based on the task description.
57
60
  6. Use the same language as the task description.
61
+ 7. If files from a previous task already exist, build on them \u2014 do NOT recreate from scratch.
58
62
 
59
63
  ## CRITICAL \u2014 When to Stop
60
64
  Once you have completed the task (files created/modified, code written, tests passing), you MUST immediately respond with a plain text summary. Do NOT call any more tools after the work is done. Your text summary should include:
@@ -76,14 +76,19 @@ let reconnectDelay = 1000; // Start at 1s, exponential backoff
76
76
 
77
77
  function connect() {
78
78
  const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
79
- const tokenParam = authToken ? `&token=${encodeURIComponent(authToken)}` : '';
80
- ws = new WebSocket(`${protocol}//${location.host}?tabId=${tabId}${tokenParam}`);
79
+ // Security: do NOT put token in URL (visible in logs/referrer). Cookie is sent automatically.
80
+ // For reconnection, also send token as first message in case cookie is blocked.
81
+ ws = new WebSocket(`${protocol}//${location.host}?tabId=${tabId}`);
81
82
 
82
83
  ws.onopen = () => {
83
84
  connected = true;
84
85
  reconnectDelay = 1000; // Reset backoff on success
85
86
  connectionStatus.textContent = '🟢 Connected';
86
87
  connectionStatus.className = 'status-connected';
88
+ // Send stored token for auth (in case cookie is not available)
89
+ if (authToken) {
90
+ ws.send(JSON.stringify({ type: 'auth', action: 'token', token: authToken }));
91
+ }
87
92
  requestSessionList();
88
93
  checkAuthStatus();
89
94
  // Start heartbeat ping every 30s
@@ -1886,6 +1891,11 @@ function handleAuthResult(msg) {
1886
1891
  authUsername = msg.username;
1887
1892
  localStorage.setItem('aicli-auth-token', msg.token);
1888
1893
  localStorage.setItem('aicli-auth-user', msg.username);
1894
+ // Set cookie for secure WebSocket auth (httpOnly cookie set by server on HTTP login,
1895
+ // but for WS-only login, set a JS cookie as fallback)
1896
+ if (msg.setCookie && msg.token) {
1897
+ document.cookie = `aicli_token=${encodeURIComponent(msg.token)}; path=/; max-age=${7 * 24 * 3600}; SameSite=Strict`;
1898
+ }
1889
1899
  hideAuthScreen();
1890
1900
  // Request session list now that we're authenticated
1891
1901
  requestSessionList();
@@ -1913,6 +1923,7 @@ function handleLogout() {
1913
1923
  authToken = '';
1914
1924
  authUsername = '';
1915
1925
  localStorage.removeItem('aicli-auth-token');
1926
+ document.cookie = 'aicli_token=; path=/; max-age=0'; // Clear auth cookie
1916
1927
  localStorage.removeItem('aicli-auth-user');
1917
1928
  sessionStorage.removeItem('aicli-active-session');
1918
1929
  // Reconnect — server will send auth_required
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jinzd-ai-cli",
3
- "version": "0.4.7",
3
+ "version": "0.4.9",
4
4
  "description": "Cross-platform REPL-style AI CLI with multi-provider support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",