@yefengr/pi-reach 0.0.0-stage → 0.0.2

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.
Files changed (129) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +110 -2
  3. package/dist/actions/handlers.d.ts +143 -0
  4. package/dist/actions/handlers.js +200 -0
  5. package/dist/actions/handlers.js.map +1 -0
  6. package/dist/actions/registry.d.ts +39 -0
  7. package/dist/actions/registry.js +58 -0
  8. package/dist/actions/registry.js.map +1 -0
  9. package/dist/actions/session_new_bridge.d.ts +35 -0
  10. package/dist/actions/session_new_bridge.js +88 -0
  11. package/dist/actions/session_new_bridge.js.map +1 -0
  12. package/dist/commands.d.ts +29 -0
  13. package/dist/commands.js +169 -0
  14. package/dist/commands.js.map +1 -0
  15. package/dist/config.d.ts +61 -0
  16. package/dist/config.js +115 -0
  17. package/dist/config.js.map +1 -0
  18. package/dist/index.d.ts +27 -0
  19. package/dist/index.js +618 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/pairing/crypto.d.ts +15 -0
  22. package/dist/pairing/crypto.js +72 -0
  23. package/dist/pairing/crypto.js.map +1 -0
  24. package/dist/pairing/identity-keyring.d.ts +22 -0
  25. package/dist/pairing/identity-keyring.js +94 -0
  26. package/dist/pairing/identity-keyring.js.map +1 -0
  27. package/dist/pairing/identity-lock.d.ts +14 -0
  28. package/dist/pairing/identity-lock.js +116 -0
  29. package/dist/pairing/identity-lock.js.map +1 -0
  30. package/dist/pairing/owner_storage.d.ts +61 -0
  31. package/dist/pairing/owner_storage.js +263 -0
  32. package/dist/pairing/owner_storage.js.map +1 -0
  33. package/dist/pairing/qr.d.ts +39 -0
  34. package/dist/pairing/qr.js +113 -0
  35. package/dist/pairing/qr.js.map +1 -0
  36. package/dist/pairing/storage.d.ts +36 -0
  37. package/dist/pairing/storage.js +263 -0
  38. package/dist/pairing/storage.js.map +1 -0
  39. package/dist/protocol/types.d.ts +225 -0
  40. package/dist/protocol/types.js +2 -0
  41. package/dist/protocol/types.js.map +1 -0
  42. package/dist/protocol/v2/codec.d.ts +6 -0
  43. package/dist/protocol/v2/codec.js +4 -0
  44. package/dist/protocol/v2/codec.js.map +1 -0
  45. package/dist/protocol/v2/index.d.ts +4 -0
  46. package/dist/protocol/v2/index.js +5 -0
  47. package/dist/protocol/v2/index.js.map +1 -0
  48. package/dist/protocol/v2/marker.d.ts +45 -0
  49. package/dist/protocol/v2/marker.js +47 -0
  50. package/dist/protocol/v2/marker.js.map +1 -0
  51. package/dist/protocol/v2/schemas.d.ts +3 -0
  52. package/dist/protocol/v2/schemas.js +4 -0
  53. package/dist/protocol/v2/schemas.js.map +1 -0
  54. package/dist/protocol/v2/session_state.d.ts +79 -0
  55. package/dist/protocol/v2/session_state.js +166 -0
  56. package/dist/protocol/v2/session_state.js.map +1 -0
  57. package/dist/runtime/endpoint_metadata.d.ts +14 -0
  58. package/dist/runtime/endpoint_metadata.js +18 -0
  59. package/dist/runtime/endpoint_metadata.js.map +1 -0
  60. package/dist/runtime/extension_owner.d.ts +12 -0
  61. package/dist/runtime/extension_owner.js +80 -0
  62. package/dist/runtime/extension_owner.js.map +1 -0
  63. package/dist/runtime/footer.d.ts +6 -0
  64. package/dist/runtime/footer.js +21 -0
  65. package/dist/runtime/footer.js.map +1 -0
  66. package/dist/runtime/owner_router.d.ts +16 -0
  67. package/dist/runtime/owner_router.js +41 -0
  68. package/dist/runtime/owner_router.js.map +1 -0
  69. package/dist/runtime/pairing_coordinator.d.ts +42 -0
  70. package/dist/runtime/pairing_coordinator.js +136 -0
  71. package/dist/runtime/pairing_coordinator.js.map +1 -0
  72. package/dist/runtime/relay_lifecycle.d.ts +64 -0
  73. package/dist/runtime/relay_lifecycle.js +275 -0
  74. package/dist/runtime/relay_lifecycle.js.map +1 -0
  75. package/dist/session/local_config.d.ts +7 -0
  76. package/dist/session/local_config.js +52 -0
  77. package/dist/session/local_config.js.map +1 -0
  78. package/dist/timeline/history.d.ts +30 -0
  79. package/dist/timeline/history.js +170 -0
  80. package/dist/timeline/history.js.map +1 -0
  81. package/dist/timeline/partial_delta.d.ts +2 -0
  82. package/dist/timeline/partial_delta.js +16 -0
  83. package/dist/timeline/partial_delta.js.map +1 -0
  84. package/dist/timeline/run_end.d.ts +16 -0
  85. package/dist/timeline/run_end.js +23 -0
  86. package/dist/timeline/run_end.js.map +1 -0
  87. package/dist/timeline/runtime.d.ts +89 -0
  88. package/dist/timeline/runtime.js +546 -0
  89. package/dist/timeline/runtime.js.map +1 -0
  90. package/dist/timeline/sequence.d.ts +7 -0
  91. package/dist/timeline/sequence.js +16 -0
  92. package/dist/timeline/sequence.js.map +1 -0
  93. package/dist/timeline/tool_lifecycle.d.ts +36 -0
  94. package/dist/timeline/tool_lifecycle.js +171 -0
  95. package/dist/timeline/tool_lifecycle.js.map +1 -0
  96. package/dist/timeline/user_delivery.d.ts +96 -0
  97. package/dist/timeline/user_delivery.js +360 -0
  98. package/dist/timeline/user_delivery.js.map +1 -0
  99. package/dist/timeline/user_delivery_binding.d.ts +43 -0
  100. package/dist/timeline/user_delivery_binding.js +107 -0
  101. package/dist/timeline/user_delivery_binding.js.map +1 -0
  102. package/dist/timeline/v2_service.d.ts +82 -0
  103. package/dist/timeline/v2_service.js +512 -0
  104. package/dist/timeline/v2_service.js.map +1 -0
  105. package/dist/transport/peer_channel.d.ts +34 -0
  106. package/dist/transport/peer_channel.js +116 -0
  107. package/dist/transport/peer_channel.js.map +1 -0
  108. package/dist/transport/relay_client.d.ts +54 -0
  109. package/dist/transport/relay_client.js +270 -0
  110. package/dist/transport/relay_client.js.map +1 -0
  111. package/dist/vendor/protocol/constants.d.ts +4 -0
  112. package/dist/vendor/protocol/constants.js +4 -0
  113. package/dist/vendor/protocol/encoding.d.ts +9 -0
  114. package/dist/vendor/protocol/encoding.js +66 -0
  115. package/dist/vendor/protocol/outer/codec.d.ts +13 -0
  116. package/dist/vendor/protocol/outer/codec.js +105 -0
  117. package/dist/vendor/protocol/outer/index.d.ts +4 -0
  118. package/dist/vendor/protocol/outer/index.js +4 -0
  119. package/dist/vendor/protocol/outer/types.d.ts +120 -0
  120. package/dist/vendor/protocol/outer/types.js +1 -0
  121. package/dist/vendor/protocol/session/codec.d.ts +41 -0
  122. package/dist/vendor/protocol/session/codec.js +236 -0
  123. package/dist/vendor/protocol/session/frames.d.ts +5220 -0
  124. package/dist/vendor/protocol/session/frames.js +83 -0
  125. package/dist/vendor/protocol/session/index.d.ts +3 -0
  126. package/dist/vendor/protocol/session/index.js +3 -0
  127. package/dist/vendor/protocol/session/schema.d.ts +858 -0
  128. package/dist/vendor/protocol/session/schema.js +256 -0
  129. package/package.json +74 -3
