@moxt-ai/mobius 0.0.24 → 0.0.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -21107,9 +21107,9 @@ function readStateDirectory(environment = process.env) {
21107
21107
  }
21108
21108
 
21109
21109
  // src/config.ts
21110
- function loadDefaultWorkspace() {
21111
- const { MOBIUS_DEFAULT_WORKSPACE: configuredWorkspace } = process.env;
21112
- return configuredWorkspace ?? join3(readStateDirectory(), "workspace");
21110
+ function loadDefaultWorkspace(environment = process.env) {
21111
+ const { MOBIUS_DEFAULT_WORKSPACE: configuredWorkspace } = environment;
21112
+ return configuredWorkspace ?? join3(readStateDirectory(environment), "workspace");
21113
21113
  }
21114
21114
  function createDaemonConfig(connection, agents = [], installableAgents = []) {
21115
21115
  return {
@@ -22462,7 +22462,7 @@ function createWritableAcpStream(output2) {
22462
22462
  if (outputFailure !== void 0) {
22463
22463
  return Promise.reject(outputFailure);
22464
22464
  }
22465
- return new Promise((resolve5, reject) => {
22465
+ return new Promise((resolve6, reject) => {
22466
22466
  pendingWriteFailures.add(reject);
22467
22467
  output2.write(chunk, (error61) => {
22468
22468
  pendingWriteFailures.delete(reject);
@@ -22475,7 +22475,7 @@ function createWritableAcpStream(output2) {
22475
22475
  reject(outputFailure);
22476
22476
  return;
22477
22477
  }
22478
- resolve5();
22478
+ resolve6();
22479
22479
  });
22480
22480
  });
22481
22481
  }
@@ -22882,9 +22882,9 @@ var AcpProcessSession = class _AcpProcessSession {
22882
22882
  stderr.on("data", (chunk) => {
22883
22883
  process.stderr.write(`[${agent.id}-acp] ${chunk}`);
22884
22884
  });
22885
- this.#processExit = new Promise((resolve5) => {
22886
- this.#child.once("error", resolve5);
22887
- this.#child.once("exit", resolve5);
22885
+ this.#processExit = new Promise((resolve6) => {
22886
+ this.#child.once("error", resolve6);
22887
+ this.#child.once("exit", resolve6);
22888
22888
  });
22889
22889
  const stream = ndJsonStream(createWritableAcpStream(stdin), readableWebStream(stdout));
22890
22890
  this.#connection = createAcpClient(this).connect(stream);
@@ -25123,7 +25123,7 @@ function decodeResponse(encoded) {
25123
25123
  }
25124
25124
  async function requestLocalBotService(socketPath, payload, signal) {
25125
25125
  signal.throwIfAborted();
25126
- return await new Promise((resolve5, reject) => {
25126
+ return await new Promise((resolve6, reject) => {
25127
25127
  const socket = createConnection(socketPath);
25128
25128
  const chunks = [];
25129
25129
  let length = 0;
@@ -25172,7 +25172,7 @@ async function requestLocalBotService(socketPath, payload, signal) {
25172
25172
  finished = true;
25173
25173
  signal.removeEventListener("abort", abort);
25174
25174
  try {
25175
- resolve5(decodeResponse(Buffer.concat(chunks).toString("utf8")));
25175
+ resolve6(decodeResponse(Buffer.concat(chunks).toString("utf8")));
25176
25176
  } catch (error61) {
25177
25177
  reject(error61);
25178
25178
  } finally {
@@ -25326,12 +25326,12 @@ var BotOutboundIpcServer = class {
25326
25326
  }
25327
25327
  });
25328
25328
  }
25329
- await new Promise((resolve5, reject) => {
25329
+ await new Promise((resolve6, reject) => {
25330
25330
  const fail = (error61) => reject(error61);
25331
25331
  this.#server.once("error", fail);
25332
25332
  this.#server.listen(this.#socketPath, () => {
25333
25333
  this.#server.removeListener("error", fail);
25334
- resolve5();
25334
+ resolve6();
25335
25335
  });
25336
25336
  });
25337
25337
  if (this.#usesFilesystemSocket) {
@@ -25344,10 +25344,10 @@ var BotOutboundIpcServer = class {
25344
25344
  };
25345
25345
  close = async () => {
25346
25346
  if (this.#server.listening) {
25347
- await new Promise((resolve5, reject) => {
25347
+ await new Promise((resolve6, reject) => {
25348
25348
  this.#server.close((error61) => {
25349
25349
  if (error61 === void 0) {
25350
- resolve5();
25350
+ resolve6();
25351
25351
  return;
25352
25352
  }
25353
25353
  reject(error61);
@@ -27270,7 +27270,9 @@ var IDENTIFIER_PATTERN6 = /^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$/;
27270
27270
  var TOKEN_PATTERN2 = /^[a-zA-Z0-9_-]{32,256}$/;
27271
27271
  var MAX_CONNECTION_FILE_LENGTH = 16 * 1024;
27272
27272
  var CLAIM_RESPONSE_KEYS = ["channelId", "daemonToken", "runtimeId"];
27273
- var SAVED_CONNECTION_KEYS = ["defaultWorkspace", "channelId", "daemonToken", "relayOrigin"];
27273
+ var REDEEM_RESPONSE_KEYS = ["channelId", "runtimeCredential", "runtimeId"];
27274
+ var LEGACY_SAVED_CONNECTION_KEYS = ["defaultWorkspace", "channelId", "daemonToken", "relayOrigin"];
27275
+ var SAVED_CONNECTION_KEYS = ["defaultWorkspace", "channelId", "relayOrigin", "runtimeCredential"];
27274
27276
  var PairingClientError = class extends Error {
27275
27277
  code;
27276
27278
  constructor(code, message, options = {}) {
@@ -27307,7 +27309,7 @@ function readSingleOption(arguments_, option2) {
27307
27309
  function assertKnownArguments(arguments_) {
27308
27310
  for (let index = 0; index < arguments_.length; index += 2) {
27309
27311
  const option2 = arguments_[index];
27310
- if (option2 !== "--url" && option2 !== "--code" && option2 !== "--workspace") {
27312
+ if (option2 !== "--url" && option2 !== "--code" && option2 !== "--pairing" && option2 !== "--workspace") {
27311
27313
  throw new PairingClientError("invalid_arguments", `Unknown pairing option: ${option2 ?? "missing option"}`);
27312
27314
  }
27313
27315
  }
@@ -27347,10 +27349,17 @@ function parsePairCommand(arguments_) {
27347
27349
  assertKnownArguments(arguments_);
27348
27350
  return {
27349
27351
  code: parsePairingCode(readSingleOption(arguments_, "--code")),
27352
+ pairingId: arguments_.includes("--pairing") ? validatePairingId(readSingleOption(arguments_, "--pairing")) : "",
27350
27353
  relayOrigin: parseRelayOrigin(readSingleOption(arguments_, "--url")),
27351
27354
  workspace: arguments_.includes("--workspace") ? readSingleOption(arguments_, "--workspace") : loadDefaultWorkspace()
27352
27355
  };
27353
27356
  }
27357
+ function validatePairingId(value) {
27358
+ if (!IDENTIFIER_PATTERN6.test(value)) {
27359
+ throw new PairingClientError("invalid_pairing_id", "The Runtime pairing ID is invalid");
27360
+ }
27361
+ return value;
27362
+ }
27354
27363
  function parseChannelArgument(arguments_) {
27355
27364
  if (arguments_.length !== 2 || arguments_[0] !== "--channel") {
27356
27365
  throw new PairingClientError("invalid_arguments", "Usage: mobius start --channel <channel-id>");
@@ -27407,17 +27416,17 @@ function readRelayError(value) {
27407
27416
  }
27408
27417
  return new PairingClientError("pairing_failed", "The relay could not pair this machine");
27409
27418
  }
27410
- function decodeClaimResponse(value) {
27411
- if (!isRecord11(value) || !hasExactKeys(value, CLAIM_RESPONSE_KEYS)) {
27419
+ function decodePairingResponse(value, credentialField, expectedKeys) {
27420
+ if (!isRecord11(value) || !hasExactKeys(value, expectedKeys)) {
27412
27421
  throw new PairingClientError("invalid_relay_response", "The relay returned an invalid pairing response");
27413
27422
  }
27414
27423
  const channelId = value["channelId"];
27415
- const daemonToken2 = value["daemonToken"];
27424
+ const runtimeCredential = value[credentialField];
27416
27425
  const runtimeId = value["runtimeId"];
27417
- if (typeof channelId !== "string" || !IDENTIFIER_PATTERN6.test(channelId) || typeof daemonToken2 !== "string" || !TOKEN_PATTERN2.test(daemonToken2) || typeof runtimeId !== "string" || !IDENTIFIER_PATTERN6.test(runtimeId)) {
27426
+ if (typeof channelId !== "string" || !IDENTIFIER_PATTERN6.test(channelId) || typeof runtimeCredential !== "string" || !TOKEN_PATTERN2.test(runtimeCredential) || typeof runtimeId !== "string" || !IDENTIFIER_PATTERN6.test(runtimeId)) {
27418
27427
  throw new PairingClientError("invalid_relay_response", "The relay returned an invalid pairing response");
27419
27428
  }
27420
- return { channelId, daemonToken: daemonToken2, runtimeId };
27429
+ return { channelId, runtimeCredential, runtimeId };
27421
27430
  }
27422
27431
  async function claimPairing(options, defaultWorkspace, localAgents, installableAgents, signal) {
27423
27432
  const agents = localAgents.map((agent) => ({
@@ -27427,7 +27436,7 @@ async function claimPairing(options, defaultWorkspace, localAgents, installableA
27427
27436
  id: agent.id,
27428
27437
  label: agent.label
27429
27438
  }));
27430
- const endpoint = mobiusApiUrl(options.relayOrigin, "/pairings/claim");
27439
+ const endpoint = options.pairingId.length > 0 ? mobiusApiUrl(options.relayOrigin, `/v1/runtime-pairings/${encodeURIComponent(options.pairingId)}/redeem`) : mobiusApiUrl(options.relayOrigin, "/pairings/claim");
27431
27440
  let response;
27432
27441
  try {
27433
27442
  response = await fetch3(endpoint, {
@@ -27450,10 +27459,10 @@ async function claimPairing(options, defaultWorkspace, localAgents, installableA
27450
27459
  if (!response.ok) {
27451
27460
  throw readRelayError(value);
27452
27461
  }
27453
- return decodeClaimResponse(value);
27462
+ return options.pairingId.length > 0 ? decodePairingResponse(value, "runtimeCredential", REDEEM_RESPONSE_KEYS) : decodePairingResponse(value, "daemonToken", CLAIM_RESPONSE_KEYS);
27454
27463
  }
27455
- async function persistConnection(channelId, relayOrigin, daemonToken2, defaultWorkspace) {
27456
- const connectionsDirectory = join14(readStateDirectory(), "connections");
27464
+ async function persistConnection(channelId, relayOrigin, runtimeCredential, defaultWorkspace, environment) {
27465
+ const connectionsDirectory = join14(readStateDirectory(environment), "connections");
27457
27466
  await mkdir4(connectionsDirectory, { mode: 448, recursive: true });
27458
27467
  const connectionPath = join14(connectionsDirectory, `${channelId}.json`);
27459
27468
  const temporaryPath = join14(connectionsDirectory, `.${channelId}.${randomUUID3()}.tmp`);
@@ -27463,8 +27472,8 @@ async function persistConnection(channelId, relayOrigin, daemonToken2, defaultWo
27463
27472
  {
27464
27473
  defaultWorkspace,
27465
27474
  channelId,
27466
- daemonToken: daemonToken2,
27467
- relayOrigin: relayOrigin.toString()
27475
+ relayOrigin: relayOrigin.toString(),
27476
+ runtimeCredential
27468
27477
  },
27469
27478
  null,
27470
27479
  2
@@ -27476,25 +27485,25 @@ async function persistConnection(channelId, relayOrigin, daemonToken2, defaultWo
27476
27485
  return connectionPath;
27477
27486
  }
27478
27487
  function decodeSavedConnection(value, expectedChannelId) {
27479
- if (!isRecord11(value) || !hasExactKeys(value, SAVED_CONNECTION_KEYS)) {
27488
+ if (!isRecord11(value) || !hasExactKeys(value, SAVED_CONNECTION_KEYS) && !hasExactKeys(value, LEGACY_SAVED_CONNECTION_KEYS)) {
27480
27489
  throw new PairingClientError("invalid_connection", "The saved connection is invalid");
27481
27490
  }
27482
27491
  const defaultWorkspace = value["defaultWorkspace"];
27483
27492
  const channelId = value["channelId"];
27484
- const daemonToken2 = value["daemonToken"];
27493
+ const runtimeCredential = value["runtimeCredential"] ?? value["daemonToken"];
27485
27494
  const relayOrigin = value["relayOrigin"];
27486
- if (typeof defaultWorkspace !== "string" || typeof channelId !== "string" || channelId !== expectedChannelId || !IDENTIFIER_PATTERN6.test(channelId) || typeof daemonToken2 !== "string" || !TOKEN_PATTERN2.test(daemonToken2) || typeof relayOrigin !== "string") {
27495
+ if (typeof defaultWorkspace !== "string" || typeof channelId !== "string" || channelId !== expectedChannelId || !IDENTIFIER_PATTERN6.test(channelId) || typeof runtimeCredential !== "string" || !TOKEN_PATTERN2.test(runtimeCredential) || typeof relayOrigin !== "string") {
27487
27496
  throw new PairingClientError("invalid_connection", "The saved connection is invalid");
27488
27497
  }
27489
27498
  return {
27490
27499
  defaultWorkspace,
27491
27500
  channelId,
27492
- daemonToken: daemonToken2,
27493
- relayOrigin: parseRelayOrigin(relayOrigin)
27501
+ relayOrigin: parseRelayOrigin(relayOrigin),
27502
+ runtimeCredential
27494
27503
  };
27495
27504
  }
27496
- async function readSavedConnection(channelId) {
27497
- const connectionPath = join14(readStateDirectory(), "connections", `${channelId}.json`);
27505
+ async function readSavedConnection(channelId, environment) {
27506
+ const connectionPath = join14(readStateDirectory(environment), "connections", `${channelId}.json`);
27498
27507
  let encoded = "";
27499
27508
  const handle = await open4(connectionPath, "r");
27500
27509
  try {
@@ -27516,8 +27525,8 @@ async function readSavedConnection(channelId) {
27516
27525
  return {
27517
27526
  defaultWorkspace: await resolveDefaultWorkspace(connection.defaultWorkspace),
27518
27527
  channelId: connection.channelId,
27519
- daemonToken: connection.daemonToken,
27520
- relayOrigin: connection.relayOrigin
27528
+ relayOrigin: connection.relayOrigin,
27529
+ runtimeCredential: connection.runtimeCredential
27521
27530
  };
27522
27531
  }
27523
27532
  function assertInventoryUsable(inventory) {
@@ -27525,15 +27534,12 @@ function assertInventoryUsable(inventory) {
27525
27534
  throw new PairingClientError("no_supported_agent", "No supported Agent is available for local discovery");
27526
27535
  }
27527
27536
  }
27528
- function buildPairedDaemon(connection, connectionPath, inventory) {
27537
+ function buildPairedDaemon(connection, connectionPath, inventory, environment) {
27529
27538
  assertInventoryUsable(inventory);
27530
- const relayUrl = mobiusApiUrl(
27531
- connection.relayOrigin,
27532
- `/channels/${encodeURIComponent(connection.channelId)}/daemon`
27533
- );
27539
+ const relayUrl = mobiusApiUrl(connection.relayOrigin, "/v1/runtimes/connect");
27534
27540
  relayUrl.protocol = relayUrl.protocol === "https:" ? "wss:" : "ws:";
27535
- relayUrl.searchParams.set("token", connection.daemonToken);
27536
- const auditDatabasePath = join14(readStateDirectory(), "audit", `${connection.channelId}.sqlite`);
27541
+ relayUrl.searchParams.set("token", connection.runtimeCredential);
27542
+ const auditDatabasePath = join14(readStateDirectory(environment), "audit", `${connection.channelId}.sqlite`);
27537
27543
  return {
27538
27544
  channelUrl: new URL(`/channels/${encodeURIComponent(connection.channelId)}`, connection.relayOrigin).toString(),
27539
27545
  config: createDaemonConfig(
@@ -27549,12 +27555,11 @@ function buildPairedDaemon(connection, connectionPath, inventory) {
27549
27555
  connectionPath
27550
27556
  };
27551
27557
  }
27552
- async function pairLocalMachine(arguments_, signal = new AbortController().signal) {
27553
- const options = parsePairCommand(arguments_);
27558
+ async function pairRuntime(options, signal, environment) {
27554
27559
  const defaultWorkspace = await resolveDefaultWorkspace(options.workspace);
27555
27560
  const inventory = await discoverLocalAgentInventory({
27556
27561
  defaultWorkspace,
27557
- environment: process.env,
27562
+ environment,
27558
27563
  signal
27559
27564
  });
27560
27565
  assertInventoryUsable(inventory);
@@ -27563,30 +27568,50 @@ async function pairLocalMachine(arguments_, signal = new AbortController().signa
27563
27568
  const connectionPath = await persistConnection(
27564
27569
  claim2.channelId,
27565
27570
  options.relayOrigin,
27566
- claim2.daemonToken,
27567
- defaultWorkspace
27571
+ claim2.runtimeCredential,
27572
+ defaultWorkspace,
27573
+ environment
27568
27574
  );
27569
27575
  return buildPairedDaemon(
27570
27576
  {
27571
27577
  defaultWorkspace,
27572
27578
  channelId: claim2.channelId,
27573
- daemonToken: claim2.daemonToken,
27574
- relayOrigin: options.relayOrigin
27579
+ relayOrigin: options.relayOrigin,
27580
+ runtimeCredential: claim2.runtimeCredential
27575
27581
  },
27576
27582
  connectionPath,
27577
- inventory
27583
+ inventory,
27584
+ environment
27578
27585
  );
27579
27586
  }
27580
- async function resumeLocalMachine(arguments_, signal = new AbortController().signal) {
27581
- const channelId = parseChannelArgument(arguments_);
27582
- const connectionPath = join14(readStateDirectory(), "connections", `${channelId}.json`);
27583
- const connection = await readSavedConnection(channelId);
27587
+ async function pairMobiusRuntime(options, signal, environment = process.env) {
27588
+ return await pairRuntime(
27589
+ {
27590
+ code: parsePairingCode(options.pairingCode),
27591
+ pairingId: validatePairingId(options.pairingId),
27592
+ relayOrigin: parseRelayOrigin(options.relayOrigin),
27593
+ workspace: options.defaultWorkspace
27594
+ },
27595
+ signal,
27596
+ environment
27597
+ );
27598
+ }
27599
+ async function pairLocalMachine(arguments_, signal = new AbortController().signal) {
27600
+ return await pairRuntime(parsePairCommand(arguments_), signal, process.env);
27601
+ }
27602
+ async function resumeMobiusRuntime(channelId, signal, environment = process.env) {
27603
+ const normalizedChannelId = validatePairingId(channelId);
27604
+ const connectionPath = join14(readStateDirectory(environment), "connections", `${normalizedChannelId}.json`);
27605
+ const connection = await readSavedConnection(normalizedChannelId, environment);
27584
27606
  const inventory = await discoverLocalAgentInventory({
27585
27607
  defaultWorkspace: connection.defaultWorkspace,
27586
- environment: process.env,
27608
+ environment,
27587
27609
  signal
27588
27610
  });
27589
- return buildPairedDaemon(connection, connectionPath, inventory);
27611
+ return buildPairedDaemon(connection, connectionPath, inventory, environment);
27612
+ }
27613
+ async function resumeLocalMachine(arguments_, signal = new AbortController().signal) {
27614
+ return await resumeMobiusRuntime(parseChannelArgument(arguments_), signal, process.env);
27590
27615
  }
27591
27616
 
27592
27617
  // src/run-daemon.ts
@@ -27605,14 +27630,19 @@ var AgentCatalogPublishError = class extends Error {
27605
27630
  this.name = "AgentCatalogPublishError";
27606
27631
  }
27607
27632
  };
27608
- function catalogEndpoint(relayUrl) {
27609
- const endpoint = new URL(relayUrl);
27633
+ function catalogEndpoint(config2) {
27634
+ const endpoint = new URL(config2.relayUrl);
27610
27635
  endpoint.protocol = endpoint.protocol === "wss:" ? "https:" : "http:";
27611
- if (!endpoint.pathname.endsWith("/daemon")) {
27612
- throw new AgentCatalogPublishError("The Relay URL is invalid");
27636
+ if (endpoint.pathname.endsWith("/daemon")) {
27637
+ endpoint.pathname = `${endpoint.pathname.slice(0, -"/daemon".length)}/catalog`;
27638
+ return endpoint;
27613
27639
  }
27614
- endpoint.pathname = `${endpoint.pathname.slice(0, -"/daemon".length)}/catalog`;
27615
- return endpoint;
27640
+ const runtimePath = "/v1/runtimes/connect";
27641
+ if (endpoint.pathname.endsWith(runtimePath)) {
27642
+ endpoint.pathname = `${endpoint.pathname.slice(0, -runtimePath.length)}/channels/${encodeURIComponent(config2.channelId)}/catalog`;
27643
+ return endpoint;
27644
+ }
27645
+ throw new AgentCatalogPublishError("The Relay URL is invalid");
27616
27646
  }
27617
27647
  async function publishAgentCatalog(config2, signal) {
27618
27648
  signal.throwIfAborted();
@@ -27621,7 +27651,7 @@ async function publishAgentCatalog(config2, signal) {
27621
27651
  let response;
27622
27652
  const catalog = config2.catalog.read();
27623
27653
  try {
27624
- response = await fetch4(catalogEndpoint(config2.relayUrl), {
27654
+ response = await fetch4(catalogEndpoint(config2), {
27625
27655
  body: JSON.stringify({
27626
27656
  agents: catalog.agents.map((agent) => ({
27627
27657
  agentPath: agent.agentIdentity.requestedPath,
@@ -28991,10 +29021,17 @@ function parseReference(resource) {
28991
29021
  function downloadUrl(config2, command, reference) {
28992
29022
  const url2 = new URL(config2.relayUrl);
28993
29023
  url2.protocol = url2.protocol === "wss:" ? "https:" : "http:";
28994
- if (!url2.pathname.endsWith("/daemon")) {
28995
- throw new PromptArtifactError("artifact_endpoint_invalid", "The Relay artifact endpoint is invalid");
29024
+ let channelPath = "";
29025
+ if (url2.pathname.endsWith("/daemon")) {
29026
+ channelPath = url2.pathname.slice(0, -"/daemon".length);
29027
+ } else {
29028
+ const runtimePath = "/v1/runtimes/connect";
29029
+ if (!url2.pathname.endsWith(runtimePath)) {
29030
+ throw new PromptArtifactError("artifact_endpoint_invalid", "The Relay artifact endpoint is invalid");
29031
+ }
29032
+ channelPath = `${url2.pathname.slice(0, -runtimePath.length)}/channels/${encodeURIComponent(config2.channelId)}`;
28996
29033
  }
28997
- url2.pathname = `${url2.pathname.slice(0, -"/daemon".length)}/sessions/${encodeURIComponent(command.sessionId)}/commands/${encodeURIComponent(command.commandId)}/artifacts/${encodeURIComponent(reference.artifactId)}/content`;
29034
+ url2.pathname = `${channelPath}/sessions/${encodeURIComponent(command.sessionId)}/commands/${encodeURIComponent(command.commandId)}/artifacts/${encodeURIComponent(reference.artifactId)}/content`;
28998
29035
  url2.searchParams.delete("liveness");
28999
29036
  return url2;
29000
29037
  }
@@ -29152,9 +29189,17 @@ var RelayConnectionError = class extends Error {
29152
29189
  };
29153
29190
  function connectRelay(url2, signal) {
29154
29191
  signal.throwIfAborted();
29155
- return new Promise((resolve5, reject) => {
29156
- const socket = new WebSocket(url2, {
29157
- dispatcher: environmentProxyDispatcher
29192
+ return new Promise((resolve6, reject) => {
29193
+ const endpoint = new URL(url2);
29194
+ const headers = {};
29195
+ if (endpoint.pathname.endsWith("/v1/runtimes/connect")) {
29196
+ const runtimeCredential = endpoint.searchParams.get("token") ?? "";
29197
+ endpoint.searchParams.delete("token");
29198
+ headers["authorization"] = `Bearer ${runtimeCredential}`;
29199
+ }
29200
+ const socket = new WebSocket(endpoint, {
29201
+ dispatcher: environmentProxyDispatcher,
29202
+ headers
29158
29203
  });
29159
29204
  let finished = false;
29160
29205
  const timeout = setTimeout(onTimeout, CONNECTION_TIMEOUT_MILLISECONDS);
@@ -29193,7 +29238,7 @@ function connectRelay(url2, signal) {
29193
29238
  }
29194
29239
  finished = true;
29195
29240
  cleanup();
29196
- resolve5(socket);
29241
+ resolve6(socket);
29197
29242
  };
29198
29243
  function onTimeout() {
29199
29244
  fail(new RelayConnectionError("relay_connection_timeout", "Relay connection timed out"));
@@ -29775,10 +29820,17 @@ var ActiveTurnReconciliationError = class extends Error {
29775
29820
  function activeTurnReconciliationEndpoint(config2, turn) {
29776
29821
  const endpoint = new URL(config2.relayUrl);
29777
29822
  endpoint.protocol = endpoint.protocol === "wss:" ? "https:" : "http:";
29778
- if (!endpoint.pathname.endsWith("/daemon")) {
29779
- throw new ActiveTurnReconciliationError("The Relay URL is invalid");
29823
+ let channelPath = "";
29824
+ if (endpoint.pathname.endsWith("/daemon")) {
29825
+ channelPath = endpoint.pathname.slice(0, -"/daemon".length);
29826
+ } else {
29827
+ const runtimePath = "/v1/runtimes/connect";
29828
+ if (!endpoint.pathname.endsWith(runtimePath)) {
29829
+ throw new ActiveTurnReconciliationError("The Relay URL is invalid");
29830
+ }
29831
+ channelPath = `${endpoint.pathname.slice(0, -runtimePath.length)}/channels/${encodeURIComponent(config2.channelId)}`;
29780
29832
  }
29781
- endpoint.pathname = `${endpoint.pathname.slice(0, -"/daemon".length)}/sessions/${encodeURIComponent(turn.command.sessionId)}/attempts/${encodeURIComponent(turn.attemptId)}/reconciliation`;
29833
+ endpoint.pathname = `${channelPath}/sessions/${encodeURIComponent(turn.command.sessionId)}/attempts/${encodeURIComponent(turn.attemptId)}/reconciliation`;
29782
29834
  endpoint.searchParams.delete("liveness");
29783
29835
  return endpoint;
29784
29836
  }
@@ -30391,12 +30443,12 @@ function waitForRelayRetry(signal, timer) {
30391
30443
  if (signal.aborted) {
30392
30444
  return Promise.resolve();
30393
30445
  }
30394
- return new Promise((resolve5) => {
30446
+ return new Promise((resolve6) => {
30395
30447
  const retry = timer.schedule(finish, RELAY_RECONNECT_DELAY_MILLISECONDS);
30396
30448
  function finish() {
30397
30449
  retry.cancel();
30398
30450
  signal.removeEventListener("abort", finish);
30399
- resolve5();
30451
+ resolve6();
30400
30452
  }
30401
30453
  signal.addEventListener("abort", finish, { once: true });
30402
30454
  });
@@ -30433,7 +30485,7 @@ async function runRelayConnection(config2, directories, audit, sessions, install
30433
30485
  const nativeRequests = /* @__PURE__ */ new Map();
30434
30486
  installationObserver.connect(socket);
30435
30487
  try {
30436
- await new Promise((resolve5, reject) => {
30488
+ await new Promise((resolve6, reject) => {
30437
30489
  let finished = false;
30438
30490
  const cleanup = () => {
30439
30491
  connectionRefresh.close();
@@ -30452,7 +30504,7 @@ async function runRelayConnection(config2, directories, audit, sessions, install
30452
30504
  }
30453
30505
  finished = true;
30454
30506
  cleanup();
30455
- resolve5();
30507
+ resolve6();
30456
30508
  };
30457
30509
  const rejectConnection = () => {
30458
30510
  if (finished) {
@@ -30632,7 +30684,7 @@ async function runRelayConnection(config2, directories, audit, sessions, install
30632
30684
  try {
30633
30685
  operationSignal.throwIfAborted();
30634
30686
  if (previous.length > 0) {
30635
- await new Promise((resolve6, reject2) => {
30687
+ await new Promise((resolve7, reject2) => {
30636
30688
  const cancel = () => reject2(operationSignal.reason);
30637
30689
  operationSignal.addEventListener("abort", cancel, {
30638
30690
  once: true
@@ -30640,7 +30692,7 @@ async function runRelayConnection(config2, directories, audit, sessions, install
30640
30692
  void Promise.all(previous.map((request) => request.completion)).then(
30641
30693
  () => {
30642
30694
  operationSignal.removeEventListener("abort", cancel);
30643
- resolve6();
30695
+ resolve7();
30644
30696
  },
30645
30697
  (error61) => {
30646
30698
  operationSignal.removeEventListener("abort", cancel);
@@ -30714,13 +30766,13 @@ async function runRelayConnection(config2, directories, audit, sessions, install
30714
30766
  try {
30715
30767
  operationSignal.throwIfAborted();
30716
30768
  if (previous.length > 0) {
30717
- await new Promise((resolve6, reject2) => {
30769
+ await new Promise((resolve7, reject2) => {
30718
30770
  const cancel = () => reject2(operationSignal.reason);
30719
30771
  operationSignal.addEventListener("abort", cancel, { once: true });
30720
30772
  void Promise.all(previous.map((request) => request.completion)).then(
30721
30773
  () => {
30722
30774
  operationSignal.removeEventListener("abort", cancel);
30723
- resolve6();
30775
+ resolve7();
30724
30776
  },
30725
30777
  (error61) => {
30726
30778
  operationSignal.removeEventListener("abort", cancel);
@@ -31102,6 +31154,147 @@ async function runDaemon(config2, audit, signal, timer = systemDaemonTimer) {
31102
31154
  }
31103
31155
  }
31104
31156
  }
31157
+
31158
+ // src/runtime.ts
31159
+ import { mkdir as mkdir5, realpath as realpath8, stat as stat8 } from "node:fs/promises";
31160
+ import { dirname as dirname8, join as join17, resolve as resolve5 } from "node:path";
31161
+ var APP_CREDENTIAL_PATTERN = /^mapp_[a-zA-Z0-9_-]{22}_[a-zA-Z0-9_-]{43}$/;
31162
+ var CONVERSATION_CREDENTIAL_PATTERN = /^mconv_[a-zA-Z0-9_-]{22}_[a-zA-Z0-9_-]{43}$/;
31163
+ var IDENTIFIER_PATTERN8 = /^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$/;
31164
+ var RUNTIME_CREDENTIAL_PATTERN = /^[a-zA-Z0-9_-]{32,256}$/;
31165
+ var MobiusRuntimeConfigurationError = class extends Error {
31166
+ code = "runtime_configuration_invalid";
31167
+ constructor(message, options = {}) {
31168
+ super(message, options);
31169
+ this.name = "MobiusRuntimeConfigurationError";
31170
+ }
31171
+ };
31172
+ var MobiusRuntimeState = class {
31173
+ };
31174
+ var IdleMobiusRuntimeState = class extends MobiusRuntimeState {
31175
+ };
31176
+ var RunningMobiusRuntimeState = class extends MobiusRuntimeState {
31177
+ };
31178
+ var FinishedMobiusRuntimeState = class extends MobiusRuntimeState {
31179
+ };
31180
+ function validateRuntimeRelay(relayUrl) {
31181
+ if (relayUrl.protocol !== "ws:" && relayUrl.protocol !== "wss:") {
31182
+ throw new MobiusRuntimeConfigurationError("The Runtime relay URL must use WebSocket transport");
31183
+ }
31184
+ const credential = relayUrl.searchParams.get("token") ?? "";
31185
+ if (credential.length === 0) {
31186
+ throw new MobiusRuntimeConfigurationError("The Runtime relay credential is required");
31187
+ }
31188
+ if (APP_CREDENTIAL_PATTERN.test(credential)) {
31189
+ throw new MobiusRuntimeConfigurationError("An AppCredential cannot be held by a local Runtime");
31190
+ }
31191
+ if (CONVERSATION_CREDENTIAL_PATTERN.test(credential)) {
31192
+ throw new MobiusRuntimeConfigurationError("A ConversationAccessToken cannot be held by a local Runtime");
31193
+ }
31194
+ if (!RUNTIME_CREDENTIAL_PATTERN.test(credential)) {
31195
+ throw new MobiusRuntimeConfigurationError("The Runtime relay credential is invalid");
31196
+ }
31197
+ }
31198
+ function validateRuntimeConfiguration(config2) {
31199
+ validateRuntimeRelay(config2.relayUrl);
31200
+ }
31201
+ function requiredEnvironmentValue(environment, name) {
31202
+ const value = environment[name]?.trim();
31203
+ if (value === void 0 || value.length === 0) {
31204
+ throw new MobiusRuntimeConfigurationError(`${name} is required`);
31205
+ }
31206
+ return value;
31207
+ }
31208
+ function relayUrlFromConnection(connection) {
31209
+ let relayUrl;
31210
+ try {
31211
+ relayUrl = new URL(connection.relayUrl);
31212
+ } catch (error61) {
31213
+ throw new MobiusRuntimeConfigurationError("The Runtime relay URL is invalid", { cause: error61 });
31214
+ }
31215
+ relayUrl.searchParams.set("token", connection.runtimeCredential);
31216
+ validateRuntimeRelay(relayUrl);
31217
+ return relayUrl;
31218
+ }
31219
+ function loadMobiusRuntimeConnection(environment = process.env) {
31220
+ const channelId = requiredEnvironmentValue(environment, "MOBIUS_CHANNEL_ID");
31221
+ if (!IDENTIFIER_PATTERN8.test(channelId)) {
31222
+ throw new MobiusRuntimeConfigurationError("MOBIUS_CHANNEL_ID is invalid");
31223
+ }
31224
+ return Object.freeze({
31225
+ auditDatabasePath: environment["MOBIUS_AUDIT_DATABASE"]?.trim() || join17(readStateDirectory(environment), "audit", `${channelId}.sqlite`),
31226
+ channelId,
31227
+ defaultWorkspace: loadDefaultWorkspace(environment),
31228
+ relayUrl: requiredEnvironmentValue(environment, "MOBIUS_RELAY_URL"),
31229
+ runtimeCredential: requiredEnvironmentValue(environment, "MOBIUS_RELAY_TOKEN")
31230
+ });
31231
+ }
31232
+ async function resolveWorkspace(path2) {
31233
+ const requested = resolve5(path2);
31234
+ try {
31235
+ await mkdir5(requested, { recursive: true, mode: 448 });
31236
+ const resolved = await realpath8(requested);
31237
+ const metadata = await stat8(resolved);
31238
+ if (!metadata.isDirectory()) {
31239
+ throw new MobiusRuntimeConfigurationError("The Runtime workspace is not a directory");
31240
+ }
31241
+ return resolved;
31242
+ } catch (error61) {
31243
+ if (error61 instanceof MobiusRuntimeConfigurationError) {
31244
+ throw error61;
31245
+ }
31246
+ throw new MobiusRuntimeConfigurationError("The Runtime workspace cannot be resolved", { cause: error61 });
31247
+ }
31248
+ }
31249
+ async function createMobiusRuntimeEngine(connection, signal, environment = process.env) {
31250
+ signal.throwIfAborted();
31251
+ const relayUrl = relayUrlFromConnection(connection);
31252
+ const defaultWorkspace = await resolveWorkspace(connection.defaultWorkspace);
31253
+ const auditDatabasePath = resolve5(connection.auditDatabasePath);
31254
+ await mkdir5(dirname8(auditDatabasePath), { recursive: true, mode: 448 });
31255
+ const inventory = await discoverLocalAgentInventory({
31256
+ defaultWorkspace,
31257
+ environment,
31258
+ signal
31259
+ });
31260
+ if (inventory.available.length + inventory.installable.length === 0) {
31261
+ throw new MobiusRuntimeConfigurationError("No supported Agent is available for the local Runtime");
31262
+ }
31263
+ return new MobiusRuntimeEngine(
31264
+ createDaemonConfig(
31265
+ {
31266
+ auditDatabasePath,
31267
+ channelId: connection.channelId,
31268
+ defaultWorkspace,
31269
+ relayUrl
31270
+ },
31271
+ inventory.readyAgents(),
31272
+ inventory.installable
31273
+ )
31274
+ );
31275
+ }
31276
+ var MobiusRuntimeEngine = class {
31277
+ #config;
31278
+ #state = new IdleMobiusRuntimeState();
31279
+ constructor(config2) {
31280
+ validateRuntimeConfiguration(config2);
31281
+ this.#config = config2;
31282
+ }
31283
+ run = async (signal) => {
31284
+ if (!(this.#state instanceof IdleMobiusRuntimeState)) {
31285
+ throw new MobiusRuntimeConfigurationError("A Mobius Runtime engine can only be started once");
31286
+ }
31287
+ signal.throwIfAborted();
31288
+ this.#state = new RunningMobiusRuntimeState();
31289
+ const audit = new AuditStore(this.#config.auditDatabasePath);
31290
+ try {
31291
+ await runDaemon(this.#config, audit, signal);
31292
+ } finally {
31293
+ audit.close();
31294
+ this.#state = new FinishedMobiusRuntimeState();
31295
+ }
31296
+ };
31297
+ };
31105
31298
  export {
31106
31299
  AcpExecutionError,
31107
31300
  AuditStore,
@@ -31109,12 +31302,18 @@ export {
31109
31302
  BotMessageCommandError,
31110
31303
  BotOutboundIpcServer,
31111
31304
  LocalAuthorizationError,
31305
+ MobiusRuntimeConfigurationError,
31306
+ MobiusRuntimeEngine,
31112
31307
  PairingClientError,
31113
31308
  createAcpClient,
31309
+ createMobiusRuntimeEngine,
31114
31310
  executeBotMessageCommand,
31115
31311
  loadDaemonConfig,
31312
+ loadMobiusRuntimeConnection,
31116
31313
  pairLocalMachine,
31314
+ pairMobiusRuntime,
31117
31315
  resumeLocalMachine,
31316
+ resumeMobiusRuntime,
31118
31317
  runAcpPrompt,
31119
31318
  runDaemon
31120
31319
  };