@parlor/agent-host 0.0.0-stage → 0.2.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.
Files changed (155) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +16 -2
  3. package/dist/acp.d.ts +165 -0
  4. package/dist/acp.d.ts.map +1 -0
  5. package/dist/acp.js +626 -0
  6. package/dist/acp.js.map +1 -0
  7. package/dist/adapter-launch.d.ts +7 -0
  8. package/dist/adapter-launch.d.ts.map +1 -0
  9. package/dist/adapter-launch.js +35 -0
  10. package/dist/adapter-launch.js.map +1 -0
  11. package/dist/agents.d.ts +32 -0
  12. package/dist/agents.d.ts.map +1 -0
  13. package/dist/agents.js +132 -0
  14. package/dist/agents.js.map +1 -0
  15. package/dist/assertion.d.ts +9 -0
  16. package/dist/assertion.d.ts.map +1 -0
  17. package/dist/assertion.js +22 -0
  18. package/dist/assertion.js.map +1 -0
  19. package/dist/control.d.ts +106 -0
  20. package/dist/control.d.ts.map +1 -0
  21. package/dist/control.js +32 -0
  22. package/dist/control.js.map +1 -0
  23. package/dist/daemon.d.ts +50 -0
  24. package/dist/daemon.d.ts.map +1 -0
  25. package/dist/daemon.js +543 -0
  26. package/dist/daemon.js.map +1 -0
  27. package/dist/dev-host.d.ts +5 -0
  28. package/dist/dev-host.d.ts.map +1 -0
  29. package/dist/dev-host.js +29 -0
  30. package/dist/dev-host.js.map +1 -0
  31. package/dist/device-list.d.ts +6 -0
  32. package/dist/device-list.d.ts.map +1 -0
  33. package/dist/device-list.js +30 -0
  34. package/dist/device-list.js.map +1 -0
  35. package/dist/embedded.d.ts +6 -0
  36. package/dist/embedded.d.ts.map +1 -0
  37. package/dist/embedded.js +3 -0
  38. package/dist/embedded.js.map +1 -0
  39. package/dist/fold.d.ts +38 -0
  40. package/dist/fold.d.ts.map +1 -0
  41. package/dist/fold.js +165 -0
  42. package/dist/fold.js.map +1 -0
  43. package/dist/grants.d.ts +54 -0
  44. package/dist/grants.d.ts.map +1 -0
  45. package/dist/grants.js +157 -0
  46. package/dist/grants.js.map +1 -0
  47. package/dist/index.d.ts +17 -0
  48. package/dist/index.d.ts.map +1 -0
  49. package/dist/index.js +17 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/json-file.d.ts +6 -0
  52. package/dist/json-file.d.ts.map +1 -0
  53. package/dist/json-file.js +35 -0
  54. package/dist/json-file.js.map +1 -0
  55. package/dist/lines.d.ts +5 -0
  56. package/dist/lines.d.ts.map +1 -0
  57. package/dist/lines.js +9 -0
  58. package/dist/lines.js.map +1 -0
  59. package/dist/link-device.d.ts +28 -0
  60. package/dist/link-device.d.ts.map +1 -0
  61. package/dist/link-device.js +40 -0
  62. package/dist/link-device.js.map +1 -0
  63. package/dist/link.d.ts +52 -0
  64. package/dist/link.d.ts.map +1 -0
  65. package/dist/link.js +277 -0
  66. package/dist/link.js.map +1 -0
  67. package/dist/lock.d.ts +7 -0
  68. package/dist/lock.d.ts.map +1 -0
  69. package/dist/lock.js +105 -0
  70. package/dist/lock.js.map +1 -0
  71. package/dist/log.d.ts +3 -0
  72. package/dist/log.d.ts.map +1 -0
  73. package/dist/log.js +18 -0
  74. package/dist/log.js.map +1 -0
  75. package/dist/main.d.ts +3 -0
  76. package/dist/main.d.ts.map +1 -0
  77. package/dist/main.js +520 -0
  78. package/dist/main.js.map +1 -0
  79. package/dist/mcp-proxy.d.ts +33 -0
  80. package/dist/mcp-proxy.d.ts.map +1 -0
  81. package/dist/mcp-proxy.js +317 -0
  82. package/dist/mcp-proxy.js.map +1 -0
  83. package/dist/naming.d.ts +7 -0
  84. package/dist/naming.d.ts.map +1 -0
  85. package/dist/naming.js +42 -0
  86. package/dist/naming.js.map +1 -0
  87. package/dist/pairing.d.ts +68 -0
  88. package/dist/pairing.d.ts.map +1 -0
  89. package/dist/pairing.js +312 -0
  90. package/dist/pairing.js.map +1 -0
  91. package/dist/paths.d.ts +19 -0
  92. package/dist/paths.d.ts.map +1 -0
  93. package/dist/paths.js +23 -0
  94. package/dist/paths.js.map +1 -0
  95. package/dist/policy.d.ts +37 -0
  96. package/dist/policy.d.ts.map +1 -0
  97. package/dist/policy.js +60 -0
  98. package/dist/policy.js.map +1 -0
  99. package/dist/pool.d.ts +34 -0
  100. package/dist/pool.d.ts.map +1 -0
  101. package/dist/pool.js +112 -0
  102. package/dist/pool.js.map +1 -0
  103. package/dist/prompter.d.ts +33 -0
  104. package/dist/prompter.d.ts.map +1 -0
  105. package/dist/prompter.js +98 -0
  106. package/dist/prompter.js.map +1 -0
  107. package/dist/release-main.d.ts +2 -0
  108. package/dist/release-main.d.ts.map +1 -0
  109. package/dist/release-main.js +50 -0
  110. package/dist/release-main.js.map +1 -0
  111. package/dist/release.d.ts +22 -0
  112. package/dist/release.d.ts.map +1 -0
  113. package/dist/release.js +81 -0
  114. package/dist/release.js.map +1 -0
  115. package/dist/revoke.d.ts +10 -0
  116. package/dist/revoke.d.ts.map +1 -0
  117. package/dist/revoke.js +23 -0
  118. package/dist/revoke.js.map +1 -0
  119. package/dist/self.d.ts +7 -0
  120. package/dist/self.d.ts.map +1 -0
  121. package/dist/self.js +10 -0
  122. package/dist/self.js.map +1 -0
  123. package/dist/service.d.ts +97 -0
  124. package/dist/service.d.ts.map +1 -0
  125. package/dist/service.js +205 -0
  126. package/dist/service.js.map +1 -0
  127. package/dist/shell-path.d.ts +10 -0
  128. package/dist/shell-path.d.ts.map +1 -0
  129. package/dist/shell-path.js +64 -0
  130. package/dist/shell-path.js.map +1 -0
  131. package/dist/skeleton-kit.d.ts +8 -0
  132. package/dist/skeleton-kit.d.ts.map +1 -0
  133. package/dist/skeleton-kit.js +143 -0
  134. package/dist/skeleton-kit.js.map +1 -0
  135. package/dist/spec-kit.d.ts +102 -0
  136. package/dist/spec-kit.d.ts.map +1 -0
  137. package/dist/spec-kit.js +372 -0
  138. package/dist/spec-kit.js.map +1 -0
  139. package/dist/testing/fake-agent.d.ts +65 -0
  140. package/dist/testing/fake-agent.d.ts.map +1 -0
  141. package/dist/testing/fake-agent.js +238 -0
  142. package/dist/testing/fake-agent.js.map +1 -0
  143. package/dist/trust.d.ts +20 -0
  144. package/dist/trust.d.ts.map +1 -0
  145. package/dist/trust.js +27 -0
  146. package/dist/trust.js.map +1 -0
  147. package/dist/turn.d.ts +32 -0
  148. package/dist/turn.d.ts.map +1 -0
  149. package/dist/turn.js +650 -0
  150. package/dist/turn.js.map +1 -0
  151. package/dist/version.d.ts +2 -0
  152. package/dist/version.d.ts.map +1 -0
  153. package/dist/version.js +6 -0
  154. package/dist/version.js.map +1 -0
  155. package/package.json +57 -3
