@openape/nest 0.3.0 → 1.0.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/dist/index.mjs CHANGED
@@ -1,8 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { createServer } from "http";
5
- import process2 from "process";
4
+ import process3 from "process";
5
+
6
+ // src/lib/intent-channel.ts
7
+ import { readdirSync, readFileSync as readFileSync2, renameSync, statSync, unlinkSync, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, chmodSync } from "fs";
8
+ import { homedir as homedir2 } from "os";
9
+ import { join as join2 } from "path";
6
10
 
7
11
  // src/api/agents.ts
8
12
  import { execFile } from "child_process";
@@ -57,14 +61,6 @@ function removeAgent(name) {
57
61
  // src/api/agents.ts
58
62
  var execFileAsync = promisify(execFile);
59
63
  var NAME_REGEX = /^[a-z][a-z0-9-]{0,23}$/;
60
- function handleNestStatus(_ctx) {
61
- return {
62
- agents: listAgents().length
63
- };
64
- }
65
- function handleAgentsList(_ctx) {
66
- return { agents: listAgents() };
67
- }
68
64
  async function handleAgentSpawn(ctx) {
69
65
  const body = ctx.body;
70
66
  const name = typeof body?.name === "string" ? body.name : "";
@@ -99,16 +95,19 @@ async function handleAgentSpawn(ctx) {
99
95
  } : void 0
100
96
  };
101
97
  upsertAgent(entry);
98
+ ctx.supervisor.reconcile(listAgents());
102
99
  return { name, email: entry.email, uid, home: entry.home };
103
100
  }
104
101
  async function handleAgentDestroy(ctx, name) {
105
102
  if (!NAME_REGEX.test(name)) throw new Error(`invalid agent name "${name}"`);
106
103
  const entry = findAgent(name);
107
104
  if (!entry) throw new Error(`agent "${name}" not registered with this nest`);
105
+ ctx.supervisor.stop(name);
108
106
  ctx.log(`nest: destroying agent "${name}"...`);
109
107
  const args = ["run", "--as", "root", "--", "apes", "agents", "destroy", name, "--force"];
110
108
  await execFileAsync(ctx.apesBin, args, { maxBuffer: 4 * 1024 * 1024 });
111
109
  removeAgent(name);
110
+ ctx.supervisor.reconcile(listAgents());
112
111
  return { name, removed: true };
113
112
  }
