@themoltnet/agent-daemon 0.49.0 → 0.50.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/cli.js +794 -362
  2. package/dist/main.js +1 -1
  3. package/package.json +16 -9
package/dist/cli.js CHANGED
@@ -30,15 +30,15 @@ import { mkdir, open, realpath, stat } from "node:fs/promises";
30
30
  import { pipeline } from "node:stream/promises";
31
31
  import cors from "@fastify/cors";
32
32
  import helmet from "@fastify/helmet";
33
- import { getOAuthProviders } from "@earendil-works/pi-ai/oauth";
34
- import { AuthStorage } from "@earendil-works/pi-coding-agent";
33
+ import { lock, lockSync } from "proper-lockfile";
34
+ import { ModelRuntime, readStoredCredential } from "@earendil-works/pi-coding-agent";
35
35
  import { Transform, Writable } from "node:stream";
36
36
  import { writePiConfig } from "@themoltnet/pi-runtime/pi-config";
37
37
  import { homedir } from "node:os";
38
- import { lock } from "proper-lockfile";
39
38
  import { StringDecoder } from "node:string_decoder";
40
39
  import rateLimit from "@fastify/rate-limit";
41
40
  import Fastify from "fastify";
41
+ import { isIP } from "node:net";
42
42
  import { createGzip } from "node:zlib";
43
43
  //#region ../../libs/tasks/src/rubric.ts
44
44
  /**
@@ -800,7 +800,7 @@ Object.freeze(ALL_CREDENTIAL_SCOPES.filter((scope) => scope !== CREDENTIAL_SCOPE
800
800
  ].filter((scope) => scope !== CREDENTIAL_SCOPES.HumanProfile);
801
801
  //#endregion
802
802
  //#region ../../libs/models/src/preview-sign.ts
803
- function schemaRef$1(schema, id) {
803
+ function schemaRef$2(schema, id) {
804
804
  return Type.Unsafe(Type.Ref(id));
805
805
  }
806
806
  var PreviewSignBase64UrlSchema = Type.String({
@@ -825,8 +825,8 @@ var PreviewSignEs256PublicKeySchema = Type.Object({
825
825
  kty: Type.Literal(2),
826
826
  algorithm: Type.Literal(-7),
827
827
  curve: Type.Literal(1),
828
- x: schemaRef$1(PreviewSignSha256Base64UrlSchema, "PreviewSignSha256Base64Url"),
829
- y: schemaRef$1(PreviewSignSha256Base64UrlSchema, "PreviewSignSha256Base64Url")
828
+ x: schemaRef$2(PreviewSignSha256Base64UrlSchema, "PreviewSignSha256Base64Url"),
829
+ y: schemaRef$2(PreviewSignSha256Base64UrlSchema, "PreviewSignSha256Base64Url")
830
830
  }, {
831
831
  $id: "PreviewSignEs256PublicKey",
832
832
  additionalProperties: false
@@ -835,8 +835,8 @@ var PreviewSignEcdhEsHkdf256PublicKeySchema = Type.Object({
835
835
  kty: Type.Literal(2),
836
836
  algorithm: Type.Literal(-25),
837
837
  curve: Type.Literal(1),
838
- x: schemaRef$1(PreviewSignSha256Base64UrlSchema, "PreviewSignSha256Base64Url"),
839
- y: schemaRef$1(PreviewSignSha256Base64UrlSchema, "PreviewSignSha256Base64Url")
838
+ x: schemaRef$2(PreviewSignSha256Base64UrlSchema, "PreviewSignSha256Base64Url"),
839
+ y: schemaRef$2(PreviewSignSha256Base64UrlSchema, "PreviewSignSha256Base64Url")
840
840
  }, {
841
841
  $id: "PreviewSignEcdhEsHkdf256PublicKey",
842
842
  additionalProperties: false
@@ -845,8 +845,8 @@ var PreviewSignEsp256PublicKeySchema = Type.Object({
845
845
  kty: Type.Literal(2),
846
846
  algorithm: Type.Literal(-9),
847
847
  curve: Type.Literal(1),
848
- x: schemaRef$1(PreviewSignSha256Base64UrlSchema, "PreviewSignSha256Base64Url"),
849
- y: schemaRef$1(PreviewSignSha256Base64UrlSchema, "PreviewSignSha256Base64Url")
848
+ x: schemaRef$2(PreviewSignSha256Base64UrlSchema, "PreviewSignSha256Base64Url"),
849
+ y: schemaRef$2(PreviewSignSha256Base64UrlSchema, "PreviewSignSha256Base64Url")
850
850
  }, {
851
851
  $id: "PreviewSignEsp256PublicKey",
852
852
  additionalProperties: false
@@ -855,18 +855,18 @@ var PreviewSignArkgSeedPublicKeySchema = Type.Object({
855
855
  kty: Type.Literal(-65537),
856
856
  algorithm: Type.Literal(-65700),
857
857
  derivedAlgorithm: Type.Literal(-9),
858
- blindingKey: schemaRef$1(PreviewSignEs256PublicKeySchema, "PreviewSignEs256PublicKey"),
859
- kemKey: schemaRef$1(PreviewSignEcdhEsHkdf256PublicKeySchema, "PreviewSignEcdhEsHkdf256PublicKey")
858
+ blindingKey: schemaRef$2(PreviewSignEs256PublicKeySchema, "PreviewSignEs256PublicKey"),
859
+ kemKey: schemaRef$2(PreviewSignEcdhEsHkdf256PublicKeySchema, "PreviewSignEcdhEsHkdf256PublicKey")
860
860
  }, {
861
861
  $id: "PreviewSignArkgSeedPublicKey",
862
862
  additionalProperties: false
863
863
  });
864
864
  var PreviewSignPublicMaterialSchema = Type.Object({
865
865
  version: Type.Literal(1),
866
- outerCredentialId: schemaRef$1(PreviewSignBase64UrlSchema, "PreviewSignBase64Url"),
867
- outerPublicKey: schemaRef$1(PreviewSignEs256PublicKeySchema, "PreviewSignEs256PublicKey"),
868
- previewKeyHandle: schemaRef$1(PreviewSignBase64UrlSchema, "PreviewSignBase64Url"),
869
- seedPublicKey: schemaRef$1(PreviewSignArkgSeedPublicKeySchema, "PreviewSignArkgSeedPublicKey")
866
+ outerCredentialId: schemaRef$2(PreviewSignBase64UrlSchema, "PreviewSignBase64Url"),
867
+ outerPublicKey: schemaRef$2(PreviewSignEs256PublicKeySchema, "PreviewSignEs256PublicKey"),
868
+ previewKeyHandle: schemaRef$2(PreviewSignBase64UrlSchema, "PreviewSignBase64Url"),
869
+ seedPublicKey: schemaRef$2(PreviewSignArkgSeedPublicKeySchema, "PreviewSignArkgSeedPublicKey")
870
870
  }, {
871
871
  $id: "PreviewSignPublicMaterial",
872
872
  additionalProperties: false
@@ -874,19 +874,19 @@ var PreviewSignPublicMaterialSchema = Type.Object({
874
874
  var PreviewSignChallengeSchema = Type.Object({
875
875
  verificationMethod: Type.Literal("human-hardware-previewsign"),
876
876
  version: Type.Literal(1),
877
- envelope: schemaRef$1(PreviewSignBase64UrlSchema, "PreviewSignBase64Url"),
878
- digest: schemaRef$1(PreviewSignSha256Base64UrlSchema, "PreviewSignSha256Base64Url"),
879
- additionalArguments: schemaRef$1(PreviewSignBase64UrlSchema, "PreviewSignBase64Url"),
880
- outerCredentialId: schemaRef$1(PreviewSignBase64UrlSchema, "PreviewSignBase64Url"),
881
- outerPublicKey: schemaRef$1(PreviewSignEs256PublicKeySchema, "PreviewSignEs256PublicKey"),
882
- previewKeyHandle: schemaRef$1(PreviewSignBase64UrlSchema, "PreviewSignBase64Url")
877
+ envelope: schemaRef$2(PreviewSignBase64UrlSchema, "PreviewSignBase64Url"),
878
+ digest: schemaRef$2(PreviewSignSha256Base64UrlSchema, "PreviewSignSha256Base64Url"),
879
+ additionalArguments: schemaRef$2(PreviewSignBase64UrlSchema, "PreviewSignBase64Url"),
880
+ outerCredentialId: schemaRef$2(PreviewSignBase64UrlSchema, "PreviewSignBase64Url"),
881
+ outerPublicKey: schemaRef$2(PreviewSignEs256PublicKeySchema, "PreviewSignEs256PublicKey"),
882
+ previewKeyHandle: schemaRef$2(PreviewSignBase64UrlSchema, "PreviewSignBase64Url")
883
883
  }, {
884
884
  $id: "PreviewSignChallenge",
885
885
  additionalProperties: false
886
886
  });
887
887
  var PreviewSignChallengeValueSchema = Type.Object({
888
888
  verificationMethod: Type.Literal("human-hardware-previewsign"),
889
- value: schemaRef$1(PreviewSignChallengeSchema, "PreviewSignChallenge")
889
+ value: schemaRef$2(PreviewSignChallengeSchema, "PreviewSignChallenge")
890
890
  }, {
891
891
  $id: "PreviewSignChallengeValue",
892
892
  additionalProperties: false
@@ -894,14 +894,14 @@ var PreviewSignChallengeValueSchema = Type.Object({
894
894
  var PreviewSignChallengeOperationSchema = Type.Union([Type.Literal("credential-registration"), Type.Literal("signing-request")], { $id: "PreviewSignChallengeOperation" });
895
895
  var PreviewSignReceiptSchema = Type.Object({
896
896
  version: Type.Literal(1),
897
- signature: schemaRef$1(PreviewSignP256DerSignatureBase64UrlSchema, "PreviewSignP256DerSignatureBase64Url")
897
+ signature: schemaRef$2(PreviewSignP256DerSignatureBase64UrlSchema, "PreviewSignP256DerSignatureBase64Url")
898
898
  }, {
899
899
  $id: "PreviewSignReceipt",
900
900
  additionalProperties: false
901
901
  });
902
902
  var PreviewSignReceiptValueSchema = Type.Object({
903
903
  verificationMethod: Type.Literal("human-hardware-previewsign"),
904
- value: schemaRef$1(PreviewSignReceiptSchema, "PreviewSignReceipt")
904
+ value: schemaRef$2(PreviewSignReceiptSchema, "PreviewSignReceipt")
905
905
  }, {
906
906
  $id: "PreviewSignReceiptValue",
907
907
  additionalProperties: false
@@ -1595,7 +1595,7 @@ Type.Union([
1595
1595
  ]);
1596
1596
  //#endregion
1597
1597
  //#region ../../libs/models/src/signer-protocol.ts
1598
- function schemaRef(schema) {
1598
+ function schemaRef$1(schema) {
1599
1599
  const id = schemaId(schema);
1600
1600
  return Type.Ref(id);
1601
1601
  }
@@ -1624,13 +1624,13 @@ var SignerProblemSchema = Type.Object({
1624
1624
  $id: "SignerProblem",
1625
1625
  additionalProperties: false
1626
1626
  });
1627
- var SignerCeremonyParamsSchema = Type.Object({ ceremonyId: Type.Unsafe(schemaRef(SignerBase64UrlSchema)) }, {
1627
+ var SignerCeremonyParamsSchema = Type.Object({ ceremonyId: Type.Unsafe(schemaRef$1(SignerBase64UrlSchema)) }, {
1628
1628
  $id: "SignerCeremonyParams",
1629
1629
  additionalProperties: false
1630
1630
  });
1631
1631
  var SignerSessionSchema = Type.Object({
1632
1632
  version: Type.Literal(1),
1633
- token: Type.Unsafe(schemaRef(SignerBase64UrlSchema)),
1633
+ token: Type.Unsafe(schemaRef$1(SignerBase64UrlSchema)),
1634
1634
  expiresAt: Type.String()
1635
1635
  }, {
1636
1636
  $id: "SignerSession",
@@ -1643,25 +1643,25 @@ var SignerEnrollmentCeremonyRequestSchema = Type.Object({
1643
1643
  minLength: 1,
1644
1644
  maxLength: 255
1645
1645
  }),
1646
- teamId: Type.Unsafe(schemaRef(SignerUuidSchema))
1646
+ teamId: Type.Unsafe(schemaRef$1(SignerUuidSchema))
1647
1647
  }, {
1648
1648
  $id: "SignerEnrollmentCeremonyRequest",
1649
1649
  additionalProperties: false
1650
1650
  });
1651
1651
  var SignerChallengeCeremonyRequestSchema = Type.Object({
1652
1652
  version: Type.Literal(1),
1653
- operation: Type.Unsafe(schemaRef(SignerChallengeOperationSchema)),
1654
- resourceId: Type.Unsafe(schemaRef(SignerUuidSchema)),
1655
- challenge: Type.Unsafe(schemaRef(SignerPreviewSignChallengeValueSchema))
1653
+ operation: Type.Unsafe(schemaRef$1(SignerChallengeOperationSchema)),
1654
+ resourceId: Type.Unsafe(schemaRef$1(SignerUuidSchema)),
1655
+ challenge: Type.Unsafe(schemaRef$1(SignerPreviewSignChallengeValueSchema))
1656
1656
  }, {
1657
1657
  $id: "SignerChallengeCeremonyRequest",
1658
1658
  additionalProperties: false
1659
1659
  });
1660
- var SignerCeremonyRequestSchema = Type.Union([Type.Unsafe(schemaRef(SignerEnrollmentCeremonyRequestSchema)), Type.Unsafe(schemaRef(SignerChallengeCeremonyRequestSchema))], { $id: "SignerCeremonyRequest" });
1660
+ var SignerCeremonyRequestSchema = Type.Union([Type.Unsafe(schemaRef$1(SignerEnrollmentCeremonyRequestSchema)), Type.Unsafe(schemaRef$1(SignerChallengeCeremonyRequestSchema))], { $id: "SignerCeremonyRequest" });
1661
1661
  var SignerCeremonySchema = Type.Object({
1662
1662
  version: Type.Literal(1),
1663
- id: Type.Unsafe(schemaRef(SignerBase64UrlSchema)),
1664
- operation: Type.Unsafe(schemaRef(SignerOperationSchema)),
1663
+ id: Type.Unsafe(schemaRef$1(SignerBase64UrlSchema)),
1664
+ operation: Type.Unsafe(schemaRef$1(SignerOperationSchema)),
1665
1665
  approvalUrl: Type.String(),
1666
1666
  expiresAt: Type.String()
1667
1667
  }, {
@@ -1671,7 +1671,7 @@ var SignerCeremonySchema = Type.Object({
1671
1671
  var SignerPendingResultSchema = Type.Object({
1672
1672
  version: Type.Literal(1),
1673
1673
  status: Type.Literal("pending"),
1674
- operation: Type.Unsafe(schemaRef(SignerOperationSchema))
1674
+ operation: Type.Unsafe(schemaRef$1(SignerOperationSchema))
1675
1675
  }, {
1676
1676
  $id: "SignerPendingResult",
1677
1677
  additionalProperties: false
@@ -1680,7 +1680,7 @@ var SignerEnrollmentResultSchema = Type.Object({
1680
1680
  version: Type.Literal(1),
1681
1681
  status: Type.Literal("completed"),
1682
1682
  operation: Type.Literal("credential-enrollment"),
1683
- publicMaterial: Type.Unsafe(schemaRef(SignerPreviewSignPublicMaterialSchema))
1683
+ publicMaterial: Type.Unsafe(schemaRef$1(SignerPreviewSignPublicMaterialSchema))
1684
1684
  }, {
1685
1685
  $id: "SignerEnrollmentResult",
1686
1686
  additionalProperties: false
@@ -1689,8 +1689,8 @@ var SignerReceiptSchema = PreviewSignReceiptValueSchema;
1689
1689
  var SignerSignatureResultSchema = Type.Object({
1690
1690
  version: Type.Literal(1),
1691
1691
  status: Type.Literal("completed"),
1692
- operation: Type.Unsafe(schemaRef(SignerChallengeOperationSchema)),
1693
- receipt: Type.Unsafe(schemaRef(SignerReceiptSchema))
1692
+ operation: Type.Unsafe(schemaRef$1(SignerChallengeOperationSchema)),
1693
+ receipt: Type.Unsafe(schemaRef$1(SignerReceiptSchema))
1694
1694
  }, {
1695
1695
  $id: "SignerSignatureResult",
1696
1696
  additionalProperties: false
@@ -1698,7 +1698,7 @@ var SignerSignatureResultSchema = Type.Object({
1698
1698
  var SignerFailedResultSchema = Type.Object({
1699
1699
  version: Type.Literal(1),
1700
1700
  status: Type.Literal("failed"),
1701
- operation: Type.Unsafe(schemaRef(SignerOperationSchema)),
1701
+ operation: Type.Unsafe(schemaRef$1(SignerOperationSchema)),
1702
1702
  code: Type.String(),
1703
1703
  message: Type.String()
1704
1704
  }, {
@@ -1706,10 +1706,10 @@ var SignerFailedResultSchema = Type.Object({
1706
1706
  additionalProperties: false
1707
1707
  });
1708
1708
  var SignerCeremonyResultSchema = Type.Union([
1709
- Type.Unsafe(schemaRef(SignerPendingResultSchema)),
1710
- Type.Unsafe(schemaRef(SignerEnrollmentResultSchema)),
1711
- Type.Unsafe(schemaRef(SignerSignatureResultSchema)),
1712
- Type.Unsafe(schemaRef(SignerFailedResultSchema))
1709
+ Type.Unsafe(schemaRef$1(SignerPendingResultSchema)),
1710
+ Type.Unsafe(schemaRef$1(SignerEnrollmentResultSchema)),
1711
+ Type.Unsafe(schemaRef$1(SignerSignatureResultSchema)),
1712
+ Type.Unsafe(schemaRef$1(SignerFailedResultSchema))
1713
1713
  ], { $id: "SignerCeremonyResult" });
1714
1714
  ({ ...previewSignSchemaContext }), schemaId(SignerUuidSchema), schemaId(SignerOperationSchema), schemaId(SignerProblemSchema), schemaId(SignerCeremonyParamsSchema), schemaId(SignerSessionSchema), schemaId(SignerEnrollmentCeremonyRequestSchema), schemaId(SignerChallengeCeremonyRequestSchema), schemaId(SignerCeremonyRequestSchema), schemaId(SignerCeremonySchema), schemaId(SignerPendingResultSchema), schemaId(SignerEnrollmentResultSchema), schemaId(SignerSignatureResultSchema), schemaId(SignerFailedResultSchema), schemaId(SignerCeremonyResultSchema);
1715
1715
  //#endregion
@@ -3426,7 +3426,7 @@ Commands:
3426
3426
  once Claim and execute one specific queued task by id, then exit.
3427
3427
  drain Poll until the queue has nothing claimable, then exit.
3428
3428
  Useful for batch eval runs and demos.
3429
- serve Loopback supervisor for console-managed runs: pairing,
3429
+ server Loopback supervisor for console-managed runs: pairing,
3430
3430
  agent/provider config store, and start/stop of poll/drain
3431
3431
  child processes. Binds 127.0.0.1 only.
3432
3432
  sync-sessions
@@ -3561,8 +3561,8 @@ Example:
3561
3561
  function isHelpFlag(args) {
3562
3562
  return args.includes("--help") || args.includes("-h");
3563
3563
  }
3564
- var SERVE_HELP = `\
3565
- agent-daemon serve — loopback supervisor for console-managed runs.
3564
+ var AGENT_SERVER_HELP = `\
3565
+ agent-daemon server — loopback supervisor for console-managed runs.
3566
3566
 
3567
3567
  Binds 127.0.0.1 only. A paired Console origin configures agents and
3568
3568
  providers (secret references only) and starts/stops poll/drain runs as
@@ -3570,12 +3570,12 @@ child processes of this supervisor.
3570
3570
 
3571
3571
  Options:
3572
3572
  --port <n> Loopback port. Default: 17374.
3573
- Env: MOLTNET_SERVE_PORT.
3573
+ Env: MOLTNET_AGENT_SERVER_PORT.
3574
3574
  --allowed-origins <csv> Exact Console origins allowed to pair.
3575
3575
  Default: https://console.themolt.net.
3576
- Env: MOLTNET_SERVE_ALLOWED_ORIGINS.
3576
+ Env: MOLTNET_AGENT_SERVER_ALLOWED_ORIGINS.
3577
3577
  --root <path> Config root. Default: ~/.config/moltnet
3578
- (or MOLTNET_SERVE_ROOT).
3578
+ (or MOLTNET_AGENT_SERVER_ROOT).
3579
3579
  --api-url <url> Default MoltNet API for new managed agents.
3580
3580
  Default: https://api.themolt.net.
3581
3581
  `;
@@ -3658,7 +3658,7 @@ async function validateStartupBinding(options) {
3658
3658
  const assessment = assessStartupBinding(whoami, options.teamId);
3659
3659
  if (!assessment.ok) throw new Error(`Daemon startup validation failed: ${assessment.reason}`);
3660
3660
  const expected = options.expectedIdentity;
3661
- if (expected && !assessIdentityPin(whoami, expected).ok) throw new Error("Daemon startup validation failed: authenticated identity does not match the serve activation.");
3661
+ if (expected && !assessIdentityPin(whoami, expected).ok) throw new Error("Daemon startup validation failed: authenticated identity does not match the Agent Server activation.");
3662
3662
  return whoami;
3663
3663
  }
3664
3664
  /**
@@ -3784,17 +3784,17 @@ function readBoolean(name, value) {
3784
3784
  function activatePiCodingAgentDir(path) {
3785
3785
  process.env["PI_CODING_AGENT_DIR"] = path;
3786
3786
  }
3787
- function loadServeEnvConfig() {
3787
+ function loadAgentServerEnvConfig() {
3788
3788
  return {
3789
- port: process.env["MOLTNET_SERVE_PORT"] ?? "",
3790
- allowedOrigins: process.env["MOLTNET_SERVE_ALLOWED_ORIGINS"] ?? "",
3791
- root: process.env["MOLTNET_SERVE_ROOT"] ?? "",
3789
+ port: process.env["MOLTNET_AGENT_SERVER_PORT"] ?? "",
3790
+ allowedOrigins: process.env["MOLTNET_AGENT_SERVER_ALLOWED_ORIGINS"] ?? "",
3791
+ root: process.env["MOLTNET_AGENT_SERVER_ROOT"] ?? "",
3792
3792
  xdgConfigHome: process.env["XDG_CONFIG_HOME"] ?? "",
3793
3793
  apiUrl: process.env["MOLTNET_API_URL"] ?? "",
3794
3794
  logLevel: process.env["LOG_LEVEL"] ?? ""
3795
3795
  };
3796
3796
  }
3797
- /** Full process environment for spawned serve run children. */
3797
+ /** Full process environment for spawned Agent Server run children. */
3798
3798
  function processEnvSnapshot() {
3799
3799
  return process.env;
3800
3800
  }
