@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.
@@ -1 +1 @@
1
- {"version":3,"file":"publish-agent-catalog.d.ts","sourceRoot":"","sources":["../../src/agent-catalog/publish-agent-catalog.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAwBjD,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAoClG"}
1
+ {"version":3,"file":"publish-agent-catalog.d.ts","sourceRoot":"","sources":["../../src/agent-catalog/publish-agent-catalog.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AA6BjD,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAoClG"}
@@ -8,14 +8,19 @@ class AgentCatalogPublishError extends Error {
8
8
  this.name = "AgentCatalogPublishError";
9
9
  }
10
10
  }
11
- function catalogEndpoint(relayUrl) {
12
- const endpoint = new URL(relayUrl);
11
+ function catalogEndpoint(config) {
12
+ const endpoint = new URL(config.relayUrl);
13
13
  endpoint.protocol = endpoint.protocol === "wss:" ? "https:" : "http:";
14
- if (!endpoint.pathname.endsWith("/daemon")) {
15
- throw new AgentCatalogPublishError("The Relay URL is invalid");
14
+ if (endpoint.pathname.endsWith("/daemon")) {
15
+ endpoint.pathname = `${endpoint.pathname.slice(0, -"/daemon".length)}/catalog`;
16
+ return endpoint;
16
17
  }
17
- endpoint.pathname = `${endpoint.pathname.slice(0, -"/daemon".length)}/catalog`;
18
- return endpoint;
18
+ const runtimePath = "/v1/runtimes/connect";
19
+ if (endpoint.pathname.endsWith(runtimePath)) {
20
+ endpoint.pathname = `${endpoint.pathname.slice(0, -runtimePath.length)}/channels/${encodeURIComponent(config.channelId)}/catalog`;
21
+ return endpoint;
22
+ }
23
+ throw new AgentCatalogPublishError("The Relay URL is invalid");
19
24
  }
20
25
  export async function publishAgentCatalog(config, signal) {
21
26
  signal.throwIfAborted();
@@ -24,7 +29,7 @@ export async function publishAgentCatalog(config, signal) {
24
29
  let response;
25
30
  const catalog = config.catalog.read();
26
31
  try {
27
- response = await fetch(catalogEndpoint(config.relayUrl), {
32
+ response = await fetch(catalogEndpoint(config), {
28
33
  body: JSON.stringify({
29
34
  agents: catalog.agents.map((agent) => ({
30
35
  agentPath: agent.agentIdentity.requestedPath,
@@ -1 +1 @@
1
- {"version":3,"file":"publish-agent-catalog.js","sourceRoot":"","sources":["../../src/agent-catalog/publish-agent-catalog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAG/B,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC;AAE7E,MAAM,mCAAmC,GAAG,MAAM,CAAC;AAEnD,MAAM,wBAAyB,SAAQ,KAAK;IAC/B,IAAI,GAAG,6BAA6B,CAAC;IAE9C,YAAY,OAAe,EAAE,OAAO,GAAiB,EAAE;QACnD,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;IAC3C,CAAC;CACJ;AAED,SAAS,eAAe,CAAC,QAAa;IAClC,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;IACnC,QAAQ,CAAC,QAAQ,GAAG,QAAQ,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC;IACtE,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QACzC,MAAM,IAAI,wBAAwB,CAAC,0BAA0B,CAAC,CAAC;IACnE,CAAC;IACD,QAAQ,CAAC,QAAQ,GAAG,GAAG,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC;IAC/E,OAAO,QAAQ,CAAC;AACpB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,MAAoB,EAAE,MAAmB;IAC/E,MAAM,CAAC,cAAc,EAAE,CAAC;IACxB,MAAM,aAAa,GAAG,WAAW,CAAC,OAAO,CAAC,mCAAmC,CAAC,CAAC;IAC/E,MAAM,aAAa,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC;IAC/D,IAAI,QAA2C,CAAC;IAChD,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;IACtC,IAAI,CAAC;QACD,QAAQ,GAAG,MAAM,KAAK,CAAC,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE;YACrD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACjB,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;oBACnC,SAAS,EAAE,KAAK,CAAC,aAAa,CAAC,aAAa;oBAC5C,YAAY,EAAE,KAAK,CAAC,YAAY;oBAChC,YAAY,EAAE,KAAK,CAAC,YAAY;oBAChC,EAAE,EAAE,KAAK,CAAC,EAAE;oBACZ,KAAK,EAAE,KAAK,CAAC,KAAK;iBACrB,CAAC,CAAC;gBACH,iBAAiB,EAAE,OAAO,CAAC,iBAAiB;aAC/C,CAAC;YACF,UAAU,EAAE,0BAA0B;YACtC,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,MAAM,EAAE,KAAK;YACb,MAAM,EAAE,aAAa;SACxB,CAAC,CAAC;IACP,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACjB,MAAM,CAAC,cAAc,EAAE,CAAC;QAC5B,CAAC;QACD,MAAM,IAAI,wBAAwB,CAC9B,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,oCAAoC,CAAC,CAAC,CAAC,wCAAwC,EACvG,EAAE,KAAK,EAAE,KAAK,EAAE,CACnB,CAAC;IACN,CAAC;IACD,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IAC9B,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACf,MAAM,IAAI,wBAAwB,CAAC,6CAA6C,CAAC,CAAC;IACtF,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"publish-agent-catalog.js","sourceRoot":"","sources":["../../src/agent-catalog/publish-agent-catalog.ts"],"names":[],"mappings":"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"}
package/dist/cli.js CHANGED
@@ -11,7 +11,7 @@ import { mkdir as mkdir5 } from "node:fs/promises";
11
11
  // package.json
12
12
  var package_default = {
13
13
  name: "@moxt-ai/mobius",
14
- version: "0.0.24",
14
+ version: "0.0.25",
15
15
  description: "Mobius CLI for pairing and running local AI agent services.",
16
16
  type: "module",
17
17
  bin: {
@@ -21,6 +21,10 @@ var package_default = {
21
21
  ".": {
22
22
  types: "./dist/index.d.ts",
23
23
  import: "./dist/index.js"
24
+ },
25
+ "./runtime": {
26
+ types: "./dist/runtime.d.ts",
27
+ import: "./dist/runtime.js"
24
28
  }
25
29
  },
26
30
  files: [
@@ -38,7 +42,7 @@ var package_default = {
38
42
  directory: "packages/cli"
39
43
  },
40
44
  scripts: {
41
- build: "tsc --project tsconfig.build.json && esbuild src/cli.ts src/index.ts --bundle --platform=node --format=esm --sourcemap --outdir=dist --external:@agentclientprotocol/* --external:undici",
45
+ build: "tsc --project tsconfig.build.json && esbuild src/cli.ts src/index.ts src/runtime.ts --bundle --platform=node --format=esm --sourcemap --outdir=dist --external:@agentclientprotocol/* --external:undici",
42
46
  dev: "node --watch src/cli.ts",
43
47
  prepack: "pnpm build",
44
48
  start: "node dist/cli.js",
@@ -2578,54 +2582,6 @@ async function discoverLocalAgentInventory(options) {
2578
2582
  return new LocalAgentInventory(discoveries);
2579
2583
  }
2580
2584
 
2581
- // src/audit/audit-store.ts
2582
- import { mkdirSync } from "node:fs";
2583
- import { dirname as dirname3 } from "node:path";
2584
- import { DatabaseSync } from "node:sqlite";
2585
- var AuditStore = class {
2586
- database;
2587
- constructor(path2) {
2588
- mkdirSync(dirname3(path2), { recursive: true });
2589
- this.database = new DatabaseSync(path2);
2590
- this.database.exec(`
2591
- CREATE TABLE IF NOT EXISTS audit_events (
2592
- id INTEGER PRIMARY KEY AUTOINCREMENT,
2593
- recorded_at TEXT NOT NULL,
2594
- event TEXT NOT NULL,
2595
- command_id TEXT NOT NULL,
2596
- session_id TEXT NOT NULL,
2597
- details_json TEXT NOT NULL DEFAULT '{}'
2598
- ) STRICT
2599
- `);
2600
- const columns = this.database.prepare("PRAGMA table_info(audit_events)").all();
2601
- if (!columns.some((column) => Reflect.get(column, "name") === "details_json")) {
2602
- this.database.exec("ALTER TABLE audit_events ADD COLUMN details_json TEXT NOT NULL DEFAULT '{}'");
2603
- }
2604
- }
2605
- record(record2) {
2606
- this.database.prepare(
2607
- `
2608
- INSERT INTO audit_events (
2609
- recorded_at,
2610
- event,
2611
- command_id,
2612
- session_id,
2613
- details_json
2614
- ) VALUES (?, ?, ?, ?, ?)
2615
- `
2616
- ).run(
2617
- (/* @__PURE__ */ new Date()).toISOString(),
2618
- record2.event,
2619
- record2.commandId,
2620
- record2.sessionId,
2621
- JSON.stringify(record2.details ?? {})
2622
- );
2623
- }
2624
- close() {
2625
- this.database.close();
2626
- }
2627
- };
2628
-
2629
2585
  // src/bots/bot-message-command.ts
2630
2586
  import { randomUUID } from "node:crypto";
2631
2587
  import { createConnection } from "node:net";
@@ -23254,9 +23210,9 @@ var decodeSessionEventAcknowledgement = (encoded) => decode5(encoded, eventAckno
23254
23210
  var decodeSessionEventRejection = (encoded) => decode5(encoded, eventRejectionSchema);
23255
23211
 
23256
23212
  // src/config.ts
23257
- function loadDefaultWorkspace() {
23258
- const { MOBIUS_DEFAULT_WORKSPACE: configuredWorkspace } = process.env;
23259
- return configuredWorkspace ?? join13(readStateDirectory(), "workspace");
23213
+ function loadDefaultWorkspace(environment = process.env) {
23214
+ const { MOBIUS_DEFAULT_WORKSPACE: configuredWorkspace } = environment;
23215
+ return configuredWorkspace ?? join13(readStateDirectory(environment), "workspace");
23260
23216
  }
23261
23217
  function createDaemonConfig(connection, agents = [], installableAgents = []) {
23262
23218
  return {
@@ -23283,7 +23239,9 @@ var IDENTIFIER_PATTERN5 = /^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$/;
23283
23239
  var TOKEN_PATTERN2 = /^[a-zA-Z0-9_-]{32,256}$/;
23284
23240
  var MAX_CONNECTION_FILE_LENGTH = 16 * 1024;
23285
23241
  var CLAIM_RESPONSE_KEYS = ["channelId", "daemonToken", "runtimeId"];
23286
- var SAVED_CONNECTION_KEYS = ["defaultWorkspace", "channelId", "daemonToken", "relayOrigin"];
23242
+ var REDEEM_RESPONSE_KEYS = ["channelId", "runtimeCredential", "runtimeId"];
23243
+ var LEGACY_SAVED_CONNECTION_KEYS = ["defaultWorkspace", "channelId", "daemonToken", "relayOrigin"];
23244
+ var SAVED_CONNECTION_KEYS = ["defaultWorkspace", "channelId", "relayOrigin", "runtimeCredential"];
23287
23245
  var PairingClientError = class extends Error {
23288
23246
  code;
23289
23247
  constructor(code, message, options = {}) {
@@ -23320,7 +23278,7 @@ function readSingleOption(arguments_, option2) {
23320
23278
  function assertKnownArguments(arguments_) {
23321
23279
  for (let index = 0; index < arguments_.length; index += 2) {
23322
23280
  const option2 = arguments_[index];
23323
- if (option2 !== "--url" && option2 !== "--code" && option2 !== "--workspace") {
23281
+ if (option2 !== "--url" && option2 !== "--code" && option2 !== "--pairing" && option2 !== "--workspace") {
23324
23282
  throw new PairingClientError("invalid_arguments", `Unknown pairing option: ${option2 ?? "missing option"}`);
23325
23283
  }
23326
23284
  }
@@ -23360,10 +23318,17 @@ function parsePairCommand(arguments_) {
23360
23318
  assertKnownArguments(arguments_);
23361
23319
  return {
23362
23320
  code: parsePairingCode(readSingleOption(arguments_, "--code")),
23321
+ pairingId: arguments_.includes("--pairing") ? validatePairingId(readSingleOption(arguments_, "--pairing")) : "",
23363
23322
  relayOrigin: parseRelayOrigin(readSingleOption(arguments_, "--url")),
23364
23323
  workspace: arguments_.includes("--workspace") ? readSingleOption(arguments_, "--workspace") : loadDefaultWorkspace()
23365
23324
  };
23366
23325
  }
23326
+ function validatePairingId(value) {
23327
+ if (!IDENTIFIER_PATTERN5.test(value)) {
23328
+ throw new PairingClientError("invalid_pairing_id", "The Runtime pairing ID is invalid");
23329
+ }
23330
+ return value;
23331
+ }
23367
23332
  function parseChannelArgument(arguments_) {
23368
23333
  if (arguments_.length !== 2 || arguments_[0] !== "--channel") {
23369
23334
  throw new PairingClientError("invalid_arguments", "Usage: mobius start --channel <channel-id>");
@@ -23420,17 +23385,17 @@ function readRelayError(value) {
23420
23385
  }
23421
23386
  return new PairingClientError("pairing_failed", "The relay could not pair this machine");
23422
23387
  }
23423
- function decodeClaimResponse(value) {
23424
- if (!isRecord7(value) || !hasExactKeys(value, CLAIM_RESPONSE_KEYS)) {
23388
+ function decodePairingResponse(value, credentialField, expectedKeys) {
23389
+ if (!isRecord7(value) || !hasExactKeys(value, expectedKeys)) {
23425
23390
  throw new PairingClientError("invalid_relay_response", "The relay returned an invalid pairing response");
23426
23391
  }
23427
23392
  const channelId = value["channelId"];
23428
- const daemonToken2 = value["daemonToken"];
23393
+ const runtimeCredential = value[credentialField];
23429
23394
  const runtimeId = value["runtimeId"];
23430
- if (typeof channelId !== "string" || !IDENTIFIER_PATTERN5.test(channelId) || typeof daemonToken2 !== "string" || !TOKEN_PATTERN2.test(daemonToken2) || typeof runtimeId !== "string" || !IDENTIFIER_PATTERN5.test(runtimeId)) {
23395
+ if (typeof channelId !== "string" || !IDENTIFIER_PATTERN5.test(channelId) || typeof runtimeCredential !== "string" || !TOKEN_PATTERN2.test(runtimeCredential) || typeof runtimeId !== "string" || !IDENTIFIER_PATTERN5.test(runtimeId)) {
23431
23396
  throw new PairingClientError("invalid_relay_response", "The relay returned an invalid pairing response");
23432
23397
  }
23433
- return { channelId, daemonToken: daemonToken2, runtimeId };
23398
+ return { channelId, runtimeCredential, runtimeId };
23434
23399
  }
23435
23400
  async function claimPairing(options, defaultWorkspace, localAgents, installableAgents, signal) {
23436
23401
  const agents = localAgents.map((agent) => ({
@@ -23440,7 +23405,7 @@ async function claimPairing(options, defaultWorkspace, localAgents, installableA
23440
23405
  id: agent.id,
23441
23406
  label: agent.label
23442
23407
  }));
23443
- const endpoint = mobiusApiUrl(options.relayOrigin, "/pairings/claim");
23408
+ const endpoint = options.pairingId.length > 0 ? mobiusApiUrl(options.relayOrigin, `/v1/runtime-pairings/${encodeURIComponent(options.pairingId)}/redeem`) : mobiusApiUrl(options.relayOrigin, "/pairings/claim");
23444
23409
  let response;
23445
23410
  try {
23446
23411
  response = await fetch2(endpoint, {
@@ -23463,10 +23428,10 @@ async function claimPairing(options, defaultWorkspace, localAgents, installableA
23463
23428
  if (!response.ok) {
23464
23429
  throw readRelayError(value);
23465
23430
  }
23466
- return decodeClaimResponse(value);
23431
+ return options.pairingId.length > 0 ? decodePairingResponse(value, "runtimeCredential", REDEEM_RESPONSE_KEYS) : decodePairingResponse(value, "daemonToken", CLAIM_RESPONSE_KEYS);
23467
23432
  }
23468
- async function persistConnection(channelId, relayOrigin, daemonToken2, defaultWorkspace) {
23469
- const connectionsDirectory = join14(readStateDirectory(), "connections");
23433
+ async function persistConnection(channelId, relayOrigin, runtimeCredential, defaultWorkspace, environment) {
23434
+ const connectionsDirectory = join14(readStateDirectory(environment), "connections");
23470
23435
  await mkdir3(connectionsDirectory, { mode: 448, recursive: true });
23471
23436
  const connectionPath = join14(connectionsDirectory, `${channelId}.json`);
23472
23437
  const temporaryPath = join14(connectionsDirectory, `.${channelId}.${randomUUID2()}.tmp`);
@@ -23476,8 +23441,8 @@ async function persistConnection(channelId, relayOrigin, daemonToken2, defaultWo
23476
23441
  {
23477
23442
  defaultWorkspace,
23478
23443
  channelId,
23479
- daemonToken: daemonToken2,
23480
- relayOrigin: relayOrigin.toString()
23444
+ relayOrigin: relayOrigin.toString(),
23445
+ runtimeCredential
23481
23446
  },
23482
23447
  null,
23483
23448
  2
@@ -23489,25 +23454,25 @@ async function persistConnection(channelId, relayOrigin, daemonToken2, defaultWo
23489
23454
  return connectionPath;
23490
23455
  }
23491
23456
  function decodeSavedConnection(value, expectedChannelId) {
23492
- if (!isRecord7(value) || !hasExactKeys(value, SAVED_CONNECTION_KEYS)) {
23457
+ if (!isRecord7(value) || !hasExactKeys(value, SAVED_CONNECTION_KEYS) && !hasExactKeys(value, LEGACY_SAVED_CONNECTION_KEYS)) {
23493
23458
  throw new PairingClientError("invalid_connection", "The saved connection is invalid");
23494
23459
  }
23495
23460
  const defaultWorkspace = value["defaultWorkspace"];
23496
23461
  const channelId = value["channelId"];
23497
- const daemonToken2 = value["daemonToken"];
23462
+ const runtimeCredential = value["runtimeCredential"] ?? value["daemonToken"];
23498
23463
  const relayOrigin = value["relayOrigin"];
23499
- if (typeof defaultWorkspace !== "string" || typeof channelId !== "string" || channelId !== expectedChannelId || !IDENTIFIER_PATTERN5.test(channelId) || typeof daemonToken2 !== "string" || !TOKEN_PATTERN2.test(daemonToken2) || typeof relayOrigin !== "string") {
23464
+ if (typeof defaultWorkspace !== "string" || typeof channelId !== "string" || channelId !== expectedChannelId || !IDENTIFIER_PATTERN5.test(channelId) || typeof runtimeCredential !== "string" || !TOKEN_PATTERN2.test(runtimeCredential) || typeof relayOrigin !== "string") {
23500
23465
  throw new PairingClientError("invalid_connection", "The saved connection is invalid");
23501
23466
  }
23502
23467
  return {
23503
23468
  defaultWorkspace,
23504
23469
  channelId,
23505
- daemonToken: daemonToken2,
23506
- relayOrigin: parseRelayOrigin(relayOrigin)
23470
+ relayOrigin: parseRelayOrigin(relayOrigin),
23471
+ runtimeCredential
23507
23472
  };
23508
23473
  }
23509
- async function readSavedConnection(channelId) {
23510
- const connectionPath = join14(readStateDirectory(), "connections", `${channelId}.json`);
23474
+ async function readSavedConnection(channelId, environment) {
23475
+ const connectionPath = join14(readStateDirectory(environment), "connections", `${channelId}.json`);
23511
23476
  let encoded = "";
23512
23477
  const handle = await open4(connectionPath, "r");
23513
23478
  try {
@@ -23529,8 +23494,8 @@ async function readSavedConnection(channelId) {
23529
23494
  return {
23530
23495
  defaultWorkspace: await resolveDefaultWorkspace(connection.defaultWorkspace),
23531
23496
  channelId: connection.channelId,
23532
- daemonToken: connection.daemonToken,
23533
- relayOrigin: connection.relayOrigin
23497
+ relayOrigin: connection.relayOrigin,
23498
+ runtimeCredential: connection.runtimeCredential
23534
23499
  };
23535
23500
  }
23536
23501
  function assertInventoryUsable(inventory) {
@@ -23538,15 +23503,12 @@ function assertInventoryUsable(inventory) {
23538
23503
  throw new PairingClientError("no_supported_agent", "No supported Agent is available for local discovery");
23539
23504
  }
23540
23505
  }
23541
- function buildPairedDaemon(connection, connectionPath, inventory) {
23506
+ function buildPairedDaemon(connection, connectionPath, inventory, environment) {
23542
23507
  assertInventoryUsable(inventory);
23543
- const relayUrl = mobiusApiUrl(
23544
- connection.relayOrigin,
23545
- `/channels/${encodeURIComponent(connection.channelId)}/daemon`
23546
- );
23508
+ const relayUrl = mobiusApiUrl(connection.relayOrigin, "/v1/runtimes/connect");
23547
23509
  relayUrl.protocol = relayUrl.protocol === "https:" ? "wss:" : "ws:";
23548
- relayUrl.searchParams.set("token", connection.daemonToken);
23549
- const auditDatabasePath = join14(readStateDirectory(), "audit", `${connection.channelId}.sqlite`);
23510
+ relayUrl.searchParams.set("token", connection.runtimeCredential);
23511
+ const auditDatabasePath = join14(readStateDirectory(environment), "audit", `${connection.channelId}.sqlite`);
23550
23512
  return {
23551
23513
  channelUrl: new URL(`/channels/${encodeURIComponent(connection.channelId)}`, connection.relayOrigin).toString(),
23552
23514
  config: createDaemonConfig(
@@ -23562,12 +23524,11 @@ function buildPairedDaemon(connection, connectionPath, inventory) {
23562
23524
  connectionPath
23563
23525
  };
23564
23526
  }
23565
- async function pairLocalMachine(arguments_, signal = new AbortController().signal) {
23566
- const options = parsePairCommand(arguments_);
23527
+ async function pairRuntime(options, signal, environment) {
23567
23528
  const defaultWorkspace = await resolveDefaultWorkspace(options.workspace);
23568
23529
  const inventory = await discoverLocalAgentInventory({
23569
23530
  defaultWorkspace,
23570
- environment: process.env,
23531
+ environment,
23571
23532
  signal
23572
23533
  });
23573
23534
  assertInventoryUsable(inventory);
@@ -23576,31 +23537,87 @@ async function pairLocalMachine(arguments_, signal = new AbortController().signa
23576
23537
  const connectionPath = await persistConnection(
23577
23538
  claim2.channelId,
23578
23539
  options.relayOrigin,
23579
- claim2.daemonToken,
23580
- defaultWorkspace
23540
+ claim2.runtimeCredential,
23541
+ defaultWorkspace,
23542
+ environment
23581
23543
  );
23582
23544
  return buildPairedDaemon(
23583
23545
  {
23584
23546
  defaultWorkspace,
23585
23547
  channelId: claim2.channelId,
23586
- daemonToken: claim2.daemonToken,
23587
- relayOrigin: options.relayOrigin
23548
+ relayOrigin: options.relayOrigin,
23549
+ runtimeCredential: claim2.runtimeCredential
23588
23550
  },
23589
23551
  connectionPath,
23590
- inventory
23552
+ inventory,
23553
+ environment
23591
23554
  );
23592
23555
  }
23593
- async function resumeLocalMachine(arguments_, signal = new AbortController().signal) {
23594
- const channelId = parseChannelArgument(arguments_);
23595
- const connectionPath = join14(readStateDirectory(), "connections", `${channelId}.json`);
23596
- const connection = await readSavedConnection(channelId);
23556
+ async function pairLocalMachine(arguments_, signal = new AbortController().signal) {
23557
+ return await pairRuntime(parsePairCommand(arguments_), signal, process.env);
23558
+ }
23559
+ async function resumeMobiusRuntime(channelId, signal, environment = process.env) {
23560
+ const normalizedChannelId = validatePairingId(channelId);
23561
+ const connectionPath = join14(readStateDirectory(environment), "connections", `${normalizedChannelId}.json`);
23562
+ const connection = await readSavedConnection(normalizedChannelId, environment);
23597
23563
  const inventory = await discoverLocalAgentInventory({
23598
23564
  defaultWorkspace: connection.defaultWorkspace,
23599
- environment: process.env,
23565
+ environment,
23600
23566
  signal
23601
23567
  });
23602
- return buildPairedDaemon(connection, connectionPath, inventory);
23568
+ return buildPairedDaemon(connection, connectionPath, inventory, environment);
23603
23569
  }
23570
+ async function resumeLocalMachine(arguments_, signal = new AbortController().signal) {
23571
+ return await resumeMobiusRuntime(parseChannelArgument(arguments_), signal, process.env);
23572
+ }
23573
+
23574
+ // src/audit/audit-store.ts
23575
+ import { mkdirSync } from "node:fs";
23576
+ import { dirname as dirname3 } from "node:path";
23577
+ import { DatabaseSync } from "node:sqlite";
23578
+ var AuditStore = class {
23579
+ database;
23580
+ constructor(path2) {
23581
+ mkdirSync(dirname3(path2), { recursive: true });
23582
+ this.database = new DatabaseSync(path2);
23583
+ this.database.exec(`
23584
+ CREATE TABLE IF NOT EXISTS audit_events (
23585
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
23586
+ recorded_at TEXT NOT NULL,
23587
+ event TEXT NOT NULL,
23588
+ command_id TEXT NOT NULL,
23589
+ session_id TEXT NOT NULL,
23590
+ details_json TEXT NOT NULL DEFAULT '{}'
23591
+ ) STRICT
23592
+ `);
23593
+ const columns = this.database.prepare("PRAGMA table_info(audit_events)").all();
23594
+ if (!columns.some((column) => Reflect.get(column, "name") === "details_json")) {
23595
+ this.database.exec("ALTER TABLE audit_events ADD COLUMN details_json TEXT NOT NULL DEFAULT '{}'");
23596
+ }
23597
+ }
23598
+ record(record2) {
23599
+ this.database.prepare(
23600
+ `
23601
+ INSERT INTO audit_events (
23602
+ recorded_at,
23603
+ event,
23604
+ command_id,
23605
+ session_id,
23606
+ details_json
23607
+ ) VALUES (?, ?, ?, ?, ?)
23608
+ `
23609
+ ).run(
23610
+ (/* @__PURE__ */ new Date()).toISOString(),
23611
+ record2.event,
23612
+ record2.commandId,
23613
+ record2.sessionId,
23614
+ JSON.stringify(record2.details ?? {})
23615
+ );
23616
+ }
23617
+ close() {
23618
+ this.database.close();
23619
+ }
23620
+ };
23604
23621
 
23605
23622
  // src/run-daemon.ts
23606
23623
  import { randomUUID as randomUUID6 } from "node:crypto";
@@ -26992,14 +27009,19 @@ var AgentCatalogPublishError = class extends Error {
26992
27009
  this.name = "AgentCatalogPublishError";
26993
27010
  }
26994
27011
  };
26995
- function catalogEndpoint(relayUrl) {
26996
- const endpoint = new URL(relayUrl);
27012
+ function catalogEndpoint(config2) {
27013
+ const endpoint = new URL(config2.relayUrl);
26997
27014
  endpoint.protocol = endpoint.protocol === "wss:" ? "https:" : "http:";
26998
- if (!endpoint.pathname.endsWith("/daemon")) {
26999
- throw new AgentCatalogPublishError("The Relay URL is invalid");
27015
+ if (endpoint.pathname.endsWith("/daemon")) {
27016
+ endpoint.pathname = `${endpoint.pathname.slice(0, -"/daemon".length)}/catalog`;
27017
+ return endpoint;
27000
27018
  }
27001
- endpoint.pathname = `${endpoint.pathname.slice(0, -"/daemon".length)}/catalog`;
27002
- return endpoint;
27019
+ const runtimePath = "/v1/runtimes/connect";
27020
+ if (endpoint.pathname.endsWith(runtimePath)) {
27021
+ endpoint.pathname = `${endpoint.pathname.slice(0, -runtimePath.length)}/channels/${encodeURIComponent(config2.channelId)}/catalog`;
27022
+ return endpoint;
27023
+ }
27024
+ throw new AgentCatalogPublishError("The Relay URL is invalid");
27003
27025
  }
27004
27026
  async function publishAgentCatalog(config2, signal) {
27005
27027
  signal.throwIfAborted();
@@ -27008,7 +27030,7 @@ async function publishAgentCatalog(config2, signal) {
27008
27030
  let response;
27009
27031
  const catalog = config2.catalog.read();
27010
27032
  try {
27011
- response = await fetch3(catalogEndpoint(config2.relayUrl), {
27033
+ response = await fetch3(catalogEndpoint(config2), {
27012
27034
  body: JSON.stringify({
27013
27035
  agents: catalog.agents.map((agent) => ({
27014
27036
  agentPath: agent.agentIdentity.requestedPath,
@@ -29064,10 +29086,17 @@ function parseReference(resource) {
29064
29086
  function downloadUrl(config2, command, reference) {
29065
29087
  const url2 = new URL(config2.relayUrl);
29066
29088
  url2.protocol = url2.protocol === "wss:" ? "https:" : "http:";
29067
- if (!url2.pathname.endsWith("/daemon")) {
29068
- throw new PromptArtifactError("artifact_endpoint_invalid", "The Relay artifact endpoint is invalid");
29089
+ let channelPath = "";
29090
+ if (url2.pathname.endsWith("/daemon")) {
29091
+ channelPath = url2.pathname.slice(0, -"/daemon".length);
29092
+ } else {
29093
+ const runtimePath = "/v1/runtimes/connect";
29094
+ if (!url2.pathname.endsWith(runtimePath)) {
29095
+ throw new PromptArtifactError("artifact_endpoint_invalid", "The Relay artifact endpoint is invalid");
29096
+ }
29097
+ channelPath = `${url2.pathname.slice(0, -runtimePath.length)}/channels/${encodeURIComponent(config2.channelId)}`;
29069
29098
  }
29070
- url2.pathname = `${url2.pathname.slice(0, -"/daemon".length)}/sessions/${encodeURIComponent(command.sessionId)}/commands/${encodeURIComponent(command.commandId)}/artifacts/${encodeURIComponent(reference.artifactId)}/content`;
29099
+ url2.pathname = `${channelPath}/sessions/${encodeURIComponent(command.sessionId)}/commands/${encodeURIComponent(command.commandId)}/artifacts/${encodeURIComponent(reference.artifactId)}/content`;
29071
29100
  url2.searchParams.delete("liveness");
29072
29101
  return url2;
29073
29102
  }
@@ -29226,8 +29255,16 @@ var RelayConnectionError = class extends Error {
29226
29255
  function connectRelay(url2, signal) {
29227
29256
  signal.throwIfAborted();
29228
29257
  return new Promise((resolve5, reject) => {
29229
- const socket = new WebSocket(url2, {
29230
- dispatcher: environmentProxyDispatcher
29258
+ const endpoint = new URL(url2);
29259
+ const headers = {};
29260
+ if (endpoint.pathname.endsWith("/v1/runtimes/connect")) {
29261
+ const runtimeCredential = endpoint.searchParams.get("token") ?? "";
29262
+ endpoint.searchParams.delete("token");
29263
+ headers["authorization"] = `Bearer ${runtimeCredential}`;
29264
+ }
29265
+ const socket = new WebSocket(endpoint, {
29266
+ dispatcher: environmentProxyDispatcher,
29267
+ headers
29231
29268
  });
29232
29269
  let finished = false;
29233
29270
  const timeout = setTimeout(onTimeout, CONNECTION_TIMEOUT_MILLISECONDS);
@@ -29848,10 +29885,17 @@ var ActiveTurnReconciliationError = class extends Error {
29848
29885
  function activeTurnReconciliationEndpoint(config2, turn) {
29849
29886
  const endpoint = new URL(config2.relayUrl);
29850
29887
  endpoint.protocol = endpoint.protocol === "wss:" ? "https:" : "http:";
29851
- if (!endpoint.pathname.endsWith("/daemon")) {
29852
- throw new ActiveTurnReconciliationError("The Relay URL is invalid");
29888
+ let channelPath = "";
29889
+ if (endpoint.pathname.endsWith("/daemon")) {
29890
+ channelPath = endpoint.pathname.slice(0, -"/daemon".length);
29891
+ } else {
29892
+ const runtimePath = "/v1/runtimes/connect";
29893
+ if (!endpoint.pathname.endsWith(runtimePath)) {
29894
+ throw new ActiveTurnReconciliationError("The Relay URL is invalid");
29895
+ }
29896
+ channelPath = `${endpoint.pathname.slice(0, -runtimePath.length)}/channels/${encodeURIComponent(config2.channelId)}`;
29853
29897
  }
29854
- endpoint.pathname = `${endpoint.pathname.slice(0, -"/daemon".length)}/sessions/${encodeURIComponent(turn.command.sessionId)}/attempts/${encodeURIComponent(turn.attemptId)}/reconciliation`;
29898
+ endpoint.pathname = `${channelPath}/sessions/${encodeURIComponent(turn.command.sessionId)}/attempts/${encodeURIComponent(turn.attemptId)}/reconciliation`;
29855
29899
  endpoint.searchParams.delete("liveness");
29856
29900
  return endpoint;
29857
29901
  }
@@ -31176,6 +31220,69 @@ async function runDaemon(config2, audit, signal, timer = systemDaemonTimer) {
31176
31220
  }
31177
31221
  }
31178
31222
 
31223
+ // src/runtime.ts
31224
+ var APP_CREDENTIAL_PATTERN = /^mapp_[a-zA-Z0-9_-]{22}_[a-zA-Z0-9_-]{43}$/;
31225
+ var CONVERSATION_CREDENTIAL_PATTERN = /^mconv_[a-zA-Z0-9_-]{22}_[a-zA-Z0-9_-]{43}$/;
31226
+ var RUNTIME_CREDENTIAL_PATTERN = /^[a-zA-Z0-9_-]{32,256}$/;
31227
+ var MobiusRuntimeConfigurationError = class extends Error {
31228
+ code = "runtime_configuration_invalid";
31229
+ constructor(message, options = {}) {
31230
+ super(message, options);
31231
+ this.name = "MobiusRuntimeConfigurationError";
31232
+ }
31233
+ };
31234
+ var MobiusRuntimeState = class {
31235
+ };
31236
+ var IdleMobiusRuntimeState = class extends MobiusRuntimeState {
31237
+ };
31238
+ var RunningMobiusRuntimeState = class extends MobiusRuntimeState {
31239
+ };
31240
+ var FinishedMobiusRuntimeState = class extends MobiusRuntimeState {
31241
+ };
31242
+ function validateRuntimeRelay(relayUrl) {
31243
+ if (relayUrl.protocol !== "ws:" && relayUrl.protocol !== "wss:") {
31244
+ throw new MobiusRuntimeConfigurationError("The Runtime relay URL must use WebSocket transport");
31245
+ }
31246
+ const credential = relayUrl.searchParams.get("token") ?? "";
31247
+ if (credential.length === 0) {
31248
+ throw new MobiusRuntimeConfigurationError("The Runtime relay credential is required");
31249
+ }
31250
+ if (APP_CREDENTIAL_PATTERN.test(credential)) {
31251
+ throw new MobiusRuntimeConfigurationError("An AppCredential cannot be held by a local Runtime");
31252
+ }
31253
+ if (CONVERSATION_CREDENTIAL_PATTERN.test(credential)) {
31254
+ throw new MobiusRuntimeConfigurationError("A ConversationAccessToken cannot be held by a local Runtime");
31255
+ }
31256
+ if (!RUNTIME_CREDENTIAL_PATTERN.test(credential)) {
31257
+ throw new MobiusRuntimeConfigurationError("The Runtime relay credential is invalid");
31258
+ }
31259
+ }
31260
+ function validateRuntimeConfiguration(config2) {
31261
+ validateRuntimeRelay(config2.relayUrl);
31262
+ }
31263
+ var MobiusRuntimeEngine = class {
31264
+ #config;
31265
+ #state = new IdleMobiusRuntimeState();
31266
+ constructor(config2) {
31267
+ validateRuntimeConfiguration(config2);
31268
+ this.#config = config2;
31269
+ }
31270
+ run = async (signal) => {
31271
+ if (!(this.#state instanceof IdleMobiusRuntimeState)) {
31272
+ throw new MobiusRuntimeConfigurationError("A Mobius Runtime engine can only be started once");
31273
+ }
31274
+ signal.throwIfAborted();
31275
+ this.#state = new RunningMobiusRuntimeState();
31276
+ const audit = new AuditStore(this.#config.auditDatabasePath);
31277
+ try {
31278
+ await runDaemon(this.#config, audit, signal);
31279
+ } finally {
31280
+ audit.close();
31281
+ this.#state = new FinishedMobiusRuntimeState();
31282
+ }
31283
+ };
31284
+ };
31285
+
31179
31286
  // src/cli.ts
31180
31287
  var shutdown = new AbortController();
31181
31288
  process.once("SIGINT", () => shutdown.abort());
@@ -31183,7 +31290,7 @@ process.once("SIGTERM", () => shutdown.abort());
31183
31290
  var USAGE = `Mobius CLI
31184
31291
 
31185
31292
  Usage:
31186
- mobius pair --url <relay-url> --code <pairing-code> [--workspace <directory>]
31293
+ mobius pair --url <relay-url> --pairing <runtime-pairing-id> --code <pairing-code> [--workspace <directory>]
31187
31294
  mobius start --channel <channel-id>
31188
31295
  mobius agents [--json]
31189
31296
  mobius bot message send --target <current|alias> --text <message>
@@ -31256,12 +31363,7 @@ async function main() {
31256
31363
  process.stdout.write(`Default workspace: ${config2.defaultWorkspace}
31257
31364
 
31258
31365
  `);
31259
- const audit = new AuditStore(config2.auditDatabasePath);
31260
- try {
31261
- await runDaemon(config2, audit, shutdown.signal);
31262
- } finally {
31263
- audit.close();
31264
- }
31366
+ await new MobiusRuntimeEngine(config2).run(shutdown.signal);
31265
31367
  }
31266
31368
  try {
31267
31369
  await main();