jinzd-ai-cli 0.4.304 → 0.4.306
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/dist/{batch-RY2F7BX5.js → batch-JLGWQ7HU.js} +2 -2
- package/dist/{chunk-3FI4TE5S.js → chunk-3RDJEYF7.js} +3 -3
- package/dist/{chunk-RWM2GFRC.js → chunk-45UVJXFQ.js} +6 -2
- package/dist/{chunk-23SJVOQD.js → chunk-72AR23R2.js} +26 -5
- package/dist/{chunk-I2WJ44D3.js → chunk-7SSCSSC6.js} +1 -1
- package/dist/{chunk-F3EJ4MIP.js → chunk-AYLK4EGA.js} +12 -11
- package/dist/{chunk-VOIOUJXI.js → chunk-C2Z77RTU.js} +13 -5
- package/dist/{chunk-2GRGLTAK.js → chunk-JDT6UG6R.js} +3 -1
- package/dist/{chunk-OECPGCHP.js → chunk-JFNBJHR6.js} +3 -3
- package/dist/{chunk-6AGTDWO3.js → chunk-K5SDDTN7.js} +1 -1
- package/dist/{chunk-7XU4BATV.js → chunk-KBDKKHXB.js} +15 -6
- package/dist/{chunk-7L7T46PI.js → chunk-LAP7TPPK.js} +1 -1
- package/dist/{chunk-4DEMGCK4.js → chunk-LF67HWWR.js} +37 -13
- package/dist/{chunk-TBTVV3KK.js → chunk-NLB7JZSO.js} +1 -1
- package/dist/{chunk-BXP6YZ2P.js → chunk-NRSYBKWA.js} +5 -4
- package/dist/{chunk-IEQAE3QG.js → chunk-OAXLMF3U.js} +5 -4
- package/dist/{chunk-XIMM47EG.js → chunk-OC2XHG5L.js} +1 -1
- package/dist/{chunk-RHCRE6XE.js → chunk-T6WQEQI4.js} +3 -1
- package/dist/{chunk-PPIISCFN.js → chunk-UFW4S3RU.js} +37 -13
- package/dist/{chunk-2PLYXL4P.js → chunk-VLDB2SVN.js} +33 -21
- package/dist/{chunk-BSBGPDCX.js → chunk-W6PLV3EY.js} +1 -1
- package/dist/{chunk-CKH4KQ4E.js → chunk-WQ3OMEC4.js} +6 -2
- package/dist/{chunk-DNGUSTL6.js → chunk-WZWRA7R2.js} +1 -1
- package/dist/{chunk-UUSRWSSX.js → chunk-XJOK5353.js} +15 -6
- package/dist/{ci-QYTLBM6Z.js → ci-6PHEL3C7.js} +4 -4
- package/dist/{ci-format-E5X27NC2.js → ci-format-AZYUP6BL.js} +2 -2
- package/dist/{constants-JDPKWZJP.js → constants-AXBD2EEB.js} +3 -1
- package/dist/{constants-F5WA5VPO.js → constants-CPNWIUGZ.js} +3 -1
- package/dist/{doctor-cli-7NSEVW63.js → doctor-cli-DDHHDMMJ.js} +4 -4
- package/dist/electron-server.js +253 -117
- package/dist/{hub-43PWTGGO.js → hub-DDYAWLF2.js} +4 -4
- package/dist/{hub-server-RF6LURXY.js → hub-server-CBKCP5VD.js} +1 -1
- package/dist/index.js +25 -24
- package/dist/{indexer-RBEKMFT4.js → indexer-FPKY7LCT.js} +2 -2
- package/dist/{indexer-TNDWGFRI.js → indexer-P3FPRTPJ.js} +2 -2
- package/dist/{persistent-memory-SHG3CJCP.js → persistent-memory-6XYYOMSC.js} +2 -2
- package/dist/{persistent-memory-A7RPDLR7.js → persistent-memory-XZISXRWT.js} +2 -2
- package/dist/{pr-LW755EL7.js → pr-UY4ZCZNI.js} +4 -4
- package/dist/{run-tests-HV37CM5U.js → run-tests-BAMLVLO6.js} +2 -2
- package/dist/{run-tests-MJ3SCW27.js → run-tests-UEE3OMLS.js} +2 -2
- package/dist/{semantic-HLAE2O4F.js → semantic-7GM5M4MU.js} +3 -3
- package/dist/{semantic-VPNKXKKK.js → semantic-QOBAXEQ4.js} +3 -3
- package/dist/{server-WI3Q4EDH.js → server-6IC33CJK.js} +9 -9
- package/dist/{server-EZ5ULLDP.js → server-DBHLVQVP.js} +193 -82
- package/dist/{store-MWNHVGJT.js → store-CMSSWZF7.js} +3 -1
- package/dist/{store-VO37H6LS.js → store-SIKHCUYJ.js} +3 -1
- package/dist/{task-orchestrator-PWW5I2KE.js → task-orchestrator-RDDETDW2.js} +9 -9
- package/dist/{usage-MAIV6XUZ.js → usage-D473ZVIP.js} +2 -2
- package/dist/{vector-store-JBAE6PS4.js → vector-store-JAXWDZYW.js} +1 -1
- package/dist/{vector-store-BBDXB5IQ.js → vector-store-PWIOQFTP.js} +1 -1
- package/dist/web/client/app.js +135 -60
- package/dist/web/client/app.js.br +0 -0
- package/dist/web/client/app.js.gz +0 -0
- package/dist/web/client/chat-input.js +38 -0
- package/dist/web/client/chat-input.js.br +0 -0
- package/dist/web/client/chat-input.js.gz +0 -0
- package/dist/web/client/index.html +1 -1
- package/dist/web/client/index.html.br +0 -0
- package/dist/web/client/index.html.gz +0 -0
- package/dist/web/client/stream-render.js +33 -0
- package/dist/web/client/style.css +2 -1
- package/dist/web/client/style.css.br +0 -0
- package/dist/web/client/style.css.gz +0 -0
- package/package.json +1 -1
|
@@ -3,8 +3,8 @@ import {
|
|
|
3
3
|
DiscussionOrchestrator,
|
|
4
4
|
getPreset,
|
|
5
5
|
resolveRoleProviders
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-72AR23R2.js";
|
|
7
|
+
import "./chunk-C2Z77RTU.js";
|
|
8
8
|
import {
|
|
9
9
|
AuthManager,
|
|
10
10
|
TOKEN_EXPIRY_MS
|
|
@@ -81,7 +81,7 @@ import {
|
|
|
81
81
|
skillHistoryNote,
|
|
82
82
|
snapshotFile,
|
|
83
83
|
stripRoutingTags
|
|
84
|
-
} from "./chunk-
|
|
84
|
+
} from "./chunk-AYLK4EGA.js";
|
|
85
85
|
import {
|
|
86
86
|
CNY_PER_USD,
|
|
87
87
|
CostTracker,
|
|
@@ -92,6 +92,7 @@ import {
|
|
|
92
92
|
setCostCurrency
|
|
93
93
|
} from "./chunk-YKJO3SI4.js";
|
|
94
94
|
import {
|
|
95
|
+
DEFAULT_MAX_TOOL_OUTPUT_CHARS,
|
|
95
96
|
ToolExecutor,
|
|
96
97
|
ToolRegistry,
|
|
97
98
|
askUserContext,
|
|
@@ -105,6 +106,7 @@ import {
|
|
|
105
106
|
estimateTokens,
|
|
106
107
|
formatPermissionProfileWarning,
|
|
107
108
|
gateMessages,
|
|
109
|
+
getMaxOutputChars,
|
|
108
110
|
getPreferredAgentName,
|
|
109
111
|
getRecentlyDeniedAutoActions,
|
|
110
112
|
googleSearchContext,
|
|
@@ -120,13 +122,11 @@ import {
|
|
|
120
122
|
resolveWriteFileArgs,
|
|
121
123
|
runSafePhases,
|
|
122
124
|
runWithSessionKey,
|
|
123
|
-
setContextWindow,
|
|
124
|
-
setMaxOutputCap,
|
|
125
125
|
setPreferredAgentName,
|
|
126
126
|
spawnAgentContext,
|
|
127
127
|
truncateOutput,
|
|
128
128
|
undoStack
|
|
129
|
-
} from "./chunk-
|
|
129
|
+
} from "./chunk-VLDB2SVN.js";
|
|
130
130
|
import {
|
|
131
131
|
buildReviewPrompt,
|
|
132
132
|
buildSecurityReviewPrompt
|
|
@@ -153,11 +153,11 @@ import {
|
|
|
153
153
|
restoreMemoryEntries,
|
|
154
154
|
updateMemoryApproval,
|
|
155
155
|
updateMemoryEntry
|
|
156
|
-
} from "./chunk-
|
|
157
|
-
import "./chunk-
|
|
158
|
-
import "./chunk-
|
|
159
|
-
import "./chunk-
|
|
160
|
-
import "./chunk-
|
|
156
|
+
} from "./chunk-LAP7TPPK.js";
|
|
157
|
+
import "./chunk-LF67HWWR.js";
|
|
158
|
+
import "./chunk-XJOK5353.js";
|
|
159
|
+
import "./chunk-WQ3OMEC4.js";
|
|
160
|
+
import "./chunk-NRSYBKWA.js";
|
|
161
161
|
import "./chunk-KHYD3WXE.js";
|
|
162
162
|
import "./chunk-5C76XDDF.js";
|
|
163
163
|
import {
|
|
@@ -172,7 +172,7 @@ import {
|
|
|
172
172
|
buildDoctorReport,
|
|
173
173
|
formatDoctorReport,
|
|
174
174
|
loadContextFiles
|
|
175
|
-
} from "./chunk-
|
|
175
|
+
} from "./chunk-JFNBJHR6.js";
|
|
176
176
|
import {
|
|
177
177
|
describePlugin,
|
|
178
178
|
getActivePluginAssets,
|
|
@@ -187,14 +187,14 @@ import {
|
|
|
187
187
|
runTool,
|
|
188
188
|
setPluginEnabled,
|
|
189
189
|
trustPlugin
|
|
190
|
-
} from "./chunk-
|
|
190
|
+
} from "./chunk-7SSCSSC6.js";
|
|
191
191
|
import "./chunk-RS7SKYTS.js";
|
|
192
192
|
import {
|
|
193
193
|
ProviderRegistry
|
|
194
194
|
} from "./chunk-VXEF2SWG.js";
|
|
195
195
|
import {
|
|
196
196
|
ConfigManager
|
|
197
|
-
} from "./chunk-
|
|
197
|
+
} from "./chunk-OC2XHG5L.js";
|
|
198
198
|
import {
|
|
199
199
|
CONTENT_ONLY_STREAM_REMINDER,
|
|
200
200
|
TEE_FINAL_USER_NUDGE,
|
|
@@ -225,7 +225,7 @@ import {
|
|
|
225
225
|
maskConfigSecrets
|
|
226
226
|
} from "./chunk-XNFIYS7G.js";
|
|
227
227
|
import "./chunk-4BKXL7SM.js";
|
|
228
|
-
import "./chunk-
|
|
228
|
+
import "./chunk-W6PLV3EY.js";
|
|
229
229
|
import {
|
|
230
230
|
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
231
231
|
AUTHOR,
|
|
@@ -241,12 +241,30 @@ import {
|
|
|
241
241
|
SKILLS_DIR_NAME,
|
|
242
242
|
VERSION,
|
|
243
243
|
buildUserIdentityPrompt
|
|
244
|
-
} from "./chunk-
|
|
244
|
+
} from "./chunk-T6WQEQI4.js";
|
|
245
245
|
|
|
246
246
|
// src/web/server.ts
|
|
247
247
|
import express from "express";
|
|
248
248
|
import { createServer } from "http";
|
|
249
249
|
import { WebSocketServer } from "ws";
|
|
250
|
+
|
|
251
|
+
// src/web/chat-limits.ts
|
|
252
|
+
var MAX_CHAT_BYTES = 16 * 1024 * 1024;
|
|
253
|
+
var MAX_IMAGE_BYTES = 10 * 1024 * 1024;
|
|
254
|
+
var MAX_IMAGE_BASE64_CHARS = Math.ceil(MAX_IMAGE_BYTES / 3) * 4;
|
|
255
|
+
var IMAGE_MIMES = /* @__PURE__ */ new Set(["image/png", "image/jpeg", "image/gif", "image/webp", "image/svg+xml"]);
|
|
256
|
+
function imageValidationError(images) {
|
|
257
|
+
if (images === void 0) return;
|
|
258
|
+
if (!Array.isArray(images)) return "Images must be an array.";
|
|
259
|
+
for (const image of images) {
|
|
260
|
+
if (!image || typeof image.data !== "string" || !IMAGE_MIMES.has(image.mime)) {
|
|
261
|
+
return "Unsupported image format. Use PNG, JPEG, GIF, WebP or SVG.";
|
|
262
|
+
}
|
|
263
|
+
if (image.data.length > MAX_IMAGE_BASE64_CHARS) return "Image too large (maximum 10 MiB before base64 encoding).";
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// src/web/server.ts
|
|
250
268
|
import { join as join2, dirname as dirname3, resolve as resolve3, relative, sep as sep2, extname, resolve as resolvePath } from "path";
|
|
251
269
|
import { existsSync as existsSync3, readFileSync as readFileSync3, readdirSync as readdirSync2, statSync as statSync2, realpathSync, createReadStream } from "fs";
|
|
252
270
|
import { networkInterfaces } from "os";
|
|
@@ -254,6 +272,27 @@ import { networkInterfaces } from "os";
|
|
|
254
272
|
// src/web/tool-executor-web.ts
|
|
255
273
|
import { randomUUID } from "crypto";
|
|
256
274
|
import { tmpdir } from "os";
|
|
275
|
+
|
|
276
|
+
// src/web/socket-send.ts
|
|
277
|
+
var MAX_PENDING_OUTPUT_BYTES = 16 * 1024 * 1024;
|
|
278
|
+
var stalled = /* @__PURE__ */ new WeakSet();
|
|
279
|
+
function sendSocketMessage(ws, message) {
|
|
280
|
+
if (ws.readyState !== ws.OPEN || stalled.has(ws)) return false;
|
|
281
|
+
const data = JSON.stringify(message);
|
|
282
|
+
if ((ws.bufferedAmount || 0) + Buffer.byteLength(data) > MAX_PENDING_OUTPUT_BYTES) {
|
|
283
|
+
stalled.add(ws);
|
|
284
|
+
ws.close(1013, "Output backlog exceeded; reconnect to reload session");
|
|
285
|
+
const timer = setTimeout(() => {
|
|
286
|
+
if (ws.readyState !== ws.CLOSED) ws.terminate();
|
|
287
|
+
}, 1e3);
|
|
288
|
+
timer.unref();
|
|
289
|
+
return false;
|
|
290
|
+
}
|
|
291
|
+
ws.send(data);
|
|
292
|
+
return true;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// src/web/tool-executor-web.ts
|
|
257
296
|
import { existsSync, readFileSync } from "fs";
|
|
258
297
|
var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
259
298
|
constructor(registry, ws) {
|
|
@@ -283,6 +322,7 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
283
322
|
// 5 minutes
|
|
284
323
|
/** Publicly readable by SessionHandler to check if confirm is active */
|
|
285
324
|
confirming = false;
|
|
325
|
+
maxOutputChars = DEFAULT_MAX_TOOL_OUTPUT_CHARS;
|
|
286
326
|
/** Session-level auto-approve toggle (/yolo command) */
|
|
287
327
|
sessionAutoApprove = false;
|
|
288
328
|
/** Session-level Auto Mode: rule-based low-risk auto approval. */
|
|
@@ -365,9 +405,7 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
365
405
|
this.confirming = false;
|
|
366
406
|
}
|
|
367
407
|
send(msg) {
|
|
368
|
-
|
|
369
|
-
this.ws.send(JSON.stringify(msg));
|
|
370
|
-
}
|
|
408
|
+
sendSocketMessage(this.ws, msg);
|
|
371
409
|
}
|
|
372
410
|
sendToolCallStart(call) {
|
|
373
411
|
const dangerLevel = getDangerLevel(call.name, call.arguments);
|
|
@@ -560,7 +598,7 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
560
598
|
if (!toolCallStarted) this.sendToolCallStart(call);
|
|
561
599
|
try {
|
|
562
600
|
const rawContent = await runTool(tool, call.arguments, call.name);
|
|
563
|
-
const content = truncateOutput(rawContent, call.name);
|
|
601
|
+
const content = truncateOutput(rawContent, call.name, this.maxOutputChars);
|
|
564
602
|
this.sendToolCallResult(call, rawContent, false);
|
|
565
603
|
runHook(this.hookConfig?.postToolExecution, { tool: call.name, status: "ok" });
|
|
566
604
|
runLifecycleHooks(this.hookConfig, "PostToolUse", { tool: call.name, status: "ok" }, { configDir: this.hookConfigDir });
|
|
@@ -625,7 +663,7 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
625
663
|
}
|
|
626
664
|
try {
|
|
627
665
|
const rawContent = await runTool(tool, call.arguments, call.name);
|
|
628
|
-
const content = truncateOutput(rawContent, call.name);
|
|
666
|
+
const content = truncateOutput(rawContent, call.name, this.maxOutputChars);
|
|
629
667
|
this.sendToolCallResult(call, rawContent, false);
|
|
630
668
|
results[i] = { callId: call.id, content, isError: false };
|
|
631
669
|
} catch (err) {
|
|
@@ -701,11 +739,11 @@ import { mkdirSync as mkdirSync2, createWriteStream as createWriteStream2 } from
|
|
|
701
739
|
import { dirname as dirname2 } from "path";
|
|
702
740
|
|
|
703
741
|
// src/web/commands/provider-commands.ts
|
|
704
|
-
function syncSessionProvider(ctx) {
|
|
742
|
+
async function syncSessionProvider(ctx) {
|
|
705
743
|
const session = ctx.sessions.current;
|
|
706
744
|
if (!session) return;
|
|
707
745
|
session.updateProvider(ctx.currentProvider, ctx.currentModel);
|
|
708
|
-
ctx.saveIfNeeded();
|
|
746
|
+
await ctx.saveIfNeeded();
|
|
709
747
|
}
|
|
710
748
|
async function handleProvider(args, ctx) {
|
|
711
749
|
const id = args[0];
|
|
@@ -748,7 +786,7 @@ async function handleProvider(args, ctx) {
|
|
|
748
786
|
ctx.currentProvider = outcome.providerId;
|
|
749
787
|
ctx.currentModel = outcome.model;
|
|
750
788
|
ctx.updateContextWindow();
|
|
751
|
-
syncSessionProvider(ctx);
|
|
789
|
+
await syncSessionProvider(ctx);
|
|
752
790
|
ctx.send({ type: "info", message: outcome.message });
|
|
753
791
|
ctx.sendStatus();
|
|
754
792
|
}
|
|
@@ -802,7 +840,7 @@ async function handleModel(args, ctx) {
|
|
|
802
840
|
}
|
|
803
841
|
ctx.currentModel = outcome.model;
|
|
804
842
|
ctx.updateContextWindow();
|
|
805
|
-
syncSessionProvider(ctx);
|
|
843
|
+
await syncSessionProvider(ctx);
|
|
806
844
|
ctx.send({
|
|
807
845
|
type: "info",
|
|
808
846
|
message: outcome.warning ? `${outcome.message} \u2014 ${outcome.warning}` : outcome.message
|
|
@@ -908,7 +946,7 @@ async function handleClear(_args, ctx) {
|
|
|
908
946
|
const prior = ctx.sessions.current;
|
|
909
947
|
const priorId = prior?.id ?? null;
|
|
910
948
|
const priorCount = prior?.messages.length ?? 0;
|
|
911
|
-
ctx.saveIfNeeded();
|
|
949
|
+
await ctx.saveIfNeeded();
|
|
912
950
|
const created = ctx.sessions.createSession(ctx.currentProvider, ctx.currentModel);
|
|
913
951
|
ctx.unsavedSessions.set(created.id, created);
|
|
914
952
|
ctx.resetWebSessionUsage();
|
|
@@ -930,7 +968,7 @@ Cache: write=${cacheCreate} read=${cacheRead}` : "";
|
|
|
930
968
|
Cost: ${formatCost(cost)}` : "";
|
|
931
969
|
let memoryLine = "";
|
|
932
970
|
try {
|
|
933
|
-
const { countPendingMemories: countPendingMemories2 } = await import("./persistent-memory-
|
|
971
|
+
const { countPendingMemories: countPendingMemories2 } = await import("./persistent-memory-6XYYOMSC.js");
|
|
934
972
|
const pending = countPendingMemories2(ctx.config.getConfigDir());
|
|
935
973
|
if (pending > 0) memoryLine = `
|
|
936
974
|
Memory: \u26A0 ${pending} pending approval \u2014 see the Memory panel or /memory`;
|
|
@@ -946,8 +984,13 @@ Tokens: in=${ctx.sessionTokenUsage.inputTokens} out=${ctx.sessionTokenUsage.outp
|
|
|
946
984
|
}
|
|
947
985
|
async function handleSession(args, ctx) {
|
|
948
986
|
const sub = args[0];
|
|
987
|
+
if (sub === "save") {
|
|
988
|
+
await ctx.saveIfNeeded();
|
|
989
|
+
if (!ctx.sessions.current?.dirty) ctx.send({ type: "info", message: "Session saved." });
|
|
990
|
+
return;
|
|
991
|
+
}
|
|
949
992
|
if (sub === "new") {
|
|
950
|
-
ctx.saveIfNeeded();
|
|
993
|
+
await ctx.saveIfNeeded();
|
|
951
994
|
const created = ctx.sessions.createSession(ctx.currentProvider, ctx.currentModel);
|
|
952
995
|
ctx.unsavedSessions.set(created.id, created);
|
|
953
996
|
ctx.resetWebSessionUsage();
|
|
@@ -958,7 +1001,7 @@ async function handleSession(args, ctx) {
|
|
|
958
1001
|
}
|
|
959
1002
|
if (sub === "load" && args[1]) {
|
|
960
1003
|
const targetId = args[1];
|
|
961
|
-
ctx.saveIfNeeded();
|
|
1004
|
+
await ctx.saveIfNeeded();
|
|
962
1005
|
const cachedExact = ctx.unsavedSessions.get(targetId);
|
|
963
1006
|
const cached = cachedExact ?? [...ctx.unsavedSessions.values()].find((s) => s.id.startsWith(targetId));
|
|
964
1007
|
if (cached) {
|
|
@@ -1854,7 +1897,7 @@ async function handleTest(args, ctx) {
|
|
|
1854
1897
|
const isCommand = argStr.includes(" ") || /^(mvn|gradle|npm|pytest|cargo|go)\b/.test(argStr);
|
|
1855
1898
|
testArgs = isCommand ? { command: argStr } : { filter: argStr };
|
|
1856
1899
|
}
|
|
1857
|
-
const runTests = ctx.runTests ?? (await import("./run-tests-
|
|
1900
|
+
const runTests = ctx.runTests ?? (await import("./run-tests-BAMLVLO6.js")).executeTests;
|
|
1858
1901
|
const report = await runTests(testArgs);
|
|
1859
1902
|
ctx.send({ type: "info", message: report });
|
|
1860
1903
|
} catch (err) {
|
|
@@ -2031,9 +2074,9 @@ async function handleContext(_args, ctx) {
|
|
|
2031
2074
|
async function handleIndex(args, ctx) {
|
|
2032
2075
|
const sub = (args[0] ?? "status").toLowerCase();
|
|
2033
2076
|
const root = process.cwd();
|
|
2034
|
-
const { loadIndex, clearIndex } = await import("./store-
|
|
2035
|
-
const { indexProject } = await import("./indexer-
|
|
2036
|
-
const { loadVectorStore, clearVectorStore } = await import("./vector-store-
|
|
2077
|
+
const { loadIndex, clearIndex } = await import("./store-CMSSWZF7.js");
|
|
2078
|
+
const { indexProject } = await import("./indexer-P3FPRTPJ.js");
|
|
2079
|
+
const { loadVectorStore, clearVectorStore } = await import("./vector-store-PWIOQFTP.js");
|
|
2037
2080
|
if (sub === "status") {
|
|
2038
2081
|
const idx = loadIndex(root);
|
|
2039
2082
|
const vec = loadVectorStore(root);
|
|
@@ -2056,6 +2099,7 @@ async function handleIndex(args, ctx) {
|
|
|
2056
2099
|
} else if (sub === "rebuild") {
|
|
2057
2100
|
ctx.send({ type: "info", message: `Indexing ${root}\u2026` });
|
|
2058
2101
|
const { stats } = await indexProject(root, { force: true });
|
|
2102
|
+
if (stats.incomplete) ctx.send({ type: "info", message: "Partial index: scan limit or unreadable files reached. Unscanned entries were retained." });
|
|
2059
2103
|
ctx.send({
|
|
2060
2104
|
type: "info",
|
|
2061
2105
|
message: `\u2713 Indexed ${stats.filesParsed} files (${stats.symbols} symbols) in ${stats.durationMs}ms`
|
|
@@ -2082,7 +2126,7 @@ async function handleIndex(args, ctx) {
|
|
|
2082
2126
|
message: `Building semantic index for ${idx.symbolCount} symbols\u2026 (first run downloads ~117 MB model)`
|
|
2083
2127
|
});
|
|
2084
2128
|
try {
|
|
2085
|
-
const { rebuildSemanticIndex } = await import("./semantic-
|
|
2129
|
+
const { rebuildSemanticIndex } = await import("./semantic-7GM5M4MU.js");
|
|
2086
2130
|
const stats = await rebuildSemanticIndex(root);
|
|
2087
2131
|
const first = stats.modelFirstLoadMs ? ` (model load+first batch ${stats.modelFirstLoadMs}ms)` : "";
|
|
2088
2132
|
ctx.send({
|
|
@@ -2129,7 +2173,7 @@ async function handleMemory(args, ctx) {
|
|
|
2129
2173
|
}
|
|
2130
2174
|
if (intent.kind === "status") {
|
|
2131
2175
|
const { getChatIndexStatus } = await import("./chat-index-2D5GK2DB.js");
|
|
2132
|
-
const { listMemoryEntries: listMemoryEntries2, isMemoryExpired: isMemoryExpired2, formatMemoryForPrompt: formatMemoryForPrompt2 } = await import("./persistent-memory-
|
|
2176
|
+
const { listMemoryEntries: listMemoryEntries2, isMemoryExpired: isMemoryExpired2, formatMemoryForPrompt: formatMemoryForPrompt2 } = await import("./persistent-memory-6XYYOMSC.js");
|
|
2133
2177
|
const memories = listMemoryEntries2(configDir, { includeExpired: true, includeRejected: true });
|
|
2134
2178
|
const injected = formatMemoryForPrompt2(configDir, process.cwd(), ctx.config.get("memory")?.maxChars);
|
|
2135
2179
|
const view = buildMemoryStatusView({
|
|
@@ -2152,7 +2196,7 @@ async function handleMemory(args, ctx) {
|
|
|
2152
2196
|
return;
|
|
2153
2197
|
}
|
|
2154
2198
|
if (intent.kind === "recall") {
|
|
2155
|
-
const { searchPersistentMemories, touchMemoryReferences } = await import("./persistent-memory-
|
|
2199
|
+
const { searchPersistentMemories, touchMemoryReferences } = await import("./persistent-memory-6XYYOMSC.js");
|
|
2156
2200
|
const { searchChatMemory } = await import("./chat-index-2D5GK2DB.js");
|
|
2157
2201
|
const persistentHits = searchPersistentMemories(configDir, intent.query, process.cwd(), 3);
|
|
2158
2202
|
if (persistentHits.length > 0) {
|
|
@@ -2273,7 +2317,7 @@ async function handleMemory(args, ctx) {
|
|
|
2273
2317
|
ctx.handleMemoryManage(intent.action, args[1], intent.action === "expire" ? args[2] : void 0);
|
|
2274
2318
|
} else if (intent.kind === "export") {
|
|
2275
2319
|
if (args[1] === "md") {
|
|
2276
|
-
const { exportMemoryEntries: exportMemoryEntries2 } = await import("./persistent-memory-
|
|
2320
|
+
const { exportMemoryEntries: exportMemoryEntries2 } = await import("./persistent-memory-6XYYOMSC.js");
|
|
2277
2321
|
ctx.send({
|
|
2278
2322
|
type: "export_data",
|
|
2279
2323
|
format: "md",
|
|
@@ -2692,7 +2736,7 @@ var webCommandHandlers = {
|
|
|
2692
2736
|
search: handleSearch,
|
|
2693
2737
|
undo: handleUndo
|
|
2694
2738
|
};
|
|
2695
|
-
var SessionHandler = class {
|
|
2739
|
+
var SessionHandler = class _SessionHandler {
|
|
2696
2740
|
ws;
|
|
2697
2741
|
config;
|
|
2698
2742
|
costTracker;
|
|
@@ -2743,6 +2787,7 @@ var SessionHandler = class {
|
|
|
2743
2787
|
abortController = null;
|
|
2744
2788
|
userInterjection = null;
|
|
2745
2789
|
processing = false;
|
|
2790
|
+
disconnected = false;
|
|
2746
2791
|
/** P4: active hub discussion orchestrator (web room), if any. */
|
|
2747
2792
|
hubOrchestrator = null;
|
|
2748
2793
|
/** P4b: pending between-rounds human-steer prompts (requestId → resolver). */
|
|
@@ -2769,9 +2814,13 @@ var SessionHandler = class {
|
|
|
2769
2814
|
* 会话一旦保存到磁盘(有消息后),会从此 Map 中移除。
|
|
2770
2815
|
*/
|
|
2771
2816
|
unsavedSessions = /* @__PURE__ */ new Map();
|
|
2817
|
+
// Failed saves survive a socket reconnect, scoped to the owning config directory.
|
|
2818
|
+
static recovery = /* @__PURE__ */ new Map();
|
|
2819
|
+
static acceptedChats = /* @__PURE__ */ new Map();
|
|
2772
2820
|
constructor(ws, shared) {
|
|
2773
2821
|
this.ws = ws;
|
|
2774
2822
|
this.config = shared.config;
|
|
2823
|
+
this.unsavedSessions = new Map(_SessionHandler.recovery.get(this.config.getConfigDir()));
|
|
2775
2824
|
this.costTracker = shared.costTracker;
|
|
2776
2825
|
this.providers = shared.providers;
|
|
2777
2826
|
this.sessions = shared.sessions;
|
|
@@ -2839,9 +2888,7 @@ var SessionHandler = class {
|
|
|
2839
2888
|
* 接口之间的任何漂移(少一个成员、签名改了)都会当场编译失败,而不是等到运行时。
|
|
2840
2889
|
*/
|
|
2841
2890
|
send(msg) {
|
|
2842
|
-
|
|
2843
|
-
this.ws.send(JSON.stringify(msg));
|
|
2844
|
-
}
|
|
2891
|
+
sendSocketMessage(this.ws, msg);
|
|
2845
2892
|
}
|
|
2846
2893
|
sendStatus() {
|
|
2847
2894
|
const providerList = this.providers.listAvailable().map((p) => ({
|
|
@@ -2895,6 +2942,7 @@ var SessionHandler = class {
|
|
|
2895
2942
|
});
|
|
2896
2943
|
}
|
|
2897
2944
|
async handleMessage(raw) {
|
|
2945
|
+
if (this.disconnected) return;
|
|
2898
2946
|
let msg;
|
|
2899
2947
|
try {
|
|
2900
2948
|
msg = JSON.parse(raw);
|
|
@@ -2903,8 +2951,33 @@ var SessionHandler = class {
|
|
|
2903
2951
|
return;
|
|
2904
2952
|
}
|
|
2905
2953
|
switch (msg.type) {
|
|
2906
|
-
case "chat":
|
|
2954
|
+
case "chat": {
|
|
2955
|
+
if (msg.messageId) {
|
|
2956
|
+
if (typeof msg.messageId !== "string" || msg.messageId.length > 200) throw new Error("Invalid message ID");
|
|
2957
|
+
const scope = this.config.getConfigDir();
|
|
2958
|
+
const accepted = _SessionHandler.acceptedChats.get(scope) ?? /* @__PURE__ */ new Map();
|
|
2959
|
+
const prior = accepted.get(msg.messageId);
|
|
2960
|
+
if (prior) {
|
|
2961
|
+
this.send({ type: "chat_ack", messageId: msg.messageId, accepted: true, duplicate: true, sessionId: prior });
|
|
2962
|
+
return;
|
|
2963
|
+
}
|
|
2964
|
+
if (this.processing || typeof msg.content !== "string") {
|
|
2965
|
+
this.send({ type: "chat_ack", messageId: msg.messageId, accepted: false, error: "A request is already running, or the message is invalid. Your draft was kept." });
|
|
2966
|
+
return;
|
|
2967
|
+
}
|
|
2968
|
+
const imageError = imageValidationError(msg.images);
|
|
2969
|
+
if (imageError) {
|
|
2970
|
+
this.send({ type: "chat_ack", messageId: msg.messageId, accepted: false, error: imageError });
|
|
2971
|
+
return;
|
|
2972
|
+
}
|
|
2973
|
+
this.ensureSession();
|
|
2974
|
+
accepted.set(msg.messageId, this.sessions.current.id);
|
|
2975
|
+
while (accepted.size > 1e3) accepted.delete(accepted.keys().next().value);
|
|
2976
|
+
_SessionHandler.acceptedChats.set(scope, accepted);
|
|
2977
|
+
this.send({ type: "chat_ack", messageId: msg.messageId, accepted: true, sessionId: this.sessions.current.id });
|
|
2978
|
+
}
|
|
2907
2979
|
return this.handleChat(msg.content, msg.images);
|
|
2980
|
+
}
|
|
2908
2981
|
case "command":
|
|
2909
2982
|
return this.handleCommand(msg.name, msg.args);
|
|
2910
2983
|
case "confirm_response":
|
|
@@ -2946,7 +3019,7 @@ var SessionHandler = class {
|
|
|
2946
3019
|
case "hub_start":
|
|
2947
3020
|
return this.handleHubStart(msg);
|
|
2948
3021
|
case "hub_abort":
|
|
2949
|
-
this.
|
|
3022
|
+
this.cancelHub();
|
|
2950
3023
|
return;
|
|
2951
3024
|
case "hub_steer": {
|
|
2952
3025
|
const resolve4 = this.pendingHubReview.get(msg.requestId);
|
|
@@ -2961,7 +3034,10 @@ var SessionHandler = class {
|
|
|
2961
3034
|
return;
|
|
2962
3035
|
}
|
|
2963
3036
|
}
|
|
2964
|
-
onDisconnect() {
|
|
3037
|
+
onDisconnect(closeSocket = true) {
|
|
3038
|
+
if (this.disconnected) return;
|
|
3039
|
+
this.disconnected = true;
|
|
3040
|
+
if (closeSocket && this.ws.readyState === this.ws.OPEN) this.ws.close(1001, "Connection closed; reconnect to restore session");
|
|
2965
3041
|
runLifecycleHooks(mergePluginHooks(this.config.get("hooks") ?? void 0, this.config.getConfigDir()), "Stop", { sessionId: this.sessions.current?.id }, { configDir: this.config.getConfigDir() });
|
|
2966
3042
|
this.toolExecutor.cancelAll();
|
|
2967
3043
|
if (this.abortController) this.abortController.abort();
|
|
@@ -2969,18 +3045,24 @@ var SessionHandler = class {
|
|
|
2969
3045
|
this.pendingAskUser.clear();
|
|
2970
3046
|
for (const resolve4 of this.pendingAutoPause.values()) resolve4({ action: "stop" });
|
|
2971
3047
|
this.pendingAutoPause.clear();
|
|
2972
|
-
this.
|
|
3048
|
+
this.cancelHub();
|
|
3049
|
+
void this.saveIfNeeded();
|
|
2973
3050
|
if (this._bashSessionKey) clearBashSession(this._bashSessionKey);
|
|
2974
3051
|
}
|
|
3052
|
+
cancelHub() {
|
|
3053
|
+
this.hubOrchestrator?.abort();
|
|
3054
|
+
for (const resolve4 of this.pendingHubReview.values()) resolve4({ action: "stop" });
|
|
3055
|
+
this.pendingHubReview.clear();
|
|
3056
|
+
}
|
|
2975
3057
|
/** 根据当前模型 context window 更新工具输出截断上限 */
|
|
2976
3058
|
updateContextWindow() {
|
|
2977
3059
|
try {
|
|
2978
3060
|
const cfgOutputCap = this.config.get("maxToolOutputChars");
|
|
2979
|
-
if (typeof cfgOutputCap === "number" && cfgOutputCap > 0) setMaxOutputCap(cfgOutputCap);
|
|
2980
3061
|
const provider = this.providers.get(this.currentProvider);
|
|
2981
3062
|
const modelInfo = provider?.info.models.find((m) => m.id === this.currentModel);
|
|
2982
|
-
|
|
3063
|
+
this.toolExecutor.maxOutputChars = getMaxOutputChars(modelInfo?.contextWindow, cfgOutputCap ?? 0);
|
|
2983
3064
|
} catch {
|
|
3065
|
+
this.toolExecutor.maxOutputChars = DEFAULT_MAX_TOOL_OUTPUT_CHARS;
|
|
2984
3066
|
}
|
|
2985
3067
|
}
|
|
2986
3068
|
/** 获取当前模型的 context window 大小(0 表示未知)*/
|
|
@@ -3032,12 +3114,24 @@ var SessionHandler = class {
|
|
|
3032
3114
|
return total;
|
|
3033
3115
|
}
|
|
3034
3116
|
/** Save session only if it exists and has messages (never persist empty "Untitled" sessions). */
|
|
3035
|
-
saveIfNeeded() {
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3117
|
+
async saveIfNeeded() {
|
|
3118
|
+
const session = this.sessions.current;
|
|
3119
|
+
if (!session?.messages.length) return;
|
|
3120
|
+
const scope = this.config.getConfigDir();
|
|
3121
|
+
try {
|
|
3122
|
+
if (session.dirty) autoTrimSessionIfNeeded(session);
|
|
3123
|
+
await this.sessions.save();
|
|
3124
|
+
if (session.dirty) return;
|
|
3125
|
+
this.unsavedSessions.delete(session.id);
|
|
3126
|
+
_SessionHandler.recovery.get(scope)?.delete(session.id);
|
|
3127
|
+
} catch (err) {
|
|
3128
|
+
this.unsavedSessions.set(session.id, session);
|
|
3129
|
+
const recovered = _SessionHandler.recovery.get(scope) ?? /* @__PURE__ */ new Map();
|
|
3130
|
+
recovered.set(session.id, session);
|
|
3131
|
+
_SessionHandler.recovery.set(scope, recovered);
|
|
3132
|
+
const message = `Session ${session.id} was NOT saved: ${err instanceof Error ? err.message : String(err)}. Content is retained in memory; use /session save to retry. Do not stop the server before saving.`;
|
|
3133
|
+
this.send({ type: "info", message });
|
|
3134
|
+
process.stderr.write(message + "\n");
|
|
3041
3135
|
}
|
|
3042
3136
|
}
|
|
3043
3137
|
/** Lazily create a session if none exists yet (deferred from constructor). */
|
|
@@ -3162,20 +3256,15 @@ var SessionHandler = class {
|
|
|
3162
3256
|
const kinds = [...new Set(secretHits.map((h) => h.kind))].join(", ");
|
|
3163
3257
|
this.send({ type: "info", message: "Possible secret in prompt (" + kinds + ")." });
|
|
3164
3258
|
}
|
|
3165
|
-
const
|
|
3166
|
-
|
|
3259
|
+
const imageError = imageValidationError(images);
|
|
3260
|
+
if (imageError) {
|
|
3261
|
+
this.send({ type: "error", message: imageError });
|
|
3262
|
+
return;
|
|
3263
|
+
}
|
|
3167
3264
|
let msgContent;
|
|
3168
3265
|
if (images && images.length > 0) {
|
|
3169
3266
|
const parts = [];
|
|
3170
3267
|
for (const img of images) {
|
|
3171
|
-
if (!ALLOWED_IMAGE_MIMES.has(img.mime)) {
|
|
3172
|
-
this.send({ type: "error", message: `Rejected image: unsupported MIME type "${img.mime}"` });
|
|
3173
|
-
return;
|
|
3174
|
-
}
|
|
3175
|
-
if (img.data && img.data.length > MAX_IMAGE_SIZE) {
|
|
3176
|
-
this.send({ type: "error", message: `Rejected image: too large (${(img.data.length / 1024 / 1024).toFixed(1)} MB, max 10 MB)` });
|
|
3177
|
-
return;
|
|
3178
|
-
}
|
|
3179
3268
|
parts.push({
|
|
3180
3269
|
type: "image_url",
|
|
3181
3270
|
image_url: { url: `data:${img.mime};base64,${img.data}` }
|
|
@@ -3229,7 +3318,7 @@ var SessionHandler = class {
|
|
|
3229
3318
|
this.send({ type: "error", message });
|
|
3230
3319
|
} finally {
|
|
3231
3320
|
this.processing = false;
|
|
3232
|
-
this.saveIfNeeded();
|
|
3321
|
+
await this.saveIfNeeded();
|
|
3233
3322
|
this.sendStatus();
|
|
3234
3323
|
this.sendSessionList();
|
|
3235
3324
|
}
|
|
@@ -3746,6 +3835,10 @@ ${summaryContent}`,
|
|
|
3746
3835
|
}
|
|
3747
3836
|
// ── Commands ─────────────────────────────────────────────────────
|
|
3748
3837
|
async handleCommand(name, args) {
|
|
3838
|
+
if (this.processing && ["session", "clear", "provider", "model", "rewind", "branch", "compact"].includes(name)) {
|
|
3839
|
+
this.send({ type: "info", message: "Stop the current request before changing sessions or conversation settings." });
|
|
3840
|
+
return;
|
|
3841
|
+
}
|
|
3749
3842
|
const cliOnlyReason = CLI_ONLY_WEB_COMMANDS.get(name);
|
|
3750
3843
|
if (cliOnlyReason) {
|
|
3751
3844
|
this.send({
|
|
@@ -4374,7 +4467,7 @@ async function startWebServer(options = {}) {
|
|
|
4374
4467
|
next();
|
|
4375
4468
|
});
|
|
4376
4469
|
const server = createServer(app);
|
|
4377
|
-
const WS_MAX_PAYLOAD =
|
|
4470
|
+
const WS_MAX_PAYLOAD = MAX_CHAT_BYTES;
|
|
4378
4471
|
const WS_MSG_RATE_PER_SEC = 30;
|
|
4379
4472
|
const WS_MSG_BURST = 60;
|
|
4380
4473
|
const WS_PREAUTH_TIMEOUT_MS = 3e4;
|
|
@@ -4447,6 +4540,7 @@ async function startWebServer(options = {}) {
|
|
|
4447
4540
|
app.get("/api/status", (_req, res) => {
|
|
4448
4541
|
res.json({
|
|
4449
4542
|
version: VERSION,
|
|
4543
|
+
chatLimits: { maxChatBytes: MAX_CHAT_BYTES, maxImageBytes: MAX_IMAGE_BYTES },
|
|
4450
4544
|
providers: availableProviders.map((p) => ({
|
|
4451
4545
|
id: p.info.id,
|
|
4452
4546
|
displayName: p.info.displayName,
|
|
@@ -4655,11 +4749,6 @@ async function startWebServer(options = {}) {
|
|
|
4655
4749
|
const tabId = params.get("tabId") || `tab-${Date.now()}`;
|
|
4656
4750
|
const cookies = parseCookie(req.headers.cookie ?? "");
|
|
4657
4751
|
const token = cookies["aicli_token"] || "";
|
|
4658
|
-
const existing = handlers.get(tabId);
|
|
4659
|
-
if (existing) {
|
|
4660
|
-
existing.onDisconnect();
|
|
4661
|
-
handlers.delete(tabId);
|
|
4662
|
-
}
|
|
4663
4752
|
let tokens = WS_MSG_BURST;
|
|
4664
4753
|
let lastRefill = Date.now();
|
|
4665
4754
|
const takeToken = () => {
|
|
@@ -4675,6 +4764,32 @@ async function startWebServer(options = {}) {
|
|
|
4675
4764
|
};
|
|
4676
4765
|
let authenticatedUser = null;
|
|
4677
4766
|
let handler = null;
|
|
4767
|
+
let handlerKey = "";
|
|
4768
|
+
const attachHandler = (resources) => {
|
|
4769
|
+
if (handler) {
|
|
4770
|
+
handler.onDisconnect(false);
|
|
4771
|
+
if (handlers.get(handlerKey) === handler) handlers.delete(handlerKey);
|
|
4772
|
+
}
|
|
4773
|
+
handlerKey = JSON.stringify([authenticatedUser, tabId]);
|
|
4774
|
+
const existing = handlers.get(handlerKey);
|
|
4775
|
+
existing?.onDisconnect();
|
|
4776
|
+
const attached = new SessionHandler(ws, { ...resources, sessions: new SessionManager(resources.config) });
|
|
4777
|
+
handlers.set(handlerKey, attached);
|
|
4778
|
+
return attached;
|
|
4779
|
+
};
|
|
4780
|
+
let alive = true;
|
|
4781
|
+
ws.on("pong", () => {
|
|
4782
|
+
alive = true;
|
|
4783
|
+
});
|
|
4784
|
+
const heartbeat = setInterval(() => {
|
|
4785
|
+
if (!alive) {
|
|
4786
|
+
ws.terminate();
|
|
4787
|
+
return;
|
|
4788
|
+
}
|
|
4789
|
+
alive = false;
|
|
4790
|
+
if (ws.readyState === ws.OPEN) ws.ping();
|
|
4791
|
+
}, 3e4);
|
|
4792
|
+
heartbeat.unref();
|
|
4678
4793
|
let preAuthTimer = setTimeout(() => {
|
|
4679
4794
|
if (!handler && ws.readyState === ws.OPEN) {
|
|
4680
4795
|
try {
|
|
@@ -4695,14 +4810,12 @@ async function startWebServer(options = {}) {
|
|
|
4695
4810
|
const authRequired = authManager.isEnabled();
|
|
4696
4811
|
if (!authRequired) {
|
|
4697
4812
|
console.log(` \u2713 Tab connected: ${tabId.slice(0, 12)} (no auth) (total: ${handlers.size + 1})`);
|
|
4698
|
-
handler =
|
|
4699
|
-
handlers.set(tabId, handler);
|
|
4813
|
+
handler = attachHandler(shared);
|
|
4700
4814
|
clearPreAuthTimer();
|
|
4701
4815
|
} else if (authenticatedUser) {
|
|
4702
4816
|
console.log(` \u2713 Tab connected: ${tabId.slice(0, 12)} (user: ${authenticatedUser}) (total: ${handlers.size + 1})`);
|
|
4703
4817
|
const userShared = getUserShared(authenticatedUser);
|
|
4704
|
-
handler =
|
|
4705
|
-
handlers.set(tabId, handler);
|
|
4818
|
+
handler = attachHandler(userShared);
|
|
4706
4819
|
clearPreAuthTimer();
|
|
4707
4820
|
} else {
|
|
4708
4821
|
console.log(` \u23F3 Tab waiting auth: ${tabId.slice(0, 12)}`);
|
|
@@ -4747,8 +4860,7 @@ async function startWebServer(options = {}) {
|
|
|
4747
4860
|
authManager.login(username, password);
|
|
4748
4861
|
authenticatedUser = username;
|
|
4749
4862
|
const userShared = getUserShared(username);
|
|
4750
|
-
handler =
|
|
4751
|
-
handlers.set(tabId, handler);
|
|
4863
|
+
handler = attachHandler(userShared);
|
|
4752
4864
|
clearPreAuthTimer();
|
|
4753
4865
|
console.log(` \u2713 User registered & connected: ${username} (tab: ${tabId.slice(0, 12)})`);
|
|
4754
4866
|
ws.send(JSON.stringify({ type: "auth_result", success: true, username }));
|
|
@@ -4763,8 +4875,7 @@ async function startWebServer(options = {}) {
|
|
|
4763
4875
|
}
|
|
4764
4876
|
authenticatedUser = verifiedUser;
|
|
4765
4877
|
const userShared = getUserShared(verifiedUser);
|
|
4766
|
-
handler =
|
|
4767
|
-
handlers.set(tabId, handler);
|
|
4878
|
+
handler = attachHandler(userShared);
|
|
4768
4879
|
clearPreAuthTimer();
|
|
4769
4880
|
console.log(` \u2713 Token auth: ${verifiedUser} (tab: ${tabId.slice(0, 12)})`);
|
|
4770
4881
|
ws.send(JSON.stringify({ type: "auth_result", success: true, username: verifiedUser }));
|
|
@@ -4778,8 +4889,7 @@ async function startWebServer(options = {}) {
|
|
|
4778
4889
|
}
|
|
4779
4890
|
authenticatedUser = username;
|
|
4780
4891
|
const userShared = getUserShared(username);
|
|
4781
|
-
handler =
|
|
4782
|
-
handlers.set(tabId, handler);
|
|
4892
|
+
handler = attachHandler(userShared);
|
|
4783
4893
|
clearPreAuthTimer();
|
|
4784
4894
|
console.log(` \u2713 User logged in: ${username} (tab: ${tabId.slice(0, 12)})`);
|
|
4785
4895
|
ws.send(JSON.stringify({ type: "auth_result", success: true, username }));
|
|
@@ -4807,10 +4917,11 @@ async function startWebServer(options = {}) {
|
|
|
4807
4917
|
}
|
|
4808
4918
|
});
|
|
4809
4919
|
ws.on("close", () => {
|
|
4920
|
+
clearInterval(heartbeat);
|
|
4810
4921
|
clearPreAuthTimer();
|
|
4811
|
-
const wasTracked = handlers.
|
|
4922
|
+
const wasTracked = handlers.get(handlerKey) === handler;
|
|
4812
4923
|
if (handler) handler.onDisconnect();
|
|
4813
|
-
if (wasTracked) handlers.delete(
|
|
4924
|
+
if (wasTracked) handlers.delete(handlerKey);
|
|
4814
4925
|
console.log(` \u2717 Tab disconnected: ${tabId.slice(0, 12)} (total: ${handlers.size})`);
|
|
4815
4926
|
});
|
|
4816
4927
|
ws.on("error", (err) => {
|
|
@@ -5,8 +5,9 @@ import {
|
|
|
5
5
|
loadIndex,
|
|
6
6
|
removeFile,
|
|
7
7
|
saveIndex,
|
|
8
|
+
symbolFingerprint,
|
|
8
9
|
upsertFileSymbols
|
|
9
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-WQ3OMEC4.js";
|
|
10
11
|
import "./chunk-IW3Q7AE5.js";
|
|
11
12
|
export {
|
|
12
13
|
clearIndex,
|
|
@@ -14,5 +15,6 @@ export {
|
|
|
14
15
|
loadIndex,
|
|
15
16
|
removeFile,
|
|
16
17
|
saveIndex,
|
|
18
|
+
symbolFingerprint,
|
|
17
19
|
upsertFileSymbols
|
|
18
20
|
};
|