@@ -4859,7 +4859,7 @@ function executionIntentFromRuntimeProfile(input) {
4859
4859
  * local configuration and logs its value-free decisions. It gates nothing
4860
4860
  * yet — enforcement is a separate, later change.
4861
4861
  */
4862
- var DEFAULT_GOVERNANCE_OBSERVE_TIMEOUT_MS = 2e3;
4862
+ var DEFAULT_GOVERNANCE_OBAGENT_SERVER_TIMEOUT_MS = 2e3;
4863
4863
  /**
4864
4864
  * Two distinct provenance sources (#2022 review): requirements come from a
4865
4865
  * profile-side/private input (`MOLTNET_PROFILE_CREDENTIAL_REQUIREMENTS`, a
@@ -4988,7 +4988,7 @@ async function observeGovernancePlan(input) {
4988
4988
  * The observer is intentionally non-gating: failures and deadlines are logged
4989
4989
  * with claim correlation and converted to `null`.
4990
4990
  */
4991
- async function observeGovernancePlanSafely(input, timeoutMs = DEFAULT_GOVERNANCE_OBSERVE_TIMEOUT_MS) {
4991
+ async function observeGovernancePlanSafely(input, timeoutMs = DEFAULT_GOVERNANCE_OBAGENT_SERVER_TIMEOUT_MS) {
4992
4992
  let timeout;
4993
4993
  try {
4994
4994
  return await Promise.race([observeGovernancePlan(input), new Promise((_resolve, reject) => {
@@ -7056,6 +7056,8 @@ function requireOriginHeader(headers) {
7056
7056
  //#endregion
7057
7057
  //#region ../../libs/loopback-companion/src/fastify.ts
7058
7058
  var CORS_PREFLIGHT_MAX_AGE_SECONDS = 600;
7059
+ var PRIVATE_NETWORK_REQUEST_HEADER = "access-control-request-private-network";
7060
+ var PRIVATE_NETWORK_ALLOW_HEADER = "access-control-allow-private-network";
7059
7061
  /**
7060
7062
  * Enforce that the `Host` header identifies loopback. Blocks DNS-rebinding
7061
7063
  * setups where a public hostname resolves to 127.0.0.1: the browser then
@@ -7093,8 +7095,9 @@ function registerLoopbackSecurity(app, options) {
7093
7095
  requireLoopbackHost(request);
7094
7096
  done();
7095
7097
  });
7096
- app.addHook("onSend", async (_request, reply, payload) => {
7098
+ app.addHook("onSend", async (request, reply, payload) => {
7097
7099
  reply.header("cache-control", "no-store");
7100
+ if (request.method === "OPTIONS" && request.headers[PRIVATE_NETWORK_REQUEST_HEADER] === "true" && reply.getHeader("access-control-allow-origin") === request.headers.origin) reply.header(PRIVATE_NETWORK_ALLOW_HEADER, "true");
7098
7101
  return payload;
7099
7102
  });
7100
7103
  app.removeContentTypeParser("application/json");
@@ -7170,9 +7173,65 @@ function rejectExplicitCrossSite(headers) {
7170
7173
  if (headerValue(headers, "sec-fetch-site") === "cross-site") throw new LoopbackViolationError("cross_site_rejected", "Request must not originate cross-site");
7171
7174
  }
7172
7175
  //#endregion
7173
- //#region src/lib/serve/pairing.ts
7176
+ //#region src/lib/agent-server/lock.ts
7177
+ var AgentServerLockError = class extends Error {
7178
+ name = "AgentServerLockError";
7179
+ constructor(code, message, options) {
7180
+ super(message, options);
7181
+ this.code = code;
7182
+ }
7183
+ };
7184
+ /**
7185
+ * Acquire the per-root singleton lock. `proper-lockfile` uses an atomic lock
7186
+ * directory at exactly `<root>/agent-server.lock`, recovers stale owners, and keeps
7187
+ * the mtime fresh while the supervisor is alive.
7188
+ */
7189
+ async function acquireAgentServerLock(root, options = {}) {
7190
+ const path = join(root, "agent-server.lock");
7191
+ let releaseLock;
7192
+ try {
7193
+ releaseLock = await lock(root, {
7194
+ lockfilePath: path,
7195
+ realpath: false,
7196
+ retries: 0,
7197
+ ...options.staleMs === void 0 ? {} : { stale: options.staleMs },
7198
+ ...options.updateMs === void 0 ? {} : { update: options.updateMs },
7199
+ onCompromised: (cause) => {
7200
+ const error = new AgentServerLockError("compromised", `Agent Server lock ${path} was compromised: ${cause.message}`, { cause });
7201
+ if (options.onCompromised) {
7202
+ options.onCompromised(error);
7203
+ return;
7204
+ }
7205
+ throw error;
7206
+ }
7207
+ });
7208
+ } catch (cause) {
7209
+ if (cause.code === "ELOCKED") throw new AgentServerLockError("held", `another moltnet-agent server process already owns ${path}`, { cause });
7210
+ throw new AgentServerLockError("failed", `could not acquire Agent Server lock ${path}: ${cause.message}`, { cause });
7211
+ }
7212
+ let released = false;
7213
+ return {
7214
+ path,
7215
+ async release() {
7216
+ if (released) return;
7217
+ released = true;
7218
+ await releaseLock();
7219
+ }
7220
+ };
7221
+ }
7222
+ /** Always release after normal completion or startup/runtime failure. */
7223
+ async function withAgentServerLock(root, work, options) {
7224
+ const held = await acquireAgentServerLock(root, options);
7225
+ try {
7226
+ return await work();
7227
+ } finally {
7228
+ await held.release();
7229
+ }
7230
+ }
7231
+ //#endregion
7232
+ //#region src/lib/agent-server/pairing.ts
7174
7233
  /**
7175
- * One-click pairing ceremony for the serve supervisor, mirroring the signer's
7234
+ * One-click pairing ceremony for the agent server, mirroring the signer's
7176
7235
  * session/ceremony pattern (#2062 design):
7177
7236
  *
7178
7237
  * 1. Console (allowed origin) POSTs `/v1/pairings` → pending pairing bound
@@ -7193,8 +7252,8 @@ function rejectExplicitCrossSite(headers) {
7193
7252
  * supervisor process.
7194
7253
  */
7195
7254
  var PENDING_TTL_MS = 600 * 1e3;
7196
- var ServePairingError = class extends Error {
7197
- name = "ServePairingError";
7255
+ var AgentServerPairingError = class extends Error {
7256
+ name = "AgentServerPairingError";
7198
7257
  constructor(code, message) {
7199
7258
  super(message);
7200
7259
  this.code = code;
@@ -7242,7 +7301,7 @@ var PairingService = class {
7242
7301
  /** Data for the local approval page. */
7243
7302
  approval(pairingId) {
7244
7303
  const pairing = this.require(pairingId);
7245
- if (pairing.approved) throw new ServePairingError("pairing_invalid", "Pairing is already approved");
7304
+ if (pairing.approved) throw new AgentServerPairingError("pairing_invalid", "Pairing is already approved");
7246
7305
  return {
7247
7306
  origin: pairing.origin,
7248
7307
  confirmToken: pairing.confirmToken
@@ -7250,15 +7309,15 @@ var PairingService = class {
7250
7309
  }
7251
7310
  confirm(pairingId, confirmToken) {
7252
7311
  const pairing = this.require(pairingId);
7253
- if (pairing.approved || !safeEqual(pairing.confirmToken, confirmToken)) throw new ServePairingError("pairing_invalid", "Confirmation token is not valid");
7312
+ if (pairing.approved || !safeEqual(pairing.confirmToken, confirmToken)) throw new AgentServerPairingError("pairing_invalid", "Confirmation token is not valid");
7254
7313
  pairing.approved = true;
7255
7314
  pairing.bearerToken = this.token();
7256
7315
  return { origin: pairing.origin };
7257
7316
  }
7258
7317
  claim(pairingId, origin) {
7259
7318
  const pairing = this.require(pairingId);
7260
- if (pairing.origin !== origin) throw new ServePairingError("pairing_origin_mismatch", "Pairing belongs to a different origin");
7261
- if (!pairing.approved || !pairing.bearerToken) throw new ServePairingError("pairing_not_approved", "Pairing has not been approved yet");
7319
+ if (pairing.origin !== origin) throw new AgentServerPairingError("pairing_origin_mismatch", "Pairing belongs to a different origin");
7320
+ if (!pairing.approved || !pairing.bearerToken) throw new AgentServerPairingError("pairing_not_approved", "Pairing has not been approved yet");
7262
7321
  const token = pairing.bearerToken;
7263
7322
  this.pending.delete(pairingId);
7264
7323
  this.paired.set(origin, sha256Hex(token));
@@ -7266,12 +7325,12 @@ var PairingService = class {
7266
7325
  }
7267
7326
  verify(origin, token) {
7268
7327
  const tokenHash = this.paired.get(origin);
7269
- if (!tokenHash || !safeEqual(tokenHash, sha256Hex(token))) throw new ServePairingError("pairing_token_invalid", "Pairing token is not valid for this origin");
7328
+ if (!tokenHash || !safeEqual(tokenHash, sha256Hex(token))) throw new AgentServerPairingError("pairing_token_invalid", "Pairing token is not valid for this origin");
7270
7329
  }
7271
7330
  require(pairingId) {
7272
7331
  this.sweep();
7273
7332
  const pairing = this.pending.get(pairingId);
7274
- if (!pairing) throw new ServePairingError("pairing_not_found", "Pairing was not found or has expired");
7333
+ if (!pairing) throw new AgentServerPairingError("pairing_not_found", "Pairing was not found or has expired");
7275
7334
  return pairing;
7276
7335
  }
7277
7336
  };
@@ -7321,24 +7380,25 @@ function renderPairingResultPage(input) {
7321
7380
  `;
7322
7381
  }
7323
7382
  //#endregion
7324
- //#region src/lib/serve/provider-login.ts
7383
+ //#region src/lib/agent-server/provider-login.ts
7325
7384
  /**
7326
- * Subscription-provider OAuth brokering for `serve` (#2061 slice 4).
7385
+ * Subscription-provider OAuth brokering for Agent Server (#2061 slice 4).
7327
7386
  *
7328
- * The console clicks "Connect"; serve runs the Pi OAuth flow host-side via
7329
- * `AuthStorage.login()` (which owns persistence into the shared
7387
+ * The console clicks "Connect"; agent server runs the Pi OAuth flow host-side via
7388
+ * `ModelRuntime.login()` (which owns persistence into the shared
7330
7389
  * `pi/auth.json` and token rotation thereafter). The browser only ever sees
7331
7390
  * the provider's authorize URL or device code — never tokens.
7332
7391
  *
7333
- * Provider ids come from pi-ai's own OAuth registry (`getOAuthProviders`),
7334
- * so serve stays in lockstep with what Pi can actually authenticate
7335
- * (anthropic, openai-codex, github-copilot, …) without hardcoding.
7392
+ * Provider ids come from Pi's model runtime, so Agent Server stays in lockstep
7393
+ * with supported subscription providers. GitHub Copilot is intentionally
7394
+ * excluded: MoltNet does not broker editor-seat subscription credentials.
7336
7395
  */
7337
7396
  var LOGIN_TTL_MS = 600 * 1e3;
7338
7397
  /** How long `start()` waits for the flow to surface a URL / device code. */
7339
7398
  var START_INFO_TIMEOUT_MS = 5e3;
7340
- var ServeSubscriptionError = class extends Error {
7341
- name = "ServeSubscriptionError";
7399
+ var EXCLUDED_SUBSCRIPTION_PROVIDERS = new Set(["github-copilot"]);
7400
+ var AgentServerSubscriptionError = class extends Error {
7401
+ name = "AgentServerSubscriptionError";
7342
7402
  constructor(code, message) {
7343
7403
  super(message);
7344
7404
  this.code = code;
@@ -7349,37 +7409,49 @@ var silentLogger = {
7349
7409
  warn: () => void 0,
7350
7410
  error: () => void 0
7351
7411
  };
7352
- var ProviderLoginService = class {
7412
+ var ProviderLoginService = class ProviderLoginService {
7353
7413
  logins = /* @__PURE__ */ new Map();
7354
- authStorage;
7355
7414
  logger;
7356
7415
  constructor(options) {
7357
7416
  this.options = options;
7358
- this.authStorage = options.authStorage ?? AuthStorage.create(this.options.authPath);
7359
7417
  this.logger = options.logger ?? silentLogger;
7360
7418
  }
7419
+ static async create(options) {
7420
+ const modelRuntime = await ModelRuntime.create({
7421
+ authPath: options.authPath,
7422
+ refreshOnCreate: false
7423
+ });
7424
+ return new ProviderLoginService({
7425
+ ...options,
7426
+ modelRuntime
7427
+ });
7428
+ }
7361
7429
  now() {
7362
7430
  return this.options.now?.() ?? Date.now();
7363
7431
  }
7364
7432
  providers() {
7365
- return this.options.listProviders?.() ?? getOAuthProviders().map((provider) => ({
7433
+ return (this.options.listProviders ? this.options.listProviders() : this.runtime().getProviders().filter((provider) => provider.auth.oauth !== void 0).map((provider) => ({
7366
7434
  id: provider.id,
7367
7435
  name: provider.name
7368
- }));
7436
+ }))).filter((provider) => !EXCLUDED_SUBSCRIPTION_PROVIDERS.has(provider.id));
7369
7437
  }
7370
7438
  connected(providerId) {
7371
7439
  if (this.options.isConnected) return this.options.isConnected(providerId);
7372
7440
  try {
7373
- return (this.options.authStorage ? this.authStorage : AuthStorage.create(this.options.authPath)).getAuthStatus(providerId).configured;
7441
+ return readStoredCredential(providerId, this.options.authPath) !== void 0;
7374
7442
  } catch (error) {
7375
7443
  this.logger.warn({
7376
- event: "serve.subscription_auth_read_failed",
7444
+ event: "agent-server.subscription_auth_read_failed",
7377
7445
  providerId,
7378
7446
  ...safeLoginError(error)
7379
7447
  }, "Could not read subscription authentication state");
7380
7448
  return false;
7381
7449
  }
7382
7450
  }
7451
+ runtime() {
7452
+ if (!this.options.modelRuntime) throw new Error("ProviderLoginService requires a model runtime when production adapters are not overridden");
7453
+ return this.options.modelRuntime;
7454
+ }
7383
7455
  list() {
7384
7456
  this.sweep();
7385
7457
  return this.providers().map((provider) => ({
@@ -7390,40 +7462,42 @@ var ProviderLoginService = class {
7390
7462
  sweep() {
7391
7463
  const now = this.now();
7392
7464
  for (const [id, login] of this.logins) if (login.startedAt + LOGIN_TTL_MS <= now) {
7393
- this.invalidate(login, "expired");
7465
+ if (login.status === "pending") this.invalidate(login, "expired");
7394
7466
  this.logins.delete(id);
7395
7467
  }
7396
7468
  }
7397
7469
  restoreCredential(login) {
7398
7470
  try {
7399
- if (login.previousCredential) this.authStorage.set(login.providerId, login.previousCredential);
7400
- else this.authStorage.logout(login.providerId);
7471
+ restoreStoredCredential(this.options.authPath, login.providerId, login.previousCredential);
7472
+ return true;
7401
7473
  } catch (error) {
7402
7474
  this.logger.error({
7403
- event: "serve.subscription_login_cleanup_failed",
7475
+ event: "agent-server.subscription_login_cleanup_failed",
7404
7476
  operationId: login.operationId,
7405
7477
  providerId: login.providerId,
7406
7478
  ...safeLoginError(error)
7407
7479
  }, "Could not restore subscription credentials after an invalidated login");
7480
+ return false;
7408
7481
  }
7409
7482
  }
7410
7483
  invalidate(login, transition) {
7411
- if (login.invalidated) return;
7484
+ if (login.invalidated) return true;
7412
7485
  login.invalidated = true;
7413
7486
  login.abort.abort(/* @__PURE__ */ new Error(`subscription login ${transition}`));
7414
- this.restoreCredential(login);
7487
+ const restored = this.restoreCredential(login);
7415
7488
  login.infoArrived();
7416
7489
  this.logger.info({
7417
- event: "serve.subscription_login_transition",
7490
+ event: "agent-server.subscription_login_transition",
7418
7491
  operationId: login.operationId,
7419
7492
  providerId: login.providerId,
7420
7493
  transition
7421
7494
  }, "Subscription login invalidated");
7495
+ return restored;
7422
7496
  }
7423
7497
  status(providerId) {
7424
7498
  this.sweep();
7425
7499
  const login = this.logins.get(providerId);
7426
- if (!login) throw new ServeSubscriptionError("login_not_found", `no login in progress for "${providerId}"`);
7500
+ if (!login) throw new AgentServerSubscriptionError("login_not_found", `no login in progress for "${providerId}"`);
7427
7501
  return snapshot(login);
7428
7502
  }
7429
7503
  /**
@@ -7433,7 +7507,7 @@ var ProviderLoginService = class {
7433
7507
  */
7434
7508
  async start(providerId) {
7435
7509
  this.sweep();
7436
- if (!this.providers().some((provider) => provider.id === providerId)) throw new ServeSubscriptionError("provider_unknown", `"${providerId}" is not a known subscription provider`);
7510
+ if (!this.providers().some((provider) => provider.id === providerId)) throw new AgentServerSubscriptionError("provider_unknown", `"${providerId}" is not a known subscription provider`);
7437
7511
  const existing = this.logins.get(providerId);
7438
7512
  if (existing && existing.status === "pending") return snapshot(existing);
7439
7513
  let infoArrived = () => void 0;
@@ -7449,17 +7523,17 @@ var ProviderLoginService = class {
7449
7523
  infoArrived,
7450
7524
  abort,
7451
7525
  invalidated: false,
7452
- previousCredential: this.authStorage.get(providerId)
7526
+ previousCredential: readStoredCredential(providerId, this.options.authPath)
7453
7527
  };
7454
7528
  this.logins.set(providerId, login);
7455
7529
  this.logger.info({
7456
- event: "serve.subscription_login_transition",
7530
+ event: "agent-server.subscription_login_transition",
7457
7531
  operationId: login.operationId,
7458
7532
  providerId,
7459
7533
  transition: "started"
7460
7534
  }, "Subscription login started");
7461
7535
  const callbacks = createLoginCallbacks(login, this.logger);
7462
- (this.options.runLogin ?? ((id, loginCallbacks) => this.authStorage.login(id, loginCallbacks)))(providerId, callbacks).then(() => {
7536
+ (this.options.runLogin ?? ((id, loginCallbacks) => this.runtime().login(id, "oauth", toAuthInteraction(loginCallbacks)).then(() => void 0)))(providerId, callbacks).then(() => {
7463
7537
  if (login.invalidated) {
7464
7538
  this.restoreCredential(login);
7465
7539
  return;
@@ -7468,7 +7542,7 @@ var ProviderLoginService = class {
7468
7542
  login.status = "failed";
7469
7543
  login.error = "Subscription sign-in completed, but credentials were not persisted. Start again to retry.";
7470
7544
  this.logger.error({
7471
- event: "serve.subscription_login_transition",
7545
+ event: "agent-server.subscription_login_transition",
7472
7546
  operationId: login.operationId,
7473
7547
  providerId,
7474
7548
  transition: "persistence_failed"
@@ -7476,7 +7550,7 @@ var ProviderLoginService = class {
7476
7550
  } else {
7477
7551
  login.status = "completed";
7478
7552
  this.logger.info({
7479
- event: "serve.subscription_login_transition",
7553
+ event: "agent-server.subscription_login_transition",
7480
7554
  operationId: login.operationId,
7481
7555
  providerId,
7482
7556
  transition: "completed"
@@ -7491,7 +7565,7 @@ var ProviderLoginService = class {
7491
7565
  login.status = "failed";
7492
7566
  login.error = publicLoginError(error);
7493
7567
  this.logger.warn({
7494
- event: "serve.subscription_login_transition",
7568
+ event: "agent-server.subscription_login_transition",
7495
7569
  operationId: login.operationId,
7496
7570
  providerId,
7497
7571
  transition: "failed",
@@ -7499,16 +7573,24 @@ var ProviderLoginService = class {
7499
7573
  }, "Subscription login failed");
7500
7574
  login.infoArrived();
7501
7575
  });
7502
- await Promise.race([infoPromise, new Promise((resolvePromise) => {
7503
- setTimeout(resolvePromise, START_INFO_TIMEOUT_MS).unref?.();
7504
- })]);
7576
+ if (!await Promise.race([infoPromise, new Promise((resolvePromise) => {
7577
+ setTimeout(() => resolvePromise(), START_INFO_TIMEOUT_MS).unref?.();
7578
+ })]).then(() => login.authUrl !== void 0 || login.userCode !== void 0 || login.status !== "pending")) {
7579
+ login.waitingForAuthorization = true;
7580
+ this.logger.warn({
7581
+ event: "serve.subscription_login_start_info_timeout",
7582
+ operationId: login.operationId,
7583
+ providerId,
7584
+ timeoutMs: START_INFO_TIMEOUT_MS
7585
+ }, "Subscription login is still waiting for authorization information");
7586
+ }
7505
7587
  return snapshot(login);
7506
7588
  }
7507
7589
  /** Abort an in-flight login and forget it. */
7508
7590
  cancel(providerId) {
7509
7591
  const login = this.logins.get(providerId);
7510
- if (!login) throw new ServeSubscriptionError("login_not_found", `no login in progress for "${providerId}"`);
7511
- this.invalidate(login, "cancelled");
7592
+ if (!login) throw new AgentServerSubscriptionError("login_not_found", `no login in progress for "${providerId}"`);
7593
+ if (!this.invalidate(login, "cancelled")) throw new AgentServerSubscriptionError("login_cleanup_failed", `could not safely cancel login for "${providerId}"; credential cleanup requires intervention`);
7512
7594
  this.logins.delete(providerId);
7513
7595
  return {
7514
7596
  providerId,
@@ -7517,17 +7599,82 @@ var ProviderLoginService = class {
7517
7599
  }
7518
7600
  /** Abort every pending flow during supervisor shutdown. */
7519
7601
  close() {
7520
- for (const login of this.logins.values()) this.invalidate(login, "shutdown");
7602
+ for (const login of this.logins.values()) if (login.status === "pending") this.invalidate(login, "shutdown");
7521
7603
  this.logins.clear();
7522
7604
  }
7523
7605
  };
7606
+ function toAuthInteraction(callbacks) {
7607
+ return {
7608
+ ...callbacks.signal ? { signal: callbacks.signal } : {},
7609
+ notify: (event) => {
7610
+ switch (event.type) {
7611
+ case "auth_url":
7612
+ callbacks.onAuth({
7613
+ url: event.url,
7614
+ ...event.instructions ? { instructions: event.instructions } : {}
7615
+ });
7616
+ break;
7617
+ case "device_code":
7618
+ callbacks.onDeviceCode({
7619
+ userCode: event.userCode,
7620
+ verificationUri: event.verificationUri
7621
+ });
7622
+ break;
7623
+ case "info":
7624
+ case "progress":
7625
+ callbacks.onProgress?.(event.message);
7626
+ break;
7627
+ }
7628
+ },
7629
+ prompt: async (prompt) => {
7630
+ if (prompt.type !== "select") return callbacks.onPrompt({ message: prompt.message });
7631
+ const selected = await callbacks.onSelect({
7632
+ message: prompt.message,
7633
+ options: prompt.options.map(({ id, label }) => ({
7634
+ id,
7635
+ label
7636
+ }))
7637
+ });
7638
+ if (!selected) throw new AgentServerSubscriptionError("login_unsupported_prompt", "This provider flow did not offer a supported sign-in method");
7639
+ return selected;
7640
+ }
7641
+ };
7642
+ }
7643
+ function restoreStoredCredential(authPath, providerId, credential) {
7644
+ mkdirSync(dirname(authPath), {
7645
+ recursive: true,
7646
+ mode: 448
7647
+ });
7648
+ try {
7649
+ writeFileSync(authPath, "{}\n", {
7650
+ flag: "wx",
7651
+ mode: 384
7652
+ });
7653
+ } catch (error) {
7654
+ if (error.code !== "EEXIST") throw error;
7655
+ }
7656
+ const release = lockSync(authPath, { realpath: false });
7657
+ const temp = `${authPath}.${randomBytes(6).toString("hex")}.tmp`;
7658
+ try {
7659
+ const parsed = JSON.parse(readFileSync(authPath, "utf8"));
7660
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error(`Pi credential store is not an object: ${authPath}`);
7661
+ const credentials = parsed;
7662
+ if (credential) credentials[providerId] = credential;
7663
+ else delete credentials[providerId];
7664
+ writeFileSync(temp, `${JSON.stringify(credentials, null, 2)}\n`, { mode: 384 });
7665
+ renameSync(temp, authPath);
7666
+ } finally {
7667
+ rmSync(temp, { force: true });
7668
+ release();
7669
+ }
7670
+ }
7524
7671
  function createLoginCallbacks(login, logger) {
7525
7672
  return {
7526
7673
  onAuth: (info) => {
7527
7674
  login.authUrl = info.url;
7528
7675
  if (info.instructions) login.instructions = info.instructions;
7529
7676
  logger.info({
7530
- event: "serve.subscription_login_transition",
7677
+ event: "agent-server.subscription_login_transition",
7531
7678
  operationId: login.operationId,
7532
7679
  providerId: login.providerId,
7533
7680
  transition: "authorization_ready"
@@ -7538,20 +7685,20 @@ function createLoginCallbacks(login, logger) {
7538
7685
  login.userCode = info.userCode;
7539
7686
  login.verificationUri = info.verificationUri;
7540
7687
  logger.info({
7541
- event: "serve.subscription_login_transition",
7688
+ event: "agent-server.subscription_login_transition",
7542
7689
  operationId: login.operationId,
7543
7690
  providerId: login.providerId,
7544
7691
  transition: "device_code_ready"
7545
7692
  }, "Subscription device code ready");
7546
7693
  login.infoArrived();
7547
7694
  },
7548
- onPrompt: () => Promise.reject(new ServeSubscriptionError("login_unsupported_prompt", "This provider flow needs an interactive prompt; run `pi /login` in a terminal instead")),
7695
+ onPrompt: () => Promise.reject(new AgentServerSubscriptionError("login_unsupported_prompt", "This provider flow needs an interactive prompt; run `pi /login` in a terminal instead")),
7549
7696
  onSelect: (prompt) => Promise.resolve((prompt.options.find((option) => /device/i.test(option.id)) ?? prompt.options[0])?.id),
7550
7697
  signal: login.abort.signal
7551
7698
  };
7552
7699
  }
7553
7700
  function publicLoginError(error) {
7554
- if (error instanceof ServeSubscriptionError) return error.message;
7701
+ if (error instanceof AgentServerSubscriptionError) return error.message;
7555
7702
  return "Subscription sign-in failed. Start again to retry.";
7556
7703
  }
7557
7704
  function safeLoginError(error) {
@@ -7561,7 +7708,7 @@ function safeLoginError(error) {
7561
7708
  return result;
7562
7709
  }
7563
7710
  function snapshot(login) {
7564
- const { providerId, status, authUrl, instructions, userCode, verificationUri, error } = login;
7711
+ const { providerId, status, authUrl, instructions, userCode, verificationUri, error, waitingForAuthorization } = login;
7565
7712
  return {
7566
7713
  providerId,
7567
7714
  status,
@@ -7569,28 +7716,29 @@ function snapshot(login) {
7569
7716
  ...instructions ? { instructions } : {},
7570
7717
  ...userCode ? { userCode } : {},
7571
7718
  ...verificationUri ? { verificationUri } : {},
7572
- ...error ? { error } : {}
7719
+ ...error ? { error } : {},
7720
+ ...waitingForAuthorization ? { waitingForAuthorization } : {}
7573
7721
  };
7574
7722
  }
7575
7723
  var NAME_RE = /^[a-z0-9][a-z0-9_-]{0,63}$/i;
7576
7724
  var PROVIDER_ID_RE = /^[a-z0-9][a-z0-9-]{0,63}$/;
7577
7725
  function assertStoreName(kind, value) {
7578
- if (!NAME_RE.test(value)) throw new ServeStoreError("invalid_name", `${kind} must match ${NAME_RE.source}`);
7726
+ if (!NAME_RE.test(value)) throw new AgentServerStoreError("invalid_name", `${kind} must match ${NAME_RE.source}`);
7579
7727
  return value;
7580
7728
  }
7581
7729
  function assertProviderId(value) {
7582
- if (!PROVIDER_ID_RE.test(value)) throw new ServeStoreError("invalid_name", `provider id must match ${PROVIDER_ID_RE.source}`);
7730
+ if (!PROVIDER_ID_RE.test(value)) throw new AgentServerStoreError("invalid_name", `provider id must match ${PROVIDER_ID_RE.source}`);
7583
7731
  return value;
7584
7732
  }
7585
- var ServeStoreError = class extends Error {
7586
- name = "ServeStoreError";
7733
+ var AgentServerStoreError = class extends Error {
7734
+ name = "AgentServerStoreError";
7587
7735
  constructor(code, message, options) {
7588
7736
  super(message, options);
7589
7737
  this.code = code;
7590
7738
  }
7591
7739
  };
7592
- /** `MOLTNET_SERVE_ROOT` override, else `$XDG_CONFIG_HOME/moltnet`, else `~/.config/moltnet`. */
7593
- function resolveServeRoot(input) {
7740
+ /** `MOLTNET_AGENT_SERVER_ROOT` override, else `$XDG_CONFIG_HOME/moltnet`, else `~/.config/moltnet`. */
7741
+ function resolveAgentServerRoot(input) {
7594
7742
  const override = input.root?.trim();
7595
7743
  if (override) return override;
7596
7744
  const xdg = input.xdgConfigHome?.trim();
@@ -7601,7 +7749,7 @@ function providerEnvName(providerId) {
7601
7749
  }
7602
7750
  function assertProviderEnvName(providerId, value) {
7603
7751
  const expected = providerEnvName(providerId);
7604
- if (value !== expected) throw new ServeStoreError("invalid_state", `provider envName must be ${expected}`);
7752
+ if (value !== expected) throw new AgentServerStoreError("invalid_state", `provider envName must be ${expected}`);
7605
7753
  return value;
7606
7754
  }
7607
7755
  function readJson(path) {
@@ -7610,12 +7758,12 @@ function readJson(path) {
7610
7758
  raw = readFileSync(path, "utf8");
7611
7759
  } catch (cause) {
7612
7760
  if (cause.code === "ENOENT") return null;
7613
- throw new ServeStoreError("io_error", `could not read state at ${path}`, { cause });
7761
+ throw new AgentServerStoreError("io_error", `could not read state at ${path}`, { cause });
7614
7762
  }
7615
7763
  try {
7616
7764
  return JSON.parse(raw);
7617
7765
  } catch (cause) {
7618
- throw new ServeStoreError("invalid_state", `corrupt JSON at ${path}`, { cause });
7766
+ throw new AgentServerStoreError("invalid_state", `corrupt JSON at ${path}`, { cause });
7619
7767
  }
7620
7768
  }
7621
7769
  function writeJsonAtomic(path, value) {
@@ -7630,7 +7778,7 @@ function writeJsonAtomic(path, value) {
7630
7778
  throw cause;
7631
7779
  }
7632
7780
  }
7633
- var ServeStore = class {
7781
+ var AgentServerStore = class {
7634
7782
  root;
7635
7783
  agentsDir;
7636
7784
  runsDir;
@@ -7660,23 +7808,23 @@ var ServeStore = class {
7660
7808
  });
7661
7809
  return this;
7662
7810
  }
7663
- get servePath() {
7664
- return join(this.root, "serve.json");
7811
+ get statePath() {
7812
+ return join(this.root, "agent-server.json");
7665
7813
  }
7666
- readServeState() {
7667
- const state = readJson(this.servePath);
7814
+ readAgentServerState() {
7815
+ const state = readJson(this.statePath);
7668
7816
  if (!state) return {
7669
7817
  version: 1,
7670
7818
  pendingRegistrations: {},
7671
7819
  activations: {}
7672
7820
  };
7673
- if (!isRecord$1(state) || state.version !== 1) throw new ServeStoreError("invalid_state", `serve.json version ${String(isRecord$1(state) ? state.version : void 0)} is not supported`);
7674
- if ("pairedOrigins" in state) throw new ServeStoreError("invalid_state", "serve.json uses the obsolete pairing format; clear the unreleased serve store and reconfigure it");
7675
- if (!isRecord$1(state.pendingRegistrations) || !isRecord$1(state.activations)) throw new ServeStoreError("invalid_state", "serve.json is missing the version 1 activation map; clear the unreleased serve store and reconfigure it");
7821
+ if (!isRecord$1(state) || state.version !== 1) throw new AgentServerStoreError("invalid_state", `agent-server.json version ${String(isRecord$1(state) ? state.version : void 0)} is not supported`);
7822
+ if ("pairedOrigins" in state) throw new AgentServerStoreError("invalid_state", "agent-server.json uses the obsolete pairing format; clear the unreleased agent server store and reconfigure it");
7823
+ if (!isRecord$1(state.pendingRegistrations) || !isRecord$1(state.activations)) throw new AgentServerStoreError("invalid_state", "agent-server.json is missing the version 1 activation map; clear the unreleased agent server store and reconfigure it");
7676
7824
  for (const [alias, activation] of Object.entries(state.activations)) validateActivation(alias, activation);
7677
7825
  for (const [alias, registration] of Object.entries(state.pendingRegistrations)) {
7678
7826
  assertStoreName("agent name", alias);
7679
- if (!isRecord$1(registration) || typeof registration.apiUrl !== "string" || registration.apiUrl.length === 0 || typeof registration.createdAt !== "string" || registration.createdAt.length === 0) throw new ServeStoreError("invalid_state", `pending registration "${alias}" is not valid`);
7827
+ if (!isRecord$1(registration) || typeof registration.apiUrl !== "string" || registration.apiUrl.length === 0 || typeof registration.createdAt !== "string" || registration.createdAt.length === 0) throw new AgentServerStoreError("invalid_state", `pending registration "${alias}" is not valid`);
7680
7828
  }
7681
7829
  return {
7682
7830
  version: 1,
@@ -7684,8 +7832,8 @@ var ServeStore = class {
7684
7832
  activations: state.activations
7685
7833
  };
7686
7834
  }
7687
- writeServeState(state) {
7688
- writeJsonAtomic(this.servePath, state);
7835
+ writeAgentServerState(state) {
7836
+ writeJsonAtomic(this.statePath, state);
7689
7837
  }
7690
7838
  agentPath(name) {
7691
7839
  return storeChildPath(this.agentsDir, "agent name", name, ".json");
@@ -7700,37 +7848,37 @@ var ServeStore = class {
7700
7848
  rmSync(this.agentPath(alias), { force: true });
7701
7849
  }
7702
7850
  readActivation(alias) {
7703
- return this.readServeState().activations[assertStoreName("agent name", alias)] ?? null;
7851
+ return this.readAgentServerState().activations[assertStoreName("agent name", alias)] ?? null;
7704
7852
  }
7705
7853
  hasPendingRegistration(alias) {
7706
- return Boolean(this.readServeState().pendingRegistrations[assertStoreName("agent name", alias)]);
7854
+ return Boolean(this.readAgentServerState().pendingRegistrations[assertStoreName("agent name", alias)]);
7707
7855
  }
7708
7856
  reserveRegistration(alias, apiUrl) {
7709
7857
  const name = assertStoreName("agent name", alias);
7710
- const state = this.readServeState();
7711
- if (state.activations[name] || state.pendingRegistrations[name]) throw new ServeStoreError("already_exists", `agent "${name}" already exists in the serve store`);
7858
+ const state = this.readAgentServerState();
7859
+ if (state.activations[name] || state.pendingRegistrations[name]) throw new AgentServerStoreError("already_exists", `agent "${name}" already exists in the agent server store`);
7712
7860
  state.pendingRegistrations[name] = {
7713
7861
  apiUrl,
7714
7862
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
7715
7863
  };
7716
- this.writeServeState(state);
7864
+ this.writeAgentServerState(state);
7717
7865
  }
7718
7866
  clearPendingRegistration(alias) {
7719
7867
  const name = assertStoreName("agent name", alias);
7720
- const state = this.readServeState();
7868
+ const state = this.readAgentServerState();
7721
7869
  delete state.pendingRegistrations[name];
7722
- this.writeServeState(state);
7870
+ this.writeAgentServerState(state);
7723
7871
  }
7724
7872
  writeActivation(activation) {
7725
7873
  const alias = assertStoreName("agent name", activation.alias);
7726
7874
  validateActivation(alias, activation);
7727
- const state = this.readServeState();
7875
+ const state = this.readAgentServerState();
7728
7876
  state.activations[alias] = activation;
7729
7877
  if (activation.source === "managed") delete state.pendingRegistrations[alias];
7730
- this.writeServeState(state);
7878
+ this.writeAgentServerState(state);
7731
7879
  }
7732
7880
  listActivations() {
7733
- return Object.values(this.readServeState().activations).sort((a, b) => a.alias.localeCompare(b.alias));
7881
+ return Object.values(this.readAgentServerState().activations).sort((a, b) => a.alias.localeCompare(b.alias));
7734
7882
  }
7735
7883
  get providersPath() {
7736
7884
  return join(this.root, "providers.json");
@@ -7760,17 +7908,17 @@ var ServeStore = class {
7760
7908
  root = realpathSync(this.runsDir);
7761
7909
  runDir = realpathSync(this.runDir(id));
7762
7910
  } catch (cause) {
7763
- throw new ServeStoreError("io_error", "could not resolve run directory", { cause });
7911
+ throw new AgentServerStoreError("io_error", "could not resolve run directory", { cause });
7764
7912
  }
7765
- if (!isStrictDescendant(root, runDir)) throw new ServeStoreError("invalid_state", "run directory escapes its store");
7913
+ if (!isStrictDescendant(root, runDir)) throw new AgentServerStoreError("invalid_state", "run directory escapes its store");
7766
7914
  const logPath = join(runDir, "daemon.log");
7767
7915
  try {
7768
- if (lstatSync(logPath).isSymbolicLink()) throw new ServeStoreError("invalid_state", "run log must not be a symbolic link");
7916
+ if (lstatSync(logPath).isSymbolicLink()) throw new AgentServerStoreError("invalid_state", "run log must not be a symbolic link");
7769
7917
  const resolvedLog = realpathSync(logPath);
7770
- if (!isStrictDescendant(runDir, resolvedLog)) throw new ServeStoreError("invalid_state", "run log escapes its store");
7918
+ if (!isStrictDescendant(runDir, resolvedLog)) throw new AgentServerStoreError("invalid_state", "run log escapes its store");
7771
7919
  } catch (cause) {
7772
- if (cause instanceof ServeStoreError) throw cause;
7773
- if (cause.code !== "ENOENT") throw new ServeStoreError("io_error", "could not resolve run log", { cause });
7920
+ if (cause instanceof AgentServerStoreError) throw cause;
7921
+ if (cause.code !== "ENOENT") throw new AgentServerStoreError("io_error", "could not resolve run log", { cause });
7774
7922
  }
7775
7923
  return logPath;
7776
7924
  }
@@ -7857,7 +8005,7 @@ function storeChildPath(root, kind, value, suffix = "") {
7857
8005
  const name = assertStoreName(kind, value);
7858
8006
  const normalizedRoot = resolve(root);
7859
8007
  const candidate = resolve(normalizedRoot, `${name}${suffix}`);
7860
- if (!isStrictDescendant(normalizedRoot, candidate)) throw new ServeStoreError("invalid_name", `${kind} escapes its store`);
8008
+ if (!isStrictDescendant(normalizedRoot, candidate)) throw new AgentServerStoreError("invalid_name", `${kind} escapes its store`);
7861
8009
  return candidate;
7862
8010
  }
7863
8011
  function isStrictDescendant(root, candidate) {
@@ -7866,7 +8014,7 @@ function isStrictDescendant(root, candidate) {
7866
8014
  }
7867
8015
  function validateActivation(alias, value) {
7868
8016
  const invalid = () => {
7869
- throw new ServeStoreError("invalid_state", `activation "${alias}" is not a valid version 1 activation`);
8017
+ throw new AgentServerStoreError("invalid_state", `activation "${alias}" is not a valid version 1 activation`);
7870
8018
  };
7871
8019
  if (!isRecord$1(value)) invalid();
7872
8020
  const activation = value;
@@ -7884,20 +8032,20 @@ function validateActivation(alias, value) {
7884
8032
  if (activation.source !== "external" || typeof activation.configPath !== "string" || activation.configPath.length === 0 || typeof activation.configApiUrl !== "string" || activation.configApiUrl.length === 0 || activation.apiUrl !== void 0 && typeof activation.apiUrl !== "string") invalid();
7885
8033
  }
7886
8034
  //#endregion
7887
- //#region src/lib/serve/identity.ts
8035
+ //#region src/lib/agent-server/identity.ts
7888
8036
  /**
7889
- * Serve identity activation (#2061/#1834 boundary).
8037
+ * AgentServer identity activation (#2061/#1834 boundary).
7890
8038
  *
7891
8039
  * Managed agent files are canonical `MoltNetConfig` documents. Alias,
7892
8040
  * provenance, pinned identity material, and external config paths live only
7893
- * in the versioned activation map in `serve.json`. Every attach and run loads
8041
+ * in the versioned activation map in `agent-server.json`. Every attach and run loads
7894
8042
  * the current config and verifies it with authenticated `whoami` before use.
7895
8043
  */
7896
8044
  var MAX_CONFIG_BYTES = 64 * 1024;
7897
8045
  var IDENTITY_OPERATION_TIMEOUT_MS = 15e3;
7898
8046
  var pendingAliases = /* @__PURE__ */ new WeakMap();
7899
- var ServeIdentityError = class extends Error {
7900
- name = "ServeIdentityError";
8047
+ var AgentServerIdentityError = class extends Error {
8048
+ name = "AgentServerIdentityError";
7901
8049
  constructor(code, message, options) {
7902
8050
  super(message, options);
7903
8051
  this.code = code;
@@ -7909,13 +8057,13 @@ function reserveAlias(store, alias) {
7909
8057
  pending = /* @__PURE__ */ new Set();
7910
8058
  pendingAliases.set(store, pending);
7911
8059
  }
7912
- if (pending.has(alias) || store.hasPendingRegistration(alias) || store.readActivation(alias) || store.readAgentConfig(alias)) throw new ServeIdentityError("agent_exists", `agent "${alias}" already exists in the serve store`);
8060
+ if (pending.has(alias) || store.hasPendingRegistration(alias) || store.readActivation(alias) || store.readAgentConfig(alias)) throw new AgentServerIdentityError("agent_exists", `agent "${alias}" already exists in the agent server store`);
7913
8061
  pending.add(alias);
7914
8062
  return () => pending.delete(alias);
7915
8063
  }
7916
8064
  async function createManagedAgent(store, secrets, input, connectAgent = connect) {
7917
8065
  const alias = assertStoreName("agent name", input.name);
7918
- if (!input.enrollmentToken.trim()) throw new ServeIdentityError("enrollment_required", "an enrollment token from the target team is required — a self-registered agent would be stranded in its own personal team");
8066
+ if (!input.enrollmentToken.trim()) throw new AgentServerIdentityError("enrollment_required", "an enrollment token from the target team is required — a self-registered agent would be stranded in its own personal team");
7919
8067
  const releaseAlias = reserveAlias(store, alias);
7920
8068
  let registeredIdentityId;
7921
8069
  try {
@@ -7923,7 +8071,7 @@ async function createManagedAgent(store, secrets, input, connectAgent = connect)
7923
8071
  try {
7924
8072
  apiUrl = requireSecureCredentialApiUrl(input.apiUrl);
7925
8073
  } catch (cause) {
7926
- throw new ServeIdentityError("registration_failed", "registration API URL must use HTTPS or HTTP loopback", { cause });
8074
+ throw new AgentServerIdentityError("registration_failed", "registration API URL must use HTTPS or HTTP loopback", { cause });
7927
8075
  }
7928
8076
  store.reserveRegistration(alias, apiUrl);
7929
8077
  const result = await register({
@@ -7932,7 +8080,7 @@ async function createManagedAgent(store, secrets, input, connectAgent = connect)
7932
8080
  enrollmentToken: input.enrollmentToken,
7933
8081
  signal: boundedIdentitySignal(input.signal)
7934
8082
  });
7935
- if (result.credentials.type !== "agent_key") throw new ServeIdentityError("unsupported_credential", `registration returned credential type "${result.credentials.type}"; serve manages agent-key credentials only`);
8083
+ if (result.credentials.type !== "agent_key") throw new AgentServerIdentityError("unsupported_credential", `registration returned credential type "${result.credentials.type}"; agent server manages agent-key credentials only`);
7936
8084
  const now = (/* @__PURE__ */ new Date()).toISOString();
7937
8085
  const { identityId, fingerprint, publicKey, privateKey } = result.identity;
7938
8086
  registeredIdentityId = identityId;
@@ -7990,9 +8138,9 @@ async function createManagedAgent(store, secrets, input, connectAgent = connect)
7990
8138
  } catch (cause) {
7991
8139
  if (!registeredIdentityId && cause instanceof MoltNetError && cause.statusCode !== void 0 && cause.statusCode >= 400 && cause.statusCode < 500) {
7992
8140
  store.clearPendingRegistration(alias);
7993
- throw new ServeIdentityError("registration_failed", `registration for "${alias}" was rejected`, { cause });
8141
+ throw new AgentServerIdentityError("registration_failed", `registration for "${alias}" was rejected`, { cause });
7994
8142
  }
7995
- if (store.hasPendingRegistration(alias)) throw new ServeIdentityError("registration_incomplete", registeredIdentityId ? `identity "${registeredIdentityId}" was registered but local activation is incomplete; reconcile or clear its pending serve record before retrying` : `registration for "${alias}" may be incomplete; inspect the remote API before changing its pending serve record`, { cause });
8143
+ if (store.hasPendingRegistration(alias)) throw new AgentServerIdentityError("registration_incomplete", registeredIdentityId ? `identity "${registeredIdentityId}" was registered but local activation is incomplete; reconcile or clear its pending Agent Server record before retrying` : `registration for "${alias}" may be incomplete; inspect the remote API before changing its pending Agent Server record`, { cause });
7996
8144
  throw cause;
7997
8145
  } finally {
7998
8146
  releaseAlias();
@@ -8004,22 +8152,22 @@ async function reconcileManagedRegistration(store, secrets, aliasInput, action,
8004
8152
  const activation = store.readActivation(alias);
8005
8153
  if (activation) {
8006
8154
  const config = store.readAgentConfig(alias);
8007
- if (!config) throw new ServeIdentityError("registration_incomplete", `managed activation for "${alias}" is missing its canonical config`);
8155
+ if (!config) throw new AgentServerIdentityError("registration_incomplete", `managed activation for "${alias}" is missing its canonical config`);
8008
8156
  return {
8009
8157
  activation,
8010
8158
  config
8011
8159
  };
8012
8160
  }
8013
- if (!store.hasPendingRegistration(alias)) throw new ServeIdentityError("config_not_found", `agent "${alias}" has no pending registration to reconcile`);
8161
+ if (!store.hasPendingRegistration(alias)) throw new AgentServerIdentityError("config_not_found", `agent "${alias}" has no pending registration to reconcile`);
8014
8162
  const config = store.readAgentConfig(alias);
8015
8163
  if (action === "abandon") {
8016
8164
  store.removeAgentConfig(alias);
8017
8165
  store.clearPendingRegistration(alias);
8018
8166
  return null;
8019
8167
  }
8020
- if (!config?.agent_key_ref || config.agent_key_ref.provider !== FILE_SECRET_PROVIDER || config.keys.private_key_ref?.provider !== FILE_SECRET_PROVIDER) throw new ServeIdentityError("registration_incomplete", `pending registration for "${alias}" does not have complete managed references`);
8168
+ if (!config?.agent_key_ref || config.agent_key_ref.provider !== FILE_SECRET_PROVIDER || config.keys.private_key_ref?.provider !== FILE_SECRET_PROVIDER) throw new AgentServerIdentityError("registration_incomplete", `pending registration for "${alias}" does not have complete managed references`);
8021
8169
  const [agentKey, privateKeyState] = await Promise.all([secrets.read(config.agent_key_ref.key), secrets.probe(config.keys.private_key_ref.key)]);
8022
- if (!agentKey || privateKeyState !== "present") throw new ServeIdentityError("registration_incomplete", `pending registration for "${alias}" is missing persisted secret material`);
8170
+ if (!agentKey || privateKeyState !== "present") throw new AgentServerIdentityError("registration_incomplete", `pending registration for "${alias}" is missing persisted secret material`);
8023
8171
  const identity = identityFromConfig(config);
8024
8172
  const apiUrl = requireConfigApiUrl(config, store.agentPath(alias));
8025
8173
  const whoami = await callWhoami(connectAgent, {
@@ -8047,7 +8195,7 @@ async function attachExternalAgent(store, secretProviders, input, connectAgent =
8047
8195
  const alias = assertStoreName("agent name", input.name);
8048
8196
  const releaseAlias = reserveAlias(store, alias);
8049
8197
  try {
8050
- if (!isAbsolute(input.configDir)) throw new ServeIdentityError("config_not_found", "external configDir must be an absolute path");
8198
+ if (!isAbsolute(input.configDir)) throw new AgentServerIdentityError("config_not_found", "external configDir must be an absolute path");
8051
8199
  const configPath = join(input.configDir, "moltnet.json");
8052
8200
  externalAgentLocation(configPath);
8053
8201
  const config = await readCurrentConfig(configPath);
@@ -8083,7 +8231,7 @@ async function verifyAgentActivation(store, alias, managedSecretProviders, exter
8083
8231
  const verified = activation.source === "managed" ? await verifyManagedActivation(store, activation, managedSecretProviders, connectAgent, signal) : await verifyExternalActivation(activation, externalSecretProviders, connectAgent, signal);
8084
8232
  assertIdentityMatches(verified.whoami, activation, "authenticated whoami", `agent "${activation.alias}" pinned activation`);
8085
8233
  const boundTeamId = boundTeamIdFromWhoami(verified.whoami);
8086
- if (activation.boundTeamId !== boundTeamId) throw new ServeIdentityError("verification_failed", `authenticated whoami team binding does not match agent "${activation.alias}" pinned activation`);
8234
+ if (activation.boundTeamId !== boundTeamId) throw new AgentServerIdentityError("verification_failed", `authenticated whoami team binding does not match agent "${activation.alias}" pinned activation`);
8087
8235
  return {
8088
8236
  activation,
8089
8237
  config: verified.config,
@@ -8100,7 +8248,7 @@ async function verifyManagedActivation(store, activation, secretProviders, conne
8100
8248
  } catch (cause) {
8101
8249
  throw verificationError(`could not resolve the managed agent key for "${activation.alias}"`, cause);
8102
8250
  }
8103
- if (!agentKey) throw new ServeIdentityError("verification_failed", `managed config for "${activation.alias}" has no agent_key_ref`);
8251
+ if (!agentKey) throw new AgentServerIdentityError("verification_failed", `managed config for "${activation.alias}" has no agent_key_ref`);
8104
8252
  return {
8105
8253
  config,
8106
8254
  whoami: await callWhoami(connectAgent, {
@@ -8123,16 +8271,16 @@ async function verifyExternalActivation(activation, secretProviders, connectAgen
8123
8271
  /** Never let request or activation metadata redirect persisted credentials. */
8124
8272
  function requireTrustedApiOverride(override, configApiUrl, configPath) {
8125
8273
  if (!override) return configApiUrl;
8126
- if (override !== configApiUrl) throw new ServeIdentityError("verification_failed", `API override for ${configPath} does not match its configured endpoint`);
8274
+ if (override !== configApiUrl) throw new AgentServerIdentityError("verification_failed", `API override for ${configPath} does not match its configured endpoint`);
8127
8275
  return configApiUrl;
8128
8276
  }
8129
8277
  function assertActivatedConfig(config, activation, configPath, currentApiUrl, pinnedApiUrl) {
8130
- if (currentApiUrl !== pinnedApiUrl) throw new ServeIdentityError("verification_failed", `agent config at ${configPath} API endpoint does not match its pinned activation`);
8278
+ if (currentApiUrl !== pinnedApiUrl) throw new AgentServerIdentityError("verification_failed", `agent config at ${configPath} API endpoint does not match its pinned activation`);
8131
8279
  assertIdentityMatches(identityFromConfig(config), activation, configPath, `agent "${activation.alias}" pinned activation`);
8132
8280
  }
8133
8281
  function requireConfigApiUrl(config, configPath) {
8134
8282
  const apiUrl = config?.endpoints?.api?.trim();
8135
- if (!apiUrl) throw new ServeIdentityError("verification_failed", `agent config at ${configPath} is missing endpoints.api`);
8283
+ if (!apiUrl) throw new AgentServerIdentityError("verification_failed", `agent config at ${configPath} is missing endpoints.api`);
8136
8284
  return apiUrl;
8137
8285
  }
8138
8286
  function requireTrustedConfigApiUrl(config, configPath) {
@@ -8149,14 +8297,14 @@ async function readCurrentConfig(configPath) {
8149
8297
  try {
8150
8298
  file = await open(configPath, "r");
8151
8299
  } catch (cause) {
8152
- throw new ServeIdentityError("config_not_found", `agent config is missing at ${configPath}`, { cause });
8300
+ throw new AgentServerIdentityError("config_not_found", `agent config is missing at ${configPath}`, { cause });
8153
8301
  }
8154
8302
  let raw;
8155
8303
  try {
8156
- if (!(await file.stat()).isFile()) throw new ServeIdentityError("verification_failed", `agent config at ${configPath} must be a regular file no larger than ${MAX_CONFIG_BYTES} bytes`);
8304
+ if (!(await file.stat()).isFile()) throw new AgentServerIdentityError("verification_failed", `agent config at ${configPath} must be a regular file no larger than ${MAX_CONFIG_BYTES} bytes`);
8157
8305
  const buffer = Buffer.alloc(MAX_CONFIG_BYTES + 1);
8158
8306
  const { bytesRead } = await file.read(buffer, 0, buffer.length, 0);
8159
- if (bytesRead > MAX_CONFIG_BYTES) throw new ServeIdentityError("verification_failed", `agent config at ${configPath} must be a regular file no larger than ${MAX_CONFIG_BYTES} bytes`);
8307
+ if (bytesRead > MAX_CONFIG_BYTES) throw new AgentServerIdentityError("verification_failed", `agent config at ${configPath} must be a regular file no larger than ${MAX_CONFIG_BYTES} bytes`);
8160
8308
  raw = buffer.toString("utf8", 0, bytesRead);
8161
8309
  } finally {
8162
8310
  await file.close();
@@ -8171,7 +8319,7 @@ async function readCurrentConfig(configPath) {
8171
8319
  function externalAgentLocation(configPath) {
8172
8320
  const configDir = dirname(configPath);
8173
8321
  const moltnetDir = dirname(configDir);
8174
- if (!isAbsolute(configPath) || basename(configPath) !== "moltnet.json" || basename(moltnetDir) !== ".moltnet") throw new ServeIdentityError("config_not_found", `external config must be at an absolute <agent-root>/.moltnet/<agent>/moltnet.json path`);
8322
+ if (!isAbsolute(configPath) || basename(configPath) !== "moltnet.json" || basename(moltnetDir) !== ".moltnet") throw new AgentServerIdentityError("config_not_found", `external config must be at an absolute <agent-root>/.moltnet/<agent>/moltnet.json path`);
8175
8323
  return {
8176
8324
  agentName: assertStoreName("external config agent name", basename(configDir)),
8177
8325
  agentRoot: dirname(moltnetDir)
@@ -8203,7 +8351,7 @@ function identityFromConfig(config) {
8203
8351
  const identityId = config?.identity_id?.trim();
8204
8352
  const publicKey = config?.keys?.public_key?.trim();
8205
8353
  const fingerprint = config?.keys?.fingerprint?.trim();
8206
- if (!identityId || !publicKey || !fingerprint) throw new ServeIdentityError("verification_failed", "agent config is missing canonical identity_id, keys.public_key, or keys.fingerprint");
8354
+ if (!identityId || !publicKey || !fingerprint) throw new AgentServerIdentityError("verification_failed", "agent config is missing canonical identity_id, keys.public_key, or keys.fingerprint");
8207
8355
  return {
8208
8356
  identityId,
8209
8357
  publicKey,
@@ -8212,10 +8360,10 @@ function identityFromConfig(config) {
8212
8360
  }
8213
8361
  function assertIdentityMatches(current, expected, currentLabel, expectedLabel) {
8214
8362
  const assessment = assessIdentityPin(current, expected);
8215
- if (!assessment.ok) throw new ServeIdentityError("verification_failed", `${currentLabel} ${assessment.label} does not match ${expectedLabel}`);
8363
+ if (!assessment.ok) throw new AgentServerIdentityError("verification_failed", `${currentLabel} ${assessment.label} does not match ${expectedLabel}`);
8216
8364
  }
8217
8365
  function verificationError(message, cause) {
8218
- return new ServeIdentityError("verification_failed", message, { cause });
8366
+ return new AgentServerIdentityError("verification_failed", message, { cause });
8219
8367
  }
8220
8368
  /** Non-secret projection preserving the existing `/v1` response shape. */
8221
8369
  function publicAgentView(store, activation) {
@@ -8249,11 +8397,11 @@ function boundTeamIdFromWhoami(whoami) {
8249
8397
  }
8250
8398
  function requireActivation(store, alias) {
8251
8399
  const activation = store.readActivation(alias);
8252
- if (!activation) throw new ServeStoreError("not_found", `agent "${alias}" is not configured`);
8400
+ if (!activation) throw new AgentServerStoreError("not_found", `agent "${alias}" is not configured`);
8253
8401
  return activation;
8254
8402
  }
8255
8403
  //#endregion
8256
- //#region src/lib/serve/runs.ts
8404
+ //#region src/lib/agent-server/runs.ts
8257
8405
  var STOP_GRACE_MS = 1e4;
8258
8406
  var STOP_FORCE_MS = 2e3;
8259
8407
  var DEFAULT_MAX_LOG_BYTES = 10 * 1024 * 1024;
@@ -8292,19 +8440,19 @@ var INHERITED_MOLTNET_ENV_NAMES = new Set([
8292
8440
  "MOLTNET_SIGNER_URL",
8293
8441
  "MOLTNET_TRACE_IDLE_POLLING"
8294
8442
  ]);
8295
- var ServeRunError = class extends Error {
8296
- name = "ServeRunError";
8443
+ var AgentServerRunError = class extends Error {
8444
+ name = "AgentServerRunError";
8297
8445
  constructor(code, message) {
8298
8446
  super(message);
8299
8447
  this.code = code;
8300
8448
  }
8301
8449
  };
8302
8450
  function validateRunSpec(spec) {
8303
- if (spec.mode !== "poll" && spec.mode !== "drain") throw new ServeRunError("invalid_spec", "mode must be poll or drain");
8304
- if (!spec.agent) throw new ServeRunError("invalid_spec", "agent is required");
8305
- if (!spec.teamId) throw new ServeRunError("invalid_spec", "teamId is required");
8306
- if (!Array.isArray(spec.profiles) || spec.profiles.length === 0) throw new ServeRunError("invalid_spec", "at least one profile is required");
8307
- if (!Array.isArray(spec.taskTypes) || spec.taskTypes.length === 0) throw new ServeRunError("invalid_spec", "at least one task type is required");
8451
+ if (spec.mode !== "poll" && spec.mode !== "drain") throw new AgentServerRunError("invalid_spec", "mode must be poll or drain");
8452
+ if (!spec.agent) throw new AgentServerRunError("invalid_spec", "agent is required");
8453
+ if (!spec.teamId) throw new AgentServerRunError("invalid_spec", "teamId is required");
8454
+ if (!Array.isArray(spec.profiles) || spec.profiles.length === 0) throw new AgentServerRunError("invalid_spec", "at least one profile is required");
8455
+ if (!Array.isArray(spec.taskTypes) || spec.taskTypes.length === 0) throw new AgentServerRunError("invalid_spec", "at least one task type is required");
8308
8456
  }
8309
8457
  var RunManager = class {
8310
8458
  active = /* @__PURE__ */ new Map();
@@ -8362,7 +8510,7 @@ var RunManager = class {
8362
8510
  ...target.extraArgs
8363
8511
  ];
8364
8512
  if (activation.source === "managed") {
8365
- if (!config.agent_key_ref || !config.keys.private_key_ref) throw new ServeRunError("invalid_spec", `managed config for "${activation.alias}" is missing canonical secret references`);
8513
+ if (!config.agent_key_ref || !config.keys.private_key_ref) throw new AgentServerRunError("invalid_spec", `managed config for "${activation.alias}" is missing canonical secret references`);
8366
8514
  env["MOLTNET_API_URL"] = activation.apiUrl;
8367
8515
  env["MOLTNET_AGENT_KEY_REF"] = formatSecretReferenceString(config.agent_key_ref);
8368
8516
  env["MOLTNET_PRIVATE_KEY_REF"] = formatSecretReferenceString(config.keys.private_key_ref);
@@ -8380,7 +8528,7 @@ var RunManager = class {
8380
8528
  }
8381
8529
  if (!agentKey && config.keys.private_key_ref) env["MOLTNET_PRIVATE_KEY"] = await resolveIdentitySeed(config, this.options.externalSecretProviders);
8382
8530
  } catch {
8383
- throw new ServeRunError("invalid_spec", `external credentials for "${activation.alias}" could not be projected`);
8531
+ throw new AgentServerRunError("invalid_spec", `external credentials for "${activation.alias}" could not be projected`);
8384
8532
  }
8385
8533
  }
8386
8534
  env["MOLTNET_EXPECTED_IDENTITY_ID"] = activation.identityId;
@@ -8393,7 +8541,7 @@ var RunManager = class {
8393
8541
  try {
8394
8542
  value = await this.options.secretProviders.resolve(parseSecretReferenceString(provider.apiKeyRef));
8395
8543
  } catch {
8396
- throw new ServeRunError("invalid_spec", `provider "${providerId}" API key could not be resolved`);
8544
+ throw new AgentServerRunError("invalid_spec", `provider "${providerId}" API key could not be resolved`);
8397
8545
  }
8398
8546
  env[provider.envName] = value;
8399
8547
  }
@@ -8416,7 +8564,7 @@ var RunManager = class {
8416
8564
  this.assertStartOpen(signal);
8417
8565
  const agent = await (this.options.verifyActivationImpl ?? verifyAgentActivation)(this.store, spec.agent, this.options.secretProviders, this.options.externalSecretProviders, void 0, signal);
8418
8566
  this.assertStartOpen(signal);
8419
- if (agent.boundTeamId && agent.boundTeamId !== spec.teamId) throw new ServeRunError("invalid_spec", `agent "${spec.agent}" has a key bound to team ${agent.boundTeamId}; start the run with that team, or create a new agent with an enrollment token from team ${spec.teamId}`);
8567
+ if (agent.boundTeamId && agent.boundTeamId !== spec.teamId) throw new AgentServerRunError("invalid_spec", `agent "${spec.agent}" has a key bound to team ${agent.boundTeamId}; start the run with that team, or create a new agent with an enrollment token from team ${spec.teamId}`);
8420
8568
  const id = `${Date.now().toString(36)}-${randomBytes(4).toString("hex")}`;
8421
8569
  const runDir = this.store.runDir(id);
8422
8570
  const piDir = join(runDir, "pi");
@@ -8449,20 +8597,20 @@ var RunManager = class {
8449
8597
  try {
8450
8598
  (this.options.symlinkImpl ?? symlinkSync)(this.store.piAuthJsonPath, join(piDir, "auth.json"));
8451
8599
  } catch (cause) {
8452
- throw new ServeStoreError("io_error", "could not link subscription credentials into the run", { cause });
8600
+ throw new AgentServerStoreError("io_error", "could not link subscription credentials into the run", { cause });
8453
8601
  }
8454
8602
  const entry = this.entrypoint();
8455
8603
  logStream = createWriteStream(logPath, {
8456
8604
  fd: openSync(logPath, "a", 384),
8457
8605
  autoClose: true
8458
8606
  });
8459
- logLimiter = createByteLimitTransform(this.options.maxLogBytes ?? DEFAULT_MAX_LOG_BYTES, () => this.log("warn", "serve run log output truncated", {
8607
+ logLimiter = createByteLimitTransform(this.options.maxLogBytes ?? DEFAULT_MAX_LOG_BYTES, () => this.log("warn", "agent server run log output truncated", {
8460
8608
  ...runContext(id, spec.agent, child),
8461
8609
  transition: "log_truncated"
8462
8610
  }));
8463
8611
  logLimiter.pipe(logStream);
8464
8612
  logStream.on("error", (error) => {
8465
- this.log("error", "serve run log stream failed", {
8613
+ this.log("error", "agent server run log stream failed", {
8466
8614
  ...runContext(id, spec.agent, child),
8467
8615
  transition: "log_failed",
8468
8616
  ...safeRunError(error)
@@ -8508,7 +8656,7 @@ var RunManager = class {
8508
8656
  this.active.delete(id);
8509
8657
  logLimiter?.end();
8510
8658
  const status = activeRun?.stopRequested ? "stopped" : code === 0 ? "exited" : "failed";
8511
- this.log(status === "failed" ? "error" : "info", "serve run exited", {
8659
+ this.log(status === "failed" ? "error" : "info", "agent server run exited", {
8512
8660
  ...runContext(id, spec.agent, child),
8513
8661
  transition: status,
8514
8662
  exitCode: code,
@@ -8522,7 +8670,7 @@ var RunManager = class {
8522
8670
  child.once("error", (error) => {
8523
8671
  this.active.delete(id);
8524
8672
  logLimiter?.end();
8525
- this.log("error", "serve run child process failed", {
8673
+ this.log("error", "agent server run child process failed", {
8526
8674
  ...runContext(id, spec.agent, child),
8527
8675
  transition: "spawn_failed",
8528
8676
  ...safeRunError(error)
@@ -8530,7 +8678,7 @@ var RunManager = class {
8530
8678
  this.persistRunCompletion(id, spec.agent, { status: "failed" });
8531
8679
  });
8532
8680
  this.store.writeRun(record);
8533
- this.log("info", "serve run started", {
8681
+ this.log("info", "agent server run started", {
8534
8682
  ...runContext(id, spec.agent, child),
8535
8683
  transition: "running"
8536
8684
  });
@@ -8544,7 +8692,7 @@ var RunManager = class {
8544
8692
  recursive: true,
8545
8693
  force: true
8546
8694
  });
8547
- this.log("error", "serve run failed to start", {
8695
+ this.log("error", "agent server run failed to start", {
8548
8696
  ...runContext(id, spec.agent, child),
8549
8697
  transition: "start_failed",
8550
8698
  ...safeRunError(cause)
@@ -8554,11 +8702,11 @@ var RunManager = class {
8554
8702
  }
8555
8703
  stop(id) {
8556
8704
  const record = this.store.readRun(id);
8557
- if (!record) throw new ServeRunError("run_not_found", `run "${id}" was not found`);
8705
+ if (!record) throw new AgentServerRunError("run_not_found", `run "${id}" was not found`);
8558
8706
  const activeRun = this.active.get(id);
8559
- if (!activeRun) throw new ServeRunError("run_not_active", `run "${id}" is not running`);
8707
+ if (!activeRun) throw new AgentServerRunError("run_not_active", `run "${id}" is not running`);
8560
8708
  activeRun.stopRequested = true;
8561
- this.log("info", "serve run stop requested", {
8709
+ this.log("info", "agent server run stop requested", {
8562
8710
  ...runContext(id, record.agent, activeRun.child),
8563
8711
  transition: "stopping",
8564
8712
  signal: "SIGTERM"
@@ -8572,7 +8720,7 @@ var RunManager = class {
8572
8720
  /** Live status merged over the persisted record. */
8573
8721
  status(id) {
8574
8722
  const record = this.store.readRun(id);
8575
- if (!record) throw new ServeStoreError("not_found", `run "${id}" was not found`);
8723
+ if (!record) throw new AgentServerStoreError("not_found", `run "${id}" was not found`);
8576
8724
  return record;
8577
8725
  }
8578
8726
  list(limit = Number.POSITIVE_INFINITY) {
@@ -8620,7 +8768,7 @@ var RunManager = class {
8620
8768
  this.closing = true;
8621
8769
  for (const [id, activeRun] of this.active) {
8622
8770
  activeRun.stopRequested = true;
8623
- this.log("warn", "serve run force stop requested", {
8771
+ this.log("warn", "agent server run force stop requested", {
8624
8772
  ...runContext(id, this.store.readRun(id)?.agent ?? "unknown", activeRun.child),
8625
8773
  transition: "force_stopping",
8626
8774
  signal: "SIGKILL"
@@ -8638,7 +8786,7 @@ var RunManager = class {
8638
8786
  exitCode: null,
8639
8787
  endedAt
8640
8788
  });
8641
- this.log("warn", "serve run interrupted by supervisor replacement", {
8789
+ this.log("warn", "agent server run interrupted by supervisor replacement", {
8642
8790
  runId: record.id,
8643
8791
  agent: record.agent,
8644
8792
  pid: record.pid,
@@ -8647,10 +8795,10 @@ var RunManager = class {
8647
8795
  }
8648
8796
  }
8649
8797
  reserveStart(agent) {
8650
- if (this.closing) throw new ServeRunError("invalid_spec", "serve is shutting down");
8798
+ if (this.closing) throw new AgentServerRunError("invalid_spec", "agent server is shutting down");
8651
8799
  const agentStarting = this.startingByAgent.get(agent) ?? 0;
8652
8800
  const agentActive = [...this.active.values()].filter((run) => run.agent === agent).length;
8653
- if (this.active.size + this.starting >= (this.options.maxActiveRuns ?? DEFAULT_MAX_ACTIVE_RUNS) || agentActive + agentStarting >= (this.options.maxActiveRunsPerAgent ?? DEFAULT_MAX_ACTIVE_RUNS_PER_AGENT)) throw new ServeRunError("invalid_spec", `active run limit reached for agent "${agent}"`);
8801
+ if (this.active.size + this.starting >= (this.options.maxActiveRuns ?? DEFAULT_MAX_ACTIVE_RUNS) || agentActive + agentStarting >= (this.options.maxActiveRunsPerAgent ?? DEFAULT_MAX_ACTIVE_RUNS_PER_AGENT)) throw new AgentServerRunError("invalid_spec", `active run limit reached for agent "${agent}"`);
8654
8802
  this.starting += 1;
8655
8803
  this.startingByAgent.set(agent, agentStarting + 1);
8656
8804
  return () => {
@@ -8661,7 +8809,7 @@ var RunManager = class {
8661
8809
  };
8662
8810
  }
8663
8811
  assertStartOpen(signal) {
8664
- if (this.closing || signal?.aborted) throw new ServeRunError("invalid_spec", "serve is shutting down");
8812
+ if (this.closing || signal?.aborted) throw new AgentServerRunError("invalid_spec", "agent server is shutting down");
8665
8813
  }
8666
8814
  pruneCompletedRuns() {
8667
8815
  const removed = this.store.pruneCompletedRuns({
@@ -8670,7 +8818,7 @@ var RunManager = class {
8670
8818
  maxBytes: this.options.maxRunStorageBytes ?? DEFAULT_MAX_RUN_STORAGE_BYTES,
8671
8819
  now: this.options.now?.() ?? /* @__PURE__ */ new Date()
8672
8820
  });
8673
- if (removed.length > 0) this.log("info", "serve run retention removed completed artifacts", {
8821
+ if (removed.length > 0) this.log("info", "agent server run retention removed completed artifacts", {
8674
8822
  transition: "pruned",
8675
8823
  removedCount: removed.length
8676
8824
  });
@@ -8685,7 +8833,7 @@ var RunManager = class {
8685
8833
  });
8686
8834
  this.pruneCompletedRuns();
8687
8835
  } catch (error) {
8688
- this.log("error", "serve run completion persistence failed", {
8836
+ this.log("error", "agent server run completion persistence failed", {
8689
8837
  runId: id,
8690
8838
  agent,
8691
8839
  transition: "persistence_failed",
@@ -8749,64 +8897,8 @@ async function terminateChild(child) {
8749
8897
  child.kill("SIGKILL");
8750
8898
  });
8751
8899
  }
8752
- //#endregion
8753
- //#region src/lib/serve/serve-lock.ts
8754
- var ServeLockError = class extends Error {
8755
- name = "ServeLockError";
8756
- constructor(code, message, options) {
8757
- super(message, options);
8758
- this.code = code;
8759
- }
8760
- };
8761
- /**
8762
- * Acquire the per-root singleton lock. `proper-lockfile` uses an atomic lock
8763
- * directory at exactly `<root>/serve.lock`, recovers stale owners, and keeps
8764
- * the mtime fresh while the supervisor is alive.
8765
- */
8766
- async function acquireServeLock(root, options = {}) {
8767
- const path = join(root, "serve.lock");
8768
- let releaseLock;
8769
- try {
8770
- releaseLock = await lock(root, {
8771
- lockfilePath: path,
8772
- realpath: false,
8773
- retries: 0,
8774
- ...options.staleMs === void 0 ? {} : { stale: options.staleMs },
8775
- ...options.updateMs === void 0 ? {} : { update: options.updateMs },
8776
- onCompromised: (cause) => {
8777
- const error = new ServeLockError("compromised", `serve lock ${path} was compromised: ${cause.message}`, { cause });
8778
- if (options.onCompromised) {
8779
- options.onCompromised(error);
8780
- return;
8781
- }
8782
- throw error;
8783
- }
8784
- });
8785
- } catch (cause) {
8786
- if (cause.code === "ELOCKED") throw new ServeLockError("held", `another moltnet-agent serve process already owns ${path}`, { cause });
8787
- throw new ServeLockError("failed", `could not acquire serve lock ${path}: ${cause.message}`, { cause });
8788
- }
8789
- let released = false;
8790
- return {
8791
- path,
8792
- async release() {
8793
- if (released) return;
8794
- released = true;
8795
- await releaseLock();
8796
- }
8797
- };
8798
- }
8799
- /** Always release after normal completion or startup/runtime failure. */
8800
- async function withServeLock(root, work, options) {
8801
- const held = await acquireServeLock(root, options);
8802
- try {
8803
- return await work();
8804
- } finally {
8805
- await held.release();
8806
- }
8807
- }
8808
- var ServeModelDiscoveryError = class extends Error {
8809
- name = "ServeModelDiscoveryError";
8900
+ var AgentServerModelDiscoveryError = class extends Error {
8901
+ name = "AgentServerModelDiscoveryError";
8810
8902
  constructor(code, message, statusCode, options) {
8811
8903
  super(message, options);
8812
8904
  this.code = code;
@@ -8847,32 +8939,352 @@ function parseProviderBaseUrl(value, providerId) {
8847
8939
  try {
8848
8940
  parsed = new URL(value);
8849
8941
  } catch (cause) {
8850
- throw new ServeModelDiscoveryError("invalid_provider", `provider "${providerId}" has an invalid base URL`, 400, { cause });
8942
+ throw new AgentServerModelDiscoveryError("invalid_provider", `provider "${providerId}" has an invalid base URL`, 400, { cause });
8851
8943
  }
8852
- if (parsed.protocol !== "http:" && parsed.protocol !== "https:" || parsed.username || parsed.password || parsed.search || parsed.hash) throw new ServeModelDiscoveryError("invalid_provider", `provider "${providerId}" base URL must be HTTP(S) without credentials, query, or fragment`, 400);
8944
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:" || parsed.username || parsed.password || parsed.search || parsed.hash) throw new AgentServerModelDiscoveryError("invalid_provider", `provider "${providerId}" base URL must be HTTP(S) without credentials, query, or fragment`, 400);
8945
+ if (isNonLoopbackPrivateAddress(parsed.hostname)) throw new AgentServerModelDiscoveryError("invalid_provider", `provider "${providerId}" base URL must not target a private network address`, 400);
8853
8946
  return parsed;
8854
8947
  }
8948
+ function isNonLoopbackPrivateAddress(hostname) {
8949
+ if (isIP(hostname) !== 4) return false;
8950
+ const [first, second] = hostname.split(".").map(Number);
8951
+ if (first === 127) return false;
8952
+ return first === 10 || first === 172 && second >= 16 && second <= 31 || first === 192 && second === 168 || first === 169 && second === 254;
8953
+ }
8855
8954
  function discoveryFailure(providerId, failures) {
8856
- if (failures.some((failure) => failure.kind === "http" && (failure.status === 401 || failure.status === 403))) return new ServeModelDiscoveryError("discovery_unauthorized", `provider "${providerId}" rejected model discovery; check its API key`, 502);
8857
- if (failures.some((failure) => failure.kind === "network")) return new ServeModelDiscoveryError("discovery_unavailable", `provider "${providerId}" could not be reached for model discovery`, 502);
8858
- if (failures.some((failure) => failure.kind === "invalid_response")) return new ServeModelDiscoveryError("discovery_invalid_response", `provider "${providerId}" returned an invalid model response`, 502);
8859
- return new ServeModelDiscoveryError("discovery_failed", `no models discovered for provider "${providerId}"`, 502);
8955
+ if (failures.some((failure) => failure.kind === "http" && (failure.status === 401 || failure.status === 403))) return new AgentServerModelDiscoveryError("discovery_unauthorized", `provider "${providerId}" rejected model discovery; check its API key`, 502);
8956
+ if (failures.some((failure) => failure.kind === "network")) return new AgentServerModelDiscoveryError("discovery_unavailable", `provider "${providerId}" could not be reached for model discovery`, 502);
8957
+ if (failures.some((failure) => failure.kind === "invalid_response")) return new AgentServerModelDiscoveryError("discovery_invalid_response", `provider "${providerId}" returned an invalid model response`, 502);
8958
+ return new AgentServerModelDiscoveryError("discovery_failed", `no models discovered for provider "${providerId}"`, 502);
8860
8959
  }
8861
8960
  function isRecord(value) {
8862
8961
  return typeof value === "object" && value !== null && !Array.isArray(value);
8863
8962
  }
8864
8963
  //#endregion
8865
- //#region src/lib/serve/server.ts
8964
+ //#region src/lib/agent-server/protocol.ts
8965
+ var DateTime = Type.String({ format: "date-time" });
8966
+ var StringList = Type.Array(Type.String());
8967
+ function schemaRef(schema) {
8968
+ const id = schema.$id;
8969
+ if (typeof id !== "string" || id.length === 0) throw new Error("Agent Server protocol schemas must have an identifier");
8970
+ return Type.Ref(id);
8971
+ }
8972
+ var AgentServerHealthSchema = Type.Object({ status: Type.Literal("ok") }, { $id: "AgentServerHealth" });
8973
+ var AgentServerProblemSchema = Type.Object({
8974
+ code: Type.String(),
8975
+ message: Type.String()
8976
+ }, { $id: "AgentServerProblem" });
8977
+ var AgentServerAgentSchema = Type.Object({
8978
+ kind: Type.Union([Type.Literal("managed"), Type.Literal("external")]),
8979
+ agentName: Type.String(),
8980
+ identityId: Type.Optional(Type.String()),
8981
+ fingerprint: Type.Optional(Type.String()),
8982
+ apiUrl: Type.Optional(Type.String()),
8983
+ teamId: Type.Optional(Type.String()),
8984
+ configDir: Type.Optional(Type.String()),
8985
+ createdAt: DateTime,
8986
+ hasAgentKey: Type.Optional(Type.Boolean()),
8987
+ hasPrivateKey: Type.Optional(Type.Boolean())
8988
+ }, { $id: "AgentServerAgent" });
8989
+ var AgentServerProviderSchema = Type.Object({
8990
+ api: Type.String(),
8991
+ baseUrl: Type.String({ format: "uri" }),
8992
+ envName: Type.String(),
8993
+ models: StringList,
8994
+ hasApiKey: Type.Boolean()
8995
+ }, { $id: "AgentServerProvider" });
8996
+ var AgentServerRunRecordSchema = Type.Object({
8997
+ id: Type.String(),
8998
+ agent: Type.String(),
8999
+ teamId: Type.String(),
9000
+ profiles: StringList,
9001
+ taskTypes: StringList,
9002
+ mode: Type.Union([Type.Literal("poll"), Type.Literal("drain")]),
9003
+ status: Type.Union([
9004
+ Type.Literal("running"),
9005
+ Type.Literal("exited"),
9006
+ Type.Literal("stopped"),
9007
+ Type.Literal("failed")
9008
+ ]),
9009
+ pid: Type.Optional(Type.Number()),
9010
+ exitCode: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
9011
+ startedAt: DateTime,
9012
+ endedAt: Type.Optional(DateTime)
9013
+ }, { $id: "AgentServerRunRecord" });
9014
+ var AgentServerRunSchema = Type.Intersect([schemaRef(AgentServerRunRecordSchema), Type.Object({ active: Type.Boolean() })], { $id: "AgentServerRun" });
9015
+ var AgentServerSubscriptionSchema = Type.Object({
9016
+ id: Type.String(),
9017
+ name: Type.String(),
9018
+ connected: Type.Boolean()
9019
+ }, { $id: "AgentServerSubscription" });
9020
+ var AgentServerSubscriptionLoginSchema = Type.Object({
9021
+ providerId: Type.String(),
9022
+ status: Type.Union([
9023
+ Type.Literal("pending"),
9024
+ Type.Literal("completed"),
9025
+ Type.Literal("failed")
9026
+ ]),
9027
+ authUrl: Type.Optional(Type.String({ format: "uri" })),
9028
+ instructions: Type.Optional(Type.String()),
9029
+ userCode: Type.Optional(Type.String()),
9030
+ verificationUri: Type.Optional(Type.String({ format: "uri" })),
9031
+ error: Type.Optional(Type.String())
9032
+ }, { $id: "AgentServerSubscriptionLogin" });
9033
+ var AgentServerStatusSchema = Type.Object({
9034
+ version: Type.String(),
9035
+ platform: Type.String(),
9036
+ subscriptions: Type.Array(schemaRef(AgentServerSubscriptionSchema)),
9037
+ agents: Type.Array(schemaRef(AgentServerAgentSchema)),
9038
+ providers: Type.Record(Type.String(), schemaRef(AgentServerProviderSchema)),
9039
+ runs: Type.Array(schemaRef(AgentServerRunSchema))
9040
+ }, { $id: "AgentServerStatus" });
9041
+ var PairingStartedSchema = Type.Object({
9042
+ pairingId: Type.String(),
9043
+ approvalPath: Type.String()
9044
+ }, { $id: "PairingStarted" });
9045
+ var PairingClaimedSchema = Type.Object({ token: Type.String() }, { $id: "PairingClaimed" });
9046
+ var PairingParamsSchema = Type.Object({ pairingId: Type.String() });
9047
+ var ProviderParamsSchema = Type.Object({ providerId: Type.String() });
9048
+ var AgentParamsSchema = Type.Object({ agentName: Type.String() });
9049
+ var RunParamsSchema = Type.Object({ runId: Type.String() });
9050
+ var CreateAgentSchema = Type.Object({
9051
+ kind: Type.Union([Type.Literal("managed"), Type.Literal("external")]),
9052
+ name: Type.String()
9053
+ }, { anyOf: [Type.Object({
9054
+ kind: Type.Literal("managed"),
9055
+ enrollmentToken: Type.String()
9056
+ }), Type.Object({
9057
+ kind: Type.Literal("external"),
9058
+ configDir: Type.String(),
9059
+ apiUrl: Type.Optional(Type.String({ format: "uri" }))
9060
+ })] });
9061
+ var ReconcileAgentSchema = Type.Object({ action: Type.Union([Type.Literal("resume"), Type.Literal("abandon")]) });
9062
+ var ReconcileAgentResultSchema = Type.Union([schemaRef(AgentServerAgentSchema), Type.Object({ abandoned: Type.Literal(true) })], { $id: "ReconcileAgentResult" });
9063
+ var PutProviderSchema = Type.Object({
9064
+ api: Type.String(),
9065
+ baseUrl: Type.String({ format: "uri" }),
9066
+ envName: Type.String(),
9067
+ models: StringList,
9068
+ apiKey: Type.Optional(Type.String())
9069
+ });
9070
+ var DiscoverModelsSchema = Type.Object({ models: StringList }, { $id: "DiscoveredModels" });
9071
+ var StartRunSchema = Type.Object({
9072
+ agent: Type.String(),
9073
+ teamId: Type.String(),
9074
+ profiles: StringList,
9075
+ taskTypes: StringList,
9076
+ mode: Type.Union([Type.Literal("poll"), Type.Literal("drain")])
9077
+ });
9078
+ var CancelledSubscriptionSchema = Type.Object({
9079
+ providerId: Type.String(),
9080
+ status: Type.Literal("cancelled")
9081
+ }, { $id: "CancelledSubscription" });
9082
+ var LogStreamSchema = Type.String({
9083
+ $id: "AgentServerLogStream",
9084
+ contentMediaType: "text/event-stream"
9085
+ });
9086
+ var AGENT_SERVER_SCHEMAS = [
9087
+ AgentServerHealthSchema,
9088
+ AgentServerProblemSchema,
9089
+ AgentServerAgentSchema,
9090
+ AgentServerProviderSchema,
9091
+ AgentServerRunRecordSchema,
9092
+ AgentServerRunSchema,
9093
+ AgentServerSubscriptionSchema,
9094
+ AgentServerSubscriptionLoginSchema,
9095
+ AgentServerStatusSchema,
9096
+ PairingStartedSchema,
9097
+ PairingClaimedSchema,
9098
+ ReconcileAgentResultSchema,
9099
+ DiscoverModelsSchema,
9100
+ CancelledSubscriptionSchema,
9101
+ LogStreamSchema
9102
+ ];
9103
+ var pairedSecurity = [{ agentServerToken: [] }];
9104
+ var problemResponse = { default: schemaRef(AgentServerProblemSchema) };
9105
+ var AgentServerRouteSchemas = {
9106
+ health: {
9107
+ operationId: "getAgentServerHealth",
9108
+ tags: ["system"],
9109
+ response: { 200: schemaRef(AgentServerHealthSchema) }
9110
+ },
9111
+ startPairing: {
9112
+ operationId: "startAgentServerPairing",
9113
+ tags: ["pairing"],
9114
+ response: {
9115
+ 201: schemaRef(PairingStartedSchema),
9116
+ ...problemResponse
9117
+ }
9118
+ },
9119
+ claimPairing: {
9120
+ operationId: "claimAgentServerPairing",
9121
+ tags: ["pairing"],
9122
+ params: PairingParamsSchema,
9123
+ response: {
9124
+ 200: schemaRef(PairingClaimedSchema),
9125
+ ...problemResponse
9126
+ }
9127
+ },
9128
+ status: {
9129
+ operationId: "getAgentServerStatus",
9130
+ tags: ["system"],
9131
+ security: pairedSecurity,
9132
+ response: {
9133
+ 200: schemaRef(AgentServerStatusSchema),
9134
+ ...problemResponse
9135
+ }
9136
+ },
9137
+ listAgents: {
9138
+ operationId: "listAgentServerAgents",
9139
+ tags: ["agents"],
9140
+ security: pairedSecurity,
9141
+ response: {
9142
+ 200: Type.Array(schemaRef(AgentServerAgentSchema)),
9143
+ ...problemResponse
9144
+ }
9145
+ },
9146
+ createAgent: {
9147
+ operationId: "createAgentServerAgent",
9148
+ tags: ["agents"],
9149
+ security: pairedSecurity,
9150
+ body: CreateAgentSchema,
9151
+ response: {
9152
+ 201: schemaRef(AgentServerAgentSchema),
9153
+ ...problemResponse
9154
+ }
9155
+ },
9156
+ reconcileAgent: {
9157
+ operationId: "reconcileAgentServerAgent",
9158
+ tags: ["agents"],
9159
+ security: pairedSecurity,
9160
+ params: AgentParamsSchema,
9161
+ body: ReconcileAgentSchema,
9162
+ response: {
9163
+ 200: schemaRef(ReconcileAgentResultSchema),
9164
+ ...problemResponse
9165
+ }
9166
+ },
9167
+ listProviders: {
9168
+ operationId: "listAgentServerProviders",
9169
+ tags: ["providers"],
9170
+ security: pairedSecurity,
9171
+ response: {
9172
+ 200: Type.Record(Type.String(), schemaRef(AgentServerProviderSchema)),
9173
+ ...problemResponse
9174
+ }
9175
+ },
9176
+ discoverModels: {
9177
+ operationId: "discoverAgentServerProviderModels",
9178
+ tags: ["providers"],
9179
+ security: pairedSecurity,
9180
+ params: ProviderParamsSchema,
9181
+ response: {
9182
+ 200: schemaRef(DiscoverModelsSchema),
9183
+ ...problemResponse
9184
+ }
9185
+ },
9186
+ putProvider: {
9187
+ operationId: "putAgentServerProvider",
9188
+ tags: ["providers"],
9189
+ security: pairedSecurity,
9190
+ params: ProviderParamsSchema,
9191
+ body: PutProviderSchema,
9192
+ response: {
9193
+ 200: schemaRef(AgentServerProviderSchema),
9194
+ ...problemResponse
9195
+ }
9196
+ },
9197
+ listSubscriptions: {
9198
+ operationId: "listAgentServerSubscriptions",
9199
+ tags: ["subscriptions"],
9200
+ security: pairedSecurity,
9201
+ response: {
9202
+ 200: Type.Array(schemaRef(AgentServerSubscriptionSchema)),
9203
+ ...problemResponse
9204
+ }
9205
+ },
9206
+ startSubscriptionLogin: {
9207
+ operationId: "startAgentServerSubscriptionLogin",
9208
+ tags: ["subscriptions"],
9209
+ security: pairedSecurity,
9210
+ params: ProviderParamsSchema,
9211
+ response: {
9212
+ 201: schemaRef(AgentServerSubscriptionLoginSchema),
9213
+ ...problemResponse
9214
+ }
9215
+ },
9216
+ getSubscriptionLogin: {
9217
+ operationId: "getAgentServerSubscriptionLogin",
9218
+ tags: ["subscriptions"],
9219
+ security: pairedSecurity,
9220
+ params: ProviderParamsSchema,
9221
+ response: {
9222
+ 200: schemaRef(AgentServerSubscriptionLoginSchema),
9223
+ ...problemResponse
9224
+ }
9225
+ },
9226
+ cancelSubscriptionLogin: {
9227
+ operationId: "cancelAgentServerSubscriptionLogin",
9228
+ tags: ["subscriptions"],
9229
+ security: pairedSecurity,
9230
+ params: ProviderParamsSchema,
9231
+ response: {
9232
+ 200: schemaRef(CancelledSubscriptionSchema),
9233
+ ...problemResponse
9234
+ }
9235
+ },
9236
+ listRuns: {
9237
+ operationId: "listAgentServerRuns",
9238
+ tags: ["runs"],
9239
+ security: pairedSecurity,
9240
+ response: {
9241
+ 200: Type.Array(schemaRef(AgentServerRunSchema)),
9242
+ ...problemResponse
9243
+ }
9244
+ },
9245
+ startRun: {
9246
+ operationId: "startAgentServerRun",
9247
+ tags: ["runs"],
9248
+ security: pairedSecurity,
9249
+ body: StartRunSchema,
9250
+ response: {
9251
+ 201: schemaRef(AgentServerRunSchema),
9252
+ ...problemResponse
9253
+ }
9254
+ },
9255
+ stopRun: {
9256
+ operationId: "stopAgentServerRun",
9257
+ tags: ["runs"],
9258
+ security: pairedSecurity,
9259
+ params: RunParamsSchema,
9260
+ response: {
9261
+ 200: schemaRef(AgentServerRunRecordSchema),
9262
+ ...problemResponse
9263
+ }
9264
+ },
9265
+ streamRunLogs: {
9266
+ operationId: "streamAgentServerRunLogs",
9267
+ tags: ["runs"],
9268
+ security: pairedSecurity,
9269
+ params: RunParamsSchema,
9270
+ response: {
9271
+ 200: schemaRef(LogStreamSchema),
9272
+ ...problemResponse
9273
+ }
9274
+ }
9275
+ };
9276
+ //#endregion
9277
+ //#region src/lib/agent-server/server.ts
8866
9278
  /**
8867
- * HTTP surface of `moltnet-agent serve` (#2061), built on the shared
9279
+ * HTTP surface of `moltnet-agent server` (#2061), built on the shared
8868
9280
  * loopback-companion security profile (#2066): loopback Host enforcement,
8869
9281
  * exact-origin CORS, Fetch-Metadata guards, strict JSON parsing.
8870
9282
  *
8871
9283
  * Everything under `/v1` except the pairing bootstrap requires a paired
8872
- * origin: the `x-moltnet-serve-token` header must verify against the
9284
+ * origin: the `x-moltnet-agent-server-token` header must verify against the
8873
9285
  * origin-bound token issued by the one-click pairing ceremony.
8874
9286
  */
8875
- var SERVE_TOKEN_HEADER = "x-moltnet-serve-token";
9287
+ var AGENT_SERVER_TOKEN_HEADER = "x-moltnet-agent-server-token";
8876
9288
  var BODY_LIMIT = 64 * 1024;
8877
9289
  var LOG_POLL_INTERVAL_MS = 500;
8878
9290
  var LOG_STREAM_MAX_DURATION_MS = 3600 * 1e3;
@@ -8881,7 +9293,7 @@ var LOG_READ_LIMIT_BYTES = 256 * 1024;
8881
9293
  var RUN_HISTORY_LIMIT = 100;
8882
9294
  var RATE_LIMIT_MAX = 120;
8883
9295
  var RATE_LIMIT_WINDOW_MS = 6e4;
8884
- async function readServeLogDelta(handle, state, limit = LOG_READ_LIMIT_BYTES) {
9296
+ async function readAgentServerLogDelta(handle, state, limit = LOG_READ_LIMIT_BYTES) {
8885
9297
  const info = await handle.stat();
8886
9298
  if (!info.isFile() || info.size <= state.offset) return {
8887
9299
  lines: [],
@@ -8929,8 +9341,8 @@ async function readServeLogDelta(handle, state, limit = LOG_READ_LIMIT_BYTES) {
8929
9341
  omitted
8930
9342
  };
8931
9343
  }
8932
- var ServeHttpError = class extends Error {
8933
- name = "ServeHttpError";
9344
+ var AgentServerHttpError = class extends Error {
9345
+ name = "AgentServerHttpError";
8934
9346
  constructor(statusCode, code, message) {
8935
9347
  super(message);
8936
9348
  this.statusCode = statusCode;
@@ -8939,23 +9351,23 @@ var ServeHttpError = class extends Error {
8939
9351
  };
8940
9352
  function requireBody(request) {
8941
9353
  const body = request.body;
8942
- if (typeof body !== "object" || body === null || Array.isArray(body)) throw new ServeHttpError(400, "invalid_body", "JSON object body required");
9354
+ if (typeof body !== "object" || body === null || Array.isArray(body)) throw new AgentServerHttpError(400, "invalid_body", "JSON object body required");
8943
9355
  return body;
8944
9356
  }
8945
9357
  function requireString(body, field) {
8946
9358
  const value = body[field];
8947
- if (typeof value !== "string" || value.trim().length === 0) throw new ServeHttpError(400, "invalid_body", `"${field}" must be a non-empty string`);
9359
+ if (typeof value !== "string" || value.trim().length === 0) throw new AgentServerHttpError(400, "invalid_body", `"${field}" must be a non-empty string`);
8948
9360
  return value.trim();
8949
9361
  }
8950
9362
  function optionalString(body, field) {
8951
9363
  const value = body[field];
8952
9364
  if (value === void 0 || value === null) return void 0;
8953
- if (typeof value !== "string" || value.trim().length === 0) throw new ServeHttpError(400, "invalid_body", `"${field}" must be a non-empty string when present`);
9365
+ if (typeof value !== "string" || value.trim().length === 0) throw new AgentServerHttpError(400, "invalid_body", `"${field}" must be a non-empty string when present`);
8954
9366
  return value.trim();
8955
9367
  }
8956
9368
  function stringArray(body, field, options = {}) {
8957
9369
  const value = body[field];
8958
- if (!Array.isArray(value) || !options.allowEmpty && value.length === 0 || value.some((item) => typeof item !== "string" || item.length === 0)) throw new ServeHttpError(400, "invalid_body", `"${field}" must be ${options.allowEmpty ? "a" : "a non-empty"} string array`);
9370
+ if (!Array.isArray(value) || !options.allowEmpty && value.length === 0 || value.some((item) => typeof item !== "string" || item.length === 0)) throw new AgentServerHttpError(400, "invalid_body", `"${field}" must be ${options.allowEmpty ? "a" : "a non-empty"} string array`);
8959
9371
  return value;
8960
9372
  }
8961
9373
  function requestOperationSignal(request, shutdownSignal) {
@@ -8964,16 +9376,18 @@ function requestOperationSignal(request, shutdownSignal) {
8964
9376
  else request.raw.once("aborted", () => disconnected.abort());
8965
9377
  return shutdownSignal ? AbortSignal.any([disconnected.signal, shutdownSignal]) : disconnected.signal;
8966
9378
  }
8967
- function buildServeServer(options) {
9379
+ function buildAgentServer(options) {
8968
9380
  const { pairing } = options;
8969
9381
  const app = options.logger ? Fastify({
8970
9382
  bodyLimit: BODY_LIMIT,
8971
9383
  loggerInstance: options.logger
8972
9384
  }) : Fastify({ bodyLimit: BODY_LIMIT });
9385
+ options.registerOpenApi?.(app);
9386
+ for (const schema of AGENT_SERVER_SCHEMAS) app.addSchema(schema);
8973
9387
  registerLoopbackSecurity(app, {
8974
9388
  allowedOrigins: options.allowedOrigins,
8975
9389
  ...options.selfOrigin ? { selfOrigins: [options.selfOrigin] } : {},
8976
- allowedHeaders: [SERVE_TOKEN_HEADER],
9390
+ allowedHeaders: [AGENT_SERVER_TOKEN_HEADER],
8977
9391
  methods: [
8978
9392
  "GET",
8979
9393
  "POST",
@@ -8986,35 +9400,37 @@ function buildServeServer(options) {
8986
9400
  global: false,
8987
9401
  max: options.rateLimitMax ?? RATE_LIMIT_MAX,
8988
9402
  timeWindow: RATE_LIMIT_WINDOW_MS,
8989
- errorResponseBuilder: () => new ServeHttpError(429, "rate_limited", "Too many requests"),
9403
+ errorResponseBuilder: () => new AgentServerHttpError(429, "rate_limited", "Too many requests"),
8990
9404
  keyGenerator: (request) => {
8991
9405
  const origin = request.headers.origin;
8992
9406
  return isConfiguredOrigin(origin, options) ? `origin:${origin}` : `ip:${request.ip}`;
8993
9407
  }
8994
9408
  });
8995
- app.after(() => app.addHook("onRequest", app.rateLimit()));
8996
9409
  app.addContentTypeParser("application/x-www-form-urlencoded", { parseAs: "buffer" }, (_request, body, done) => {
8997
9410
  try {
8998
9411
  const form = new TextDecoder("utf-8", { fatal: true }).decode(typeof body === "string" ? Buffer.from(body) : body);
8999
9412
  done(null, new URLSearchParams(form));
9000
9413
  } catch {
9001
- done(new ServeHttpError(400, "invalid_body", "Form body is invalid"), void 0);
9414
+ done(new AgentServerHttpError(400, "invalid_body", "Form body is invalid"), void 0);
9002
9415
  }
9003
9416
  });
9004
9417
  const requirePairedOrigin = (request) => {
9005
9418
  const origin = requireOriginHeader(request.headers);
9006
- const token = request.headers[SERVE_TOKEN_HEADER];
9007
- if (typeof token !== "string" || token.length === 0) throw new ServeHttpError(401, "pairing_required", "Pairing token is required");
9419
+ const token = request.headers[AGENT_SERVER_TOKEN_HEADER];
9420
+ if (typeof token !== "string" || token.length === 0) throw new AgentServerHttpError(401, "pairing_required", "Pairing token is required");
9008
9421
  pairing.verify(origin, token);
9009
9422
  return origin;
9010
9423
  };
9011
- app.get("/health", async () => ({ status: "ok" }));
9012
- registerPairingRoutes(app, pairing);
9013
- registerStatusRoute(app, options, requirePairedOrigin);
9014
- registerAgentRoutes(app, options, requirePairedOrigin);
9015
- registerProviderRoutes(app, options, requirePairedOrigin);
9016
- registerSubscriptionRoutes(app, options, requirePairedOrigin);
9017
- registerRunRoutes(app, options, requirePairedOrigin);
9424
+ app.after(() => {
9425
+ app.addHook("onRequest", app.rateLimit());
9426
+ app.get("/health", { schema: AgentServerRouteSchemas.health }, async () => ({ status: "ok" }));
9427
+ registerPairingRoutes(app, pairing);
9428
+ registerStatusRoute(app, options, requirePairedOrigin);
9429
+ registerAgentRoutes(app, options, requirePairedOrigin);
9430
+ registerProviderRoutes(app, options, requirePairedOrigin);
9431
+ registerSubscriptionRoutes(app, options, requirePairedOrigin);
9432
+ registerRunRoutes(app, options, requirePairedOrigin);
9433
+ });
9018
9434
  app.addHook("onClose", () => {
9019
9435
  options.subscriptions.close();
9020
9436
  });
@@ -9023,13 +9439,13 @@ function buildServeServer(options) {
9023
9439
  message: "Route is not available"
9024
9440
  }));
9025
9441
  app.setErrorHandler(async (error, request, reply) => {
9026
- const { statusCode, code, message } = normalizeServeError(error);
9442
+ const { statusCode, code, message } = normalizeAgentServerError(error);
9027
9443
  if (statusCode === 500) request.log.error({
9028
9444
  ...safeErrorContext(error),
9029
- code: "serve_request_failed",
9445
+ code: "agent_server_request_failed",
9030
9446
  method: request.method,
9031
9447
  route: request.routeOptions.url
9032
- }, "Serve request failed");
9448
+ }, "AgentServer request failed");
9033
9449
  return reply.code(statusCode).send({
9034
9450
  code,
9035
9451
  message
@@ -9038,11 +9454,11 @@ function buildServeServer(options) {
9038
9454
  return app;
9039
9455
  }
9040
9456
  function registerPairingRoutes(app, pairing) {
9041
- app.post("/v1/pairings", async (request, reply) => {
9457
+ app.post("/v1/pairings", { schema: AgentServerRouteSchemas.startPairing }, async (request, reply) => {
9042
9458
  const origin = requireOriginHeader(request.headers);
9043
9459
  return reply.code(201).send(pairing.start(origin));
9044
9460
  });
9045
- app.get("/pairings/:pairingId", async (request, reply) => {
9461
+ app.get("/pairings/:pairingId", { schema: { hide: true } }, async (request, reply) => {
9046
9462
  assertNavigationRequest(request.headers);
9047
9463
  const { pairingId } = request.params;
9048
9464
  const approval = pairing.approval(pairingId);
@@ -9052,17 +9468,17 @@ function registerPairingRoutes(app, pairing) {
9052
9468
  confirmToken: approval.confirmToken
9053
9469
  }));
9054
9470
  });
9055
- app.post("/pairings/:pairingId/confirm", async (request, reply) => {
9471
+ app.post("/pairings/:pairingId/confirm", { schema: { hide: true } }, async (request, reply) => {
9056
9472
  rejectExplicitCrossSite(request.headers);
9057
9473
  const { pairingId } = request.params;
9058
- if (!(request.body instanceof URLSearchParams)) throw new ServeHttpError(400, "invalid_body", "Confirmation form is invalid");
9474
+ if (!(request.body instanceof URLSearchParams)) throw new AgentServerHttpError(400, "invalid_body", "Confirmation form is invalid");
9059
9475
  const { origin } = pairing.confirm(pairingId, request.body.get("confirmToken") ?? "");
9060
9476
  return reply.type("text/html; charset=utf-8").send(renderPairingResultPage({
9061
9477
  title: "Connection approved",
9062
9478
  message: `${origin} can now manage local MoltNet agents on this machine.`
9063
9479
  }));
9064
9480
  });
9065
- app.post("/v1/pairings/:pairingId/claim", async (request) => {
9481
+ app.post("/v1/pairings/:pairingId/claim", { schema: AgentServerRouteSchemas.claimPairing }, async (request) => {
9066
9482
  const origin = requireOriginHeader(request.headers);
9067
9483
  const { pairingId } = request.params;
9068
9484
  return pairing.claim(pairingId, origin);
@@ -9070,7 +9486,7 @@ function registerPairingRoutes(app, pairing) {
9070
9486
  }
9071
9487
  function registerStatusRoute(app, options, requirePairedOrigin) {
9072
9488
  const { store, runs } = options;
9073
- app.get("/v1/status", async (request) => {
9489
+ app.get("/v1/status", { schema: AgentServerRouteSchemas.status }, async (request) => {
9074
9490
  requirePairedOrigin(request);
9075
9491
  return {
9076
9492
  version: options.version,
@@ -9084,17 +9500,20 @@ function registerStatusRoute(app, options, requirePairedOrigin) {
9084
9500
  }
9085
9501
  function registerAgentRoutes(app, options, requirePairedOrigin) {
9086
9502
  const { store } = options;
9087
- app.get("/v1/agents", async (request) => {
9503
+ app.get("/v1/agents", { schema: AgentServerRouteSchemas.listAgents }, async (request) => {
9088
9504
  requirePairedOrigin(request);
9089
9505
  return store.listActivations().map((activation) => publicAgentView(store, activation));
9090
9506
  });
9091
- app.post("/v1/agents", async (request, reply) => {
9507
+ app.post("/v1/agents", {
9508
+ schema: AgentServerRouteSchemas.createAgent,
9509
+ attachValidation: true
9510
+ }, async (request, reply) => {
9092
9511
  requirePairedOrigin(request);
9093
9512
  const body = requireBody(request);
9094
9513
  const signal = requestOperationSignal(request, options.shutdownSignal);
9095
9514
  const kind = requireString(body, "kind");
9096
9515
  if (kind === "managed") {
9097
- if (body["apiUrl"] !== void 0) throw new ServeHttpError(400, "invalid_body", "managed agent registration uses the configured MoltNet API URL; apiUrl cannot be overridden");
9516
+ if (body["apiUrl"] !== void 0) throw new AgentServerHttpError(400, "invalid_body", "managed agent registration uses the configured MoltNet API URL; apiUrl cannot be overridden");
9098
9517
  const entry = await createManagedAgent(store, options.secrets, {
9099
9518
  name: requireString(body, "name"),
9100
9519
  apiUrl: options.defaultApiUrl,
@@ -9113,13 +9532,16 @@ function registerAgentRoutes(app, options, requirePairedOrigin) {
9113
9532
  });
9114
9533
  return reply.code(201).send(publicAgentView(store, entry.activation));
9115
9534
  }
9116
- throw new ServeHttpError(400, "invalid_body", "\"kind\" must be \"managed\" or \"external\"");
9535
+ throw new AgentServerHttpError(400, "invalid_body", "\"kind\" must be \"managed\" or \"external\"");
9117
9536
  });
9118
- app.post("/v1/agents/:agentName/reconcile", async (request) => {
9537
+ app.post("/v1/agents/:agentName/reconcile", {
9538
+ schema: AgentServerRouteSchemas.reconcileAgent,
9539
+ attachValidation: true
9540
+ }, async (request) => {
9119
9541
  requirePairedOrigin(request);
9120
9542
  const { agentName } = request.params;
9121
9543
  const action = requireString(requireBody(request), "action");
9122
- if (action !== "resume" && action !== "abandon") throw new ServeHttpError(400, "invalid_body", "\"action\" must be \"resume\" or \"abandon\"");
9544
+ if (action !== "resume" && action !== "abandon") throw new AgentServerHttpError(400, "invalid_body", "\"action\" must be \"resume\" or \"abandon\"");
9123
9545
  const reconciled = await reconcileManagedRegistration(store, options.secrets, agentName, action, void 0, requestOperationSignal(request, options.shutdownSignal));
9124
9546
  return reconciled ? publicAgentView(store, reconciled.activation) : { abandoned: true };
9125
9547
  });
@@ -9132,16 +9554,16 @@ function registerProviderRoutes(app, options, requirePairedOrigin) {
9132
9554
  mutationQueue = result.then(() => void 0, () => void 0);
9133
9555
  return result;
9134
9556
  };
9135
- app.get("/v1/providers", async (request) => {
9557
+ app.get("/v1/providers", { schema: AgentServerRouteSchemas.listProviders }, async (request) => {
9136
9558
  requirePairedOrigin(request);
9137
9559
  return Object.fromEntries(Object.entries(store.readProviders()).map(([id, provider]) => [id, providerView(provider)]));
9138
9560
  });
9139
- app.post("/v1/providers/:providerId/discover-models", async (request) => {
9561
+ app.post("/v1/providers/:providerId/discover-models", { schema: AgentServerRouteSchemas.discoverModels }, async (request) => {
9140
9562
  requirePairedOrigin(request);
9141
9563
  const { providerId: rawProviderId } = request.params;
9142
9564
  const providerId = assertProviderId(rawProviderId);
9143
9565
  const provider = store.readProviders()[providerId];
9144
- if (!provider) throw new ServeHttpError(404, "provider_not_found", `provider "${providerId}" was not found`);
9566
+ if (!provider) throw new AgentServerHttpError(404, "provider_not_found", `provider "${providerId}" was not found`);
9145
9567
  const parsed = parseProviderBaseUrl(provider.baseUrl, providerId);
9146
9568
  const baseUrl = parsed.href.replace(/\/$/u, "");
9147
9569
  let apiKey;
@@ -9150,10 +9572,10 @@ function registerProviderRoutes(app, options, requirePairedOrigin) {
9150
9572
  } catch (error) {
9151
9573
  request.log.warn({
9152
9574
  ...safeErrorContext(error),
9153
- code: "serve_provider_secret_unavailable",
9575
+ code: "agent_server_provider_secret_unavailable",
9154
9576
  providerId
9155
9577
  }, "Provider API key could not be resolved for model discovery");
9156
- throw new ServeHttpError(400, "provider_secret_unavailable", `provider "${providerId}" API key could not be resolved`);
9578
+ throw new AgentServerHttpError(400, "provider_secret_unavailable", `provider "${providerId}" API key could not be resolved`);
9157
9579
  }
9158
9580
  const headers = apiKey ? { authorization: `Bearer ${apiKey}` } : {};
9159
9581
  const fetchImpl = options.discoverFetch ?? fetch;
@@ -9174,7 +9596,7 @@ function registerProviderRoutes(app, options, requirePairedOrigin) {
9174
9596
  errorType
9175
9597
  });
9176
9598
  request.log.warn({
9177
- code: "serve_provider_discovery_request_failed",
9599
+ code: "agent_server_provider_discovery_request_failed",
9178
9600
  endpoint,
9179
9601
  errorType,
9180
9602
  providerId
@@ -9187,7 +9609,7 @@ function registerProviderRoutes(app, options, requirePairedOrigin) {
9187
9609
  status: response.status
9188
9610
  });
9189
9611
  const context = {
9190
- code: "serve_provider_discovery_upstream_error",
9612
+ code: "agent_server_provider_discovery_upstream_error",
9191
9613
  endpoint,
9192
9614
  providerId,
9193
9615
  statusCode: response.status
@@ -9201,7 +9623,7 @@ function registerProviderRoutes(app, options, requirePairedOrigin) {
9201
9623
  } catch {
9202
9624
  failures.push({ kind: "invalid_response" });
9203
9625
  request.log.warn({
9204
- code: "serve_provider_discovery_invalid_json",
9626
+ code: "agent_server_provider_discovery_invalid_json",
9205
9627
  endpoint,
9206
9628
  providerId
9207
9629
  }, "Provider model discovery returned invalid JSON");
@@ -9212,19 +9634,22 @@ function registerProviderRoutes(app, options, requirePairedOrigin) {
9212
9634
  if (collector.size === 0) collector.addOllamaResponse(await tryJson("ollama_tags", `${parsed.origin}/api/tags`));
9213
9635
  const result = collector.result(providerId, failures);
9214
9636
  if (result.discoveredCount > result.models.length) request.log.warn({
9215
- code: "serve_provider_discovery_truncated",
9637
+ code: "agent_server_provider_discovery_truncated",
9216
9638
  discoveredCount: result.discoveredCount,
9217
9639
  providerId,
9218
9640
  returnedCount: 500
9219
9641
  }, "Provider model discovery result was truncated");
9220
9642
  request.log.info({
9221
- code: "serve_provider_discovery_completed",
9643
+ code: "agent_server_provider_discovery_completed",
9222
9644
  modelCount: result.models.length,
9223
9645
  providerId
9224
9646
  }, "Provider model discovery completed");
9225
9647
  return { models: result.models };
9226
9648
  });
9227
- app.put("/v1/providers/:providerId", async (request, reply) => {
9649
+ app.put("/v1/providers/:providerId", {
9650
+ schema: AgentServerRouteSchemas.putProvider,
9651
+ attachValidation: true
9652
+ }, async (request, reply) => {
9228
9653
  requirePairedOrigin(request);
9229
9654
  const { providerId: rawProviderId } = request.params;
9230
9655
  const providerId = assertProviderId(rawProviderId);
@@ -9261,22 +9686,22 @@ function runViews(runs) {
9261
9686
  }));
9262
9687
  }
9263
9688
  function registerSubscriptionRoutes(app, options, requirePairedOrigin) {
9264
- app.get("/v1/subscriptions", async (request) => {
9689
+ app.get("/v1/subscriptions", { schema: AgentServerRouteSchemas.listSubscriptions }, async (request) => {
9265
9690
  requirePairedOrigin(request);
9266
9691
  return options.subscriptions.list();
9267
9692
  });
9268
- app.post("/v1/subscriptions/:providerId/login", async (request, reply) => {
9693
+ app.post("/v1/subscriptions/:providerId/login", { schema: AgentServerRouteSchemas.startSubscriptionLogin }, async (request, reply) => {
9269
9694
  requirePairedOrigin(request);
9270
9695
  const { providerId } = request.params;
9271
9696
  const login = await options.subscriptions.start(providerId);
9272
9697
  return reply.code(201).send(login);
9273
9698
  });
9274
- app.get("/v1/subscriptions/:providerId/login", async (request) => {
9699
+ app.get("/v1/subscriptions/:providerId/login", { schema: AgentServerRouteSchemas.getSubscriptionLogin }, async (request) => {
9275
9700
  requirePairedOrigin(request);
9276
9701
  const { providerId } = request.params;
9277
9702
  return options.subscriptions.status(providerId);
9278
9703
  });
9279
- app.delete("/v1/subscriptions/:providerId/login", async (request) => {
9704
+ app.delete("/v1/subscriptions/:providerId/login", { schema: AgentServerRouteSchemas.cancelSubscriptionLogin }, async (request) => {
9280
9705
  requirePairedOrigin(request);
9281
9706
  const { providerId } = request.params;
9282
9707
  return options.subscriptions.cancel(providerId);
@@ -9284,11 +9709,14 @@ function registerSubscriptionRoutes(app, options, requirePairedOrigin) {
9284
9709
  }
9285
9710
  function registerRunRoutes(app, options, requirePairedOrigin) {
9286
9711
  const { runs } = options;
9287
- app.get("/v1/runs", async (request) => {
9712
+ app.get("/v1/runs", { schema: AgentServerRouteSchemas.listRuns }, async (request) => {
9288
9713
  requirePairedOrigin(request);
9289
9714
  return runViews(runs);
9290
9715
  });
9291
- app.post("/v1/runs", async (request, reply) => {
9716
+ app.post("/v1/runs", {
9717
+ schema: AgentServerRouteSchemas.startRun,
9718
+ attachValidation: true
9719
+ }, async (request, reply) => {
9292
9720
  requirePairedOrigin(request);
9293
9721
  const body = requireBody(request);
9294
9722
  const record = await runs.start({
@@ -9298,9 +9726,12 @@ function registerRunRoutes(app, options, requirePairedOrigin) {
9298
9726
  taskTypes: stringArray(body, "taskTypes"),
9299
9727
  mode: requireString(body, "mode")
9300
9728
  }, requestOperationSignal(request, options.shutdownSignal));
9301
- return reply.code(201).send(record);
9729
+ return reply.code(201).send({
9730
+ ...record,
9731
+ active: runs.isActive(record.id)
9732
+ });
9302
9733
  });
9303
- app.delete("/v1/runs/:runId", async (request) => {
9734
+ app.delete("/v1/runs/:runId", { schema: AgentServerRouteSchemas.stopRun }, async (request) => {
9304
9735
  requirePairedOrigin(request);
9305
9736
  const { runId } = request.params;
9306
9737
  return runs.stop(runId);
@@ -9310,13 +9741,14 @@ function registerRunRoutes(app, options, requirePairedOrigin) {
9310
9741
  function registerRunLogRoute(app, options, requirePairedOrigin) {
9311
9742
  const { runs, store } = options;
9312
9743
  let openStreams = 0;
9313
- app.get("/v1/runs/:runId/logs", async (request, reply) => {
9744
+ app.get("/v1/runs/:runId/logs", { schema: AgentServerRouteSchemas.streamRunLogs }, async (request, reply) => {
9314
9745
  requirePairedOrigin(request);
9315
9746
  const { runId } = request.params;
9316
9747
  const record = runs.status(runId);
9317
9748
  store.resolveRunLogPath(record.id);
9318
- if (openStreams >= MAX_LOG_STREAMS) throw new ServeHttpError(429, "rate_limited", "Too many concurrent log streams");
9749
+ if (openStreams >= MAX_LOG_STREAMS) throw new AgentServerHttpError(429, "rate_limited", "Too many concurrent log streams");
9319
9750
  openStreams += 1;
9751
+ reply.hijack();
9320
9752
  reply.raw.writeHead(200, {
9321
9753
  "content-type": "text/event-stream",
9322
9754
  "cache-control": "no-store",
@@ -9355,7 +9787,7 @@ function registerRunLogRoute(app, options, requirePairedOrigin) {
9355
9787
  const push = async () => {
9356
9788
  const handle = await open(store.resolveRunLogPath(record.id), constants.O_RDONLY | constants.O_NOFOLLOW);
9357
9789
  try {
9358
- const { lines, omitted } = await readServeLogDelta(handle, readState);
9790
+ const { lines, omitted } = await readAgentServerLogDelta(handle, readState);
9359
9791
  if (omitted) await writeData("[older log output omitted]");
9360
9792
  for (const line of lines) await writeData(line);
9361
9793
  } finally {
@@ -9366,9 +9798,9 @@ function registerRunLogRoute(app, options, requirePairedOrigin) {
9366
9798
  if (closed) return;
9367
9799
  request.log.warn({
9368
9800
  ...safeErrorContext(error),
9369
- code: "serve_log_tail_failed",
9801
+ code: "agent_server_log_tail_failed",
9370
9802
  runId
9371
- }, "serve log tail failed");
9803
+ }, "Agent Server log tail failed");
9372
9804
  finish(true);
9373
9805
  };
9374
9806
  const schedule = () => {
@@ -9381,7 +9813,7 @@ function registerRunLogRoute(app, options, requirePairedOrigin) {
9381
9813
  }, LOG_POLL_INTERVAL_MS);
9382
9814
  pollTimer.unref();
9383
9815
  };
9384
- request.raw.on("close", () => finish());
9816
+ reply.raw.on("close", () => finish());
9385
9817
  try {
9386
9818
  await push();
9387
9819
  if (runs.isActive(record.id)) schedule();
@@ -9426,8 +9858,8 @@ function corsHeadersFor(request, options) {
9426
9858
  function isConfiguredOrigin(origin, options) {
9427
9859
  return typeof origin === "string" && (options.allowedOrigins.includes(origin) || origin === options.selfOrigin);
9428
9860
  }
9429
- function normalizeServeError(error) {
9430
- if (error instanceof ServeHttpError) return {
9861
+ function normalizeAgentServerError(error) {
9862
+ if (error instanceof AgentServerHttpError) return {
9431
9863
  statusCode: error.statusCode,
9432
9864
  code: error.code,
9433
9865
  message: error.message
@@ -9437,32 +9869,32 @@ function normalizeServeError(error) {
9437
9869
  code: error.kind,
9438
9870
  message: error.message
9439
9871
  };
9440
- if (error instanceof ServePairingError) return {
9872
+ if (error instanceof AgentServerPairingError) return {
9441
9873
  statusCode: error.code === "pairing_not_found" ? 404 : error.code === "pairing_token_invalid" || error.code === "pairing_not_approved" ? 401 : 403,
9442
9874
  code: error.code,
9443
9875
  message: error.message
9444
9876
  };
9445
- if (error instanceof ServeStoreError) return {
9877
+ if (error instanceof AgentServerStoreError) return {
9446
9878
  statusCode: error.code === "not_found" ? 404 : error.code === "io_error" ? 500 : 400,
9447
9879
  code: error.code,
9448
9880
  message: error.message
9449
9881
  };
9450
- if (error instanceof ServeRunError) return {
9882
+ if (error instanceof AgentServerRunError) return {
9451
9883
  statusCode: error.code === "run_not_found" ? 404 : 400,
9452
9884
  code: error.code,
9453
9885
  message: error.message
9454
9886
  };
9455
- if (error instanceof ServeSubscriptionError) return {
9887
+ if (error instanceof AgentServerSubscriptionError) return {
9456
9888
  statusCode: error.code === "login_not_found" ? 404 : error.code === "provider_unknown" ? 404 : 400,
9457
9889
  code: error.code,
9458
9890
  message: error.message
9459
9891
  };
9460
- if (error instanceof ServeModelDiscoveryError) return {
9892
+ if (error instanceof AgentServerModelDiscoveryError) return {
9461
9893
  statusCode: error.statusCode,
9462
9894
  code: error.code,
9463
9895
  message: error.message
9464
9896
  };
9465
- if (error instanceof ServeIdentityError) return {
9897
+ if (error instanceof AgentServerIdentityError) return {
9466
9898
  statusCode: error.code === "agent_exists" ? 409 : 400,
9467
9899
  code: error.code,
9468
9900
  message: error.message
@@ -9474,9 +9906,9 @@ function normalizeServeError(error) {
9474
9906
  };
9475
9907
  }
9476
9908
  //#endregion
9477
- //#region src/cli/serve.ts
9909
+ //#region src/cli/server.ts
9478
9910
  /**
9479
- * `moltnet-agent serve` — per-user loopback supervisor (#2061).
9911
+ * `moltnet-agent server` — per-user loopback supervisor (#2061).
9480
9912
  *
9481
9913
  * Starts nothing on its own: it binds 127.0.0.1 and waits for a paired
9482
9914
  * Console origin to configure agents/providers and start/stop runs.
@@ -9485,12 +9917,12 @@ var DEFAULT_PORT = 17374;
9485
9917
  var DEFAULT_ALLOWED_ORIGINS = "https://console.themolt.net";
9486
9918
  var DEFAULT_API_URL = "https://api.themolt.net";
9487
9919
  var SHUTDOWN_TIMEOUT_MS = 15e3;
9488
- async function runServe(argv) {
9920
+ async function runAgentServer(argv) {
9489
9921
  if (isHelpFlag(argv)) {
9490
- console.log(SERVE_HELP);
9922
+ console.log(AGENT_SERVER_HELP);
9491
9923
  return 0;
9492
9924
  }
9493
- const envConfig = loadServeEnvConfig();
9925
+ const envConfig = loadAgentServerEnvConfig();
9494
9926
  const { values } = parseArgs({
9495
9927
  args: argv,
9496
9928
  options: {
@@ -9506,19 +9938,19 @@ async function runServe(argv) {
9506
9938
  return 1;
9507
9939
  }
9508
9940
  const allowedOrigins = parseAllowedOrigins(values["allowed-origins"] ?? (envConfig.allowedOrigins || DEFAULT_ALLOWED_ORIGINS));
9509
- const root = values.root ?? resolveServeRoot({
9941
+ const root = values.root ?? resolveAgentServerRoot({
9510
9942
  root: envConfig.root,
9511
9943
  xdgConfigHome: envConfig.xdgConfigHome
9512
9944
  });
9513
9945
  const defaultApiUrl = values["api-url"] ?? (envConfig.apiUrl || DEFAULT_API_URL);
9514
- const store = new ServeStore(root).ensure();
9946
+ const store = new AgentServerStore(root).ensure();
9515
9947
  const { logger, shutdown: shutdownLogger } = createRootLogger({
9516
- name: "agent-daemon.serve",
9948
+ name: "agent-daemon.server",
9517
9949
  level: envConfig.logLevel || "info"
9518
9950
  });
9519
9951
  try {
9520
9952
  try {
9521
- return await withServeLock(root, async () => {
9953
+ return await withAgentServerLock(root, async () => {
9522
9954
  const secrets = new FileSecretProvider({
9523
9955
  root: store.secretsDir,
9524
9956
  writable: true
@@ -9527,7 +9959,7 @@ async function runServe(argv) {
9527
9959
  const externalSecretProviders = createNodeSecretProviderRegistry();
9528
9960
  const pairing = new PairingService();
9529
9961
  const shutdownController = new AbortController();
9530
- const subscriptions = new ProviderLoginService({
9962
+ const subscriptions = await ProviderLoginService.create({
9531
9963
  authPath: store.piAuthJsonPath,
9532
9964
  logger
9533
9965
  });
@@ -9538,7 +9970,7 @@ async function runServe(argv) {
9538
9970
  baseEnv: processEnvSnapshot(),
9539
9971
  logger
9540
9972
  });
9541
- const app = buildServeServer({
9973
+ const app = buildAgentServer({
9542
9974
  store,
9543
9975
  secrets,
9544
9976
  secretProviders,
@@ -9558,11 +9990,11 @@ async function runServe(argv) {
9558
9990
  host: "127.0.0.1",
9559
9991
  port
9560
9992
  });
9561
- console.error(`moltnet-agent serve listening on ${address}`);
9993
+ console.error(`moltnet-agent server listening on ${address}`);
9562
9994
  console.error(`config root: ${root}`);
9563
9995
  console.error(`allowed origins: ${allowedOrigins.join(", ")}`);
9564
9996
  console.error("Pair from the Console \"Local runtime\" page; approve the one-click prompt this server opens.");
9565
- return await waitForServeShutdown(runs, app, shutdownController);
9997
+ return await waitForAgentServerShutdown(runs, app, shutdownController);
9566
9998
  } catch (cause) {
9567
9999
  await app.close().catch(() => void 0);
9568
10000
  throw cause;
@@ -9573,7 +10005,7 @@ async function runServe(argv) {
9573
10005
  process.kill(process.pid, "SIGTERM");
9574
10006
  } });
9575
10007
  } catch (cause) {
9576
- if (cause instanceof ServeLockError) {
10008
+ if (cause instanceof AgentServerLockError) {
9577
10009
  console.error(cause.message);
9578
10010
  return 1;
9579
10011
  }
@@ -9583,7 +10015,7 @@ async function runServe(argv) {
9583
10015
  await shutdownLogger();
9584
10016
  }
9585
10017
  }
9586
- function waitForServeShutdown(runs, app, shutdownController) {
10018
+ function waitForAgentServerShutdown(runs, app, shutdownController) {
9587
10019
  return new Promise((resolvePromise) => {
9588
10020
  let shuttingDown = false;
9589
10021
  const shutdown = () => {
@@ -9808,7 +10240,7 @@ async function runAgentDaemonCli(options) {
9808
10240
  case "poll": return runPoll(rest, options.runtime);
9809
10241
  case "once": return runOnce(rest, options.runtime);
9810
10242
  case "drain": return runDrain(rest, options.runtime);
9811
- case "serve": return runServe(rest);
10243
+ case "server": return runAgentServer(rest);
9812
10244
  case "sync-sessions": return runSyncSessions(rest);
9813
10245
  case "-h":
9814
10246
  case "--help":