[Init] Initial commit - NetMesh terminal manager
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
This commit is contained in:
117
electron/bridges/mcpServerBridge/retainOwnedSessions.cjs
Normal file
117
electron/bridges/mcpServerBridge/retainOwnedSessions.cjs
Normal file
@@ -0,0 +1,117 @@
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Merge two scoped snapshots without letting an older cross-scope copy
|
||||
* overwrite newer connection state. Metadata revisions are assigned by the
|
||||
* main-process bridge when a renderer update arrives.
|
||||
*
|
||||
* @param {Record<string, unknown> | null | undefined} previous
|
||||
* @param {Record<string, unknown> | null | undefined} fallback
|
||||
* @returns {Record<string, unknown> | null}
|
||||
*/
|
||||
function mergeRetentionMeta(previous, fallback) {
|
||||
if (!previous && !fallback) return null;
|
||||
if (!previous) return fallback && typeof fallback === "object" ? fallback : null;
|
||||
if (!fallback || typeof fallback !== "object") return previous;
|
||||
|
||||
const previousRevision = Number.isSafeInteger(previous._revision) ? previous._revision : 0;
|
||||
const fallbackRevision = Number.isSafeInteger(fallback._revision) ? fallback._revision : 0;
|
||||
// Unversioned direct helper inputs retain the historical fallback-wins
|
||||
// behavior. Bridge-owned metadata is always versioned.
|
||||
const fallbackIsNewer = fallbackRevision >= previousRevision;
|
||||
const newer = fallbackIsNewer ? fallback : previous;
|
||||
const older = fallbackIsNewer ? previous : fallback;
|
||||
const connected = Object.prototype.hasOwnProperty.call(newer, "connected")
|
||||
? newer.connected !== false
|
||||
: older.connected !== false;
|
||||
return {
|
||||
...older,
|
||||
...newer,
|
||||
hostname: newer.hostname || older.hostname,
|
||||
label: newer.label || older.label,
|
||||
os: newer.os || older.os,
|
||||
username: newer.username || older.username,
|
||||
protocol: newer.protocol || older.protocol,
|
||||
shellType: newer.shellType || older.shellType,
|
||||
deviceType: newer.deviceType || older.deviceType,
|
||||
hostId: newer.hostId || older.hostId,
|
||||
hostChain: Array.isArray(newer.hostChain) ? newer.hostChain : older.hostChain,
|
||||
// Explicit empty arrays in the newer snapshot clear stopped forwards.
|
||||
activePortForwards: Array.isArray(newer.activePortForwards)
|
||||
? newer.activePortForwards
|
||||
: older.activePortForwards,
|
||||
connected,
|
||||
...(Math.max(previousRevision, fallbackRevision) > 0
|
||||
? { _revision: Math.max(previousRevision, fallbackRevision) }
|
||||
: {}),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep host_open-owned sessions in a chat scope when a full metadata replace
|
||||
* would otherwise drop them (e.g. AIChatSidePanel pushing only the current
|
||||
* terminal tab after a mid-turn host_open).
|
||||
*
|
||||
* Empty incoming lists are treated as authoritative clears and are not retained.
|
||||
*
|
||||
* @param {{
|
||||
* incomingSessions: Array<Record<string, unknown>>,
|
||||
* ownedSessionIds: string[],
|
||||
* previousById?: Map<string, Record<string, unknown>> | null,
|
||||
* findFallbackMeta?: ((sessionId: string) => Record<string, unknown> | null | undefined) | null,
|
||||
* }} args
|
||||
* @returns {Array<Record<string, unknown>>}
|
||||
*/
|
||||
function retainOwnedSessions({
|
||||
incomingSessions,
|
||||
ownedSessionIds,
|
||||
previousById = null,
|
||||
findFallbackMeta = null,
|
||||
}) {
|
||||
if (!Array.isArray(incomingSessions) || incomingSessions.length === 0) {
|
||||
return incomingSessions;
|
||||
}
|
||||
if (!Array.isArray(ownedSessionIds) || ownedSessionIds.length === 0) {
|
||||
return incomingSessions;
|
||||
}
|
||||
|
||||
const byId = new Map();
|
||||
for (const entry of incomingSessions) {
|
||||
if (!entry || typeof entry !== "object" || !entry.sessionId) continue;
|
||||
byId.set(String(entry.sessionId), entry);
|
||||
}
|
||||
|
||||
for (const ownedIdRaw of ownedSessionIds) {
|
||||
const ownedId = typeof ownedIdRaw === "string" ? ownedIdRaw.trim() : "";
|
||||
if (!ownedId || byId.has(ownedId)) continue;
|
||||
|
||||
const previous = previousById?.get?.(ownedId) || null;
|
||||
// Always consult fallback — a stale previous connected:false must not
|
||||
// block a fresher cross-scope snapshot (e.g. External MCP / other tab).
|
||||
const fallback = typeof findFallbackMeta === "function"
|
||||
? findFallbackMeta(ownedId)
|
||||
: null;
|
||||
const meta = mergeRetentionMeta(previous, fallback);
|
||||
if (!meta || typeof meta !== "object") continue;
|
||||
|
||||
byId.set(ownedId, {
|
||||
sessionId: ownedId,
|
||||
hostname: meta.hostname || "",
|
||||
label: meta.label || "",
|
||||
os: meta.os || "",
|
||||
username: meta.username || "",
|
||||
protocol: meta.protocol || "",
|
||||
shellType: meta.shellType || "",
|
||||
deviceType: meta.deviceType || "",
|
||||
connected: meta.connected !== false,
|
||||
hostId: meta.hostId || "",
|
||||
hostChain: Array.isArray(meta.hostChain) ? meta.hostChain : [],
|
||||
activePortForwards: Array.isArray(meta.activePortForwards) ? meta.activePortForwards : [],
|
||||
...(Number.isSafeInteger(meta._revision) ? { _revision: meta._revision } : {}),
|
||||
});
|
||||
}
|
||||
|
||||
return Array.from(byId.values());
|
||||
}
|
||||
|
||||
module.exports = { retainOwnedSessions, mergeRetentionMeta };
|
||||
Reference in New Issue
Block a user