jinzd-ai-cli 0.4.307 → 0.4.309
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.
- package/README.md +2 -1
- package/README.zh-CN.md +1 -1
- package/THIRD_PARTY_NOTICES.md +3 -0
- package/dist/{agent-client-6GX6QQDU.js → agent-client-TIEAYZXV.js} +4 -1
- package/dist/{auth-PE3Z5OHS.js → auth-QTQNZ6IX.js} +2 -2
- package/dist/{batch-HMADXNOD.js → batch-H4WWSZQ3.js} +3 -3
- package/dist/{chat-index-2D5GK2DB.js → chat-index-7CLR4CBG.js} +4 -3
- package/dist/{chat-index-DGUOUUQW.js → chat-index-RLVLQRRC.js} +3 -2
- package/dist/{chunk-CIM2PV2F.js → chunk-2E5W2E64.js} +2 -2
- package/dist/chunk-2XMUCC42.js +67 -0
- package/dist/{chunk-VAV2IJ3R.js → chunk-3CNPJLVI.js} +1 -1
- package/dist/{chunk-KRCYR46A.js → chunk-3GEO2LPO.js} +264 -55
- package/dist/{chunk-OAXLMF3U.js → chunk-3YVNDYOY.js} +1 -1
- package/dist/{chunk-5GQL4TSZ.js → chunk-4BXTGXZ6.js} +2 -2
- package/dist/{chunk-RYQBBEMM.js → chunk-4PR5AVJ5.js} +115 -19
- package/dist/{chunk-GHWZVPUV.js → chunk-6JHACXB3.js} +7 -5
- package/dist/{chunk-IUVDZVZ4.js → chunk-AXTLYOA7.js} +24 -17
- package/dist/{chunk-XJOK5353.js → chunk-B3MPYI4E.js} +2 -2
- package/dist/chunk-BMLQPSHP.js +69 -0
- package/dist/{chunk-NRSYBKWA.js → chunk-C7CKDFJS.js} +1 -1
- package/dist/{chunk-SJFZPS65.js → chunk-EAVX3RLD.js} +1 -1
- package/dist/{chunk-VXEF2SWG.js → chunk-EHMMBIZT.js} +16 -12
- package/dist/chunk-FVXNEZEW.js +161 -0
- package/dist/{chunk-AHL6KOGM.js → chunk-HXINWG37.js} +2 -2
- package/dist/{chunk-3CKJOTVM.js → chunk-IW6Q66WS.js} +4 -4
- package/dist/chunk-JJFJWD35.js +162 -0
- package/dist/{chunk-NYNSYLFY.js → chunk-JL44HGZC.js} +1 -1
- package/dist/{chunk-3TBXHLSC.js → chunk-K4F7QEGW.js} +1 -1
- package/dist/{chunk-NUJTNH76.js → chunk-LBWKMTT3.js} +1 -1
- package/dist/{chunk-YKJO3SI4.js → chunk-LONB36SP.js} +16 -3
- package/dist/{chunk-WQ3OMEC4.js → chunk-M6HLYG33.js} +1 -1
- package/dist/{chunk-I4GQRMFV.js → chunk-M6YV5KSS.js} +6 -4
- package/dist/{chunk-KBDKKHXB.js → chunk-O6CGOVD4.js} +2 -2
- package/dist/{chunk-L2RJPVHV.js → chunk-O7MMYJAD.js} +266 -512
- package/dist/chunk-QMFMST2T.js +39 -0
- package/dist/{chunk-AQF6IXSR.js → chunk-RBCKX2FZ.js} +11 -6
- package/dist/{chunk-4W6VDVQU.js → chunk-RLERORUI.js} +246 -56
- package/dist/{chunk-MI4SFZGX.js → chunk-RMEJR3AW.js} +5 -2
- package/dist/{chunk-UFW4S3RU.js → chunk-RVMCY6DP.js} +1 -1
- package/dist/{chunk-LF67HWWR.js → chunk-RWRZVQCB.js} +1 -1
- package/dist/{chunk-NQJSPMY2.js → chunk-SCG2VBPB.js} +4 -4
- package/dist/chunk-SXIJLSVF.js +41 -0
- package/dist/{chunk-YFDQIUQ3.js → chunk-T42GXE7Y.js} +18 -18
- package/dist/chunk-TDQW2YHK.js +65 -0
- package/dist/{chunk-45UVJXFQ.js → chunk-W2UOVGRC.js} +1 -1
- package/dist/{ci-QD2Q7NWY.js → ci-H6GPHG4T.js} +7 -7
- package/dist/{ci-format-42XIZO5P.js → ci-format-U47GSGYU.js} +2 -2
- package/dist/{constants-5CMEFZQB.js → constants-GYJTB6KI.js} +1 -1
- package/dist/{constants-NMASHFDL.js → constants-SNJR6546.js} +1 -1
- package/dist/doctor-cli-J6LAQ5LU.js +18 -0
- package/dist/electron-server.js +922 -842
- package/dist/file-checkpoint-64A2AXHE.js +7 -0
- package/dist/{file-checkpoint-NKBHGC7L.js → file-checkpoint-VVPUJNCI.js} +2 -1
- package/dist/{hub-ZLF6GMQD.js → hub-G5FMMTVZ.js} +3 -3
- package/dist/{hub-server-CBKCP5VD.js → hub-server-JLC7CNP7.js} +55 -17
- package/dist/index.js +48 -42
- package/dist/{indexer-P3FPRTPJ.js → indexer-GKIWSSRP.js} +3 -3
- package/dist/indexer-VRXDZ74A.js +10 -0
- package/dist/{persist-S77WRVLD.js → persist-ZZXRWFBX.js} +4 -4
- package/dist/{persistent-memory-GJE7HKVK.js → persistent-memory-EXJBXL33.js} +3 -3
- package/dist/{persistent-memory-MBSBTBGY.js → persistent-memory-LEE454DW.js} +3 -3
- package/dist/{pr-SXRW24JG.js → pr-6PUHVJ6K.js} +7 -7
- package/dist/{project-trust-MUG325AW.js → project-trust-6CJB5ZMX.js} +1 -1
- package/dist/{project-trust-NKYHL3VZ.js → project-trust-KGG2BWKC.js} +1 -1
- package/dist/run-tests-2C7MWZBN.js +12 -0
- package/dist/run-tests-33ZVQE3Q.js +13 -0
- package/dist/{semantic-7GM5M4MU.js → semantic-N227CLSC.js} +4 -4
- package/dist/{semantic-QOBAXEQ4.js → semantic-TRQAZSRY.js} +4 -4
- package/dist/{server-GGEO7SA2.js → server-AJH5TBVO.js} +231 -103
- package/dist/{server-MO3EU2XD.js → server-CYAB56O3.js} +16 -14
- package/dist/{store-SIKHCUYJ.js → store-2N6PPPBP.js} +2 -2
- package/dist/{store-CMSSWZF7.js → store-76VVFDXM.js} +2 -2
- package/dist/{task-orchestrator-JGEEQ2OE.js → task-orchestrator-HNRV3YUD.js} +15 -14
- package/dist/{usage-B7RIAPPT.js → usage-ZN5DU6Z7.js} +4 -4
- package/dist/{vector-store-JAXWDZYW.js → vector-store-T2DVUMY4.js} +2 -2
- package/dist/{vector-store-PWIOQFTP.js → vector-store-V6UGEYWK.js} +2 -2
- package/dist/web/client/app.js +7 -0
- package/dist/web/client/app.js.br +0 -0
- package/dist/web/client/app.js.gz +0 -0
- package/dist/web/client/util.js +14 -23
- package/dist/web/client/util.js.br +0 -0
- package/dist/web/client/util.js.gz +0 -0
- package/dist/web/client/vendor/DOMPurify-LICENSE.txt +202 -0
- package/dist/web/client/vendor/purify.es.mjs +2722 -0
- package/dist/web/client/vendor/purify.es.mjs.br +0 -0
- package/dist/web/client/vendor/purify.es.mjs.gz +0 -0
- package/package.json +4 -2
- package/dist/chunk-4BKXL7SM.js +0 -98
- package/dist/chunk-6BUTA5VW.js +0 -38
- package/dist/chunk-IW3Q7AE5.js +0 -40
- package/dist/chunk-SKET65WZ.js +0 -96
- package/dist/doctor-cli-IOVSHBDI.js +0 -18
- package/dist/file-checkpoint-CGH6OJVI.js +0 -6
- package/dist/indexer-FPKY7LCT.js +0 -10
- package/dist/run-tests-EQCAMFQE.js +0 -10
- package/dist/run-tests-V3N7YV3P.js +0 -9
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
atomicWriteFileSync
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-BMLQPSHP.js";
|
|
5
5
|
|
|
6
6
|
// src/web/auth.ts
|
|
7
|
-
import { existsSync, readFileSync,
|
|
7
|
+
import { existsSync, readFileSync, mkdirSync, readdirSync, openSync, closeSync, unlinkSync } from "fs";
|
|
8
8
|
import { isAbsolute, join, relative } from "path";
|
|
9
|
-
import { createHmac, randomBytes, timingSafeEqual, pbkdf2Sync } from "crypto";
|
|
9
|
+
import { createHmac, randomBytes, timingSafeEqual, pbkdf2Sync, pbkdf2 } from "crypto";
|
|
10
10
|
var USERS_FILE = "users.json";
|
|
11
11
|
var TOKEN_EXPIRY_HOURS = 24;
|
|
12
12
|
var TOKEN_EXPIRY_MS = TOKEN_EXPIRY_HOURS * 3600 * 1e3;
|
|
@@ -21,6 +21,7 @@ var AuthManager = class {
|
|
|
21
21
|
usersFile;
|
|
22
22
|
baseDir;
|
|
23
23
|
db;
|
|
24
|
+
loadedRaw;
|
|
24
25
|
constructor(baseDir) {
|
|
25
26
|
this.baseDir = baseDir;
|
|
26
27
|
this.usersFile = join(baseDir, USERS_FILE);
|
|
@@ -29,6 +30,7 @@ var AuthManager = class {
|
|
|
29
30
|
// ── Public API ─────────────────────────────────────────────────
|
|
30
31
|
/** Check if any users exist (first-run detection) */
|
|
31
32
|
hasUsers() {
|
|
33
|
+
this.refresh();
|
|
32
34
|
return this.db.users.length > 0;
|
|
33
35
|
}
|
|
34
36
|
/** Check if auth is enabled (has users.json with at least 1 user) */
|
|
@@ -37,6 +39,18 @@ var AuthManager = class {
|
|
|
37
39
|
}
|
|
38
40
|
/** Register a new user. Returns error string or null on success. */
|
|
39
41
|
register(username, password) {
|
|
42
|
+
return this.registerPrepared(username, password);
|
|
43
|
+
}
|
|
44
|
+
async registerAsync(username, password) {
|
|
45
|
+
if (typeof username !== "string" || typeof password !== "string" || password.length > 1024) return "Invalid username or password";
|
|
46
|
+
if (!/^[a-z0-9_-]{2,32}$/i.test(username.trim()) || password.length < 4) return "Invalid username or password";
|
|
47
|
+
const salt = randomBytes(16).toString("hex");
|
|
48
|
+
const hash = await hashPasswordAsync(password, salt);
|
|
49
|
+
return this.registerPrepared(username, password, { salt, hash });
|
|
50
|
+
}
|
|
51
|
+
registerPrepared(username, password, prepared) {
|
|
52
|
+
this.refresh();
|
|
53
|
+
if (typeof username !== "string" || typeof password !== "string" || password.length > 1024) return "Invalid username or password";
|
|
40
54
|
username = username.trim().toLowerCase();
|
|
41
55
|
if (!username || username.length < 2 || username.length > 32) {
|
|
42
56
|
return "Username must be 2-32 characters";
|
|
@@ -50,11 +64,12 @@ var AuthManager = class {
|
|
|
50
64
|
if (this.db.users.find((u) => u.username === username)) {
|
|
51
65
|
return "Username already exists";
|
|
52
66
|
}
|
|
53
|
-
const salt = randomBytes(16).toString("hex");
|
|
54
|
-
const passwordHash = this.hashPassword(password, salt);
|
|
67
|
+
const salt = prepared?.salt ?? randomBytes(16).toString("hex");
|
|
68
|
+
const passwordHash = prepared?.hash ?? this.hashPassword(password, salt);
|
|
55
69
|
const dataDir = join(USERS_DIR, username);
|
|
56
70
|
const fullDataDir = join(this.baseDir, dataDir);
|
|
57
|
-
mkdirSync(
|
|
71
|
+
mkdirSync(fullDataDir, { recursive: true, mode: 448 });
|
|
72
|
+
mkdirSync(join(fullDataDir, "history"), { recursive: true, mode: 448 });
|
|
58
73
|
const user = {
|
|
59
74
|
username,
|
|
60
75
|
passwordHash,
|
|
@@ -77,6 +92,8 @@ var AuthManager = class {
|
|
|
77
92
|
* resets the counter.
|
|
78
93
|
*/
|
|
79
94
|
login(username, password) {
|
|
95
|
+
this.refresh();
|
|
96
|
+
if (!validCredentials(username, password)) return null;
|
|
80
97
|
username = username.trim().toLowerCase();
|
|
81
98
|
const lockState = this.getLockState(username);
|
|
82
99
|
if (lockState.locked) return null;
|
|
@@ -100,19 +117,51 @@ var AuthManager = class {
|
|
|
100
117
|
user.hashVersion = 2;
|
|
101
118
|
this.save();
|
|
102
119
|
}
|
|
103
|
-
loginAttempts.delete(username);
|
|
120
|
+
loginAttempts.delete(this.attemptKey(username));
|
|
104
121
|
return this.createToken(username);
|
|
105
122
|
}
|
|
106
123
|
/**
|
|
107
124
|
* Returns current lockout state for a username and lazily expires it.
|
|
108
125
|
* Exposed (read-only) for tests and the `aicli user` CLI status output.
|
|
109
126
|
*/
|
|
127
|
+
async loginAsync(username, password) {
|
|
128
|
+
this.refresh();
|
|
129
|
+
if (!validCredentials(username, password)) return null;
|
|
130
|
+
username = username.trim().toLowerCase();
|
|
131
|
+
if (this.getLockState(username).locked) return null;
|
|
132
|
+
const user = this.db.users.find((u) => u.username === username);
|
|
133
|
+
if (!user) {
|
|
134
|
+
this.recordFailedLogin(username);
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
const snapshot = JSON.stringify(user);
|
|
138
|
+
const legacy = !user.hashVersion || user.hashVersion < 2;
|
|
139
|
+
const hash = legacy ? this.hashPasswordLegacy(password, user.salt) : await hashPasswordAsync(password, user.salt);
|
|
140
|
+
const salt = legacy ? randomBytes(16).toString("hex") : user.salt;
|
|
141
|
+
const upgraded = legacy ? await hashPasswordAsync(password, salt) : hash;
|
|
142
|
+
this.refresh();
|
|
143
|
+
const current = this.db.users.find((u) => u.username === username);
|
|
144
|
+
if (!current || JSON.stringify(current) !== snapshot || this.getLockState(username).locked) return null;
|
|
145
|
+
const a = Buffer.from(hash), b = Buffer.from(current.passwordHash);
|
|
146
|
+
if (a.length !== b.length || !timingSafeEqual(a, b)) {
|
|
147
|
+
this.recordFailedLogin(username);
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
if (legacy) {
|
|
151
|
+
current.salt = salt;
|
|
152
|
+
current.passwordHash = upgraded;
|
|
153
|
+
current.hashVersion = 2;
|
|
154
|
+
this.save();
|
|
155
|
+
}
|
|
156
|
+
loginAttempts.delete(this.attemptKey(username));
|
|
157
|
+
return this.createToken(username);
|
|
158
|
+
}
|
|
110
159
|
getLockState(username) {
|
|
111
160
|
username = username.trim().toLowerCase();
|
|
112
|
-
const state = loginAttempts.get(username);
|
|
161
|
+
const state = loginAttempts.get(this.attemptKey(username));
|
|
113
162
|
if (!state) return { locked: false, remainingMs: 0, fails: 0 };
|
|
114
|
-
if (
|
|
115
|
-
loginAttempts.delete(username);
|
|
163
|
+
if (Date.now() >= state.expiresAt) {
|
|
164
|
+
loginAttempts.delete(this.attemptKey(username));
|
|
116
165
|
return { locked: false, remainingMs: 0, fails: 0 };
|
|
117
166
|
}
|
|
118
167
|
if (state.lockedUntil > 0) {
|
|
@@ -120,8 +169,15 @@ var AuthManager = class {
|
|
|
120
169
|
}
|
|
121
170
|
return { locked: false, remainingMs: 0, fails: state.fails };
|
|
122
171
|
}
|
|
172
|
+
attemptKey(username) {
|
|
173
|
+
return this.usersFile + "\0" + username;
|
|
174
|
+
}
|
|
123
175
|
recordFailedLogin(username) {
|
|
124
|
-
|
|
176
|
+
username = this.attemptKey(username);
|
|
177
|
+
const now = Date.now();
|
|
178
|
+
for (const [key, value] of loginAttempts) if (value.expiresAt <= now) loginAttempts.delete(key);
|
|
179
|
+
if (!loginAttempts.has(username) && loginAttempts.size >= 1e4) loginAttempts.delete(loginAttempts.keys().next().value);
|
|
180
|
+
const state = loginAttempts.get(username) ?? { fails: 0, lockedUntil: 0, expiresAt: now + LOGIN_LOCKOUT_MS };
|
|
125
181
|
state.fails += 1;
|
|
126
182
|
if (state.fails >= LOGIN_MAX_FAILS) {
|
|
127
183
|
state.lockedUntil = Date.now() + LOGIN_LOCKOUT_MS;
|
|
@@ -131,6 +187,8 @@ var AuthManager = class {
|
|
|
131
187
|
/** Verify a token. Returns username or null. */
|
|
132
188
|
verifyToken(token) {
|
|
133
189
|
try {
|
|
190
|
+
this.refresh();
|
|
191
|
+
if (typeof token !== "string" || token.length > 4096) return null;
|
|
134
192
|
const parts = token.split(".");
|
|
135
193
|
if (parts.length !== 2) return null;
|
|
136
194
|
const [payloadB64, signature] = parts;
|
|
@@ -141,7 +199,7 @@ var AuthManager = class {
|
|
|
141
199
|
const payload = JSON.parse(
|
|
142
200
|
Buffer.from(payloadB64, "base64url").toString("utf-8")
|
|
143
201
|
);
|
|
144
|
-
if (Date.now()
|
|
202
|
+
if (!Number.isFinite(payload.exp) || Date.now() >= payload.exp) return null;
|
|
145
203
|
const user = this.db.users.find((u) => u.username === payload.username);
|
|
146
204
|
if (!user) return null;
|
|
147
205
|
if (user.tokensRevokedBefore && (!payload.iat || payload.iat < user.tokensRevokedBefore)) {
|
|
@@ -158,6 +216,7 @@ var AuthManager = class {
|
|
|
158
216
|
* v0.4.114). Returns true if the user existed.
|
|
159
217
|
*/
|
|
160
218
|
logoutAll(username) {
|
|
219
|
+
this.refresh();
|
|
161
220
|
username = username.trim().toLowerCase();
|
|
162
221
|
const user = this.db.users.find((u) => u.username === username);
|
|
163
222
|
if (!user) return false;
|
|
@@ -167,16 +226,19 @@ var AuthManager = class {
|
|
|
167
226
|
}
|
|
168
227
|
/** Get user's data directory (absolute path) */
|
|
169
228
|
getUserDataDir(username) {
|
|
229
|
+
this.refresh();
|
|
170
230
|
const user = this.db.users.find((u) => u.username === username);
|
|
171
231
|
if (!user) throw new Error(`User not found: ${username}`);
|
|
172
232
|
return join(this.baseDir, user.dataDir);
|
|
173
233
|
}
|
|
174
234
|
/** List all usernames */
|
|
175
235
|
listUsers() {
|
|
236
|
+
this.refresh();
|
|
176
237
|
return this.db.users.map((u) => u.username);
|
|
177
238
|
}
|
|
178
239
|
/** Delete a user. Returns error string or null on success. */
|
|
179
240
|
deleteUser(username) {
|
|
241
|
+
this.refresh();
|
|
180
242
|
username = username.trim().toLowerCase();
|
|
181
243
|
const idx = this.db.users.findIndex((u) => u.username === username);
|
|
182
244
|
if (idx === -1) return `User '${username}' not found`;
|
|
@@ -186,10 +248,11 @@ var AuthManager = class {
|
|
|
186
248
|
}
|
|
187
249
|
/** Reset a user's password. Returns error string or null on success. */
|
|
188
250
|
resetPassword(username, newPassword) {
|
|
251
|
+
this.refresh();
|
|
189
252
|
username = username.trim().toLowerCase();
|
|
190
253
|
const user = this.db.users.find((u) => u.username === username);
|
|
191
254
|
if (!user) return `User '${username}' not found`;
|
|
192
|
-
if (
|
|
255
|
+
if (typeof newPassword !== "string" || newPassword.length > 1024 || newPassword.length < 4) {
|
|
193
256
|
return "Password must be at least 4 characters";
|
|
194
257
|
}
|
|
195
258
|
const salt = randomBytes(16).toString("hex");
|
|
@@ -209,7 +272,7 @@ var AuthManager = class {
|
|
|
209
272
|
if (existsSync(globalConfig)) {
|
|
210
273
|
try {
|
|
211
274
|
const content = readFileSync(globalConfig, "utf-8");
|
|
212
|
-
|
|
275
|
+
atomicWriteFileSync(join(userDir, "config.json"), content, { mode: 384 });
|
|
213
276
|
} catch {
|
|
214
277
|
}
|
|
215
278
|
}
|
|
@@ -217,7 +280,7 @@ var AuthManager = class {
|
|
|
217
280
|
if (existsSync(globalMemory)) {
|
|
218
281
|
try {
|
|
219
282
|
const content = readFileSync(globalMemory, "utf-8");
|
|
220
|
-
|
|
283
|
+
atomicWriteFileSync(join(userDir, "memory.md"), content, { mode: 384 });
|
|
221
284
|
} catch {
|
|
222
285
|
}
|
|
223
286
|
}
|
|
@@ -228,7 +291,7 @@ var AuthManager = class {
|
|
|
228
291
|
const userHistory = join(userDir, "history");
|
|
229
292
|
for (const f of files) {
|
|
230
293
|
try {
|
|
231
|
-
|
|
294
|
+
atomicWriteFileSync(join(userHistory, f), readFileSync(join(globalHistory, f), "utf8"), { mode: 384 });
|
|
232
295
|
} catch {
|
|
233
296
|
}
|
|
234
297
|
}
|
|
@@ -238,13 +301,22 @@ var AuthManager = class {
|
|
|
238
301
|
return null;
|
|
239
302
|
}
|
|
240
303
|
// ── Private methods ────────────────────────────────────────────
|
|
304
|
+
refresh() {
|
|
305
|
+
const raw = readFileSync(this.usersFile, "utf8");
|
|
306
|
+
const parsed = JSON.parse(raw);
|
|
307
|
+
if (!this.isValidUsersDB(parsed)) throw new Error("Authentication database is invalid; access refused");
|
|
308
|
+
this.db = parsed;
|
|
309
|
+
this.loadedRaw = raw;
|
|
310
|
+
}
|
|
241
311
|
loadOrCreate() {
|
|
242
312
|
if (existsSync(this.usersFile)) {
|
|
243
313
|
try {
|
|
244
|
-
const
|
|
314
|
+
const raw = readFileSync(this.usersFile, "utf-8");
|
|
315
|
+
const parsed = JSON.parse(raw);
|
|
245
316
|
if (!this.isValidUsersDB(parsed)) {
|
|
246
317
|
throw new Error("database structure is invalid");
|
|
247
318
|
}
|
|
319
|
+
this.loadedRaw = raw;
|
|
248
320
|
return parsed;
|
|
249
321
|
} catch (err) {
|
|
250
322
|
const detail = err instanceof Error ? err.message : String(err);
|
|
@@ -287,8 +359,19 @@ var AuthManager = class {
|
|
|
287
359
|
this.saveDB(this.db);
|
|
288
360
|
}
|
|
289
361
|
saveDB(db) {
|
|
290
|
-
mkdirSync(this.baseDir, { recursive: true });
|
|
291
|
-
|
|
362
|
+
mkdirSync(this.baseDir, { recursive: true, mode: 448 });
|
|
363
|
+
const lock = this.usersFile + ".lock";
|
|
364
|
+
const fd = openSync(lock, "wx", 384);
|
|
365
|
+
try {
|
|
366
|
+
const current = existsSync(this.usersFile) ? readFileSync(this.usersFile, "utf8") : void 0;
|
|
367
|
+
if (current !== this.loadedRaw) throw new Error("Authentication database changed concurrently; retry the operation");
|
|
368
|
+
const raw = JSON.stringify(db, null, 2);
|
|
369
|
+
atomicWriteFileSync(this.usersFile, raw, { mode: 384 });
|
|
370
|
+
this.loadedRaw = raw;
|
|
371
|
+
} finally {
|
|
372
|
+
closeSync(fd);
|
|
373
|
+
unlinkSync(lock);
|
|
374
|
+
}
|
|
292
375
|
}
|
|
293
376
|
/** Legacy hash — kept only for migrating old users (v0.2.x) */
|
|
294
377
|
hashPasswordLegacy(password, salt) {
|
|
@@ -312,6 +395,19 @@ var AuthManager = class {
|
|
|
312
395
|
return createHmac("sha256", this.db.secret).update(data).digest("base64url");
|
|
313
396
|
}
|
|
314
397
|
};
|
|
398
|
+
function validCredentials(username, password) {
|
|
399
|
+
return typeof username === "string" && /^[a-z0-9_-]{2,32}$/i.test(username.trim()) && typeof password === "string" && password.length > 0 && password.length <= 1024;
|
|
400
|
+
}
|
|
401
|
+
var activeHashes = 0;
|
|
402
|
+
async function hashPasswordAsync(password, salt) {
|
|
403
|
+
if (activeHashes >= 4) throw new Error("Authentication capacity reached; try again later");
|
|
404
|
+
activeHashes++;
|
|
405
|
+
try {
|
|
406
|
+
return await new Promise((resolve, reject) => pbkdf2(password, salt, 1e5, 64, "sha512", (error, hash) => error ? reject(error) : resolve(hash.toString("hex"))));
|
|
407
|
+
} finally {
|
|
408
|
+
activeHashes--;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
315
411
|
|
|
316
412
|
export {
|
|
317
413
|
TOKEN_EXPIRY_MS,
|
|
@@ -6,18 +6,20 @@ import {
|
|
|
6
6
|
} from "./chunk-KHYD3WXE.js";
|
|
7
7
|
import {
|
|
8
8
|
getIndexMtimeMap
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-JL44HGZC.js";
|
|
10
10
|
import {
|
|
11
11
|
redactString
|
|
12
12
|
} from "./chunk-3FEZSLGI.js";
|
|
13
13
|
import {
|
|
14
14
|
atomicWriteFileSync
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-BMLQPSHP.js";
|
|
16
|
+
import {
|
|
17
|
+
getExecutionConfigDir
|
|
18
|
+
} from "./chunk-SXIJLSVF.js";
|
|
16
19
|
|
|
17
20
|
// src/memory/chat-index.ts
|
|
18
21
|
import fs from "fs";
|
|
19
22
|
import path from "path";
|
|
20
|
-
import os from "os";
|
|
21
23
|
import crypto from "crypto";
|
|
22
24
|
var MEMORY_DIR_NAME = "memory-index";
|
|
23
25
|
var CHUNKS_FILE = "chunks.json";
|
|
@@ -26,7 +28,7 @@ var VEC_MAGIC = 1094929750;
|
|
|
26
28
|
var VEC_VERSION = 1;
|
|
27
29
|
var VEC_HEADER_BYTES = 16;
|
|
28
30
|
function memoryIndexDir() {
|
|
29
|
-
return path.join(
|
|
31
|
+
return path.join(getExecutionConfigDir(), MEMORY_DIR_NAME);
|
|
30
32
|
}
|
|
31
33
|
function chunksPath() {
|
|
32
34
|
return path.join(memoryIndexDir(), CHUNKS_FILE);
|
|
@@ -35,7 +37,7 @@ function vectorsPath() {
|
|
|
35
37
|
return path.join(memoryIndexDir(), VECTORS_FILE);
|
|
36
38
|
}
|
|
37
39
|
function historyDir() {
|
|
38
|
-
return path.join(
|
|
40
|
+
return path.join(getExecutionConfigDir(), "history");
|
|
39
41
|
}
|
|
40
42
|
var MAX_CHUNK_CHARS = 1200;
|
|
41
43
|
var MIN_CHUNK_CHARS = 40;
|
|
@@ -6,13 +6,13 @@ import {
|
|
|
6
6
|
safeDate,
|
|
7
7
|
searchCandidates,
|
|
8
8
|
updateSessionInIndex
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-JL44HGZC.js";
|
|
10
10
|
import {
|
|
11
11
|
redactJson
|
|
12
12
|
} from "./chunk-3FEZSLGI.js";
|
|
13
13
|
import {
|
|
14
|
-
|
|
15
|
-
} from "./chunk-
|
|
14
|
+
atomicWriteFile
|
|
15
|
+
} from "./chunk-BMLQPSHP.js";
|
|
16
16
|
|
|
17
17
|
// src/session/session-manager.ts
|
|
18
18
|
import { readFileSync, existsSync, mkdirSync, readdirSync, unlinkSync, statSync } from "fs";
|
|
@@ -616,26 +616,33 @@ var SessionManager = class {
|
|
|
616
616
|
}
|
|
617
617
|
/** P1-PERF-03: `force` 跳过脏检查(用于进程退出、fork 等必须落盘场景) */
|
|
618
618
|
async save(force = false) {
|
|
619
|
-
|
|
620
|
-
if (
|
|
621
|
-
if (
|
|
619
|
+
const session = this._current;
|
|
620
|
+
if (!session) return;
|
|
621
|
+
if (session.messages.length === 0) return;
|
|
622
|
+
if (!force && !session.dirty) return;
|
|
622
623
|
mkdirSync(this.historyDir, { recursive: true });
|
|
623
|
-
const filePath = join(this.historyDir, `${
|
|
624
|
-
const raw =
|
|
624
|
+
const filePath = join(this.historyDir, `${session.id}.json`);
|
|
625
|
+
const raw = session.toJSON();
|
|
625
626
|
const opts = this.redactOptionsForSave();
|
|
626
627
|
const { value: payload, hits } = redactJson(raw, opts);
|
|
627
628
|
this.lastRedactionHits = hits.length;
|
|
628
|
-
|
|
629
|
-
|
|
629
|
+
const serialized = JSON.stringify(payload, null, 2);
|
|
630
|
+
session.dirty = false;
|
|
631
|
+
try {
|
|
632
|
+
await atomicWriteFile(filePath, serialized, { mode: 384 });
|
|
633
|
+
} catch (error) {
|
|
634
|
+
session.dirty = true;
|
|
635
|
+
throw error;
|
|
636
|
+
}
|
|
630
637
|
try {
|
|
631
638
|
updateSessionInIndex(this.historyDir, {
|
|
632
|
-
id:
|
|
633
|
-
provider:
|
|
634
|
-
model:
|
|
635
|
-
title:
|
|
636
|
-
messageCount:
|
|
637
|
-
created:
|
|
638
|
-
updated:
|
|
639
|
+
id: session.id,
|
|
640
|
+
provider: session.provider,
|
|
641
|
+
model: session.model,
|
|
642
|
+
title: session.title,
|
|
643
|
+
messageCount: session.messages.length,
|
|
644
|
+
created: session.created,
|
|
645
|
+
updated: session.updated
|
|
639
646
|
}, filePath);
|
|
640
647
|
} catch {
|
|
641
648
|
}
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
import {
|
|
3
3
|
loadIndex,
|
|
4
4
|
symbolFingerprint
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-M6HLYG33.js";
|
|
6
6
|
import {
|
|
7
7
|
loadVectorStore,
|
|
8
8
|
saveVectorStore,
|
|
9
9
|
searchVectorStore
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-C7CKDFJS.js";
|
|
11
11
|
import {
|
|
12
12
|
EMBEDDING_DIM,
|
|
13
13
|
embed,
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/core/atomic-write.ts
|
|
4
|
+
import fs from "fs";
|
|
5
|
+
import { randomUUID } from "crypto";
|
|
6
|
+
import { setTimeout as delay } from "timers/promises";
|
|
7
|
+
function modeFor(target, options) {
|
|
8
|
+
if (options.mode !== void 0) return options.mode;
|
|
9
|
+
try {
|
|
10
|
+
return fs.statSync(target).mode & 511;
|
|
11
|
+
} catch {
|
|
12
|
+
return 384;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function atomicWriteFileSync(target, data, options = {}) {
|
|
16
|
+
const tmp = `${target}.${process.pid}.${randomUUID()}.tmp`;
|
|
17
|
+
try {
|
|
18
|
+
fs.writeFileSync(tmp, data, { flag: "wx", mode: modeFor(target, options) });
|
|
19
|
+
fs.renameSync(tmp, target);
|
|
20
|
+
} finally {
|
|
21
|
+
try {
|
|
22
|
+
fs.unlinkSync(tmp);
|
|
23
|
+
} catch {
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
var pending = /* @__PURE__ */ new Map();
|
|
28
|
+
async function atomicWriteFile(target, data, options = {}) {
|
|
29
|
+
const previous = pending.get(target) ?? Promise.resolve();
|
|
30
|
+
const operation = previous.catch(() => {
|
|
31
|
+
}).then(async () => {
|
|
32
|
+
const tmp = `${target}.${process.pid}.${randomUUID()}.tmp`;
|
|
33
|
+
try {
|
|
34
|
+
let mode = options.mode ?? 384;
|
|
35
|
+
if (options.mode === void 0) {
|
|
36
|
+
try {
|
|
37
|
+
mode = (await fs.promises.stat(target)).mode & 511;
|
|
38
|
+
} catch {
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
await fs.promises.writeFile(tmp, data, { flag: "wx", mode });
|
|
42
|
+
for (let attempt = 0; ; attempt++) {
|
|
43
|
+
try {
|
|
44
|
+
await fs.promises.rename(tmp, target);
|
|
45
|
+
break;
|
|
46
|
+
} catch (error) {
|
|
47
|
+
if (attempt >= 10 || !["EPERM", "EBUSY", "EACCES"].includes(error.code ?? "")) throw error;
|
|
48
|
+
await delay(5 * (attempt + 1));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
} finally {
|
|
52
|
+
try {
|
|
53
|
+
await fs.promises.unlink(tmp);
|
|
54
|
+
} catch {
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
pending.set(target, operation);
|
|
59
|
+
try {
|
|
60
|
+
await operation;
|
|
61
|
+
} finally {
|
|
62
|
+
if (pending.get(target) === operation) pending.delete(target);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export {
|
|
67
|
+
atomicWriteFileSync,
|
|
68
|
+
atomicWriteFile
|
|
69
|
+
};
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
detectsHallucinatedFileOp,
|
|
4
4
|
parseToolCallArguments,
|
|
5
5
|
schemaToJsonSchema
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-RMEJR3AW.js";
|
|
7
7
|
import {
|
|
8
8
|
AuthError,
|
|
9
9
|
ProviderError,
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
} from "./chunk-TU3L3PW7.js";
|
|
13
13
|
import {
|
|
14
14
|
atomicWriteFileSync
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-BMLQPSHP.js";
|
|
16
16
|
|
|
17
17
|
// src/providers/claude.ts
|
|
18
18
|
import Anthropic from "@anthropic-ai/sdk";
|
|
@@ -1670,30 +1670,34 @@ var DeepSeekProvider = class extends OpenAICompatibleProvider {
|
|
|
1670
1670
|
info = {
|
|
1671
1671
|
id: "deepseek",
|
|
1672
1672
|
displayName: "DeepSeek",
|
|
1673
|
-
defaultModel: "deepseek-
|
|
1673
|
+
defaultModel: "deepseek-flash",
|
|
1674
1674
|
apiKeyEnvVar: "AICLI_API_KEY_DEEPSEEK",
|
|
1675
1675
|
requiresApiKey: true,
|
|
1676
1676
|
baseUrl: this.defaultBaseUrl,
|
|
1677
1677
|
models: [
|
|
1678
|
-
// ── V4
|
|
1678
|
+
// ── V4.1 Flash (2026-09):1M context,原生多模态,Thinking / Non-Thinking 双模式 ──
|
|
1679
|
+
// model code 实测取自端点本身:`/v1/models` 只列 `deepseek-flash` 与 `deepseek-v4-pro`,
|
|
1680
|
+
// 传 `deepseek-v4.1-flash` 报 400 并把合法名字写在错误消息里。**不要**按发布公告里的
|
|
1681
|
+
// 版本号拼 code(v0.4.198 的 `glm-5.2[1m]` 就是这么栽的)。
|
|
1679
1682
|
{
|
|
1680
|
-
id: "deepseek-
|
|
1681
|
-
displayName: "DeepSeek V4
|
|
1683
|
+
id: "deepseek-flash",
|
|
1684
|
+
displayName: "DeepSeek V4.1 Flash (multimodal, 1M)",
|
|
1682
1685
|
contextWindow: 1048576,
|
|
1683
1686
|
supportsStreaming: true,
|
|
1684
1687
|
supportsThinking: true
|
|
1685
1688
|
},
|
|
1686
1689
|
{
|
|
1687
|
-
id: "deepseek-v4-
|
|
1688
|
-
displayName: "DeepSeek V4
|
|
1690
|
+
id: "deepseek-v4-pro",
|
|
1691
|
+
displayName: "DeepSeek V4 Pro (text-only; routes to V4.1 Flash from 2026-09-14)",
|
|
1689
1692
|
contextWindow: 1048576,
|
|
1690
1693
|
supportsStreaming: true,
|
|
1691
1694
|
supportsThinking: true
|
|
1692
1695
|
}
|
|
1693
|
-
// Legacy aliases
|
|
1694
|
-
//
|
|
1695
|
-
//
|
|
1696
|
-
//
|
|
1696
|
+
// Legacy aliases:`deepseek-v4-flash` / `deepseek-v4-flash-vision-exp` 的底层模型已退役,
|
|
1697
|
+
// 端点仍接受这两个名字并路由到 V4.1 Flash(实测传 `deepseek-v4-flash` 返回 200 且响应
|
|
1698
|
+
// `model` 字段回显 `deepseek-flash`)。它们不再列进推荐清单,config 里 pin 过的会被
|
|
1699
|
+
// `migrateLegacyModels()` 自愈到 `deepseek-flash`;pricing.ts 保留旧 id 供历史成本回算。
|
|
1700
|
+
// `deepseek-chat` / `deepseek-reasoner` 早在 v0.4.140 移除(2026-07-24 停用)。
|
|
1697
1701
|
]
|
|
1698
1702
|
};
|
|
1699
1703
|
/**
|