@camerontaylor/paseo-client 0.8.0-fork.1 → 0.9.0-beta.2.fork.1

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.
@@ -1,107 +1,60 @@
1
- import { decodeTerminalSnapshotPayload, encodeTerminalResizePayload, encodeTerminalStreamFrame, TerminalStreamOpcode, } from "@camerontaylor/paseo-protocol/binary-frames/index";
1
+ import { decodeTerminalSnapshotPayload, TerminalStreamOpcode, } from "@camerontaylor/paseo-protocol/binary-frames/index";
2
2
  export class TerminalStreamRouter {
3
3
  constructor() {
4
- this.terminalSlots = new Map();
5
- this.slotTerminals = new Map();
4
+ this.slots = new Map();
6
5
  this.listeners = new Set();
7
6
  }
7
+ /** Passive wire diagnostics. App state consumes the registration's callback. */
8
8
  onEvent(handler) {
9
9
  this.listeners.add(handler);
10
10
  return () => {
11
11
  this.listeners.delete(handler);
12
12
  };
13
13
  }
14
- setSlot(terminalId, slot) {
15
- const existingTerminalId = this.slotTerminals.get(slot);
16
- if (existingTerminalId && existingTerminalId !== terminalId) {
17
- this.terminalSlots.delete(existingTerminalId);
18
- }
19
- const existingSlot = this.terminalSlots.get(terminalId);
20
- if (typeof existingSlot === "number" && existingSlot !== slot) {
21
- this.slotTerminals.delete(existingSlot);
22
- }
23
- this.terminalSlots.set(terminalId, slot);
24
- this.slotTerminals.set(slot, terminalId);
25
- }
26
- removeTerminal(terminalId) {
27
- const slot = this.terminalSlots.get(terminalId);
28
- if (typeof slot !== "number") {
29
- return;
30
- }
31
- this.terminalSlots.delete(terminalId);
32
- if (this.slotTerminals.get(slot) === terminalId) {
33
- this.slotTerminals.delete(slot);
34
- }
14
+ attach(subscriptionId, terminalId, slot, receive) {
15
+ const registration = { subscriptionId, terminalId, receive };
16
+ const registrations = this.slots.get(slot) ?? new Set();
17
+ registrations.add(registration);
18
+ this.slots.set(slot, registrations);
19
+ return () => {
20
+ registrations.delete(registration);
21
+ if (registrations.size === 0)
22
+ this.slots.delete(slot);
23
+ };
35
24
  }
36
25
  clearSlots() {
37
- this.terminalSlots.clear();
38
- this.slotTerminals.clear();
39
- }
40
- encodeInput(terminalId, message) {
41
- const slot = this.terminalSlots.get(terminalId);
42
- if (typeof slot !== "number") {
43
- return null;
44
- }
45
- if (message.type === "input") {
46
- return encodeTerminalStreamFrame({
47
- opcode: TerminalStreamOpcode.Input,
48
- slot,
49
- payload: message.data,
50
- });
51
- }
52
- if (message.type === "resize") {
53
- return encodeTerminalStreamFrame({
54
- opcode: TerminalStreamOpcode.Resize,
55
- slot,
56
- payload: encodeTerminalResizePayload({
57
- rows: message.rows,
58
- cols: message.cols,
59
- ...(message.intent ? { intent: message.intent } : {}),
60
- }),
61
- });
62
- }
63
- return null;
26
+ this.slots.clear();
64
27
  }
65
28
  handleFrame(frame) {
66
- const terminalId = this.slotTerminals.get(frame.slot);
67
- if (!terminalId) {
68
- return;
69
- }
70
- if (frame.opcode === TerminalStreamOpcode.Output) {
71
- this.emit({
72
- terminalId,
73
- type: "output",
74
- data: frame.payload,
75
- });
29
+ const registrations = this.slots.get(frame.slot);
30
+ if (!registrations)
76
31
  return;
77
- }
78
- if (frame.opcode === TerminalStreamOpcode.Restore) {
79
- this.emit({
80
- terminalId,
81
- type: "restore",
82
- data: frame.payload,
83
- });
84
- return;
85
- }
86
- if (frame.opcode === TerminalStreamOpcode.Snapshot) {
87
- const state = decodeTerminalSnapshotPayload(frame.payload);
88
- if (!state) {
89
- return;
32
+ for (const registration of registrations) {
33
+ const identity = {
34
+ subscriptionId: registration.subscriptionId,
35
+ terminalId: registration.terminalId,
36
+ };
37
+ let event;
38
+ if (frame.opcode === TerminalStreamOpcode.Output)
39
+ event = { ...identity, type: "output", data: frame.payload };
40
+ else if (frame.opcode === TerminalStreamOpcode.Restore)
41
+ event = { ...identity, type: "restore", data: frame.payload };
42
+ else if (frame.opcode === TerminalStreamOpcode.Snapshot) {
43
+ const state = decodeTerminalSnapshotPayload(frame.payload);
44
+ if (!state)
45
+ return;
46
+ event = { ...identity, type: "snapshot", state };
90
47
  }
91
- this.emit({
92
- terminalId,
93
- type: "snapshot",
94
- state,
95
- });
96
- }
97
- }
98
- emit(event) {
99
- for (const listener of this.listeners) {
100
- try {
101
- listener(event);
102
- }
103
- catch {
104
- // no-op
48
+ else
49
+ return;
50
+ registration.receive(event);
51
+ for (const listener of this.listeners) {
52
+ try {
53
+ listener(event);
54
+ }
55
+ catch {
56
+ /* Passive diagnostics cannot interrupt delivery. */
57
+ }
105
58
  }
106
59
  }
107
60
  }
@@ -0,0 +1,25 @@
1
+ import type { SessionOutboundMessage } from "@camerontaylor/paseo-protocol/messages";
2
+ import type { OwnedSubscription, TimelineSubscription } from "../connection/index.js";
3
+ type TimelineUpdate = Extract<SessionOutboundMessage, {
4
+ type: "agent_stream" | "agent.timeline.replacement";
5
+ }>;
6
+ /** Local SDK lifecycle messages; these are not additional wire RPCs. */
7
+ export type TimelineMessage = TimelineUpdate | {
8
+ type: "agent.timeline.subscription_restored";
9
+ payload: {
10
+ agentId: string;
11
+ subscriptionId: string;
12
+ };
13
+ } | {
14
+ type: "agent.timeline.error";
15
+ payload: {
16
+ agentId: string;
17
+ error: string;
18
+ };
19
+ };
20
+ /** Restores live delivery; the consumer owns history reads and gap recovery. */
21
+ export declare function subscribeTimeline(agentId: string, observation: OwnedSubscription<{
22
+ agentIds: string[];
23
+ }>, handler: (message: TimelineMessage) => void, reportError: (error: unknown) => void): TimelineSubscription;
24
+ export {};
25
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,56 @@
1
+ /** Restores live delivery; the consumer owns history reads and gap recovery. */
2
+ export function subscribeTimeline(agentId, observation, handler, reportError) {
3
+ let established = false;
4
+ let released = false;
5
+ const notify = (message) => {
6
+ if (released)
7
+ return;
8
+ try {
9
+ handler(message);
10
+ }
11
+ catch (error) {
12
+ reportError(error);
13
+ }
14
+ };
15
+ const release = () => {
16
+ released = true;
17
+ return observation.release();
18
+ };
19
+ const stop = () => {
20
+ // Use the public release entry point so the owning API also forgets this handle.
21
+ void subscription.release().catch(reportError);
22
+ };
23
+ const fail = (error) => {
24
+ notify({
25
+ type: "agent.timeline.error",
26
+ payload: { agentId, error: error instanceof Error ? error.message : String(error) },
27
+ });
28
+ stop();
29
+ };
30
+ observation.subscribe({
31
+ snapshot: ({ subscriptionId }) => {
32
+ if (!established) {
33
+ established = true;
34
+ return;
35
+ }
36
+ notify({
37
+ type: "agent.timeline.subscription_restored",
38
+ payload: { agentId, subscriptionId },
39
+ });
40
+ },
41
+ update: (message) => {
42
+ if (message.type === "agent_stream" || message.type === "agent.timeline.replacement")
43
+ notify(message);
44
+ },
45
+ error: fail,
46
+ });
47
+ const subscription = Object.assign(stop, {
48
+ ready: observation.ready.then(() => undefined),
49
+ release,
50
+ subscriptionId: null,
51
+ });
52
+ Object.defineProperty(subscription, "subscriptionId", { get: () => observation.subscriptionId });
53
+ void subscription.ready.catch(fail);
54
+ return subscription;
55
+ }
56
+ //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camerontaylor/paseo-client",
3
- "version": "0.8.0-fork.1",
3
+ "version": "0.9.0-beta.2.fork.1",
4
4
  "description": "Paseo client SDK package",
5
5
  "files": [
6
6
  "dist",
@@ -39,13 +39,17 @@
39
39
  "test": "vitest run"
40
40
  },
41
41
  "dependencies": {
42
- "@camerontaylor/paseo-protocol": "0.8.0-fork.1",
43
- "@camerontaylor/paseo-relay": "0.8.0-fork.1",
42
+ "@camerontaylor/paseo-protocol": "0.9.0-beta.2.fork.1",
43
+ "@camerontaylor/paseo-relay": "0.9.0-beta.2.fork.1",
44
44
  "zod": "^4.4.3"
45
45
  },
46
46
  "devDependencies": {
47
47
  "@types/node": "^20.9.0",
48
48
  "typescript": "^5.2.2",
49
49
  "vitest": "^4.1.6"
50
+ },
51
+ "repository": {
52
+ "type": "git",
53
+ "url": "https://github.com/camerontaylor/paseo"
50
54
  }
51
55
  }