jinzd-ai-cli 0.4.8 → 0.4.10

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
  };
@@ -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.8";
11
+ var VERSION = "0.4.10";
12
12
  var APP_NAME = "ai-cli";
13
13
  var CONFIG_DIR_NAME = ".aicli";
14
14
  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-G3YURBV7.js";
9
+ } from "./chunk-4EUTAZ6Q.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-GDMXHX27.js";
5
+ } from "./chunk-IIVTIEWB.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-G3YURBV7.js";
18
+ } from "./chunk-4EUTAZ6Q.js";
19
19
 
20
20
  // src/config/config-manager.ts
21
21
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
@@ -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.8";
9
+ var VERSION = "0.4.10";
10
10
  var APP_NAME = "ai-cli";
11
11
  var CONFIG_DIR_NAME = ".aicli";
12
12
  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-OXDO5UWG.js");
384
+ const { TaskOrchestrator } = await import("./task-orchestrator-W56DGIZ4.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-T2W7CI24.js";
26
+ } from "./chunk-J3J44O4N.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-GDMXHX27.js";
41
+ } from "./chunk-IIVTIEWB.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-G3YURBV7.js";
61
+ } from "./chunk-4EUTAZ6Q.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-L2NQAT2M.js");
1917
+ const { executeTests } = await import("./run-tests-YBNVSXTV.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-JYSNPBYK.js");
5527
+ const { startWebServer } = await import("./server-YLN3YUUI.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-V27PRAPU.js");
5760
+ const { startHub } = await import("./hub-B3YT75IB.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-56MYFIG4.js";
4
+ } from "./chunk-WC6IEG2I.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-G3YURBV7.js";
5
+ } from "./chunk-4EUTAZ6Q.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-T2W7CI24.js";
21
+ } from "./chunk-J3J44O4N.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-GDMXHX27.js";
35
+ } from "./chunk-IIVTIEWB.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-G3YURBV7.js";
47
+ } from "./chunk-4EUTAZ6Q.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
@@ -996,8 +996,6 @@ Tokens: in=${this.sessionTokenUsage.inputTokens} out=${this.sessionTokenUsage.ou
996
996
  if (found) {
997
997
  this.sessions.loadSession(found.id);
998
998
  this.sessionTokenUsage = { inputTokens: 0, outputTokens: 0 };
999
- if (found.provider) this.currentProvider = found.provider;
1000
- if (found.model) this.currentModel = found.model;
1001
999
  this.send({ type: "info", message: `Loaded session: ${found.id.slice(0, 8)} "${found.title ?? ""}" (${found.messageCount} messages)` });
1002
1000
  this.sendSessionMessages();
1003
1001
  this.sendStatus();
@@ -1440,7 +1438,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
1440
1438
  case "test": {
1441
1439
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
1442
1440
  try {
1443
- const { executeTests } = await import("./run-tests-L2NQAT2M.js");
1441
+ const { executeTests } = await import("./run-tests-YBNVSXTV.js");
1444
1442
  const argStr = args.join(" ").trim();
1445
1443
  let testArgs = {};
1446
1444
  if (argStr) {
@@ -2062,6 +2060,30 @@ async function startWebServer(options = {}) {
2062
2060
  const app = express();
2063
2061
  const server = createServer(app);
2064
2062
  const wss = new WebSocketServer({ server });
2063
+ const authManager = new AuthManager(config.getConfigDir());
2064
+ if (authManager.isEnabled()) {
2065
+ console.log(` Auth: ${authManager.listUsers().length} user(s) registered`);
2066
+ } else {
2067
+ console.log(` Auth: disabled (no users registered)`);
2068
+ }
2069
+ const requireAuth = (req, res, next) => {
2070
+ if (!authManager.isEnabled()) return next();
2071
+ const authHeader = req.headers.authorization;
2072
+ const token = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : null;
2073
+ const cookieToken = parseCookie(req.headers.cookie ?? "")["aicli_token"];
2074
+ const effectiveToken = token ?? cookieToken;
2075
+ if (!effectiveToken) {
2076
+ res.status(401).json({ error: "Authentication required" });
2077
+ return;
2078
+ }
2079
+ const username = authManager.verifyToken(effectiveToken);
2080
+ if (!username) {
2081
+ res.status(401).json({ error: "Invalid or expired token" });
2082
+ return;
2083
+ }
2084
+ req._authUser = username;
2085
+ next();
2086
+ };
2065
2087
  const moduleDir = getModuleDir();
2066
2088
  let clientDir = join3(moduleDir, "web", "client");
2067
2089
  if (!existsSync4(clientDir)) {
@@ -2105,13 +2127,21 @@ async function startWebServer(options = {}) {
2105
2127
  }
2106
2128
  const token = authManager.login(username, password);
2107
2129
  console.log(` \u2713 User registered via API: ${username}`);
2130
+ res.cookie("aicli_token", token, { httpOnly: true, sameSite: "strict", maxAge: 7 * 24 * 3600 * 1e3 });
2108
2131
  res.json({ success: true, token, username });
2109
2132
  });
2110
- app.get("/api/files", (req, res) => {
2133
+ app.get("/api/files", requireAuth, (req, res) => {
2111
2134
  const cwd = process.cwd();
2112
2135
  const prefix = req.query.prefix || "";
2113
2136
  const targetDir = join3(cwd, prefix);
2114
- if (!resolve2(targetDir).startsWith(resolve2(cwd))) {
2137
+ try {
2138
+ const canonicalTarget = realpathSync(resolve2(targetDir));
2139
+ const canonicalCwd = realpathSync(resolve2(cwd));
2140
+ if (!canonicalTarget.startsWith(canonicalCwd + sep) && canonicalTarget !== canonicalCwd) {
2141
+ res.json({ files: [] });
2142
+ return;
2143
+ }
2144
+ } catch {
2115
2145
  res.json({ files: [] });
2116
2146
  return;
2117
2147
  }
@@ -2128,7 +2158,7 @@ async function startWebServer(options = {}) {
2128
2158
  res.json({ files: [] });
2129
2159
  }
2130
2160
  });
2131
- app.get("/api/sessions", (_req, res) => {
2161
+ app.get("/api/sessions", requireAuth, (_req, res) => {
2132
2162
  try {
2133
2163
  const list = sessions.listSessions();
2134
2164
  res.json({
@@ -2145,7 +2175,7 @@ async function startWebServer(options = {}) {
2145
2175
  res.json({ sessions: [] });
2146
2176
  }
2147
2177
  });
2148
- app.get("/api/file-content", (req, res) => {
2178
+ app.get("/api/file-content", requireAuth, (req, res) => {
2149
2179
  const filePath = req.query.path;
2150
2180
  if (!filePath) {
2151
2181
  res.json({ error: "Missing path" });
@@ -2153,8 +2183,15 @@ async function startWebServer(options = {}) {
2153
2183
  }
2154
2184
  const cwd = process.cwd();
2155
2185
  const fullPath = resolve2(join3(cwd, filePath));
2156
- if (!fullPath.startsWith(resolve2(cwd))) {
2157
- res.json({ error: "Access denied" });
2186
+ try {
2187
+ const canonicalFull = realpathSync(fullPath);
2188
+ const canonicalCwd = realpathSync(resolve2(cwd));
2189
+ if (!canonicalFull.startsWith(canonicalCwd + sep) && canonicalFull !== canonicalCwd) {
2190
+ res.json({ error: "Access denied" });
2191
+ return;
2192
+ }
2193
+ } catch {
2194
+ res.json({ error: "File not found" });
2158
2195
  return;
2159
2196
  }
2160
2197
  try {
@@ -2165,16 +2202,10 @@ async function startWebServer(options = {}) {
2165
2202
  }
2166
2203
  const content = readFileSync4(fullPath, "utf-8");
2167
2204
  res.json({ content, size: stat.size });
2168
- } catch (err) {
2169
- res.json({ error: `Cannot read: ${err.message}` });
2205
+ } catch {
2206
+ res.json({ error: "Cannot read file" });
2170
2207
  }
2171
2208
  });
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
2209
  const userResources = /* @__PURE__ */ new Map();
2179
2210
  function getUserShared(username) {
2180
2211
  const cached = userResources.get(username);
@@ -2203,7 +2234,8 @@ async function startWebServer(options = {}) {
2203
2234
  const qMark = urlStr.indexOf("?");
2204
2235
  const params = new URLSearchParams(qMark >= 0 ? urlStr.slice(qMark + 1) : "");
2205
2236
  const tabId = params.get("tabId") || `tab-${Date.now()}`;
2206
- const token = params.get("token") || "";
2237
+ const cookies = parseCookie(req.headers.cookie ?? "");
2238
+ const token = cookies["aicli_token"] || "";
2207
2239
  const existing = handlers.get(tabId);
2208
2240
  if (existing) {
2209
2241
  existing.onDisconnect();
@@ -2257,7 +2289,22 @@ async function startWebServer(options = {}) {
2257
2289
  handler = new SessionHandler(ws, userShared);
2258
2290
  handlers.set(tabId, handler);
2259
2291
  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 }));
2292
+ ws.send(JSON.stringify({ type: "auth_result", success: true, token: newToken, username, setCookie: true }));
2293
+ return;
2294
+ }
2295
+ if (action === "token") {
2296
+ const { token: clientToken } = parsed;
2297
+ const verifiedUser = authManager.verifyToken(clientToken);
2298
+ if (!verifiedUser) {
2299
+ ws.send(JSON.stringify({ type: "auth_result", success: false, error: "Invalid or expired token" }));
2300
+ return;
2301
+ }
2302
+ authenticatedUser = verifiedUser;
2303
+ const userShared = getUserShared(verifiedUser);
2304
+ handler = new SessionHandler(ws, userShared);
2305
+ handlers.set(tabId, handler);
2306
+ console.log(` \u2713 Token auth: ${verifiedUser} (tab: ${tabId.slice(0, 12)})`);
2307
+ ws.send(JSON.stringify({ type: "auth_result", success: true, token: clientToken, username: verifiedUser }));
2261
2308
  return;
2262
2309
  }
2263
2310
  if (action === "login") {
@@ -2271,7 +2318,7 @@ async function startWebServer(options = {}) {
2271
2318
  handler = new SessionHandler(ws, userShared);
2272
2319
  handlers.set(tabId, handler);
2273
2320
  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 }));
2321
+ ws.send(JSON.stringify({ type: "auth_result", success: true, token: loginToken, username, setCookie: true }));
2275
2322
  return;
2276
2323
  }
2277
2324
  ws.send(JSON.stringify({ type: "auth_result", success: false, error: "Unknown auth action" }));
@@ -2366,6 +2413,14 @@ function loadProjectMcpConfig() {
2366
2413
  return null;
2367
2414
  }
2368
2415
  }
2416
+ function parseCookie(cookie) {
2417
+ const result = {};
2418
+ for (const part of cookie.split(";")) {
2419
+ const [key, ...rest] = part.trim().split("=");
2420
+ if (key) result[key.trim()] = rest.join("=").trim();
2421
+ }
2422
+ return result;
2423
+ }
2369
2424
  async function openBrowser(url) {
2370
2425
  try {
2371
2426
  const { exec } = await import("child_process");
@@ -4,10 +4,10 @@ import {
4
4
  getDangerLevel,
5
5
  googleSearchContext,
6
6
  truncateOutput
7
- } from "./chunk-GDMXHX27.js";
7
+ } from "./chunk-IIVTIEWB.js";
8
8
  import {
9
9
  SUBAGENT_ALLOWED_TOOLS
10
- } from "./chunk-G3YURBV7.js";
10
+ } from "./chunk-4EUTAZ6Q.js";
11
11
 
12
12
  // src/hub/task-orchestrator.ts
13
13
  import { createInterface } from "readline";
@@ -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.8",
3
+ "version": "0.4.10",
4
4
  "description": "Cross-platform REPL-style AI CLI with multi-provider support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",