114
113
  async function readUidFromDscl(name) {
@@ -121,1514 +120,293 @@ async function readUidFromDscl(name) {
121
120
  return -1;
122
121
  }
123
122
 
124
- // src/lib/auth.ts
125
- import { hostname } from "os";
126
-
127
- // ../../node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/runtime/base64url.js
128
- import { Buffer as Buffer2 } from "buffer";
129
-
130
- // ../../node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/lib/buffer_utils.js
131
- var encoder = new TextEncoder();
132
- var decoder = new TextDecoder();
133
- var MAX_INT32 = 2 ** 32;
134
- function concat(...buffers) {
135
- const size = buffers.reduce((acc, { length }) => acc + length, 0);
136
- const buf = new Uint8Array(size);
137
- let i = 0;
138
- for (const buffer of buffers) {
139
- buf.set(buffer, i);
140
- i += buffer.length;
141
- }
142
- return buf;
143
- }
144
-
145
- // ../../node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/runtime/base64url.js
146
- function normalize(input) {
147
- let encoded = input;
148
- if (encoded instanceof Uint8Array) {
149
- encoded = decoder.decode(encoded);
150
- }
151
- return encoded;
152
- }
153
- var decode = (input) => new Uint8Array(Buffer2.from(normalize(input), "base64url"));
154
-
155
- // ../../node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/util/errors.js
156
- var JOSEError = class extends Error {
157
- static code = "ERR_JOSE_GENERIC";
158
- code = "ERR_JOSE_GENERIC";
159
- constructor(message2, options) {
160
- super(message2, options);
161
- this.name = this.constructor.name;
162
- Error.captureStackTrace?.(this, this.constructor);
163
- }
164
- };
165
- var JWTClaimValidationFailed = class extends JOSEError {
166
- static code = "ERR_JWT_CLAIM_VALIDATION_FAILED";
167
- code = "ERR_JWT_CLAIM_VALIDATION_FAILED";
168
- claim;
169
- reason;
170
- payload;
171
- constructor(message2, payload, claim = "unspecified", reason = "unspecified") {
172
- super(message2, { cause: { claim, reason, payload } });
173
- this.claim = claim;
174
- this.reason = reason;
175
- this.payload = payload;
176
- }
177
- };
178
- var JWTExpired = class extends JOSEError {
179
- static code = "ERR_JWT_EXPIRED";
180
- code = "ERR_JWT_EXPIRED";
181
- claim;
182
- reason;
183
- payload;
184
- constructor(message2, payload, claim = "unspecified", reason = "unspecified") {
185
- super(message2, { cause: { claim, reason, payload } });
186
- this.claim = claim;
187
- this.reason = reason;
188
- this.payload = payload;
189
- }
190
- };
191
- var JOSEAlgNotAllowed = class extends JOSEError {
192
- static code = "ERR_JOSE_ALG_NOT_ALLOWED";
193
- code = "ERR_JOSE_ALG_NOT_ALLOWED";
194
- };
195
- var JOSENotSupported = class extends JOSEError {
196
- static code = "ERR_JOSE_NOT_SUPPORTED";
197
- code = "ERR_JOSE_NOT_SUPPORTED";
198
- };
199
- var JWSInvalid = class extends JOSEError {
200
- static code = "ERR_JWS_INVALID";
201
- code = "ERR_JWS_INVALID";
202
- };
203
- var JWTInvalid = class extends JOSEError {
204
- static code = "ERR_JWT_INVALID";
205
- code = "ERR_JWT_INVALID";
206
- };
207
- var JWKSInvalid = class extends JOSEError {
208
- static code = "ERR_JWKS_INVALID";
209
- code = "ERR_JWKS_INVALID";
210
- };
211
- var JWKSNoMatchingKey = class extends JOSEError {
212
- static code = "ERR_JWKS_NO_MATCHING_KEY";
213
- code = "ERR_JWKS_NO_MATCHING_KEY";
214
- constructor(message2 = "no applicable key found in the JSON Web Key Set", options) {
215
- super(message2, options);
216
- }
217
- };
218
- var JWKSMultipleMatchingKeys = class extends JOSEError {
219
- [Symbol.asyncIterator];
220
- static code = "ERR_JWKS_MULTIPLE_MATCHING_KEYS";
221
- code = "ERR_JWKS_MULTIPLE_MATCHING_KEYS";
222
- constructor(message2 = "multiple matching keys found in the JSON Web Key Set", options) {
223
- super(message2, options);
224
- }
225
- };
226
- var JWKSTimeout = class extends JOSEError {
227
- static code = "ERR_JWKS_TIMEOUT";
228
- code = "ERR_JWKS_TIMEOUT";
229
- constructor(message2 = "request timed out", options) {
230
- super(message2, options);
231
- }
232
- };
233
- var JWSSignatureVerificationFailed = class extends JOSEError {
234
- static code = "ERR_JWS_SIGNATURE_VERIFICATION_FAILED";
235
- code = "ERR_JWS_SIGNATURE_VERIFICATION_FAILED";
236
- constructor(message2 = "signature verification failed", options) {
237
- super(message2, options);
238
- }
239
- };
240
-
241
- // ../../node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/runtime/is_key_object.js
242
- import * as util from "util";
243
- var is_key_object_default = (obj) => util.types.isKeyObject(obj);
244
-
245
- // ../../node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/runtime/webcrypto.js
246
- import * as crypto2 from "crypto";
247
- import * as util2 from "util";
248
- var webcrypto2 = crypto2.webcrypto;
249
- var webcrypto_default = webcrypto2;
250
- var isCryptoKey = (key) => util2.types.isCryptoKey(key);
251
-
252
- // ../../node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/lib/crypto_key.js
253
- function unusable(name, prop = "algorithm.name") {
254
- return new TypeError(`CryptoKey does not support this operation, its ${prop} must be ${name}`);
255
- }
256
- function isAlgorithm(algorithm, name) {
257
- return algorithm.name === name;
258
- }
259
- function getHashLength(hash) {
260
- return parseInt(hash.name.slice(4), 10);
261
- }
262
- function getNamedCurve(alg) {
263
- switch (alg) {
264
- case "ES256":
265
- return "P-256";
266
- case "ES384":
267
- return "P-384";
268
- case "ES512":
269
- return "P-521";
270
- default:
271
- throw new Error("unreachable");
272
- }
273
- }
274
- function checkUsage(key, usages) {
275
- if (usages.length && !usages.some((expected) => key.usages.includes(expected))) {
276
- let msg = "CryptoKey does not support this operation, its usages must include ";
277
- if (usages.length > 2) {
278
- const last = usages.pop();
279
- msg += `one of ${usages.join(", ")}, or ${last}.`;
280
- } else if (usages.length === 2) {
281
- msg += `one of ${usages[0]} or ${usages[1]}.`;
282
- } else {
283
- msg += `${usages[0]}.`;
284
- }
285
- throw new TypeError(msg);
286
- }
287
- }
288
- function checkSigCryptoKey(key, alg, ...usages) {
289
- switch (alg) {
290
- case "HS256":
291
- case "HS384":
292
- case "HS512": {
293
- if (!isAlgorithm(key.algorithm, "HMAC"))
294
- throw unusable("HMAC");
295
- const expected = parseInt(alg.slice(2), 10);
296
- const actual = getHashLength(key.algorithm.hash);
297
- if (actual !== expected)
298
- throw unusable(`SHA-${expected}`, "algorithm.hash");
299
- break;
300
- }
301
- case "RS256":
302
- case "RS384":
303
- case "RS512": {
304
- if (!isAlgorithm(key.algorithm, "RSASSA-PKCS1-v1_5"))
305
- throw unusable("RSASSA-PKCS1-v1_5");
306
- const expected = parseInt(alg.slice(2), 10);
307
- const actual = getHashLength(key.algorithm.hash);
308
- if (actual !== expected)
309
- throw unusable(`SHA-${expected}`, "algorithm.hash");
310
- break;
311
- }
312
- case "PS256":
313
- case "PS384":
314
- case "PS512": {
315
- if (!isAlgorithm(key.algorithm, "RSA-PSS"))
316
- throw unusable("RSA-PSS");
317
- const expected = parseInt(alg.slice(2), 10);
318
- const actual = getHashLength(key.algorithm.hash);
319
- if (actual !== expected)
320
- throw unusable(`SHA-${expected}`, "algorithm.hash");
321
- break;
322
- }
323
- case "EdDSA": {
324
- if (key.algorithm.name !== "Ed25519" && key.algorithm.name !== "Ed448") {
325
- throw unusable("Ed25519 or Ed448");
326
- }
327
- break;
328
- }
329
- case "Ed25519": {
330
- if (!isAlgorithm(key.algorithm, "Ed25519"))
331
- throw unusable("Ed25519");
332
- break;
333
- }
334
- case "ES256":
335
- case "ES384":
336
- case "ES512": {
337
- if (!isAlgorithm(key.algorithm, "ECDSA"))
338
- throw unusable("ECDSA");
339
- const expected = getNamedCurve(alg);
340
- const actual = key.algorithm.namedCurve;
341
- if (actual !== expected)
342
- throw unusable(expected, "algorithm.namedCurve");
343
- break;
344
- }
345
- default:
346
- throw new TypeError("CryptoKey does not support this operation");
347
- }
348
- checkUsage(key, usages);
349
- }
350
-
351
- // ../../node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/lib/invalid_key_input.js
352
- function message(msg, actual, ...types4) {
353
- types4 = types4.filter(Boolean);
354
- if (types4.length > 2) {
355
- const last = types4.pop();
356
- msg += `one of type ${types4.join(", ")}, or ${last}.`;
357
- } else if (types4.length === 2) {
358
- msg += `one of type ${types4[0]} or ${types4[1]}.`;
359
- } else {
360
- msg += `of type ${types4[0]}.`;
361
- }
362
- if (actual == null) {
363
- msg += ` Received ${actual}`;
364
- } else if (typeof actual === "function" && actual.name) {
365
- msg += ` Received function ${actual.name}`;
366
- } else if (typeof actual === "object" && actual != null) {
367
- if (actual.constructor?.name) {
368
- msg += ` Received an instance of ${actual.constructor.name}`;
369
- }
370
- }
371
- return msg;
372
- }
373
- var invalid_key_input_default = (actual, ...types4) => {
374
- return message("Key must be ", actual, ...types4);
375
- };
376
- function withAlg(alg, actual, ...types4) {
377
- return message(`Key for the ${alg} algorithm must be `, actual, ...types4);
378
- }
379
-
380
- // ../../node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/runtime/is_key_like.js
381
- var is_key_like_default = (key) => is_key_object_default(key) || isCryptoKey(key);
382
- var types3 = ["KeyObject"];
383
- if (globalThis.CryptoKey || webcrypto_default?.CryptoKey) {
384
- types3.push("CryptoKey");
385
- }
386
-
387
- // ../../node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/lib/is_disjoint.js
388
- var isDisjoint = (...headers) => {
389
- const sources = headers.filter(Boolean);
390
- if (sources.length === 0 || sources.length === 1) {
391
- return true;
392
- }
393
- let acc;
394
- for (const header of sources) {
395
- const parameters = Object.keys(header);
396
- if (!acc || acc.size === 0) {
397
- acc = new Set(parameters);
398
- continue;
399
- }
400
- for (const parameter of parameters) {
401
- if (acc.has(parameter)) {
402
- return false;
403
- }
404
- acc.add(parameter);
405
- }
406
- }
407
- return true;
408
- };
409
- var is_disjoint_default = isDisjoint;
410
-
411
- // ../../node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/lib/is_object.js
412
- function isObjectLike(value) {
413
- return typeof value === "object" && value !== null;
414
- }
415
- function isObject(input) {
416
- if (!isObjectLike(input) || Object.prototype.toString.call(input) !== "[object Object]") {
417
- return false;
418
- }
419
- if (Object.getPrototypeOf(input) === null) {
420
- return true;
421
- }
422
- let proto = input;
423
- while (Object.getPrototypeOf(proto) !== null) {
424
- proto = Object.getPrototypeOf(proto);
425
- }
426
- return Object.getPrototypeOf(input) === proto;
427
- }
428
-
429
- // ../../node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/runtime/get_named_curve.js
430
- import { KeyObject } from "crypto";
431
-
432
- // ../../node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/lib/is_jwk.js
433
- function isJWK(key) {
434
- return isObject(key) && typeof key.kty === "string";
435
- }
436
- function isPrivateJWK(key) {
437
- return key.kty !== "oct" && typeof key.d === "string";
438
- }
439
- function isPublicJWK(key) {
440
- return key.kty !== "oct" && typeof key.d === "undefined";
441
- }
442
- function isSecretJWK(key) {
443
- return isJWK(key) && key.kty === "oct" && typeof key.k === "string";
444
- }
445
-
446
- // ../../node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/runtime/get_named_curve.js
447
- var namedCurveToJOSE = (namedCurve) => {
448
- switch (namedCurve) {
449
- case "prime256v1":
450
- return "P-256";
451
- case "secp384r1":
452
- return "P-384";
453
- case "secp521r1":
454
- return "P-521";
455
- case "secp256k1":
456
- return "secp256k1";
457
- default:
458
- throw new JOSENotSupported("Unsupported key curve for this operation");
459
- }
460
- };
461
- var getNamedCurve2 = (kee, raw) => {
462
- let key;
463
- if (isCryptoKey(kee)) {
464
- key = KeyObject.from(kee);
465
- } else if (is_key_object_default(kee)) {
466
- key = kee;
467
- } else if (isJWK(kee)) {
468
- return kee.crv;
469
- } else {
470
- throw new TypeError(invalid_key_input_default(kee, ...types3));
471
- }
472
- if (key.type === "secret") {
473
- throw new TypeError('only "private" or "public" type keys can be used for this operation');
474
- }
475
- switch (key.asymmetricKeyType) {
476
- case "ed25519":
477
- case "ed448":
478
- return `Ed${key.asymmetricKeyType.slice(2)}`;
479
- case "x25519":
480
- case "x448":
481
- return `X${key.asymmetricKeyType.slice(1)}`;
482
- case "ec": {
483
- const namedCurve = key.asymmetricKeyDetails.namedCurve;
484
- if (raw) {
485
- return namedCurve;
486
- }
487
- return namedCurveToJOSE(namedCurve);
123
+ // src/lib/intent-channel.ts
124
+ var POLL_MS = 1e3;
125
+ var INTENTS_DIR = join2(homedir2(), "intents");
126
+ var IntentChannel = class {
127
+ constructor(deps) {
128
+ this.deps = deps;
129
+ mkdirSync2(INTENTS_DIR, { recursive: true });
130
+ chmodSync(INTENTS_DIR, 504);
131
+ }
132
+ timer;
133
+ inflight = /* @__PURE__ */ new Set();
134
+ start() {
135
+ if (this.timer) return;
136
+ this.timer = setInterval(() => void this.tick(), POLL_MS);
137
+ this.deps.log(`intent-channel: polling ${INTENTS_DIR}`);
138
+ }
139
+ stop() {
140
+ if (this.timer) clearInterval(this.timer);
141
+ this.timer = void 0;
142
+ }
143
+ async tick() {
144
+ let entries;
145
+ try {
146
+ entries = readdirSync(INTENTS_DIR);
147
+ } catch {
148
+ return;
488
149
  }
489
- default:
490
- throw new TypeError("Invalid asymmetric key type for this operation");
491
- }
492
- };
493
- var get_named_curve_default = getNamedCurve2;
494
-
495
- // ../../node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/runtime/check_key_length.js
496
- import { KeyObject as KeyObject2 } from "crypto";
497
- var check_key_length_default = (key, alg) => {
498
- let modulusLength;
499
- try {
500
- if (key instanceof KeyObject2) {
501
- modulusLength = key.asymmetricKeyDetails?.modulusLength;
502
- } else {
503
- modulusLength = Buffer.from(key.n, "base64url").byteLength << 3;
150
+ for (const f of entries) {
151
+ if (!f.endsWith(".json")) continue;
152
+ if (this.inflight.has(f)) continue;
153
+ this.inflight.add(f);
154
+ void this.process(f).finally(() => this.inflight.delete(f));
504
155
  }
505
- } catch {
506
- }
507
- if (typeof modulusLength !== "number" || modulusLength < 2048) {
508
- throw new TypeError(`${alg} requires key modulusLength to be 2048 bits or larger`);
509
- }
510
- };
511
-
512
- // ../../node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/runtime/jwk_to_key.js
513
- import { createPrivateKey, createPublicKey } from "crypto";
514
- var parse = (key) => {
515
- if (key.d) {
516
- return createPrivateKey({ format: "jwk", key });
517
- }
518
- return createPublicKey({ format: "jwk", key });
519
- };
520
- var jwk_to_key_default = parse;
521
-
522
- // ../../node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/key/import.js
523
- async function importJWK(jwk, alg) {
524
- if (!isObject(jwk)) {
525
- throw new TypeError("JWK must be an object");
526
156
  }
527
- alg ||= jwk.alg;
528
- switch (jwk.kty) {
529
- case "oct":
530
- if (typeof jwk.k !== "string" || !jwk.k) {
531
- throw new TypeError('missing "k" (Key Value) Parameter value');
532
- }
533
- return decode(jwk.k);
534
- case "RSA":
535
- if ("oth" in jwk && jwk.oth !== void 0) {
536
- throw new JOSENotSupported('RSA JWK "oth" (Other Primes Info) Parameter value is not supported');
157
+ async process(filename) {
158
+ const path = join2(INTENTS_DIR, filename);
159
+ let intent;
160
+ try {
161
+ const raw = readFileSync2(path, "utf8");
162
+ intent = JSON.parse(raw);
163
+ } catch (err) {
164
+ this.deps.log(`intent-channel: failed to read ${filename}: ${err instanceof Error ? err.message : String(err)}`);
165
+ try {
166
+ unlinkSync(path);
167
+ } catch {
537
168
  }
538
- case "EC":
539
- case "OKP":
540
- return jwk_to_key_default({ ...jwk, alg });
541
- default:
542
- throw new JOSENotSupported('Unsupported "kty" (Key Type) Parameter value');
543
- }
544
- }
545
-
546
- // ../../node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/lib/check_key_type.js
547
- var tag = (key) => key?.[Symbol.toStringTag];
548
- var jwkMatchesOp = (alg, key, usage) => {
549
- if (key.use !== void 0 && key.use !== "sig") {
550
- throw new TypeError("Invalid key for this operation, when present its use must be sig");
551
- }
552
- if (key.key_ops !== void 0 && key.key_ops.includes?.(usage) !== true) {
553
- throw new TypeError(`Invalid key for this operation, when present its key_ops must include ${usage}`);
554
- }
555
- if (key.alg !== void 0 && key.alg !== alg) {
556
- throw new TypeError(`Invalid key for this operation, when present its alg must be ${alg}`);
557
- }
558
- return true;
559
- };
560
- var symmetricTypeCheck = (alg, key, usage, allowJwk) => {
561
- if (key instanceof Uint8Array)
562
- return;
563
- if (allowJwk && isJWK(key)) {
564
- if (isSecretJWK(key) && jwkMatchesOp(alg, key, usage))
565
169
  return;
566
- throw new TypeError(`JSON Web Key for symmetric algorithms must have JWK "kty" (Key Type) equal to "oct" and the JWK "k" (Key Value) present`);
567
- }
568
- if (!is_key_like_default(key)) {
569
- throw new TypeError(withAlg(alg, key, ...types3, "Uint8Array", allowJwk ? "JSON Web Key" : null));
570
- }
571
- if (key.type !== "secret") {
572
- throw new TypeError(`${tag(key)} instances for symmetric algorithms must be of type "secret"`);
573
- }
574
- };
575
- var asymmetricTypeCheck = (alg, key, usage, allowJwk) => {
576
- if (allowJwk && isJWK(key)) {
577
- switch (usage) {
578
- case "sign":
579
- if (isPrivateJWK(key) && jwkMatchesOp(alg, key, usage))
580
- return;
581
- throw new TypeError(`JSON Web Key for this operation be a private JWK`);
582
- case "verify":
583
- if (isPublicJWK(key) && jwkMatchesOp(alg, key, usage))
584
- return;
585
- throw new TypeError(`JSON Web Key for this operation be a public JWK`);
586
- }
587
- }
588
- if (!is_key_like_default(key)) {
589
- throw new TypeError(withAlg(alg, key, ...types3, allowJwk ? "JSON Web Key" : null));
590
- }
591
- if (key.type === "secret") {
592
- throw new TypeError(`${tag(key)} instances for asymmetric algorithms must not be of type "secret"`);
593
- }
594
- if (usage === "sign" && key.type === "public") {
595
- throw new TypeError(`${tag(key)} instances for asymmetric algorithm signing must be of type "private"`);
596
- }
597
- if (usage === "decrypt" && key.type === "public") {
598
- throw new TypeError(`${tag(key)} instances for asymmetric algorithm decryption must be of type "private"`);
599
- }
600
- if (key.algorithm && usage === "verify" && key.type === "private") {
601
- throw new TypeError(`${tag(key)} instances for asymmetric algorithm verifying must be of type "public"`);
602
- }
603
- if (key.algorithm && usage === "encrypt" && key.type === "private") {
604
- throw new TypeError(`${tag(key)} instances for asymmetric algorithm encryption must be of type "public"`);
605
- }
606
- };
607
- function checkKeyType(allowJwk, alg, key, usage) {
608
- const symmetric = alg.startsWith("HS") || alg === "dir" || alg.startsWith("PBES2") || /^A\d{3}(?:GCM)?KW$/.test(alg);
609
- if (symmetric) {
610
- symmetricTypeCheck(alg, key, usage, allowJwk);
611
- } else {
612
- asymmetricTypeCheck(alg, key, usage, allowJwk);
613
- }
614
- }
615
- var check_key_type_default = checkKeyType.bind(void 0, false);
616
- var checkKeyTypeWithJwk = checkKeyType.bind(void 0, true);
617
-
618
- // ../../node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/lib/validate_crit.js
619
- function validateCrit(Err, recognizedDefault, recognizedOption, protectedHeader, joseHeader) {
620
- if (joseHeader.crit !== void 0 && protectedHeader?.crit === void 0) {
621
- throw new Err('"crit" (Critical) Header Parameter MUST be integrity protected');
622
- }
623
- if (!protectedHeader || protectedHeader.crit === void 0) {
624
- return /* @__PURE__ */ new Set();
625
- }
626
- if (!Array.isArray(protectedHeader.crit) || protectedHeader.crit.length === 0 || protectedHeader.crit.some((input) => typeof input !== "string" || input.length === 0)) {
627
- throw new Err('"crit" (Critical) Header Parameter MUST be an array of non-empty strings when present');
628
- }
629
- let recognized;
630
- if (recognizedOption !== void 0) {
631
- recognized = new Map([...Object.entries(recognizedOption), ...recognizedDefault.entries()]);
632
- } else {
633
- recognized = recognizedDefault;
634
- }
635
- for (const parameter of protectedHeader.crit) {
636
- if (!recognized.has(parameter)) {
637
- throw new JOSENotSupported(`Extension Header Parameter "${parameter}" is not recognized`);
638
- }
639
- if (joseHeader[parameter] === void 0) {
640
- throw new Err(`Extension Header Parameter "${parameter}" is missing`);
641
170
  }
642
- if (recognized.get(parameter) && protectedHeader[parameter] === void 0) {
643
- throw new Err(`Extension Header Parameter "${parameter}" MUST be integrity protected`);
644
- }
645
- }
646
- return new Set(protectedHeader.crit);
647
- }
648
- var validate_crit_default = validateCrit;
649
-
650
- // ../../node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/lib/validate_algorithms.js
651
- var validateAlgorithms = (option, algorithms) => {
652
- if (algorithms !== void 0 && (!Array.isArray(algorithms) || algorithms.some((s) => typeof s !== "string"))) {
653
- throw new TypeError(`"${option}" option must be an array of strings`);
654
- }
655
- if (!algorithms) {
656
- return void 0;
657
- }
658
- return new Set(algorithms);
659
- };
660
- var validate_algorithms_default = validateAlgorithms;
661
-
662
- // ../../node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/runtime/verify.js
663
- import * as crypto4 from "crypto";
664
- import { promisify as promisify3 } from "util";
665
-
666
- // ../../node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/runtime/dsa_digest.js
667
- function dsaDigest(alg) {
668
- switch (alg) {
669
- case "PS256":
670
- case "RS256":
671
- case "ES256":
672
- case "ES256K":
673
- return "sha256";
674
- case "PS384":
675
- case "RS384":
676
- case "ES384":
677
- return "sha384";
678
- case "PS512":
679
- case "RS512":
680
- case "ES512":
681
- return "sha512";
682
- case "Ed25519":
683
- case "EdDSA":
684
- return void 0;
685
- default:
686
- throw new JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`);
687
- }
688
- }
689
-
690
- // ../../node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/runtime/node_key.js
691
- import { constants, KeyObject as KeyObject3 } from "crypto";
692
- var ecCurveAlgMap = /* @__PURE__ */ new Map([
693
- ["ES256", "P-256"],
694
- ["ES256K", "secp256k1"],
695
- ["ES384", "P-384"],
696
- ["ES512", "P-521"]
697
- ]);
698
- function keyForCrypto(alg, key) {
699
- let asymmetricKeyType;
700
- let asymmetricKeyDetails;
701
- let isJWK2;
702
- if (key instanceof KeyObject3) {
703
- asymmetricKeyType = key.asymmetricKeyType;
704
- asymmetricKeyDetails = key.asymmetricKeyDetails;
705
- } else {
706
- isJWK2 = true;
707
- switch (key.kty) {
708
- case "RSA":
709
- asymmetricKeyType = "rsa";
710
- break;
711
- case "EC":
712
- asymmetricKeyType = "ec";
713
- break;
714
- case "OKP": {
715
- if (key.crv === "Ed25519") {
716
- asymmetricKeyType = "ed25519";
171
+ this.deps.log(`intent-channel: processing ${intent.action} (id=${intent.id})`);
172
+ let response;
173
+ try {
174
+ const ctx = {
175
+ url: new URL("intent:/"),
176
+ body: intent,
177
+ log: this.deps.log,
178
+ apesBin: this.deps.apesBin,
179
+ caller: "<intent-channel>",
180
+ grantId: intent.id,
181
+ supervisor: this.deps.supervisor
182
+ };
183
+ let result;
184
+ switch (intent.action) {
185
+ case "spawn":
186
+ result = await handleAgentSpawn(ctx);
717
187
  break;
718
- }
719
- if (key.crv === "Ed448") {
720
- asymmetricKeyType = "ed448";
188
+ case "destroy":
189
+ result = await handleAgentDestroy(ctx, intent.name);
721
190
  break;
722
- }
723
- throw new TypeError("Invalid key for this operation, its crv must be Ed25519 or Ed448");
724
- }
725
- default:
726
- throw new TypeError("Invalid key for this operation, its kty must be RSA, OKP, or EC");
727
- }
728
- }
729
- let options;
730
- switch (alg) {
731
- case "Ed25519":
732
- if (asymmetricKeyType !== "ed25519") {
733
- throw new TypeError(`Invalid key for this operation, its asymmetricKeyType must be ed25519`);
734
- }
735
- break;
736
- case "EdDSA":
737
- if (!["ed25519", "ed448"].includes(asymmetricKeyType)) {
738
- throw new TypeError("Invalid key for this operation, its asymmetricKeyType must be ed25519 or ed448");
739
- }
740
- break;
741
- case "RS256":
742
- case "RS384":
743
- case "RS512":
744
- if (asymmetricKeyType !== "rsa") {
745
- throw new TypeError("Invalid key for this operation, its asymmetricKeyType must be rsa");
746
- }
747
- check_key_length_default(key, alg);
748
- break;
749
- case "PS256":
750
- case "PS384":
751
- case "PS512":
752
- if (asymmetricKeyType === "rsa-pss") {
753
- const { hashAlgorithm, mgf1HashAlgorithm, saltLength } = asymmetricKeyDetails;
754
- const length = parseInt(alg.slice(-3), 10);
755
- if (hashAlgorithm !== void 0 && (hashAlgorithm !== `sha${length}` || mgf1HashAlgorithm !== hashAlgorithm)) {
756
- throw new TypeError(`Invalid key for this operation, its RSA-PSS parameters do not meet the requirements of "alg" ${alg}`);
757
- }
758
- if (saltLength !== void 0 && saltLength > length >> 3) {
759
- throw new TypeError(`Invalid key for this operation, its RSA-PSS parameter saltLength does not meet the requirements of "alg" ${alg}`);
760
- }
761
- } else if (asymmetricKeyType !== "rsa") {
762
- throw new TypeError("Invalid key for this operation, its asymmetricKeyType must be rsa or rsa-pss");
763
- }
764
- check_key_length_default(key, alg);
765
- options = {
766
- padding: constants.RSA_PKCS1_PSS_PADDING,
767
- saltLength: constants.RSA_PSS_SALTLEN_DIGEST
768
- };
769
- break;
770
- case "ES256":
771
- case "ES256K":
772
- case "ES384":
773
- case "ES512": {
774
- if (asymmetricKeyType !== "ec") {
775
- throw new TypeError("Invalid key for this operation, its asymmetricKeyType must be ec");
776
- }
777
- const actual = get_named_curve_default(key);
778
- const expected = ecCurveAlgMap.get(alg);
779
- if (actual !== expected) {
780
- throw new TypeError(`Invalid key curve for the algorithm, its curve must be ${expected}, got ${actual}`);
191
+ case "list":
192
+ result = { agents: listAgents() };
193
+ break;
194
+ default:
195
+ throw new Error(`unknown action: ${intent.action ?? "<undefined>"}`);
781
196
  }
782
- options = { dsaEncoding: "ieee-p1363" };
783
- break;
784
- }
785
- default:
786
- throw new JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`);
787
- }
788
- if (isJWK2) {
789
- return { format: "jwk", key, ...options };
790
- }
791
- return options ? { ...options, key } : key;
792
- }
793
-
794
- // ../../node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/runtime/sign.js
795
- import * as crypto3 from "crypto";
796
- import { promisify as promisify2 } from "util";
797
-
798
- // ../../node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/runtime/hmac_digest.js
799
- function hmacDigest(alg) {
800
- switch (alg) {
801
- case "HS256":
802
- return "sha256";
803
- case "HS384":
804
- return "sha384";
805
- case "HS512":
806
- return "sha512";
807
- default:
808
- throw new JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`);
809
- }
810
- }
811
-
812
- // ../../node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/runtime/get_sign_verify_key.js
813
- import { KeyObject as KeyObject4, createSecretKey } from "crypto";
814
- function getSignVerifyKey(alg, key, usage) {
815
- if (key instanceof Uint8Array) {
816
- if (!alg.startsWith("HS")) {
817
- throw new TypeError(invalid_key_input_default(key, ...types3));
818
- }
819
- return createSecretKey(key);
820
- }
821
- if (key instanceof KeyObject4) {
822
- return key;
823
- }
824
- if (isCryptoKey(key)) {
825
- checkSigCryptoKey(key, alg, usage);
826
- return KeyObject4.from(key);
827
- }
828
- if (isJWK(key)) {
829
- if (alg.startsWith("HS")) {
830
- return createSecretKey(Buffer.from(key.k, "base64url"));
831
- }
832
- return key;
833
- }
834
- throw new TypeError(invalid_key_input_default(key, ...types3, "Uint8Array", "JSON Web Key"));
835
- }
836
-
837
- // ../../node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/runtime/sign.js
838
- var oneShotSign = promisify2(crypto3.sign);
839
- var sign2 = async (alg, key, data) => {
840
- const k = getSignVerifyKey(alg, key, "sign");
841
- if (alg.startsWith("HS")) {
842
- const hmac = crypto3.createHmac(hmacDigest(alg), k);
843
- hmac.update(data);
844
- return hmac.digest();
845
- }
846
- return oneShotSign(dsaDigest(alg), data, keyForCrypto(alg, k));
847
- };
848
- var sign_default = sign2;
849
-
850
- // ../../node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/runtime/verify.js
851
- var oneShotVerify = promisify3(crypto4.verify);
852
- var verify2 = async (alg, key, signature, data) => {
853
- const k = getSignVerifyKey(alg, key, "verify");
854
- if (alg.startsWith("HS")) {
855
- const expected = await sign_default(alg, k, data);
856
- const actual = signature;
197
+ response = { ok: true, result };
198
+ } catch (err) {
199
+ const msg = err instanceof Error ? err.message : String(err);
200
+ this.deps.log(`intent-channel: ${intent.action} failed: ${msg}`);
201
+ response = { ok: false, error: msg };
202
+ }
203
+ const respTmp = `${path.replace(/\.json$/, "")}.response.tmp`;
204
+ const respFinal = `${path.replace(/\.json$/, "")}.response`;
205
+ writeFileSync2(respTmp, `${JSON.stringify(response)}
206
+ `, { mode: 432 });
207
+ renameSync(respTmp, respFinal);
857
208
  try {
858
- return crypto4.timingSafeEqual(actual, expected);
209
+ unlinkSync(path);
859
210
  } catch {
860
- return false;
861
211
  }
862
212
  }
863
- const algorithm = dsaDigest(alg);
864
- const keyInput = keyForCrypto(alg, k);
865
- try {
866
- return await oneShotVerify(algorithm, data, keyInput, signature);
867
- } catch {
868
- return false;
869
- }
870
213
  };
871
- var verify_default = verify2;
872
-
873
- // ../../node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/jws/flattened/verify.js
874
- async function flattenedVerify(jws, key, options) {
875
- if (!isObject(jws)) {
876
- throw new JWSInvalid("Flattened JWS must be an object");
877
- }
878
- if (jws.protected === void 0 && jws.header === void 0) {
879
- throw new JWSInvalid('Flattened JWS must have either of the "protected" or "header" members');
880
- }
881
- if (jws.protected !== void 0 && typeof jws.protected !== "string") {
882
- throw new JWSInvalid("JWS Protected Header incorrect type");
883
- }
884
- if (jws.payload === void 0) {
885
- throw new JWSInvalid("JWS Payload missing");
886
- }
887
- if (typeof jws.signature !== "string") {
888
- throw new JWSInvalid("JWS Signature missing or incorrect type");
889
- }
890
- if (jws.header !== void 0 && !isObject(jws.header)) {
891
- throw new JWSInvalid("JWS Unprotected Header incorrect type");
892
- }
893
- let parsedProt = {};
894
- if (jws.protected) {
895
- try {
896
- const protectedHeader = decode(jws.protected);
897
- parsedProt = JSON.parse(decoder.decode(protectedHeader));
898
- } catch {
899
- throw new JWSInvalid("JWS Protected Header is invalid");
900
- }
901
- }
902
- if (!is_disjoint_default(parsedProt, jws.header)) {
903
- throw new JWSInvalid("JWS Protected and JWS Unprotected Header Parameter names must be disjoint");
904
- }
905
- const joseHeader = {
906
- ...parsedProt,
907
- ...jws.header
908
- };
909
- const extensions = validate_crit_default(JWSInvalid, /* @__PURE__ */ new Map([["b64", true]]), options?.crit, parsedProt, joseHeader);
910
- let b64 = true;
911
- if (extensions.has("b64")) {
912
- b64 = parsedProt.b64;
913
- if (typeof b64 !== "boolean") {
914
- throw new JWSInvalid('The "b64" (base64url-encode payload) Header Parameter must be a boolean');
915
- }
916
- }
917
- const { alg } = joseHeader;
918
- if (typeof alg !== "string" || !alg) {
919
- throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid');
920
- }
921
- const algorithms = options && validate_algorithms_default("algorithms", options.algorithms);
922
- if (algorithms && !algorithms.has(alg)) {
923
- throw new JOSEAlgNotAllowed('"alg" (Algorithm) Header Parameter value not allowed');
924
- }
925
- if (b64) {
926
- if (typeof jws.payload !== "string") {
927
- throw new JWSInvalid("JWS Payload must be a string");
928
- }
929
- } else if (typeof jws.payload !== "string" && !(jws.payload instanceof Uint8Array)) {
930
- throw new JWSInvalid("JWS Payload must be a string or an Uint8Array instance");
931
- }
932
- let resolvedKey = false;
933
- if (typeof key === "function") {
934
- key = await key(parsedProt, jws);
935
- resolvedKey = true;
936
- checkKeyTypeWithJwk(alg, key, "verify");
937
- if (isJWK(key)) {
938
- key = await importJWK(key, alg);
939
- }
940
- } else {
941
- checkKeyTypeWithJwk(alg, key, "verify");
942
- }
943
- const data = concat(encoder.encode(jws.protected ?? ""), encoder.encode("."), typeof jws.payload === "string" ? encoder.encode(jws.payload) : jws.payload);
944
- let signature;
214
+ function reapStaleResponses(log2) {
215
+ let entries;
945
216
  try {
946
- signature = decode(jws.signature);
217
+ entries = readdirSync(INTENTS_DIR);
947
218
  } catch {
948
- throw new JWSInvalid("Failed to base64url decode the signature");
949
- }
950
- const verified = await verify_default(alg, key, signature, data);
951
- if (!verified) {
952
- throw new JWSSignatureVerificationFailed();
219
+ return;
953
220
  }
954
- let payload;
955
- if (b64) {
221
+ const now = Date.now();
222
+ for (const f of entries) {
223
+ if (!f.endsWith(".response")) continue;
224
+ const path = join2(INTENTS_DIR, f);
956
225
  try {
957
- payload = decode(jws.payload);
226
+ const st = statSync(path);
227
+ if (now - st.mtimeMs > 60 * 60 * 1e3) {
228
+ unlinkSync(path);
229
+ log2(`intent-channel: reaped stale ${f}`);
230
+ }
958
231
  } catch {
959
- throw new JWSInvalid("Failed to base64url decode the payload");
960
232
  }
961
- } else if (typeof jws.payload === "string") {
962
- payload = encoder.encode(jws.payload);
963
- } else {
964
- payload = jws.payload;
965
- }
966
- const result = { payload };
967
- if (jws.protected !== void 0) {
968
- result.protectedHeader = parsedProt;
969
- }
970
- if (jws.header !== void 0) {
971
- result.unprotectedHeader = jws.header;
972
- }
973
- if (resolvedKey) {
974
- return { ...result, key };
975
- }
976
- return result;
977
- }
978
-
979
- // ../../node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/jws/compact/verify.js
980
- async function compactVerify(jws, key, options) {
981
- if (jws instanceof Uint8Array) {
982
- jws = decoder.decode(jws);
983
233
  }
984
- if (typeof jws !== "string") {
985
- throw new JWSInvalid("Compact JWS must be a string or Uint8Array");
986
- }
987
- const { 0: protectedHeader, 1: payload, 2: signature, length } = jws.split(".");
988
- if (length !== 3) {
989
- throw new JWSInvalid("Invalid Compact JWS");
990
- }
991
- const verified = await flattenedVerify({ payload, protected: protectedHeader, signature }, key, options);
992
- const result = { payload: verified.payload, protectedHeader: verified.protectedHeader };
993
- if (typeof key === "function") {
994
- return { ...result, key: verified.key };
995
- }
996
- return result;
997
234
  }
998
235
 
999
- // ../../node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/lib/epoch.js
1000
- var epoch_default = (date) => Math.floor(date.getTime() / 1e3);
1001
-
1002
- // ../../node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/lib/secs.js
1003
- var minute = 60;
1004
- var hour = minute * 60;
1005
- var day = hour * 24;
1006
- var week = day * 7;
1007
- var year = day * 365.25;
1008
- var REGEX = /^(\+|\-)? ?(\d+|\d+\.\d+) ?(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)(?: (ago|from now))?$/i;
1009
- var secs_default = (str) => {
1010
- const matched = REGEX.exec(str);
1011
- if (!matched || matched[4] && matched[1]) {
1012
- throw new TypeError("Invalid time period format");
1013
- }
1014
- const value = parseFloat(matched[2]);
1015
- const unit = matched[3].toLowerCase();
1016
- let numericDate;
1017
- switch (unit) {
1018
- case "sec":
1019
- case "secs":
1020
- case "second":
1021
- case "seconds":
1022
- case "s":
1023
- numericDate = Math.round(value);
1024
- break;
1025
- case "minute":
1026
- case "minutes":
1027
- case "min":
1028
- case "mins":
1029
- case "m":
1030
- numericDate = Math.round(value * minute);
1031
- break;
1032
- case "hour":
1033
- case "hours":
1034
- case "hr":
1035
- case "hrs":
1036
- case "h":
1037
- numericDate = Math.round(value * hour);
1038
- break;
1039
- case "day":
1040
- case "days":
1041
- case "d":
1042
- numericDate = Math.round(value * day);
1043
- break;
1044
- case "week":
1045
- case "weeks":
1046
- case "w":
1047
- numericDate = Math.round(value * week);
1048
- break;
1049
- default:
1050
- numericDate = Math.round(value * year);
1051
- break;
1052
- }
1053
- if (matched[1] === "-" || matched[4] === "ago") {
1054
- return -numericDate;
1055
- }
1056
- return numericDate;
1057
- };
1058
-
1059
- // ../../node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/lib/jwt_claims_set.js
1060
- var normalizeTyp = (value) => value.toLowerCase().replace(/^application\//, "");
1061
- var checkAudiencePresence = (audPayload, audOption) => {
1062
- if (typeof audPayload === "string") {
1063
- return audOption.includes(audPayload);
1064
- }
1065
- if (Array.isArray(audPayload)) {
1066
- return audOption.some(Set.prototype.has.bind(new Set(audPayload)));
1067
- }
1068
- return false;
1069
- };
1070
- var jwt_claims_set_default = (protectedHeader, encodedPayload, options = {}) => {
1071
- let payload;
1072
- try {
1073
- payload = JSON.parse(decoder.decode(encodedPayload));
1074
- } catch {
1075
- }
1076
- if (!isObject(payload)) {
1077
- throw new JWTInvalid("JWT Claims Set must be a top-level JSON object");
1078
- }
1079
- const { typ } = options;
1080
- if (typ && (typeof protectedHeader.typ !== "string" || normalizeTyp(protectedHeader.typ) !== normalizeTyp(typ))) {
1081
- throw new JWTClaimValidationFailed('unexpected "typ" JWT header value', payload, "typ", "check_failed");
1082
- }
1083
- const { requiredClaims = [], issuer, subject, audience, maxTokenAge } = options;
1084
- const presenceCheck = [...requiredClaims];
1085
- if (maxTokenAge !== void 0)
1086
- presenceCheck.push("iat");
1087
- if (audience !== void 0)
1088
- presenceCheck.push("aud");
1089
- if (subject !== void 0)
1090
- presenceCheck.push("sub");
1091
- if (issuer !== void 0)
1092
- presenceCheck.push("iss");
1093
- for (const claim of new Set(presenceCheck.reverse())) {
1094
- if (!(claim in payload)) {
1095
- throw new JWTClaimValidationFailed(`missing required "${claim}" claim`, payload, claim, "missing");
1096
- }
1097
- }
1098
- if (issuer && !(Array.isArray(issuer) ? issuer : [issuer]).includes(payload.iss)) {
1099
- throw new JWTClaimValidationFailed('unexpected "iss" claim value', payload, "iss", "check_failed");
1100
- }
1101
- if (subject && payload.sub !== subject) {
1102
- throw new JWTClaimValidationFailed('unexpected "sub" claim value', payload, "sub", "check_failed");
1103
- }
1104
- if (audience && !checkAudiencePresence(payload.aud, typeof audience === "string" ? [audience] : audience)) {
1105
- throw new JWTClaimValidationFailed('unexpected "aud" claim value', payload, "aud", "check_failed");
1106
- }
1107
- let tolerance;
1108
- switch (typeof options.clockTolerance) {
1109
- case "string":
1110
- tolerance = secs_default(options.clockTolerance);
1111
- break;
1112
- case "number":
1113
- tolerance = options.clockTolerance;
1114
- break;
1115
- case "undefined":
1116
- tolerance = 0;
1117
- break;
1118
- default:
1119
- throw new TypeError("Invalid clockTolerance option type");
1120
- }
1121
- const { currentDate } = options;
1122
- const now = epoch_default(currentDate || /* @__PURE__ */ new Date());
1123
- if ((payload.iat !== void 0 || maxTokenAge) && typeof payload.iat !== "number") {
1124
- throw new JWTClaimValidationFailed('"iat" claim must be a number', payload, "iat", "invalid");
1125
- }
1126
- if (payload.nbf !== void 0) {
1127
- if (typeof payload.nbf !== "number") {
1128
- throw new JWTClaimValidationFailed('"nbf" claim must be a number', payload, "nbf", "invalid");
1129
- }
1130
- if (payload.nbf > now + tolerance) {
1131
- throw new JWTClaimValidationFailed('"nbf" claim timestamp check failed', payload, "nbf", "check_failed");
1132
- }
1133
- }
1134
- if (payload.exp !== void 0) {
1135
- if (typeof payload.exp !== "number") {
1136
- throw new JWTClaimValidationFailed('"exp" claim must be a number', payload, "exp", "invalid");
1137
- }
1138
- if (payload.exp <= now - tolerance) {
1139
- throw new JWTExpired('"exp" claim timestamp check failed', payload, "exp", "check_failed");
1140
- }
1141
- }
1142
- if (maxTokenAge) {
1143
- const age = now - payload.iat;
1144
- const max = typeof maxTokenAge === "number" ? maxTokenAge : secs_default(maxTokenAge);
1145
- if (age - tolerance > max) {
1146
- throw new JWTExpired('"iat" claim timestamp check failed (too far in the past)', payload, "iat", "check_failed");
1147
- }
1148
- if (age < 0 - tolerance) {
1149
- throw new JWTClaimValidationFailed('"iat" claim timestamp check failed (it should be in the past)', payload, "iat", "check_failed");
236
+ // src/lib/supervisor.ts
237
+ import { spawn } from "child_process";
238
+ import process from "process";
239
+ var MIN_BACKOFF_MS = 2e3;
240
+ var MAX_BACKOFF_MS = 6e4;
241
+ var STABLE_RUNTIME_MS = 3e4;
242
+ var Supervisor = class {
243
+ constructor(deps) {
244
+ this.deps = deps;
245
+ }
246
+ children = /* @__PURE__ */ new Map();
247
+ /** Bring the supervised set in line with the desired set. */
248
+ reconcile(desired) {
249
+ const desiredNames = new Set(desired.filter((a) => a.bridge != null).map((a) => a.name));
250
+ for (const [name] of this.children) {
251
+ if (!desiredNames.has(name)) this.stop(name);
252
+ }
253
+ for (const agent of desired) {
254
+ if (agent.bridge == null) continue;
255
+ if (!this.children.has(agent.name)) this.start(agent);
256
+ }
257
+ }
258
+ size() {
259
+ return this.children.size;
260
+ }
261
+ status() {
262
+ const now = Date.now();
263
+ return Array.from(this.children.entries()).map(([name, s]) => ({
264
+ name,
265
+ pid: s.child.pid ?? -1,
266
+ uptimeSec: Math.floor((now - s.startedAt) / 1e3),
267
+ consecutiveCrashes: s.consecutiveCrashes
268
+ }));
269
+ }
270
+ start(agent) {
271
+ if (this.children.has(agent.name)) return;
272
+ this.deps.log(`supervisor: starting bridge for ${agent.name}`);
273
+ this.spawnChild(agent, 0);
274
+ }
275
+ stop(name) {
276
+ const s = this.children.get(name);
277
+ if (!s) return;
278
+ this.deps.log(`supervisor: stopping bridge for ${name}`);
279
+ if (s.restartTimer) clearTimeout(s.restartTimer);
280
+ this.children.delete(name);
281
+ try {
282
+ s.child.kill("SIGTERM");
283
+ } catch {
1150
284
  }
1151
285
  }
1152
- return payload;
1153
- };
1154
-
1155
- // ../../node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/jwt/verify.js
1156
- async function jwtVerify(jwt, key, options) {
1157
- const verified = await compactVerify(jwt, key, options);
1158
- if (verified.protectedHeader.crit?.includes("b64") && verified.protectedHeader.b64 === false) {
1159
- throw new JWTInvalid("JWTs MUST NOT use unencoded payload");
1160
- }
1161
- const payload = jwt_claims_set_default(verified.protectedHeader, verified.payload, options);
1162
- const result = { payload, protectedHeader: verified.protectedHeader };
1163
- if (typeof key === "function") {
1164
- return { ...result, key: verified.key };
1165
- }
1166
- return result;
1167
- }
1168
-
1169
- // ../../node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/jwks/local.js
1170
- function getKtyFromAlg(alg) {
1171
- switch (typeof alg === "string" && alg.slice(0, 2)) {
1172
- case "RS":
1173
- case "PS":
1174
- return "RSA";
1175
- case "ES":
1176
- return "EC";
1177
- case "Ed":
1178
- return "OKP";
1179
- default:
1180
- throw new JOSENotSupported('Unsupported "alg" value for a JSON Web Key Set');
1181
- }
1182
- }
1183
- function isJWKSLike(jwks) {
1184
- return jwks && typeof jwks === "object" && Array.isArray(jwks.keys) && jwks.keys.every(isJWKLike);
1185
- }
1186
- function isJWKLike(key) {
1187
- return isObject(key);
1188
- }
1189
- function clone(obj) {
1190
- if (typeof structuredClone === "function") {
1191
- return structuredClone(obj);
1192
- }
1193
- return JSON.parse(JSON.stringify(obj));
1194
- }
1195
- var LocalJWKSet = class {
1196
- _jwks;
1197
- _cached = /* @__PURE__ */ new WeakMap();
1198
- constructor(jwks) {
1199
- if (!isJWKSLike(jwks)) {
1200
- throw new JWKSInvalid("JSON Web Key Set malformed");
1201
- }
1202
- this._jwks = clone(jwks);
286
+ stopAll() {
287
+ for (const name of Array.from(this.children.keys())) this.stop(name);
1203
288
  }
1204
- async getKey(protectedHeader, token) {
1205
- const { alg, kid } = { ...protectedHeader, ...token?.header };
1206
- const kty = getKtyFromAlg(alg);
1207
- const candidates = this._jwks.keys.filter((jwk2) => {
1208
- let candidate = kty === jwk2.kty;
1209
- if (candidate && typeof kid === "string") {
1210
- candidate = kid === jwk2.kid;
1211
- }
1212
- if (candidate && typeof jwk2.alg === "string") {
1213
- candidate = alg === jwk2.alg;
1214
- }
1215
- if (candidate && typeof jwk2.use === "string") {
1216
- candidate = jwk2.use === "sig";
1217
- }
1218
- if (candidate && Array.isArray(jwk2.key_ops)) {
1219
- candidate = jwk2.key_ops.includes("verify");
1220
- }
1221
- if (candidate) {
1222
- switch (alg) {
1223
- case "ES256":
1224
- candidate = jwk2.crv === "P-256";
1225
- break;
1226
- case "ES256K":
1227
- candidate = jwk2.crv === "secp256k1";
1228
- break;
1229
- case "ES384":
1230
- candidate = jwk2.crv === "P-384";
1231
- break;
1232
- case "ES512":
1233
- candidate = jwk2.crv === "P-521";
1234
- break;
1235
- case "Ed25519":
1236
- candidate = jwk2.crv === "Ed25519";
1237
- break;
1238
- case "EdDSA":
1239
- candidate = jwk2.crv === "Ed25519" || jwk2.crv === "Ed448";
1240
- break;
1241
- }
1242
- }
1243
- return candidate;
289
+ spawnChild(agent, prevCrashes) {
290
+ const args = ["run", "--as", agent.name, "--wait", "--", "openape-chat-bridge"];
291
+ const child = spawn(this.deps.apesBin, args, {
292
+ stdio: ["ignore", "pipe", "pipe"],
293
+ detached: false,
294
+ // Inherit env most importantly PATH (host bin dirs from
295
+ // captureHostBinDirs at install time) and HOME (the Nest's
296
+ // data dir, where its own auth.json lives so apes-cli reads
297
+ // the nest identity for the YOLO grant).
298
+ env: process.env
299
+ });
300
+ child.stdout?.on("data", (chunk) => this.forwardLog(agent.name, "stdout", chunk));
301
+ child.stderr?.on("data", (chunk) => this.forwardLog(agent.name, "stderr", chunk));
302
+ const supervised = {
303
+ child,
304
+ consecutiveCrashes: prevCrashes,
305
+ startedAt: Date.now()
306
+ };
307
+ this.children.set(agent.name, supervised);
308
+ child.on("exit", (code, signal) => {
309
+ const stillManaged = this.children.get(agent.name) === supervised;
310
+ if (!stillManaged) return;
311
+ const ranLongEnough = Date.now() - supervised.startedAt > STABLE_RUNTIME_MS;
312
+ const nextCrashes = ranLongEnough ? 1 : prevCrashes + 1;
313
+ const backoff = Math.min(MAX_BACKOFF_MS, MIN_BACKOFF_MS * 2 ** Math.max(0, nextCrashes - 1));
314
+ this.deps.log(
315
+ `supervisor: ${agent.name} bridge exited code=${code} signal=${signal ?? "none"} consecutive=${nextCrashes} \u2192 respawn in ${backoff}ms`
316
+ );
317
+ supervised.restartTimer = setTimeout(() => {
318
+ if (this.children.get(agent.name) !== supervised) return;
319
+ this.children.delete(agent.name);
320
+ this.spawnChild(agent, nextCrashes);
321
+ }, backoff);
1244
322
  });
1245
- const { 0: jwk, length } = candidates;
1246
- if (length === 0) {
1247
- throw new JWKSNoMatchingKey();
1248
- }
1249
- if (length !== 1) {
1250
- const error = new JWKSMultipleMatchingKeys();
1251
- const { _cached } = this;
1252
- error[Symbol.asyncIterator] = async function* () {
1253
- for (const jwk2 of candidates) {
1254
- try {
1255
- yield await importWithAlgCache(_cached, jwk2, alg);
1256
- } catch {
1257
- }
1258
- }
1259
- };
1260
- throw error;
1261
- }
1262
- return importWithAlgCache(this._cached, jwk, alg);
1263
323
  }
1264
- };
1265
- async function importWithAlgCache(cache, jwk, alg) {
1266
- const cached = cache.get(jwk) || cache.set(jwk, {}).get(jwk);
1267
- if (cached[alg] === void 0) {
1268
- const key = await importJWK({ ...jwk, ext: true }, alg);
1269
- if (key instanceof Uint8Array || key.type !== "public") {
1270
- throw new JWKSInvalid("JSON Web Key Set members must be public keys");
324
+ forwardLog(name, stream, chunk) {
325
+ const text = chunk.toString("utf8");
326
+ for (const line of text.split("\n")) {
327
+ const trimmed = line.trimEnd();
328
+ if (!trimmed) continue;
329
+ this.deps.log(`[${name}/${stream}] ${trimmed}`);
1271
330
  }
1272
- cached[alg] = key;
1273
- }
1274
- return cached[alg];
1275
- }
1276
- function createLocalJWKSet(jwks) {
1277
- const set = new LocalJWKSet(jwks);
1278
- const localJWKSet = async (protectedHeader, token) => set.getKey(protectedHeader, token);
1279
- Object.defineProperties(localJWKSet, {
1280
- jwks: {
1281
- value: () => clone(set._jwks),
1282
- enumerable: true,
1283
- configurable: false,
1284
- writable: false
1285
- }
1286
- });
1287
- return localJWKSet;
1288
- }
1289
-
1290
- // ../../node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/runtime/fetch_jwks.js
1291
- import * as http from "http";
1292
- import * as https from "https";
1293
- import { once } from "events";
1294
- var fetchJwks = async (url, timeout, options) => {
1295
- let get3;
1296
- switch (url.protocol) {
1297
- case "https:":
1298
- get3 = https.get;
1299
- break;
1300
- case "http:":
1301
- get3 = http.get;
1302
- break;
1303
- default:
1304
- throw new TypeError("Unsupported URL protocol.");
1305
- }
1306
- const { agent, headers } = options;
1307
- const req = get3(url.href, {
1308
- agent,
1309
- timeout,
1310
- headers
1311
- });
1312
- const [response] = await Promise.race([once(req, "response"), once(req, "timeout")]);
1313
- if (!response) {
1314
- req.destroy();
1315
- throw new JWKSTimeout();
1316
- }
1317
- if (response.statusCode !== 200) {
1318
- throw new JOSEError("Expected 200 OK from the JSON Web Key Set HTTP response");
1319
- }
1320
- const parts = [];
1321
- for await (const part of response) {
1322
- parts.push(part);
1323
- }
1324
- try {
1325
- return JSON.parse(decoder.decode(concat(...parts)));
1326
- } catch {
1327
- throw new JOSEError("Failed to parse the JSON Web Key Set HTTP response as JSON");
1328
331
  }
1329
332
  };
1330
- var fetch_jwks_default = fetchJwks;
1331
333
 
1332
- // ../../node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/jwks/remote.js
1333
- function isCloudflareWorkers() {
1334
- return typeof WebSocketPair !== "undefined" || typeof navigator !== "undefined" && navigator.userAgent === "Cloudflare-Workers" || typeof EdgeRuntime !== "undefined" && EdgeRuntime === "vercel";
1335
- }
1336
- var USER_AGENT;
1337
- if (typeof navigator === "undefined" || !navigator.userAgent?.startsWith?.("Mozilla/5.0 ")) {
1338
- const NAME = "jose";
1339
- const VERSION = "v5.10.0";
1340
- USER_AGENT = `${NAME}/${VERSION}`;
1341
- }
1342
- var jwksCache = /* @__PURE__ */ Symbol();
1343
- function isFreshJwksCache(input, cacheMaxAge) {
1344
- if (typeof input !== "object" || input === null) {
1345
- return false;
1346
- }
1347
- if (!("uat" in input) || typeof input.uat !== "number" || Date.now() - input.uat >= cacheMaxAge) {
1348
- return false;
1349
- }
1350
- if (!("jwks" in input) || !isObject(input.jwks) || !Array.isArray(input.jwks.keys) || !Array.prototype.every.call(input.jwks.keys, isObject)) {
1351
- return false;
1352
- }
1353
- return true;
1354
- }
1355
- var RemoteJWKSet = class {
1356
- _url;
1357
- _timeoutDuration;
1358
- _cooldownDuration;
1359
- _cacheMaxAge;
1360
- _jwksTimestamp;
1361
- _pendingFetch;
1362
- _options;
1363
- _local;
1364
- _cache;
1365
- constructor(url, options) {
1366
- if (!(url instanceof URL)) {
1367
- throw new TypeError("url must be an instance of URL");
1368
- }
1369
- this._url = new URL(url.href);
1370
- this._options = { agent: options?.agent, headers: options?.headers };
1371
- this._timeoutDuration = typeof options?.timeoutDuration === "number" ? options?.timeoutDuration : 5e3;
1372
- this._cooldownDuration = typeof options?.cooldownDuration === "number" ? options?.cooldownDuration : 3e4;
1373
- this._cacheMaxAge = typeof options?.cacheMaxAge === "number" ? options?.cacheMaxAge : 6e5;
1374
- if (options?.[jwksCache] !== void 0) {
1375
- this._cache = options?.[jwksCache];
1376
- if (isFreshJwksCache(options?.[jwksCache], this._cacheMaxAge)) {
1377
- this._jwksTimestamp = this._cache.uat;
1378
- this._local = createLocalJWKSet(this._cache.jwks);
334
+ // src/lib/troop-sync.ts
335
+ import { execFile as execFile2 } from "child_process";
336
+ import process2 from "process";
337
+ import { promisify as promisify2 } from "util";
338
+ var execFileAsync2 = promisify2(execFile2);
339
+ var TICK_MS = 5 * 60 * 1e3;
340
+ var TroopSync = class {
341
+ constructor(deps) {
342
+ this.deps = deps;
343
+ }
344
+ timer;
345
+ inflight = false;
346
+ start() {
347
+ if (this.timer) return;
348
+ setTimeout(() => this.tick(), 3e4).unref();
349
+ this.timer = setInterval(() => this.tick(), TICK_MS);
350
+ this.deps.log("troop-sync: loop started (interval=5min)");
351
+ }
352
+ stop() {
353
+ if (this.timer) clearInterval(this.timer);
354
+ this.timer = void 0;
355
+ }
356
+ async tick() {
357
+ if (this.inflight) return;
358
+ this.inflight = true;
359
+ try {
360
+ const agents = listAgents();
361
+ if (agents.length === 0) return;
362
+ this.deps.log(`troop-sync: reconciling ${agents.length} agent(s)`);
363
+ for (const agent of agents) {
364
+ await this.syncOne(agent.name);
1379
365
  }
366
+ } finally {
367
+ this.inflight = false;
1380
368
  }
1381
369
  }
1382
- coolingDown() {
1383
- return typeof this._jwksTimestamp === "number" ? Date.now() < this._jwksTimestamp + this._cooldownDuration : false;
1384
- }
1385
- fresh() {
1386
- return typeof this._jwksTimestamp === "number" ? Date.now() < this._jwksTimestamp + this._cacheMaxAge : false;
1387
- }
1388
- async getKey(protectedHeader, token) {
1389
- if (!this._local || !this.fresh()) {
1390
- await this.reload();
1391
- }
370
+ async syncOne(name) {
1392
371
  try {
1393
- return await this._local(protectedHeader, token);
372
+ await execFileAsync2(
373
+ this.deps.apesBin,
374
+ ["run", "--as", name, "--wait", "--", "apes", "agents", "sync"],
375
+ { maxBuffer: 1024 * 1024, env: process2.env, timeout: 6e4 }
376
+ );
1394
377
  } catch (err) {
1395
- if (err instanceof JWKSNoMatchingKey) {
1396
- if (this.coolingDown() === false) {
1397
- await this.reload();
1398
- return this._local(protectedHeader, token);
1399
- }
1400
- }
1401
- throw err;
1402
- }
1403
- }
1404
- async reload() {
1405
- if (this._pendingFetch && isCloudflareWorkers()) {
1406
- this._pendingFetch = void 0;
1407
- }
1408
- const headers = new Headers(this._options.headers);
1409
- if (USER_AGENT && !headers.has("User-Agent")) {
1410
- headers.set("User-Agent", USER_AGENT);
1411
- this._options.headers = Object.fromEntries(headers.entries());
1412
- }
1413
- this._pendingFetch ||= fetch_jwks_default(this._url, this._timeoutDuration, this._options).then((json) => {
1414
- this._local = createLocalJWKSet(json);
1415
- if (this._cache) {
1416
- this._cache.uat = Date.now();
1417
- this._cache.jwks = json;
1418
- }
1419
- this._jwksTimestamp = Date.now();
1420
- this._pendingFetch = void 0;
1421
- }).catch((err) => {
1422
- this._pendingFetch = void 0;
1423
- throw err;
1424
- });
1425
- await this._pendingFetch;
1426
- }
1427
- };
1428
- function createRemoteJWKSet(url, options) {
1429
- const set = new RemoteJWKSet(url, options);
1430
- const remoteJWKSet = async (protectedHeader, token) => set.getKey(protectedHeader, token);
1431
- Object.defineProperties(remoteJWKSet, {
1432
- coolingDown: {
1433
- get: () => set.coolingDown(),
1434
- enumerable: true,
1435
- configurable: false
1436
- },
1437
- fresh: {
1438
- get: () => set.fresh(),
1439
- enumerable: true,
1440
- configurable: false
1441
- },
1442
- reload: {
1443
- value: () => set.reload(),
1444
- enumerable: true,
1445
- configurable: false,
1446
- writable: false
1447
- },
1448
- reloading: {
1449
- get: () => !!set._pendingFetch,
1450
- enumerable: true,
1451
- configurable: false
1452
- },
1453
- jwks: {
1454
- value: () => set._local?.jwks(),
1455
- enumerable: true,
1456
- configurable: false,
1457
- writable: false
378
+ this.deps.log(`troop-sync: ${name} failed: ${err instanceof Error ? err.message.split("\n")[0] : String(err)}`);
1458
379
  }
1459
- });
1460
- return remoteJWKSet;
1461
- }
1462
-
1463
- // ../../packages/core/dist/index.js
1464
- var ALGORITHM = "EdDSA";
1465
- async function verifyJWT(token, keyOrJWKS, options) {
1466
- const verifyOptions = { algorithms: [ALGORITHM], ...options };
1467
- const result = typeof keyOrJWKS === "function" ? await jwtVerify(token, keyOrJWKS, verifyOptions) : await jwtVerify(token, keyOrJWKS, verifyOptions);
1468
- return result;
1469
- }
1470
- function createRemoteJWKS(jwksUri) {
1471
- return createRemoteJWKSet(new URL(jwksUri));
1472
- }
1473
-
1474
- // src/lib/auth.ts
1475
- var NEST_AUDIENCE = "nest";
1476
- var idpUrl = process.env.OPENAPE_IDP_URL ?? "https://id.openape.ai";
1477
- var _jwks = null;
1478
- function getJwks() {
1479
- if (!_jwks) {
1480
- const url = new URL("/.well-known/jwks.json", idpUrl).toString();
1481
- _jwks = createRemoteJWKS(url);
1482
- }
1483
- return _jwks;
1484
- }
1485
- var NestAuthError = class extends Error {
1486
- constructor(status, title) {
1487
- super(title);
1488
- this.status = status;
1489
- this.title = title;
1490
380
  }
1491
381
  };
1492
- async function verifyNestGrant(token, expectedCommand) {
1493
- let claims;
1494
- try {
1495
- const result = await verifyJWT(token, getJwks(), {
1496
- issuer: idpUrl,
1497
- audience: NEST_AUDIENCE
1498
- });
1499
- claims = result.payload;
1500
- } catch (err) {
1501
- throw new NestAuthError(401, `Invalid or expired grant token: ${err instanceof Error ? err.message : String(err)}`);
1502
- }
1503
- if (typeof claims.sub !== "string" || !claims.sub) {
1504
- throw new NestAuthError(401, "Grant token missing sub claim");
1505
- }
1506
- if (claims.target_host !== hostname()) {
1507
- throw new NestAuthError(403, `Grant target_host (${claims.target_host}) does not match this nest (${hostname()})`);
1508
- }
1509
- const cmd = claims.command ?? [];
1510
- if (cmd.length !== expectedCommand.length || cmd.some((c, i) => c !== expectedCommand[i])) {
1511
- throw new NestAuthError(
1512
- 403,
1513
- `Grant command (${JSON.stringify(cmd)}) does not match this route (${JSON.stringify(expectedCommand)})`
1514
- );
1515
- }
1516
- return { caller: claims.sub, grantId: claims.grant_id, command: cmd };
1517
- }
1518
- async function primeJwksCache(log2) {
1519
- try {
1520
- await verifyJWT("eyJhbGciOiJFZERTQSJ9.e30.invalid", getJwks(), {}).catch(() => {
1521
- });
1522
- log2(`nest: JWKS primed from ${idpUrl}/.well-known/jwks.json`);
1523
- } catch (err) {
1524
- log2(`nest: warning \u2014 JWKS prime failed: ${err instanceof Error ? err.message : String(err)}`);
1525
- }
1526
- }
1527
382
 
1528
383
  // src/index.ts
1529
- var HOST = "127.0.0.1";
1530
- var PORT = Number(process2.env.OPENAPE_NEST_PORT ?? 9091);
1531
- var APES_BIN = process2.env.OPENAPE_APES_BIN ?? "apes";
384
+ var APES_BIN = process3.env.OPENAPE_APES_BIN ?? "apes";
1532
385
  function log(line) {
1533
- process2.stderr.write(`${(/* @__PURE__ */ new Date()).toISOString()} ${line}
386
+ process3.stderr.write(`${(/* @__PURE__ */ new Date()).toISOString()} ${line}
1534
387
  `);
1535
388
  }
1536
- async function readJsonBody(req) {
1537
- const chunks = [];
1538
- for await (const chunk of req) chunks.push(chunk);
1539
- if (chunks.length === 0) return {};
1540
- const text = Buffer.concat(chunks).toString("utf8");
1541
- if (!text.trim()) return {};
1542
- try {
1543
- return JSON.parse(text);
1544
- } catch {
1545
- throw new Error("invalid JSON body");
1546
- }
1547
- }
1548
- function send(res, status, body) {
1549
- res.writeHead(status, { "content-type": "application/json" });
1550
- res.end(JSON.stringify(body));
1551
- }
1552
- function sendProblem(res, status, title) {
1553
- send(res, status, { type: "about:blank", status, title });
1554
- }
1555
- async function requireNestGrant(req, res, expectedCommand) {
1556
- const auth = req.headers.authorization;
1557
- if (!auth || !auth.toLowerCase().startsWith("bearer ")) {
1558
- sendProblem(res, 401, "Bearer grant token required");
1559
- return null;
1560
- }
1561
- const token = auth.slice(7).trim();
1562
- try {
1563
- return await verifyNestGrant(token, expectedCommand);
1564
- } catch (err) {
1565
- if (err instanceof NestAuthError) {
1566
- sendProblem(res, err.status, err.title);
1567
- return null;
1568
- }
1569
- sendProblem(res, 401, err instanceof Error ? err.message : String(err));
1570
- return null;
1571
- }
1572
- }
1573
- var server = createServer((req, res) => {
1574
- ;
1575
- (async () => {
1576
- try {
1577
- const url = new URL(req.url ?? "/", `http://${HOST}:${PORT}`);
1578
- const body = req.method && ["POST", "PUT", "PATCH"].includes(req.method) ? await readJsonBody(req) : {};
1579
- if (req.method === "GET" && url.pathname === "/status") {
1580
- const grant = await requireNestGrant(req, res, ["nest", "status"]);
1581
- if (!grant) return;
1582
- log(`nest: GET /status authorized (caller=${grant.caller}, grant=${grant.grantId})`);
1583
- const ctx = { url, body, log, apesBin: APES_BIN, caller: grant.caller, grantId: grant.grantId };
1584
- return send(res, 200, handleNestStatus(ctx));
1585
- }
1586
- if (req.method === "GET" && url.pathname === "/agents") {
1587
- const grant = await requireNestGrant(req, res, ["nest", "list"]);
1588
- if (!grant) return;
1589
- log(`nest: GET /agents authorized (caller=${grant.caller}, grant=${grant.grantId})`);
1590
- const ctx = { url, body, log, apesBin: APES_BIN, caller: grant.caller, grantId: grant.grantId };
1591
- return send(res, 200, handleAgentsList(ctx));
1592
- }
1593
- if (req.method === "POST" && url.pathname === "/agents") {
1594
- const name = body?.name;
1595
- if (typeof name !== "string" || !name) {
1596
- return sendProblem(res, 400, "POST /agents requires body.name (string)");
1597
- }
1598
- const grant = await requireNestGrant(req, res, ["nest", "spawn"]);
1599
- if (!grant) return;
1600
- log(`nest: POST /agents (spawn ${name}) authorized (caller=${grant.caller}, grant=${grant.grantId})`);
1601
- const ctx = { url, body, log, apesBin: APES_BIN, caller: grant.caller, grantId: grant.grantId };
1602
- const result = await handleAgentSpawn(ctx);
1603
- return send(res, 201, result);
1604
- }
1605
- const destroyMatch = req.method === "DELETE" && url.pathname.match(/^\/agents\/([^/]+)$/);
1606
- if (destroyMatch) {
1607
- const name = destroyMatch[1];
1608
- const grant = await requireNestGrant(req, res, ["nest", "destroy", name]);
1609
- if (!grant) return;
1610
- log(`nest: DELETE /agents/${name} authorized (caller=${grant.caller}, grant=${grant.grantId})`);
1611
- const ctx = { url, body, log, apesBin: APES_BIN, caller: grant.caller, grantId: grant.grantId };
1612
- const result = await handleAgentDestroy(ctx, name);
1613
- return send(res, 200, result);
1614
- }
1615
- send(res, 404, { error: "not found" });
1616
- } catch (err) {
1617
- const msg = err instanceof Error ? err.message : String(err);
1618
- log(`nest: request failed: ${msg}`);
1619
- send(res, 500, { error: msg });
1620
- }
1621
- })();
1622
- });
1623
- void primeJwksCache(log);
1624
- server.listen(PORT, HOST, () => {
1625
- log(`nest: listening on http://${HOST}:${PORT}`);
1626
- });
1627
- process2.on("SIGTERM", () => {
1628
- log("nest: SIGTERM \u2014 shutting down");
1629
- server.close(() => process2.exit(0));
389
+ var supervisor = new Supervisor({ apesBin: APES_BIN, log });
390
+ var troopSync = new TroopSync({ apesBin: APES_BIN, log });
391
+ var intentChannel = new IntentChannel({ apesBin: APES_BIN, supervisor, log });
392
+ supervisor.reconcile(listAgents());
393
+ log(`nest: supervisor reconciled, ${supervisor.size()} bridge process(es) starting`);
394
+ troopSync.start();
395
+ intentChannel.start();
396
+ var reaperTimer = setInterval(reapStaleResponses, 60 * 60 * 1e3, log);
397
+ process3.on("SIGTERM", () => {
398
+ log("nest: SIGTERM \u2014 stopping");
399
+ supervisor.stopAll();
400
+ troopSync.stop();
401
+ intentChannel.stop();
402
+ clearInterval(reaperTimer);
403
+ process3.exit(0);
1630
404
  });
1631
- process2.on("SIGINT", () => {
1632
- log("nest: SIGINT \u2014 shutting down");
1633
- server.close(() => process2.exit(0));
405
+ process3.on("SIGINT", () => {
406
+ log("nest: SIGINT \u2014 stopping");
407
+ supervisor.stopAll();
408
+ troopSync.stop();
409
+ intentChannel.stop();
410
+ clearInterval(reaperTimer);
411
+ process3.exit(0);
1634
412
  });