very-happy-cli 0.2.120 → 0.2.122
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/{AcpBackend-tou8FQXI.cjs → AcpBackend-YPVwACkW.cjs} +1 -1
- package/dist/{AcpBackend-DiIJaJWG.mjs → AcpBackend-pgDnK76l.mjs} +1 -1
- package/dist/{AcpSessionManager-fF_HFB98.cjs → AcpSessionManager-6rypahwH.cjs} +51 -11
- package/dist/{AcpSessionManager-CZ0Az0GG.mjs → AcpSessionManager-Bq5Tiky1.mjs} +51 -11
- package/dist/{config-CGDxsTZg.mjs → config-BTEW041n.mjs} +2 -2
- package/dist/{config-CEbOao_0.cjs → config-rQdv_Yr0.cjs} +2 -2
- package/dist/{index-CYizH-JC.cjs → index-BHg0P9cY.cjs} +2 -2
- package/dist/{index-CUJ6faw1.mjs → index-BTnlYGaP.mjs} +139 -41
- package/dist/{index-D4_LW69g.mjs → index-BeHr5uMY.mjs} +25 -11
- package/dist/{index-DKj_0dqB.mjs → index-CaIsj1q7.mjs} +2 -2
- package/dist/{index-BlkIz9BQ.cjs → index-DKRU6E-x.cjs} +143 -44
- package/dist/{index-HKcuQ_fQ.cjs → index-uPpFZhhr.cjs} +24 -10
- package/dist/index.cjs +2 -2
- package/dist/index.mjs +2 -2
- package/dist/{installTerminalHooks-MWVPJDxV.cjs → installTerminalHooks-CGqopr1A.cjs} +2 -2
- package/dist/{installTerminalHooks-B3EGNaJr.mjs → installTerminalHooks-DFmKhCto.mjs} +2 -2
- package/dist/lib.cjs +1 -1
- package/dist/lib.d.cts +25 -1
- package/dist/lib.d.mts +25 -1
- package/dist/lib.mjs +1 -1
- package/dist/{mcp-CZ1Z6AI5.mjs → mcp-DnKBKXYa.mjs} +2 -2
- package/dist/{mcp-DBKk5L6_.cjs → mcp-FlbfMw6_.cjs} +2 -2
- package/dist/{runGemini-C3BCKTMp.mjs → runGemini-BsfadDSa.mjs} +9 -5
- package/dist/{runGemini-BRUmjZgV.cjs → runGemini-DYawZQTu.cjs} +9 -5
- package/dist/{runOpenClaw-o1ImBlcJ.cjs → runOpenClaw-C2XjFP2m.cjs} +3 -3
- package/dist/{runOpenClaw-DEYzcGSV.mjs → runOpenClaw-xGtbJ7f8.mjs} +3 -3
- package/dist/{send-B9fh96vR.cjs → send-B0Wojz18.cjs} +2 -2
- package/dist/{send-DFGH_-s_.mjs → send-DRZppEr2.mjs} +2 -2
- package/dist/{sessions-CKVfXHyi.mjs → sessions-BuMgxuxd.mjs} +11 -4
- package/dist/{sessions-BNyEJ2_6.cjs → sessions-CTuUV0TW.cjs} +11 -4
- package/dist/{spawn-xPV7-s4S.cjs → spawn-7U_4QTHw.cjs} +2 -2
- package/dist/{spawn-B6kP4YjA.mjs → spawn-BRdfNDsu.mjs} +2 -2
- package/dist/{types-7ZSxm6Pi.cjs → types-Jag0GwhO.cjs} +209 -41
- package/dist/{types-Di0o0BdV.mjs → types-q7wtjfxb.mjs} +208 -41
- package/package.json +7 -7
|
@@ -51,7 +51,7 @@ var z__namespace = /*#__PURE__*/_interopNamespaceDefault(z);
|
|
|
51
51
|
var pty__namespace = /*#__PURE__*/_interopNamespaceDefault(pty);
|
|
52
52
|
|
|
53
53
|
var name = "very-happy-cli";
|
|
54
|
-
var version = "0.2.
|
|
54
|
+
var version = "0.2.122";
|
|
55
55
|
var description = "Remote multi-agent workspace CLI with deep Claude integration, native terminal streaming, and self-hosting.";
|
|
56
56
|
var author = "Kirill Dubovitskiy";
|
|
57
57
|
var contributors = [
|
|
@@ -164,12 +164,12 @@ var dependencies = {
|
|
|
164
164
|
zod: "^4.0.0"
|
|
165
165
|
};
|
|
166
166
|
var optionalDependencies = {
|
|
167
|
-
"very-happy-tools-arm64-darwin": "0.2.
|
|
168
|
-
"very-happy-tools-x64-darwin": "0.2.
|
|
169
|
-
"very-happy-tools-arm64-linux": "0.2.
|
|
170
|
-
"very-happy-tools-x64-linux": "0.2.
|
|
171
|
-
"very-happy-tools-arm64-win32": "0.2.
|
|
172
|
-
"very-happy-tools-x64-win32": "0.2.
|
|
167
|
+
"very-happy-tools-arm64-darwin": "0.2.122",
|
|
168
|
+
"very-happy-tools-x64-darwin": "0.2.122",
|
|
169
|
+
"very-happy-tools-arm64-linux": "0.2.122",
|
|
170
|
+
"very-happy-tools-x64-linux": "0.2.122",
|
|
171
|
+
"very-happy-tools-arm64-win32": "0.2.122",
|
|
172
|
+
"very-happy-tools-x64-win32": "0.2.122"
|
|
173
173
|
};
|
|
174
174
|
var devDependencies = {
|
|
175
175
|
"@slopus/happy-wire": "workspace:*",
|
|
@@ -742,6 +742,11 @@ const sessionProgressEventSchema = z__namespace.object({
|
|
|
742
742
|
durationMs: z__namespace.number().optional(),
|
|
743
743
|
summary: z__namespace.string().optional()
|
|
744
744
|
});
|
|
745
|
+
const sessionQueueCancelEventSchema = z__namespace.object({
|
|
746
|
+
t: z__namespace.literal("queue-cancel"),
|
|
747
|
+
targetLocalKeys: z__namespace.array(z__namespace.string().min(1)).min(1),
|
|
748
|
+
reason: z__namespace.string().min(1).optional()
|
|
749
|
+
});
|
|
745
750
|
const sessionEventSchema = z__namespace.discriminatedUnion("t", [
|
|
746
751
|
sessionTextEventSchema,
|
|
747
752
|
sessionServiceMessageEventSchema,
|
|
@@ -752,7 +757,8 @@ const sessionEventSchema = z__namespace.discriminatedUnion("t", [
|
|
|
752
757
|
sessionStartEventSchema,
|
|
753
758
|
sessionTurnEndEventSchema,
|
|
754
759
|
sessionStopEventSchema,
|
|
755
|
-
sessionProgressEventSchema
|
|
760
|
+
sessionProgressEventSchema,
|
|
761
|
+
sessionQueueCancelEventSchema
|
|
756
762
|
]);
|
|
757
763
|
const sessionEnvelopeSchema = z__namespace.object({
|
|
758
764
|
id: z__namespace.string(),
|
|
@@ -1026,6 +1032,20 @@ const ReleaseDrainNoticeSchema = z.z.object({
|
|
|
1026
1032
|
deadline: z.z.number().int().positive(),
|
|
1027
1033
|
mode: z.z.literal("make-before-break")
|
|
1028
1034
|
});
|
|
1035
|
+
const ConnectionDiagnosticEventSchema = z.z.object({
|
|
1036
|
+
attemptId: z.z.string().uuid(),
|
|
1037
|
+
machineId: z.z.string().min(1).max(256).regex(/^[A-Za-z0-9_-]+$/).optional(),
|
|
1038
|
+
at: z.z.number().int().min(0).max(Number.MAX_SAFE_INTEGER),
|
|
1039
|
+
stage: z.z.enum(["control", "relay_discovery", "relay_connect", "relay_probe", "machine_rpc", "terminal_open"]),
|
|
1040
|
+
outcome: z.z.enum(["started", "success", "timeout", "error", "offline", "cancelled", "fallback"]),
|
|
1041
|
+
durationMs: z.z.number().int().min(0).max(3e5),
|
|
1042
|
+
deviceClass: z.z.enum(["mobile", "desktop", "unknown"]),
|
|
1043
|
+
visibility: z.z.enum(["visible", "hidden"]),
|
|
1044
|
+
client: z.z.string().regex(/^web\/(?:[a-f0-9]{7,40}|unknown)$/)
|
|
1045
|
+
}).strict();
|
|
1046
|
+
z.z.object({
|
|
1047
|
+
events: z.z.array(ConnectionDiagnosticEventSchema).min(1).max(32)
|
|
1048
|
+
}).strict();
|
|
1029
1049
|
|
|
1030
1050
|
z.z.object({
|
|
1031
1051
|
host: z.z.string(),
|
|
@@ -1685,7 +1705,7 @@ class RpcHandlerManager {
|
|
|
1685
1705
|
}
|
|
1686
1706
|
}
|
|
1687
1707
|
|
|
1688
|
-
const __dirname$1 = path$1.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('types-
|
|
1708
|
+
const __dirname$1 = path$1.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('types-Jag0GwhO.cjs', document.baseURI).href))));
|
|
1689
1709
|
function projectPath() {
|
|
1690
1710
|
const path = path$1.resolve(__dirname$1, "..");
|
|
1691
1711
|
return path;
|
|
@@ -3135,6 +3155,48 @@ class InvalidateSync {
|
|
|
3135
3155
|
};
|
|
3136
3156
|
}
|
|
3137
3157
|
|
|
3158
|
+
function sessionEnvelopeOf(body) {
|
|
3159
|
+
if (!body || typeof body !== "object") return null;
|
|
3160
|
+
const record = body;
|
|
3161
|
+
if (record.role !== "session" || !record.content || typeof record.content !== "object") return null;
|
|
3162
|
+
const content = record.content;
|
|
3163
|
+
if (content.type === "session" && content.data && typeof content.data === "object") {
|
|
3164
|
+
return content.data;
|
|
3165
|
+
}
|
|
3166
|
+
if (content.ev !== void 0) return content;
|
|
3167
|
+
return null;
|
|
3168
|
+
}
|
|
3169
|
+
function isQueuedUserText(body) {
|
|
3170
|
+
if (!body || typeof body !== "object") return false;
|
|
3171
|
+
const record = body;
|
|
3172
|
+
return record.role === "user" && record.content?.type === "text" && typeof record.meta?.queuedAt === "number";
|
|
3173
|
+
}
|
|
3174
|
+
function scanUndeliveredQueuedInputs(records, prior) {
|
|
3175
|
+
const canceled = prior?.canceled ?? /* @__PURE__ */ new Set();
|
|
3176
|
+
const undelivered = [];
|
|
3177
|
+
let reachedBoundary = false;
|
|
3178
|
+
for (const record of records) {
|
|
3179
|
+
const envelope = sessionEnvelopeOf(record.body);
|
|
3180
|
+
if (envelope) {
|
|
3181
|
+
const t = envelope.ev?.t;
|
|
3182
|
+
if (t === "turn-end") {
|
|
3183
|
+
reachedBoundary = true;
|
|
3184
|
+
break;
|
|
3185
|
+
}
|
|
3186
|
+
if (t === "queue-cancel" && Array.isArray(envelope.ev?.targetLocalKeys)) {
|
|
3187
|
+
for (const key of envelope.ev.targetLocalKeys) {
|
|
3188
|
+
if (typeof key === "string") canceled.add(key);
|
|
3189
|
+
}
|
|
3190
|
+
}
|
|
3191
|
+
continue;
|
|
3192
|
+
}
|
|
3193
|
+
if (isQueuedUserText(record.body) && record.localId && !canceled.has(record.localId)) {
|
|
3194
|
+
undelivered.push(record.localId);
|
|
3195
|
+
}
|
|
3196
|
+
}
|
|
3197
|
+
return { undeliveredLocalKeys: undelivered, reachedBoundary, canceled };
|
|
3198
|
+
}
|
|
3199
|
+
|
|
3138
3200
|
function record(value) {
|
|
3139
3201
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
3140
3202
|
}
|
|
@@ -3778,8 +3840,75 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
3778
3840
|
this.sendSync.invalidate();
|
|
3779
3841
|
}
|
|
3780
3842
|
}
|
|
3843
|
+
/** B-332: emit a queue-cancel tombstone, mirroring the web's own signal. */
|
|
3844
|
+
sendQueueCancelReason(localKeys, reason) {
|
|
3845
|
+
if (localKeys.length === 0) return;
|
|
3846
|
+
const envelope = createEnvelope("user", { t: "queue-cancel", targetLocalKeys: localKeys, reason });
|
|
3847
|
+
this.sendSessionProtocolMessage(envelope);
|
|
3848
|
+
}
|
|
3781
3849
|
/**
|
|
3782
|
-
*
|
|
3850
|
+
* B-332 site ③: find queued user input this wrapper destroyed (it was still
|
|
3851
|
+
* in the previous wrapper's in-memory queue when it died) and tell the web.
|
|
3852
|
+
*
|
|
3853
|
+
* Walks NEWEST → OLDEST via `before_seq`, decrypting each page, and stops at
|
|
3854
|
+
* the first turn-end. The runner calls this on EVERY reconnect (runClaude /
|
|
3855
|
+
* runCodex), seeded or not: a cursor seeded at the server's latest seq skips
|
|
3856
|
+
* the undelivered tail just as thoroughly as skipExistingMessages does —
|
|
3857
|
+
* and the daemon's resume/restart always seeds, so gating on the skip path
|
|
3858
|
+
* would miss the common case. Best-effort: a failed scan must not take a
|
|
3859
|
+
* session down. Needs no socket; POSTs through the same session-message
|
|
3860
|
+
* channel so the web receives it identically to a cancel-button tombstone.
|
|
3861
|
+
*/
|
|
3862
|
+
async cancelUndeliveredQueuedInputs() {
|
|
3863
|
+
const MAX_SCAN_PAGES = 10;
|
|
3864
|
+
const canceled = /* @__PURE__ */ new Set();
|
|
3865
|
+
const undelivered = [];
|
|
3866
|
+
let beforeSeq = 2147483647;
|
|
3867
|
+
let reachedBoundary = false;
|
|
3868
|
+
let pages = 0;
|
|
3869
|
+
try {
|
|
3870
|
+
while (!reachedBoundary && pages < MAX_SCAN_PAGES) {
|
|
3871
|
+
pages += 1;
|
|
3872
|
+
const response = await axios.get(
|
|
3873
|
+
`${configuration.serverUrl}/v3/sessions/${encodeURIComponent(this.sessionId)}/messages`,
|
|
3874
|
+
{
|
|
3875
|
+
params: { before_seq: beforeSeq, limit: 100 },
|
|
3876
|
+
headers: this.authHeaders(),
|
|
3877
|
+
timeout: 6e4
|
|
3878
|
+
}
|
|
3879
|
+
);
|
|
3880
|
+
const messages = Array.isArray(response.data.messages) ? response.data.messages : [];
|
|
3881
|
+
if (messages.length === 0) break;
|
|
3882
|
+
const records = messages.map((message) => {
|
|
3883
|
+
let body = null;
|
|
3884
|
+
try {
|
|
3885
|
+
body = message.content?.t === "encrypted" ? decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(message.content.c)) : null;
|
|
3886
|
+
} catch (error) {
|
|
3887
|
+
logger.debug("[API] Failed to decrypt during undelivered scan:", error);
|
|
3888
|
+
}
|
|
3889
|
+
return { seq: message.seq, localId: message.localId, body };
|
|
3890
|
+
});
|
|
3891
|
+
const scan = scanUndeliveredQueuedInputs(records, { canceled });
|
|
3892
|
+
for (const key of scan.canceled) canceled.add(key);
|
|
3893
|
+
undelivered.push(...scan.undeliveredLocalKeys);
|
|
3894
|
+
reachedBoundary = scan.reachedBoundary;
|
|
3895
|
+
const oldest = records.at(-1)?.seq;
|
|
3896
|
+
if (oldest === void 0 || messages.length < 100) break;
|
|
3897
|
+
beforeSeq = oldest;
|
|
3898
|
+
}
|
|
3899
|
+
} catch (error) {
|
|
3900
|
+
logger.debug("[API] cancelUndeliveredQueuedInputs scan failed:", error);
|
|
3901
|
+
return;
|
|
3902
|
+
}
|
|
3903
|
+
if (!reachedBoundary) {
|
|
3904
|
+
logger.debug("[API] cancelUndeliveredQueuedInputs found no turn boundary; leaving messages as-is");
|
|
3905
|
+
return;
|
|
3906
|
+
}
|
|
3907
|
+
undelivered.reverse();
|
|
3908
|
+
this.sendQueueCancelReason(undelivered, "restarted");
|
|
3909
|
+
}
|
|
3910
|
+
/**
|
|
3911
|
+
* Send a message to session
|
|
3783
3912
|
* @param body - Message body (can be MessageContent or raw content for agent messages)
|
|
3784
3913
|
*/
|
|
3785
3914
|
sendClaudeSessionMessage(body, opts) {
|
|
@@ -14391,28 +14520,50 @@ function selectStableRelay(candidates, probes, connectedRelayId, tracker = null)
|
|
|
14391
14520
|
tracker: { challengerRelayId: fastest.id, consecutiveWins }
|
|
14392
14521
|
};
|
|
14393
14522
|
}
|
|
14523
|
+
const RELAY_REFRESH_TIMEOUT_MS = 8e3;
|
|
14524
|
+
async function withinDeadline(timeoutMs, operation, parent) {
|
|
14525
|
+
const controller = new AbortController();
|
|
14526
|
+
let timer;
|
|
14527
|
+
let cancel;
|
|
14528
|
+
const deadline = new Promise((_, reject) => {
|
|
14529
|
+
cancel = () => {
|
|
14530
|
+
reject(new Error("relay request deadline exceeded"));
|
|
14531
|
+
controller.abort();
|
|
14532
|
+
};
|
|
14533
|
+
timer = setTimeout(cancel, timeoutMs);
|
|
14534
|
+
parent?.addEventListener("abort", cancel, { once: true });
|
|
14535
|
+
if (parent?.aborted) cancel();
|
|
14536
|
+
});
|
|
14537
|
+
try {
|
|
14538
|
+
return await Promise.race([Promise.resolve().then(() => {
|
|
14539
|
+
controller.signal.throwIfAborted();
|
|
14540
|
+
return operation(controller.signal);
|
|
14541
|
+
}), deadline]);
|
|
14542
|
+
} finally {
|
|
14543
|
+
clearTimeout(timer);
|
|
14544
|
+
parent?.removeEventListener("abort", cancel);
|
|
14545
|
+
}
|
|
14546
|
+
}
|
|
14394
14547
|
async function probeRelayCandidates(candidates, options = {}) {
|
|
14395
14548
|
const timeoutMs = options.timeoutMs ?? 2e3;
|
|
14396
14549
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
14397
14550
|
const now = options.now ?? (() => performance.now());
|
|
14398
14551
|
const measured = await Promise.all(candidates.map(async (candidate) => {
|
|
14399
|
-
const controller = new AbortController();
|
|
14400
|
-
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
14401
14552
|
const startedAt = now();
|
|
14402
14553
|
try {
|
|
14403
|
-
|
|
14404
|
-
|
|
14405
|
-
|
|
14406
|
-
|
|
14407
|
-
|
|
14408
|
-
|
|
14409
|
-
|
|
14410
|
-
|
|
14411
|
-
|
|
14554
|
+
return await withinDeadline(timeoutMs, async (signal) => {
|
|
14555
|
+
const response = await fetchImpl(`${candidate.url}/health`, {
|
|
14556
|
+
method: "GET",
|
|
14557
|
+
cache: "no-store",
|
|
14558
|
+
signal
|
|
14559
|
+
});
|
|
14560
|
+
if (!response.ok) return null;
|
|
14561
|
+
const body = await response.json();
|
|
14562
|
+
if (body.ok !== true || body.relayId !== candidate.id) return null;
|
|
14563
|
+
return { relayId: candidate.id, rttMs: Math.max(0, now() - startedAt) };
|
|
14564
|
+
}, options.signal);
|
|
14412
14565
|
} catch {
|
|
14413
14566
|
return null;
|
|
14414
|
-
} finally {
|
|
14415
|
-
clearTimeout(timeout);
|
|
14416
14567
|
}
|
|
14417
14568
|
}));
|
|
14418
14569
|
return measured.filter((probe) => probe !== null);
|
|
@@ -14420,25 +14571,32 @@ async function probeRelayCandidates(candidates, options = {}) {
|
|
|
14420
14571
|
async function discoverAndClaimRelay(input) {
|
|
14421
14572
|
const fetchImpl = input.fetchImpl ?? fetch;
|
|
14422
14573
|
try {
|
|
14423
|
-
|
|
14424
|
-
|
|
14425
|
-
|
|
14426
|
-
|
|
14427
|
-
|
|
14428
|
-
|
|
14429
|
-
|
|
14430
|
-
|
|
14431
|
-
|
|
14432
|
-
|
|
14433
|
-
|
|
14434
|
-
|
|
14435
|
-
|
|
14436
|
-
|
|
14437
|
-
|
|
14574
|
+
return await withinDeadline(RELAY_REFRESH_TIMEOUT_MS, async (signal) => {
|
|
14575
|
+
const discoveryResponse = await fetchImpl(`${input.controlUrl}/v1/relays`, {
|
|
14576
|
+
headers: { Authorization: `Bearer ${input.token}` },
|
|
14577
|
+
cache: "no-store",
|
|
14578
|
+
signal
|
|
14579
|
+
});
|
|
14580
|
+
if (!discoveryResponse.ok) return null;
|
|
14581
|
+
const discovery = RelayCandidatesResponseSchema.parse(await discoveryResponse.json());
|
|
14582
|
+
signal.throwIfAborted();
|
|
14583
|
+
if (!discovery.enabled || discovery.candidates.length === 0) return null;
|
|
14584
|
+
const probes = await probeRelayCandidates(discovery.candidates, { fetchImpl, signal });
|
|
14585
|
+
signal.throwIfAborted();
|
|
14586
|
+
const decision = selectStableRelay(discovery.candidates, probes, input.connectedRelayId, input.switchTracker);
|
|
14587
|
+
const selected = decision.candidate;
|
|
14588
|
+
if (!selected) return null;
|
|
14589
|
+
const claimResponse = await fetchImpl(`${input.controlUrl}/v1/relays/machines/${encodeURIComponent(input.machineId)}/claim`, {
|
|
14590
|
+
method: "POST",
|
|
14591
|
+
signal,
|
|
14592
|
+
headers: { Authorization: `Bearer ${input.token}`, "Content-Type": "application/json" },
|
|
14593
|
+
body: JSON.stringify({ relayId: selected.id, probes })
|
|
14594
|
+
});
|
|
14595
|
+
if (!claimResponse.ok) return null;
|
|
14596
|
+
const claimed = RelayAssignmentResponseSchema.parse(await claimResponse.json());
|
|
14597
|
+
signal.throwIfAborted();
|
|
14598
|
+
return claimed.assignment ? { assignment: claimed.assignment, probes, switchTracker: decision.tracker } : null;
|
|
14438
14599
|
});
|
|
14439
|
-
if (!claimResponse.ok) return null;
|
|
14440
|
-
const claimed = RelayAssignmentResponseSchema.parse(await claimResponse.json());
|
|
14441
|
-
return claimed.assignment ? { assignment: claimed.assignment, probes, switchTracker: decision.tracker } : null;
|
|
14442
14600
|
} catch {
|
|
14443
14601
|
return null;
|
|
14444
14602
|
}
|
|
@@ -14552,6 +14710,7 @@ async function withCodexAppServerClient(handler) {
|
|
|
14552
14710
|
await client.disconnect();
|
|
14553
14711
|
}
|
|
14554
14712
|
}
|
|
14713
|
+
const CLI_AVAILABILITY_RECHECK_MS = 5 * 60 * 1e3;
|
|
14555
14714
|
class ApiMachineClient {
|
|
14556
14715
|
constructor(token, machine) {
|
|
14557
14716
|
this.token = token;
|
|
@@ -14575,6 +14734,10 @@ class ApiMachineClient {
|
|
|
14575
14734
|
keepAliveInterval = null;
|
|
14576
14735
|
lastKnownCLIAvailability = null;
|
|
14577
14736
|
lastKnownResumeSupport = null;
|
|
14737
|
+
/** Wall-clock of the last CLI availability probe. Initialised as "already
|
|
14738
|
+
* stale" so the FIRST keep-alive tick always probes and populates the
|
|
14739
|
+
* machine metadata that a connecting/reconnecting web client reads. */
|
|
14740
|
+
lastCliProbeAt = Date.now() - CLI_AVAILABILITY_RECHECK_MS;
|
|
14578
14741
|
cliUpdateState = null;
|
|
14579
14742
|
cliUpdatePushChain = Promise.resolve();
|
|
14580
14743
|
claudeAuthState = null;
|
|
@@ -15442,6 +15605,10 @@ class ApiMachineClient {
|
|
|
15442
15605
|
}
|
|
15443
15606
|
this.socket.emit("machine-alive", payload);
|
|
15444
15607
|
void this.refreshRelayConnection();
|
|
15608
|
+
if (Date.now() - this.lastCliProbeAt < CLI_AVAILABILITY_RECHECK_MS) {
|
|
15609
|
+
return;
|
|
15610
|
+
}
|
|
15611
|
+
this.lastCliProbeAt = Date.now();
|
|
15445
15612
|
const newAvailability = detectCLIAvailability();
|
|
15446
15613
|
const prev = this.lastKnownCLIAvailability;
|
|
15447
15614
|
const newResumeSupport = detectResumeSupport();
|
|
@@ -16745,6 +16912,7 @@ exports.scrubTmuxClientEnv = scrubTmuxClientEnv;
|
|
|
16745
16912
|
exports.sendToVhTerminal = sendToVhTerminal;
|
|
16746
16913
|
exports.shellescape = shellescape;
|
|
16747
16914
|
exports.startOfflineReconnection = startOfflineReconnection;
|
|
16915
|
+
exports.streamKeyOf = streamKeyOf;
|
|
16748
16916
|
exports.summarizeSpawnSessionForLog = summarizeSpawnSessionForLog;
|
|
16749
16917
|
exports.tmuxArgs = tmuxArgs;
|
|
16750
16918
|
exports.updateSettings = updateSettings;
|