@songsid/agend 2.1.4 → 2.1.5-beta.10
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/backend/claude-code.js +2 -2
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/types.d.ts +22 -0
- package/dist/backend/types.js +23 -1
- package/dist/backend/types.js.map +1 -1
- package/dist/channel/adapters/discord.d.ts +2 -0
- package/dist/channel/adapters/discord.js +25 -3
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/adapters/telegram.d.ts +5 -0
- package/dist/channel/adapters/telegram.js +11 -0
- package/dist/channel/adapters/telegram.js.map +1 -1
- package/dist/channel/types.d.ts +7 -0
- package/dist/cli.js +37 -37
- package/dist/cli.js.map +1 -1
- package/dist/config.js +38 -0
- package/dist/config.js.map +1 -1
- package/dist/daemon.d.ts +38 -0
- package/dist/daemon.js +95 -43
- package/dist/daemon.js.map +1 -1
- package/dist/fleet-context.d.ts +5 -0
- package/dist/fleet-manager.d.ts +126 -0
- package/dist/fleet-manager.js +688 -95
- package/dist/fleet-manager.js.map +1 -1
- package/dist/full-restart.d.ts +20 -0
- package/dist/full-restart.js +39 -0
- package/dist/full-restart.js.map +1 -0
- package/dist/locale.js +72 -2
- package/dist/locale.js.map +1 -1
- package/dist/login-controller.d.ts +127 -0
- package/dist/login-controller.js +450 -0
- package/dist/login-controller.js.map +1 -0
- package/dist/login-flows.d.ts +41 -3
- package/dist/login-flows.js +37 -5
- package/dist/login-flows.js.map +1 -1
- package/dist/login-manager.d.ts +12 -1
- package/dist/login-manager.js +64 -7
- package/dist/login-manager.js.map +1 -1
- package/dist/login-window-lock.d.ts +26 -0
- package/dist/login-window-lock.js +64 -0
- package/dist/login-window-lock.js.map +1 -0
- package/dist/restart-progress.d.ts +35 -5
- package/dist/restart-progress.js +195 -34
- package/dist/restart-progress.js.map +1 -1
- package/dist/service-restart-selection.d.ts +27 -0
- package/dist/service-restart-selection.js +25 -0
- package/dist/service-restart-selection.js.map +1 -0
- package/dist/tmux-manager.d.ts +19 -0
- package/dist/tmux-manager.js +79 -12
- package/dist/tmux-manager.js.map +1 -1
- package/dist/topic-commands.js +23 -7
- package/dist/topic-commands.js.map +1 -1
- package/dist/types.d.ts +18 -0
- package/dist/ui/web-terminal/terminal.css +17 -0
- package/dist/ui/web-terminal/terminal.html +44 -0
- package/dist/ui/web-terminal/terminal.js +148 -0
- package/dist/ui/web-terminal/vendor/LICENSE.addon-fit +19 -0
- package/dist/ui/web-terminal/vendor/LICENSE.addon-web-links +19 -0
- package/dist/ui/web-terminal/vendor/LICENSE.xterm +21 -0
- package/dist/ui/web-terminal/vendor/VENDORED.md +22 -0
- package/dist/ui/web-terminal/vendor/addon-fit.js +1 -0
- package/dist/ui/web-terminal/vendor/addon-web-links.js +1 -0
- package/dist/ui/web-terminal/vendor/xterm.css +285 -0
- package/dist/ui/web-terminal/vendor/xterm.js +1 -0
- package/dist/update-marker.d.ts +11 -1
- package/dist/update-marker.js +19 -4
- package/dist/update-marker.js.map +1 -1
- package/dist/update-progress.d.ts +1 -1
- package/dist/update-progress.js +12 -0
- package/dist/update-progress.js.map +1 -1
- package/dist/usage/providers.d.ts +39 -0
- package/dist/usage/providers.js +69 -3
- package/dist/usage/providers.js.map +1 -1
- package/dist/web-terminal-http.d.ts +61 -0
- package/dist/web-terminal-http.js +422 -0
- package/dist/web-terminal-http.js.map +1 -0
- package/dist/web-terminal.d.ts +336 -0
- package/dist/web-terminal.js +888 -0
- package/dist/web-terminal.js.map +1 -0
- package/dist/ws-server.d.ts +69 -0
- package/dist/ws-server.js +392 -0
- package/dist/ws-server.js.map +1 -0
- package/package.json +1 -1
package/dist/restart-progress.js
CHANGED
|
@@ -1,11 +1,31 @@
|
|
|
1
1
|
import { t } from "./locale.js";
|
|
2
2
|
import { updateElapsedSeconds } from "./update-progress.js";
|
|
3
|
+
const TERMINAL_DELIVERY_RETRY_MS = 1_000;
|
|
4
|
+
export const RESTART_PROGRESS_TERMINAL_TIMEOUT_MS = 30_000;
|
|
3
5
|
function formatElapsed(ms) {
|
|
4
6
|
const seconds = Math.max(0, Math.floor(ms / 1000));
|
|
5
7
|
const minutes = Math.floor(seconds / 60);
|
|
6
8
|
const remainder = seconds % 60;
|
|
7
9
|
return minutes > 0 ? `${minutes}m ${remainder}s` : `${seconds}s`;
|
|
8
10
|
}
|
|
11
|
+
/** One terminal text shared by the in-place edit and FleetManager fallback. */
|
|
12
|
+
export function formatRestartProgressCompletion(mode, summary, startedAt, now = Date.now()) {
|
|
13
|
+
const elapsed = formatElapsed(now - startedAt);
|
|
14
|
+
const lines = mode === "update"
|
|
15
|
+
? [t("update.progress.complete", summary.version, summary.running, summary.total, updateElapsedSeconds(startedAt, now))]
|
|
16
|
+
: mode === "reload"
|
|
17
|
+
? [t("restart.progress.complete_summary", summary.version, summary.running, summary.total, updateElapsedSeconds(startedAt, now))]
|
|
18
|
+
: [`✅ Fleet ready — ${summary.running}/${summary.total} instances running (${elapsed}) · v${summary.version}`];
|
|
19
|
+
if (summary.pausedNames.length) {
|
|
20
|
+
lines.push(`⏸ Paused (${summary.pausedNames.length}): ${summary.pausedNames.join(", ")}`);
|
|
21
|
+
}
|
|
22
|
+
if (summary.failedNames?.length) {
|
|
23
|
+
lines.push(`⚠️ Failed (${summary.failedNames.length}): ${summary.failedNames.join(", ")}`);
|
|
24
|
+
}
|
|
25
|
+
if (mode === "update" && summary.tipText)
|
|
26
|
+
lines.push("", summary.tipText);
|
|
27
|
+
return lines.join("\n");
|
|
28
|
+
}
|
|
9
29
|
/** One General-topic message that is edited throughout a fleet startup. */
|
|
10
30
|
export class RestartProgress {
|
|
11
31
|
total;
|
|
@@ -17,7 +37,10 @@ export class RestartProgress {
|
|
|
17
37
|
messageId = null;
|
|
18
38
|
lastReportedReady = 0;
|
|
19
39
|
updateTimer = null;
|
|
20
|
-
|
|
40
|
+
/** At most one provider edit is active and one newest progress frame waits
|
|
41
|
+
* behind it. This prevents a slow adapter from accumulating 1 Hz history. */
|
|
42
|
+
progressEditWorker = null;
|
|
43
|
+
pendingProgressText = null;
|
|
21
44
|
finished = false;
|
|
22
45
|
mode;
|
|
23
46
|
constructor(total, startedAt, logger, options = {}) {
|
|
@@ -25,7 +48,7 @@ export class RestartProgress {
|
|
|
25
48
|
this.startedAt = startedAt;
|
|
26
49
|
this.logger = logger;
|
|
27
50
|
this.mode = options.mode ?? "restart";
|
|
28
|
-
this.enabled = this.mode === "update" || total > 5;
|
|
51
|
+
this.enabled = this.mode === "update" || this.mode === "reload" || total > 5;
|
|
29
52
|
}
|
|
30
53
|
/** May be called before the channel adapter is ready; progress is retained. */
|
|
31
54
|
markReady() {
|
|
@@ -40,9 +63,12 @@ export class RestartProgress {
|
|
|
40
63
|
async start(target) {
|
|
41
64
|
if (!this.enabled || !target || this.messageId || this.finished)
|
|
42
65
|
return false;
|
|
66
|
+
const adapter = this.resolveAdapter(target);
|
|
67
|
+
if (!adapter)
|
|
68
|
+
return false;
|
|
43
69
|
this.target = target;
|
|
44
70
|
try {
|
|
45
|
-
const sent = await
|
|
71
|
+
const sent = await adapter.sendText(target.chatId, `🔄 Fleet restarting — ${this.total} instances starting...`, { threadId: target.threadId });
|
|
46
72
|
this.messageId = sent.messageId;
|
|
47
73
|
if (this.ready >= 5)
|
|
48
74
|
this.queueProgressEdit();
|
|
@@ -58,45 +84,41 @@ export class RestartProgress {
|
|
|
58
84
|
}
|
|
59
85
|
/** Adopt the pre-update message after the new fleet process reconnects. */
|
|
60
86
|
async resume(target, messageId) {
|
|
61
|
-
if (!this.enabled || this.mode
|
|
87
|
+
if (!this.enabled || this.mode === "restart" || !target || !messageId || this.finished)
|
|
88
|
+
return false;
|
|
89
|
+
if (!target.adapter && !target.resolveAdapter)
|
|
62
90
|
return false;
|
|
63
91
|
this.target = target;
|
|
64
92
|
this.messageId = messageId;
|
|
65
|
-
|
|
66
|
-
|
|
93
|
+
// Do not let a stuck provider call hold fleet startup. The coalescing worker
|
|
94
|
+
// owns this best-effort progress frame; finish() has its own hard deadline.
|
|
95
|
+
this.scheduleProgressEdit(this.mode === "reload"
|
|
96
|
+
? t("restart.progress.starting", updateElapsedSeconds(this.startedAt))
|
|
97
|
+
: t("update.progress.starting", updateElapsedSeconds(this.startedAt)));
|
|
67
98
|
this.updateTimer = setInterval(() => this.queueProgressEdit(), 1_000);
|
|
68
99
|
this.updateTimer.unref?.();
|
|
69
100
|
return true;
|
|
70
101
|
}
|
|
71
|
-
/**
|
|
102
|
+
/** Deliver the terminal state. Returns true only after an edit or fresh fallback succeeds. */
|
|
72
103
|
async finish(summary) {
|
|
73
104
|
if (!this.enabled)
|
|
74
105
|
return false;
|
|
106
|
+
// Capture the current worker before preventing it from starting any newer
|
|
107
|
+
// pending frame. The terminal path waits for this exact edit so the final
|
|
108
|
+
// edit cannot overtake an ordinary, healthy provider round trip.
|
|
109
|
+
const progressEditWorker = this.progressEditWorker;
|
|
75
110
|
this.finished = true;
|
|
111
|
+
this.pendingProgressText = null;
|
|
76
112
|
if (this.updateTimer) {
|
|
77
113
|
clearInterval(this.updateTimer);
|
|
78
114
|
this.updateTimer = null;
|
|
79
115
|
}
|
|
80
116
|
if (!this.target || !this.messageId)
|
|
81
117
|
return false;
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
? [`✅ Fleet ready — ${summary.running}/${summary.total} instances running (${elapsed}) · v${summary.version}`]
|
|
87
|
-
: [`✅ Fleet ready — ${this.ready}/${this.total} instances started (${elapsed})`];
|
|
88
|
-
if (summary?.pausedNames.length) {
|
|
89
|
-
lines.push(`⏸ Paused (${summary.pausedNames.length}): ${summary.pausedNames.join(", ")}`);
|
|
90
|
-
}
|
|
91
|
-
if (summary?.failedNames?.length) {
|
|
92
|
-
lines.push(`⚠️ Failed (${summary.failedNames.length}): ${summary.failedNames.join(", ")}`);
|
|
93
|
-
}
|
|
94
|
-
if (this.mode === "update" && summary?.tipText) {
|
|
95
|
-
lines.push("", summary.tipText);
|
|
96
|
-
}
|
|
97
|
-
this.enqueueEdit(lines.join("\n"));
|
|
98
|
-
await this.editChain;
|
|
99
|
-
return true;
|
|
118
|
+
const text = summary
|
|
119
|
+
? formatRestartProgressCompletion(this.mode, summary, this.startedAt)
|
|
120
|
+
: `✅ Fleet ready — ${this.ready}/${this.total} instances started (${formatElapsed(Date.now() - this.startedAt)})`;
|
|
121
|
+
return this.deliverTerminal(text, progressEditWorker);
|
|
100
122
|
}
|
|
101
123
|
get readyCount() { return this.ready; }
|
|
102
124
|
queueProgressEdit() {
|
|
@@ -105,17 +127,156 @@ export class RestartProgress {
|
|
|
105
127
|
this.lastReportedReady = this.ready;
|
|
106
128
|
const text = this.mode === "update"
|
|
107
129
|
? t("update.progress.instances", this.ready, this.total, updateElapsedSeconds(this.startedAt))
|
|
108
|
-
:
|
|
109
|
-
|
|
130
|
+
: this.mode === "reload"
|
|
131
|
+
? t("restart.progress.instances", this.ready, this.total, updateElapsedSeconds(this.startedAt))
|
|
132
|
+
: `🔄 Fleet restarting — ${this.ready}/${this.total} ready...`;
|
|
133
|
+
this.scheduleProgressEdit(text);
|
|
110
134
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
135
|
+
resolveAdapter(target = this.target) {
|
|
136
|
+
if (!target)
|
|
137
|
+
return undefined;
|
|
138
|
+
return target.resolveAdapter ? target.resolveAdapter() : target.adapter;
|
|
139
|
+
}
|
|
140
|
+
adapterReady(adapter) {
|
|
141
|
+
const health = adapter.getHealthSnapshot?.();
|
|
142
|
+
return !health || health.isReady;
|
|
143
|
+
}
|
|
144
|
+
/** Tail-only coalescing for non-terminal progress. */
|
|
145
|
+
scheduleProgressEdit(text) {
|
|
146
|
+
if (this.finished || !this.target || !this.messageId)
|
|
115
147
|
return;
|
|
116
|
-
this.
|
|
117
|
-
|
|
118
|
-
|
|
148
|
+
this.pendingProgressText = text;
|
|
149
|
+
if (this.progressEditWorker)
|
|
150
|
+
return;
|
|
151
|
+
const worker = this.drainProgressEdits();
|
|
152
|
+
this.progressEditWorker = worker;
|
|
153
|
+
const complete = () => {
|
|
154
|
+
if (this.progressEditWorker === worker)
|
|
155
|
+
this.progressEditWorker = null;
|
|
156
|
+
if (!this.finished && this.pendingProgressText != null)
|
|
157
|
+
this.scheduleProgressEdit(this.pendingProgressText);
|
|
158
|
+
};
|
|
159
|
+
void worker.then(complete, err => {
|
|
160
|
+
this.logger.warn({ err }, "Fleet restart progress worker failed");
|
|
161
|
+
complete();
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
async drainProgressEdits() {
|
|
165
|
+
while (!this.finished) {
|
|
166
|
+
const text = this.pendingProgressText;
|
|
167
|
+
if (text == null)
|
|
168
|
+
return;
|
|
169
|
+
this.pendingProgressText = null;
|
|
170
|
+
const target = this.target;
|
|
171
|
+
const messageId = this.messageId;
|
|
172
|
+
const adapter = this.resolveAdapter(target);
|
|
173
|
+
if (!target || !messageId || !adapter || !this.adapterReady(adapter))
|
|
174
|
+
continue;
|
|
175
|
+
try {
|
|
176
|
+
await adapter.editMessage(target.chatId, messageId, text, target.threadId);
|
|
177
|
+
}
|
|
178
|
+
catch (err) {
|
|
179
|
+
this.logger.warn({ err }, "Failed to edit fleet restart progress");
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* A terminal state must mean "delivered", not merely "had a message id".
|
|
185
|
+
* Wait through a bounded adapter-recovery window, re-resolving replacement
|
|
186
|
+
* adapter objects each time. If editing the adopted message fails, post a
|
|
187
|
+
* fresh completion message so the user is never left at the last X/N update.
|
|
188
|
+
*/
|
|
189
|
+
async deliverTerminal(text, progressEditWorker) {
|
|
190
|
+
const target = this.target;
|
|
191
|
+
if (!target || !this.messageId)
|
|
192
|
+
return false;
|
|
193
|
+
const deadline = Date.now() + RESTART_PROGRESS_TERMINAL_TIMEOUT_MS;
|
|
194
|
+
if (progressEditWorker) {
|
|
195
|
+
const drained = await this.beforeDeadline(() => progressEditWorker, deadline);
|
|
196
|
+
if (drained.status === "timeout") {
|
|
197
|
+
this.logger.warn({}, "Timed out waiting for the in-flight fleet progress edit before terminal delivery");
|
|
198
|
+
return this.terminalDeliveryFailed();
|
|
199
|
+
}
|
|
200
|
+
if (drained.status === "rejected") {
|
|
201
|
+
this.logger.warn({ err: drained.reason }, "Fleet progress edit worker rejected before terminal delivery");
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
let adapter = await this.waitForReadyAdapter(deadline);
|
|
205
|
+
if (!adapter)
|
|
206
|
+
return this.terminalDeliveryFailed();
|
|
207
|
+
const editAdapter = adapter;
|
|
208
|
+
const edit = await this.beforeDeadline(() => editAdapter.editMessage(target.chatId, this.messageId, text, target.threadId), deadline);
|
|
209
|
+
if (edit.status === "fulfilled")
|
|
210
|
+
return true;
|
|
211
|
+
if (edit.status === "rejected") {
|
|
212
|
+
this.logger.warn({ err: edit.reason }, "Failed to edit fleet restart progress terminal state");
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
this.logger.warn({}, "Timed out editing fleet restart progress terminal state");
|
|
216
|
+
}
|
|
217
|
+
// The edit may have failed because the gateway dropped between the first
|
|
218
|
+
// readiness probe and the write. Re-check and, if necessary, wait for the
|
|
219
|
+
// replacement generation before the one and only fresh-send attempt.
|
|
220
|
+
adapter = await this.waitForReadyAdapter(deadline);
|
|
221
|
+
if (!adapter)
|
|
222
|
+
return this.terminalDeliveryFailed();
|
|
223
|
+
const sendAdapter = adapter;
|
|
224
|
+
const sent = await this.beforeDeadline(() => sendAdapter.sendText(target.chatId, text, { threadId: target.threadId }), deadline);
|
|
225
|
+
if (sent.status === "fulfilled") {
|
|
226
|
+
this.logger.warn({}, "Posted fresh fleet completion after terminal progress edit was unavailable");
|
|
227
|
+
return true;
|
|
228
|
+
}
|
|
229
|
+
if (sent.status === "rejected") {
|
|
230
|
+
this.logger.warn({ err: sent.reason }, "Failed to post fresh fleet completion after edit failure");
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
this.logger.warn({}, "Timed out posting fresh fleet completion after edit failure");
|
|
234
|
+
}
|
|
235
|
+
return this.terminalDeliveryFailed();
|
|
236
|
+
}
|
|
237
|
+
async waitForReadyAdapter(deadline) {
|
|
238
|
+
while (true) {
|
|
239
|
+
const remaining = deadline - Date.now();
|
|
240
|
+
if (remaining <= 0)
|
|
241
|
+
return undefined;
|
|
242
|
+
const adapter = this.resolveAdapter();
|
|
243
|
+
if (adapter && this.adapterReady(adapter))
|
|
244
|
+
return adapter;
|
|
245
|
+
await new Promise(resolve => {
|
|
246
|
+
const timer = setTimeout(resolve, Math.min(TERMINAL_DELIVERY_RETRY_MS, remaining));
|
|
247
|
+
timer.unref?.();
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
async beforeDeadline(start, deadline) {
|
|
252
|
+
const remaining = deadline - Date.now();
|
|
253
|
+
if (remaining <= 0)
|
|
254
|
+
return { status: "timeout" };
|
|
255
|
+
let work;
|
|
256
|
+
try {
|
|
257
|
+
work = start();
|
|
258
|
+
}
|
|
259
|
+
catch (reason) {
|
|
260
|
+
return { status: "rejected", reason };
|
|
261
|
+
}
|
|
262
|
+
let timer;
|
|
263
|
+
const timeout = new Promise(resolve => {
|
|
264
|
+
timer = setTimeout(() => resolve({ status: "timeout" }), remaining);
|
|
265
|
+
timer.unref?.();
|
|
266
|
+
});
|
|
267
|
+
const settled = work.then(value => ({ status: "fulfilled", value }), reason => ({ status: "rejected", reason }));
|
|
268
|
+
const result = await Promise.race([settled, timeout]);
|
|
269
|
+
if (timer)
|
|
270
|
+
clearTimeout(timer);
|
|
271
|
+
return result;
|
|
272
|
+
}
|
|
273
|
+
terminalDeliveryFailed() {
|
|
274
|
+
const data = { timeout_ms: RESTART_PROGRESS_TERMINAL_TIMEOUT_MS };
|
|
275
|
+
if (this.logger.error)
|
|
276
|
+
this.logger.error(data, "Fleet completion could not be delivered after adapter recovery wait");
|
|
277
|
+
else
|
|
278
|
+
this.logger.warn(data, "Fleet completion could not be delivered after adapter recovery wait");
|
|
279
|
+
return false;
|
|
119
280
|
}
|
|
120
281
|
}
|
|
121
282
|
//# sourceMappingURL=restart-progress.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"restart-progress.js","sourceRoot":"","sources":["../src/restart-progress.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,aAAa,CAAC;AAChC,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AA0B5D,SAAS,aAAa,CAAC,EAAU;IAC/B,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;IACnD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;IACzC,MAAM,SAAS,GAAG,OAAO,GAAG,EAAE,CAAC;IAC/B,OAAO,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,KAAK,SAAS,GAAG,CAAC,CAAC,CAAC,GAAG,OAAO,GAAG,CAAC;AACnE,CAAC;AAED,2EAA2E;AAC3E,MAAM,OAAO,eAAe;IAYf;IACQ;IACA;IAbV,OAAO,CAAU;IAClB,KAAK,GAAG,CAAC,CAAC;IACV,MAAM,GAAiC,IAAI,CAAC;IAC5C,SAAS,GAAkB,IAAI,CAAC;IAChC,iBAAiB,GAAG,CAAC,CAAC;IACtB,WAAW,GAA0C,IAAI,CAAC;IAC1D,SAAS,GAAkB,OAAO,CAAC,OAAO,EAAE,CAAC;IAC7C,QAAQ,GAAG,KAAK,CAAC;IACR,IAAI,CAAuB;IAE5C,YACW,KAAa,EACL,SAAiB,EACjB,MAAsB,EACvC,UAAkC,EAAE;QAH3B,UAAK,GAAL,KAAK,CAAQ;QACL,cAAS,GAAT,SAAS,CAAQ;QACjB,WAAM,GAAN,MAAM,CAAgB;QAGvC,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,SAAS,CAAC;QACtC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,GAAG,CAAC,CAAC;IACrD,CAAC;IAED,+EAA+E;IAC/E,SAAS;QACP,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QAC3C,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;QAClD,IAAI,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,iBAAiB,IAAI,CAAC,EAAE,CAAC;YAC/D,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC3B,CAAC;IACH,CAAC;IAED,gFAAgF;IAChF,KAAK,CAAC,KAAK,CAAC,MAAoC;QAC9C,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO,KAAK,CAAC;QAC9E,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,QAAQ,CACxC,MAAM,CAAC,MAAM,EACb,yBAAyB,IAAI,CAAC,KAAK,wBAAwB,EAC3D,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAC9B,CAAC;YACF,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;YAChC,IAAI,IAAI,CAAC,KAAK,IAAI,CAAC;gBAAE,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAC9C,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,MAAM,CAAC,CAAC;YACvE,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,EAAE,CAAC;YAC3B,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,EAAE,uCAAuC,CAAC,CAAC;YACnE,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;YACnB,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,2EAA2E;IAC3E,KAAK,CAAC,MAAM,CAAC,MAAoC,EAAE,SAAiB;QAClE,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,MAAM,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO,KAAK,CAAC;QACpG,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,0BAA0B,EAAE,oBAAoB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QACtF,MAAM,IAAI,CAAC,SAAS,CAAC;QACrB,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,KAAK,CAAC,CAAC;QACtE,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,EAAE,CAAC;QAC3B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,mFAAmF;IACnF,KAAK,CAAC,MAAM,CAAC,OAAgC;QAC3C,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO,KAAK,CAAC;QAChC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,aAAa,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YAChC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QAC1B,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,SAAS;YAAE,OAAO,KAAK,CAAC;QAClD,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC;QAC3D,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,OAAO;YAC7C,CAAC,CAAC,CAAC,CAAC,CAAC,0BAA0B,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,EAAE,oBAAoB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;YACxH,CAAC,CAAC,OAAO;gBACP,CAAC,CAAC,CAAC,mBAAmB,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,KAAK,uBAAuB,OAAO,QAAQ,OAAO,CAAC,OAAO,EAAE,CAAC;gBAC9G,CAAC,CAAC,CAAC,mBAAmB,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,uBAAuB,OAAO,GAAG,CAAC,CAAC;QACrF,IAAI,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,CAAC;YAChC,KAAK,CAAC,IAAI,CAAC,aAAa,OAAO,CAAC,WAAW,CAAC,MAAM,MAAM,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC5F,CAAC;QACD,IAAI,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC;YACjC,KAAK,CAAC,IAAI,CAAC,cAAc,OAAO,CAAC,WAAW,CAAC,MAAM,MAAM,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC7F,CAAC;QACD,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,OAAO,EAAE,OAAO,EAAE,CAAC;YAC/C,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QAClC,CAAC;QACD,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACnC,MAAM,IAAI,CAAC,SAAS,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,UAAU,KAAa,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IAEvC,iBAAiB;QACvB,IAAI,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,SAAS;YAAE,OAAO;QAC7D,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,KAAK,CAAC;QACpC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,KAAK,QAAQ;YACjC,CAAC,CAAC,CAAC,CAAC,2BAA2B,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,oBAAoB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC9F,CAAC,CAAC,yBAAyB,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,WAAW,CAAC;QACjE,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IACzB,CAAC;IAEO,WAAW,CAAC,IAAY;QAC9B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC3B,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;QACjC,IAAI,CAAC,MAAM,IAAI,CAAC,SAAS;YAAE,OAAO;QAClC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS;aAC5B,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;aACvF,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,EAAE,uCAAuC,CAAC,CAAC,CAAC;IACtF,CAAC;CACF"}
|
|
1
|
+
{"version":3,"file":"restart-progress.js","sourceRoot":"","sources":["../src/restart-progress.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ServiceState } from "./service-installer.js";
|
|
2
|
+
/**
|
|
3
|
+
* `restartSystemdService()` returns false for both a definitive command error
|
|
4
|
+
* and its five-minute Type=notify timeout. The latter can still finish and
|
|
5
|
+
* start a healthy replacement, so callers must leave the cross-process marker
|
|
6
|
+
* pending for that replacement instead of declaring failure.
|
|
7
|
+
*/
|
|
8
|
+
export declare const SYSTEMD_RESTART_INDETERMINATE_EXIT_CODE = 0;
|
|
9
|
+
export interface SystemdRestartSelectionInput {
|
|
10
|
+
platform: "macos" | "linux";
|
|
11
|
+
systemServiceInstalled: boolean;
|
|
12
|
+
systemState: ServiceState;
|
|
13
|
+
userServiceInstalled: boolean;
|
|
14
|
+
userState: ServiceState;
|
|
15
|
+
}
|
|
16
|
+
export interface SystemdRestartTarget {
|
|
17
|
+
unit: "agend" | "com.agend.fleet";
|
|
18
|
+
user: boolean;
|
|
19
|
+
state: ServiceState;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Select the authoritative systemd unit before considering launchd or a
|
|
23
|
+
* detached PID. This pure boundary is deliberately injectable in tests: a
|
|
24
|
+
* chat-triggered full restart must never bypass an installed systemd unit and
|
|
25
|
+
* fall back to a SIGUSR1/in-process replacement.
|
|
26
|
+
*/
|
|
27
|
+
export declare function selectSystemdRestartTarget(input: SystemdRestartSelectionInput): SystemdRestartTarget | null;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `restartSystemdService()` returns false for both a definitive command error
|
|
3
|
+
* and its five-minute Type=notify timeout. The latter can still finish and
|
|
4
|
+
* start a healthy replacement, so callers must leave the cross-process marker
|
|
5
|
+
* pending for that replacement instead of declaring failure.
|
|
6
|
+
*/
|
|
7
|
+
export const SYSTEMD_RESTART_INDETERMINATE_EXIT_CODE = 0;
|
|
8
|
+
/**
|
|
9
|
+
* Select the authoritative systemd unit before considering launchd or a
|
|
10
|
+
* detached PID. This pure boundary is deliberately injectable in tests: a
|
|
11
|
+
* chat-triggered full restart must never bypass an installed systemd unit and
|
|
12
|
+
* fall back to a SIGUSR1/in-process replacement.
|
|
13
|
+
*/
|
|
14
|
+
export function selectSystemdRestartTarget(input) {
|
|
15
|
+
if (input.platform === "macos")
|
|
16
|
+
return null;
|
|
17
|
+
if (input.systemServiceInstalled) {
|
|
18
|
+
return { unit: "agend", user: false, state: input.systemState };
|
|
19
|
+
}
|
|
20
|
+
if (input.userServiceInstalled) {
|
|
21
|
+
return { unit: "com.agend.fleet", user: true, state: input.userState };
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=service-restart-selection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-restart-selection.js","sourceRoot":"","sources":["../src/service-restart-selection.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,uCAAuC,GAAG,CAAC,CAAC;AAgBzD;;;;;GAKG;AACH,MAAM,UAAU,0BAA0B,CACxC,KAAmC;IAEnC,IAAI,KAAK,CAAC,QAAQ,KAAK,OAAO;QAAE,OAAO,IAAI,CAAC;IAC5C,IAAI,KAAK,CAAC,sBAAsB,EAAE,CAAC;QACjC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC;IAClE,CAAC;IACD,IAAI,KAAK,CAAC,oBAAoB,EAAE,CAAC;QAC/B,OAAO,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,SAAS,EAAE,CAAC;IACzE,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
package/dist/tmux-manager.d.ts
CHANGED
|
@@ -24,12 +24,25 @@ export declare class TmuxManager {
|
|
|
24
24
|
/** PID identifies a concrete tmux server generation. */
|
|
25
25
|
static getServerPid(sessionName: string): Promise<number | null>;
|
|
26
26
|
static sessionExists(name: string): Promise<boolean>;
|
|
27
|
+
/**
|
|
28
|
+
* Bounded, tri-state-safe variant for the login/install path: true = exists,
|
|
29
|
+
* false = tmux positively says the session is absent, anything else
|
|
30
|
+
* (timeout, spawn failure, unexpected exit) REJECTS — never "absent".
|
|
31
|
+
*/
|
|
32
|
+
static sessionExistsStrict(name: string): Promise<boolean>;
|
|
27
33
|
static killSession(name: string): Promise<void>;
|
|
28
34
|
static listWindows(sessionName: string): Promise<Array<{
|
|
29
35
|
id: string;
|
|
30
36
|
name: string;
|
|
31
37
|
}>>;
|
|
38
|
+
/** Bounded, failing variant for the login/install path: a timeout or tmux error REJECTS instead of reading as "no windows". */
|
|
39
|
+
static listWindowsStrict(sessionName: string): Promise<Array<{
|
|
40
|
+
id: string;
|
|
41
|
+
name: string;
|
|
42
|
+
}>>;
|
|
32
43
|
static getPanePid(sessionName: string, windowId: string): Promise<number | null>;
|
|
44
|
+
/** Window name handed to createWindow(), kept so a cleanup can find a window whose id we never learned. */
|
|
45
|
+
private pendingWindowName;
|
|
33
46
|
createWindow(command: string, cwd: string, windowName?: string): Promise<string>;
|
|
34
47
|
/** Restart the process inside the existing window, preserving its window id. */
|
|
35
48
|
respawnWindow(command: string, cwd: string): Promise<void>;
|
|
@@ -49,6 +62,12 @@ export declare class TmuxManager {
|
|
|
49
62
|
*/
|
|
50
63
|
private applyLogicalSize;
|
|
51
64
|
killWindow(): Promise<void>;
|
|
65
|
+
/**
|
|
66
|
+
* killWindow() then verify by listing: true only when the window is
|
|
67
|
+
* positively absent. A tmux error while listing is "unknown" → false, so
|
|
68
|
+
* callers never report a cleanup as done on a guess.
|
|
69
|
+
*/
|
|
70
|
+
killWindowConfirmed(): Promise<boolean>;
|
|
52
71
|
/**
|
|
53
72
|
* Check if the tmux window still exists in the session.
|
|
54
73
|
* Note: with remain-on-exit enabled, a window with a dead pane still
|
package/dist/tmux-manager.js
CHANGED
|
@@ -43,6 +43,8 @@ export function resolveTmuxLogicalSize(config) {
|
|
|
43
43
|
rows: config?.rows ?? DEFAULT_TMUX_LOGICAL_SIZE.rows,
|
|
44
44
|
};
|
|
45
45
|
}
|
|
46
|
+
/** Hard bound for tmux operations on the remote login/install path: a wedged tmux must never hold a teardown open. */
|
|
47
|
+
const LOGIN_TMUX_OP_TIMEOUT_MS = 10_000;
|
|
46
48
|
export class TmuxManager {
|
|
47
49
|
sessionName;
|
|
48
50
|
logicalSize;
|
|
@@ -74,9 +76,11 @@ export class TmuxManager {
|
|
|
74
76
|
if (existing)
|
|
75
77
|
return existing;
|
|
76
78
|
const operation = (async () => {
|
|
77
|
-
|
|
79
|
+
// Strict: only a POSITIVE "no such session" leads to creating one. A
|
|
80
|
+
// timeout or spawn failure rejects instead of being read as "absent".
|
|
81
|
+
if (!(await TmuxManager.sessionExistsStrict(name))) {
|
|
78
82
|
try {
|
|
79
|
-
await exec("tmux", TmuxManager.tmuxArgs(["new-session", "-d", "-s", name]));
|
|
83
|
+
await exec("tmux", TmuxManager.tmuxArgs(["new-session", "-d", "-s", name]), { timeout: LOGIN_TMUX_OP_TIMEOUT_MS });
|
|
80
84
|
}
|
|
81
85
|
catch (err) {
|
|
82
86
|
if (!String(err).includes("duplicate session"))
|
|
@@ -87,7 +91,7 @@ export class TmuxManager {
|
|
|
87
91
|
// global default would also change tmux sessions that AgEnD does not own.
|
|
88
92
|
await exec("tmux", TmuxManager.tmuxArgs([
|
|
89
93
|
"set-option", "-t", name, "mouse", "on",
|
|
90
|
-
]));
|
|
94
|
+
]), { timeout: LOGIN_TMUX_OP_TIMEOUT_MS });
|
|
91
95
|
})().finally(() => {
|
|
92
96
|
if (TmuxManager.ensureSessionInFlight.get(name) === operation) {
|
|
93
97
|
TmuxManager.ensureSessionInFlight.delete(name);
|
|
@@ -118,6 +122,24 @@ export class TmuxManager {
|
|
|
118
122
|
return false;
|
|
119
123
|
}
|
|
120
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* Bounded, tri-state-safe variant for the login/install path: true = exists,
|
|
127
|
+
* false = tmux positively says the session is absent, anything else
|
|
128
|
+
* (timeout, spawn failure, unexpected exit) REJECTS — never "absent".
|
|
129
|
+
*/
|
|
130
|
+
static async sessionExistsStrict(name) {
|
|
131
|
+
try {
|
|
132
|
+
await exec("tmux", TmuxManager.tmuxArgs(["has-session", "-t", name]), { timeout: LOGIN_TMUX_OP_TIMEOUT_MS });
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
catch (err) {
|
|
136
|
+
const e = err;
|
|
137
|
+
const text = String(e.stderr ?? "");
|
|
138
|
+
if (e.code === 1 && !e.killed && /can't find session|no server running|error connecting to .*No such file/.test(text))
|
|
139
|
+
return false;
|
|
140
|
+
throw new Error(`tmux has-session could not be determined for ${name}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
121
143
|
static async killSession(name) {
|
|
122
144
|
try {
|
|
123
145
|
await exec("tmux", TmuxManager.tmuxArgs(["kill-session", "-t", name]));
|
|
@@ -140,6 +162,16 @@ export class TmuxManager {
|
|
|
140
162
|
return [];
|
|
141
163
|
}
|
|
142
164
|
}
|
|
165
|
+
/** Bounded, failing variant for the login/install path: a timeout or tmux error REJECTS instead of reading as "no windows". */
|
|
166
|
+
static async listWindowsStrict(sessionName) {
|
|
167
|
+
const { stdout } = await exec("tmux", TmuxManager.tmuxArgs([
|
|
168
|
+
"list-windows", "-t", sessionName, "-F", "#{window_id}|||#{window_name}"
|
|
169
|
+
]), { timeout: LOGIN_TMUX_OP_TIMEOUT_MS });
|
|
170
|
+
return stdout.trim().split("\n").filter(Boolean).map(line => {
|
|
171
|
+
const [id, name] = line.split("|||");
|
|
172
|
+
return { id, name };
|
|
173
|
+
});
|
|
174
|
+
}
|
|
143
175
|
static async getPanePid(sessionName, windowId) {
|
|
144
176
|
try {
|
|
145
177
|
const { stdout } = await exec("tmux", TmuxManager.tmuxArgs([
|
|
@@ -153,9 +185,14 @@ export class TmuxManager {
|
|
|
153
185
|
}
|
|
154
186
|
}
|
|
155
187
|
// === Instance window methods ===
|
|
188
|
+
/** Window name handed to createWindow(), kept so a cleanup can find a window whose id we never learned. */
|
|
189
|
+
pendingWindowName = null;
|
|
156
190
|
async createWindow(command, cwd, windowName) {
|
|
191
|
+
this.pendingWindowName = windowName ?? null;
|
|
157
192
|
if (windowName) {
|
|
158
|
-
|
|
193
|
+
// Strict listing: a tmux that cannot answer must not be read as "no
|
|
194
|
+
// duplicates" — that would create a second window (fail-closed).
|
|
195
|
+
const sameName = (await TmuxManager.listWindowsStrict(this.sessionName))
|
|
159
196
|
.filter(window => window.name === windowName);
|
|
160
197
|
if (sameName.length > 0) {
|
|
161
198
|
// Reuse one exact window id instead of creating another window with the
|
|
@@ -174,7 +211,7 @@ export class TmuxManager {
|
|
|
174
211
|
await exec("tmux", TmuxManager.tmuxArgs([
|
|
175
212
|
"set-window-option", "-t", `${this.sessionName}:${this.windowId}`,
|
|
176
213
|
"allow-rename", "off",
|
|
177
|
-
])).catch(() => { });
|
|
214
|
+
]), { timeout: LOGIN_TMUX_OP_TIMEOUT_MS }).catch(() => { });
|
|
178
215
|
return this.windowId;
|
|
179
216
|
}
|
|
180
217
|
}
|
|
@@ -182,7 +219,8 @@ export class TmuxManager {
|
|
|
182
219
|
if (windowName)
|
|
183
220
|
args.push("-n", windowName);
|
|
184
221
|
args.push("-P", "-F", "#{window_id}", command);
|
|
185
|
-
|
|
222
|
+
// Hard bound: a wedged tmux must not hold a login/install teardown open forever.
|
|
223
|
+
const { stdout } = await exec("tmux", TmuxManager.tmuxArgs(args), { timeout: 15_000 });
|
|
186
224
|
this.windowId = stdout.trim();
|
|
187
225
|
try {
|
|
188
226
|
// Apply the configured geometry and hand sizing to real terminals. The
|
|
@@ -190,7 +228,7 @@ export class TmuxManager {
|
|
|
190
228
|
// what makes `window-size latest` safe here (see applyLogicalSize).
|
|
191
229
|
await this.applyLogicalSize();
|
|
192
230
|
if (windowName) {
|
|
193
|
-
await exec("tmux", TmuxManager.tmuxArgs(["set-window-option", "-t", `${this.sessionName}:${this.windowId}`, "allow-rename", "off"])).catch(() => { });
|
|
231
|
+
await exec("tmux", TmuxManager.tmuxArgs(["set-window-option", "-t", `${this.sessionName}:${this.windowId}`, "allow-rename", "off"]), { timeout: LOGIN_TMUX_OP_TIMEOUT_MS }).catch(() => { });
|
|
194
232
|
}
|
|
195
233
|
}
|
|
196
234
|
catch (err) {
|
|
@@ -210,7 +248,7 @@ export class TmuxManager {
|
|
|
210
248
|
await exec("tmux", TmuxManager.tmuxArgs([
|
|
211
249
|
"respawn-window", "-k", "-t", `${this.sessionName}:${this.windowId}`,
|
|
212
250
|
"-c", cwd, command,
|
|
213
|
-
]));
|
|
251
|
+
]), { timeout: LOGIN_TMUX_OP_TIMEOUT_MS });
|
|
214
252
|
}
|
|
215
253
|
/**
|
|
216
254
|
* Give the window its configured geometry, then hand sizing authority to real
|
|
@@ -234,21 +272,50 @@ export class TmuxManager {
|
|
|
234
272
|
"resize-window", "-t", target,
|
|
235
273
|
"-x", String(this.logicalSize.columns),
|
|
236
274
|
"-y", String(this.logicalSize.rows),
|
|
237
|
-
]));
|
|
275
|
+
]), { timeout: LOGIN_TMUX_OP_TIMEOUT_MS });
|
|
238
276
|
await exec("tmux", TmuxManager.tmuxArgs([
|
|
239
277
|
"set-window-option", "-t", target, "window-size", "latest",
|
|
240
|
-
]));
|
|
278
|
+
]), { timeout: LOGIN_TMUX_OP_TIMEOUT_MS });
|
|
241
279
|
}
|
|
242
280
|
async killWindow() {
|
|
243
281
|
if (!this.windowId)
|
|
244
282
|
return;
|
|
245
283
|
try {
|
|
246
|
-
await exec("tmux", TmuxManager.tmuxArgs(["kill-window", "-t", `${this.sessionName}:${this.windowId}`]));
|
|
284
|
+
await exec("tmux", TmuxManager.tmuxArgs(["kill-window", "-t", `${this.sessionName}:${this.windowId}`]), { timeout: LOGIN_TMUX_OP_TIMEOUT_MS });
|
|
247
285
|
}
|
|
248
286
|
catch {
|
|
249
287
|
// Expected if window already exited
|
|
250
288
|
}
|
|
251
289
|
}
|
|
290
|
+
/**
|
|
291
|
+
* killWindow() then verify by listing: true only when the window is
|
|
292
|
+
* positively absent. A tmux error while listing is "unknown" → false, so
|
|
293
|
+
* callers never report a cleanup as done on a guess.
|
|
294
|
+
*/
|
|
295
|
+
async killWindowConfirmed() {
|
|
296
|
+
const name = this.pendingWindowName;
|
|
297
|
+
if (!this.windowId && !name)
|
|
298
|
+
return true; // nothing was ever asked for
|
|
299
|
+
const mine = (w) => (this.windowId ? w.id === this.windowId : w.name === name);
|
|
300
|
+
const list = async () => {
|
|
301
|
+
const { stdout } = await exec("tmux", TmuxManager.tmuxArgs(["list-windows", "-t", this.sessionName, "-F", "#{window_id}|||#{window_name}"]), { timeout: 10_000 });
|
|
302
|
+
return stdout.trim().split("\n").filter(Boolean).map(line => { const [id, n] = line.split("|||"); return { id, name: n ?? "" }; });
|
|
303
|
+
};
|
|
304
|
+
try {
|
|
305
|
+
// Kill by id when known; otherwise every window carrying the name we
|
|
306
|
+
// asked for (a createWindow that timed out may still have made one).
|
|
307
|
+
// Kills run in PARALLEL so the total stays bounded regardless of how
|
|
308
|
+
// many duplicates exist: list (10 s) + kills (10 s) + list (10 s) ≤ 30 s.
|
|
309
|
+
const targets = (await list()).filter(mine);
|
|
310
|
+
await Promise.all(targets.map(w => exec("tmux", TmuxManager.tmuxArgs(["kill-window", "-t", `${this.sessionName}:${w.id}`]), { timeout: LOGIN_TMUX_OP_TIMEOUT_MS }).catch(() => { })));
|
|
311
|
+
return !(await list()).some(mine);
|
|
312
|
+
}
|
|
313
|
+
catch (err) {
|
|
314
|
+
// No such session at all ⇒ no window either; anything else is unknown → not confirmed.
|
|
315
|
+
const text = String(err?.stderr ?? err.message ?? "");
|
|
316
|
+
return /can't find session|no server running/.test(text);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
252
319
|
/**
|
|
253
320
|
* Check if the tmux window still exists in the session.
|
|
254
321
|
* Note: with remain-on-exit enabled, a window with a dead pane still
|
|
@@ -270,7 +337,7 @@ export class TmuxManager {
|
|
|
270
337
|
await exec("tmux", TmuxManager.tmuxArgs([
|
|
271
338
|
"set-option", "-t", `${this.sessionName}:${this.windowId}`,
|
|
272
339
|
"remain-on-exit", "on",
|
|
273
|
-
]));
|
|
340
|
+
]), { timeout: LOGIN_TMUX_OP_TIMEOUT_MS });
|
|
274
341
|
}
|
|
275
342
|
/**
|
|
276
343
|
* Get pane status. Returns null if the window doesn't exist.
|