@@ -0,0 +1,317 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { once } from "node:events";
3
+ import { chmod, mkdir, rm } from "node:fs/promises";
4
+ import { connect, createServer } from "node:net";
5
+ import { dirname } from "node:path";
6
+ import { onLines } from "./lines.js";
7
+ import { logEvent } from "./log.js";
8
+ export const NO_TURN_MESSAGE = "No turn is active: the user has not asked anything right now. Wait for their next message.";
9
+ export const DISCONNECTED_MESSAGE = "This computer no longer serves the app: it was disconnected, or Parlor Host stopped.";
10
+ const SOCKET_ENV = "PARLOR_HOST_PROXY_SOCKET";
11
+ const NONCE_ENV = "PARLOR_HOST_PROXY_NONCE";
12
+ const NO_TURN_CODE = -32000;
13
+ const REFUSED_CODE = -32001;
14
+ function parse(text) {
15
+ try {
16
+ const value = JSON.parse(text);
17
+ return typeof value === "object" && value !== null && !Array.isArray(value)
18
+ ? value
19
+ : undefined;
20
+ }
21
+ catch {
22
+ return undefined;
23
+ }
24
+ }
25
+ const result = (id, value) => JSON.stringify({ jsonrpc: "2.0", id, result: value });
26
+ const failure = (id, code, message) => JSON.stringify({ jsonrpc: "2.0", id, error: { code, message } });
27
+ /** One line per message on the agent's stdio: only a body with a raw newline is compacted. */
28
+ const asLine = (text) => text.includes("\n") ? JSON.stringify(JSON.parse(text)) : text;
29
+ function betweenTurns(live, message) {
30
+ if (message.id === undefined || typeof message.method !== "string")
31
+ return undefined;
32
+ const { id } = message;
33
+ switch (message.method) {
34
+ case "ping":
35
+ return result(id, {});
36
+ case "initialize":
37
+ return live.initialized === undefined
38
+ ? failure(id, NO_TURN_CODE, NO_TURN_MESSAGE)
39
+ : result(id, live.initialized);
40
+ case "tools/list":
41
+ return live.tools === undefined
42
+ ? failure(id, NO_TURN_CODE, NO_TURN_MESSAGE)
43
+ : result(id, live.tools);
44
+ case "tools/call":
45
+ return result(id, {
46
+ content: [{ type: "text", text: NO_TURN_MESSAGE }],
47
+ isError: true,
48
+ });
49
+ default:
50
+ return failure(id, NO_TURN_CODE, NO_TURN_MESSAGE);
51
+ }
52
+ }
53
+ async function* serverSentData(body) {
54
+ const decoder = new TextDecoder();
55
+ let buffer = "";
56
+ let data = [];
57
+ for await (const chunk of body) {
58
+ buffer += decoder.decode(chunk, { stream: true });
59
+ let end = buffer.indexOf("\n");
60
+ while (end !== -1) {
61
+ const line = buffer.slice(0, end).replace(/\r$/, "");
62
+ buffer = buffer.slice(end + 1);
63
+ if (line === "") {
64
+ if (data.length > 0)
65
+ yield data.join("\n");
66
+ data = [];
67
+ }
68
+ else if (line.startsWith("data:")) {
69
+ data.push(line.slice(line.startsWith("data: ") ? 6 : 5));
70
+ }
71
+ end = buffer.indexOf("\n");
72
+ }
73
+ }
74
+ if (data.length > 0)
75
+ yield data.join("\n");
76
+ }
77
+ function refusal(status) {
78
+ return status === 401
79
+ ? "This computer can no longer act for the app: the turn ended or the computer was disconnected."
80
+ : `The app answered HTTP ${status}.`;
81
+ }
82
+ async function forward(live, token, line, message, reply) {
83
+ const method = typeof message.method === "string" ? message.method : "response";
84
+ const controller = new AbortController();
85
+ live.inflight.add(controller);
86
+ let status = 0;
87
+ try {
88
+ const response = await fetch(`${live.appOrigin}/_parlor/mcp`, {
89
+ method: "POST",
90
+ headers: {
91
+ authorization: `Bearer ${token}`,
92
+ accept: "application/json, text/event-stream",
93
+ "content-type": "application/json",
94
+ ...(live.protocolVersion ? { "mcp-protocol-version": live.protocolVersion } : {}),
95
+ },
96
+ body: line,
97
+ signal: controller.signal,
98
+ });
99
+ status = response.status;
100
+ const bodies = [];
101
+ if ((response.headers.get("content-type") ?? "").includes("text/event-stream")) {
102
+ if (response.body)
103
+ for await (const data of serverSentData(response.body))
104
+ bodies.push(data);
105
+ }
106
+ else {
107
+ const text = await response.text();
108
+ if (text.trim() !== "")
109
+ bodies.push(text);
110
+ }
111
+ let answered = false;
112
+ for (const text of bodies) {
113
+ const answer = parse(text);
114
+ if (answer?.jsonrpc !== "2.0")
115
+ continue;
116
+ if (answer.id === message.id) {
117
+ answered = true;
118
+ if (method === "initialize" && answer.result !== undefined) {
119
+ live.initialized = answer.result;
120
+ const version = answer.result
121
+ .protocolVersion;
122
+ if (typeof version === "string")
123
+ live.protocolVersion = version;
124
+ }
125
+ if (method === "tools/list" && answer.result !== undefined)
126
+ live.tools = answer.result;
127
+ }
128
+ reply(asLine(text));
129
+ }
130
+ if (message.id !== undefined && !answered) {
131
+ reply(failure(message.id, REFUSED_CODE, refusal(status)));
132
+ }
133
+ }
134
+ catch (error) {
135
+ if (message.id !== undefined) {
136
+ reply(controller.signal.aborted
137
+ ? failure(message.id, NO_TURN_CODE, NO_TURN_MESSAGE)
138
+ : failure(message.id, REFUSED_CODE, `The app could not be reached: ${String(error)}`));
139
+ }
140
+ }
141
+ finally {
142
+ live.inflight.delete(controller);
143
+ logEvent("proxy_forward", { appOrigin: live.appOrigin, rpcMethod: method, status });
144
+ }
145
+ }
146
+ function handle(live, line, reply) {
147
+ const message = parse(line);
148
+ if (!message)
149
+ return;
150
+ const token = live.token;
151
+ if (token === undefined) {
152
+ const answer = betweenTurns(live, message);
153
+ if (answer)
154
+ reply(answer);
155
+ return;
156
+ }
157
+ if (message.method === "tools/list" &&
158
+ live.tools !== undefined &&
159
+ message.id !== undefined) {
160
+ reply(result(message.id, live.tools));
161
+ return;
162
+ }
163
+ void forward(live, token, line, message, reply);
164
+ }
165
+ export function createMcpProxy(hostBinary) {
166
+ const sessions = new Map();
167
+ let server;
168
+ let socketPath;
169
+ const accept = (socket) => {
170
+ let live;
171
+ const reply = (line) => {
172
+ if (!socket.destroyed)
173
+ socket.write(`${line}\n`);
174
+ };
175
+ onLines(socket, (line) => {
176
+ if (live !== undefined) {
177
+ handle(live, line, reply);
178
+ return;
179
+ }
180
+ live = sessions.get(line.trim());
181
+ if (live)
182
+ live.sockets.add(socket);
183
+ else
184
+ socket.destroy();
185
+ });
186
+ socket.on("close", () => live?.sockets.delete(socket));
187
+ socket.on("error", () => socket.destroy());
188
+ };
189
+ return {
190
+ async listen(path) {
191
+ await mkdir(dirname(path), { recursive: true, mode: 0o700 });
192
+ await rm(path, { force: true });
193
+ const listening = createServer(accept);
194
+ await new Promise((resolve, reject) => {
195
+ listening.once("error", reject);
196
+ listening.listen(path, () => resolve());
197
+ });
198
+ await chmod(path, 0o600);
199
+ server = listening;
200
+ socketPath = path;
201
+ },
202
+ open(appServer, appOrigin) {
203
+ if (socketPath === undefined)
204
+ throw new Error("The MCP proxy is not listening yet.");
205
+ const nonce = randomBytes(24).toString("base64url");
206
+ const live = {
207
+ appOrigin: appOrigin.replace(/\/+$/, ""),
208
+ token: undefined,
209
+ protocolVersion: undefined,
210
+ initialized: undefined,
211
+ tools: undefined,
212
+ inflight: new Set(),
213
+ sockets: new Set(),
214
+ };
215
+ sessions.set(nonce, live);
216
+ const end = () => {
217
+ live.token = undefined;
218
+ for (const controller of live.inflight)
219
+ controller.abort();
220
+ };
221
+ return {
222
+ nonce,
223
+ server: {
224
+ name: appServer,
225
+ command: hostBinary,
226
+ args: ["mcp-proxy"],
227
+ env: { [SOCKET_ENV]: socketPath, [NONCE_ENV]: nonce },
228
+ },
229
+ begin: (token) => {
230
+ live.token = token.token;
231
+ },
232
+ renew: (token) => {
233
+ if (live.token !== undefined)
234
+ live.token = token.token;
235
+ },
236
+ end,
237
+ dispose: () => {
238
+ end();
239
+ sessions.delete(nonce);
240
+ for (const socket of live.sockets)
241
+ socket.destroy();
242
+ },
243
+ };
244
+ },
245
+ async stopListening() {
246
+ const closing = server;
247
+ server = undefined;
248
+ if (!closing)
249
+ return;
250
+ closing.close();
251
+ if (socketPath)
252
+ await rm(socketPath, { force: true });
253
+ },
254
+ async close() {
255
+ for (const live of sessions.values()) {
256
+ for (const controller of live.inflight)
257
+ controller.abort();
258
+ for (const socket of live.sockets)
259
+ socket.destroy();
260
+ }
261
+ sessions.clear();
262
+ const closing = server;
263
+ server = undefined;
264
+ if (closing) {
265
+ await new Promise((resolve) => closing.close(() => resolve()));
266
+ if (socketPath)
267
+ await rm(socketPath, { force: true });
268
+ }
269
+ },
270
+ };
271
+ }
272
+ /**
273
+ * `parlor-host mcp-proxy`: stdio to the daemon socket and back. Once the daemon hangs up,
274
+ * every request still unanswered, and every later one, is answered with an error here.
275
+ */
276
+ export async function runMcpProxyProcess(env) {
277
+ const path = env[SOCKET_ENV];
278
+ const nonce = env[NONCE_ENV];
279
+ if (!path || !nonce)
280
+ throw new Error(`${SOCKET_ENV} and ${NONCE_ENV} must be set.`);
281
+ const unanswered = new Set();
282
+ const toAgent = (line) => void process.stdout.write(`${line}\n`);
283
+ const refuse = (id) => toAgent(failure(id, REFUSED_CODE, DISCONNECTED_MESSAGE));
284
+ process.stdout.on("error", () => undefined);
285
+ const daemon = connect(path);
286
+ daemon.on("error", () => daemon.destroy());
287
+ const hungUp = new Promise((resolve) => daemon.once("close", () => {
288
+ for (const id of unanswered)
289
+ refuse(id);
290
+ unanswered.clear();
291
+ resolve();
292
+ }));
293
+ daemon.write(`${nonce}\n`);
294
+ onLines(daemon, (line) => {
295
+ const message = parse(line);
296
+ if (message?.id !== undefined && message.method === undefined) {
297
+ unanswered.delete(message.id);
298
+ }
299
+ toAgent(line);
300
+ });
301
+ const agent = onLines(process.stdin, (line) => {
302
+ const message = parse(line);
303
+ const id = typeof message?.method === "string" ? message.id : undefined;
304
+ if (!daemon.destroyed) {
305
+ if (id !== undefined)
306
+ unanswered.add(id);
307
+ daemon.write(`${line}\n`);
308
+ }
309
+ else if (id !== undefined) {
310
+ refuse(id);
311
+ }
312
+ });
313
+ await once(agent, "close");
314
+ daemon.end();
315
+ await hungUp;
316
+ }
317
+ //# sourceMappingURL=mcp-proxy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-proxy.js","sourceRoot":"","sources":["../src/mcp-proxy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACnC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,YAAY,EAA4B,MAAM,UAAU,CAAC;AAC3E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AA0BpC,MAAM,CAAC,MAAM,eAAe,GAC1B,4FAA4F,CAAC;AAE/F,MAAM,CAAC,MAAM,oBAAoB,GAC/B,sFAAsF,CAAC;AAEzF,MAAM,UAAU,GAAG,0BAA0B,CAAC;AAC9C,MAAM,SAAS,GAAG,yBAAyB,CAAC;AAC5C,MAAM,YAAY,GAAG,CAAC,KAAK,CAAC;AAC5B,MAAM,YAAY,GAAG,CAAC,KAAK,CAAC;AAqB5B,SAAS,KAAK,CAAC,IAAY;IACzB,IAAI,CAAC;QACH,MAAM,KAAK,GAAY,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACxC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YACzE,CAAC,CAAE,KAAiB;YACpB,CAAC,CAAC,SAAS,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,MAAM,MAAM,GAAG,CAAC,EAAa,EAAE,KAAc,EAAE,EAAE,CAC/C,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;AAExD,MAAM,OAAO,GAAG,CAAC,EAAa,EAAE,IAAY,EAAE,OAAe,EAAE,EAAE,CAC/D,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC;AAEnE,8FAA8F;AAC9F,MAAM,MAAM,GAAG,CAAC,IAAY,EAAE,EAAE,CAC9B,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAEhE,SAAS,YAAY,CAAC,IAAU,EAAE,OAAgB;IAChD,IAAI,OAAO,CAAC,EAAE,KAAK,SAAS,IAAI,OAAO,OAAO,CAAC,MAAM,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IACrF,MAAM,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC;IACvB,QAAQ,OAAO,CAAC,MAAM,EAAE,CAAC;QACvB,KAAK,MAAM;YACT,OAAO,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QACxB,KAAK,YAAY;YACf,OAAO,IAAI,CAAC,WAAW,KAAK,SAAS;gBACnC,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,YAAY,EAAE,eAAe,CAAC;gBAC5C,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QACnC,KAAK,YAAY;YACf,OAAO,IAAI,CAAC,KAAK,KAAK,SAAS;gBAC7B,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,YAAY,EAAE,eAAe,CAAC;gBAC5C,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QAC7B,KAAK,YAAY;YACf,OAAO,MAAM,CAAC,EAAE,EAAE;gBAChB,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC;gBAClD,OAAO,EAAE,IAAI;aACd,CAAC,CAAC;QACL;YACE,OAAO,OAAO,CAAC,EAAE,EAAE,YAAY,EAAE,eAAe,CAAC,CAAC;IACtD,CAAC;AACH,CAAC;AAED,KAAK,SAAS,CAAC,CAAC,cAAc,CAAC,IAAgC;IAC7D,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;IAClC,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,IAAI,IAAI,GAAa,EAAE,CAAC;IACxB,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,IAAI,EAAE,CAAC;QAC/B,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QAClD,IAAI,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC/B,OAAO,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC;YAClB,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YACrD,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;YAC/B,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;gBAChB,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;oBAAE,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAC3C,IAAI,GAAG,EAAE,CAAC;YACZ,CAAC;iBAAM,IAAI,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;gBACpC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC3D,CAAC;YACD,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;QAAE,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC7C,CAAC;AAED,SAAS,OAAO,CAAC,MAAc;IAC7B,OAAO,MAAM,KAAK,GAAG;QACnB,CAAC,CAAC,+FAA+F;QACjG,CAAC,CAAC,yBAAyB,MAAM,GAAG,CAAC;AACzC,CAAC;AAED,KAAK,UAAU,OAAO,CACpB,IAAU,EACV,KAAa,EACb,IAAY,EACZ,OAAgB,EAChB,KAA6B;IAE7B,MAAM,MAAM,GAAG,OAAO,OAAO,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC;IAChF,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IAC9B,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,cAAc,EAAE;YAC5D,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,aAAa,EAAE,UAAU,KAAK,EAAE;gBAChC,MAAM,EAAE,qCAAqC;gBAC7C,cAAc,EAAE,kBAAkB;gBAClC,GAAG,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,sBAAsB,EAAE,IAAI,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAClF;YACD,IAAI,EAAE,IAAI;YACV,MAAM,EAAE,UAAU,CAAC,MAAM;SAC1B,CAAC,CAAC;QACH,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;QACzB,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC;YAC/E,IAAI,QAAQ,CAAC,IAAI;gBACf,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,cAAc,CAAC,QAAQ,CAAC,IAAI,CAAC;oBAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9E,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YACnC,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE;gBAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5C,CAAC;QACD,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,KAAK,MAAM,IAAI,IAAI,MAAM,EAAE,CAAC;YAC1B,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;YAC3B,IAAI,MAAM,EAAE,OAAO,KAAK,KAAK;gBAAE,SAAS;YACxC,IAAI,MAAM,CAAC,EAAE,KAAK,OAAO,CAAC,EAAE,EAAE,CAAC;gBAC7B,QAAQ,GAAG,IAAI,CAAC;gBAChB,IAAI,MAAM,KAAK,YAAY,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;oBAC3D,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC;oBACjC,MAAM,OAAO,GAAI,MAAM,CAAC,MAAiD;yBACtE,eAAe,CAAC;oBACnB,IAAI,OAAO,OAAO,KAAK,QAAQ;wBAAE,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC;gBAClE,CAAC;gBACD,IAAI,MAAM,KAAK,YAAY,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS;oBACxD,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC;YAC/B,CAAC;YACD,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;QACtB,CAAC;QACD,IAAI,OAAO,CAAC,EAAE,KAAK,SAAS,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC1C,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,YAAY,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,OAAO,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;YAC7B,KAAK,CACH,UAAU,CAAC,MAAM,CAAC,OAAO;gBACvB,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,YAAY,EAAE,eAAe,CAAC;gBACpD,CAAC,CAAC,OAAO,CACL,OAAO,CAAC,EAAE,EACV,YAAY,EACZ,iCAAiC,MAAM,CAAC,KAAK,CAAC,EAAE,CACjD,CACN,CAAC;QACJ,CAAC;IACH,CAAC;YAAS,CAAC;QACT,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QACjC,QAAQ,CAAC,eAAe,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACtF,CAAC;AACH,CAAC;AAED,SAAS,MAAM,CAAC,IAAU,EAAE,IAAY,EAAE,KAA6B;IACrE,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,CAAC,OAAO;QAAE,OAAO;IACrB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;IACzB,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC3C,IAAI,MAAM;YAAE,KAAK,CAAC,MAAM,CAAC,CAAC;QAC1B,OAAO;IACT,CAAC;IACD,IACE,OAAO,CAAC,MAAM,KAAK,YAAY;QAC/B,IAAI,CAAC,KAAK,KAAK,SAAS;QACxB,OAAO,CAAC,EAAE,KAAK,SAAS,EACxB,CAAC;QACD,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QACtC,OAAO;IACT,CAAC;IACD,KAAK,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,UAAkB;IAC/C,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAgB,CAAC;IACzC,IAAI,MAA0B,CAAC;IAC/B,IAAI,UAA8B,CAAC;IAEnC,MAAM,MAAM,GAAG,CAAC,MAAc,EAAE,EAAE;QAChC,IAAI,IAAsB,CAAC;QAC3B,MAAM,KAAK,GAAG,CAAC,IAAY,EAAE,EAAE;YAC7B,IAAI,CAAC,MAAM,CAAC,SAAS;gBAAE,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC;QACnD,CAAC,CAAC;QACF,OAAO,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;YACvB,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;gBACvB,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;gBAC1B,OAAO;YACT,CAAC;YACD,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;YACjC,IAAI,IAAI;gBAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;;gBAC9B,MAAM,CAAC,OAAO,EAAE,CAAC;QACxB,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;QACvD,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;IAC7C,CAAC,CAAC;IAEF,OAAO;QACL,KAAK,CAAC,MAAM,CAAC,IAAI;YACf,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;YAC7D,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YAChC,MAAM,SAAS,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;YACvC,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBAC1C,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;gBAChC,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;YAC1C,CAAC,CAAC,CAAC;YACH,MAAM,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YACzB,MAAM,GAAG,SAAS,CAAC;YACnB,UAAU,GAAG,IAAI,CAAC;QACpB,CAAC;QACD,IAAI,CAAC,SAAS,EAAE,SAAS;YACvB,IAAI,UAAU,KAAK,SAAS;gBAC1B,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;YACzD,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;YACpD,MAAM,IAAI,GAAS;gBACjB,SAAS,EAAE,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;gBACxC,KAAK,EAAE,SAAS;gBAChB,eAAe,EAAE,SAAS;gBAC1B,WAAW,EAAE,SAAS;gBACtB,KAAK,EAAE,SAAS;gBAChB,QAAQ,EAAE,IAAI,GAAG,EAAE;gBACnB,OAAO,EAAE,IAAI,GAAG,EAAE;aACnB,CAAC;YACF,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;YAC1B,MAAM,GAAG,GAAG,GAAG,EAAE;gBACf,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;gBACvB,KAAK,MAAM,UAAU,IAAI,IAAI,CAAC,QAAQ;oBAAE,UAAU,CAAC,KAAK,EAAE,CAAC;YAC7D,CAAC,CAAC;YACF,OAAO;gBACL,KAAK;gBACL,MAAM,EAAE;oBACN,IAAI,EAAE,SAAS;oBACf,OAAO,EAAE,UAAU;oBACnB,IAAI,EAAE,CAAC,WAAW,CAAC;oBACnB,GAAG,EAAE,EAAE,CAAC,UAAU,CAAC,EAAE,UAAU,EAAE,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE;iBACtD;gBACD,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE;oBACf,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;gBAC3B,CAAC;gBACD,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE;oBACf,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS;wBAAE,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;gBACzD,CAAC;gBACD,GAAG;gBACH,OAAO,EAAE,GAAG,EAAE;oBACZ,GAAG,EAAE,CAAC;oBACN,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;oBACvB,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO;wBAAE,MAAM,CAAC,OAAO,EAAE,CAAC;gBACtD,CAAC;aACF,CAAC;QACJ,CAAC;QACD,KAAK,CAAC,aAAa;YACjB,MAAM,OAAO,GAAG,MAAM,CAAC;YACvB,MAAM,GAAG,SAAS,CAAC;YACnB,IAAI,CAAC,OAAO;gBAAE,OAAO;YACrB,OAAO,CAAC,KAAK,EAAE,CAAC;YAChB,IAAI,UAAU;gBAAE,MAAM,EAAE,CAAC,UAAU,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACxD,CAAC;QACD,KAAK,CAAC,KAAK;YACT,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;gBACrC,KAAK,MAAM,UAAU,IAAI,IAAI,CAAC,QAAQ;oBAAE,UAAU,CAAC,KAAK,EAAE,CAAC;gBAC3D,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO;oBAAE,MAAM,CAAC,OAAO,EAAE,CAAC;YACtD,CAAC;YACD,QAAQ,CAAC,KAAK,EAAE,CAAC;YACjB,MAAM,OAAO,GAAG,MAAM,CAAC;YACvB,MAAM,GAAG,SAAS,CAAC;YACnB,IAAI,OAAO,EAAE,CAAC;gBACZ,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;gBACrE,IAAI,UAAU;oBAAE,MAAM,EAAE,CAAC,UAAU,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YACxD,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,GAAsB;IAC7D,MAAM,IAAI,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC;IAC7B,MAAM,KAAK,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC;IAC7B,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,UAAU,QAAQ,SAAS,eAAe,CAAC,CAAC;IACpF,MAAM,UAAU,GAAG,IAAI,GAAG,EAAa,CAAC;IACxC,MAAM,OAAO,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC;IACzE,MAAM,MAAM,GAAG,CAAC,EAAa,EAAE,EAAE,CAC/B,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,YAAY,EAAE,oBAAoB,CAAC,CAAC,CAAC;IAC3D,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IAE5C,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;IAC3C,MAAM,MAAM,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAC3C,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE;QACxB,KAAK,MAAM,EAAE,IAAI,UAAU;YAAE,MAAM,CAAC,EAAE,CAAC,CAAC;QACxC,UAAU,CAAC,KAAK,EAAE,CAAC;QACnB,OAAO,EAAE,CAAC;IACZ,CAAC,CAAC,CACH,CAAC;IACF,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC;IAC3B,OAAO,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;QACvB,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;QAC5B,IAAI,OAAO,EAAE,EAAE,KAAK,SAAS,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC9D,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAChC,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC;IAChB,CAAC,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE;QAC5C,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;QAC5B,MAAM,EAAE,GAAG,OAAO,OAAO,EAAE,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;QACxE,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;YACtB,IAAI,EAAE,KAAK,SAAS;gBAAE,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACzC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC;QAC5B,CAAC;aAAM,IAAI,EAAE,KAAK,SAAS,EAAE,CAAC;YAC5B,MAAM,CAAC,EAAE,CAAC,CAAC;QACb,CAAC;IACH,CAAC,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAC3B,MAAM,CAAC,GAAG,EAAE,CAAC;IACb,MAAM,MAAM,CAAC;AACf,CAAC","sourcesContent":["import { randomBytes } from \"node:crypto\";\nimport { once } from \"node:events\";\nimport { chmod, mkdir, rm } from \"node:fs/promises\";\nimport { connect, createServer, type Server, type Socket } from \"node:net\";\nimport { dirname } from \"node:path\";\n\nimport type { Delegation } from \"@parlor/server\";\n\nimport type { StdioMcpServer } from \"./acp.js\";\nimport { onLines } from \"./lines.js\";\nimport { logEvent } from \"./log.js\";\n\n/**\n * The agent's MCP server for an app is this host: a stdio server (`parlor-host\n * mcp-proxy`) that connects back to the daemon's 0600 socket and names its session by a\n * nonce. The daemon forwards each message to `<app>/_parlor/mcp` with the current turn's\n * token, so no app credential is ever in the agent's config, logs or environment.\n */\nexport interface McpProxy {\n listen(socketPath: string): Promise<void>;\n open(appServer: string, appOrigin: string): ProxySession;\n stopListening(): Promise<void>;\n close(): Promise<void>;\n}\n\nexport interface ProxySession {\n readonly nonce: string;\n /** The entry for `session/new`. */\n readonly server: StdioMcpServer;\n /** Forward with this token until `end`; a `tools/call` outside a turn gets NO_TURN_MESSAGE. */\n begin(token: Delegation): void;\n renew(token: Delegation): void;\n end(): void;\n dispose(): void;\n}\n\nexport const NO_TURN_MESSAGE =\n \"No turn is active: the user has not asked anything right now. Wait for their next message.\";\n\nexport const DISCONNECTED_MESSAGE =\n \"This computer no longer serves the app: it was disconnected, or Parlor Host stopped.\";\n\nconst SOCKET_ENV = \"PARLOR_HOST_PROXY_SOCKET\";\nconst NONCE_ENV = \"PARLOR_HOST_PROXY_NONCE\";\nconst NO_TURN_CODE = -32000;\nconst REFUSED_CODE = -32001;\n\ntype JsonRpcId = string | number | null;\n\ninterface Message {\n readonly jsonrpc?: unknown;\n readonly id?: JsonRpcId;\n readonly method?: unknown;\n readonly result?: unknown;\n}\n\ninterface Live {\n readonly appOrigin: string;\n token: string | undefined;\n protocolVersion: string | undefined;\n initialized: unknown;\n tools: unknown;\n readonly inflight: Set<AbortController>;\n readonly sockets: Set<Socket>;\n}\n\nfunction parse(text: string): Message | undefined {\n try {\n const value: unknown = JSON.parse(text);\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n ? (value as Message)\n : undefined;\n } catch {\n return undefined;\n }\n}\n\nconst result = (id: JsonRpcId, value: unknown) =>\n JSON.stringify({ jsonrpc: \"2.0\", id, result: value });\n\nconst failure = (id: JsonRpcId, code: number, message: string) =>\n JSON.stringify({ jsonrpc: \"2.0\", id, error: { code, message } });\n\n/** One line per message on the agent's stdio: only a body with a raw newline is compacted. */\nconst asLine = (text: string) =>\n text.includes(\"\\n\") ? JSON.stringify(JSON.parse(text)) : text;\n\nfunction betweenTurns(live: Live, message: Message): string | undefined {\n if (message.id === undefined || typeof message.method !== \"string\") return undefined;\n const { id } = message;\n switch (message.method) {\n case \"ping\":\n return result(id, {});\n case \"initialize\":\n return live.initialized === undefined\n ? failure(id, NO_TURN_CODE, NO_TURN_MESSAGE)\n : result(id, live.initialized);\n case \"tools/list\":\n return live.tools === undefined\n ? failure(id, NO_TURN_CODE, NO_TURN_MESSAGE)\n : result(id, live.tools);\n case \"tools/call\":\n return result(id, {\n content: [{ type: \"text\", text: NO_TURN_MESSAGE }],\n isError: true,\n });\n default:\n return failure(id, NO_TURN_CODE, NO_TURN_MESSAGE);\n }\n}\n\nasync function* serverSentData(body: ReadableStream<Uint8Array>): AsyncGenerator<string> {\n const decoder = new TextDecoder();\n let buffer = \"\";\n let data: string[] = [];\n for await (const chunk of body) {\n buffer += decoder.decode(chunk, { stream: true });\n let end = buffer.indexOf(\"\\n\");\n while (end !== -1) {\n const line = buffer.slice(0, end).replace(/\\r$/, \"\");\n buffer = buffer.slice(end + 1);\n if (line === \"\") {\n if (data.length > 0) yield data.join(\"\\n\");\n data = [];\n } else if (line.startsWith(\"data:\")) {\n data.push(line.slice(line.startsWith(\"data: \") ? 6 : 5));\n }\n end = buffer.indexOf(\"\\n\");\n }\n }\n if (data.length > 0) yield data.join(\"\\n\");\n}\n\nfunction refusal(status: number): string {\n return status === 401\n ? \"This computer can no longer act for the app: the turn ended or the computer was disconnected.\"\n : `The app answered HTTP ${status}.`;\n}\n\nasync function forward(\n live: Live,\n token: string,\n line: string,\n message: Message,\n reply: (line: string) => void,\n): Promise<void> {\n const method = typeof message.method === \"string\" ? message.method : \"response\";\n const controller = new AbortController();\n live.inflight.add(controller);\n let status = 0;\n try {\n const response = await fetch(`${live.appOrigin}/_parlor/mcp`, {\n method: \"POST\",\n headers: {\n authorization: `Bearer ${token}`,\n accept: \"application/json, text/event-stream\",\n \"content-type\": \"application/json\",\n ...(live.protocolVersion ? { \"mcp-protocol-version\": live.protocolVersion } : {}),\n },\n body: line,\n signal: controller.signal,\n });\n status = response.status;\n const bodies: string[] = [];\n if ((response.headers.get(\"content-type\") ?? \"\").includes(\"text/event-stream\")) {\n if (response.body)\n for await (const data of serverSentData(response.body)) bodies.push(data);\n } else {\n const text = await response.text();\n if (text.trim() !== \"\") bodies.push(text);\n }\n let answered = false;\n for (const text of bodies) {\n const answer = parse(text);\n if (answer?.jsonrpc !== \"2.0\") continue;\n if (answer.id === message.id) {\n answered = true;\n if (method === \"initialize\" && answer.result !== undefined) {\n live.initialized = answer.result;\n const version = (answer.result as { readonly protocolVersion?: unknown })\n .protocolVersion;\n if (typeof version === \"string\") live.protocolVersion = version;\n }\n if (method === \"tools/list\" && answer.result !== undefined)\n live.tools = answer.result;\n }\n reply(asLine(text));\n }\n if (message.id !== undefined && !answered) {\n reply(failure(message.id, REFUSED_CODE, refusal(status)));\n }\n } catch (error) {\n if (message.id !== undefined) {\n reply(\n controller.signal.aborted\n ? failure(message.id, NO_TURN_CODE, NO_TURN_MESSAGE)\n : failure(\n message.id,\n REFUSED_CODE,\n `The app could not be reached: ${String(error)}`,\n ),\n );\n }\n } finally {\n live.inflight.delete(controller);\n logEvent(\"proxy_forward\", { appOrigin: live.appOrigin, rpcMethod: method, status });\n }\n}\n\nfunction handle(live: Live, line: string, reply: (line: string) => void): void {\n const message = parse(line);\n if (!message) return;\n const token = live.token;\n if (token === undefined) {\n const answer = betweenTurns(live, message);\n if (answer) reply(answer);\n return;\n }\n if (\n message.method === \"tools/list\" &&\n live.tools !== undefined &&\n message.id !== undefined\n ) {\n reply(result(message.id, live.tools));\n return;\n }\n void forward(live, token, line, message, reply);\n}\n\nexport function createMcpProxy(hostBinary: string): McpProxy {\n const sessions = new Map<string, Live>();\n let server: Server | undefined;\n let socketPath: string | undefined;\n\n const accept = (socket: Socket) => {\n let live: Live | undefined;\n const reply = (line: string) => {\n if (!socket.destroyed) socket.write(`${line}\\n`);\n };\n onLines(socket, (line) => {\n if (live !== undefined) {\n handle(live, line, reply);\n return;\n }\n live = sessions.get(line.trim());\n if (live) live.sockets.add(socket);\n else socket.destroy();\n });\n socket.on(\"close\", () => live?.sockets.delete(socket));\n socket.on(\"error\", () => socket.destroy());\n };\n\n return {\n async listen(path) {\n await mkdir(dirname(path), { recursive: true, mode: 0o700 });\n await rm(path, { force: true });\n const listening = createServer(accept);\n await new Promise<void>((resolve, reject) => {\n listening.once(\"error\", reject);\n listening.listen(path, () => resolve());\n });\n await chmod(path, 0o600);\n server = listening;\n socketPath = path;\n },\n open(appServer, appOrigin) {\n if (socketPath === undefined)\n throw new Error(\"The MCP proxy is not listening yet.\");\n const nonce = randomBytes(24).toString(\"base64url\");\n const live: Live = {\n appOrigin: appOrigin.replace(/\\/+$/, \"\"),\n token: undefined,\n protocolVersion: undefined,\n initialized: undefined,\n tools: undefined,\n inflight: new Set(),\n sockets: new Set(),\n };\n sessions.set(nonce, live);\n const end = () => {\n live.token = undefined;\n for (const controller of live.inflight) controller.abort();\n };\n return {\n nonce,\n server: {\n name: appServer,\n command: hostBinary,\n args: [\"mcp-proxy\"],\n env: { [SOCKET_ENV]: socketPath, [NONCE_ENV]: nonce },\n },\n begin: (token) => {\n live.token = token.token;\n },\n renew: (token) => {\n if (live.token !== undefined) live.token = token.token;\n },\n end,\n dispose: () => {\n end();\n sessions.delete(nonce);\n for (const socket of live.sockets) socket.destroy();\n },\n };\n },\n async stopListening() {\n const closing = server;\n server = undefined;\n if (!closing) return;\n closing.close();\n if (socketPath) await rm(socketPath, { force: true });\n },\n async close() {\n for (const live of sessions.values()) {\n for (const controller of live.inflight) controller.abort();\n for (const socket of live.sockets) socket.destroy();\n }\n sessions.clear();\n const closing = server;\n server = undefined;\n if (closing) {\n await new Promise<void>((resolve) => closing.close(() => resolve()));\n if (socketPath) await rm(socketPath, { force: true });\n }\n },\n };\n}\n\n/**\n * `parlor-host mcp-proxy`: stdio to the daemon socket and back. Once the daemon hangs up,\n * every request still unanswered, and every later one, is answered with an error here.\n */\nexport async function runMcpProxyProcess(env: NodeJS.ProcessEnv): Promise<void> {\n const path = env[SOCKET_ENV];\n const nonce = env[NONCE_ENV];\n if (!path || !nonce) throw new Error(`${SOCKET_ENV} and ${NONCE_ENV} must be set.`);\n const unanswered = new Set<JsonRpcId>();\n const toAgent = (line: string) => void process.stdout.write(`${line}\\n`);\n const refuse = (id: JsonRpcId) =>\n toAgent(failure(id, REFUSED_CODE, DISCONNECTED_MESSAGE));\n process.stdout.on(\"error\", () => undefined);\n\n const daemon = connect(path);\n daemon.on(\"error\", () => daemon.destroy());\n const hungUp = new Promise<void>((resolve) =>\n daemon.once(\"close\", () => {\n for (const id of unanswered) refuse(id);\n unanswered.clear();\n resolve();\n }),\n );\n daemon.write(`${nonce}\\n`);\n onLines(daemon, (line) => {\n const message = parse(line);\n if (message?.id !== undefined && message.method === undefined) {\n unanswered.delete(message.id);\n }\n toAgent(line);\n });\n\n const agent = onLines(process.stdin, (line) => {\n const message = parse(line);\n const id = typeof message?.method === \"string\" ? message.id : undefined;\n if (!daemon.destroyed) {\n if (id !== undefined) unanswered.add(id);\n daemon.write(`${line}\\n`);\n } else if (id !== undefined) {\n refuse(id);\n }\n });\n await once(agent, \"close\");\n daemon.end();\n await hungUp;\n}\n"]}
@@ -0,0 +1,7 @@
1
+ export declare function workspaceOf(workspaceRoot: string, appOrigin: string): string;
2
+ export declare function appServerOf(appName: string): string;
3
+ export declare function computerName(): string;
4
+ export declare function platformName(): string;
5
+ /** Why this computer cannot connect to an app yet; undefined on macOS. */
6
+ export declare function unsupportedHostPlatform(platform?: NodeJS.Platform): string | undefined;
7
+ //# sourceMappingURL=naming.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"naming.d.ts","sourceRoot":"","sources":["../src/naming.ts"],"names":[],"mappings":"AAIA,wBAAgB,WAAW,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAE5E;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAMnD;AAED,wBAAgB,YAAY,IAAI,MAAM,CAUrC;AAQD,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED,0EAA0E;AAC1E,wBAAgB,uBAAuB,CACrC,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,MAAM,GAAG,SAAS,CAKpB"}
package/dist/naming.js ADDED
@@ -0,0 +1,42 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { hostname } from "node:os";
3
+ import { join } from "node:path";
4
+ export function workspaceOf(workspaceRoot, appOrigin) {
5
+ return join(workspaceRoot, new URL(appOrigin).host.replaceAll(":", "-"));
6
+ }
7
+ export function appServerOf(appName) {
8
+ const name = appName
9
+ .toLowerCase()
10
+ .replace(/[^a-z0-9_-]+/g, "-")
11
+ .replace(/^-+|-+$/g, "");
12
+ return name || "app";
13
+ }
14
+ export function computerName() {
15
+ if (process.platform === "darwin") {
16
+ try {
17
+ const name = execFileSync("scutil", ["--get", "ComputerName"], {
18
+ encoding: "utf8",
19
+ }).trim();
20
+ if (name)
21
+ return name;
22
+ }
23
+ catch { }
24
+ }
25
+ return hostname();
26
+ }
27
+ const PLATFORMS = {
28
+ darwin: "macos",
29
+ win32: "windows",
30
+ linux: "linux",
31
+ };
32
+ export function platformName() {
33
+ return PLATFORMS[process.platform] ?? process.platform;
34
+ }
35
+ /** Why this computer cannot connect to an app yet; undefined on macOS. */
36
+ export function unsupportedHostPlatform(platform = process.platform) {
37
+ if (platform === "darwin")
38
+ return undefined;
39
+ const name = { win32: "Windows", linux: "Linux" }[platform] ?? platform;
40
+ return `Parlor Host connects to apps from macOS only for now; ${name} is not supported yet, because it keeps its keys in the macOS Keychain. \`parlor dev\` works on every platform.`;
41
+ }
42
+ //# sourceMappingURL=naming.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"naming.js","sourceRoot":"","sources":["../src/naming.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,UAAU,WAAW,CAAC,aAAqB,EAAE,SAAiB;IAClE,OAAO,IAAI,CAAC,aAAa,EAAE,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;AAC3E,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,MAAM,IAAI,GAAG,OAAO;SACjB,WAAW,EAAE;SACb,OAAO,CAAC,eAAe,EAAE,GAAG,CAAC;SAC7B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAC3B,OAAO,IAAI,IAAI,KAAK,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAClC,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,YAAY,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,cAAc,CAAC,EAAE;gBAC7D,QAAQ,EAAE,MAAM;aACjB,CAAC,CAAC,IAAI,EAAE,CAAC;YACV,IAAI,IAAI;gBAAE,OAAO,IAAI,CAAC;QACxB,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IACZ,CAAC;IACD,OAAO,QAAQ,EAAE,CAAC;AACpB,CAAC;AAED,MAAM,SAAS,GAAqC;IAClD,MAAM,EAAE,OAAO;IACf,KAAK,EAAE,SAAS;IAChB,KAAK,EAAE,OAAO;CACf,CAAC;AAEF,MAAM,UAAU,YAAY;IAC1B,OAAO,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC;AACzD,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,uBAAuB,CACrC,QAAQ,GAAoB,OAAO,CAAC,QAAQ;IAE5C,IAAI,QAAQ,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAC5C,MAAM,IAAI,GACP,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAY,CAAC,QAAkB,CAAC,IAAI,QAAQ,CAAC;IAClF,OAAO,yDAAyD,IAAI,iHAAiH,CAAC;AACxL,CAAC","sourcesContent":["import { execFileSync } from \"node:child_process\";\nimport { hostname } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport function workspaceOf(workspaceRoot: string, appOrigin: string): string {\n return join(workspaceRoot, new URL(appOrigin).host.replaceAll(\":\", \"-\"));\n}\n\nexport function appServerOf(appName: string): string {\n const name = appName\n .toLowerCase()\n .replace(/[^a-z0-9_-]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n return name || \"app\";\n}\n\nexport function computerName(): string {\n if (process.platform === \"darwin\") {\n try {\n const name = execFileSync(\"scutil\", [\"--get\", \"ComputerName\"], {\n encoding: \"utf8\",\n }).trim();\n if (name) return name;\n } catch {}\n }\n return hostname();\n}\n\nconst PLATFORMS: Readonly<Record<string, string>> = {\n darwin: \"macos\",\n win32: \"windows\",\n linux: \"linux\",\n};\n\nexport function platformName(): string {\n return PLATFORMS[process.platform] ?? process.platform;\n}\n\n/** Why this computer cannot connect to an app yet; undefined on macOS. */\nexport function unsupportedHostPlatform(\n platform: NodeJS.Platform = process.platform,\n): string | undefined {\n if (platform === \"darwin\") return undefined;\n const name =\n ({ win32: \"Windows\", linux: \"Linux\" } as const)[platform as string] ?? platform;\n return `Parlor Host connects to apps from macOS only for now; ${name} is not supported yet, because it keeps its keys in the macOS Keychain. \\`parlor dev\\` works on every platform.`;\n}\n"]}
@@ -0,0 +1,68 @@
1
+ import { type AccountSummary, type HostGrantResponse } from "@parlor/auth";
2
+ import type { HostPairing, PairingDevice } from "@parlor/capabilities";
3
+ import type { AppGrant, HostKeys } from "./grants.js";
4
+ export declare const PAIRING_TIMEOUT_MS: number;
5
+ export interface PairingReview {
6
+ readonly appName: string;
7
+ readonly appOrigin: string;
8
+ readonly account: AccountSummary;
9
+ readonly matchingCode: string;
10
+ }
11
+ /**
12
+ * `browser`: a one-shot 127.0.0.1 listener (RFC 8252) accepting exactly this attempt's
13
+ * `state`; the consent page is the one decision. `ipc`: the desktop shell's signed-in
14
+ * webview gets the code from the backend and hands it over Tauri IPC, with no consent page,
15
+ * so the shell's native dialog confirms the account the host got back.
16
+ */
17
+ export type PairingChannel = {
18
+ readonly kind: "browser";
19
+ open(url: string): Promise<void>;
20
+ } | {
21
+ readonly kind: "ipc";
22
+ authorize(pairing: HostPairing): Promise<string>;
23
+ confirm(review: PairingReview): Promise<boolean>;
24
+ };
25
+ export interface PairInput {
26
+ readonly appOrigin: string;
27
+ readonly hostName: string;
28
+ readonly platform: string;
29
+ readonly channel: PairingChannel;
30
+ readonly keys: HostKeys;
31
+ readonly workspaceRoot: string;
32
+ }
33
+ export type PairOutcome = {
34
+ readonly paired: true;
35
+ readonly grant: AppGrant;
36
+ readonly approvedBy: HostGrantResponse["approvedBy"];
37
+ } | {
38
+ readonly paired: false;
39
+ readonly reason: "declined_locally" | "declined_in_app" | "timeout";
40
+ };
41
+ export declare class UnrevokedGrant extends Error {
42
+ readonly grant: AppGrant;
43
+ readonly name = "UnrevokedGrant";
44
+ constructor(grant: AppGrant, cause: Error);
45
+ }
46
+ /**
47
+ * Only the host starts a pairing: it makes the PKCE verifier, `state`, the matching code
48
+ * and the key pair, so nothing from the web side can complete one.
49
+ */
50
+ export declare function pair(input: PairInput): Promise<PairOutcome>;
51
+ export interface LocalPairingReview {
52
+ readonly account: string;
53
+ readonly appName: string;
54
+ readonly matchingCode: string;
55
+ }
56
+ /**
57
+ * A pairing driven over the control socket by the desktop shell: `pairing` once the host
58
+ * made its key and code, `finish` with the code the signed-in page got from the backend,
59
+ * `confirm` with the user's answer in the native dialog.
60
+ */
61
+ export interface LocalPairing {
62
+ readonly pairing: Promise<HostPairing>;
63
+ finish(code: string): Promise<LocalPairingReview>;
64
+ confirm(connect: boolean): Promise<PairOutcome>;
65
+ }
66
+ export declare function startLocalPairing(input: Omit<PairInput, "channel">): LocalPairing;
67
+ export declare function pairingDeviceOf(value: unknown): PairingDevice | undefined;
68
+ //# sourceMappingURL=pairing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pairing.d.ts","sourceRoot":"","sources":["../src/pairing.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,KAAK,cAAc,EAEnB,KAAK,iBAAiB,EACvB,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAGvE,OAAO,KAAK,EAAE,QAAQ,EAAe,QAAQ,EAAE,MAAM,aAAa,CAAC;AAKnE,eAAO,MAAM,kBAAkB,QAAc,CAAC;AAE9C,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IACjC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GACtB;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,GAC9D;IACE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IACrB,SAAS,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACjD,OAAO,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAClD,CAAC;AAEN,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IACjC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,MAAM,WAAW,GACnB;IACE,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,iBAAiB,CAAC,YAAY,CAAC,CAAC;CACtD,GACD;IACE,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,kBAAkB,GAAG,iBAAiB,GAAG,SAAS,CAAC;CACrE,CAAC;AAEN,qBAAa,cAAe,SAAQ,KAAK;IAGrC,QAAQ,CAAC,KAAK,EAAE,QAAQ;IAF1B,SAAkB,IAAI,oBAAoB;IAC1C,YACW,KAAK,EAAE,QAAQ,EACxB,KAAK,EAAE,KAAK,EAGb;CACF;AA6HD;;;GAGG;AACH,wBAAsB,IAAI,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC,CAoGjE;AAmBD,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IACvC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAClD,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;CACjD;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,GAAG,YAAY,CA+CjF;AAgCD,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,GAAG,SAAS,CAmBzE"}