@co0ontty/wand 4.39.0 → 4.40.0
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/build-info.json +3 -3
- package/dist/process-manager.d.ts +4 -0
- package/dist/process-manager.js +4 -0
- package/dist/server-file-routes.js +119 -1
- package/dist/server-session-routes.js +2 -0
- package/dist/server-workspace-routes.d.ts +16 -0
- package/dist/server-workspace-routes.js +373 -0
- package/dist/server.js +6 -0
- package/dist/storage.d.ts +41 -1
- package/dist/storage.js +261 -4
- package/dist/structured-session-manager.d.ts +4 -0
- package/dist/structured-session-manager.js +2 -0
- package/dist/types.d.ts +82 -0
- package/dist/web-ui/content/scripts.js +494 -75
- package/dist/web-ui/content/styles.css +1 -1
- package/dist/web-ui/embedded-assets.d.ts +1 -1
- package/dist/web-ui/embedded-assets.js +3 -3
- package/dist/ws-broadcast.d.ts +1 -0
- package/dist/ws-broadcast.js +43 -27
- package/package.json +1 -1
package/dist/ws-broadcast.d.ts
CHANGED
package/dist/ws-broadcast.js
CHANGED
|
@@ -92,10 +92,7 @@ export class WsBroadcastManager {
|
|
|
92
92
|
outputSeqBySession: new Map(),
|
|
93
93
|
pendingResyncSessions: new Set(),
|
|
94
94
|
lastSeenAt: Date.now(),
|
|
95
|
-
|
|
96
|
-
supportsPtyAck: false,
|
|
97
|
-
unackedPtyBytes: 0,
|
|
98
|
-
ptyPausedSessionId: null,
|
|
95
|
+
ptySubscriptions: new Map(),
|
|
99
96
|
};
|
|
100
97
|
this.clients.add(client);
|
|
101
98
|
ws.on("close", () => {
|
|
@@ -120,12 +117,20 @@ export class WsBroadcastManager {
|
|
|
120
117
|
if (Number.isSafeInteger(msg.blockBudget) && msg.blockBudget > 0) {
|
|
121
118
|
client.blockBudget = Math.min(msg.blockBudget, MAX_BLOCK_BUDGET);
|
|
122
119
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
120
|
+
// 默认仍是历史的“切换当前会话”;分屏池显式用 mode:add 叠加订阅。
|
|
121
|
+
if (msg.mode !== "add") {
|
|
122
|
+
this.releaseAllPtyPauses(client);
|
|
123
|
+
client.ptySubscriptions.clear();
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
this.releasePtyPause(client, msg.sessionId);
|
|
127
|
+
}
|
|
128
|
+
client.ptySubscriptions.set(msg.sessionId, {
|
|
129
|
+
supportsAck: msg.capabilities?.ptyAck === true,
|
|
130
|
+
unackedBytes: 0,
|
|
131
|
+
paused: false,
|
|
132
|
+
});
|
|
126
133
|
this.flushOutput(msg.sessionId);
|
|
127
|
-
client.subscribedSessionId = msg.sessionId;
|
|
128
|
-
client.unackedPtyBytes = 0;
|
|
129
134
|
const snapshot = this.port?.getSession(msg.sessionId) ?? null;
|
|
130
135
|
if (snapshot) {
|
|
131
136
|
this.sendInit(client, msg.sessionId, snapshot, false);
|
|
@@ -138,6 +143,10 @@ export class WsBroadcastManager {
|
|
|
138
143
|
}));
|
|
139
144
|
}
|
|
140
145
|
}
|
|
146
|
+
else if (msg.type === "unsubscribe" && msg.sessionId) {
|
|
147
|
+
this.releasePtyPause(client, msg.sessionId);
|
|
148
|
+
client.ptySubscriptions.delete(msg.sessionId);
|
|
149
|
+
}
|
|
141
150
|
else if (msg.type === "resync" && msg.sessionId) {
|
|
142
151
|
this.flushOutput(msg.sessionId);
|
|
143
152
|
const snapshot = this.port?.getSession(msg.sessionId) ?? null;
|
|
@@ -145,7 +154,7 @@ export class WsBroadcastManager {
|
|
|
145
154
|
this.sendInit(client, msg.sessionId, snapshot, true);
|
|
146
155
|
}
|
|
147
156
|
else if (msg.type === "pty_input" && msg.sessionId && typeof msg.data === "string") {
|
|
148
|
-
if (client.
|
|
157
|
+
if (!client.ptySubscriptions.has(msg.sessionId) || msg.data.length > MAX_PTY_INPUT_CHARS)
|
|
149
158
|
return;
|
|
150
159
|
try {
|
|
151
160
|
this.port?.sendPtyInput?.(msg.sessionId, msg.data, typeof msg.shortcutKey === "string" ? msg.shortcutKey : undefined, msg.userInput !== false);
|
|
@@ -155,7 +164,7 @@ export class WsBroadcastManager {
|
|
|
155
164
|
}
|
|
156
165
|
}
|
|
157
166
|
else if (msg.type === "pty_resize" && msg.sessionId) {
|
|
158
|
-
if (client.
|
|
167
|
+
if (!client.ptySubscriptions.has(msg.sessionId))
|
|
159
168
|
return;
|
|
160
169
|
if (!Number.isFinite(msg.cols) || !Number.isFinite(msg.rows))
|
|
161
170
|
return;
|
|
@@ -167,11 +176,12 @@ export class WsBroadcastManager {
|
|
|
167
176
|
}
|
|
168
177
|
}
|
|
169
178
|
else if (msg.type === "pty_ack" && msg.sessionId && Number.isFinite(msg.bytes)) {
|
|
170
|
-
|
|
179
|
+
const subscription = client.ptySubscriptions.get(msg.sessionId);
|
|
180
|
+
if (!subscription?.supportsAck)
|
|
171
181
|
return;
|
|
172
|
-
|
|
173
|
-
if (
|
|
174
|
-
this.releasePtyPause(client);
|
|
182
|
+
subscription.unackedBytes = Math.max(0, subscription.unackedBytes - Math.max(0, Math.floor(msg.bytes)));
|
|
183
|
+
if (subscription.paused && subscription.unackedBytes <= PTY_UNACKED_LOW_WATER) {
|
|
184
|
+
this.releasePtyPause(client, msg.sessionId);
|
|
175
185
|
}
|
|
176
186
|
}
|
|
177
187
|
else if (msg.type === "pong") {
|
|
@@ -389,7 +399,7 @@ export class WsBroadcastManager {
|
|
|
389
399
|
for (const client of this.clients) {
|
|
390
400
|
if (client.ws.readyState !== WebSocket.OPEN)
|
|
391
401
|
continue;
|
|
392
|
-
if (isRawPtyOutput && client.
|
|
402
|
+
if (isRawPtyOutput && !client.ptySubscriptions.has(event.sessionId))
|
|
393
403
|
continue;
|
|
394
404
|
const clientEvent = eventForClient(client);
|
|
395
405
|
// Stamp output events with a per-(client, session) sequence number so
|
|
@@ -400,11 +410,12 @@ export class WsBroadcastManager {
|
|
|
400
410
|
client.outputSeqBySession.set(event.sessionId, seq);
|
|
401
411
|
outgoing = { ...clientEvent, seq };
|
|
402
412
|
}
|
|
403
|
-
const
|
|
413
|
+
const subscription = client.ptySubscriptions.get(event.sessionId);
|
|
414
|
+
const usesPtyAckFlowControl = isRawPtyOutput && subscription?.supportsAck === true;
|
|
404
415
|
if (usesPtyAckFlowControl) {
|
|
405
416
|
outgoing = { ...outgoing, ptyBytes };
|
|
406
|
-
|
|
407
|
-
if (
|
|
417
|
+
subscription.unackedBytes += ptyBytes;
|
|
418
|
+
if (subscription.unackedBytes >= PTY_UNACKED_HIGH_WATER) {
|
|
408
419
|
this.acquirePtyPause(client, event.sessionId);
|
|
409
420
|
}
|
|
410
421
|
}
|
|
@@ -451,7 +462,7 @@ export class WsBroadcastManager {
|
|
|
451
462
|
}
|
|
452
463
|
}
|
|
453
464
|
discardClient(client, terminate) {
|
|
454
|
-
this.
|
|
465
|
+
this.releaseAllPtyPauses(client);
|
|
455
466
|
client.sendQueue.length = 0;
|
|
456
467
|
client.pendingResyncSessions.clear();
|
|
457
468
|
client.sendInProgress = false;
|
|
@@ -465,20 +476,20 @@ export class WsBroadcastManager {
|
|
|
465
476
|
}
|
|
466
477
|
}
|
|
467
478
|
acquirePtyPause(client, sessionId) {
|
|
468
|
-
|
|
479
|
+
const subscription = client.ptySubscriptions.get(sessionId);
|
|
480
|
+
if (!subscription || subscription.paused)
|
|
469
481
|
return;
|
|
470
|
-
|
|
471
|
-
client.ptyPausedSessionId = sessionId;
|
|
482
|
+
subscription.paused = true;
|
|
472
483
|
const next = (this.ptyPauseRefs.get(sessionId) ?? 0) + 1;
|
|
473
484
|
this.ptyPauseRefs.set(sessionId, next);
|
|
474
485
|
if (next === 1)
|
|
475
486
|
this.port?.pausePtyOutput?.(sessionId);
|
|
476
487
|
}
|
|
477
|
-
releasePtyPause(client) {
|
|
478
|
-
const
|
|
479
|
-
if (!
|
|
488
|
+
releasePtyPause(client, sessionId) {
|
|
489
|
+
const subscription = client.ptySubscriptions.get(sessionId);
|
|
490
|
+
if (!subscription?.paused)
|
|
480
491
|
return;
|
|
481
|
-
|
|
492
|
+
subscription.paused = false;
|
|
482
493
|
const next = Math.max(0, (this.ptyPauseRefs.get(sessionId) ?? 1) - 1);
|
|
483
494
|
if (next === 0) {
|
|
484
495
|
this.ptyPauseRefs.delete(sessionId);
|
|
@@ -488,6 +499,11 @@ export class WsBroadcastManager {
|
|
|
488
499
|
this.ptyPauseRefs.set(sessionId, next);
|
|
489
500
|
}
|
|
490
501
|
}
|
|
502
|
+
releaseAllPtyPauses(client) {
|
|
503
|
+
for (const sessionId of client.ptySubscriptions.keys()) {
|
|
504
|
+
this.releasePtyPause(client, sessionId);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
491
507
|
sendPtyError(client, sessionId, error) {
|
|
492
508
|
if (client.ws.readyState !== WebSocket.OPEN)
|
|
493
509
|
return;
|