@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,79 @@
1
+ export type V2IdempotencyStatus = "received" | "queued" | "accepted" | "committed" | "unknown_delivery";
2
+ export type V2ChannelState = {
3
+ readonly senderRef: string;
4
+ readonly channelId: string;
5
+ readonly ready: true;
6
+ readonly lastSeen: number;
7
+ };
8
+ export type V2IdempotencyRequest = {
9
+ senderRef: string;
10
+ clientRequestId: string;
11
+ payload: unknown;
12
+ };
13
+ export type V2IdempotencyRecord = {
14
+ readonly key: string;
15
+ readonly sessionId: string;
16
+ readonly leafId: string | null;
17
+ readonly senderRef: string;
18
+ readonly clientRequestId: string;
19
+ readonly fingerprint: string;
20
+ status: V2IdempotencyStatus;
21
+ messageId?: string;
22
+ groupId?: string;
23
+ touchedAt: number;
24
+ };
25
+ type Clock = () => number;
26
+ export type V2SessionStateOptions = {
27
+ sessionId: string;
28
+ leafId: string | null;
29
+ maxEntries?: number;
30
+ ttlMs?: number;
31
+ clock?: Clock;
32
+ };
33
+ type BeginResult = {
34
+ kind: "new";
35
+ record: V2IdempotencyRecord;
36
+ } | {
37
+ kind: "replay";
38
+ record: V2IdempotencyRecord;
39
+ } | {
40
+ kind: "conflict";
41
+ record: V2IdempotencyRecord;
42
+ };
43
+ export declare class V2SessionState {
44
+ private sessionIdValue;
45
+ private leafIdValue;
46
+ private readonly maxEntries;
47
+ private readonly ttlMs;
48
+ private readonly clock;
49
+ private readonly channels;
50
+ private readonly records;
51
+ constructor(options: V2SessionStateOptions);
52
+ get sessionId(): string;
53
+ get leafId(): string | null;
54
+ setScope(sessionId: string, leafId: string | null): void;
55
+ hello(senderRef: string, channelId: string): V2ChannelState;
56
+ touch(senderRef: string, channelId: string): V2ChannelState | undefined;
57
+ get(senderRef: string, channelId: string): V2ChannelState | undefined;
58
+ removeChannel(senderRef: string, channelId: string): void;
59
+ removeOwner(senderRef: string): void;
60
+ begin(request: V2IdempotencyRequest): BeginResult;
61
+ update(key: string, status: V2IdempotencyStatus, details?: {
62
+ messageId?: string;
63
+ groupId?: string;
64
+ }): V2IdempotencyRecord | undefined;
65
+ rememberUnknownDelivery(key: string): V2IdempotencyRecord | undefined;
66
+ getRecord(key: string): V2IdempotencyRecord | undefined;
67
+ forget(key: string): boolean;
68
+ snapshot(): {
69
+ channels: V2ChannelState[];
70
+ records: V2IdempotencyRecord[];
71
+ };
72
+ private prune;
73
+ private pruneCapacity;
74
+ private recordKey;
75
+ private channelKey;
76
+ private assertId;
77
+ }
78
+ export declare function fingerprintV2Payload(value: unknown): string;
79
+ export {};
@@ -0,0 +1,166 @@
1
+ import { createHash } from "node:crypto";
2
+ export class V2SessionState {
3
+ sessionIdValue;
4
+ leafIdValue;
5
+ maxEntries;
6
+ ttlMs;
7
+ clock;
8
+ channels = new Map();
9
+ records = new Map();
10
+ constructor(options) {
11
+ this.assertId(options.sessionId, "sessionId");
12
+ this.sessionIdValue = options.sessionId;
13
+ this.leafIdValue = options.leafId;
14
+ this.maxEntries = Math.max(1, Math.floor(options.maxEntries ?? 512));
15
+ this.ttlMs = Math.max(1, options.ttlMs ?? 10 * 60 * 1000);
16
+ this.clock = options.clock ?? Date.now;
17
+ }
18
+ get sessionId() {
19
+ return this.sessionIdValue;
20
+ }
21
+ get leafId() {
22
+ return this.leafIdValue;
23
+ }
24
+ setScope(sessionId, leafId) {
25
+ this.assertId(sessionId, "sessionId");
26
+ if (sessionId === this.sessionIdValue && leafId === this.leafIdValue)
27
+ return;
28
+ this.sessionIdValue = sessionId;
29
+ this.leafIdValue = leafId;
30
+ this.records.clear();
31
+ }
32
+ hello(senderRef, channelId) {
33
+ this.assertId(senderRef, "senderRef");
34
+ this.assertId(channelId, "channelId");
35
+ const state = { senderRef, channelId, ready: true, lastSeen: this.clock() };
36
+ this.channels.set(this.channelKey(senderRef, channelId), state);
37
+ return state;
38
+ }
39
+ touch(senderRef, channelId) {
40
+ const current = this.get(senderRef, channelId);
41
+ if (!current)
42
+ return undefined;
43
+ const state = { ...current, lastSeen: this.clock() };
44
+ this.channels.set(this.channelKey(senderRef, channelId), state);
45
+ return state;
46
+ }
47
+ get(senderRef, channelId) {
48
+ return this.channels.get(this.channelKey(senderRef, channelId));
49
+ }
50
+ removeChannel(senderRef, channelId) {
51
+ this.channels.delete(this.channelKey(senderRef, channelId));
52
+ }
53
+ removeOwner(senderRef) {
54
+ for (const [key, channel] of this.channels)
55
+ if (channel.senderRef === senderRef)
56
+ this.channels.delete(key);
57
+ for (const [key, record] of this.records)
58
+ if (record.senderRef === senderRef)
59
+ this.records.delete(key);
60
+ }
61
+ begin(request) {
62
+ this.prune();
63
+ this.assertId(request.senderRef, "senderRef");
64
+ this.assertId(request.clientRequestId, "clientRequestId");
65
+ const key = this.recordKey(request.senderRef, request.clientRequestId);
66
+ const fingerprint = fingerprintPayload(request.payload);
67
+ const existing = this.records.get(key);
68
+ if (existing) {
69
+ existing.touchedAt = this.clock();
70
+ this.records.delete(key);
71
+ this.records.set(key, existing);
72
+ return { kind: existing.fingerprint === fingerprint ? "replay" : "conflict", record: { ...existing } };
73
+ }
74
+ const record = {
75
+ key,
76
+ sessionId: this.sessionIdValue,
77
+ leafId: this.leafIdValue,
78
+ senderRef: request.senderRef,
79
+ clientRequestId: request.clientRequestId,
80
+ fingerprint,
81
+ status: "received",
82
+ touchedAt: this.clock(),
83
+ };
84
+ this.records.set(key, record);
85
+ this.pruneCapacity();
86
+ return { kind: "new", record: { ...record } };
87
+ }
88
+ update(key, status, details = {}) {
89
+ this.prune();
90
+ const record = this.records.get(key);
91
+ if (!record)
92
+ return undefined;
93
+ record.status = status;
94
+ if (details.messageId !== undefined)
95
+ record.messageId = details.messageId;
96
+ if (details.groupId !== undefined)
97
+ record.groupId = details.groupId;
98
+ record.touchedAt = this.clock();
99
+ this.records.delete(key);
100
+ this.records.set(key, record);
101
+ return { ...record };
102
+ }
103
+ rememberUnknownDelivery(key) {
104
+ return this.update(key, "unknown_delivery");
105
+ }
106
+ getRecord(key) {
107
+ this.prune();
108
+ const record = this.records.get(key);
109
+ return record ? { ...record } : undefined;
110
+ }
111
+ forget(key) {
112
+ return this.records.delete(key);
113
+ }
114
+ snapshot() {
115
+ this.prune();
116
+ return { channels: [...this.channels.values()].map((channel) => ({ ...channel })), records: [...this.records.values()].map((record) => ({ ...record })) };
117
+ }
118
+ prune() {
119
+ const now = this.clock();
120
+ for (const [key, record] of this.records)
121
+ if (now - record.touchedAt >= this.ttlMs)
122
+ this.records.delete(key);
123
+ this.pruneCapacity();
124
+ }
125
+ pruneCapacity() {
126
+ while (this.records.size > this.maxEntries) {
127
+ const oldest = this.records.keys().next().value;
128
+ if (oldest === undefined)
129
+ break;
130
+ this.records.delete(oldest);
131
+ }
132
+ }
133
+ recordKey(senderRef, clientRequestId) {
134
+ return `${this.sessionIdValue}\u0000${this.leafIdValue ?? ""}\u0000${senderRef}\u0000${clientRequestId}`;
135
+ }
136
+ channelKey(senderRef, channelId) {
137
+ return `${senderRef}\u0000${channelId}`;
138
+ }
139
+ assertId(value, name) {
140
+ if (!value || value.length > 256)
141
+ throw new Error(`${name} is invalid`);
142
+ }
143
+ }
144
+ function fingerprintPayload(value) {
145
+ const canonical = canonicalize(value);
146
+ return createHash("sha256").update(JSON.stringify(canonical), "utf8").digest("hex");
147
+ }
148
+ function canonicalize(value) {
149
+ if (value === null || typeof value === "string" || typeof value === "boolean")
150
+ return value;
151
+ if (typeof value === "number")
152
+ return Number.isFinite(value) ? value : null;
153
+ if (Array.isArray(value))
154
+ return value.map(canonicalize);
155
+ if (typeof value === "object") {
156
+ const result = {};
157
+ for (const key of Object.keys(value).sort())
158
+ result[key] = canonicalize(value[key]);
159
+ return result;
160
+ }
161
+ return String(value);
162
+ }
163
+ export function fingerprintV2Payload(value) {
164
+ return fingerprintPayload(value);
165
+ }
166
+ //# sourceMappingURL=session_state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session_state.js","sourceRoot":"","sources":["../../../src/protocol/v2/session_state.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { SessionManager } from "@earendil-works/pi-coding-agent";
2
+ import type { EndpointMetadata } from "../transport/relay_client.js";
3
+ export declare const UNTITLED_SESSION_NAME = "Untitled session";
4
+ type SessionNameSource = Pick<SessionManager, "getSessionName"> | null | undefined;
5
+ type EndpointMetadataInput = {
6
+ sessionManager: SessionNameSource;
7
+ cwd: string;
8
+ model?: string;
9
+ thinking?: string;
10
+ working: boolean;
11
+ };
12
+ export declare function currentSessionName(sessionManager: SessionNameSource): string;
13
+ export declare function buildEndpointMetadata({ sessionManager, cwd, model, thinking, working, }: EndpointMetadataInput): EndpointMetadata;
14
+ export {};
@@ -0,0 +1,18 @@
1
+ export const UNTITLED_SESSION_NAME = "Untitled session";
2
+ export function currentSessionName(sessionManager) {
3
+ const name = sessionManager?.getSessionName?.();
4
+ return name?.trim() || UNTITLED_SESSION_NAME;
5
+ }
6
+ export function buildEndpointMetadata({ sessionManager, cwd, model, thinking, working, }) {
7
+ return {
8
+ kind: "interactive",
9
+ name: currentSessionName(sessionManager),
10
+ cwd,
11
+ pid: process.pid,
12
+ started_at: Date.now(),
13
+ ...(model ? { model } : {}),
14
+ ...(thinking ? { thinking } : {}),
15
+ working,
16
+ };
17
+ }
18
+ //# sourceMappingURL=endpoint_metadata.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"endpoint_metadata.js","sourceRoot":"","sources":["../../src/runtime/endpoint_metadata.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,MAAM,qBAAqB,GAAG,kBAAkB,CAAC;AAYxD,MAAM,UAAU,kBAAkB,CAAC,cAAiC;IAClE,MAAM,IAAI,GAAG,cAAc,EAAE,cAAc,EAAE,EAAE,CAAC;IAChD,OAAO,IAAI,EAAE,IAAI,EAAE,IAAI,qBAAqB,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,EACpC,cAAc,EACd,GAAG,EACH,KAAK,EACL,QAAQ,EACR,OAAO,GACe;IACtB,OAAO;QACL,IAAI,EAAE,aAAa;QACnB,IAAI,EAAE,kBAAkB,CAAC,cAAc,CAAC;QACxC,GAAG;QACH,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE;QACtB,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3B,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjC,OAAO;KACR,CAAC;AACJ,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { ExtensionAPI, ExtensionContext, SessionShutdownEvent, SessionStartEvent } from "@earendil-works/pi-coding-agent";
2
+ /** 一个进程只暴露主会话;同进程 SDK 子会话不得接管它的连接或生命周期。 */
3
+ export declare class ExtensionOwner {
4
+ private rejected;
5
+ private session;
6
+ isCurrent(): boolean;
7
+ activate(event: SessionStartEvent, ctx: ExtensionContext): boolean;
8
+ release(event: SessionShutdownEvent): void;
9
+ private matchesHandoff;
10
+ guard(pi: ExtensionAPI): ExtensionAPI;
11
+ }
12
+ export declare function resetExtensionOwnerForTest(): void;
@@ -0,0 +1,80 @@
1
+ const OWNER_KEY = Symbol.for("pi-reach.endpoint-extension-owner");
2
+ function ownerState() {
3
+ return globalThis[OWNER_KEY] ??= {};
4
+ }
5
+ /** 一个进程只暴露主会话;同进程 SDK 子会话不得接管它的连接或生命周期。 */
6
+ export class ExtensionOwner {
7
+ rejected = false;
8
+ session;
9
+ isCurrent() {
10
+ return ownerState().owner === this;
11
+ }
12
+ activate(event, ctx) {
13
+ if (this.rejected)
14
+ return false;
15
+ const state = ownerState();
16
+ if (state.owner && state.owner !== this) {
17
+ this.rejected = true;
18
+ return false;
19
+ }
20
+ const session = { sessionId: ctx.sessionManager.getSessionId(), sessionFile: ctx.sessionManager.getSessionFile?.() };
21
+ const handoff = state.handoff;
22
+ if (handoff && !this.matchesHandoff(handoff, event, session)) {
23
+ this.rejected = true;
24
+ return false;
25
+ }
26
+ state.owner = this;
27
+ state.handoff = undefined;
28
+ this.session = session;
29
+ return true;
30
+ }
31
+ release(event) {
32
+ if (!this.isCurrent())
33
+ return;
34
+ const state = ownerState();
35
+ state.owner = undefined;
36
+ state.handoff = event.reason !== "quit" && this.session
37
+ ? { ...this.session, owner: this, reason: event.reason, targetSessionFile: event.targetSessionFile }
38
+ : undefined;
39
+ // 即使主会话已退出,先前拒绝的子实例也不能由后续事件抢占 endpoint。
40
+ }
41
+ matchesHandoff(handoff, event, session) {
42
+ if (event.reason !== handoff.reason)
43
+ return false;
44
+ if (event.reason === "reload")
45
+ return session.sessionId === handoff.sessionId;
46
+ if (event.previousSessionFile !== handoff.sessionFile)
47
+ return false;
48
+ return !handoff.targetSessionFile || session.sessionFile === handoff.targetSessionFile;
49
+ }
50
+ guard(pi) {
51
+ const on = ((name, handler) => {
52
+ pi.on(name, (...args) => this.isCurrent() ? handler(...args) : undefined);
53
+ });
54
+ const registerCommand = (name, command) => {
55
+ pi.registerCommand(name, {
56
+ ...command,
57
+ handler: async (args, ctx) => {
58
+ if (!this.isCurrent() && ownerState().handoff?.owner !== this) {
59
+ ctx.ui.notify("[pi-reach] This endpoint belongs to the primary Pi session.", "warning");
60
+ return;
61
+ }
62
+ await command.handler(args, ctx);
63
+ },
64
+ });
65
+ };
66
+ return new Proxy(pi, {
67
+ get(target, key, receiver) {
68
+ if (key === "on")
69
+ return on;
70
+ if (key === "registerCommand")
71
+ return registerCommand;
72
+ return Reflect.get(target, key, receiver);
73
+ },
74
+ });
75
+ }
76
+ }
77
+ export function resetExtensionOwnerForTest() {
78
+ delete globalThis[OWNER_KEY];
79
+ }
80
+ //# sourceMappingURL=extension_owner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension_owner.js","sourceRoot":"","sources":["../../src/runtime/extension_owner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
2
+ import type { RelayDisplayStatus } from "./relay_lifecycle.js";
3
+ type FooterUi = Pick<ExtensionContext["ui"], "setStatus" | "theme">;
4
+ export declare function clearLegacyRelayStatuses(ui: FooterUi): void;
5
+ export declare function renderRelayFooter(ui: FooterUi, displayStatus: RelayDisplayStatus): void;
6
+ export {};
@@ -0,0 +1,21 @@
1
+ const LEGACY_STATUS_KEYS = ["pi-reach:control", "pi-reach:owner-active", "pi-reach:session"];
2
+ const STATUS_PRESENTATION = {
3
+ connecting: { color: "dim", text: "连接中" },
4
+ connected: { color: "dim", text: "已连接" },
5
+ reconnecting: { color: "warning", text: "重连中" },
6
+ off: { color: "dim", text: "已关闭" },
7
+ failed: { color: "error", text: "启动失败" },
8
+ };
9
+ export function clearLegacyRelayStatuses(ui) {
10
+ for (const key of LEGACY_STATUS_KEYS)
11
+ ui.setStatus(key, undefined);
12
+ }
13
+ export function renderRelayFooter(ui, displayStatus) {
14
+ const { color, text } = STATUS_PRESENTATION[displayStatus];
15
+ const prefix = "Pi Reach · ";
16
+ const status = color === "dim"
17
+ ? ui.theme.fg("dim", prefix + text)
18
+ : ui.theme.fg("dim", prefix) + ui.theme.fg(color, text);
19
+ ui.setStatus("pi-reach:relay", status);
20
+ }
21
+ //# sourceMappingURL=footer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"footer.js","sourceRoot":"","sources":["../../src/runtime/footer.ts"],"names":[],"mappings":"AAKA,MAAM,kBAAkB,GAAG,CAAC,kBAAkB,EAAE,uBAAuB,EAAE,kBAAkB,CAAU,CAAC;AACtG,MAAM,mBAAmB,GAAG;IAC1B,UAAU,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE;IACzC,SAAS,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE;IACxC,YAAY,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE;IAC/C,GAAG,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE;IAClC,MAAM,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE;CACkD,CAAC;AAE7F,MAAM,UAAU,wBAAwB,CAAC,EAAY;IACnD,KAAK,MAAM,GAAG,IAAI,kBAAkB;QAAE,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,EAAY,EAAE,aAAiC;IAC/E,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,mBAAmB,CAAC,aAAa,CAAC,CAAC;IAC3D,MAAM,MAAM,GAAG,aAAa,CAAC;IAC7B,MAAM,MAAM,GAAG,KAAK,KAAK,KAAK;QAC5B,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;QACnC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAC1D,EAAE,CAAC,SAAS,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC;AACzC,CAAC"}
@@ -0,0 +1,16 @@
1
+ import { type ClientFrame } from "../protocol/v2/index.js";
2
+ import type { RelayClient } from "../transport/relay_client.js";
3
+ import { type HostRouteIdentity } from "../transport/peer_channel.js";
4
+ export interface OwnerRouterDependencies {
5
+ isCurrent(relay: RelayClient): boolean;
6
+ routeIdentity(): HostRouteIdentity;
7
+ hasOwner(ownerId: string): boolean;
8
+ findKnownOwner(ownerId: string): Promise<boolean>;
9
+ attachOwner(relay: RelayClient, ownerId: string): void;
10
+ routeClientFrame(ownerId: string, frame: ClientFrame): void;
11
+ handlePairRequest(relay: RelayClient, ownerId: string, frame: Extract<ClientFrame, {
12
+ type: "pair_request";
13
+ }>): Promise<void>;
14
+ }
15
+ /** Routes unbound Owner frames without duplicating bound peer-channel traffic. */
16
+ export declare function installOwnerRouter(relay: RelayClient, deps: OwnerRouterDependencies): () => void;
@@ -0,0 +1,41 @@
1
+ import { decodeClientFrameV2 } from "../protocol/v2/index.js";
2
+ import { decodeRoutePayload } from "../vendor/protocol/outer/index.js";
3
+ import { parseIncomingRoute } from "../transport/peer_channel.js";
4
+ /** Routes unbound Owner frames without duplicating bound peer-channel traffic. */
5
+ export function installOwnerRouter(relay, deps) {
6
+ const listener = (line) => {
7
+ if (!deps.isCurrent(relay))
8
+ return;
9
+ const route = parseIncomingRoute(line, deps.routeIdentity());
10
+ if (!route?.source_owner_id)
11
+ return;
12
+ const payload = decodeRoutePayload(route);
13
+ if (!payload)
14
+ return;
15
+ let frame;
16
+ try {
17
+ frame = decodeClientFrameV2(payload);
18
+ }
19
+ catch {
20
+ return;
21
+ }
22
+ if ((frame.type === "pair_request") !== (route.purpose === "pairing"))
23
+ return;
24
+ const ownerId = route.source_owner_id;
25
+ if (frame.type === "pair_request") {
26
+ void deps.handlePairRequest(relay, ownerId, frame);
27
+ return;
28
+ }
29
+ if (deps.hasOwner(ownerId))
30
+ return;
31
+ void deps.findKnownOwner(ownerId).then((known) => {
32
+ if (!known || !deps.isCurrent(relay))
33
+ return;
34
+ deps.attachOwner(relay, ownerId);
35
+ deps.routeClientFrame(ownerId, frame);
36
+ });
37
+ };
38
+ relay.on("message", listener);
39
+ return () => relay.off("message", listener);
40
+ }
41
+ //# sourceMappingURL=owner_router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"owner_router.js","sourceRoot":"","sources":["../../src/runtime/owner_router.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAoB,MAAM,yBAAyB,CAAC;AAChF,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAE9D,OAAO,EAAE,kBAAkB,EAA0B,MAAM,8BAA8B,CAAC;AAY1F,kFAAkF;AAClF,MAAM,UAAU,kBAAkB,CAAC,KAAkB,EAAE,IAA6B;IAClF,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAQ,EAAE;QACtC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;YAAE,OAAO;QACnC,MAAM,KAAK,GAAG,kBAAkB,CAAC,IAAI,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC;QAC7D,IAAI,CAAC,KAAK,EAAE,eAAe;YAAE,OAAO;QACpC,MAAM,OAAO,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;QAC1C,IAAI,CAAC,OAAO;YAAE,OAAO;QACrB,IAAI,KAAkB,CAAC;QACvB,IAAI,CAAC;YAAC,KAAK,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;QAAC,CAAC;QAC7C,MAAM,CAAC;YAAC,OAAO;QAAC,CAAC;QACjB,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,cAAc,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,KAAK,SAAS,CAAC;YAAE,OAAO;QAC9E,MAAM,OAAO,GAAG,KAAK,CAAC,eAAe,CAAC;QACtC,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;YAClC,KAAK,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;YACnD,OAAO;QACT,CAAC;QACD,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;YAAE,OAAO;QACnC,KAAK,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE;YAC/C,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;gBAAE,OAAO;YAC7C,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;YACjC,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACxC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;IACF,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAC9B,OAAO,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AAC9C,CAAC"}
@@ -0,0 +1,42 @@
1
+ import type { PeerRecord, PeerWriteReceipt } from "../pairing/storage.js";
2
+ import type { QRSession } from "../pairing/qr.js";
3
+ import type { ClientFrame, ServerFrame } from "../protocol/v2/index.js";
4
+ import type { RelayClient } from "../transport/relay_client.js";
5
+ import { V2PeerChannel, type HostRouteIdentity } from "../transport/peer_channel.js";
6
+ type PairRequest = Extract<ClientFrame, {
7
+ type: "pair_request";
8
+ }>;
9
+ type PairOk = Extract<ServerFrame, {
10
+ type: "pair_ok";
11
+ }>;
12
+ export interface PairingBinding {
13
+ readonly channel: V2PeerChannel;
14
+ }
15
+ export interface PairingCoordinatorDependencies {
16
+ readonly qrSession: QRSession;
17
+ readonly routeIdentity: () => HostRouteIdentity;
18
+ readonly isRelayCurrent: (relay: RelayClient) => boolean;
19
+ readonly attachOwner: (relay: RelayClient, ownerId: string) => PairingBinding | null;
20
+ readonly activeBinding: (ownerId: string) => PairingBinding | undefined;
21
+ readonly addPeer: (record: PeerRecord) => Promise<PeerWriteReceipt>;
22
+ readonly rollbackPeer: (receipt: PeerWriteReceipt) => Promise<unknown>;
23
+ readonly updateEndpoint: (relay: RelayClient) => Promise<boolean>;
24
+ readonly refreshCurrentEndpoint: () => Promise<boolean>;
25
+ readonly buildPairOk: (frame: PairRequest) => PairOk;
26
+ }
27
+ /** Coordinates one-process pairing retries across storage and Relay lifecycles. */
28
+ export declare class PairingCoordinator {
29
+ private readonly deps;
30
+ private readonly attempts;
31
+ constructor(deps: PairingCoordinatorDependencies);
32
+ handle(relay: RelayClient, ownerId: string, frame: PairRequest): Promise<void>;
33
+ abandonInactive(): void;
34
+ private run;
35
+ private rollback;
36
+ private lifecycleIsCurrent;
37
+ private canCommit;
38
+ private reject;
39
+ private sendError;
40
+ private attemptKey;
41
+ }
42
+ export {};