@@ -0,0 +1,136 @@
1
+ import { V2PeerChannel } from "../transport/peer_channel.js";
2
+ /** Coordinates one-process pairing retries across storage and Relay lifecycles. */
3
+ export class PairingCoordinator {
4
+ deps;
5
+ attempts = new Map();
6
+ constructor(deps) {
7
+ this.deps = deps;
8
+ }
9
+ async handle(relay, ownerId, frame) {
10
+ if (!this.deps.isRelayCurrent(relay))
11
+ return;
12
+ const key = this.attemptKey(ownerId, frame.id);
13
+ const pending = this.attempts.get(key);
14
+ if (pending) {
15
+ await pending.completion;
16
+ return this.handle(relay, ownerId, frame);
17
+ }
18
+ const reservation = this.deps.qrSession.reserveCode(frame.code, ownerId, frame.id);
19
+ if (reservation.status === "expired" || reservation.status === "consumed" || reservation.status === "unknown") {
20
+ const code = reservation.status === "expired" ? "token_expired" : reservation.status === "consumed" ? "token_consumed" : "token_unknown";
21
+ this.reject(relay, ownerId, frame, code, reservation.status === "consumed" ? "Pairing code is already in use" : "Pairing code is invalid or expired");
22
+ return;
23
+ }
24
+ if (reservation.status === "committed") {
25
+ const activeBinding = this.deps.activeBinding(ownerId);
26
+ if (activeBinding) {
27
+ activeBinding.channel.sendV2(reservation.completion);
28
+ return;
29
+ }
30
+ const replayBinding = this.deps.attachOwner(relay, ownerId);
31
+ replayBinding?.channel.sendV2(reservation.completion);
32
+ return;
33
+ }
34
+ if (reservation.status !== "reserved")
35
+ return;
36
+ const binding = this.deps.attachOwner(relay, ownerId);
37
+ if (!binding) {
38
+ this.deps.qrSession.releaseCode(reservation.reservation);
39
+ return;
40
+ }
41
+ let resolveCompletion;
42
+ const completion = new Promise((resolve) => { resolveCompletion = resolve; });
43
+ const attempt = {
44
+ key,
45
+ ownerId,
46
+ relay,
47
+ binding,
48
+ reservation: reservation.reservation,
49
+ completion,
50
+ resolveCompletion,
51
+ };
52
+ this.attempts.set(key, attempt);
53
+ void this.run(attempt, frame);
54
+ }
55
+ abandonInactive() {
56
+ for (const attempt of [...this.attempts.values()]) {
57
+ if (this.lifecycleIsCurrent(attempt))
58
+ continue;
59
+ this.attempts.delete(attempt.key);
60
+ this.deps.qrSession.releaseCode(attempt.reservation);
61
+ attempt.resolveCompletion(null);
62
+ }
63
+ }
64
+ async run(attempt, frame) {
65
+ let completion = null;
66
+ let receipt;
67
+ try {
68
+ receipt = await this.deps.addPeer({ name: frame.device_name, remote_epk: attempt.ownerId, paired_at: new Date().toISOString() });
69
+ if (!this.canCommit(attempt)) {
70
+ await this.rollback(receipt);
71
+ return;
72
+ }
73
+ // peers.json is durable authority, but do not confirm pairing while the
74
+ // current Relay has explicitly rejected the ACL frame. A later retry of
75
+ // the same request can safely repeat the idempotent write.
76
+ const aclSent = await this.deps.updateEndpoint(attempt.relay);
77
+ if (!aclSent || !this.canCommit(attempt)) {
78
+ await this.rollback(receipt);
79
+ return;
80
+ }
81
+ const pairOk = this.deps.buildPairOk(frame);
82
+ if (!this.deps.qrSession.commitCode(attempt.reservation, pairOk)) {
83
+ await this.rollback(receipt);
84
+ return;
85
+ }
86
+ completion = pairOk;
87
+ attempt.binding.channel.sendV2(pairOk);
88
+ }
89
+ catch {
90
+ if (receipt)
91
+ await this.rollback(receipt);
92
+ if (this.lifecycleIsCurrent(attempt)) {
93
+ this.sendError(attempt.binding.channel, frame, "internal_error", "Failed to persist pairing");
94
+ }
95
+ }
96
+ finally {
97
+ if (!completion)
98
+ this.deps.qrSession.releaseCode(attempt.reservation);
99
+ if (this.attempts.get(attempt.key) === attempt)
100
+ this.attempts.delete(attempt.key);
101
+ attempt.resolveCompletion(completion);
102
+ }
103
+ }
104
+ async rollback(receipt) {
105
+ try {
106
+ await this.deps.rollbackPeer(receipt);
107
+ }
108
+ catch {
109
+ // A failed compensation leaves peers.json authoritative.
110
+ }
111
+ // A new Relay may have announced the transient write while this old
112
+ // attempt was pending. Always refresh whichever Relay is current now.
113
+ try {
114
+ await this.deps.refreshCurrentEndpoint();
115
+ }
116
+ catch { /* reconnect owns recovery */ }
117
+ }
118
+ lifecycleIsCurrent(attempt) {
119
+ return this.deps.isRelayCurrent(attempt.relay) && this.deps.activeBinding(attempt.ownerId) === attempt.binding;
120
+ }
121
+ canCommit(attempt) {
122
+ return this.lifecycleIsCurrent(attempt) && this.deps.qrSession.isReservationCurrent(attempt.reservation);
123
+ }
124
+ reject(relay, ownerId, frame, code, message) {
125
+ const channel = new V2PeerChannel(relay, ownerId, this.deps.routeIdentity(), () => undefined);
126
+ this.sendError(channel, frame, code, message);
127
+ channel.detach();
128
+ }
129
+ sendError(channel, frame, code, message) {
130
+ channel.sendV2({ protocol_version: 2, type: "pair_error", in_reply_to: frame.id, code, message });
131
+ }
132
+ attemptKey(ownerId, requestId) {
133
+ return `${ownerId}\u0000${requestId}`;
134
+ }
135
+ }
136
+ //# sourceMappingURL=pairing_coordinator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pairing_coordinator.js","sourceRoot":"","sources":["../../src/runtime/pairing_coordinator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
2
+ import type { RelayResolution } from "../config.js";
3
+ import type { Ed25519Keypair } from "../pairing/crypto.js";
4
+ import type { HostConnectOptions, RelayClient } from "../transport/relay_client.js";
5
+ export type RemoteState = "idle" | "started";
6
+ export type RelayConnectivity = "connected" | "reconnecting" | "disconnected";
7
+ export type RelayDisplayStatus = "off" | "connecting" | "connected" | "reconnecting" | "failed";
8
+ export type InitialRelayResult = "completed" | "cancelled";
9
+ export type RelayStartContext = Pick<ExtensionContext, "ui" | "cwd">;
10
+ export interface RelayLifecycleDependencies {
11
+ loadIdentity(): Promise<Ed25519Keypair>;
12
+ resolveRelayUrl(): RelayResolution;
13
+ createClient(relayUrl: string, identity: Ed25519Keypair): RelayClient;
14
+ buildConnectOptions(cwd?: string): Promise<HostConnectOptions>;
15
+ handleIdentityError(error: unknown, ctx: RelayStartContext): boolean;
16
+ describeConnection(resolution: RelayResolution): string;
17
+ onStateChange(): void;
18
+ onDisplayStatusChange(): void;
19
+ onConnected(client: RelayClient, ctx?: RelayStartContext): void;
20
+ onDisconnected(): void;
21
+ }
22
+ export declare class RelayLifecycle {
23
+ private readonly deps;
24
+ private activeRelay;
25
+ private pendingCandidate;
26
+ private currentRelayUrl;
27
+ private identity;
28
+ private initialAttempt;
29
+ private cancelInitialAttempt;
30
+ private reconnectTimer;
31
+ private reconnectAttempt;
32
+ private generation;
33
+ private isDisposed;
34
+ private currentState;
35
+ private currentDisplayStatus;
36
+ private readonly closedCandidates;
37
+ constructor(deps: RelayLifecycleDependencies);
38
+ get state(): RemoteState;
39
+ get relay(): RelayClient | null;
40
+ get relayUrl(): string | null;
41
+ get keypair(): Ed25519Keypair | null;
42
+ get disposed(): boolean;
43
+ get hasPendingReconnect(): boolean;
44
+ get status(): RelayConnectivity;
45
+ get displayStatus(): RelayDisplayStatus;
46
+ isCurrent(candidate: RelayClient): boolean;
47
+ setDisposed(value: boolean): void;
48
+ start(ctx: RelayStartContext): Promise<InitialRelayResult>;
49
+ waitForInitial(): Promise<InitialRelayResult>;
50
+ stop(): void;
51
+ private startInitial;
52
+ private scheduleReconnect;
53
+ private reconnect;
54
+ private onRelayClose;
55
+ private handleConnectionFailure;
56
+ private markInitialFailure;
57
+ private setDisplayStatus;
58
+ private clearCandidate;
59
+ private closeCandidateOnce;
60
+ private clearInitialAttempt;
61
+ private isOperationCurrent;
62
+ private isReconnectCurrent;
63
+ private notify;
64
+ }
@@ -0,0 +1,275 @@
1
+ const RECONNECT_BACKOFFS_MS = [1_000, 2_000, 5_000, 10_000, 30_000];
2
+ export class RelayLifecycle {
3
+ deps;
4
+ activeRelay = null;
5
+ pendingCandidate = null;
6
+ currentRelayUrl = null;
7
+ identity = null;
8
+ initialAttempt = null;
9
+ cancelInitialAttempt = null;
10
+ reconnectTimer = null;
11
+ reconnectAttempt = 0;
12
+ generation = 0;
13
+ isDisposed = false;
14
+ currentState = "idle";
15
+ currentDisplayStatus = "off";
16
+ closedCandidates = new WeakSet();
17
+ constructor(deps) {
18
+ this.deps = deps;
19
+ }
20
+ get state() { return this.currentState; }
21
+ get relay() { return this.activeRelay; }
22
+ get relayUrl() { return this.currentRelayUrl; }
23
+ get keypair() { return this.identity; }
24
+ get disposed() { return this.isDisposed; }
25
+ get hasPendingReconnect() { return this.reconnectTimer !== null; }
26
+ get status() {
27
+ if (this.currentState === "idle")
28
+ return "disconnected";
29
+ return this.activeRelay?.isOpen() ? "connected" : "reconnecting";
30
+ }
31
+ get displayStatus() { return this.currentDisplayStatus; }
32
+ isCurrent(candidate) {
33
+ return !this.isDisposed && this.currentState === "started" && this.activeRelay === candidate;
34
+ }
35
+ setDisposed(value) {
36
+ this.isDisposed = value;
37
+ }
38
+ start(ctx) {
39
+ const existing = this.initialAttempt;
40
+ if (existing)
41
+ return existing;
42
+ if (this.currentState !== "idle") {
43
+ const message = this.status === "reconnecting"
44
+ ? "[pi-reach] Relay is reconnecting in background."
45
+ : "[pi-reach] Already connected.";
46
+ this.notify(ctx, message, "warning");
47
+ return Promise.resolve("completed");
48
+ }
49
+ const expectedGeneration = ++this.generation;
50
+ this.setDisplayStatus("connecting");
51
+ const operation = this.startInitial(ctx, expectedGeneration).catch((error) => {
52
+ this.markInitialFailure(expectedGeneration);
53
+ throw error;
54
+ });
55
+ let cancel;
56
+ const cancellation = new Promise((resolve) => {
57
+ cancel = () => resolve("cancelled");
58
+ });
59
+ const attempt = Promise.race([operation.then(() => "completed"), cancellation]);
60
+ this.initialAttempt = attempt;
61
+ this.cancelInitialAttempt = cancel;
62
+ void operation.catch(() => undefined);
63
+ void attempt.then(() => this.clearInitialAttempt(attempt, cancel), () => this.clearInitialAttempt(attempt, cancel));
64
+ return attempt;
65
+ }
66
+ waitForInitial() {
67
+ return this.initialAttempt ?? Promise.resolve("completed");
68
+ }
69
+ stop() {
70
+ this.generation += 1;
71
+ if (this.reconnectTimer) {
72
+ clearTimeout(this.reconnectTimer);
73
+ this.reconnectTimer = null;
74
+ }
75
+ this.reconnectAttempt = 0;
76
+ const active = this.activeRelay;
77
+ const pending = this.pendingCandidate;
78
+ const cancel = this.cancelInitialAttempt;
79
+ this.activeRelay = null;
80
+ this.pendingCandidate = null;
81
+ this.currentRelayUrl = null;
82
+ this.initialAttempt = null;
83
+ this.cancelInitialAttempt = null;
84
+ this.currentState = "idle";
85
+ this.setDisplayStatus("off");
86
+ cancel?.();
87
+ if (active)
88
+ this.closeCandidateOnce(active);
89
+ if (pending && pending !== active)
90
+ this.closeCandidateOnce(pending);
91
+ this.deps.onDisconnected();
92
+ this.deps.onStateChange();
93
+ }
94
+ async startInitial(ctx, expectedGeneration) {
95
+ let identity;
96
+ try {
97
+ identity = await this.deps.loadIdentity();
98
+ }
99
+ catch (error) {
100
+ if (!this.isOperationCurrent(expectedGeneration))
101
+ return;
102
+ if (this.deps.handleIdentityError(error, ctx)) {
103
+ this.setDisplayStatus("failed");
104
+ return;
105
+ }
106
+ throw error;
107
+ }
108
+ if (!this.isOperationCurrent(expectedGeneration))
109
+ return;
110
+ this.identity = identity;
111
+ const resolution = this.deps.resolveRelayUrl();
112
+ this.currentRelayUrl = resolution.url;
113
+ this.currentState = "started";
114
+ this.deps.onStateChange();
115
+ this.notify(ctx, this.deps.describeConnection(resolution));
116
+ let options;
117
+ try {
118
+ options = await this.deps.buildConnectOptions(ctx.cwd);
119
+ }
120
+ catch (error) {
121
+ this.handleConnectionFailure(expectedGeneration, resolution.url, ctx, error);
122
+ return;
123
+ }
124
+ if (!this.isReconnectCurrent(expectedGeneration))
125
+ return;
126
+ const candidate = this.deps.createClient(resolution.url, identity);
127
+ this.pendingCandidate = candidate;
128
+ try {
129
+ await candidate.connect(options);
130
+ }
131
+ catch (error) {
132
+ this.clearCandidate(candidate, true);
133
+ this.handleConnectionFailure(expectedGeneration, resolution.url, ctx, error);
134
+ return;
135
+ }
136
+ if (!this.isReconnectCurrent(expectedGeneration) || this.pendingCandidate !== candidate) {
137
+ this.closeCandidateOnce(candidate);
138
+ return;
139
+ }
140
+ this.pendingCandidate = null;
141
+ this.activeRelay = candidate;
142
+ this.reconnectAttempt = 0;
143
+ this.setDisplayStatus("connected");
144
+ candidate.on("close", () => this.onRelayClose(candidate));
145
+ this.deps.onConnected(candidate, ctx);
146
+ this.deps.onStateChange();
147
+ }
148
+ scheduleReconnect(url) {
149
+ if (!this.identity || this.isDisposed || this.currentState !== "started")
150
+ return;
151
+ if (this.reconnectTimer)
152
+ clearTimeout(this.reconnectTimer);
153
+ const expectedGeneration = ++this.generation;
154
+ const delay = RECONNECT_BACKOFFS_MS[Math.min(this.reconnectAttempt++, RECONNECT_BACKOFFS_MS.length - 1)];
155
+ this.reconnectTimer = setTimeout(() => {
156
+ this.reconnectTimer = null;
157
+ if (!this.isReconnectCurrent(expectedGeneration))
158
+ return;
159
+ void this.reconnect(url, expectedGeneration);
160
+ }, delay);
161
+ }
162
+ async reconnect(url, expectedGeneration) {
163
+ const identity = this.identity;
164
+ if (!identity || !this.isReconnectCurrent(expectedGeneration))
165
+ return;
166
+ let options;
167
+ try {
168
+ options = await this.deps.buildConnectOptions();
169
+ }
170
+ catch {
171
+ if (this.isReconnectCurrent(expectedGeneration)) {
172
+ this.deps.onStateChange();
173
+ this.scheduleReconnect(url);
174
+ }
175
+ return;
176
+ }
177
+ if (!this.isReconnectCurrent(expectedGeneration) || this.identity !== identity)
178
+ return;
179
+ const candidate = this.deps.createClient(url, identity);
180
+ this.pendingCandidate = candidate;
181
+ try {
182
+ await candidate.connect(options);
183
+ }
184
+ catch {
185
+ this.clearCandidate(candidate, true);
186
+ if (this.isReconnectCurrent(expectedGeneration)) {
187
+ this.deps.onStateChange();
188
+ this.scheduleReconnect(url);
189
+ }
190
+ return;
191
+ }
192
+ if (!this.isReconnectCurrent(expectedGeneration) || this.pendingCandidate !== candidate) {
193
+ this.closeCandidateOnce(candidate);
194
+ return;
195
+ }
196
+ this.pendingCandidate = null;
197
+ this.activeRelay = candidate;
198
+ this.reconnectAttempt = 0;
199
+ this.setDisplayStatus("connected");
200
+ candidate.on("close", () => this.onRelayClose(candidate));
201
+ this.deps.onConnected(candidate);
202
+ this.deps.onStateChange();
203
+ }
204
+ onRelayClose(closed) {
205
+ if (closed !== this.activeRelay || this.currentState === "idle")
206
+ return;
207
+ this.activeRelay = null;
208
+ this.setDisplayStatus("reconnecting");
209
+ this.deps.onDisconnected();
210
+ this.deps.onStateChange();
211
+ if (this.currentRelayUrl)
212
+ this.scheduleReconnect(this.currentRelayUrl);
213
+ }
214
+ handleConnectionFailure(expectedGeneration, url, ctx, error) {
215
+ if (!this.isReconnectCurrent(expectedGeneration))
216
+ return;
217
+ this.notify(ctx, `[pi-reach] Relay unavailable; reconnecting in background: ${String(error)}`, "warning");
218
+ this.setDisplayStatus("reconnecting");
219
+ this.deps.onStateChange();
220
+ this.scheduleReconnect(url);
221
+ }
222
+ markInitialFailure(expectedGeneration) {
223
+ if (!this.isOperationCurrent(expectedGeneration))
224
+ return;
225
+ const active = this.activeRelay;
226
+ const pending = this.pendingCandidate;
227
+ this.activeRelay = null;
228
+ this.pendingCandidate = null;
229
+ this.currentRelayUrl = null;
230
+ this.currentState = "idle";
231
+ this.reconnectAttempt = 0;
232
+ if (active)
233
+ this.closeCandidateOnce(active);
234
+ if (pending && pending !== active)
235
+ this.closeCandidateOnce(pending);
236
+ this.setDisplayStatus("failed");
237
+ }
238
+ setDisplayStatus(status) {
239
+ this.currentDisplayStatus = status;
240
+ // 本地展示状态不改变既有 Relay 控制事件的发送时机。
241
+ this.deps.onDisplayStatusChange();
242
+ }
243
+ clearCandidate(candidate, close) {
244
+ if (this.pendingCandidate !== candidate)
245
+ return;
246
+ this.pendingCandidate = null;
247
+ if (close)
248
+ this.closeCandidateOnce(candidate);
249
+ }
250
+ closeCandidateOnce(candidate) {
251
+ if (this.closedCandidates.has(candidate))
252
+ return;
253
+ this.closedCandidates.add(candidate);
254
+ candidate.close();
255
+ }
256
+ clearInitialAttempt(attempt, cancel) {
257
+ if (this.initialAttempt === attempt)
258
+ this.initialAttempt = null;
259
+ if (this.cancelInitialAttempt === cancel)
260
+ this.cancelInitialAttempt = null;
261
+ }
262
+ isOperationCurrent(expected) {
263
+ return !this.isDisposed && this.generation === expected;
264
+ }
265
+ isReconnectCurrent(expected) {
266
+ return this.currentState === "started" && this.isOperationCurrent(expected);
267
+ }
268
+ notify(ctx, message, kind = "info") {
269
+ try {
270
+ ctx.ui.notify(message, kind);
271
+ }
272
+ catch { /* stale context */ }
273
+ }
274
+ }
275
+ //# sourceMappingURL=relay_lifecycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relay_lifecycle.js","sourceRoot":"","sources":["../../src/runtime/relay_lifecycle.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export interface LocalConfig {
2
+ agent_name?: string;
3
+ }
4
+ export declare function localConfigExists(cwd: string): boolean;
5
+ export declare function loadLocalConfig(cwd: string): LocalConfig;
6
+ export declare function saveLocalConfig(cwd: string, patch: Partial<LocalConfig>): void;
7
+ export declare function defaultAgentName(cwd: string): string;
@@ -0,0 +1,52 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { basename, dirname, join } from "node:path";
3
+ const LOCAL_DIR = ".pi/pi-reach";
4
+ const LOCAL_FILE = "config.json";
5
+ function pathFor(cwd) {
6
+ return join(cwd, LOCAL_DIR, LOCAL_FILE);
7
+ }
8
+ function parseLocalConfig(raw) {
9
+ let parsed;
10
+ try {
11
+ parsed = JSON.parse(raw);
12
+ }
13
+ catch {
14
+ return null;
15
+ }
16
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
17
+ return null;
18
+ const agentName = parsed["agent_name"];
19
+ return typeof agentName === "string" && agentName.trim()
20
+ ? { agent_name: agentName.trim() }
21
+ : {};
22
+ }
23
+ export function localConfigExists(cwd) {
24
+ return existsSync(pathFor(cwd));
25
+ }
26
+ export function loadLocalConfig(cwd) {
27
+ const path = pathFor(cwd);
28
+ if (!existsSync(path))
29
+ return {};
30
+ try {
31
+ return parseLocalConfig(readFileSync(path, "utf8")) ?? {};
32
+ }
33
+ catch {
34
+ return {};
35
+ }
36
+ }
37
+ export function saveLocalConfig(cwd, patch) {
38
+ const path = pathFor(cwd);
39
+ const next = { ...loadLocalConfig(cwd), ...patch };
40
+ try {
41
+ mkdirSync(dirname(path), { recursive: true });
42
+ writeFileSync(path, JSON.stringify(next, null, 2));
43
+ }
44
+ catch (error) {
45
+ const message = error instanceof Error ? error.message : String(error);
46
+ console.warn(`[pi-reach] could not persist local config ${path}: ${message}`);
47
+ }
48
+ }
49
+ export function defaultAgentName(cwd) {
50
+ return basename(cwd) || "agent";
51
+ }
52
+ //# sourceMappingURL=local_config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local_config.js","sourceRoot":"","sources":["../../src/session/local_config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEpD,MAAM,SAAS,GAAG,cAAc,CAAC;AACjC,MAAM,UAAU,GAAG,aAAa,CAAC;AAMjC,SAAS,OAAO,CAAC,GAAW;IAC1B,OAAO,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;AAC1C,CAAC;AAED,SAAS,gBAAgB,CAAC,GAAW;IACnC,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAY,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC;IAChF,MAAM,SAAS,GAAI,MAAkC,CAAC,YAAY,CAAC,CAAC;IACpE,OAAO,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,CAAC,IAAI,EAAE;QACtD,CAAC,CAAC,EAAE,UAAU,EAAE,SAAS,CAAC,IAAI,EAAE,EAAE;QAClC,CAAC,CAAC,EAAE,CAAC;AACT,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,GAAW;IAC3C,OAAO,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAC1B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IACjC,IAAI,CAAC;QACH,OAAO,gBAAgB,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;IAC5D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,GAAW,EAAE,KAA2B;IACtE,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAC1B,MAAM,IAAI,GAAgB,EAAE,GAAG,eAAe,CAAC,GAAG,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC;IAChE,IAAI,CAAC;QACH,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9C,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACrD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvE,OAAO,CAAC,IAAI,CAAC,6CAA6C,IAAI,KAAK,OAAO,EAAE,CAAC,CAAC;IAChF,CAAC;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,OAAO,QAAQ,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC;AAClC,CAAC"}
@@ -0,0 +1,30 @@
1
+ import type { SessionManager } from "@earendil-works/pi-coding-agent";
2
+ import { type ServerFrame, type TimelineEvent } from "../protocol/v2/index.js";
3
+ type RecoveryFn = (sessionManager: SessionManager) => readonly TimelineEvent[];
4
+ type HistorySyncRequest = {
5
+ requestId: string;
6
+ targetChannelId: string;
7
+ leafId: string | null;
8
+ before: number | null;
9
+ limit?: number;
10
+ };
11
+ export type TimelineHistorySync = HistorySyncRequest;
12
+ export type TimelineHistoryRecovery = RecoveryFn;
13
+ export declare class TimelineHistoryPager {
14
+ private readonly sessionManager;
15
+ private readonly recoverFn;
16
+ constructor(sessionManager: SessionManager, recoverFn: RecoveryFn);
17
+ sync(request: HistorySyncRequest): ServerFrame[];
18
+ private validateRequest;
19
+ private captureSnapshot;
20
+ private snapshotInvalidReason;
21
+ private recoverRecords;
22
+ private selectWindow;
23
+ private fragmentWindow;
24
+ private makeChunk;
25
+ private nonFinalFrameBytes;
26
+ private finalFrameBytes;
27
+ private protocolError;
28
+ private reset;
29
+ }
30
+ export {};