tmux-ide 2.9.16 → 2.9.17
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/bin/cli.js +285 -48
- package/package.json +1 -1
- package/packages/core/src/terminal-fnv64-wasm.ts +8 -1
- package/packages/core/src/terminal-replica-hash-cache.ts +108 -1
- package/packages/core/src/terminal-replica.ts +14 -0
- package/packages/daemon/dist/command-center/discovery.js +39 -6
- package/packages/daemon/dist/lib/catalog-fleet-read.js +10 -0
- package/packages/daemon/dist/lib/daemon-embed.js +14 -3
- package/packages/daemon/dist/lib/daemon-service.js +13 -5
- package/packages/daemon/dist/lib/tmux-server-owners.js +20 -1
- package/packages/daemon/dist/lib/tui-binary.js +83 -17
- package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/Info.plist +2 -2
- package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/MacOS/tmux-ide-notifier +0 -0
- package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/Resources/AppIcon.icns +0 -0
- package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/Resources/Assets.car +0 -0
- package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/_CodeSignature/CodeResources +4 -4
- package/packages/daemon/src/command-center/discovery.ts +72 -7
- package/packages/daemon/src/lib/catalog-fleet-read.ts +40 -0
- package/packages/daemon/src/lib/daemon-embed.ts +27 -12
- package/packages/daemon/src/lib/daemon-service.ts +17 -4
- package/packages/daemon/src/lib/tmux-server-owners.ts +17 -1
- package/packages/daemon/src/lib/tui-binary.ts +114 -22
package/bin/cli.js
CHANGED
|
@@ -12403,7 +12403,10 @@ __export(tui_binary_exports, {
|
|
|
12403
12403
|
MAX_TUI_COMPRESSED_BYTES: () => MAX_TUI_COMPRESSED_BYTES,
|
|
12404
12404
|
MIN_TUI_BINARY_BYTES: () => MIN_TUI_BINARY_BYTES,
|
|
12405
12405
|
RELEASE_REPO: () => RELEASE_REPO,
|
|
12406
|
-
|
|
12406
|
+
TUI_DOWNLOAD_MAX_ATTEMPTS: () => TUI_DOWNLOAD_MAX_ATTEMPTS,
|
|
12407
|
+
TUI_DOWNLOAD_RETRY_DELAY_MS: () => TUI_DOWNLOAD_RETRY_DELAY_MS,
|
|
12408
|
+
TUI_DOWNLOAD_STALL_TIMEOUT_MS: () => TUI_DOWNLOAD_STALL_TIMEOUT_MS,
|
|
12409
|
+
TUI_DOWNLOAD_TOTAL_TIMEOUT_MS: () => TUI_DOWNLOAD_TOTAL_TIMEOUT_MS,
|
|
12407
12410
|
bunTargetForTag: () => bunTargetForTag,
|
|
12408
12411
|
downloadTuiBinary: () => downloadTuiBinary,
|
|
12409
12412
|
downloadedTuiPath: () => downloadedTuiPath,
|
|
@@ -12548,30 +12551,54 @@ function purgeCachedBinary(path2) {
|
|
|
12548
12551
|
}
|
|
12549
12552
|
}
|
|
12550
12553
|
}
|
|
12551
|
-
|
|
12554
|
+
function isTransientHttpStatus(status2) {
|
|
12555
|
+
return status2 === 408 || status2 === 429 || status2 >= 500;
|
|
12556
|
+
}
|
|
12557
|
+
async function readBoundedResponse(fetchImpl, url, maxBytes, policy) {
|
|
12552
12558
|
const controller = new AbortController();
|
|
12553
|
-
|
|
12559
|
+
let total = 0;
|
|
12560
|
+
let timeoutMessage = null;
|
|
12561
|
+
const abortWith = (message) => () => {
|
|
12562
|
+
timeoutMessage = message();
|
|
12563
|
+
controller.abort();
|
|
12564
|
+
};
|
|
12565
|
+
const overallTimer = setTimeout(
|
|
12566
|
+
abortWith(() => `download did not finish within ${policy.totalTimeoutMs}ms`),
|
|
12567
|
+
Math.max(0, policy.deadline - Date.now())
|
|
12568
|
+
);
|
|
12569
|
+
const onStall = abortWith(
|
|
12570
|
+
() => `download stalled: no data for ${policy.stallTimeoutMs}ms after ${total} bytes`
|
|
12571
|
+
);
|
|
12572
|
+
let stallTimer = setTimeout(onStall, policy.stallTimeoutMs);
|
|
12573
|
+
const markProgress = () => {
|
|
12574
|
+
clearTimeout(stallTimer);
|
|
12575
|
+
stallTimer = setTimeout(onStall, policy.stallTimeoutMs);
|
|
12576
|
+
};
|
|
12554
12577
|
try {
|
|
12555
12578
|
const response3 = await fetchImpl(url, { signal: controller.signal });
|
|
12556
12579
|
if (!response3.ok) {
|
|
12557
|
-
|
|
12580
|
+
const message = `HTTP ${response3.status} ${response3.statusText}`;
|
|
12581
|
+
if (isTransientHttpStatus(response3.status)) throw new Error(message);
|
|
12582
|
+
throw new PermanentDownloadError(message);
|
|
12558
12583
|
}
|
|
12584
|
+
markProgress();
|
|
12559
12585
|
const declaredLength = Number(response3.headers.get("content-length"));
|
|
12560
12586
|
if (Number.isFinite(declaredLength) && declaredLength > maxBytes) {
|
|
12561
|
-
throw new
|
|
12587
|
+
throw new PermanentDownloadError(`declared size ${declaredLength} exceeds ${maxBytes} bytes`);
|
|
12562
12588
|
}
|
|
12563
12589
|
if (!response3.body) throw new Error("response has no body");
|
|
12564
12590
|
const chunks = [];
|
|
12565
|
-
let total = 0;
|
|
12566
12591
|
const reader = response3.body.getReader();
|
|
12567
12592
|
try {
|
|
12568
12593
|
while (true) {
|
|
12569
12594
|
const { done, value } = await reader.read();
|
|
12570
12595
|
if (done) break;
|
|
12596
|
+
if (value.byteLength === 0) continue;
|
|
12597
|
+
markProgress();
|
|
12571
12598
|
total += value.byteLength;
|
|
12572
12599
|
if (total > maxBytes) {
|
|
12573
12600
|
await reader.cancel();
|
|
12574
|
-
throw new
|
|
12601
|
+
throw new PermanentDownloadError(`download exceeds ${maxBytes} bytes`);
|
|
12575
12602
|
}
|
|
12576
12603
|
chunks.push(Buffer.from(value));
|
|
12577
12604
|
}
|
|
@@ -12580,12 +12607,27 @@ async function readBoundedResponse(fetchImpl, url, maxBytes, timeoutMs) {
|
|
|
12580
12607
|
}
|
|
12581
12608
|
return Buffer.concat(chunks, total);
|
|
12582
12609
|
} catch (error) {
|
|
12583
|
-
if (
|
|
12584
|
-
throw new Error(`download timed out after ${timeoutMs}ms`, { cause: error });
|
|
12585
|
-
}
|
|
12610
|
+
if (timeoutMessage) throw new Error(timeoutMessage, { cause: error });
|
|
12586
12611
|
throw error;
|
|
12587
12612
|
} finally {
|
|
12588
|
-
clearTimeout(
|
|
12613
|
+
clearTimeout(overallTimer);
|
|
12614
|
+
clearTimeout(stallTimer);
|
|
12615
|
+
controller.abort();
|
|
12616
|
+
}
|
|
12617
|
+
}
|
|
12618
|
+
async function readWithRetries(fetchImpl, url, maxBytes, policy, log) {
|
|
12619
|
+
for (let attempt = 1; ; attempt++) {
|
|
12620
|
+
try {
|
|
12621
|
+
return await readBoundedResponse(fetchImpl, url, maxBytes, policy);
|
|
12622
|
+
} catch (error) {
|
|
12623
|
+
const delayMs = policy.retryDelayMs * attempt;
|
|
12624
|
+
if (error instanceof PermanentDownloadError || attempt >= policy.maxAttempts || Date.now() + delayMs >= policy.deadline) {
|
|
12625
|
+
throw error;
|
|
12626
|
+
}
|
|
12627
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
12628
|
+
log(`download attempt ${attempt}/${policy.maxAttempts} failed (${message}); retrying`);
|
|
12629
|
+
await new Promise((resolve45) => setTimeout(resolve45, delayMs));
|
|
12630
|
+
}
|
|
12589
12631
|
}
|
|
12590
12632
|
}
|
|
12591
12633
|
async function downloadTuiBinary(opts = {}) {
|
|
@@ -12606,14 +12648,18 @@ async function downloadTuiBinary(opts = {}) {
|
|
|
12606
12648
|
const limits = {
|
|
12607
12649
|
minBinaryBytes: opts.limits?.minBinaryBytes ?? MIN_TUI_BINARY_BYTES,
|
|
12608
12650
|
maxCompressedBytes: opts.limits?.maxCompressedBytes ?? MAX_TUI_COMPRESSED_BYTES,
|
|
12609
|
-
maxBinaryBytes: opts.limits?.maxBinaryBytes ?? MAX_TUI_BINARY_BYTES
|
|
12610
|
-
timeoutMs: opts.timeoutMs ?? TUI_DOWNLOAD_TIMEOUT_MS
|
|
12651
|
+
maxBinaryBytes: opts.limits?.maxBinaryBytes ?? MAX_TUI_BINARY_BYTES
|
|
12611
12652
|
};
|
|
12653
|
+
const totalTimeoutMs = opts.totalTimeoutMs ?? TUI_DOWNLOAD_TOTAL_TIMEOUT_MS;
|
|
12612
12654
|
mkdirSync5(dirname9(dest), { recursive: true });
|
|
12655
|
+
const lockNotice = setTimeout(
|
|
12656
|
+
() => log("waiting for another tmux-ide download of this version to finish"),
|
|
12657
|
+
TUI_DOWNLOAD_LOCK_NOTICE_MS
|
|
12658
|
+
);
|
|
12613
12659
|
const releaseLock = await acquirePrivateOperationLock(
|
|
12614
12660
|
downloadLockPath(dest),
|
|
12615
|
-
|
|
12616
|
-
);
|
|
12661
|
+
totalTimeoutMs + TUI_DOWNLOAD_LOCK_MARGIN_MS
|
|
12662
|
+
).finally(() => clearTimeout(lockNotice));
|
|
12617
12663
|
const suffix = `${process.pid}.${randomUUID2()}.tmp`;
|
|
12618
12664
|
const binaryTmp = `${dest}.${suffix}`;
|
|
12619
12665
|
const checksumTmp = `${checksumPath(dest)}.${suffix}`;
|
|
@@ -12622,21 +12668,31 @@ async function downloadTuiBinary(opts = {}) {
|
|
|
12622
12668
|
if (cached2) return { path: dest, bytes: cached2.bytes };
|
|
12623
12669
|
purgeCachedBinary(dest);
|
|
12624
12670
|
const fetchImpl = opts.fetch ?? globalThis.fetch;
|
|
12671
|
+
const policy = {
|
|
12672
|
+
stallTimeoutMs: opts.stallTimeoutMs ?? TUI_DOWNLOAD_STALL_TIMEOUT_MS,
|
|
12673
|
+
deadline: Date.now() + totalTimeoutMs,
|
|
12674
|
+
totalTimeoutMs,
|
|
12675
|
+
maxAttempts: opts.maxAttempts ?? TUI_DOWNLOAD_MAX_ATTEMPTS,
|
|
12676
|
+
retryDelayMs: opts.retryDelayMs ?? TUI_DOWNLOAD_RETRY_DELAY_MS
|
|
12677
|
+
};
|
|
12625
12678
|
log(`downloading checksum manifest ${checksumUrl}`);
|
|
12626
12679
|
let manifestBytes;
|
|
12627
12680
|
let gz;
|
|
12628
12681
|
try {
|
|
12629
|
-
manifestBytes = await
|
|
12682
|
+
manifestBytes = await readWithRetries(
|
|
12630
12683
|
fetchImpl,
|
|
12631
12684
|
checksumUrl,
|
|
12632
12685
|
MAX_CHECKSUM_MANIFEST_BYTES,
|
|
12633
|
-
|
|
12686
|
+
policy,
|
|
12687
|
+
log
|
|
12634
12688
|
);
|
|
12635
|
-
|
|
12689
|
+
log(`downloading ${url}`);
|
|
12690
|
+
gz = await readWithRetries(fetchImpl, url, limits.maxCompressedBytes, policy, log);
|
|
12636
12691
|
} catch (error) {
|
|
12637
12692
|
const message = error instanceof Error ? error.message : String(error);
|
|
12693
|
+
const hint = error instanceof PermanentDownloadError ? "Check that the release published both exact-version assets." : "Check your network connection, then retry with `tmux-ide update --tui-binary`, or run the installer again if tmux-ide is not installed yet.";
|
|
12638
12694
|
throw new Error(
|
|
12639
|
-
`could not download the TUI binary and checksum for v${version} (${message}).
|
|
12695
|
+
`could not download the TUI binary and checksum for v${version} (${message}). ${hint}`,
|
|
12640
12696
|
{ cause: error }
|
|
12641
12697
|
);
|
|
12642
12698
|
}
|
|
@@ -12681,7 +12737,7 @@ async function downloadTuiBinary(opts = {}) {
|
|
|
12681
12737
|
releaseLock();
|
|
12682
12738
|
}
|
|
12683
12739
|
}
|
|
12684
|
-
var RELEASE_REPO, MIN_TUI_BINARY_BYTES, MAX_TUI_COMPRESSED_BYTES, MAX_TUI_BINARY_BYTES,
|
|
12740
|
+
var RELEASE_REPO, MIN_TUI_BINARY_BYTES, MAX_TUI_COMPRESSED_BYTES, MAX_TUI_BINARY_BYTES, TUI_DOWNLOAD_STALL_TIMEOUT_MS, TUI_DOWNLOAD_TOTAL_TIMEOUT_MS, TUI_DOWNLOAD_MAX_ATTEMPTS, TUI_DOWNLOAD_RETRY_DELAY_MS, MAX_CHECKSUM_MANIFEST_BYTES, TUI_DOWNLOAD_LOCK_NOTICE_MS, TUI_DOWNLOAD_LOCK_MARGIN_MS, SUPPORTED, PermanentDownloadError;
|
|
12685
12741
|
var init_tui_binary = __esm({
|
|
12686
12742
|
"packages/daemon/src/lib/tui-binary.ts"() {
|
|
12687
12743
|
"use strict";
|
|
@@ -12692,13 +12748,20 @@ var init_tui_binary = __esm({
|
|
|
12692
12748
|
MIN_TUI_BINARY_BYTES = 10 * 1024 * 1024;
|
|
12693
12749
|
MAX_TUI_COMPRESSED_BYTES = 128 * 1024 * 1024;
|
|
12694
12750
|
MAX_TUI_BINARY_BYTES = 256 * 1024 * 1024;
|
|
12695
|
-
|
|
12751
|
+
TUI_DOWNLOAD_STALL_TIMEOUT_MS = 3e4;
|
|
12752
|
+
TUI_DOWNLOAD_TOTAL_TIMEOUT_MS = 15 * 6e4;
|
|
12753
|
+
TUI_DOWNLOAD_MAX_ATTEMPTS = 3;
|
|
12754
|
+
TUI_DOWNLOAD_RETRY_DELAY_MS = 1e3;
|
|
12696
12755
|
MAX_CHECKSUM_MANIFEST_BYTES = 4 * 1024;
|
|
12756
|
+
TUI_DOWNLOAD_LOCK_NOTICE_MS = 2e3;
|
|
12757
|
+
TUI_DOWNLOAD_LOCK_MARGIN_MS = 5e3;
|
|
12697
12758
|
SUPPORTED = {
|
|
12698
12759
|
"darwin-arm64": "darwin-arm64",
|
|
12699
12760
|
"linux-x64": "linux-x64",
|
|
12700
12761
|
"linux-arm64": "linux-arm64"
|
|
12701
12762
|
};
|
|
12763
|
+
PermanentDownloadError = class extends Error {
|
|
12764
|
+
};
|
|
12702
12765
|
}
|
|
12703
12766
|
});
|
|
12704
12767
|
|
|
@@ -13082,7 +13145,7 @@ var require_package = __commonJS({
|
|
|
13082
13145
|
"package.json"(exports, module) {
|
|
13083
13146
|
module.exports = {
|
|
13084
13147
|
name: "tmux-ide",
|
|
13085
|
-
version: "2.9.
|
|
13148
|
+
version: "2.9.17",
|
|
13086
13149
|
description: "The open-source workspace for coding agents: run Claude Code, Codex and other agents in tmux with live status",
|
|
13087
13150
|
type: "module",
|
|
13088
13151
|
bin: {
|
|
@@ -27074,10 +27137,13 @@ function isVisibleFleetSession(name) {
|
|
|
27074
27137
|
function readAdoptedSessionNames(runTmux2 = _tmuxRunner) {
|
|
27075
27138
|
let raw;
|
|
27076
27139
|
try {
|
|
27077
|
-
raw = runTmux2(
|
|
27140
|
+
raw = runTmux2(ADOPTED_SESSIONS_ARGS);
|
|
27078
27141
|
} catch {
|
|
27079
27142
|
return null;
|
|
27080
27143
|
}
|
|
27144
|
+
return parseAdoptedSessionNames(raw);
|
|
27145
|
+
}
|
|
27146
|
+
function parseAdoptedSessionNames(raw) {
|
|
27081
27147
|
const names = [];
|
|
27082
27148
|
for (const line of raw.split("\n")) {
|
|
27083
27149
|
if (!line) continue;
|
|
@@ -27096,16 +27162,51 @@ function readAdoptedFleet(registry = getDefaultWorkspaceRegistry(), runTmux2 = _
|
|
|
27096
27162
|
const nativeEpoch = nativeServerEpoch?.() ?? null;
|
|
27097
27163
|
const adopted = readAdoptedSessionNames(runTmux2);
|
|
27098
27164
|
if (adopted === null) return null;
|
|
27099
|
-
|
|
27100
|
-
if (adoptedSet.size === 0) return [];
|
|
27101
|
-
const appCreatedSessions = new Set(registry.list().map((workspace) => workspace.sessionName));
|
|
27102
|
-
const panesBySession = /* @__PURE__ */ new Map();
|
|
27165
|
+
if (adopted.length === 0) return [];
|
|
27103
27166
|
let panesRaw;
|
|
27104
27167
|
try {
|
|
27105
|
-
panesRaw = runTmux2(
|
|
27168
|
+
panesRaw = runTmux2(FLEET_PANES_ARGS);
|
|
27106
27169
|
} catch {
|
|
27107
27170
|
panesRaw = "";
|
|
27108
27171
|
}
|
|
27172
|
+
return assembleAdoptedFleet(
|
|
27173
|
+
adopted,
|
|
27174
|
+
panesRaw,
|
|
27175
|
+
registry,
|
|
27176
|
+
nativeEpoch,
|
|
27177
|
+
resolveInteractionEndpoint,
|
|
27178
|
+
nativeServerEpoch
|
|
27179
|
+
);
|
|
27180
|
+
}
|
|
27181
|
+
async function readAdoptedFleetAsync(registry, runTmux2, resolveInteractionEndpoint, nativeServerEpoch) {
|
|
27182
|
+
const nativeEpoch = nativeServerEpoch?.() ?? null;
|
|
27183
|
+
let sessionsRaw;
|
|
27184
|
+
try {
|
|
27185
|
+
sessionsRaw = await runTmux2(ADOPTED_SESSIONS_ARGS);
|
|
27186
|
+
} catch {
|
|
27187
|
+
return null;
|
|
27188
|
+
}
|
|
27189
|
+
const adopted = parseAdoptedSessionNames(sessionsRaw);
|
|
27190
|
+
if (adopted.length === 0) return [];
|
|
27191
|
+
let panesRaw;
|
|
27192
|
+
try {
|
|
27193
|
+
panesRaw = await runTmux2(FLEET_PANES_ARGS);
|
|
27194
|
+
} catch {
|
|
27195
|
+
panesRaw = "";
|
|
27196
|
+
}
|
|
27197
|
+
return assembleAdoptedFleet(
|
|
27198
|
+
adopted,
|
|
27199
|
+
panesRaw,
|
|
27200
|
+
registry,
|
|
27201
|
+
nativeEpoch,
|
|
27202
|
+
resolveInteractionEndpoint,
|
|
27203
|
+
nativeServerEpoch
|
|
27204
|
+
);
|
|
27205
|
+
}
|
|
27206
|
+
function assembleAdoptedFleet(adopted, panesRaw, registry, nativeEpoch, resolveInteractionEndpoint, nativeServerEpoch) {
|
|
27207
|
+
const adoptedSet = new Set(adopted);
|
|
27208
|
+
const appCreatedSessions = new Set(registry.list().map((workspace) => workspace.sessionName));
|
|
27209
|
+
const panesBySession = /* @__PURE__ */ new Map();
|
|
27109
27210
|
for (const line of panesRaw.split("\n")) {
|
|
27110
27211
|
if (!line) continue;
|
|
27111
27212
|
const fields = line.split(FLEET_FIELD_SEPARATOR);
|
|
@@ -27193,7 +27294,7 @@ function buildProjectDetail(info) {
|
|
|
27193
27294
|
panes: info.panes.map(({ semanticPaneId: _semanticPaneId, ...pane }) => pane)
|
|
27194
27295
|
};
|
|
27195
27296
|
}
|
|
27196
|
-
var _tmuxRunner, FLEET_FIELD_SEPARATOR, FLEET_LINE_SENTINEL, FLEET_PANE_FORMAT;
|
|
27297
|
+
var _tmuxRunner, ADOPTED_SESSIONS_ARGS, FLEET_FIELD_SEPARATOR, FLEET_LINE_SENTINEL, FLEET_PANE_FORMAT, FLEET_PANES_ARGS;
|
|
27197
27298
|
var init_discovery = __esm({
|
|
27198
27299
|
"packages/daemon/src/command-center/discovery.ts"() {
|
|
27199
27300
|
"use strict";
|
|
@@ -27210,6 +27311,7 @@ var init_discovery = __esm({
|
|
|
27210
27311
|
maxBuffer: 1024 * 1024,
|
|
27211
27312
|
stdio: ["ignore", "pipe", "ignore"]
|
|
27212
27313
|
}).trim();
|
|
27314
|
+
ADOPTED_SESSIONS_ARGS = ["list-sessions", "-F", `#{session_name} #{${ADOPTED_OPTION}}`];
|
|
27213
27315
|
FLEET_FIELD_SEPARATOR = "|tmux-ide-fleet-field-v1|";
|
|
27214
27316
|
FLEET_LINE_SENTINEL = "tmux-ide-fleet-v1";
|
|
27215
27317
|
FLEET_PANE_FORMAT = [
|
|
@@ -27231,6 +27333,7 @@ var init_discovery = __esm({
|
|
|
27231
27333
|
"#{@tmux_ide_team}",
|
|
27232
27334
|
FLEET_LINE_SENTINEL
|
|
27233
27335
|
].join(FLEET_FIELD_SEPARATOR);
|
|
27336
|
+
FLEET_PANES_ARGS = ["list-panes", "-a", "-F", FLEET_PANE_FORMAT];
|
|
27234
27337
|
}
|
|
27235
27338
|
});
|
|
27236
27339
|
|
|
@@ -43804,11 +43907,14 @@ function initialize() {
|
|
|
43804
43907
|
}
|
|
43805
43908
|
return native;
|
|
43806
43909
|
}
|
|
43910
|
+
function nativeFnv64() {
|
|
43911
|
+
return initialize();
|
|
43912
|
+
}
|
|
43807
43913
|
function createBufferedFnv64(initialState = OFFSET_BASIS) {
|
|
43808
43914
|
const hasher = initialize();
|
|
43809
43915
|
return hasher ? new BufferedHasher(hasher, initialState) : null;
|
|
43810
43916
|
}
|
|
43811
|
-
var SCRATCH_BYTES, OFFSET_BASIS, initialized, native, BufferedHasher;
|
|
43917
|
+
var SCRATCH_BYTES, OFFSET_BASIS, initialized, native, FNV64_OFFSET_BASIS, BufferedHasher;
|
|
43812
43918
|
var init_terminal_fnv64_wasm = __esm({
|
|
43813
43919
|
"packages/core/src/terminal-fnv64-wasm.ts"() {
|
|
43814
43920
|
"use strict";
|
|
@@ -43817,6 +43923,7 @@ var init_terminal_fnv64_wasm = __esm({
|
|
|
43817
43923
|
OFFSET_BASIS = 0xcbf29ce484222325n;
|
|
43818
43924
|
initialized = false;
|
|
43819
43925
|
native = null;
|
|
43926
|
+
FNV64_OFFSET_BASIS = OFFSET_BASIS;
|
|
43820
43927
|
BufferedHasher = class {
|
|
43821
43928
|
#native;
|
|
43822
43929
|
#buffer = new Uint8Array(SCRATCH_BYTES);
|
|
@@ -43954,6 +44061,12 @@ function hashTerminalReplicaRowCached(row, onMiss, encodingCache) {
|
|
|
43954
44061
|
const cached2 = ROW_HASH_CACHE.get(row);
|
|
43955
44062
|
if (cached2) return cached2;
|
|
43956
44063
|
onMiss?.();
|
|
44064
|
+
const native2 = nativeFnv64();
|
|
44065
|
+
if (native2) {
|
|
44066
|
+
const digest4 = hashRowBytes(row, native2);
|
|
44067
|
+
if (isTerminalReplicaRowDeeplyFrozen(row)) ROW_HASH_CACHE.set(row, digest4);
|
|
44068
|
+
return digest4;
|
|
44069
|
+
}
|
|
43957
44070
|
const hash = encodingCache?.createHash() ?? new CanonicalFnv64();
|
|
43958
44071
|
let canonicalBytes = 10 + String(row.cells.length).length;
|
|
43959
44072
|
hash.ascii("a2:");
|
|
@@ -44017,6 +44130,10 @@ async function hashTerminalReplicaRowCooperatively(row, yieldControl, bytesPerSl
|
|
|
44017
44130
|
}
|
|
44018
44131
|
function isTerminalReplicaRowDeeplyFrozen(row) {
|
|
44019
44132
|
if (DEEPLY_FROZEN_ROWS.has(row)) return true;
|
|
44133
|
+
if (isOwnedTerminalReplicaRow(row)) {
|
|
44134
|
+
DEEPLY_FROZEN_ROWS.add(row);
|
|
44135
|
+
return true;
|
|
44136
|
+
}
|
|
44020
44137
|
if (!Object.isFrozen(row) || !Object.isFrozen(row.cells)) return false;
|
|
44021
44138
|
for (const cell of row.cells) {
|
|
44022
44139
|
if (!Object.isFrozen(cell) || !Object.isFrozen(cell.foreground) || !Object.isFrozen(cell.background))
|
|
@@ -44025,7 +44142,74 @@ function isTerminalReplicaRowDeeplyFrozen(row) {
|
|
|
44025
44142
|
DEEPLY_FROZEN_ROWS.add(row);
|
|
44026
44143
|
return true;
|
|
44027
44144
|
}
|
|
44028
|
-
|
|
44145
|
+
function canonicalNumber(value) {
|
|
44146
|
+
const text2 = String(value);
|
|
44147
|
+
return `d${text2.length}:${text2};`;
|
|
44148
|
+
}
|
|
44149
|
+
function canonicalColor(color3) {
|
|
44150
|
+
if (color3.kind === "default") return "o1:s4:kind;s7:default;;";
|
|
44151
|
+
if (color3.kind === "indexed")
|
|
44152
|
+
return `o2:s5:index;${canonicalNumber(color3.index)}s4:kind;s7:indexed;;`;
|
|
44153
|
+
return `o2:s4:kind;s3:rgb;s5:value;${canonicalNumber(color3.value)};`;
|
|
44154
|
+
}
|
|
44155
|
+
function encodeCell(cell) {
|
|
44156
|
+
const grapheme = cell.grapheme;
|
|
44157
|
+
let length = grapheme.length;
|
|
44158
|
+
for (let index = 0; index < grapheme.length; index += 1)
|
|
44159
|
+
if (grapheme.charCodeAt(index) > 127) {
|
|
44160
|
+
length = UTF8_ENCODER.encode(grapheme).byteLength;
|
|
44161
|
+
break;
|
|
44162
|
+
}
|
|
44163
|
+
return UTF8_ENCODER.encode(
|
|
44164
|
+
`a5:s${length}:${grapheme};${canonicalNumber(cell.width)}${canonicalColor(
|
|
44165
|
+
cell.foreground
|
|
44166
|
+
)}${canonicalColor(cell.background)}${canonicalNumber(cell.attributes)};`
|
|
44167
|
+
);
|
|
44168
|
+
}
|
|
44169
|
+
function remember(cache3, key2, bytes) {
|
|
44170
|
+
if (cache3.size >= CELL_BYTES_LIMIT) cache3.clear();
|
|
44171
|
+
cache3.set(key2, bytes);
|
|
44172
|
+
return bytes;
|
|
44173
|
+
}
|
|
44174
|
+
function cellBytes(cell) {
|
|
44175
|
+
const { grapheme, width, attributes, foreground, background } = cell;
|
|
44176
|
+
if (width === 1 && attributes === 0 && foreground.kind === "default" && background.kind === "default")
|
|
44177
|
+
return PLAIN_CELL_BYTES.get(grapheme) ?? remember(PLAIN_CELL_BYTES, grapheme, encodeCell(cell));
|
|
44178
|
+
const key2 = `${width}\0${attributes}\0${canonicalColor(foreground)}\0${canonicalColor(
|
|
44179
|
+
background
|
|
44180
|
+
)}\0${grapheme}`;
|
|
44181
|
+
return STYLED_CELL_BYTES.get(key2) ?? remember(STYLED_CELL_BYTES, key2, encodeCell(cell));
|
|
44182
|
+
}
|
|
44183
|
+
function hashRowBytes(row, native2) {
|
|
44184
|
+
const scratch = native2.scratch;
|
|
44185
|
+
const capacity = scratch.byteLength;
|
|
44186
|
+
let state = FNV64_OFFSET_BASIS;
|
|
44187
|
+
let length = 0;
|
|
44188
|
+
const write2 = (bytes) => {
|
|
44189
|
+
if (bytes.byteLength <= capacity - length) {
|
|
44190
|
+
scratch.set(bytes, length);
|
|
44191
|
+
length += bytes.byteLength;
|
|
44192
|
+
return;
|
|
44193
|
+
}
|
|
44194
|
+
let offset = 0;
|
|
44195
|
+
while (offset < bytes.byteLength) {
|
|
44196
|
+
const count = Math.min(capacity - length, bytes.byteLength - offset);
|
|
44197
|
+
scratch.set(bytes.subarray(offset, offset + count), length);
|
|
44198
|
+
length += count;
|
|
44199
|
+
offset += count;
|
|
44200
|
+
if (length === capacity) {
|
|
44201
|
+
state = BigInt.asUintN(64, native2.update(state, length));
|
|
44202
|
+
length = 0;
|
|
44203
|
+
}
|
|
44204
|
+
}
|
|
44205
|
+
};
|
|
44206
|
+
write2(ASCII_ENCODER.encode(`a2:${row.wrapped ? "b1;" : "b0;"}a${row.cells.length}:`));
|
|
44207
|
+
for (const cell of row.cells) write2(cellBytes(cell));
|
|
44208
|
+
write2(ROW_TRAILER);
|
|
44209
|
+
state = BigInt.asUintN(64, native2.update(state, length));
|
|
44210
|
+
return state.toString(16).padStart(16, "0");
|
|
44211
|
+
}
|
|
44212
|
+
var ROW_HASH_CACHE, DEEPLY_FROZEN_ROWS, UTF8_ENCODER, SHORT_ASCII_HEADERS, DEFAULT_COLOR_KEYS, DEFAULT_COLOR_KEY_ORDER, CanonicalFnv64, compactColorKey, TerminalReplicaRunEncodingCache, DECODED_DEFAULT_COLOR, ASCII_ENCODER, CELL_BYTES_LIMIT, PLAIN_CELL_BYTES, STYLED_CELL_BYTES, ROW_TRAILER;
|
|
44029
44213
|
var init_terminal_replica_hash_cache = __esm({
|
|
44030
44214
|
"packages/core/src/terminal-replica-hash-cache.ts"() {
|
|
44031
44215
|
"use strict";
|
|
@@ -44272,6 +44456,11 @@ var init_terminal_replica_hash_cache = __esm({
|
|
|
44272
44456
|
}
|
|
44273
44457
|
};
|
|
44274
44458
|
DECODED_DEFAULT_COLOR = Object.freeze({ kind: "default" });
|
|
44459
|
+
ASCII_ENCODER = new TextEncoder();
|
|
44460
|
+
CELL_BYTES_LIMIT = 4096;
|
|
44461
|
+
PLAIN_CELL_BYTES = /* @__PURE__ */ new Map();
|
|
44462
|
+
STYLED_CELL_BYTES = /* @__PURE__ */ new Map();
|
|
44463
|
+
ROW_TRAILER = ASCII_ENCODER.encode(";;");
|
|
44275
44464
|
}
|
|
44276
44465
|
});
|
|
44277
44466
|
|
|
@@ -44891,6 +45080,16 @@ function resolveFrameHash(update, options, profile) {
|
|
|
44891
45080
|
return hash;
|
|
44892
45081
|
}
|
|
44893
45082
|
function terminalReplicaFrameIdentity(update) {
|
|
45083
|
+
if (update.type === "terminal.patch") {
|
|
45084
|
+
const { rows, history: history2, historyDelta, ...rest2 } = update.patch;
|
|
45085
|
+
return [
|
|
45086
|
+
"terminal-replica-patch-frame-v2",
|
|
45087
|
+
{ ...update, patch: rest2 },
|
|
45088
|
+
rows.map(({ index, row }) => [index, hashTerminalReplicaRow(row)]),
|
|
45089
|
+
history2 === void 0 ? null : hashTerminalReplicaRows(history2),
|
|
45090
|
+
historyDelta === void 0 ? null : [historyDelta.trim, hashTerminalReplicaRows(historyDelta.append)]
|
|
45091
|
+
];
|
|
45092
|
+
}
|
|
44894
45093
|
if (update.type !== "terminal.seed") return update;
|
|
44895
45094
|
const { grid, history, ...rest } = update.snapshot;
|
|
44896
45095
|
return [
|
|
@@ -76428,6 +76627,8 @@ var init_tmux_server_owners = __esm({
|
|
|
76428
76627
|
#tail = Promise.resolve();
|
|
76429
76628
|
#disposed = false;
|
|
76430
76629
|
#disposePromise = null;
|
|
76630
|
+
#queuedRefresh = null;
|
|
76631
|
+
#queuedRefreshTail = null;
|
|
76431
76632
|
#serial(work) {
|
|
76432
76633
|
const run = this.#tail.then(() => {
|
|
76433
76634
|
if (this.#disposed) throw new TmuxServerScopeError("disposed");
|
|
@@ -76528,10 +76729,19 @@ var init_tmux_server_owners = __esm({
|
|
|
76528
76729
|
this.#entries.set(scope2.serverId, { registration, scope: scope2, observation: observation2, owner });
|
|
76529
76730
|
}
|
|
76530
76731
|
refresh() {
|
|
76531
|
-
|
|
76732
|
+
if (this.#queuedRefresh && this.#tail === this.#queuedRefreshTail) return this.#queuedRefresh;
|
|
76733
|
+
const queued = this.#serial(async () => {
|
|
76734
|
+
if (this.#queuedRefresh === queued) this.#queuedRefresh = null;
|
|
76532
76735
|
for (const entry of this.#entries.values()) await this.#refresh(entry);
|
|
76533
76736
|
return this.list();
|
|
76534
76737
|
});
|
|
76738
|
+
this.#queuedRefresh = queued;
|
|
76739
|
+
this.#queuedRefreshTail = this.#tail;
|
|
76740
|
+
const settle = () => {
|
|
76741
|
+
if (this.#queuedRefresh === queued) this.#queuedRefresh = null;
|
|
76742
|
+
};
|
|
76743
|
+
queued.then(settle, settle);
|
|
76744
|
+
return queued;
|
|
76535
76745
|
}
|
|
76536
76746
|
current(scopeInput) {
|
|
76537
76747
|
const scope2 = TmuxServerScopeSchemaZ.parse(scopeInput);
|
|
@@ -80687,6 +80897,22 @@ var init_daemon_shutdown = __esm({
|
|
|
80687
80897
|
}
|
|
80688
80898
|
});
|
|
80689
80899
|
|
|
80900
|
+
// packages/daemon/src/lib/catalog-fleet-read.ts
|
|
80901
|
+
async function readCatalogFleet(registry, runners, resolveInteractionEndpoint, nativeServerEpoch) {
|
|
80902
|
+
return runners.fence.active ? readAdoptedFleet(registry, runners.fencedSync, resolveInteractionEndpoint, nativeServerEpoch) : readAdoptedFleetAsync(
|
|
80903
|
+
registry,
|
|
80904
|
+
runners.generationFencedAsync,
|
|
80905
|
+
resolveInteractionEndpoint,
|
|
80906
|
+
nativeServerEpoch
|
|
80907
|
+
);
|
|
80908
|
+
}
|
|
80909
|
+
var init_catalog_fleet_read = __esm({
|
|
80910
|
+
"packages/daemon/src/lib/catalog-fleet-read.ts"() {
|
|
80911
|
+
"use strict";
|
|
80912
|
+
init_discovery();
|
|
80913
|
+
}
|
|
80914
|
+
});
|
|
80915
|
+
|
|
80690
80916
|
// packages/daemon/src/lib/workspace-open.ts
|
|
80691
80917
|
import { createHash as createHash25 } from "node:crypto";
|
|
80692
80918
|
import { realpathSync as realpathSync19, statSync as statSync17 } from "node:fs";
|
|
@@ -84873,7 +85099,7 @@ function createSessionFaviconResolver(options = {}) {
|
|
|
84873
85099
|
const missTtlMs = options.missTtlMs ?? 3e4;
|
|
84874
85100
|
const maxEntries = options.maxEntries ?? 256;
|
|
84875
85101
|
const cache3 = /* @__PURE__ */ new Map();
|
|
84876
|
-
const
|
|
85102
|
+
const remember2 = (sessionPath, entry) => {
|
|
84877
85103
|
cache3.delete(sessionPath);
|
|
84878
85104
|
cache3.set(sessionPath, entry);
|
|
84879
85105
|
while (cache3.size > maxEntries) cache3.delete(cache3.keys().next().value);
|
|
@@ -84918,18 +85144,18 @@ function createSessionFaviconResolver(options = {}) {
|
|
|
84918
85144
|
if (!sessionPath || !isAbsolute24(sessionPath) || sessionPath.includes("\0")) return null;
|
|
84919
85145
|
const cached2 = cache3.get(sessionPath);
|
|
84920
85146
|
if (cached2?.kind === "miss" && now() - cached2.at < missTtlMs) {
|
|
84921
|
-
|
|
85147
|
+
remember2(sessionPath, cached2);
|
|
84922
85148
|
return null;
|
|
84923
85149
|
}
|
|
84924
85150
|
if (cached2?.kind === "hit") {
|
|
84925
85151
|
const stats = await lstatOrNull(io, cached2.file);
|
|
84926
85152
|
if (stats && stats.isFile() && stats.mtimeMs === cached2.mtimeMs && stats.size === cached2.size) {
|
|
84927
|
-
|
|
85153
|
+
remember2(sessionPath, cached2);
|
|
84928
85154
|
return cached2.favicon;
|
|
84929
85155
|
}
|
|
84930
85156
|
}
|
|
84931
85157
|
const entry = await scan(sessionPath);
|
|
84932
|
-
|
|
85158
|
+
remember2(sessionPath, entry);
|
|
84933
85159
|
return entry.kind === "hit" ? entry.favicon : null;
|
|
84934
85160
|
};
|
|
84935
85161
|
}
|
|
@@ -96230,15 +96456,21 @@ async function startEmbeddedDaemonGeneration(opts) {
|
|
|
96230
96456
|
paneStreamRuntime,
|
|
96231
96457
|
catalogLiveSessions: () => discoverLiveSessionSummaries(catalogTmuxRunner),
|
|
96232
96458
|
catalogFleet: async () => {
|
|
96233
|
-
const
|
|
96459
|
+
const endpoint = (sessionName2, pane) => pane.semanticPaneId ? interactionEvidence?.captureInventoryEndpoint(
|
|
96460
|
+
sessionName2,
|
|
96461
|
+
pane.runtimePaneId,
|
|
96462
|
+
pane.semanticPaneId
|
|
96463
|
+
) ?? null : null;
|
|
96464
|
+
const epoch = () => observationSelector?.nativeServerEpoch ?? null;
|
|
96465
|
+
const fleet = await readCatalogFleet(
|
|
96234
96466
|
workspaceRegistry,
|
|
96235
|
-
|
|
96236
|
-
|
|
96237
|
-
|
|
96238
|
-
|
|
96239
|
-
|
|
96240
|
-
|
|
96241
|
-
|
|
96467
|
+
{
|
|
96468
|
+
fence: sessionMutationFence,
|
|
96469
|
+
fencedSync: nativeGenerationTmuxRunner,
|
|
96470
|
+
generationFencedAsync: (args) => fleetFactsTmuxRunner(args)
|
|
96471
|
+
},
|
|
96472
|
+
endpoint,
|
|
96473
|
+
epoch
|
|
96242
96474
|
);
|
|
96243
96475
|
if (!fleet) return null;
|
|
96244
96476
|
const names = await readTeamMemberships(
|
|
@@ -96690,6 +96922,7 @@ var init_daemon_embed = __esm({
|
|
|
96690
96922
|
init_workspace_registry();
|
|
96691
96923
|
init_workspace_pane_creation2();
|
|
96692
96924
|
init_discovery();
|
|
96925
|
+
init_catalog_fleet_read();
|
|
96693
96926
|
init_workspace_open2();
|
|
96694
96927
|
init_workspace_promotion2();
|
|
96695
96928
|
init_workspace_open_handoff2();
|
|
@@ -98009,13 +98242,13 @@ async function manageDaemonService(action, executable, deps2 = defaults()) {
|
|
|
98009
98242
|
refused("The service manager loaded a different definition; refusing to control it");
|
|
98010
98243
|
return state;
|
|
98011
98244
|
};
|
|
98012
|
-
const ready = async (previousInstance) => {
|
|
98245
|
+
const ready = async (previousInstance, retiredPid) => {
|
|
98013
98246
|
const deadline = Date.now() + deps2.waitMs;
|
|
98014
98247
|
do {
|
|
98015
98248
|
const state = deps2.inspect();
|
|
98016
98249
|
if (!boundTo(state, plan.supervisionId))
|
|
98017
98250
|
refused("Service reservation changed during startup");
|
|
98018
|
-
if (state.status === "valid" && state.info.instanceId !== previousInstance) {
|
|
98251
|
+
if (state.status === "valid" && state.info.instanceId !== previousInstance && state.info.pid !== retiredPid) {
|
|
98019
98252
|
const identity4 = await deps2.probe(state.info);
|
|
98020
98253
|
const service2 = await inspectManager();
|
|
98021
98254
|
if (identity4 && service2.active && service2.pid === state.info.pid && identity4.pid === state.info.pid && identity4.instanceId === state.info.instanceId && identity4.startedAt === state.info.startedAt && identity4.protocolVersion === state.info.protocolVersion)
|
|
@@ -98116,9 +98349,13 @@ async function manageDaemonService(action, executable, deps2 = defaults()) {
|
|
|
98116
98349
|
}
|
|
98117
98350
|
const before = deps2.inspect();
|
|
98118
98351
|
if (action === "install" && service.active) return await ready();
|
|
98119
|
-
|
|
98352
|
+
const current = service.loaded ? await inspectManager() : service;
|
|
98353
|
+
if (current.loaded) await manager.restart(current.pid !== null);
|
|
98120
98354
|
else await manager.install();
|
|
98121
|
-
return await ready(
|
|
98355
|
+
return await ready(
|
|
98356
|
+
before.status === "valid" ? before.info.instanceId : void 0,
|
|
98357
|
+
current.loaded ? current.pid : null
|
|
98358
|
+
);
|
|
98122
98359
|
} catch (error) {
|
|
98123
98360
|
if (!activationAttempted && wroteRecord) {
|
|
98124
98361
|
if (wroteUnit && ownedFile(plan.unitPath) === contents) unlinkSync9(plan.unitPath);
|
package/package.json
CHANGED
|
@@ -10,7 +10,7 @@ export interface BufferedFnv64 {
|
|
|
10
10
|
digest(): string;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
interface NativeHasher {
|
|
13
|
+
export interface NativeHasher {
|
|
14
14
|
readonly scratch: Uint8Array;
|
|
15
15
|
readonly update: (state: bigint, length: number) => bigint;
|
|
16
16
|
}
|
|
@@ -49,6 +49,13 @@ function initialize(): NativeHasher | null {
|
|
|
49
49
|
return native;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
/** Direct access to the WASM scratch buffer and its update, for one synchronous
|
|
53
|
+
* hash at a time. Callers must not yield between filling the scratch and update. */
|
|
54
|
+
export function nativeFnv64(): NativeHasher | null {
|
|
55
|
+
return initialize();
|
|
56
|
+
}
|
|
57
|
+
export const FNV64_OFFSET_BASIS = OFFSET_BASIS;
|
|
58
|
+
|
|
52
59
|
/** Internal optional accelerator. Canonical serialization remains the caller's responsibility. */
|
|
53
60
|
export function createBufferedFnv64(initialState = OFFSET_BASIS): BufferedFnv64 | null {
|
|
54
61
|
const hasher = initialize();
|