@parlor/agent-host 0.0.0-stage → 0.1.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.
- package/LICENSE +21 -0
- package/README.md +16 -2
- package/dist/acp.d.ts +165 -0
- package/dist/acp.d.ts.map +1 -0
- package/dist/acp.js +626 -0
- package/dist/acp.js.map +1 -0
- package/dist/adapter-launch.d.ts +7 -0
- package/dist/adapter-launch.d.ts.map +1 -0
- package/dist/adapter-launch.js +35 -0
- package/dist/adapter-launch.js.map +1 -0
- package/dist/agents.d.ts +32 -0
- package/dist/agents.d.ts.map +1 -0
- package/dist/agents.js +132 -0
- package/dist/agents.js.map +1 -0
- package/dist/assertion.d.ts +9 -0
- package/dist/assertion.d.ts.map +1 -0
- package/dist/assertion.js +22 -0
- package/dist/assertion.js.map +1 -0
- package/dist/control.d.ts +106 -0
- package/dist/control.d.ts.map +1 -0
- package/dist/control.js +32 -0
- package/dist/control.js.map +1 -0
- package/dist/daemon.d.ts +50 -0
- package/dist/daemon.d.ts.map +1 -0
- package/dist/daemon.js +543 -0
- package/dist/daemon.js.map +1 -0
- package/dist/dev-host.d.ts +5 -0
- package/dist/dev-host.d.ts.map +1 -0
- package/dist/dev-host.js +29 -0
- package/dist/dev-host.js.map +1 -0
- package/dist/device-list.d.ts +6 -0
- package/dist/device-list.d.ts.map +1 -0
- package/dist/device-list.js +30 -0
- package/dist/device-list.js.map +1 -0
- package/dist/embedded.d.ts +6 -0
- package/dist/embedded.d.ts.map +1 -0
- package/dist/embedded.js +3 -0
- package/dist/embedded.js.map +1 -0
- package/dist/fold.d.ts +38 -0
- package/dist/fold.d.ts.map +1 -0
- package/dist/fold.js +165 -0
- package/dist/fold.js.map +1 -0
- package/dist/grants.d.ts +54 -0
- package/dist/grants.d.ts.map +1 -0
- package/dist/grants.js +157 -0
- package/dist/grants.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/json-file.d.ts +6 -0
- package/dist/json-file.d.ts.map +1 -0
- package/dist/json-file.js +35 -0
- package/dist/json-file.js.map +1 -0
- package/dist/lines.d.ts +5 -0
- package/dist/lines.d.ts.map +1 -0
- package/dist/lines.js +9 -0
- package/dist/lines.js.map +1 -0
- package/dist/link-device.d.ts +28 -0
- package/dist/link-device.d.ts.map +1 -0
- package/dist/link-device.js +40 -0
- package/dist/link-device.js.map +1 -0
- package/dist/link.d.ts +52 -0
- package/dist/link.d.ts.map +1 -0
- package/dist/link.js +277 -0
- package/dist/link.js.map +1 -0
- package/dist/lock.d.ts +7 -0
- package/dist/lock.d.ts.map +1 -0
- package/dist/lock.js +105 -0
- package/dist/lock.js.map +1 -0
- package/dist/log.d.ts +3 -0
- package/dist/log.d.ts.map +1 -0
- package/dist/log.js +18 -0
- package/dist/log.js.map +1 -0
- package/dist/main.d.ts +3 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +520 -0
- package/dist/main.js.map +1 -0
- package/dist/mcp-proxy.d.ts +33 -0
- package/dist/mcp-proxy.d.ts.map +1 -0
- package/dist/mcp-proxy.js +317 -0
- package/dist/mcp-proxy.js.map +1 -0
- package/dist/naming.d.ts +7 -0
- package/dist/naming.d.ts.map +1 -0
- package/dist/naming.js +42 -0
- package/dist/naming.js.map +1 -0
- package/dist/pairing.d.ts +68 -0
- package/dist/pairing.d.ts.map +1 -0
- package/dist/pairing.js +312 -0
- package/dist/pairing.js.map +1 -0
- package/dist/paths.d.ts +19 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +23 -0
- package/dist/paths.js.map +1 -0
- package/dist/policy.d.ts +37 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +60 -0
- package/dist/policy.js.map +1 -0
- package/dist/pool.d.ts +34 -0
- package/dist/pool.d.ts.map +1 -0
- package/dist/pool.js +112 -0
- package/dist/pool.js.map +1 -0
- package/dist/prompter.d.ts +33 -0
- package/dist/prompter.d.ts.map +1 -0
- package/dist/prompter.js +98 -0
- package/dist/prompter.js.map +1 -0
- package/dist/release-main.d.ts +2 -0
- package/dist/release-main.d.ts.map +1 -0
- package/dist/release-main.js +50 -0
- package/dist/release-main.js.map +1 -0
- package/dist/release.d.ts +22 -0
- package/dist/release.d.ts.map +1 -0
- package/dist/release.js +81 -0
- package/dist/release.js.map +1 -0
- package/dist/revoke.d.ts +10 -0
- package/dist/revoke.d.ts.map +1 -0
- package/dist/revoke.js +23 -0
- package/dist/revoke.js.map +1 -0
- package/dist/self.d.ts +7 -0
- package/dist/self.d.ts.map +1 -0
- package/dist/self.js +10 -0
- package/dist/self.js.map +1 -0
- package/dist/service.d.ts +97 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +205 -0
- package/dist/service.js.map +1 -0
- package/dist/shell-path.d.ts +10 -0
- package/dist/shell-path.d.ts.map +1 -0
- package/dist/shell-path.js +64 -0
- package/dist/shell-path.js.map +1 -0
- package/dist/skeleton-kit.d.ts +8 -0
- package/dist/skeleton-kit.d.ts.map +1 -0
- package/dist/skeleton-kit.js +143 -0
- package/dist/skeleton-kit.js.map +1 -0
- package/dist/spec-kit.d.ts +102 -0
- package/dist/spec-kit.d.ts.map +1 -0
- package/dist/spec-kit.js +372 -0
- package/dist/spec-kit.js.map +1 -0
- package/dist/testing/fake-agent.d.ts +65 -0
- package/dist/testing/fake-agent.d.ts.map +1 -0
- package/dist/testing/fake-agent.js +238 -0
- package/dist/testing/fake-agent.js.map +1 -0
- package/dist/trust.d.ts +20 -0
- package/dist/trust.d.ts.map +1 -0
- package/dist/trust.js +27 -0
- package/dist/trust.js.map +1 -0
- package/dist/turn.d.ts +32 -0
- package/dist/turn.d.ts.map +1 -0
- package/dist/turn.js +650 -0
- package/dist/turn.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +6 -0
- package/dist/version.js.map +1 -0
- package/package.json +57 -3
package/dist/pairing.js
ADDED
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import { createHash, randomBytes, randomInt } from "node:crypto";
|
|
2
|
+
import { createServer } from "node:http";
|
|
3
|
+
import { HOST_PAIRING_FRAGMENT, } from "@parlor/auth";
|
|
4
|
+
import { hostAssertion } from "./assertion.js";
|
|
5
|
+
import { workspaceOf } from "./naming.js";
|
|
6
|
+
import { printable } from "./prompter.js";
|
|
7
|
+
import { revokeGrant } from "./revoke.js";
|
|
8
|
+
export const PAIRING_TIMEOUT_MS = 10 * 60_000;
|
|
9
|
+
export class UnrevokedGrant extends Error {
|
|
10
|
+
grant;
|
|
11
|
+
name = "UnrevokedGrant";
|
|
12
|
+
constructor(grant, cause) {
|
|
13
|
+
super(cause.message, { cause });
|
|
14
|
+
this.grant = grant;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const MATCHING_ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
|
|
18
|
+
const base64url = (bytes) => bytes.toString("base64url");
|
|
19
|
+
function matchingCode() {
|
|
20
|
+
const part = () => Array.from({ length: 3 }, () => MATCHING_ALPHABET[randomInt(MATCHING_ALPHABET.length)]).join("");
|
|
21
|
+
return `${part()}-${part()}`;
|
|
22
|
+
}
|
|
23
|
+
async function appNameOf(origin) {
|
|
24
|
+
const response = await fetch(`${origin}/_parlor/manifest`);
|
|
25
|
+
if (!response.ok) {
|
|
26
|
+
throw new Error(`${origin} answered HTTP ${response.status}; is it a Parlor app?`);
|
|
27
|
+
}
|
|
28
|
+
const manifest = (await response.json());
|
|
29
|
+
return typeof manifest.app?.name === "string"
|
|
30
|
+
? manifest.app.name
|
|
31
|
+
: new URL(origin).host;
|
|
32
|
+
}
|
|
33
|
+
const escapeHtml = (text) => printable(text).replace(/[&<>"']/g, (char) => `&#${char.charCodeAt(0)};`);
|
|
34
|
+
const RESULT_STYLE = `:root{color-scheme:light dark;--bg:#f6f6f4;--card:#fff;--text:#1d1d1b;--muted:#5f5f5a;--line:#e2e2dc}@media (prefers-color-scheme:dark){:root{--bg:#151514;--card:#1f1f1d;--text:#ededea;--muted:#a3a39d;--line:#34342f}}*{box-sizing:border-box}body{margin:0;min-height:100vh;display:grid;place-items:center;padding:16px;background:var(--bg);color:var(--text);font:15px/1.5 system-ui,-apple-system,"Segoe UI",sans-serif}main{width:100%;max-width:420px;background:var(--card);border:1px solid var(--line);border-radius:12px;padding:28px}h1{font-size:20px;line-height:1.3;margin:0 0 8px}p{margin:0;color:var(--muted)}`;
|
|
35
|
+
function resultPage(heading, detail) {
|
|
36
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>${escapeHtml(heading)}</title><style>${RESULT_STYLE}</style></head><body><main><h1>${escapeHtml(heading)}</h1><p>${escapeHtml(detail)}</p></main></body></html>`;
|
|
37
|
+
}
|
|
38
|
+
/** Holds the browser's request until the exchange settles, so the tab says what happened. */
|
|
39
|
+
async function loopback(state) {
|
|
40
|
+
let deliver = () => undefined;
|
|
41
|
+
const callback = new Promise((resolve) => (deliver = resolve));
|
|
42
|
+
let waiting;
|
|
43
|
+
const server = createServer((request, response) => {
|
|
44
|
+
const url = new URL(request.url ?? "/", "http://127.0.0.1");
|
|
45
|
+
if (waiting ||
|
|
46
|
+
url.pathname !== "/callback" ||
|
|
47
|
+
url.searchParams.get("state") !== state) {
|
|
48
|
+
response
|
|
49
|
+
.writeHead(404, { "content-type": "text/plain" })
|
|
50
|
+
.end("Not this connection.");
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
waiting = response;
|
|
54
|
+
const code = url.searchParams.get("code");
|
|
55
|
+
deliver(code ? { code } : { declined: true });
|
|
56
|
+
});
|
|
57
|
+
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
|
|
58
|
+
const { port } = server.address();
|
|
59
|
+
const answer = (heading, detail) => {
|
|
60
|
+
if (!waiting || waiting.headersSent)
|
|
61
|
+
return;
|
|
62
|
+
waiting
|
|
63
|
+
.writeHead(200, { "content-type": "text/html; charset=utf-8" })
|
|
64
|
+
.end(resultPage(heading, detail));
|
|
65
|
+
};
|
|
66
|
+
return {
|
|
67
|
+
redirectUri: `http://127.0.0.1:${port}/callback`,
|
|
68
|
+
callback,
|
|
69
|
+
answer,
|
|
70
|
+
redirect: (url) => {
|
|
71
|
+
if (!waiting || waiting.headersSent)
|
|
72
|
+
return;
|
|
73
|
+
waiting.writeHead(303, { location: url, "cache-control": "no-store" }).end();
|
|
74
|
+
},
|
|
75
|
+
close: () => {
|
|
76
|
+
answer("Parlor Host stopped", "Go back to your terminal.");
|
|
77
|
+
server.close();
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
async function exchange(origin, key, code, codeVerifier) {
|
|
82
|
+
const response = await fetch(`${origin}/_parlor/auth/hosts/token`, {
|
|
83
|
+
method: "POST",
|
|
84
|
+
headers: { "content-type": "application/json" },
|
|
85
|
+
body: JSON.stringify({
|
|
86
|
+
code,
|
|
87
|
+
codeVerifier,
|
|
88
|
+
publicKey: key.publicKey,
|
|
89
|
+
proof: await key.sign(new TextEncoder().encode(code)),
|
|
90
|
+
}),
|
|
91
|
+
});
|
|
92
|
+
const body = (await response.json().catch(() => undefined));
|
|
93
|
+
if (!response.ok || !body?.grant) {
|
|
94
|
+
throw new Error(body?.error?.message ?? `The app answered HTTP ${response.status}.`);
|
|
95
|
+
}
|
|
96
|
+
return body;
|
|
97
|
+
}
|
|
98
|
+
function timeout(ms) {
|
|
99
|
+
let timer;
|
|
100
|
+
const expired = new Promise((resolve) => {
|
|
101
|
+
timer = setTimeout(() => resolve("timeout"), ms);
|
|
102
|
+
});
|
|
103
|
+
return { expired, clear: () => clearTimeout(timer) };
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Only the host starts a pairing: it makes the PKCE verifier, `state`, the matching code
|
|
107
|
+
* and the key pair, so nothing from the web side can complete one.
|
|
108
|
+
*/
|
|
109
|
+
export async function pair(input) {
|
|
110
|
+
const origin = new URL(input.appOrigin).origin;
|
|
111
|
+
const appName = await appNameOf(origin);
|
|
112
|
+
const key = await input.keys.create(origin);
|
|
113
|
+
const codeVerifier = base64url(randomBytes(32));
|
|
114
|
+
const pairing = {
|
|
115
|
+
hostName: input.hostName,
|
|
116
|
+
platform: input.platform,
|
|
117
|
+
codeChallenge: base64url(createHash("sha256").update(codeVerifier).digest()),
|
|
118
|
+
state: base64url(randomBytes(16)),
|
|
119
|
+
matchingCode: matchingCode(),
|
|
120
|
+
keyThumbprint: key.thumbprint,
|
|
121
|
+
};
|
|
122
|
+
const grantOf = (granted) => ({
|
|
123
|
+
appOrigin: origin,
|
|
124
|
+
appName,
|
|
125
|
+
grantId: granted.grant.id,
|
|
126
|
+
hostId: granted.grant.hostId,
|
|
127
|
+
account: granted.account,
|
|
128
|
+
workspace: workspaceOf(input.workspaceRoot, origin),
|
|
129
|
+
createdAt: Date.now(),
|
|
130
|
+
});
|
|
131
|
+
const deadline = timeout(PAIRING_TIMEOUT_MS);
|
|
132
|
+
try {
|
|
133
|
+
if (input.channel.kind === "browser") {
|
|
134
|
+
const listener = await loopback(pairing.state);
|
|
135
|
+
try {
|
|
136
|
+
const query = new URLSearchParams({
|
|
137
|
+
redirectUri: listener.redirectUri,
|
|
138
|
+
...pairing,
|
|
139
|
+
});
|
|
140
|
+
await input.channel.open(`${origin}/_parlor/auth/hosts/authorize?${query}`);
|
|
141
|
+
const callback = await Promise.race([listener.callback, deadline.expired]);
|
|
142
|
+
if (callback === "timeout")
|
|
143
|
+
return { paired: false, reason: "timeout" };
|
|
144
|
+
if (!("code" in callback)) {
|
|
145
|
+
listener.answer("Not connected", "You cancelled the connection.");
|
|
146
|
+
return { paired: false, reason: "declined_in_app" };
|
|
147
|
+
}
|
|
148
|
+
let granted;
|
|
149
|
+
try {
|
|
150
|
+
granted = await exchange(origin, key, callback.code, codeVerifier);
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
listener.answer("Not connected", error.message);
|
|
154
|
+
throw error;
|
|
155
|
+
}
|
|
156
|
+
await key.keep(granted.grant.id);
|
|
157
|
+
const pairingSecret = base64url(randomBytes(32));
|
|
158
|
+
listener.redirect(`${origin}/#${new URLSearchParams({ [HOST_PAIRING_FRAGMENT]: pairingSecret })}`);
|
|
159
|
+
return {
|
|
160
|
+
paired: true,
|
|
161
|
+
grant: { ...grantOf(granted), pairingSecret },
|
|
162
|
+
approvedBy: granted.approvedBy,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
finally {
|
|
166
|
+
listener.close();
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
const callback = await Promise.race([
|
|
170
|
+
input.channel.authorize(pairing).then((code) => ({ code })),
|
|
171
|
+
deadline.expired,
|
|
172
|
+
]);
|
|
173
|
+
if (callback === "timeout")
|
|
174
|
+
return { paired: false, reason: "timeout" };
|
|
175
|
+
const granted = await exchange(origin, key, callback.code, codeVerifier);
|
|
176
|
+
const grant = grantOf(granted);
|
|
177
|
+
const confirmed = await Promise.race([
|
|
178
|
+
input.channel.confirm({
|
|
179
|
+
appName,
|
|
180
|
+
appOrigin: origin,
|
|
181
|
+
account: granted.account,
|
|
182
|
+
matchingCode: pairing.matchingCode,
|
|
183
|
+
}),
|
|
184
|
+
deadline.expired,
|
|
185
|
+
]);
|
|
186
|
+
if (confirmed !== true) {
|
|
187
|
+
await revokeGrant({
|
|
188
|
+
appOrigin: origin,
|
|
189
|
+
appName,
|
|
190
|
+
assert: (request) => hostAssertion((payload) => key.sign(payload), { kid: key.thumbprint, grantId: grant.grantId }, request),
|
|
191
|
+
}).catch(async (error) => {
|
|
192
|
+
await key.keep(grant.grantId);
|
|
193
|
+
throw new UnrevokedGrant({ ...grant, declined: true }, error);
|
|
194
|
+
});
|
|
195
|
+
return {
|
|
196
|
+
paired: false,
|
|
197
|
+
reason: confirmed === "timeout" ? "timeout" : "declined_locally",
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
await key.keep(grant.grantId);
|
|
201
|
+
return { paired: true, grant, approvedBy: granted.approvedBy };
|
|
202
|
+
}
|
|
203
|
+
finally {
|
|
204
|
+
deadline.clear();
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
function deferred() {
|
|
208
|
+
let resolve;
|
|
209
|
+
let reject;
|
|
210
|
+
const promise = new Promise((done, fail) => {
|
|
211
|
+
resolve = done;
|
|
212
|
+
reject = fail;
|
|
213
|
+
});
|
|
214
|
+
promise.catch(() => undefined);
|
|
215
|
+
return { promise, resolve, reject };
|
|
216
|
+
}
|
|
217
|
+
export function startLocalPairing(input) {
|
|
218
|
+
const ready = deferred();
|
|
219
|
+
const code = deferred();
|
|
220
|
+
const review = deferred();
|
|
221
|
+
const decision = deferred();
|
|
222
|
+
const outcome = pair({
|
|
223
|
+
...input,
|
|
224
|
+
channel: {
|
|
225
|
+
kind: "ipc",
|
|
226
|
+
authorize: (pairing) => {
|
|
227
|
+
ready.resolve(pairing);
|
|
228
|
+
return code.promise;
|
|
229
|
+
},
|
|
230
|
+
confirm: (shown) => {
|
|
231
|
+
review.resolve({
|
|
232
|
+
account: printable(shown.account.email ?? shown.account.name),
|
|
233
|
+
appName: printable(shown.appName),
|
|
234
|
+
matchingCode: shown.matchingCode,
|
|
235
|
+
});
|
|
236
|
+
return decision.promise;
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
outcome.then((result) => {
|
|
241
|
+
if (result.paired)
|
|
242
|
+
return;
|
|
243
|
+
const stopped = new Error(`The pairing ended: ${result.reason}.`);
|
|
244
|
+
ready.reject(stopped);
|
|
245
|
+
review.reject(stopped);
|
|
246
|
+
}, (error) => {
|
|
247
|
+
ready.reject(error);
|
|
248
|
+
review.reject(error);
|
|
249
|
+
});
|
|
250
|
+
outcome.catch(() => undefined);
|
|
251
|
+
return {
|
|
252
|
+
pairing: ready.promise,
|
|
253
|
+
finish: (value) => {
|
|
254
|
+
code.resolve(value);
|
|
255
|
+
return review.promise;
|
|
256
|
+
},
|
|
257
|
+
confirm: (connect) => {
|
|
258
|
+
decision.resolve(connect);
|
|
259
|
+
return outcome;
|
|
260
|
+
},
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
const isText = (value) => typeof value === "string" && value !== "";
|
|
264
|
+
const isPoint = (value) => {
|
|
265
|
+
if (typeof value !== "object" || value === null)
|
|
266
|
+
return false;
|
|
267
|
+
const key = value;
|
|
268
|
+
return key.kty === "EC" && key.crv === "P-256" && isText(key.x) && isText(key.y);
|
|
269
|
+
};
|
|
270
|
+
function presenceKeyOf(value) {
|
|
271
|
+
if (typeof value !== "object" || value === null)
|
|
272
|
+
return undefined;
|
|
273
|
+
const key = value;
|
|
274
|
+
if (!isPoint(key.publicKey))
|
|
275
|
+
return undefined;
|
|
276
|
+
const publicKey = { kty: "EC", crv: "P-256", x: key.publicKey.x, y: key.publicKey.y };
|
|
277
|
+
if (key.format === "webauthn" && isText(key.credentialId) && isText(key.rpId)) {
|
|
278
|
+
return {
|
|
279
|
+
format: "webauthn",
|
|
280
|
+
credentialId: key.credentialId,
|
|
281
|
+
rpId: key.rpId,
|
|
282
|
+
publicKey,
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
if (key.format === "es256-raw" && isText(key.keyId)) {
|
|
286
|
+
return { format: "es256-raw", keyId: key.keyId, publicKey };
|
|
287
|
+
}
|
|
288
|
+
return undefined;
|
|
289
|
+
}
|
|
290
|
+
export function pairingDeviceOf(value) {
|
|
291
|
+
if (typeof value !== "object" || value === null)
|
|
292
|
+
return undefined;
|
|
293
|
+
const device = value;
|
|
294
|
+
if (!isText(device.deviceId) || !isText(device.name) || !isText(device.platform)) {
|
|
295
|
+
return undefined;
|
|
296
|
+
}
|
|
297
|
+
if (!isPoint(device.signingKey))
|
|
298
|
+
return undefined;
|
|
299
|
+
return {
|
|
300
|
+
deviceId: device.deviceId,
|
|
301
|
+
name: device.name,
|
|
302
|
+
platform: device.platform,
|
|
303
|
+
signingKey: {
|
|
304
|
+
kty: "EC",
|
|
305
|
+
crv: "P-256",
|
|
306
|
+
x: device.signingKey.x,
|
|
307
|
+
y: device.signingKey.y,
|
|
308
|
+
},
|
|
309
|
+
presenceKey: presenceKeyOf(device.presenceKey),
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
//# sourceMappingURL=pairing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pairing.js","sourceRoot":"","sources":["../src/pairing.ts"],"names":[],"mappings":"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"}
|
package/dist/paths.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface HostPaths {
|
|
2
|
+
readonly root: string;
|
|
3
|
+
readonly grants: string;
|
|
4
|
+
readonly trust: string;
|
|
5
|
+
readonly policy: string;
|
|
6
|
+
readonly sessions: string;
|
|
7
|
+
readonly agents: string;
|
|
8
|
+
readonly control: string;
|
|
9
|
+
readonly proxy: string;
|
|
10
|
+
readonly versions: string;
|
|
11
|
+
/** What the login item points at; a link into `versions/<version>/`, never into an app bundle. */
|
|
12
|
+
readonly current: string;
|
|
13
|
+
readonly lock: string;
|
|
14
|
+
readonly logs: string;
|
|
15
|
+
readonly launchAgent: string;
|
|
16
|
+
readonly workspaces: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function hostPaths(root?: string): HostPaths;
|
|
19
|
+
//# sourceMappingURL=paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,kGAAkG;IAClG,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,wBAAgB,SAAS,CAAC,IAAI,GAAE,MAA2C,GAAG,SAAS,CAmBtF"}
|
package/dist/paths.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
export function hostPaths(root = join(homedir(), ".parlor", "host")) {
|
|
4
|
+
const run = join(root, "run");
|
|
5
|
+
const home = dirname(dirname(root));
|
|
6
|
+
return {
|
|
7
|
+
root,
|
|
8
|
+
grants: join(root, "grants.json"),
|
|
9
|
+
trust: join(root, "trust.json"),
|
|
10
|
+
policy: join(root, "policy.json"),
|
|
11
|
+
sessions: join(root, "sessions.json"),
|
|
12
|
+
agents: join(root, "agents.json"),
|
|
13
|
+
control: join(run, "control.sock"),
|
|
14
|
+
proxy: join(run, "proxy.sock"),
|
|
15
|
+
versions: join(root, "versions"),
|
|
16
|
+
current: join(root, "current"),
|
|
17
|
+
lock: join(root, "service.lock"),
|
|
18
|
+
logs: join(root, "logs"),
|
|
19
|
+
launchAgent: join(home, "Library", "LaunchAgents", "dev.parlor.host.plist"),
|
|
20
|
+
workspaces: join(home, "Parlor"),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAoB1C,MAAM,UAAU,SAAS,CAAC,IAAI,GAAW,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC;IACzE,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC9B,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACpC,OAAO;QACL,IAAI;QACJ,MAAM,EAAE,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC;QACjC,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC;QAC/B,MAAM,EAAE,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC;QACjC,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,eAAe,CAAC;QACrC,MAAM,EAAE,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC;QACjC,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC;QAClC,KAAK,EAAE,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC;QAC9B,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC;QAChC,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC;QAC9B,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC;QAChC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC;QACxB,WAAW,EAAE,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,cAAc,EAAE,uBAAuB,CAAC;QAC3E,UAAU,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC;KACjC,CAAC;AACJ,CAAC"}
|
package/dist/policy.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { AgentAsk } from "./acp.js";
|
|
2
|
+
import type { TrustedDevice } from "./trust.js";
|
|
3
|
+
/**
|
|
4
|
+
* What this computer enforces on remote turns, whatever the app or its backend says. Set
|
|
5
|
+
* only on this computer (`parlor-host config` or the Mac app's settings).
|
|
6
|
+
*/
|
|
7
|
+
export interface RemotePolicy {
|
|
8
|
+
/** `local`: an untrusted device is approved in a dialog here before its first prompt runs. */
|
|
9
|
+
readonly deviceApproval: "local" | "off";
|
|
10
|
+
/**
|
|
11
|
+
* Where an answer to the agent's own permission prompts counts: on any trusted device
|
|
12
|
+
* with presence, only on this computer, or (`parlor dev` only) anywhere.
|
|
13
|
+
*/
|
|
14
|
+
readonly answers: "presence" | "computer" | "off";
|
|
15
|
+
/**
|
|
16
|
+
* How long a linked device with Face ID or Touch ID sends before its next prompt needs them
|
|
17
|
+
* again. Absent: never.
|
|
18
|
+
*/
|
|
19
|
+
readonly promptPresence?: {
|
|
20
|
+
readonly maxAgeHours: number;
|
|
21
|
+
} | "off" | undefined;
|
|
22
|
+
}
|
|
23
|
+
export declare const DEFAULT_POLICY: RemotePolicy;
|
|
24
|
+
export declare const DEV_POLICY: RemotePolicy;
|
|
25
|
+
/** When `device`'s prompts need Face ID or Touch ID again; undefined when they never do. */
|
|
26
|
+
export declare function presenceDueAt(policy: RemotePolicy, device: TrustedDevice): number | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* The app's own tools are allowed once without asking: the server already enforces the
|
|
29
|
+
* app's `confirm: true`, and asking twice only teaches users to click through.
|
|
30
|
+
*/
|
|
31
|
+
export declare function isAppTool(ask: AgentAsk, appServer: string): boolean;
|
|
32
|
+
/** The ask item's `approval`; `undefined` lets any trusted device answer without presence. */
|
|
33
|
+
export declare function approvalFor(policy: RemotePolicy, ask: AgentAsk): "presence" | "computer" | undefined;
|
|
34
|
+
/** `base` with the settings `parlor-host config` wrote to `file`; read afresh on every call. */
|
|
35
|
+
export declare function loadPolicy(file: string, base: RemotePolicy): Promise<RemotePolicy>;
|
|
36
|
+
export declare function changePolicy(file: string, base: RemotePolicy, change: Partial<RemotePolicy>): Promise<RemotePolicy>;
|
|
37
|
+
//# sourceMappingURL=policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,8FAA8F;IAC9F,QAAQ,CAAC,cAAc,EAAE,OAAO,GAAG,KAAK,CAAC;IACzC;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,UAAU,GAAG,UAAU,GAAG,KAAK,CAAC;IAClD;;;OAGG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE;QAAE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,KAAK,GAAG,SAAS,CAAC;CAChF;AAED,eAAO,MAAM,cAAc,EAAE,YAI5B,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,YAIxB,CAAC;AAEF,4FAA4F;AAC5F,wBAAgB,aAAa,CAC3B,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE,aAAa,GACpB,MAAM,GAAG,SAAS,CAIpB;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAEnE;AAED,8FAA8F;AAC9F,wBAAgB,WAAW,CACzB,MAAM,EAAE,YAAY,EACpB,GAAG,EAAE,QAAQ,GACZ,UAAU,GAAG,UAAU,GAAG,SAAS,CAGrC;AAOD,gGAAgG;AAChG,wBAAsB,UAAU,CAC9B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,YAAY,CAAC,CAYvB;AAaD,wBAAsB,YAAY,CAChC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,YAAY,EAClB,MAAM,EAAE,OAAO,CAAC,YAAY,CAAC,GAC5B,OAAO,CAAC,YAAY,CAAC,CAGvB"}
|
package/dist/policy.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { jsonFile } from "./json-file.js";
|
|
2
|
+
export const DEFAULT_POLICY = {
|
|
3
|
+
deviceApproval: "local",
|
|
4
|
+
answers: "presence",
|
|
5
|
+
promptPresence: { maxAgeHours: 18 },
|
|
6
|
+
};
|
|
7
|
+
export const DEV_POLICY = {
|
|
8
|
+
deviceApproval: "off",
|
|
9
|
+
answers: "off",
|
|
10
|
+
promptPresence: "off",
|
|
11
|
+
};
|
|
12
|
+
/** When `device`'s prompts need Face ID or Touch ID again; undefined when they never do. */
|
|
13
|
+
export function presenceDueAt(policy, device) {
|
|
14
|
+
const setting = policy.promptPresence;
|
|
15
|
+
if (setting === undefined || setting === "off" || !device.presenceKey)
|
|
16
|
+
return undefined;
|
|
17
|
+
return (device.presenceAt ?? device.approvedAt) + setting.maxAgeHours * 3_600_000;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The app's own tools are allowed once without asking: the server already enforces the
|
|
21
|
+
* app's `confirm: true`, and asking twice only teaches users to click through.
|
|
22
|
+
*/
|
|
23
|
+
export function isAppTool(ask, appServer) {
|
|
24
|
+
return ask.kind === "permission" && ask.mcp?.server === appServer;
|
|
25
|
+
}
|
|
26
|
+
/** The ask item's `approval`; `undefined` lets any trusted device answer without presence. */
|
|
27
|
+
export function approvalFor(policy, ask) {
|
|
28
|
+
if (ask.kind === "question" || policy.answers === "off")
|
|
29
|
+
return undefined;
|
|
30
|
+
return policy.answers;
|
|
31
|
+
}
|
|
32
|
+
const choose = (allowed, value, base) => allowed.find((candidate) => candidate === value) ?? base;
|
|
33
|
+
/** `base` with the settings `parlor-host config` wrote to `file`; read afresh on every call. */
|
|
34
|
+
export async function loadPolicy(file, base) {
|
|
35
|
+
let overlay;
|
|
36
|
+
try {
|
|
37
|
+
overlay = await jsonFile(file).read();
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return base;
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
deviceApproval: choose(["local", "off"], overlay.deviceApproval, base.deviceApproval),
|
|
44
|
+
answers: choose(["presence", "computer", "off"], overlay.answers, base.answers),
|
|
45
|
+
promptPresence: promptPresenceOf(overlay.promptPresence, base.promptPresence),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function promptPresenceOf(value, base) {
|
|
49
|
+
if (value === "off")
|
|
50
|
+
return "off";
|
|
51
|
+
const hours = value?.maxAgeHours;
|
|
52
|
+
return typeof hours === "number" && Number.isFinite(hours) && hours > 0
|
|
53
|
+
? { maxAgeHours: hours }
|
|
54
|
+
: base;
|
|
55
|
+
}
|
|
56
|
+
export async function changePolicy(file, base, change) {
|
|
57
|
+
await jsonFile(file).change((overlay) => Object.assign(overlay, change));
|
|
58
|
+
return loadPolicy(file, base);
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"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"}
|
package/dist/pool.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { AgentConnection, AgentSession } from "./acp.js";
|
|
2
|
+
import type { AppGrant } from "./grants.js";
|
|
3
|
+
import type { ProxySession } from "./mcp-proxy.js";
|
|
4
|
+
export interface LiveSession {
|
|
5
|
+
readonly session: AgentSession;
|
|
6
|
+
readonly proxy: ProxySession;
|
|
7
|
+
}
|
|
8
|
+
export type PooledGrant = Pick<AppGrant, "appOrigin" | "grantId">;
|
|
9
|
+
/**
|
|
10
|
+
* Processes per (app, agent), so two apps never share an agent process or its MCP
|
|
11
|
+
* servers; sessions per (grant, thread, agent), so a thread keeps its context across
|
|
12
|
+
* turns and never carries it to another account.
|
|
13
|
+
*/
|
|
14
|
+
export interface SessionPool {
|
|
15
|
+
process(appOrigin: string, agentId: string): AgentConnection | undefined;
|
|
16
|
+
/**
|
|
17
|
+
* The running process, or the one `start` launches for every turn that asks meanwhile.
|
|
18
|
+
* A `drop` during the launch aborts `signal` and kills whatever `start` returns.
|
|
19
|
+
*/
|
|
20
|
+
launch(appOrigin: string, agentId: string, start: (signal: AbortSignal) => Promise<AgentConnection>): Promise<AgentConnection>;
|
|
21
|
+
session(grant: PooledGrant, threadId: string, agentId: string): LiveSession | undefined;
|
|
22
|
+
remember(grant: PooledGrant, threadId: string, agentId: string, session: LiveSession): void;
|
|
23
|
+
/** The last session id, from `sessions.json`, for `session/resume` after a restart. */
|
|
24
|
+
resumable(grant: PooledGrant, threadId: string, agentId: string): Promise<string | undefined>;
|
|
25
|
+
drop(appOrigin: string, agentId?: string): Promise<void>;
|
|
26
|
+
/** Erases the grant's remembered session ids, once the grant is gone. */
|
|
27
|
+
forget(grant: PooledGrant): Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Sessions live inside their process: `launch()` hands out the connection with its
|
|
31
|
+
* sessions tracked, so dropping a process forgets every session it held.
|
|
32
|
+
*/
|
|
33
|
+
export declare function createSessionPool(stateFile: string): SessionPool;
|
|
34
|
+
//# sourceMappingURL=pool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../src/pool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC9D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;CAC9B;AAED,MAAM,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,EAAE,WAAW,GAAG,SAAS,CAAC,CAAC;AAElE;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAAC;IACzE;;;OAGG;IACH,MAAM,CACJ,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,eAAe,CAAC,GACvD,OAAO,CAAC,eAAe,CAAC,CAAC;IAC5B,OAAO,CAAC,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAAC;IACxF,QAAQ,CACN,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,WAAW,GACnB,IAAI,CAAC;IACR,uFAAuF;IACvF,SAAS,CACP,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAC/B,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,yEAAyE;IACzE,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3C;AAYD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,CAyGhE"}
|
package/dist/pool.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { jsonFile } from "./json-file.js";
|
|
2
|
+
const keyOf = (...parts) => parts.join("\u0000");
|
|
3
|
+
const sessionKeyOf = (grant, threadId, agentId) => keyOf(grant.appOrigin, grant.grantId, threadId, agentId);
|
|
4
|
+
/**
|
|
5
|
+
* Sessions live inside their process: `launch()` hands out the connection with its
|
|
6
|
+
* sessions tracked, so dropping a process forgets every session it held.
|
|
7
|
+
*/
|
|
8
|
+
export function createSessionPool(stateFile) {
|
|
9
|
+
const processes = new Map();
|
|
10
|
+
const launching = new Map();
|
|
11
|
+
const owners = new WeakMap();
|
|
12
|
+
const sessions = new Map();
|
|
13
|
+
const remembered = jsonFile(stateFile);
|
|
14
|
+
const discard = (owner) => {
|
|
15
|
+
processes.delete(owner);
|
|
16
|
+
for (const [key, entry] of sessions) {
|
|
17
|
+
if (entry.owner !== owner)
|
|
18
|
+
continue;
|
|
19
|
+
entry.live.proxy.dispose();
|
|
20
|
+
sessions.delete(key);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
const adopt = (owner, connection) => {
|
|
24
|
+
const tracked = {
|
|
25
|
+
capabilities: connection.capabilities,
|
|
26
|
+
exited: connection.exited,
|
|
27
|
+
kill: () => connection.kill(),
|
|
28
|
+
openSession: async (input) => {
|
|
29
|
+
const session = await connection.openSession(input);
|
|
30
|
+
owners.set(session, owner);
|
|
31
|
+
return session;
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
processes.set(owner, tracked);
|
|
35
|
+
void connection.exited.then(() => {
|
|
36
|
+
if (processes.get(owner) === tracked)
|
|
37
|
+
discard(owner);
|
|
38
|
+
});
|
|
39
|
+
return tracked;
|
|
40
|
+
};
|
|
41
|
+
const ownedBy = (appOrigin, agentId) => (owner) => agentId === undefined
|
|
42
|
+
? owner.startsWith(keyOf(appOrigin, ""))
|
|
43
|
+
: owner === keyOf(appOrigin, agentId);
|
|
44
|
+
return {
|
|
45
|
+
process: (appOrigin, agentId) => processes.get(keyOf(appOrigin, agentId)),
|
|
46
|
+
launch(appOrigin, agentId, start) {
|
|
47
|
+
const owner = keyOf(appOrigin, agentId);
|
|
48
|
+
const running = processes.get(owner);
|
|
49
|
+
if (running)
|
|
50
|
+
return Promise.resolve(running);
|
|
51
|
+
const pending = launching.get(owner);
|
|
52
|
+
if (pending)
|
|
53
|
+
return pending.connection;
|
|
54
|
+
const controller = new AbortController();
|
|
55
|
+
const connection = (async () => {
|
|
56
|
+
try {
|
|
57
|
+
const started = await start(controller.signal);
|
|
58
|
+
if (controller.signal.aborted) {
|
|
59
|
+
await started.kill();
|
|
60
|
+
throw new Error("This computer stopped serving the app while the agent started.");
|
|
61
|
+
}
|
|
62
|
+
return adopt(owner, started);
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
if (launching.get(owner)?.controller === controller)
|
|
66
|
+
launching.delete(owner);
|
|
67
|
+
}
|
|
68
|
+
})();
|
|
69
|
+
launching.set(owner, { controller, connection });
|
|
70
|
+
return connection;
|
|
71
|
+
},
|
|
72
|
+
session: (grant, threadId, agentId) => sessions.get(sessionKeyOf(grant, threadId, agentId))?.live,
|
|
73
|
+
remember(grant, threadId, agentId, live) {
|
|
74
|
+
const owner = keyOf(grant.appOrigin, agentId);
|
|
75
|
+
if (owners.get(live.session) !== owner || processes.get(owner) === undefined) {
|
|
76
|
+
throw new Error("Remember a session opened through the pool's own process.");
|
|
77
|
+
}
|
|
78
|
+
const key = sessionKeyOf(grant, threadId, agentId);
|
|
79
|
+
sessions.set(key, { live, owner });
|
|
80
|
+
void remembered
|
|
81
|
+
.change((all) => void (all[key] = live.session.id))
|
|
82
|
+
.catch(() => undefined);
|
|
83
|
+
},
|
|
84
|
+
resumable: async (grant, threadId, agentId) => {
|
|
85
|
+
const all = await remembered.read().catch(() => ({}));
|
|
86
|
+
return all[sessionKeyOf(grant, threadId, agentId)];
|
|
87
|
+
},
|
|
88
|
+
async drop(appOrigin, agentId) {
|
|
89
|
+
const owned = ownedBy(appOrigin, agentId);
|
|
90
|
+
for (const [owner, pending] of launching) {
|
|
91
|
+
if (!owned(owner))
|
|
92
|
+
continue;
|
|
93
|
+
launching.delete(owner);
|
|
94
|
+
pending.controller.abort();
|
|
95
|
+
}
|
|
96
|
+
const dropped = [...processes].filter(([owner]) => owned(owner));
|
|
97
|
+
for (const [owner] of dropped)
|
|
98
|
+
discard(owner);
|
|
99
|
+
await Promise.all(dropped.map(([, connection]) => connection.kill()));
|
|
100
|
+
},
|
|
101
|
+
async forget(grant) {
|
|
102
|
+
const prefix = keyOf(grant.appOrigin, grant.grantId, "");
|
|
103
|
+
await remembered.change((all) => {
|
|
104
|
+
for (const key of Object.keys(all)) {
|
|
105
|
+
if (key.startsWith(prefix))
|
|
106
|
+
delete all[key];
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=pool.js.map
|