MCPcopy Create free account
hub / github.com/TraderAlice/OpenAlice / attach

Method attach

src/workspaces/persistent-session.ts:305–394  ·  view source on GitHub ↗

Swap in `ws` as the attached client. A controller claim makes ownership * explicit: a second controller is rejected unless it deliberately takes * over. This is the shared rule for browser, Electron IPC, future IM bridges, * and any debug client that can write to the PTY.

(
    ws: WebSocket,
    cols: number,
    rows: number,
    since: number | undefined,
    claim?: SessionControllerClaim,
  )

Source from the content-addressed store, hash-verified

303 * over. This is the shared rule for browser, Electron IPC, future IM bridges,
304 * and any debug client that can write to the PTY. */
305 attach(
306 ws: WebSocket,
307 cols: number,
308 rows: number,
309 since: number | undefined,
310 claim?: SessionControllerClaim,
311 ): SessionAttachResult {
312 if (this.disposed) {
313 try {
314 ws.close(1011, 'session disposed');
315 } catch {
316 // ignore
317 }
318 return { ok: false, reason: 'locked', owner: this.controller ?? { id: 'disposed', kind: 'session' } };
319 }
320
321 const owner = normalizeClaim(claim);
322 if (
323 owner &&
324 this.ws !== null &&
325 this.controller !== null &&
326 this.controller.id !== owner.id &&
327 !claim?.takeover
328 ) {
329 try {
330 ws.close(4409, 'session locked by another controller');
331 } catch {
332 // ignore
333 }
334 return { ok: false, reason: 'locked', owner: this.controller };
335 }
336
337 // Kick previous client.
338 if (this.ws !== null && this.ws !== ws) {
339 const prev = this.ws;
340 this.unwireWs(prev);
341 this.ws = null;
342 try {
343 prev.close(4001, 'kicked by new attach');
344 } catch {
345 // ignore
346 }
347 }
348
349 this.ws = ws;
350 this.controller = owner;
351 // A previous client may have dropped mid-backpressure, leaving the PTY
352 // paused at the OS level. Clearing only the flag (not resuming the term)
353 // would strand it paused forever; resumePty() un-sticks both.
354 this.resumePty();
355 this.resize(cols, rows);
356
357 // Compute replay window. Cold attach (since=undefined) replays the full
358 // buffer — without that, a fresh browser tab on a workspace where the
359 // agent is already idle would just see a black void instead of the prompt
360 // and recent output. Hot attach (since=N) only fills in what was missed.
361 const requested = since ?? 0;
362 const slice = this.buffer.since(requested);

Callers 2

attachByIdMethod · 0.80

Calls 10

unwireWsMethod · 0.95
resumePtyMethod · 0.95
resizeMethod · 0.95
wireWsMethod · 0.95
startCursorTimerMethod · 0.95
normalizeClaimFunction · 0.85
sinceMethod · 0.80
eventMethod · 0.80
closeMethod · 0.65
sendMethod · 0.65

Tested by

no test coverage detected