@adhdev/daemon-standalone 0.8.75 → 0.8.77
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/index.js +1133 -177
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/public/assets/index-BcJIZCMQ.js +72 -0
- package/public/assets/index-DnFQ3_W9.css +1 -0
- package/public/assets/{terminal-fZXg9GuG.js → terminal-2SYILvaH.js} +39 -17
- package/public/index.html +2 -2
- package/public/assets/index-B57vauO6.css +0 -1
- package/public/assets/index-BwdOhq7c.js +0 -72
package/dist/index.js
CHANGED
|
@@ -9244,7 +9244,7 @@ var require_dist = __commonJS({
|
|
|
9244
9244
|
}
|
|
9245
9245
|
}
|
|
9246
9246
|
};
|
|
9247
|
-
var
|
|
9247
|
+
var import_crypto3 = require("crypto");
|
|
9248
9248
|
var path5 = __toESM2(require("path"));
|
|
9249
9249
|
function normalizeSlug(input) {
|
|
9250
9250
|
return input.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
|
|
@@ -9360,7 +9360,7 @@ var require_dist = __commonJS({
|
|
|
9360
9360
|
var SessionHostRegistry = class {
|
|
9361
9361
|
sessions = /* @__PURE__ */ new Map();
|
|
9362
9362
|
createSession(payload) {
|
|
9363
|
-
const sessionId = payload.sessionId || (0,
|
|
9363
|
+
const sessionId = payload.sessionId || (0, import_crypto3.randomUUID)();
|
|
9364
9364
|
if (this.sessions.has(sessionId)) {
|
|
9365
9365
|
throw new Error(`Session already exists: ${sessionId}`);
|
|
9366
9366
|
}
|
|
@@ -26823,9 +26823,9 @@ var init_handler = __esm({
|
|
|
26823
26823
|
if (this.fsw.closed) {
|
|
26824
26824
|
return;
|
|
26825
26825
|
}
|
|
26826
|
-
const
|
|
26826
|
+
const dirname4 = sp.dirname(file2);
|
|
26827
26827
|
const basename3 = sp.basename(file2);
|
|
26828
|
-
const parent = this.fsw._getWatchedDir(
|
|
26828
|
+
const parent = this.fsw._getWatchedDir(dirname4);
|
|
26829
26829
|
let prevStats = stats;
|
|
26830
26830
|
if (parent.has(basename3))
|
|
26831
26831
|
return;
|
|
@@ -26852,7 +26852,7 @@ var init_handler = __esm({
|
|
|
26852
26852
|
prevStats = newStats2;
|
|
26853
26853
|
}
|
|
26854
26854
|
} catch (error48) {
|
|
26855
|
-
this.fsw._remove(
|
|
26855
|
+
this.fsw._remove(dirname4, basename3);
|
|
26856
26856
|
}
|
|
26857
26857
|
} else if (parent.has(basename3)) {
|
|
26858
26858
|
const at2 = newStats.atimeMs;
|
|
@@ -27927,7 +27927,7 @@ var require_dist2 = __commonJS({
|
|
|
27927
27927
|
};
|
|
27928
27928
|
}
|
|
27929
27929
|
function generateMachineId() {
|
|
27930
|
-
return `${MACHINE_ID_PREFIX}${(0,
|
|
27930
|
+
return `${MACHINE_ID_PREFIX}${(0, import_crypto3.randomUUID)().replace(/-/g, "")}`;
|
|
27931
27931
|
}
|
|
27932
27932
|
function isStableMachineId(machineId) {
|
|
27933
27933
|
return typeof machineId === "string" && machineId.startsWith(MACHINE_ID_PREFIX);
|
|
@@ -28046,7 +28046,7 @@ var require_dist2 = __commonJS({
|
|
|
28046
28046
|
var import_os2;
|
|
28047
28047
|
var import_path2;
|
|
28048
28048
|
var import_fs;
|
|
28049
|
-
var
|
|
28049
|
+
var import_crypto3;
|
|
28050
28050
|
var DEFAULT_CONFIG;
|
|
28051
28051
|
var MACHINE_ID_PREFIX;
|
|
28052
28052
|
var init_config = __esm2({
|
|
@@ -28055,7 +28055,7 @@ var require_dist2 = __commonJS({
|
|
|
28055
28055
|
import_os2 = require("os");
|
|
28056
28056
|
import_path2 = require("path");
|
|
28057
28057
|
import_fs = require("fs");
|
|
28058
|
-
|
|
28058
|
+
import_crypto3 = require("crypto");
|
|
28059
28059
|
DEFAULT_CONFIG = {
|
|
28060
28060
|
serverUrl: "https://api.adhf.dev",
|
|
28061
28061
|
allowServerApiProxy: false,
|
|
@@ -31510,6 +31510,7 @@ ${data.message || ""}`.trim();
|
|
|
31510
31510
|
resolveChatMessageKind: () => resolveChatMessageKind,
|
|
31511
31511
|
resolveDebugRuntimeConfig: () => resolveDebugRuntimeConfig,
|
|
31512
31512
|
resolveSessionHostAppName: () => resolveSessionHostAppName2,
|
|
31513
|
+
runAsyncBatch: () => runAsyncBatch2,
|
|
31513
31514
|
saveConfig: () => saveConfig,
|
|
31514
31515
|
saveState: () => saveState,
|
|
31515
31516
|
setDebugRuntimeConfig: () => setDebugRuntimeConfig,
|
|
@@ -32228,12 +32229,66 @@ ${data.message || ""}`.trim();
|
|
|
32228
32229
|
const availableMem = darwinAvail != null ? darwinAvail : freeMem;
|
|
32229
32230
|
return { totalMem, freeMem, availableMem };
|
|
32230
32231
|
}
|
|
32232
|
+
var LIVE_LIFECYCLES = /* @__PURE__ */ new Set(["starting", "running", "stopping", "interrupted"]);
|
|
32233
|
+
function isSessionHostLiveRuntime(record2) {
|
|
32234
|
+
const lifecycle = String(record2?.lifecycle || "").trim();
|
|
32235
|
+
return LIVE_LIFECYCLES.has(lifecycle);
|
|
32236
|
+
}
|
|
32237
|
+
function getSessionHostRecoveryLabel(meta3) {
|
|
32238
|
+
const recoveryState = typeof meta3?.runtimeRecoveryState === "string" ? String(meta3.runtimeRecoveryState).trim() : "";
|
|
32239
|
+
if (!recoveryState) return null;
|
|
32240
|
+
if (recoveryState === "auto_resumed") return "restored after restart";
|
|
32241
|
+
if (recoveryState === "resume_failed") return "restore failed";
|
|
32242
|
+
if (recoveryState === "host_restart_interrupted") return "host restart interrupted";
|
|
32243
|
+
if (recoveryState === "orphan_snapshot") return "snapshot recovered";
|
|
32244
|
+
return recoveryState.replace(/_/g, " ");
|
|
32245
|
+
}
|
|
32246
|
+
function isSessionHostRecoverySnapshot(record2) {
|
|
32247
|
+
if (!record2) return false;
|
|
32248
|
+
if (isSessionHostLiveRuntime(record2)) return false;
|
|
32249
|
+
const lifecycle = String(record2.lifecycle || "").trim();
|
|
32250
|
+
if (lifecycle && lifecycle !== "stopped" && lifecycle !== "failed") {
|
|
32251
|
+
return false;
|
|
32252
|
+
}
|
|
32253
|
+
const meta3 = record2.meta || void 0;
|
|
32254
|
+
if (meta3?.restoredFromStorage === true) return true;
|
|
32255
|
+
return getSessionHostRecoveryLabel(meta3) !== null;
|
|
32256
|
+
}
|
|
32257
|
+
function getSessionHostSurfaceKind(record2) {
|
|
32258
|
+
if (isSessionHostLiveRuntime(record2)) return "live_runtime";
|
|
32259
|
+
if (isSessionHostRecoverySnapshot(record2)) return "recovery_snapshot";
|
|
32260
|
+
return "inactive_record";
|
|
32261
|
+
}
|
|
32262
|
+
function partitionSessionHostRecords(records) {
|
|
32263
|
+
const liveRuntimes = [];
|
|
32264
|
+
const recoverySnapshots = [];
|
|
32265
|
+
const inactiveRecords = [];
|
|
32266
|
+
for (const record2 of records) {
|
|
32267
|
+
const kind = getSessionHostSurfaceKind(record2);
|
|
32268
|
+
if (kind === "live_runtime") {
|
|
32269
|
+
liveRuntimes.push(record2);
|
|
32270
|
+
} else if (kind === "recovery_snapshot") {
|
|
32271
|
+
recoverySnapshots.push(record2);
|
|
32272
|
+
} else {
|
|
32273
|
+
inactiveRecords.push(record2);
|
|
32274
|
+
}
|
|
32275
|
+
}
|
|
32276
|
+
return {
|
|
32277
|
+
liveRuntimes,
|
|
32278
|
+
recoverySnapshots,
|
|
32279
|
+
inactiveRecords
|
|
32280
|
+
};
|
|
32281
|
+
}
|
|
32282
|
+
function partitionSessionHostDiagnosticsSessions(records) {
|
|
32283
|
+
return partitionSessionHostRecords(records || []);
|
|
32284
|
+
}
|
|
32231
32285
|
var DEFAULT_ACTIVE_CHAT_POLL_STATUSES = /* @__PURE__ */ new Set([
|
|
32232
32286
|
"generating",
|
|
32233
32287
|
"waiting_approval",
|
|
32234
32288
|
"starting"
|
|
32235
32289
|
]);
|
|
32236
32290
|
var DEFAULT_CHAT_TAIL_RECENT_MESSAGE_GRACE_MS = 8e3;
|
|
32291
|
+
var LIVE_RUNTIME_LIFECYCLES = /* @__PURE__ */ new Set(["starting", "running", "stopping", "interrupted"]);
|
|
32237
32292
|
function parseMessageTimestamp(value) {
|
|
32238
32293
|
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
32239
32294
|
if (typeof value === "string") {
|
|
@@ -32242,6 +32297,23 @@ ${data.message || ""}`.trim();
|
|
|
32242
32297
|
}
|
|
32243
32298
|
return 0;
|
|
32244
32299
|
}
|
|
32300
|
+
function isDefinitelyNonLiveRuntimeSession(session) {
|
|
32301
|
+
const surfaceKind = String(session?.runtimeSurfaceKind || "").trim();
|
|
32302
|
+
if (surfaceKind === "live_runtime") return false;
|
|
32303
|
+
if (surfaceKind === "recovery_snapshot") return true;
|
|
32304
|
+
if (surfaceKind === "inactive_record") return false;
|
|
32305
|
+
const lifecycle = String(session?.runtimeLifecycle || "").trim();
|
|
32306
|
+
if (lifecycle && LIVE_RUNTIME_LIFECYCLES.has(lifecycle)) return false;
|
|
32307
|
+
const inferredSurfaceKind = getSessionHostSurfaceKind({
|
|
32308
|
+
lifecycle: lifecycle || null,
|
|
32309
|
+
meta: {
|
|
32310
|
+
restoredFromStorage: session?.runtimeRestoredFromStorage === true,
|
|
32311
|
+
...session?.runtimeRecoveryState ? { runtimeRecoveryState: session.runtimeRecoveryState } : {}
|
|
32312
|
+
}
|
|
32313
|
+
});
|
|
32314
|
+
if (inferredSurfaceKind === "recovery_snapshot") return true;
|
|
32315
|
+
return false;
|
|
32316
|
+
}
|
|
32245
32317
|
function classifyHotChatSessionsForSubscriptionFlush2(sessions, previousHotSessionIds, options = {}) {
|
|
32246
32318
|
const now = options.now ?? Date.now();
|
|
32247
32319
|
const recentMessageGraceMs = Math.max(
|
|
@@ -32250,9 +32322,14 @@ ${data.message || ""}`.trim();
|
|
|
32250
32322
|
);
|
|
32251
32323
|
const activeStatuses = options.activeStatuses ?? DEFAULT_ACTIVE_CHAT_POLL_STATUSES;
|
|
32252
32324
|
const active = /* @__PURE__ */ new Set();
|
|
32325
|
+
const excluded = /* @__PURE__ */ new Set();
|
|
32253
32326
|
for (const session of sessions) {
|
|
32254
32327
|
const sessionId = typeof session?.id === "string" ? session.id : "";
|
|
32255
32328
|
if (!sessionId) continue;
|
|
32329
|
+
if (isDefinitelyNonLiveRuntimeSession(session)) {
|
|
32330
|
+
excluded.add(sessionId);
|
|
32331
|
+
continue;
|
|
32332
|
+
}
|
|
32256
32333
|
const status = String(session?.status || "").toLowerCase();
|
|
32257
32334
|
const lastMessageAt = parseMessageTimestamp(session?.lastMessageAt);
|
|
32258
32335
|
const recentlyUpdated = lastMessageAt > 0 && now - lastMessageAt <= recentMessageGraceMs;
|
|
@@ -32261,13 +32338,39 @@ ${data.message || ""}`.trim();
|
|
|
32261
32338
|
}
|
|
32262
32339
|
}
|
|
32263
32340
|
const finalizing = new Set(
|
|
32264
|
-
Array.from(previousHotSessionIds).filter((sessionId) => !active.has(sessionId))
|
|
32341
|
+
Array.from(previousHotSessionIds).filter((sessionId) => !active.has(sessionId) && !excluded.has(sessionId))
|
|
32265
32342
|
);
|
|
32266
32343
|
return { active, finalizing };
|
|
32267
32344
|
}
|
|
32268
32345
|
var import_ws2 = __toESM2(require("ws"));
|
|
32269
32346
|
var http = __toESM2(require("http"));
|
|
32270
32347
|
init_logger();
|
|
32348
|
+
function normalizeTitle(value) {
|
|
32349
|
+
return String(value || "").trim().replace(/\s+/g, " ").toLowerCase();
|
|
32350
|
+
}
|
|
32351
|
+
function titlesMatch(lhs, rhs) {
|
|
32352
|
+
const a = normalizeTitle(lhs);
|
|
32353
|
+
const b2 = normalizeTitle(rhs);
|
|
32354
|
+
if (!a || !b2) return false;
|
|
32355
|
+
return a === b2 || a.includes(b2) || b2.includes(a);
|
|
32356
|
+
}
|
|
32357
|
+
function resolveCdpPageTarget(params) {
|
|
32358
|
+
const { pages, pinnedTargetId, previousPageTitle } = params;
|
|
32359
|
+
if (pages.length === 0) return { target: null, retargeted: false };
|
|
32360
|
+
if (!pinnedTargetId) {
|
|
32361
|
+
return { target: pages[0] || null, retargeted: false };
|
|
32362
|
+
}
|
|
32363
|
+
const exact = pages.find((page) => page.id === pinnedTargetId);
|
|
32364
|
+
if (exact) return { target: exact, retargeted: false };
|
|
32365
|
+
const titleMatchesList = pages.filter((page) => titlesMatch(page.title, previousPageTitle));
|
|
32366
|
+
if (titleMatchesList.length === 1) {
|
|
32367
|
+
return { target: titleMatchesList[0], retargeted: true };
|
|
32368
|
+
}
|
|
32369
|
+
if (pages.length === 1) {
|
|
32370
|
+
return { target: pages[0], retargeted: true };
|
|
32371
|
+
}
|
|
32372
|
+
return { target: null, retargeted: false };
|
|
32373
|
+
}
|
|
32271
32374
|
var DaemonCdpManager = class {
|
|
32272
32375
|
ws = null;
|
|
32273
32376
|
browserWs = null;
|
|
@@ -32428,18 +32531,28 @@ ${data.message || ""}`.trim();
|
|
|
32428
32531
|
resolve11(targets.find((t) => t.webSocketDebuggerUrl) || null);
|
|
32429
32532
|
return;
|
|
32430
32533
|
}
|
|
32431
|
-
const
|
|
32432
|
-
const
|
|
32534
|
+
const titleFilteredPages = pages.filter((t) => !this.isNonMainTitle(t.title || ""));
|
|
32535
|
+
const mainPages = titleFilteredPages.filter((t) => this.isMainPageUrl(t.url));
|
|
32536
|
+
const list = mainPages.length > 0 ? mainPages : titleFilteredPages.length > 0 ? titleFilteredPages : pages;
|
|
32433
32537
|
this.log(`[CDP] pages(${list.length}): ${list.map((t) => `"${t.title}"`).join(", ")}`);
|
|
32434
|
-
|
|
32435
|
-
|
|
32436
|
-
|
|
32437
|
-
|
|
32438
|
-
|
|
32439
|
-
|
|
32440
|
-
|
|
32441
|
-
|
|
32538
|
+
const previousTargetId = this._targetId;
|
|
32539
|
+
const selected = resolveCdpPageTarget({
|
|
32540
|
+
pages: list,
|
|
32541
|
+
pinnedTargetId: previousTargetId,
|
|
32542
|
+
previousPageTitle: this._pageTitle
|
|
32543
|
+
});
|
|
32544
|
+
if (selected.target) {
|
|
32545
|
+
if (selected.retargeted && previousTargetId && previousTargetId !== selected.target.id) {
|
|
32546
|
+
this.log(`[CDP] Target ${previousTargetId} rekeyed to ${selected.target.id}`);
|
|
32547
|
+
this._targetId = selected.target.id;
|
|
32442
32548
|
}
|
|
32549
|
+
this._pageTitle = selected.target.title || "";
|
|
32550
|
+
resolve11(selected.target);
|
|
32551
|
+
return;
|
|
32552
|
+
}
|
|
32553
|
+
if (previousTargetId) {
|
|
32554
|
+
this.log(`[CDP] Target ${previousTargetId} not found in page list`);
|
|
32555
|
+
resolve11(null);
|
|
32443
32556
|
return;
|
|
32444
32557
|
}
|
|
32445
32558
|
this._pageTitle = list[0]?.title || "";
|
|
@@ -33867,7 +33980,17 @@ ${cleanBody}`;
|
|
|
33867
33980
|
init_chat_message_normalization();
|
|
33868
33981
|
var HISTORY_DIR = path7.join(os52.homedir(), ".adhdev", "history");
|
|
33869
33982
|
var RETAIN_DAYS = 30;
|
|
33983
|
+
var SAVED_HISTORY_INDEX_VERSION = 1;
|
|
33984
|
+
var SAVED_HISTORY_INDEX_FILE = ".saved-history-index.json";
|
|
33985
|
+
var SAVED_HISTORY_INDEX_LOCK_SUFFIX = ".lock";
|
|
33986
|
+
var SAVED_HISTORY_INDEX_LOCK_WAIT_MS = 1500;
|
|
33987
|
+
var SAVED_HISTORY_INDEX_LOCK_STALE_MS = 15e3;
|
|
33988
|
+
var SAVED_HISTORY_INDEX_LOCK_POLL_MS = 25;
|
|
33989
|
+
var SAVED_HISTORY_ROLLUP_THRESHOLD_BYTES = 16 * 1024 * 1024;
|
|
33870
33990
|
var savedHistorySessionCache = /* @__PURE__ */ new Map();
|
|
33991
|
+
var savedHistoryFileSummaryCache = /* @__PURE__ */ new Map();
|
|
33992
|
+
var savedHistoryBackgroundRefresh = /* @__PURE__ */ new Set();
|
|
33993
|
+
var savedHistoryRollupInFlight = /* @__PURE__ */ new Set();
|
|
33871
33994
|
var CODEX_STARTER_PROMPT_RE = /^(?:[›❯]\s*)?(?:Find and fix a bug in @filename|Improve documentation in @filename|Write tests for @filename|Explain this codebase|Summarize recent commits|Implement \{feature\}|Use \/skills(?: to list available skills)?|Run \/review on my current changes)$/i;
|
|
33872
33995
|
function normalizeHistoryComparable(text) {
|
|
33873
33996
|
return String(text || "").replace(/\s+/g, " ").trim();
|
|
@@ -33925,6 +34048,68 @@ ${cleanBody}`;
|
|
|
33925
34048
|
content
|
|
33926
34049
|
};
|
|
33927
34050
|
}
|
|
34051
|
+
function sortSavedHistorySessionSummaries(summaries) {
|
|
34052
|
+
return summaries.slice().sort((a, b2) => b2.lastMessageAt - a.lastMessageAt);
|
|
34053
|
+
}
|
|
34054
|
+
function buildSavedHistorySessionSummaryMapFromEntries(entries) {
|
|
34055
|
+
const summaries = /* @__PURE__ */ new Map();
|
|
34056
|
+
for (const entry of Array.from(entries.values())) {
|
|
34057
|
+
const fileSummary = entry.summary;
|
|
34058
|
+
if (!fileSummary || fileSummary.messageCount <= 0 || !fileSummary.lastMessageAt) continue;
|
|
34059
|
+
const existing = summaries.get(fileSummary.historySessionId);
|
|
34060
|
+
if (!existing) {
|
|
34061
|
+
summaries.set(fileSummary.historySessionId, {
|
|
34062
|
+
historySessionId: fileSummary.historySessionId,
|
|
34063
|
+
sessionTitle: fileSummary.sessionTitle,
|
|
34064
|
+
messageCount: fileSummary.messageCount,
|
|
34065
|
+
firstMessageAt: fileSummary.firstMessageAt,
|
|
34066
|
+
lastMessageAt: fileSummary.lastMessageAt,
|
|
34067
|
+
preview: fileSummary.preview,
|
|
34068
|
+
workspace: fileSummary.workspace
|
|
34069
|
+
});
|
|
34070
|
+
continue;
|
|
34071
|
+
}
|
|
34072
|
+
existing.messageCount += fileSummary.messageCount;
|
|
34073
|
+
if (!existing.firstMessageAt || fileSummary.firstMessageAt < existing.firstMessageAt) {
|
|
34074
|
+
existing.firstMessageAt = fileSummary.firstMessageAt;
|
|
34075
|
+
}
|
|
34076
|
+
if (fileSummary.lastMessageAt >= existing.lastMessageAt) {
|
|
34077
|
+
existing.lastMessageAt = fileSummary.lastMessageAt;
|
|
34078
|
+
if (fileSummary.sessionTitle) existing.sessionTitle = fileSummary.sessionTitle;
|
|
34079
|
+
if (fileSummary.preview) existing.preview = fileSummary.preview;
|
|
34080
|
+
}
|
|
34081
|
+
if (!existing.workspace && fileSummary.workspace) {
|
|
34082
|
+
existing.workspace = fileSummary.workspace;
|
|
34083
|
+
}
|
|
34084
|
+
}
|
|
34085
|
+
return Object.fromEntries(sortSavedHistorySessionSummaries(Array.from(summaries.values())).map((summary) => [summary.historySessionId, summary]));
|
|
34086
|
+
}
|
|
34087
|
+
function readPersistedSavedHistorySessionSummaries(dir) {
|
|
34088
|
+
try {
|
|
34089
|
+
const filePath = getSavedHistoryIndexFilePath(dir);
|
|
34090
|
+
if (!fs32.existsSync(filePath)) return null;
|
|
34091
|
+
const raw = JSON.parse(fs32.readFileSync(filePath, "utf-8"));
|
|
34092
|
+
if (!raw || raw.version !== SAVED_HISTORY_INDEX_VERSION || !raw.sessions || typeof raw.sessions !== "object") {
|
|
34093
|
+
return null;
|
|
34094
|
+
}
|
|
34095
|
+
return sortSavedHistorySessionSummaries(
|
|
34096
|
+
Object.values(raw.sessions).filter((summary) => !!summary && typeof summary.historySessionId === "string" && summary.messageCount > 0 && summary.lastMessageAt > 0).map((summary) => ({
|
|
34097
|
+
historySessionId: summary.historySessionId,
|
|
34098
|
+
sessionTitle: summary.sessionTitle,
|
|
34099
|
+
messageCount: summary.messageCount,
|
|
34100
|
+
firstMessageAt: summary.firstMessageAt,
|
|
34101
|
+
lastMessageAt: summary.lastMessageAt,
|
|
34102
|
+
preview: summary.preview,
|
|
34103
|
+
workspace: summary.workspace
|
|
34104
|
+
}))
|
|
34105
|
+
);
|
|
34106
|
+
} catch {
|
|
34107
|
+
return null;
|
|
34108
|
+
}
|
|
34109
|
+
}
|
|
34110
|
+
function shouldScheduleSavedHistoryRollup(totalBytes) {
|
|
34111
|
+
return Number.isFinite(totalBytes) && totalBytes >= SAVED_HISTORY_ROLLUP_THRESHOLD_BYTES;
|
|
34112
|
+
}
|
|
33928
34113
|
function sanitizeHistoryFileSegment(value) {
|
|
33929
34114
|
return String(value || "").replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
33930
34115
|
}
|
|
@@ -33938,71 +34123,386 @@ ${cleanBody}`;
|
|
|
33938
34123
|
return true;
|
|
33939
34124
|
}).sort().reverse();
|
|
33940
34125
|
}
|
|
33941
|
-
function
|
|
33942
|
-
|
|
34126
|
+
function normalizeSavedHistorySessionId(agentType, historySessionId) {
|
|
34127
|
+
const normalizedId = String(historySessionId || "").trim();
|
|
34128
|
+
if (!normalizedId) return "";
|
|
34129
|
+
const strictProviderId = normalizeProviderSessionId(agentType, normalizedId);
|
|
34130
|
+
if (strictProviderId) return strictProviderId;
|
|
34131
|
+
return agentType === "hermes-cli" ? "" : normalizedId;
|
|
34132
|
+
}
|
|
34133
|
+
function extractSavedHistorySessionIdFromFile(agentType, file2) {
|
|
34134
|
+
const match = file2.match(/^([A-Za-z0-9_-]+)_\d{4}-\d{2}-\d{2}\.jsonl$/);
|
|
34135
|
+
return normalizeSavedHistorySessionId(agentType, match?.[1] || "");
|
|
34136
|
+
}
|
|
34137
|
+
function buildSavedHistoryFileSignatureMap(dir, files) {
|
|
34138
|
+
return new Map(files.map((file2) => {
|
|
33943
34139
|
try {
|
|
33944
34140
|
const stat4 = fs32.statSync(path7.join(dir, file2));
|
|
33945
|
-
return `${file2}:${stat4.size}:${Math.trunc(stat4.mtimeMs)}
|
|
34141
|
+
return [file2, `${file2}:${stat4.size}:${Math.trunc(stat4.mtimeMs)}`];
|
|
33946
34142
|
} catch {
|
|
33947
|
-
return `${file2}:missing
|
|
33948
|
-
}
|
|
33949
|
-
})
|
|
33950
|
-
}
|
|
33951
|
-
function
|
|
33952
|
-
|
|
33953
|
-
|
|
33954
|
-
|
|
33955
|
-
|
|
33956
|
-
|
|
33957
|
-
|
|
33958
|
-
|
|
33959
|
-
|
|
33960
|
-
|
|
33961
|
-
|
|
33962
|
-
|
|
33963
|
-
|
|
33964
|
-
|
|
33965
|
-
|
|
33966
|
-
|
|
33967
|
-
|
|
33968
|
-
|
|
33969
|
-
|
|
33970
|
-
|
|
33971
|
-
|
|
33972
|
-
|
|
33973
|
-
|
|
33974
|
-
|
|
33975
|
-
|
|
34143
|
+
return [file2, `${file2}:missing`];
|
|
34144
|
+
}
|
|
34145
|
+
}));
|
|
34146
|
+
}
|
|
34147
|
+
function buildSavedHistoryCacheSignature(files, fileSignatures) {
|
|
34148
|
+
return files.map((file2) => fileSignatures.get(file2) || `${file2}:missing`).join("|");
|
|
34149
|
+
}
|
|
34150
|
+
function getSavedHistoryIndexFilePath(dir) {
|
|
34151
|
+
return path7.join(dir, SAVED_HISTORY_INDEX_FILE);
|
|
34152
|
+
}
|
|
34153
|
+
function getSavedHistoryIndexLockPath(dir) {
|
|
34154
|
+
return `${getSavedHistoryIndexFilePath(dir)}${SAVED_HISTORY_INDEX_LOCK_SUFFIX}`;
|
|
34155
|
+
}
|
|
34156
|
+
function sleepBlocking(ms2) {
|
|
34157
|
+
if (ms2 <= 0) return;
|
|
34158
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms2);
|
|
34159
|
+
}
|
|
34160
|
+
function loadPersistedSavedHistoryIndexFromFile(dir) {
|
|
34161
|
+
try {
|
|
34162
|
+
const filePath = getSavedHistoryIndexFilePath(dir);
|
|
34163
|
+
if (!fs32.existsSync(filePath)) return /* @__PURE__ */ new Map();
|
|
34164
|
+
const raw = JSON.parse(fs32.readFileSync(filePath, "utf-8"));
|
|
34165
|
+
if (!raw || raw.version !== SAVED_HISTORY_INDEX_VERSION || !raw.files || typeof raw.files !== "object") {
|
|
34166
|
+
return /* @__PURE__ */ new Map();
|
|
34167
|
+
}
|
|
34168
|
+
return new Map(
|
|
34169
|
+
Object.entries(raw.files).filter(([file2, entry]) => !!file2 && !!entry && typeof entry.signature === "string").map(([file2, entry]) => [file2, {
|
|
34170
|
+
signature: entry.signature,
|
|
34171
|
+
summary: entry.summary || null
|
|
34172
|
+
}])
|
|
34173
|
+
);
|
|
34174
|
+
} catch {
|
|
34175
|
+
return /* @__PURE__ */ new Map();
|
|
34176
|
+
}
|
|
34177
|
+
}
|
|
34178
|
+
function writePersistedSavedHistoryIndexFile(dir, entries) {
|
|
34179
|
+
const filePath = getSavedHistoryIndexFilePath(dir);
|
|
34180
|
+
const tempPath = `${filePath}.tmp`;
|
|
34181
|
+
const payload = {
|
|
34182
|
+
version: SAVED_HISTORY_INDEX_VERSION,
|
|
34183
|
+
files: Object.fromEntries(entries.entries()),
|
|
34184
|
+
sessions: buildSavedHistorySessionSummaryMapFromEntries(entries)
|
|
34185
|
+
};
|
|
34186
|
+
fs32.writeFileSync(tempPath, JSON.stringify(payload), "utf-8");
|
|
34187
|
+
fs32.renameSync(tempPath, filePath);
|
|
34188
|
+
}
|
|
34189
|
+
function acquireSavedHistoryIndexLock(dir) {
|
|
34190
|
+
const lockPath = getSavedHistoryIndexLockPath(dir);
|
|
34191
|
+
const deadline = Date.now() + SAVED_HISTORY_INDEX_LOCK_WAIT_MS;
|
|
34192
|
+
while (Date.now() <= deadline) {
|
|
34193
|
+
try {
|
|
34194
|
+
fs32.mkdirSync(lockPath);
|
|
34195
|
+
return () => {
|
|
33976
34196
|
try {
|
|
33977
|
-
|
|
34197
|
+
fs32.rmSync(lockPath, { recursive: true, force: true });
|
|
33978
34198
|
} catch {
|
|
33979
|
-
parsed = null;
|
|
33980
34199
|
}
|
|
33981
|
-
|
|
33982
|
-
|
|
33983
|
-
|
|
34200
|
+
};
|
|
34201
|
+
} catch (error48) {
|
|
34202
|
+
if (error48?.code !== "EEXIST") return null;
|
|
34203
|
+
try {
|
|
34204
|
+
const stat4 = fs32.statSync(lockPath);
|
|
34205
|
+
if (Date.now() - stat4.mtimeMs > SAVED_HISTORY_INDEX_LOCK_STALE_MS) {
|
|
34206
|
+
fs32.rmSync(lockPath, { recursive: true, force: true });
|
|
33984
34207
|
continue;
|
|
33985
34208
|
}
|
|
33986
|
-
|
|
33987
|
-
|
|
33988
|
-
|
|
33989
|
-
|
|
33990
|
-
|
|
33991
|
-
|
|
33992
|
-
|
|
33993
|
-
|
|
33994
|
-
|
|
33995
|
-
|
|
33996
|
-
|
|
33997
|
-
|
|
33998
|
-
|
|
33999
|
-
|
|
34000
|
-
|
|
34001
|
-
|
|
34002
|
-
|
|
34209
|
+
} catch {
|
|
34210
|
+
continue;
|
|
34211
|
+
}
|
|
34212
|
+
sleepBlocking(SAVED_HISTORY_INDEX_LOCK_POLL_MS);
|
|
34213
|
+
}
|
|
34214
|
+
}
|
|
34215
|
+
return null;
|
|
34216
|
+
}
|
|
34217
|
+
function withLockedPersistedSavedHistoryIndex(dir, callback) {
|
|
34218
|
+
const release2 = acquireSavedHistoryIndexLock(dir);
|
|
34219
|
+
if (!release2) return null;
|
|
34220
|
+
try {
|
|
34221
|
+
const entries = loadPersistedSavedHistoryIndexFromFile(dir);
|
|
34222
|
+
const result = callback(entries);
|
|
34223
|
+
writePersistedSavedHistoryIndexFile(dir, entries);
|
|
34224
|
+
return result;
|
|
34225
|
+
} catch {
|
|
34226
|
+
return null;
|
|
34227
|
+
} finally {
|
|
34228
|
+
release2();
|
|
34229
|
+
}
|
|
34230
|
+
}
|
|
34231
|
+
function loadPersistedSavedHistoryIndex(dir) {
|
|
34232
|
+
return loadPersistedSavedHistoryIndexFromFile(dir);
|
|
34233
|
+
}
|
|
34234
|
+
function savePersistedSavedHistoryIndex(dir, entries) {
|
|
34235
|
+
withLockedPersistedSavedHistoryIndex(dir, (currentEntries) => {
|
|
34236
|
+
const incomingFiles = new Set(Array.from(entries.keys()));
|
|
34237
|
+
for (const [file2, entry] of Array.from(entries.entries())) {
|
|
34238
|
+
const liveSignature = buildSavedHistoryFileSignature(dir, file2);
|
|
34239
|
+
const existingEntry = currentEntries.get(file2);
|
|
34240
|
+
if (existingEntry && existingEntry.signature !== liveSignature && entry.signature !== liveSignature) {
|
|
34241
|
+
continue;
|
|
34242
|
+
}
|
|
34243
|
+
if (entry.signature !== liveSignature && (!existingEntry || existingEntry.signature !== liveSignature)) {
|
|
34244
|
+
continue;
|
|
34245
|
+
}
|
|
34246
|
+
currentEntries.set(file2, entry.signature === liveSignature ? entry : {
|
|
34247
|
+
signature: liveSignature,
|
|
34248
|
+
summary: existingEntry?.summary || entry.summary
|
|
34249
|
+
});
|
|
34250
|
+
}
|
|
34251
|
+
for (const file2 of Array.from(currentEntries.keys())) {
|
|
34252
|
+
if (incomingFiles.has(file2)) continue;
|
|
34253
|
+
if (!fs32.existsSync(path7.join(dir, file2))) {
|
|
34254
|
+
currentEntries.delete(file2);
|
|
34255
|
+
}
|
|
34256
|
+
}
|
|
34257
|
+
});
|
|
34258
|
+
}
|
|
34259
|
+
function invalidatePersistedSavedHistoryIndex(agentType, dir) {
|
|
34260
|
+
try {
|
|
34261
|
+
fs32.rmSync(getSavedHistoryIndexFilePath(dir), { force: true });
|
|
34262
|
+
} catch {
|
|
34263
|
+
}
|
|
34264
|
+
savedHistorySessionCache.delete(agentType.replace(/[^a-zA-Z0-9_-]/g, "_"));
|
|
34265
|
+
}
|
|
34266
|
+
function buildSavedHistoryIndexFileSignature(dir) {
|
|
34267
|
+
try {
|
|
34268
|
+
const stat4 = fs32.statSync(getSavedHistoryIndexFilePath(dir));
|
|
34269
|
+
return `index:${stat4.size}:${Math.trunc(stat4.mtimeMs)}`;
|
|
34270
|
+
} catch {
|
|
34271
|
+
return "index:missing";
|
|
34003
34272
|
}
|
|
34004
|
-
|
|
34005
|
-
|
|
34273
|
+
}
|
|
34274
|
+
function historyDirectoryHasFilesNewerThanIndex(dir) {
|
|
34275
|
+
try {
|
|
34276
|
+
const indexStat = fs32.statSync(getSavedHistoryIndexFilePath(dir));
|
|
34277
|
+
const files = listHistoryFiles(dir);
|
|
34278
|
+
for (const file2 of files) {
|
|
34279
|
+
const stat4 = fs32.statSync(path7.join(dir, file2));
|
|
34280
|
+
if (stat4.mtimeMs > indexStat.mtimeMs) return true;
|
|
34281
|
+
}
|
|
34282
|
+
return false;
|
|
34283
|
+
} catch {
|
|
34284
|
+
return true;
|
|
34285
|
+
}
|
|
34286
|
+
}
|
|
34287
|
+
function buildSavedHistoryFileSignature(dir, file2) {
|
|
34288
|
+
try {
|
|
34289
|
+
const stat4 = fs32.statSync(path7.join(dir, file2));
|
|
34290
|
+
return `${file2}:${stat4.size}:${Math.trunc(stat4.mtimeMs)}`;
|
|
34291
|
+
} catch {
|
|
34292
|
+
return `${file2}:missing`;
|
|
34293
|
+
}
|
|
34294
|
+
}
|
|
34295
|
+
function persistSavedHistoryFileSummaryEntry(agentType, dir, file2, updater) {
|
|
34296
|
+
const filePath = path7.join(dir, file2);
|
|
34297
|
+
const result = withLockedPersistedSavedHistoryIndex(dir, (entries) => {
|
|
34298
|
+
const currentEntry = entries.get(file2) || null;
|
|
34299
|
+
const nextSummary = updater(currentEntry?.summary || null);
|
|
34300
|
+
const nextEntry = {
|
|
34301
|
+
signature: buildSavedHistoryFileSignature(dir, file2),
|
|
34302
|
+
summary: nextSummary
|
|
34303
|
+
};
|
|
34304
|
+
entries.set(file2, nextEntry);
|
|
34305
|
+
savedHistoryFileSummaryCache.set(filePath, nextEntry);
|
|
34306
|
+
return nextEntry;
|
|
34307
|
+
});
|
|
34308
|
+
if (!result) return;
|
|
34309
|
+
if (result.summary?.historySessionId && shouldScheduleSavedHistoryRollupForSignature(result.signature)) {
|
|
34310
|
+
scheduleSavedHistoryRollup(agentType, result.summary.historySessionId);
|
|
34311
|
+
}
|
|
34312
|
+
}
|
|
34313
|
+
function updateSavedHistoryIndexForSessionStart(agentType, dir, file2, historySessionId, workspace) {
|
|
34314
|
+
const normalizedSessionId = normalizeSavedHistorySessionId(agentType, historySessionId);
|
|
34315
|
+
const normalizedWorkspace = String(workspace || "").trim();
|
|
34316
|
+
if (!normalizedSessionId || !normalizedWorkspace) return;
|
|
34317
|
+
persistSavedHistoryFileSummaryEntry(agentType, dir, file2, (currentSummary) => ({
|
|
34318
|
+
file: file2,
|
|
34319
|
+
historySessionId: normalizedSessionId,
|
|
34320
|
+
messageCount: currentSummary?.messageCount || 0,
|
|
34321
|
+
firstMessageAt: currentSummary?.firstMessageAt || 0,
|
|
34322
|
+
lastMessageAt: currentSummary?.lastMessageAt || 0,
|
|
34323
|
+
sessionTitle: currentSummary?.sessionTitle,
|
|
34324
|
+
preview: currentSummary?.preview,
|
|
34325
|
+
workspace: normalizedWorkspace
|
|
34326
|
+
}));
|
|
34327
|
+
}
|
|
34328
|
+
function updateSavedHistoryIndexForAppendedMessages(agentType, dir, file2, historySessionId, messages) {
|
|
34329
|
+
const normalizedSessionId = normalizeSavedHistorySessionId(agentType, historySessionId || "");
|
|
34330
|
+
if (!normalizedSessionId || messages.length === 0) return;
|
|
34331
|
+
persistSavedHistoryFileSummaryEntry(agentType, dir, file2, (currentSummary) => {
|
|
34332
|
+
const nextSummary = {
|
|
34333
|
+
file: file2,
|
|
34334
|
+
historySessionId: normalizedSessionId,
|
|
34335
|
+
messageCount: currentSummary?.messageCount || 0,
|
|
34336
|
+
firstMessageAt: currentSummary?.firstMessageAt || 0,
|
|
34337
|
+
lastMessageAt: currentSummary?.lastMessageAt || 0,
|
|
34338
|
+
sessionTitle: currentSummary?.sessionTitle,
|
|
34339
|
+
preview: currentSummary?.preview,
|
|
34340
|
+
workspace: currentSummary?.workspace
|
|
34341
|
+
};
|
|
34342
|
+
for (const message of messages) {
|
|
34343
|
+
if (!message || message.historySessionId !== historySessionId) continue;
|
|
34344
|
+
if (message.kind === "session_start") {
|
|
34345
|
+
if (message.workspace) nextSummary.workspace = message.workspace;
|
|
34346
|
+
continue;
|
|
34347
|
+
}
|
|
34348
|
+
nextSummary.messageCount += 1;
|
|
34349
|
+
if (!nextSummary.firstMessageAt || message.receivedAt < nextSummary.firstMessageAt) {
|
|
34350
|
+
nextSummary.firstMessageAt = message.receivedAt;
|
|
34351
|
+
}
|
|
34352
|
+
if (!nextSummary.lastMessageAt || message.receivedAt >= nextSummary.lastMessageAt) {
|
|
34353
|
+
nextSummary.lastMessageAt = message.receivedAt;
|
|
34354
|
+
if (message.sessionTitle) nextSummary.sessionTitle = message.sessionTitle;
|
|
34355
|
+
if (message.role !== "system" && message.content.trim()) nextSummary.preview = message.content.trim();
|
|
34356
|
+
} else if (message.sessionTitle) {
|
|
34357
|
+
nextSummary.sessionTitle = message.sessionTitle;
|
|
34358
|
+
}
|
|
34359
|
+
if (!nextSummary.preview && message.role !== "system" && message.content.trim()) {
|
|
34360
|
+
nextSummary.preview = message.content.trim();
|
|
34361
|
+
}
|
|
34362
|
+
}
|
|
34363
|
+
return nextSummary;
|
|
34364
|
+
});
|
|
34365
|
+
}
|
|
34366
|
+
function computeSavedHistoryFileSummary(agentType, dir, file2) {
|
|
34367
|
+
const historySessionId = extractSavedHistorySessionIdFromFile(agentType, file2);
|
|
34368
|
+
if (!historySessionId) return null;
|
|
34369
|
+
const filePath = path7.join(dir, file2);
|
|
34370
|
+
const content = fs32.readFileSync(filePath, "utf-8");
|
|
34371
|
+
const lines = content.split("\n").filter(Boolean);
|
|
34372
|
+
let messageCount = 0;
|
|
34373
|
+
let firstMessageAt = 0;
|
|
34374
|
+
let lastMessageAt = 0;
|
|
34375
|
+
let sessionTitle = "";
|
|
34376
|
+
let preview = "";
|
|
34377
|
+
let workspace = "";
|
|
34378
|
+
for (const line of lines) {
|
|
34379
|
+
let parsed = null;
|
|
34380
|
+
try {
|
|
34381
|
+
parsed = JSON.parse(line);
|
|
34382
|
+
} catch {
|
|
34383
|
+
parsed = null;
|
|
34384
|
+
}
|
|
34385
|
+
if (!parsed || parsed.historySessionId !== historySessionId) continue;
|
|
34386
|
+
if (parsed.kind === "session_start") {
|
|
34387
|
+
if (!workspace && parsed.workspace) workspace = parsed.workspace;
|
|
34388
|
+
continue;
|
|
34389
|
+
}
|
|
34390
|
+
messageCount += 1;
|
|
34391
|
+
if (!firstMessageAt || parsed.receivedAt < firstMessageAt) firstMessageAt = parsed.receivedAt;
|
|
34392
|
+
if (!lastMessageAt || parsed.receivedAt > lastMessageAt) lastMessageAt = parsed.receivedAt;
|
|
34393
|
+
if (parsed.sessionTitle) sessionTitle = parsed.sessionTitle;
|
|
34394
|
+
if (parsed.role !== "system" && parsed.content.trim()) preview = parsed.content.trim();
|
|
34395
|
+
}
|
|
34396
|
+
if (messageCount === 0 || !lastMessageAt) return null;
|
|
34397
|
+
return {
|
|
34398
|
+
file: file2,
|
|
34399
|
+
historySessionId,
|
|
34400
|
+
messageCount,
|
|
34401
|
+
firstMessageAt,
|
|
34402
|
+
lastMessageAt,
|
|
34403
|
+
sessionTitle: sessionTitle || void 0,
|
|
34404
|
+
preview: preview || void 0,
|
|
34405
|
+
workspace: workspace || void 0
|
|
34406
|
+
};
|
|
34407
|
+
}
|
|
34408
|
+
function shouldScheduleSavedHistoryRollupForSignature(signature) {
|
|
34409
|
+
const parts = String(signature || "").split(":");
|
|
34410
|
+
const size = Number(parts[1] || 0);
|
|
34411
|
+
return shouldScheduleSavedHistoryRollup(size);
|
|
34412
|
+
}
|
|
34413
|
+
function scheduleSavedHistoryRollup(agentType, historySessionId) {
|
|
34414
|
+
const key = `${agentType}:${historySessionId}`;
|
|
34415
|
+
if (!historySessionId || savedHistoryRollupInFlight.has(key)) return;
|
|
34416
|
+
savedHistoryRollupInFlight.add(key);
|
|
34417
|
+
setTimeout(() => {
|
|
34418
|
+
try {
|
|
34419
|
+
new ChatHistoryWriter().compactHistorySession(agentType, historySessionId);
|
|
34420
|
+
} finally {
|
|
34421
|
+
savedHistoryRollupInFlight.delete(key);
|
|
34422
|
+
}
|
|
34423
|
+
}, 0);
|
|
34424
|
+
}
|
|
34425
|
+
function scheduleSavedHistoryBackgroundRefresh(agentType, dir) {
|
|
34426
|
+
const key = `${agentType}:${dir}`;
|
|
34427
|
+
if (savedHistoryBackgroundRefresh.has(key)) return;
|
|
34428
|
+
savedHistoryBackgroundRefresh.add(key);
|
|
34429
|
+
setTimeout(() => {
|
|
34430
|
+
try {
|
|
34431
|
+
if (!fs32.existsSync(dir)) return;
|
|
34432
|
+
const files = listHistoryFiles(dir);
|
|
34433
|
+
const fileSignatures = buildSavedHistoryFileSignatureMap(dir, files);
|
|
34434
|
+
const persistedEntries = loadPersistedSavedHistoryIndex(dir);
|
|
34435
|
+
const computed = computeSavedHistorySessionSummaries(agentType, dir, files, fileSignatures, persistedEntries);
|
|
34436
|
+
savePersistedSavedHistoryIndex(dir, computed.persistedEntries || /* @__PURE__ */ new Map());
|
|
34437
|
+
const refreshedIndexSignature = buildSavedHistoryIndexFileSignature(dir);
|
|
34438
|
+
savedHistorySessionCache.set(agentType.replace(/[^a-zA-Z0-9_-]/g, "_"), {
|
|
34439
|
+
signature: refreshedIndexSignature,
|
|
34440
|
+
summaries: computed.summaries || []
|
|
34441
|
+
});
|
|
34442
|
+
for (const [file2, entry] of Array.from(computed.persistedEntries.entries())) {
|
|
34443
|
+
if (!entry?.summary || !shouldScheduleSavedHistoryRollupForSignature(entry.signature)) continue;
|
|
34444
|
+
scheduleSavedHistoryRollup(agentType, entry.summary.historySessionId);
|
|
34445
|
+
}
|
|
34446
|
+
} catch {
|
|
34447
|
+
} finally {
|
|
34448
|
+
savedHistoryBackgroundRefresh.delete(key);
|
|
34449
|
+
}
|
|
34450
|
+
}, 0);
|
|
34451
|
+
}
|
|
34452
|
+
function computeSavedHistorySessionSummaries(agentType, dir, files, fileSignatures, persistedEntries) {
|
|
34453
|
+
const summaryBySessionId = /* @__PURE__ */ new Map();
|
|
34454
|
+
const nextPersistedEntries = /* @__PURE__ */ new Map();
|
|
34455
|
+
for (const file2 of files.slice().sort()) {
|
|
34456
|
+
const filePath = path7.join(dir, file2);
|
|
34457
|
+
const signature = fileSignatures.get(file2) || `${file2}:missing`;
|
|
34458
|
+
const cached2 = savedHistoryFileSummaryCache.get(filePath);
|
|
34459
|
+
const persisted = persistedEntries.get(file2);
|
|
34460
|
+
const reusableEntry = cached2?.signature === signature ? cached2 : persisted?.signature === signature ? persisted : null;
|
|
34461
|
+
const fileSummary = reusableEntry?.summary || computeSavedHistoryFileSummary(agentType, dir, file2);
|
|
34462
|
+
const nextEntry = reusableEntry || {
|
|
34463
|
+
signature,
|
|
34464
|
+
summary: fileSummary
|
|
34465
|
+
};
|
|
34466
|
+
if (!reusableEntry) {
|
|
34467
|
+
nextEntry.signature = signature;
|
|
34468
|
+
nextEntry.summary = fileSummary;
|
|
34469
|
+
}
|
|
34470
|
+
savedHistoryFileSummaryCache.set(filePath, nextEntry);
|
|
34471
|
+
nextPersistedEntries.set(file2, nextEntry);
|
|
34472
|
+
if (!fileSummary) continue;
|
|
34473
|
+
const existing = summaryBySessionId.get(fileSummary.historySessionId);
|
|
34474
|
+
if (fileSummary.messageCount <= 0 || !fileSummary.lastMessageAt) {
|
|
34475
|
+
continue;
|
|
34476
|
+
}
|
|
34477
|
+
if (!existing) {
|
|
34478
|
+
summaryBySessionId.set(fileSummary.historySessionId, {
|
|
34479
|
+
historySessionId: fileSummary.historySessionId,
|
|
34480
|
+
sessionTitle: fileSummary.sessionTitle,
|
|
34481
|
+
messageCount: fileSummary.messageCount,
|
|
34482
|
+
firstMessageAt: fileSummary.firstMessageAt,
|
|
34483
|
+
lastMessageAt: fileSummary.lastMessageAt,
|
|
34484
|
+
preview: fileSummary.preview,
|
|
34485
|
+
workspace: fileSummary.workspace
|
|
34486
|
+
});
|
|
34487
|
+
continue;
|
|
34488
|
+
}
|
|
34489
|
+
existing.messageCount += fileSummary.messageCount;
|
|
34490
|
+
if (!existing.firstMessageAt || fileSummary.firstMessageAt < existing.firstMessageAt) {
|
|
34491
|
+
existing.firstMessageAt = fileSummary.firstMessageAt;
|
|
34492
|
+
}
|
|
34493
|
+
if (fileSummary.lastMessageAt >= existing.lastMessageAt) {
|
|
34494
|
+
existing.lastMessageAt = fileSummary.lastMessageAt;
|
|
34495
|
+
if (fileSummary.sessionTitle) existing.sessionTitle = fileSummary.sessionTitle;
|
|
34496
|
+
if (fileSummary.preview) existing.preview = fileSummary.preview;
|
|
34497
|
+
}
|
|
34498
|
+
if (!existing.workspace && fileSummary.workspace) {
|
|
34499
|
+
existing.workspace = fileSummary.workspace;
|
|
34500
|
+
}
|
|
34501
|
+
}
|
|
34502
|
+
return {
|
|
34503
|
+
summaries: Array.from(summaryBySessionId.values()).sort((a, b2) => b2.lastMessageAt - a.lastMessageAt),
|
|
34504
|
+
persistedEntries: nextPersistedEntries
|
|
34505
|
+
};
|
|
34006
34506
|
}
|
|
34007
34507
|
var ChatHistoryWriter = class {
|
|
34008
34508
|
/** Last seen message count per agent (deduplication) */
|
|
@@ -34077,9 +34577,11 @@ ${cleanBody}`;
|
|
|
34077
34577
|
fs32.mkdirSync(dir, { recursive: true });
|
|
34078
34578
|
const date5 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
34079
34579
|
const filePrefix = effectiveHistoryKey ? `${this.sanitize(effectiveHistoryKey)}_` : "";
|
|
34080
|
-
const
|
|
34580
|
+
const fileName = `${filePrefix}${date5}.jsonl`;
|
|
34581
|
+
const filePath = path7.join(dir, fileName);
|
|
34081
34582
|
const lines = newMessages.map((m) => JSON.stringify(m)).join("\n") + "\n";
|
|
34082
34583
|
fs32.appendFileSync(filePath, lines, "utf-8");
|
|
34584
|
+
updateSavedHistoryIndexForAppendedMessages(agentType, dir, fileName, effectiveHistoryKey, newMessages);
|
|
34083
34585
|
const prevCount = this.lastSeenCounts.get(dedupKey) || 0;
|
|
34084
34586
|
if (!historySessionId && messages.length < prevCount * 0.5 && prevCount > 3) {
|
|
34085
34587
|
seenHashes.clear();
|
|
@@ -34170,7 +34672,8 @@ ${cleanBody}`;
|
|
|
34170
34672
|
const dir = path7.join(HISTORY_DIR, this.sanitize(agentType));
|
|
34171
34673
|
fs32.mkdirSync(dir, { recursive: true });
|
|
34172
34674
|
const date5 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
34173
|
-
const
|
|
34675
|
+
const fileName = `${this.sanitize(id)}_${date5}.jsonl`;
|
|
34676
|
+
const filePath = path7.join(dir, fileName);
|
|
34174
34677
|
const record2 = {
|
|
34175
34678
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
34176
34679
|
receivedAt: Date.now(),
|
|
@@ -34183,6 +34686,7 @@ ${cleanBody}`;
|
|
|
34183
34686
|
workspace: ws2
|
|
34184
34687
|
};
|
|
34185
34688
|
fs32.appendFileSync(filePath, JSON.stringify(record2) + "\n", "utf-8");
|
|
34689
|
+
updateSavedHistoryIndexForSessionStart(agentType, dir, fileName, id, ws2);
|
|
34186
34690
|
} catch {
|
|
34187
34691
|
}
|
|
34188
34692
|
}
|
|
@@ -34248,6 +34752,7 @@ ${cleanBody}`;
|
|
|
34248
34752
|
}
|
|
34249
34753
|
fs32.unlinkSync(sourcePath);
|
|
34250
34754
|
}
|
|
34755
|
+
invalidatePersistedSavedHistoryIndex(agentType, dir);
|
|
34251
34756
|
} catch {
|
|
34252
34757
|
}
|
|
34253
34758
|
}
|
|
@@ -34297,6 +34802,7 @@ ${cleanBody}`;
|
|
|
34297
34802
|
fs32.writeFileSync(filePath, `${collapsed.map((entry) => JSON.stringify(entry)).join("\n")}
|
|
34298
34803
|
`, "utf-8");
|
|
34299
34804
|
}
|
|
34805
|
+
invalidatePersistedSavedHistoryIndex(agentType, dir);
|
|
34300
34806
|
} catch {
|
|
34301
34807
|
}
|
|
34302
34808
|
}
|
|
@@ -34316,13 +34822,18 @@ ${cleanBody}`;
|
|
|
34316
34822
|
for (const dir of agentDirs) {
|
|
34317
34823
|
const dirPath = path7.join(HISTORY_DIR, dir.name);
|
|
34318
34824
|
const files = fs32.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl") || f.endsWith(".terminal.log"));
|
|
34825
|
+
let removedAny = false;
|
|
34319
34826
|
for (const file2 of files) {
|
|
34320
34827
|
const filePath = path7.join(dirPath, file2);
|
|
34321
34828
|
const stat4 = fs32.statSync(filePath);
|
|
34322
34829
|
if (stat4.mtimeMs < cutoff) {
|
|
34323
34830
|
fs32.unlinkSync(filePath);
|
|
34831
|
+
removedAny = true;
|
|
34324
34832
|
}
|
|
34325
34833
|
}
|
|
34834
|
+
if (removedAny) {
|
|
34835
|
+
invalidatePersistedSavedHistoryIndex(dir.name, dirPath);
|
|
34836
|
+
}
|
|
34326
34837
|
}
|
|
34327
34838
|
} catch {
|
|
34328
34839
|
}
|
|
@@ -34388,18 +34899,51 @@ ${cleanBody}`;
|
|
|
34388
34899
|
savedHistorySessionCache.delete(sanitized);
|
|
34389
34900
|
return { sessions: [], hasMore: false };
|
|
34390
34901
|
}
|
|
34391
|
-
const files = listHistoryFiles(dir);
|
|
34392
|
-
const signature = buildSavedHistoryCacheSignature(dir, files);
|
|
34393
34902
|
const cached2 = savedHistorySessionCache.get(sanitized);
|
|
34394
|
-
const
|
|
34395
|
-
|
|
34903
|
+
const offset = Math.max(0, options.offset || 0);
|
|
34904
|
+
const limit = Math.max(1, options.limit || 30);
|
|
34905
|
+
const indexSignature = buildSavedHistoryIndexFileSignature(dir);
|
|
34906
|
+
let cacheWasInvalidated = false;
|
|
34907
|
+
if (cached2) {
|
|
34908
|
+
const cacheLooksPersisted = cached2.signature.startsWith("index:");
|
|
34909
|
+
const cacheStillValid = cacheLooksPersisted ? cached2.signature === indexSignature : (() => {
|
|
34910
|
+
const files2 = listHistoryFiles(dir);
|
|
34911
|
+
const fileSignatures2 = buildSavedHistoryFileSignatureMap(dir, files2);
|
|
34912
|
+
return cached2.signature === buildSavedHistoryCacheSignature(files2, fileSignatures2);
|
|
34913
|
+
})();
|
|
34914
|
+
if (cacheStillValid) {
|
|
34915
|
+
const sliced2 = cached2.summaries.slice(offset, offset + limit);
|
|
34916
|
+
return {
|
|
34917
|
+
sessions: sliced2,
|
|
34918
|
+
hasMore: cached2.summaries.length > offset + limit
|
|
34919
|
+
};
|
|
34920
|
+
}
|
|
34921
|
+
cacheWasInvalidated = true;
|
|
34922
|
+
}
|
|
34923
|
+
const persistedSessions = readPersistedSavedHistorySessionSummaries(dir);
|
|
34924
|
+
if (!cacheWasInvalidated && persistedSessions?.length && !historyDirectoryHasFilesNewerThanIndex(dir)) {
|
|
34396
34925
|
savedHistorySessionCache.set(sanitized, {
|
|
34397
|
-
signature,
|
|
34398
|
-
summaries
|
|
34926
|
+
signature: indexSignature,
|
|
34927
|
+
summaries: persistedSessions
|
|
34399
34928
|
});
|
|
34929
|
+
scheduleSavedHistoryBackgroundRefresh(agentType, dir);
|
|
34930
|
+
const sliced2 = persistedSessions.slice(offset, offset + limit);
|
|
34931
|
+
return {
|
|
34932
|
+
sessions: sliced2,
|
|
34933
|
+
hasMore: persistedSessions.length > offset + limit
|
|
34934
|
+
};
|
|
34400
34935
|
}
|
|
34401
|
-
const
|
|
34402
|
-
const
|
|
34936
|
+
const files = listHistoryFiles(dir);
|
|
34937
|
+
const fileSignatures = buildSavedHistoryFileSignatureMap(dir, files);
|
|
34938
|
+
const signature = buildSavedHistoryCacheSignature(files, fileSignatures);
|
|
34939
|
+
const persistedEntries = loadPersistedSavedHistoryIndex(dir);
|
|
34940
|
+
const computed = computeSavedHistorySessionSummaries(agentType, dir, files, fileSignatures, persistedEntries);
|
|
34941
|
+
const summaries = computed.summaries || [];
|
|
34942
|
+
savePersistedSavedHistoryIndex(dir, computed.persistedEntries || /* @__PURE__ */ new Map());
|
|
34943
|
+
savedHistorySessionCache.set(sanitized, {
|
|
34944
|
+
signature,
|
|
34945
|
+
summaries
|
|
34946
|
+
});
|
|
34403
34947
|
const sliced = summaries.slice(offset, offset + limit);
|
|
34404
34948
|
return {
|
|
34405
34949
|
sessions: sliced,
|
|
@@ -36013,7 +36557,7 @@ ${effect.notification.body || ""}`.trim();
|
|
|
36013
36557
|
return profile !== "live";
|
|
36014
36558
|
}
|
|
36015
36559
|
function shouldIncludeRuntimeMetadata(profile) {
|
|
36016
|
-
return
|
|
36560
|
+
return true;
|
|
36017
36561
|
}
|
|
36018
36562
|
function findCdpManager(cdpManagers, key) {
|
|
36019
36563
|
const exact = cdpManagers.get(key);
|
|
@@ -36128,6 +36672,21 @@ ${effect.notification.body || ""}`.trim();
|
|
|
36128
36672
|
lastUpdated: ext.lastUpdated
|
|
36129
36673
|
};
|
|
36130
36674
|
}
|
|
36675
|
+
function shouldIncludeExtensionSession(ext) {
|
|
36676
|
+
const status = String(ext.status || "").trim().toLowerCase();
|
|
36677
|
+
const hasActiveChat = !!ext.activeChat;
|
|
36678
|
+
const hasMessages = Array.isArray(ext.activeChat?.messages) && ext.activeChat.messages.length > 0;
|
|
36679
|
+
const hasModal = !!ext.activeChat?.activeModal;
|
|
36680
|
+
const hasStreams = Array.isArray(ext.agentStreams) && ext.agentStreams.length > 0;
|
|
36681
|
+
const hasProviderSessionId = typeof ext.providerSessionId === "string" && ext.providerSessionId.trim().length > 0;
|
|
36682
|
+
const hasControlValues = !!(ext.controlValues && Object.keys(ext.controlValues).length > 0);
|
|
36683
|
+
const hasProviderControls = Array.isArray(ext.providerControls) && ext.providerControls.length > 0;
|
|
36684
|
+
const hasOpenPanelCapability = Array.isArray(ext.sessionCapabilities) && ext.sessionCapabilities.includes("open_panel");
|
|
36685
|
+
const hasSummaryMetadata = !!ext.summaryMetadata;
|
|
36686
|
+
const hasError = typeof ext.errorMessage === "string" && ext.errorMessage.trim().length > 0;
|
|
36687
|
+
const hasInterestingStatus = !!status && !["idle", "panel_hidden", "disconnected", "not_monitored"].includes(status);
|
|
36688
|
+
return hasActiveChat || hasMessages || hasModal || hasStreams || hasProviderSessionId || hasControlValues || hasProviderControls || hasOpenPanelCapability || hasSummaryMetadata || hasError || hasInterestingStatus;
|
|
36689
|
+
}
|
|
36131
36690
|
function buildCliSession(state, options) {
|
|
36132
36691
|
const profile = options.profile || "full";
|
|
36133
36692
|
const activeChat = normalizeActiveChatData(state.activeChat, getActiveChatOptions(profile));
|
|
@@ -36153,8 +36712,12 @@ ${effect.notification.body || ""}`.trim();
|
|
|
36153
36712
|
runtimeKey: state.runtime?.runtimeKey,
|
|
36154
36713
|
runtimeDisplayName: state.runtime?.displayName,
|
|
36155
36714
|
runtimeWorkspaceLabel: state.runtime?.workspaceLabel,
|
|
36715
|
+
runtimeLifecycle: state.runtime?.lifecycle ?? null,
|
|
36716
|
+
runtimeSurfaceKind: state.runtime?.surfaceKind,
|
|
36156
36717
|
runtimeWriteOwner: state.runtime?.writeOwner || null,
|
|
36157
|
-
runtimeAttachedClients: state.runtime?.attachedClients || []
|
|
36718
|
+
runtimeAttachedClients: state.runtime?.attachedClients || [],
|
|
36719
|
+
runtimeRestoredFromStorage: state.runtime?.restoredFromStorage === true,
|
|
36720
|
+
runtimeRecoveryState: state.runtime?.recoveryState ?? null
|
|
36158
36721
|
},
|
|
36159
36722
|
mode: state.mode,
|
|
36160
36723
|
resume: state.resume,
|
|
@@ -36211,6 +36774,7 @@ ${effect.notification.body || ""}`.trim();
|
|
|
36211
36774
|
for (const state of ideStates) {
|
|
36212
36775
|
sessions.push(buildIdeWorkspaceSession(state, cdpManagers, options));
|
|
36213
36776
|
for (const ext of state.extensions) {
|
|
36777
|
+
if (!shouldIncludeExtensionSession(ext)) continue;
|
|
36214
36778
|
sessions.push(buildExtensionAgentSession(state, ext, options));
|
|
36215
36779
|
}
|
|
36216
36780
|
}
|
|
@@ -38351,7 +38915,9 @@ ${effect.notification.body || ""}`.trim();
|
|
|
38351
38915
|
});
|
|
38352
38916
|
}
|
|
38353
38917
|
async function executeProviderScript(h, args, scriptName) {
|
|
38354
|
-
const
|
|
38918
|
+
const explicitTargetSessionId = typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : "";
|
|
38919
|
+
const targetSession = explicitTargetSessionId ? h.ctx.sessionRegistry?.get(explicitTargetSessionId) : void 0;
|
|
38920
|
+
const resolvedProviderType = targetSession?.providerType || h.currentSession?.providerType || h.currentProviderType || args?.agentType || args?.providerType;
|
|
38355
38921
|
if (!resolvedProviderType) return { success: false, error: "targetSessionId or providerType is required" };
|
|
38356
38922
|
const loader = h.ctx.providerLoader;
|
|
38357
38923
|
if (!loader) return { success: false, error: "ProviderLoader not initialized" };
|
|
@@ -38394,16 +38960,16 @@ ${effect.notification.body || ""}`.trim();
|
|
|
38394
38960
|
const scriptFn = provider.scripts[actualScriptName];
|
|
38395
38961
|
const scriptCode = scriptFn(normalizedArgs);
|
|
38396
38962
|
if (!scriptCode) return { success: false, error: `Script '${actualScriptName}' returned null` };
|
|
38397
|
-
const cdpKey = provider.category === "ide" ? h.currentSession?.cdpManagerKey || h.currentManagerKey || resolvedProviderType : h.currentSession?.cdpManagerKey || h.currentManagerKey;
|
|
38963
|
+
const cdpKey = provider.category === "ide" ? targetSession?.cdpManagerKey || h.currentSession?.cdpManagerKey || h.currentManagerKey || resolvedProviderType : targetSession?.cdpManagerKey || h.currentSession?.cdpManagerKey || h.currentManagerKey;
|
|
38398
38964
|
LOG2.info("Command", `[ExtScript] provider=${provider.type} category=${provider.category} cdpKey=${cdpKey}`);
|
|
38399
38965
|
const cdp = h.getCdp(cdpKey);
|
|
38400
38966
|
if (!cdp?.isConnected) return { success: false, error: `No CDP connection for ${cdpKey || "any"}` };
|
|
38401
38967
|
try {
|
|
38402
38968
|
let result;
|
|
38403
38969
|
if (provider.category === "extension") {
|
|
38404
|
-
const runtimeSessionId = h.currentSession?.sessionId
|
|
38970
|
+
const runtimeSessionId = explicitTargetSessionId || h.currentSession?.sessionId;
|
|
38405
38971
|
if (!runtimeSessionId) return { success: false, error: `No target session found for ${resolvedProviderType}` };
|
|
38406
|
-
const parentSessionId = h.currentSession?.parentSessionId;
|
|
38972
|
+
const parentSessionId = targetSession?.parentSessionId || h.currentSession?.parentSessionId;
|
|
38407
38973
|
if (parentSessionId) {
|
|
38408
38974
|
await h.agentStream?.setActiveSession(cdp, parentSessionId, runtimeSessionId);
|
|
38409
38975
|
await h.agentStream?.syncActiveSession(cdp, parentSessionId);
|
|
@@ -39398,8 +39964,12 @@ ${effect.notification.body || ""}`.trim();
|
|
|
39398
39964
|
runtimeKey: runtime.runtimeKey,
|
|
39399
39965
|
displayName: runtime.displayName,
|
|
39400
39966
|
workspaceLabel: runtime.workspaceLabel,
|
|
39967
|
+
lifecycle: runtime.lifecycle ?? null,
|
|
39968
|
+
surfaceKind: runtime.surfaceKind,
|
|
39401
39969
|
writeOwner: runtime.writeOwner || null,
|
|
39402
|
-
attachedClients: runtime.attachedClients || []
|
|
39970
|
+
attachedClients: runtime.attachedClients || [],
|
|
39971
|
+
restoredFromStorage: runtime.restoredFromStorage === true,
|
|
39972
|
+
recoveryState: runtime.recoveryState ?? null
|
|
39403
39973
|
} : void 0,
|
|
39404
39974
|
resume: this.provider.resume,
|
|
39405
39975
|
controlValues: surface.controlValues,
|
|
@@ -43681,59 +44251,6 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
43681
44251
|
}
|
|
43682
44252
|
cleanOldFiles();
|
|
43683
44253
|
init_logger();
|
|
43684
|
-
var LIVE_LIFECYCLES = /* @__PURE__ */ new Set(["starting", "running", "stopping", "interrupted"]);
|
|
43685
|
-
function isSessionHostLiveRuntime(record2) {
|
|
43686
|
-
const lifecycle = String(record2?.lifecycle || "").trim();
|
|
43687
|
-
return LIVE_LIFECYCLES.has(lifecycle);
|
|
43688
|
-
}
|
|
43689
|
-
function getSessionHostRecoveryLabel(meta3) {
|
|
43690
|
-
const recoveryState = typeof meta3?.runtimeRecoveryState === "string" ? String(meta3.runtimeRecoveryState).trim() : "";
|
|
43691
|
-
if (!recoveryState) return null;
|
|
43692
|
-
if (recoveryState === "auto_resumed") return "restored after restart";
|
|
43693
|
-
if (recoveryState === "resume_failed") return "restore failed";
|
|
43694
|
-
if (recoveryState === "host_restart_interrupted") return "host restart interrupted";
|
|
43695
|
-
if (recoveryState === "orphan_snapshot") return "snapshot recovered";
|
|
43696
|
-
return recoveryState.replace(/_/g, " ");
|
|
43697
|
-
}
|
|
43698
|
-
function isSessionHostRecoverySnapshot(record2) {
|
|
43699
|
-
if (!record2) return false;
|
|
43700
|
-
if (isSessionHostLiveRuntime(record2)) return false;
|
|
43701
|
-
const lifecycle = String(record2.lifecycle || "").trim();
|
|
43702
|
-
if (lifecycle && lifecycle !== "stopped" && lifecycle !== "failed") {
|
|
43703
|
-
return false;
|
|
43704
|
-
}
|
|
43705
|
-
const meta3 = record2.meta || void 0;
|
|
43706
|
-
if (meta3?.restoredFromStorage === true) return true;
|
|
43707
|
-
return getSessionHostRecoveryLabel(meta3) !== null;
|
|
43708
|
-
}
|
|
43709
|
-
function getSessionHostSurfaceKind(record2) {
|
|
43710
|
-
if (isSessionHostLiveRuntime(record2)) return "live_runtime";
|
|
43711
|
-
if (isSessionHostRecoverySnapshot(record2)) return "recovery_snapshot";
|
|
43712
|
-
return "inactive_record";
|
|
43713
|
-
}
|
|
43714
|
-
function partitionSessionHostRecords(records) {
|
|
43715
|
-
const liveRuntimes = [];
|
|
43716
|
-
const recoverySnapshots = [];
|
|
43717
|
-
const inactiveRecords = [];
|
|
43718
|
-
for (const record2 of records) {
|
|
43719
|
-
const kind = getSessionHostSurfaceKind(record2);
|
|
43720
|
-
if (kind === "live_runtime") {
|
|
43721
|
-
liveRuntimes.push(record2);
|
|
43722
|
-
} else if (kind === "recovery_snapshot") {
|
|
43723
|
-
recoverySnapshots.push(record2);
|
|
43724
|
-
} else {
|
|
43725
|
-
inactiveRecords.push(record2);
|
|
43726
|
-
}
|
|
43727
|
-
}
|
|
43728
|
-
return {
|
|
43729
|
-
liveRuntimes,
|
|
43730
|
-
recoverySnapshots,
|
|
43731
|
-
inactiveRecords
|
|
43732
|
-
};
|
|
43733
|
-
}
|
|
43734
|
-
function partitionSessionHostDiagnosticsSessions(records) {
|
|
43735
|
-
return partitionSessionHostRecords(records || []);
|
|
43736
|
-
}
|
|
43737
44254
|
var os16 = __toESM2(require("os"));
|
|
43738
44255
|
init_config();
|
|
43739
44256
|
init_terminal_screen();
|
|
@@ -45277,6 +45794,21 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
45277
45794
|
}
|
|
45278
45795
|
};
|
|
45279
45796
|
}
|
|
45797
|
+
async function runAsyncBatch2(items, worker, options = {}) {
|
|
45798
|
+
const list = Array.from(items);
|
|
45799
|
+
if (list.length === 0) return;
|
|
45800
|
+
const concurrency = Math.max(1, Math.min(list.length, Math.floor(options.concurrency || 1)));
|
|
45801
|
+
let nextIndex = 0;
|
|
45802
|
+
const runners = Array.from({ length: concurrency }, async () => {
|
|
45803
|
+
while (true) {
|
|
45804
|
+
const currentIndex = nextIndex;
|
|
45805
|
+
nextIndex += 1;
|
|
45806
|
+
if (currentIndex >= list.length) return;
|
|
45807
|
+
await worker(list[currentIndex], currentIndex);
|
|
45808
|
+
}
|
|
45809
|
+
});
|
|
45810
|
+
await Promise.all(runners);
|
|
45811
|
+
}
|
|
45280
45812
|
init_read_chat_contract();
|
|
45281
45813
|
init_chat_message_normalization();
|
|
45282
45814
|
var ProviderStreamAdapter = class {
|
|
@@ -45742,10 +46274,12 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
45742
46274
|
}
|
|
45743
46275
|
}
|
|
45744
46276
|
/** Collect active extension session state */
|
|
45745
|
-
async collectActiveSession(cdp, parentSessionId) {
|
|
46277
|
+
async collectActiveSession(cdp, parentSessionId, attemptedSessionIds = /* @__PURE__ */ new Set(), originSessionId) {
|
|
45746
46278
|
if (!this.enabled) return null;
|
|
45747
46279
|
const activeSessionId = this.getActiveSessionId(parentSessionId);
|
|
45748
46280
|
if (!activeSessionId) return null;
|
|
46281
|
+
const resolvedOriginSessionId = originSessionId || activeSessionId;
|
|
46282
|
+
attemptedSessionIds.add(activeSessionId);
|
|
45749
46283
|
let agent = this.managedBySessionId.get(activeSessionId);
|
|
45750
46284
|
if (!agent) {
|
|
45751
46285
|
agent = await this.connectManagedSession(cdp, parentSessionId, activeSessionId) || void 0;
|
|
@@ -45758,18 +46292,44 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
45758
46292
|
try {
|
|
45759
46293
|
const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
|
|
45760
46294
|
const state = await agent.adapter.readChat(evaluate);
|
|
45761
|
-
const
|
|
45762
|
-
const
|
|
45763
|
-
|
|
45764
|
-
|
|
46295
|
+
const resolvedProviderSessionId = typeof state.providerSessionId === "string" && state.providerSessionId.trim() ? state.providerSessionId.trim() : typeof state.sessionId === "string" && state.sessionId.trim() && state.sessionId !== agent.runtimeSessionId ? state.sessionId.trim() : void 0;
|
|
46296
|
+
const normalizedState = {
|
|
46297
|
+
...state,
|
|
46298
|
+
sessionId: agent.runtimeSessionId,
|
|
46299
|
+
...resolvedProviderSessionId ? { providerSessionId: resolvedProviderSessionId } : {}
|
|
46300
|
+
};
|
|
46301
|
+
const stateError = this.getStateError(normalizedState);
|
|
46302
|
+
const selectedModelValue = typeof normalizedState.controlValues?.model === "string" ? normalizedState.controlValues.model : "";
|
|
46303
|
+
LOG2.debug("AgentStream", `[AgentStream] readChat(${type}) result: status=${normalizedState.status} msgs=${normalizedState.messages?.length || 0} model=${selectedModelValue}${normalizedState.status === "error" ? " error=" + JSON.stringify(stateError) : ""}`);
|
|
46304
|
+
if (normalizedState.status === "error" && this.isRecoverableSessionError(stateError)) {
|
|
45765
46305
|
throw new Error(stateError);
|
|
45766
46306
|
}
|
|
45767
|
-
agent.lastState =
|
|
46307
|
+
agent.lastState = normalizedState;
|
|
45768
46308
|
agent.lastError = null;
|
|
45769
|
-
if (
|
|
46309
|
+
if (normalizedState.status === "panel_hidden") {
|
|
46310
|
+
const discovered = await cdp.discoverAgentWebviews().catch(() => []);
|
|
46311
|
+
const fallbackTarget = discovered.find((entry) => {
|
|
46312
|
+
if (entry.agentType === type) return false;
|
|
46313
|
+
const fallbackSessionId = this.resolveSessionIdForTarget(parentSessionId, entry.agentType);
|
|
46314
|
+
return !!fallbackSessionId && fallbackSessionId !== activeSessionId && !attemptedSessionIds.has(fallbackSessionId);
|
|
46315
|
+
});
|
|
46316
|
+
if (fallbackTarget) {
|
|
46317
|
+
const fallbackSessionId = this.resolveSessionIdForTarget(parentSessionId, fallbackTarget.agentType);
|
|
46318
|
+
if (fallbackSessionId && fallbackSessionId !== activeSessionId && !attemptedSessionIds.has(fallbackSessionId)) {
|
|
46319
|
+
this.logFn(`[AgentStream] Active session ${type} is hidden; switching to visible agent ${fallbackTarget.agentType} (${parentSessionId})`);
|
|
46320
|
+
await this.setActiveSession(cdp, parentSessionId, fallbackSessionId);
|
|
46321
|
+
await this.syncActiveSession(cdp, parentSessionId);
|
|
46322
|
+
const fallbackState = await this.collectActiveSession(cdp, parentSessionId, attemptedSessionIds, resolvedOriginSessionId);
|
|
46323
|
+
if (fallbackState?.status === "panel_hidden" && resolvedOriginSessionId !== fallbackSessionId) {
|
|
46324
|
+
await this.setActiveSession(cdp, parentSessionId, resolvedOriginSessionId);
|
|
46325
|
+
await this.syncActiveSession(cdp, parentSessionId);
|
|
46326
|
+
}
|
|
46327
|
+
return fallbackState;
|
|
46328
|
+
}
|
|
46329
|
+
}
|
|
45770
46330
|
agent.lastHiddenCheckTime = Date.now();
|
|
45771
46331
|
}
|
|
45772
|
-
return
|
|
46332
|
+
return normalizedState;
|
|
45773
46333
|
} catch (e) {
|
|
45774
46334
|
const errorMsg = e?.message || String(e);
|
|
45775
46335
|
this.logFn(`[AgentStream] readChat(${type}) error: ${errorMsg.slice(0, 200)}`);
|
|
@@ -46063,6 +46623,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
46063
46623
|
try {
|
|
46064
46624
|
await agentStreamManager.syncActiveSession(cdp, parentSessionId);
|
|
46065
46625
|
let stream = await agentStreamManager.collectActiveSession(cdp, parentSessionId);
|
|
46626
|
+
resolvedActiveSessionId = stream?.sessionId || agentStreamManager.getActiveSessionId(parentSessionId) || resolvedActiveSessionId;
|
|
46066
46627
|
if (stream?.status === "waiting_approval") {
|
|
46067
46628
|
const autoApprove = providerLoader.getSettings(stream.agentType).autoApprove !== false;
|
|
46068
46629
|
if (autoApprove && resolvedActiveSessionId) {
|
|
@@ -52017,6 +52578,8 @@ data: ${JSON.stringify(msg.data)}
|
|
|
52017
52578
|
runtimeKey: record2.runtimeKey,
|
|
52018
52579
|
displayName: record2.displayName,
|
|
52019
52580
|
workspaceLabel: record2.workspaceLabel,
|
|
52581
|
+
lifecycle: typeof record2.lifecycle === "string" ? record2.lifecycle : null,
|
|
52582
|
+
surfaceKind: record2.surfaceKind,
|
|
52020
52583
|
writeOwner: record2.writeOwner ? {
|
|
52021
52584
|
clientId: record2.writeOwner.clientId,
|
|
52022
52585
|
ownerType: record2.writeOwner.ownerType
|
|
@@ -52619,6 +53182,7 @@ var import_ws = require("ws");
|
|
|
52619
53182
|
var path4 = __toESM(require("path"));
|
|
52620
53183
|
var fs3 = __toESM(require("fs"));
|
|
52621
53184
|
var os5 = __toESM(require("os"));
|
|
53185
|
+
var import_crypto2 = require("crypto");
|
|
52622
53186
|
var import_daemon_core2 = __toESM(require_dist2());
|
|
52623
53187
|
|
|
52624
53188
|
// src/session-host.ts
|
|
@@ -53124,6 +53688,165 @@ async function getWorkspaceSocketInfo(workspaceName) {
|
|
|
53124
53688
|
var import_daemon_core3 = __toESM(require_dist2());
|
|
53125
53689
|
var DEFAULT_PORT = 3847;
|
|
53126
53690
|
var STATUS_INTERVAL = 2e3;
|
|
53691
|
+
var STANDALONE_AUTH_SESSION_COOKIE = "adhdev_standalone_session";
|
|
53692
|
+
var STANDALONE_PASSWORD_CONFIG_FILE = "standalone-auth.json";
|
|
53693
|
+
var STANDALONE_BIND_HOST_CONFIG_FILE = "standalone-network.json";
|
|
53694
|
+
var STANDALONE_BIND_HOST_DEFAULT = "127.0.0.1";
|
|
53695
|
+
var PASSWORD_KEYLEN = 64;
|
|
53696
|
+
var DEFAULT_SESSION_TTL_MS = 1e3 * 60 * 60 * 24 * 30;
|
|
53697
|
+
function getStandalonePasswordConfigPath() {
|
|
53698
|
+
const dir = path4.join(os5.homedir(), ".adhdev");
|
|
53699
|
+
if (!fs3.existsSync(dir)) {
|
|
53700
|
+
fs3.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
53701
|
+
}
|
|
53702
|
+
return path4.join(dir, STANDALONE_PASSWORD_CONFIG_FILE);
|
|
53703
|
+
}
|
|
53704
|
+
function getStandaloneConfigJsonPath() {
|
|
53705
|
+
const dir = path4.join(os5.homedir(), ".adhdev");
|
|
53706
|
+
if (!fs3.existsSync(dir)) {
|
|
53707
|
+
fs3.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
53708
|
+
}
|
|
53709
|
+
return path4.join(dir, STANDALONE_BIND_HOST_CONFIG_FILE);
|
|
53710
|
+
}
|
|
53711
|
+
function loadStandaloneBindHostPreference() {
|
|
53712
|
+
try {
|
|
53713
|
+
const configPath = getStandaloneConfigJsonPath();
|
|
53714
|
+
if (!fs3.existsSync(configPath)) return STANDALONE_BIND_HOST_DEFAULT;
|
|
53715
|
+
const parsed = JSON.parse(fs3.readFileSync(configPath, "utf8"));
|
|
53716
|
+
return parsed?.standaloneBindHost === "0.0.0.0" ? "0.0.0.0" : STANDALONE_BIND_HOST_DEFAULT;
|
|
53717
|
+
} catch {
|
|
53718
|
+
return STANDALONE_BIND_HOST_DEFAULT;
|
|
53719
|
+
}
|
|
53720
|
+
}
|
|
53721
|
+
function saveStandaloneBindHostPreference(bindHost) {
|
|
53722
|
+
const configPath = getStandaloneConfigJsonPath();
|
|
53723
|
+
let parsed = {};
|
|
53724
|
+
try {
|
|
53725
|
+
if (fs3.existsSync(configPath)) {
|
|
53726
|
+
const raw = fs3.readFileSync(configPath, "utf8");
|
|
53727
|
+
const next = JSON.parse(raw);
|
|
53728
|
+
if (next && typeof next === "object" && !Array.isArray(next)) parsed = next;
|
|
53729
|
+
}
|
|
53730
|
+
} catch {
|
|
53731
|
+
parsed = {};
|
|
53732
|
+
}
|
|
53733
|
+
parsed.standaloneBindHost = bindHost;
|
|
53734
|
+
fs3.writeFileSync(configPath, JSON.stringify(parsed, null, 2), { encoding: "utf8", mode: 384 });
|
|
53735
|
+
try {
|
|
53736
|
+
fs3.chmodSync(configPath, 384);
|
|
53737
|
+
} catch {
|
|
53738
|
+
}
|
|
53739
|
+
return bindHost;
|
|
53740
|
+
}
|
|
53741
|
+
function createPasswordRecord(password, salt = (0, import_crypto2.randomBytes)(16).toString("hex")) {
|
|
53742
|
+
return {
|
|
53743
|
+
passwordHash: (0, import_crypto2.scryptSync)(`${password || ""}`, salt, PASSWORD_KEYLEN).toString("hex"),
|
|
53744
|
+
passwordSalt: salt,
|
|
53745
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
53746
|
+
};
|
|
53747
|
+
}
|
|
53748
|
+
function verifyPassword(password, config2) {
|
|
53749
|
+
if (!config2?.passwordHash || !config2.passwordSalt) return false;
|
|
53750
|
+
const actual = Buffer.from((0, import_crypto2.scryptSync)(`${password || ""}`, config2.passwordSalt, PASSWORD_KEYLEN).toString("hex"), "utf8");
|
|
53751
|
+
const expected = Buffer.from(config2.passwordHash, "utf8");
|
|
53752
|
+
return actual.length === expected.length && (0, import_crypto2.timingSafeEqual)(actual, expected);
|
|
53753
|
+
}
|
|
53754
|
+
function loadStandalonePasswordConfig(filePath = getStandalonePasswordConfigPath()) {
|
|
53755
|
+
if (!fs3.existsSync(filePath)) return null;
|
|
53756
|
+
try {
|
|
53757
|
+
const parsed = JSON.parse(fs3.readFileSync(filePath, "utf8"));
|
|
53758
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
53759
|
+
if (typeof parsed.passwordHash !== "string" || typeof parsed.passwordSalt !== "string") return null;
|
|
53760
|
+
return {
|
|
53761
|
+
passwordHash: parsed.passwordHash,
|
|
53762
|
+
passwordSalt: parsed.passwordSalt,
|
|
53763
|
+
updatedAt: typeof parsed.updatedAt === "string" ? parsed.updatedAt : (/* @__PURE__ */ new Date(0)).toISOString()
|
|
53764
|
+
};
|
|
53765
|
+
} catch {
|
|
53766
|
+
return null;
|
|
53767
|
+
}
|
|
53768
|
+
}
|
|
53769
|
+
function saveStandalonePasswordConfig(filePath, config2) {
|
|
53770
|
+
const dir = path4.dirname(filePath);
|
|
53771
|
+
if (!fs3.existsSync(dir)) {
|
|
53772
|
+
fs3.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
53773
|
+
}
|
|
53774
|
+
fs3.writeFileSync(filePath, JSON.stringify(config2, null, 2), { encoding: "utf8", mode: 384 });
|
|
53775
|
+
try {
|
|
53776
|
+
fs3.chmodSync(filePath, 384);
|
|
53777
|
+
} catch {
|
|
53778
|
+
}
|
|
53779
|
+
}
|
|
53780
|
+
function clearStandalonePasswordConfig(filePath = getStandalonePasswordConfigPath()) {
|
|
53781
|
+
if (fs3.existsSync(filePath)) {
|
|
53782
|
+
fs3.rmSync(filePath, { force: true });
|
|
53783
|
+
}
|
|
53784
|
+
}
|
|
53785
|
+
function shouldWarnForPublicUnauthenticatedHost(input) {
|
|
53786
|
+
return input.host === "0.0.0.0" && !input.hasTokenAuth && !input.hasPasswordAuth;
|
|
53787
|
+
}
|
|
53788
|
+
function parseCookies(cookieHeader) {
|
|
53789
|
+
if (!cookieHeader) return {};
|
|
53790
|
+
return Object.fromEntries(
|
|
53791
|
+
cookieHeader.split(";").map((part) => part.trim()).filter(Boolean).map((part) => {
|
|
53792
|
+
const eq = part.indexOf("=");
|
|
53793
|
+
if (eq === -1) return [part, ""];
|
|
53794
|
+
return [part.slice(0, eq), decodeURIComponent(part.slice(eq + 1))];
|
|
53795
|
+
})
|
|
53796
|
+
);
|
|
53797
|
+
}
|
|
53798
|
+
function buildSessionCookie(sessionId, secure, maxAgeMs = DEFAULT_SESSION_TTL_MS) {
|
|
53799
|
+
const parts = [
|
|
53800
|
+
`${STANDALONE_AUTH_SESSION_COOKIE}=${encodeURIComponent(sessionId)}`,
|
|
53801
|
+
"Path=/",
|
|
53802
|
+
"HttpOnly",
|
|
53803
|
+
"SameSite=Lax",
|
|
53804
|
+
`Max-Age=${Math.max(0, Math.floor(maxAgeMs / 1e3))}`
|
|
53805
|
+
];
|
|
53806
|
+
if (secure) parts.push("Secure");
|
|
53807
|
+
return parts.join("; ");
|
|
53808
|
+
}
|
|
53809
|
+
function buildClearedSessionCookie(secure) {
|
|
53810
|
+
return buildSessionCookie("", secure, 0);
|
|
53811
|
+
}
|
|
53812
|
+
var StandaloneSessionStore = class {
|
|
53813
|
+
sessions = /* @__PURE__ */ new Map();
|
|
53814
|
+
create(ttlMs = DEFAULT_SESSION_TTL_MS) {
|
|
53815
|
+
const id = (0, import_crypto2.randomBytes)(24).toString("hex");
|
|
53816
|
+
this.sessions.set(id, Date.now() + ttlMs);
|
|
53817
|
+
return id;
|
|
53818
|
+
}
|
|
53819
|
+
has(sessionId) {
|
|
53820
|
+
if (!sessionId) return false;
|
|
53821
|
+
const expiresAt = this.sessions.get(sessionId);
|
|
53822
|
+
if (!expiresAt) return false;
|
|
53823
|
+
if (expiresAt <= Date.now()) {
|
|
53824
|
+
this.sessions.delete(sessionId);
|
|
53825
|
+
return false;
|
|
53826
|
+
}
|
|
53827
|
+
return true;
|
|
53828
|
+
}
|
|
53829
|
+
revoke(sessionId) {
|
|
53830
|
+
if (!sessionId) return;
|
|
53831
|
+
this.sessions.delete(sessionId);
|
|
53832
|
+
}
|
|
53833
|
+
clear() {
|
|
53834
|
+
this.sessions.clear();
|
|
53835
|
+
}
|
|
53836
|
+
};
|
|
53837
|
+
function isStandaloneRequestAuthenticated(input) {
|
|
53838
|
+
const hasTokenAuth = !!input.configuredToken;
|
|
53839
|
+
const hasPasswordAuth = !!input.passwordConfig;
|
|
53840
|
+
if (!hasTokenAuth && !hasPasswordAuth) return true;
|
|
53841
|
+
if (hasTokenAuth && (input.bearerToken === input.configuredToken || input.queryToken === input.configuredToken)) {
|
|
53842
|
+
return true;
|
|
53843
|
+
}
|
|
53844
|
+
if (hasPasswordAuth) {
|
|
53845
|
+
const cookies = parseCookies(input.cookieHeader);
|
|
53846
|
+
return input.sessionStore.has(cookies[STANDALONE_AUTH_SESSION_COOKIE]);
|
|
53847
|
+
}
|
|
53848
|
+
return false;
|
|
53849
|
+
}
|
|
53127
53850
|
var pkgVersion = process.env.ADHDEV_PKG_VERSION || "unknown";
|
|
53128
53851
|
if (pkgVersion === "unknown") {
|
|
53129
53852
|
try {
|
|
@@ -53172,6 +53895,10 @@ var StandaloneServer = class {
|
|
|
53172
53895
|
wsSessionModalSubscriptions = /* @__PURE__ */ new Map();
|
|
53173
53896
|
wsDaemonMetadataSubscriptions = /* @__PURE__ */ new Map();
|
|
53174
53897
|
authToken = null;
|
|
53898
|
+
passwordConfigPath = getStandalonePasswordConfigPath();
|
|
53899
|
+
passwordConfig = null;
|
|
53900
|
+
authSessions = new StandaloneSessionStore();
|
|
53901
|
+
listenHost = "127.0.0.1";
|
|
53175
53902
|
statusTimer = null;
|
|
53176
53903
|
lastStatusBroadcastAt = 0;
|
|
53177
53904
|
statusBroadcastPending = false;
|
|
@@ -53228,10 +53955,88 @@ var StandaloneServer = class {
|
|
|
53228
53955
|
const mode = this.getCliPresentationMode(sessionId);
|
|
53229
53956
|
return mode === "chat" || mode === "terminal";
|
|
53230
53957
|
}
|
|
53958
|
+
hasPasswordAuth() {
|
|
53959
|
+
return !!this.passwordConfig;
|
|
53960
|
+
}
|
|
53961
|
+
hasAnyAuth() {
|
|
53962
|
+
return !!this.authToken || this.hasPasswordAuth();
|
|
53963
|
+
}
|
|
53964
|
+
getCookieSecureFlag(req) {
|
|
53965
|
+
const forwardedProto = req.headers["x-forwarded-proto"];
|
|
53966
|
+
return !!req.socket.encrypted || typeof forwardedProto === "string" && forwardedProto.toLowerCase().includes("https");
|
|
53967
|
+
}
|
|
53968
|
+
getRequestTokens(req, rawUrl) {
|
|
53969
|
+
const authHeader = req.headers["authorization"];
|
|
53970
|
+
const bearerToken = typeof authHeader === "string" && authHeader.startsWith("Bearer ") ? authHeader.slice(7) : null;
|
|
53971
|
+
const queryToken = new URL(rawUrl, `http://${req.headers.host || "localhost"}`).searchParams.get("token");
|
|
53972
|
+
return { bearerToken, queryToken };
|
|
53973
|
+
}
|
|
53974
|
+
isRequestAuthenticated(req, rawUrl) {
|
|
53975
|
+
const { bearerToken, queryToken } = this.getRequestTokens(req, rawUrl);
|
|
53976
|
+
return isStandaloneRequestAuthenticated({
|
|
53977
|
+
configuredToken: this.authToken,
|
|
53978
|
+
passwordConfig: this.passwordConfig,
|
|
53979
|
+
bearerToken,
|
|
53980
|
+
queryToken,
|
|
53981
|
+
cookieHeader: typeof req.headers.cookie === "string" ? req.headers.cookie : void 0,
|
|
53982
|
+
sessionStore: this.authSessions
|
|
53983
|
+
});
|
|
53984
|
+
}
|
|
53985
|
+
getRequestSessionId(req) {
|
|
53986
|
+
const cookies = parseCookies(typeof req.headers.cookie === "string" ? req.headers.cookie : void 0);
|
|
53987
|
+
return cookies.adhdev_standalone_session || null;
|
|
53988
|
+
}
|
|
53989
|
+
buildAuthStatus(req, rawUrl) {
|
|
53990
|
+
const required2 = this.hasAnyAuth();
|
|
53991
|
+
return {
|
|
53992
|
+
required: required2,
|
|
53993
|
+
authenticated: this.isRequestAuthenticated(req, rawUrl),
|
|
53994
|
+
hasTokenAuth: !!this.authToken,
|
|
53995
|
+
hasPasswordAuth: this.hasPasswordAuth(),
|
|
53996
|
+
publicHostWarning: shouldWarnForPublicUnauthenticatedHost({
|
|
53997
|
+
host: this.listenHost,
|
|
53998
|
+
hasTokenAuth: !!this.authToken,
|
|
53999
|
+
hasPasswordAuth: this.hasPasswordAuth()
|
|
54000
|
+
}),
|
|
54001
|
+
boundHost: this.listenHost
|
|
54002
|
+
};
|
|
54003
|
+
}
|
|
54004
|
+
isTrustedStandaloneMutationRequest(req) {
|
|
54005
|
+
const originHeader = req.headers.origin;
|
|
54006
|
+
if (typeof originHeader !== "string" || !originHeader.trim()) return true;
|
|
54007
|
+
try {
|
|
54008
|
+
const origin = new URL(originHeader);
|
|
54009
|
+
const host = req.headers.host || "";
|
|
54010
|
+
return origin.host === host;
|
|
54011
|
+
} catch {
|
|
54012
|
+
return false;
|
|
54013
|
+
}
|
|
54014
|
+
}
|
|
54015
|
+
async readJsonBody(req) {
|
|
54016
|
+
return await new Promise((resolve4, reject) => {
|
|
54017
|
+
let body = "";
|
|
54018
|
+
req.on("data", (chunk) => {
|
|
54019
|
+
body += chunk;
|
|
54020
|
+
});
|
|
54021
|
+
req.on("end", () => {
|
|
54022
|
+
try {
|
|
54023
|
+
resolve4(body ? JSON.parse(body) : {});
|
|
54024
|
+
} catch (error48) {
|
|
54025
|
+
reject(error48);
|
|
54026
|
+
}
|
|
54027
|
+
});
|
|
54028
|
+
req.on("error", reject);
|
|
54029
|
+
});
|
|
54030
|
+
}
|
|
53231
54031
|
async start(options = {}) {
|
|
53232
|
-
const
|
|
53233
|
-
const host = options.host || "127.0.0.1";
|
|
54032
|
+
const persistedStandaloneBindHost = loadStandaloneBindHostPreference();
|
|
53234
54033
|
const cfg = (0, import_daemon_core2.loadConfig)();
|
|
54034
|
+
if (!options.host && persistedStandaloneBindHost !== STANDALONE_BIND_HOST_DEFAULT) {
|
|
54035
|
+
saveStandaloneBindHostPreference(persistedStandaloneBindHost);
|
|
54036
|
+
}
|
|
54037
|
+
const port = options.port || DEFAULT_PORT;
|
|
54038
|
+
const host = options.host || persistedStandaloneBindHost;
|
|
54039
|
+
this.listenHost = host;
|
|
53235
54040
|
const sessionHostEndpoint = await ensureSessionHostReady();
|
|
53236
54041
|
this.sessionHostEndpoint = sessionHostEndpoint;
|
|
53237
54042
|
const sessionHostControl = new StandaloneSessionHostControlPlane(
|
|
@@ -53239,6 +54044,7 @@ var StandaloneServer = class {
|
|
|
53239
54044
|
);
|
|
53240
54045
|
this.sessionHostControl = sessionHostControl;
|
|
53241
54046
|
this.authToken = options.token || process.env.ADHDEV_TOKEN || null;
|
|
54047
|
+
this.passwordConfig = loadStandalonePasswordConfig(this.passwordConfigPath);
|
|
53242
54048
|
const statusInstanceId = `standalone_${cfg.machineId || "mach_unknown"}`;
|
|
53243
54049
|
this.components = await (0, import_daemon_core2.initDaemonComponents)({
|
|
53244
54050
|
cliManagerDeps: {
|
|
@@ -53256,7 +54062,7 @@ var StandaloneServer = class {
|
|
|
53256
54062
|
}),
|
|
53257
54063
|
onStatusChange: () => {
|
|
53258
54064
|
this.scheduleBroadcastStatus();
|
|
53259
|
-
void this.flushWsChatSubscriptions(void 0, { onlyActive:
|
|
54065
|
+
void this.flushWsChatSubscriptions(void 0, { onlyActive: true });
|
|
53260
54066
|
},
|
|
53261
54067
|
removeAgentTracking: () => {
|
|
53262
54068
|
},
|
|
@@ -53285,7 +54091,7 @@ var StandaloneServer = class {
|
|
|
53285
54091
|
statusDaemonMode: false,
|
|
53286
54092
|
onStatusChange: () => {
|
|
53287
54093
|
this.scheduleBroadcastStatus();
|
|
53288
|
-
void this.flushWsChatSubscriptions(void 0, { onlyActive:
|
|
54094
|
+
void this.flushWsChatSubscriptions(void 0, { onlyActive: true });
|
|
53289
54095
|
},
|
|
53290
54096
|
sessionHostControl,
|
|
53291
54097
|
onStreamsUpdated: (ideType, streams) => {
|
|
@@ -53312,13 +54118,10 @@ var StandaloneServer = class {
|
|
|
53312
54118
|
this.httpServer.on("upgrade", (req, socket, head) => {
|
|
53313
54119
|
const wsUrl = new URL(req.url || "/", `http://${req.headers.host || "localhost"}`);
|
|
53314
54120
|
if (wsUrl.pathname === "/ws") {
|
|
53315
|
-
if (this.
|
|
53316
|
-
|
|
53317
|
-
|
|
53318
|
-
|
|
53319
|
-
socket.destroy();
|
|
53320
|
-
return;
|
|
53321
|
-
}
|
|
54121
|
+
if (!this.isRequestAuthenticated(req, req.url || "/")) {
|
|
54122
|
+
socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n");
|
|
54123
|
+
socket.destroy();
|
|
54124
|
+
return;
|
|
53322
54125
|
}
|
|
53323
54126
|
this.wss.handleUpgrade(req, socket, head, (ws2) => {
|
|
53324
54127
|
this.handleWsConnection(ws2);
|
|
@@ -53351,7 +54154,14 @@ var StandaloneServer = class {
|
|
|
53351
54154
|
}
|
|
53352
54155
|
}
|
|
53353
54156
|
if (this.authToken) {
|
|
53354
|
-
console.log(
|
|
54157
|
+
console.log(" \u{1F511} Token auth: enabled");
|
|
54158
|
+
}
|
|
54159
|
+
if (this.passwordConfig) {
|
|
54160
|
+
console.log(" \u{1F510} Password auth: enabled");
|
|
54161
|
+
}
|
|
54162
|
+
if (shouldWarnForPublicUnauthenticatedHost({ host, hasTokenAuth: !!this.authToken, hasPasswordAuth: !!this.passwordConfig })) {
|
|
54163
|
+
console.warn(" \u26A0\uFE0F Public host mode is enabled without any auth.");
|
|
54164
|
+
console.warn(" Anyone on your LAN can open and control this dashboard until you set a password or token.");
|
|
53355
54165
|
}
|
|
53356
54166
|
console.log("");
|
|
53357
54167
|
const cdpCount = [...this.components.cdpManagers.values()].filter((m) => m.isConnected).length;
|
|
@@ -53391,18 +54201,148 @@ var StandaloneServer = class {
|
|
|
53391
54201
|
res.end();
|
|
53392
54202
|
return;
|
|
53393
54203
|
}
|
|
53394
|
-
|
|
53395
|
-
|
|
53396
|
-
|
|
53397
|
-
|
|
53398
|
-
|
|
53399
|
-
|
|
53400
|
-
|
|
53401
|
-
|
|
53402
|
-
|
|
54204
|
+
const parsedUrl = new URL(url2, `http://${req.headers.host || "localhost"}`);
|
|
54205
|
+
if (parsedUrl.pathname === "/auth/session" && method === "GET") {
|
|
54206
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
54207
|
+
res.end(JSON.stringify(this.buildAuthStatus(req, url2)));
|
|
54208
|
+
return;
|
|
54209
|
+
}
|
|
54210
|
+
if (parsedUrl.pathname === "/auth/login" && method === "POST") {
|
|
54211
|
+
void (async () => {
|
|
54212
|
+
if (!this.passwordConfig) {
|
|
54213
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
54214
|
+
res.end(JSON.stringify({ error: "Password auth is not configured." }));
|
|
54215
|
+
return;
|
|
54216
|
+
}
|
|
54217
|
+
const body = await this.readJsonBody(req);
|
|
54218
|
+
if (!verifyPassword(typeof body.password === "string" ? body.password : "", this.passwordConfig)) {
|
|
54219
|
+
res.writeHead(401, { "Content-Type": "application/json" });
|
|
54220
|
+
res.end(JSON.stringify({ error: "Incorrect password." }));
|
|
54221
|
+
return;
|
|
54222
|
+
}
|
|
54223
|
+
this.authSessions.clear();
|
|
54224
|
+
const sessionId = this.authSessions.create();
|
|
54225
|
+
res.setHeader("Set-Cookie", buildSessionCookie(sessionId, this.getCookieSecureFlag(req)));
|
|
54226
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
54227
|
+
res.end(JSON.stringify({ ...this.buildAuthStatus(req, url2), authenticated: true }));
|
|
54228
|
+
})().catch((error48) => {
|
|
54229
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
54230
|
+
res.end(JSON.stringify({ error: error48?.message || String(error48) }));
|
|
54231
|
+
});
|
|
54232
|
+
return;
|
|
54233
|
+
}
|
|
54234
|
+
if (parsedUrl.pathname === "/auth/logout" && method === "POST") {
|
|
54235
|
+
this.authSessions.revoke(this.getRequestSessionId(req));
|
|
54236
|
+
res.setHeader("Set-Cookie", buildClearedSessionCookie(this.getCookieSecureFlag(req)));
|
|
54237
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
54238
|
+
res.end(JSON.stringify({ success: true }));
|
|
54239
|
+
return;
|
|
54240
|
+
}
|
|
54241
|
+
if (parsedUrl.pathname === "/auth/password" && method === "POST") {
|
|
54242
|
+
void (async () => {
|
|
54243
|
+
if (!this.hasAnyAuth() && !this.isTrustedStandaloneMutationRequest(req)) {
|
|
54244
|
+
res.writeHead(403, { "Content-Type": "application/json" });
|
|
54245
|
+
res.end(JSON.stringify({ error: "Cross-origin standalone settings changes are not allowed without existing auth." }));
|
|
54246
|
+
return;
|
|
54247
|
+
}
|
|
54248
|
+
if (this.hasAnyAuth() && !this.isRequestAuthenticated(req, url2)) {
|
|
54249
|
+
res.writeHead(401, { "Content-Type": "application/json" });
|
|
54250
|
+
res.end(JSON.stringify({ error: "Unauthorized" }));
|
|
54251
|
+
return;
|
|
54252
|
+
}
|
|
54253
|
+
const body = await this.readJsonBody(req);
|
|
54254
|
+
const currentPassword = typeof body.currentPassword === "string" ? body.currentPassword : "";
|
|
54255
|
+
const newPassword = typeof body.newPassword === "string" ? body.newPassword : "";
|
|
54256
|
+
const clearPassword = body.clear === true;
|
|
54257
|
+
if (this.passwordConfig && !verifyPassword(currentPassword, this.passwordConfig)) {
|
|
54258
|
+
res.writeHead(403, { "Content-Type": "application/json" });
|
|
54259
|
+
res.end(JSON.stringify({ error: "Current password is incorrect." }));
|
|
54260
|
+
return;
|
|
54261
|
+
}
|
|
54262
|
+
if (clearPassword) {
|
|
54263
|
+
clearStandalonePasswordConfig(this.passwordConfigPath);
|
|
54264
|
+
this.passwordConfig = null;
|
|
54265
|
+
this.authSessions.clear();
|
|
54266
|
+
res.setHeader("Set-Cookie", buildClearedSessionCookie(this.getCookieSecureFlag(req)));
|
|
54267
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
54268
|
+
res.end(JSON.stringify({ success: true, ...this.buildAuthStatus(req, url2), authenticated: !this.hasAnyAuth() }));
|
|
54269
|
+
return;
|
|
54270
|
+
}
|
|
54271
|
+
if (newPassword.trim().length < 4) {
|
|
54272
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
54273
|
+
res.end(JSON.stringify({ error: "Password must be at least 4 characters." }));
|
|
54274
|
+
return;
|
|
54275
|
+
}
|
|
54276
|
+
const nextConfig = createPasswordRecord(newPassword.trim());
|
|
54277
|
+
saveStandalonePasswordConfig(this.passwordConfigPath, nextConfig);
|
|
54278
|
+
this.passwordConfig = nextConfig;
|
|
54279
|
+
this.authSessions.clear();
|
|
54280
|
+
const sessionId = this.authSessions.create();
|
|
54281
|
+
res.setHeader("Set-Cookie", buildSessionCookie(sessionId, this.getCookieSecureFlag(req)));
|
|
54282
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
54283
|
+
res.end(JSON.stringify({ success: true, ...this.buildAuthStatus(req, url2), authenticated: true }));
|
|
54284
|
+
})().catch((error48) => {
|
|
54285
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
54286
|
+
res.end(JSON.stringify({ error: error48?.message || String(error48) }));
|
|
54287
|
+
});
|
|
54288
|
+
return;
|
|
54289
|
+
}
|
|
54290
|
+
if (parsedUrl.pathname === "/api/v1/standalone/preferences" && method === "GET") {
|
|
54291
|
+
const configuredBindHost = loadStandaloneBindHostPreference();
|
|
54292
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
54293
|
+
res.end(JSON.stringify({
|
|
54294
|
+
standaloneBindHost: configuredBindHost,
|
|
54295
|
+
currentBindHost: this.listenHost,
|
|
54296
|
+
hasPasswordAuth: !!this.passwordConfig,
|
|
54297
|
+
hasTokenAuth: !!this.authToken,
|
|
54298
|
+
publicHostWarning: shouldWarnForPublicUnauthenticatedHost({
|
|
54299
|
+
host: configuredBindHost,
|
|
54300
|
+
hasTokenAuth: !!this.authToken,
|
|
54301
|
+
hasPasswordAuth: !!this.passwordConfig
|
|
54302
|
+
})
|
|
54303
|
+
}));
|
|
54304
|
+
return;
|
|
54305
|
+
}
|
|
54306
|
+
if (parsedUrl.pathname === "/api/v1/standalone/preferences" && method === "POST") {
|
|
54307
|
+
void (async () => {
|
|
54308
|
+
if (!this.hasAnyAuth() && !this.isTrustedStandaloneMutationRequest(req)) {
|
|
54309
|
+
res.writeHead(403, { "Content-Type": "application/json" });
|
|
54310
|
+
res.end(JSON.stringify({ error: "Cross-origin standalone settings changes are not allowed without existing auth." }));
|
|
54311
|
+
return;
|
|
54312
|
+
}
|
|
54313
|
+
if (this.hasAnyAuth() && !this.isRequestAuthenticated(req, url2)) {
|
|
54314
|
+
res.writeHead(401, { "Content-Type": "application/json" });
|
|
54315
|
+
res.end(JSON.stringify({ error: "Unauthorized" }));
|
|
54316
|
+
return;
|
|
54317
|
+
}
|
|
54318
|
+
const body = await this.readJsonBody(req);
|
|
54319
|
+
const nextHost = body?.standaloneBindHost === "0.0.0.0" ? "0.0.0.0" : "127.0.0.1";
|
|
54320
|
+
const savedHost = saveStandaloneBindHostPreference(nextHost);
|
|
54321
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
54322
|
+
res.end(JSON.stringify({
|
|
54323
|
+
success: true,
|
|
54324
|
+
standaloneBindHost: savedHost,
|
|
54325
|
+
currentBindHost: this.listenHost,
|
|
54326
|
+
hasPasswordAuth: !!this.passwordConfig,
|
|
54327
|
+
hasTokenAuth: !!this.authToken,
|
|
54328
|
+
publicHostWarning: shouldWarnForPublicUnauthenticatedHost({
|
|
54329
|
+
host: savedHost,
|
|
54330
|
+
hasTokenAuth: !!this.authToken,
|
|
54331
|
+
hasPasswordAuth: !!this.passwordConfig
|
|
54332
|
+
})
|
|
54333
|
+
}));
|
|
54334
|
+
})().catch((error48) => {
|
|
54335
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
54336
|
+
res.end(JSON.stringify({ error: error48?.message || String(error48) }));
|
|
54337
|
+
});
|
|
54338
|
+
return;
|
|
54339
|
+
}
|
|
54340
|
+
if (url2.startsWith("/api/") && !this.isRequestAuthenticated(req, url2)) {
|
|
54341
|
+
res.writeHead(401, { "Content-Type": "application/json" });
|
|
54342
|
+
res.end(JSON.stringify({ error: "Unauthorized. Provide dashboard session cookie or token auth." }));
|
|
54343
|
+
return;
|
|
53403
54344
|
}
|
|
53404
54345
|
const apiPath = url2.startsWith("/api/v1/") ? url2.slice(7) : null;
|
|
53405
|
-
const parsedUrl = new URL(url2, `http://${req.headers.host || "localhost"}`);
|
|
53406
54346
|
if (apiPath === "/status" && method === "GET") {
|
|
53407
54347
|
const status = this.getStatus(getSharedSnapshot());
|
|
53408
54348
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
@@ -53885,6 +54825,7 @@ var StandaloneServer = class {
|
|
|
53885
54825
|
try {
|
|
53886
54826
|
const targets = targetWs ? [targetWs] : Array.from(this.clients);
|
|
53887
54827
|
const hotSessionIds = options.onlyActive ? this.getHotChatSessionIdsForWsFlush() : null;
|
|
54828
|
+
const tasks = [];
|
|
53888
54829
|
for (const ws2 of targets) {
|
|
53889
54830
|
if (ws2.readyState !== import_ws.WebSocket.OPEN) continue;
|
|
53890
54831
|
const subs = this.wsSubscriptions.get(ws2);
|
|
@@ -53894,11 +54835,18 @@ var StandaloneServer = class {
|
|
|
53894
54835
|
if (hotSessionIds && !hotSessionIds.active.has(targetSessionId) && !hotSessionIds.finalizing.has(targetSessionId)) {
|
|
53895
54836
|
continue;
|
|
53896
54837
|
}
|
|
54838
|
+
tasks.push({ ws: ws2, key, sub });
|
|
54839
|
+
}
|
|
54840
|
+
}
|
|
54841
|
+
await (0, import_daemon_core2.runAsyncBatch)(tasks, async ({ ws: ws2, key, sub }) => {
|
|
54842
|
+
try {
|
|
53897
54843
|
const update = await this.buildChatTailUpdate(sub.request.params, sub, key);
|
|
53898
|
-
if (!update || ws2.readyState !== import_ws.WebSocket.OPEN)
|
|
54844
|
+
if (!update || ws2.readyState !== import_ws.WebSocket.OPEN) return;
|
|
53899
54845
|
ws2.send(JSON.stringify({ type: "topic_update", update }));
|
|
54846
|
+
} catch (error48) {
|
|
54847
|
+
import_daemon_core2.LOG.warn("Standalone", `[chat_tail] skipped session=${sub.request.params.targetSessionId} key=${key} error=${error48?.message || error48}`);
|
|
53900
54848
|
}
|
|
53901
|
-
}
|
|
54849
|
+
}, { concurrency: 4 });
|
|
53902
54850
|
} finally {
|
|
53903
54851
|
this.wsChatFlushInFlight = false;
|
|
53904
54852
|
if (this.pendingWsChatFlush) {
|
|
@@ -54282,6 +55230,7 @@ async function main() {
|
|
|
54282
55230
|
process.exit(exitCode);
|
|
54283
55231
|
}
|
|
54284
55232
|
const options = {};
|
|
55233
|
+
let hostExplicit = false;
|
|
54285
55234
|
for (let i = 0; i < args.length; i++) {
|
|
54286
55235
|
if ((args[i] === "--port" || args[i] === "-p") && args[i + 1]) {
|
|
54287
55236
|
options.port = parseInt(args[i + 1]);
|
|
@@ -54289,6 +55238,7 @@ async function main() {
|
|
|
54289
55238
|
}
|
|
54290
55239
|
if (args[i] === "--host" || args[i] === "-H") {
|
|
54291
55240
|
options.host = "0.0.0.0";
|
|
55241
|
+
hostExplicit = true;
|
|
54292
55242
|
}
|
|
54293
55243
|
if (args[i] === "--public" && args[i + 1]) {
|
|
54294
55244
|
options.publicDir = args[i + 1];
|
|
@@ -54330,6 +55280,9 @@ Runtime commands:
|
|
|
54330
55280
|
process.exit(0);
|
|
54331
55281
|
}
|
|
54332
55282
|
}
|
|
55283
|
+
if (!hostExplicit) {
|
|
55284
|
+
options.host = loadStandaloneBindHostPreference();
|
|
55285
|
+
}
|
|
54333
55286
|
if (!options.publicDir) {
|
|
54334
55287
|
const candidates = [
|
|
54335
55288
|
path4.join(__dirname, "../../web-standalone/dist"),
|
|
@@ -54345,6 +55298,9 @@ Runtime commands:
|
|
|
54345
55298
|
}
|
|
54346
55299
|
const server = new StandaloneServer();
|
|
54347
55300
|
await server.start(options);
|
|
55301
|
+
if (!hostExplicit) {
|
|
55302
|
+
saveStandaloneBindHostPreference(options.host === "0.0.0.0" ? "0.0.0.0" : "127.0.0.1");
|
|
55303
|
+
}
|
|
54348
55304
|
await new Promise(() => {
|
|
54349
55305
|
});
|
|
54350
55306
|
}
|