@moxt-ai/mobius 0.0.24 → 0.0.26
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/agent-catalog/publish-agent-catalog.d.ts.map +1 -1
- package/dist/agent-catalog/publish-agent-catalog.js +12 -7
- package/dist/agent-catalog/publish-agent-catalog.js.map +1 -1
- package/dist/cli.js +685 -290
- package/dist/cli.js.map +4 -4
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -3
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +746 -254
- package/dist/index.js.map +4 -4
- package/dist/network/environment-proxy.d.ts +1 -0
- package/dist/network/environment-proxy.d.ts.map +1 -1
- package/dist/network/environment-proxy.js +47 -0
- package/dist/network/environment-proxy.js.map +1 -1
- package/dist/pairing/pair-local-machine.d.ts +8 -0
- package/dist/pairing/pair-local-machine.d.ts.map +1 -1
- package/dist/pairing/pair-local-machine.js +64 -37
- package/dist/pairing/pair-local-machine.js.map +1 -1
- package/dist/prompt-artifact-resolver.d.ts.map +1 -1
- package/dist/prompt-artifact-resolver.js +11 -3
- package/dist/prompt-artifact-resolver.js.map +1 -1
- package/dist/relay-connection/connect.d.ts.map +1 -1
- package/dist/relay-connection/connect.js +25 -9
- package/dist/relay-connection/connect.js.map +1 -1
- package/dist/relay-connection/diagnostics.d.ts +11 -0
- package/dist/relay-connection/diagnostics.d.ts.map +1 -0
- package/dist/relay-connection/diagnostics.js +122 -0
- package/dist/relay-connection/diagnostics.js.map +1 -0
- package/dist/relay-connection/liveness.d.ts +1 -1
- package/dist/relay-connection/liveness.d.ts.map +1 -1
- package/dist/relay-connection/liveness.js +2 -1
- package/dist/relay-connection/liveness.js.map +1 -1
- package/dist/relay-connection/session-event-outbox.d.ts +2 -1
- package/dist/relay-connection/session-event-outbox.d.ts.map +1 -1
- package/dist/relay-connection/session-event-outbox.js +48 -8
- package/dist/relay-connection/session-event-outbox.js.map +1 -1
- package/dist/run-daemon.d.ts.map +1 -1
- package/dist/run-daemon.js +193 -149
- package/dist/run-daemon.js.map +1 -1
- package/dist/runtime.d.ts +23 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +31339 -0
- package/dist/runtime.js.map +7 -0
- package/package.json +6 -2
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.
|
|
14
|
+
version: "0.0.26",
|
|
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",
|
|
@@ -1574,7 +1578,53 @@ import { fetch } from "undici";
|
|
|
1574
1578
|
|
|
1575
1579
|
// src/network/environment-proxy.ts
|
|
1576
1580
|
import { EnvHttpProxyAgent } from "undici";
|
|
1581
|
+
var configuredHttpProxy = process.env["http_proxy"] ?? process.env["HTTP_PROXY"] ?? "";
|
|
1582
|
+
var configuredHttpsProxy = process.env["https_proxy"] ?? process.env["HTTPS_PROXY"] ?? configuredHttpProxy;
|
|
1577
1583
|
var environmentProxyDispatcher = new EnvHttpProxyAgent();
|
|
1584
|
+
function bypassesProxy(target) {
|
|
1585
|
+
const noProxy = process.env["no_proxy"] ?? process.env["NO_PROXY"] ?? "";
|
|
1586
|
+
if (noProxy === "*") {
|
|
1587
|
+
return true;
|
|
1588
|
+
}
|
|
1589
|
+
const hostname4 = target.hostname.replace(/^\[|\]$/gu, "").toLowerCase();
|
|
1590
|
+
const defaultPort = target.protocol === "wss:" || target.protocol === "https:" ? 443 : 80;
|
|
1591
|
+
const port = Number.parseInt(target.port, 10) || defaultPort;
|
|
1592
|
+
for (const rawEntry of noProxy.split(/[,\s]/u)) {
|
|
1593
|
+
if (rawEntry.length === 0) {
|
|
1594
|
+
continue;
|
|
1595
|
+
}
|
|
1596
|
+
const parsed = rawEntry.match(/^(.+):(\d+)$/u);
|
|
1597
|
+
const entryHostname = (parsed?.[1] ?? rawEntry).replace(/^\*?\./u, "").toLowerCase();
|
|
1598
|
+
const entryPort = parsed?.[2] === void 0 ? 0 : Number.parseInt(parsed[2], 10);
|
|
1599
|
+
if (entryPort !== 0 && entryPort !== port) {
|
|
1600
|
+
continue;
|
|
1601
|
+
}
|
|
1602
|
+
if (hostname4 === entryHostname || hostname4.endsWith(`.${entryHostname}`)) {
|
|
1603
|
+
return true;
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
return false;
|
|
1607
|
+
}
|
|
1608
|
+
function sanitizedProxyOrigin(proxyUrl) {
|
|
1609
|
+
try {
|
|
1610
|
+
return new URL(proxyUrl).origin;
|
|
1611
|
+
} catch {
|
|
1612
|
+
return "configured-invalid-url";
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
function describeEnvironmentProxyRoute(target) {
|
|
1616
|
+
if (bypassesProxy(target)) {
|
|
1617
|
+
return { proxyRoute: "direct" };
|
|
1618
|
+
}
|
|
1619
|
+
const proxyUrl = target.protocol === "wss:" || target.protocol === "https:" ? configuredHttpsProxy : configuredHttpProxy;
|
|
1620
|
+
if (proxyUrl.length === 0) {
|
|
1621
|
+
return { proxyRoute: "direct" };
|
|
1622
|
+
}
|
|
1623
|
+
return {
|
|
1624
|
+
proxyOrigin: sanitizedProxyOrigin(proxyUrl),
|
|
1625
|
+
proxyRoute: "proxy"
|
|
1626
|
+
};
|
|
1627
|
+
}
|
|
1578
1628
|
|
|
1579
1629
|
// src/agent-installation/managed-agent-archive.ts
|
|
1580
1630
|
var ARCHIVE_DOWNLOAD_LIMIT_BYTES = 128 * 1024 * 1024;
|
|
@@ -2578,54 +2628,6 @@ async function discoverLocalAgentInventory(options) {
|
|
|
2578
2628
|
return new LocalAgentInventory(discoveries);
|
|
2579
2629
|
}
|
|
2580
2630
|
|
|
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
2631
|
// src/bots/bot-message-command.ts
|
|
2630
2632
|
import { randomUUID } from "node:crypto";
|
|
2631
2633
|
import { createConnection } from "node:net";
|
|
@@ -23249,14 +23251,15 @@ var encode4 = (message, schema) => {
|
|
|
23249
23251
|
}
|
|
23250
23252
|
return JSON.stringify(decoded.data);
|
|
23251
23253
|
};
|
|
23254
|
+
var decodeSessionEventEnvelope = (encoded) => decode5(encoded, eventEnvelopeSchema);
|
|
23252
23255
|
var encodeSessionEventEnvelope = (message) => encode4(message, eventEnvelopeSchema);
|
|
23253
23256
|
var decodeSessionEventAcknowledgement = (encoded) => decode5(encoded, eventAcknowledgementSchema);
|
|
23254
23257
|
var decodeSessionEventRejection = (encoded) => decode5(encoded, eventRejectionSchema);
|
|
23255
23258
|
|
|
23256
23259
|
// src/config.ts
|
|
23257
|
-
function loadDefaultWorkspace() {
|
|
23258
|
-
const { MOBIUS_DEFAULT_WORKSPACE: configuredWorkspace } =
|
|
23259
|
-
return configuredWorkspace ?? join13(readStateDirectory(), "workspace");
|
|
23260
|
+
function loadDefaultWorkspace(environment = process.env) {
|
|
23261
|
+
const { MOBIUS_DEFAULT_WORKSPACE: configuredWorkspace } = environment;
|
|
23262
|
+
return configuredWorkspace ?? join13(readStateDirectory(environment), "workspace");
|
|
23260
23263
|
}
|
|
23261
23264
|
function createDaemonConfig(connection, agents = [], installableAgents = []) {
|
|
23262
23265
|
return {
|
|
@@ -23283,7 +23286,9 @@ var IDENTIFIER_PATTERN5 = /^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$/;
|
|
|
23283
23286
|
var TOKEN_PATTERN2 = /^[a-zA-Z0-9_-]{32,256}$/;
|
|
23284
23287
|
var MAX_CONNECTION_FILE_LENGTH = 16 * 1024;
|
|
23285
23288
|
var CLAIM_RESPONSE_KEYS = ["channelId", "daemonToken", "runtimeId"];
|
|
23286
|
-
var
|
|
23289
|
+
var REDEEM_RESPONSE_KEYS = ["channelId", "runtimeCredential", "runtimeId"];
|
|
23290
|
+
var LEGACY_SAVED_CONNECTION_KEYS = ["defaultWorkspace", "channelId", "daemonToken", "relayOrigin"];
|
|
23291
|
+
var SAVED_CONNECTION_KEYS = ["defaultWorkspace", "channelId", "relayOrigin", "runtimeCredential"];
|
|
23287
23292
|
var PairingClientError = class extends Error {
|
|
23288
23293
|
code;
|
|
23289
23294
|
constructor(code, message, options = {}) {
|
|
@@ -23320,7 +23325,7 @@ function readSingleOption(arguments_, option2) {
|
|
|
23320
23325
|
function assertKnownArguments(arguments_) {
|
|
23321
23326
|
for (let index = 0; index < arguments_.length; index += 2) {
|
|
23322
23327
|
const option2 = arguments_[index];
|
|
23323
|
-
if (option2 !== "--url" && option2 !== "--code" && option2 !== "--workspace") {
|
|
23328
|
+
if (option2 !== "--url" && option2 !== "--code" && option2 !== "--pairing" && option2 !== "--workspace") {
|
|
23324
23329
|
throw new PairingClientError("invalid_arguments", `Unknown pairing option: ${option2 ?? "missing option"}`);
|
|
23325
23330
|
}
|
|
23326
23331
|
}
|
|
@@ -23360,10 +23365,17 @@ function parsePairCommand(arguments_) {
|
|
|
23360
23365
|
assertKnownArguments(arguments_);
|
|
23361
23366
|
return {
|
|
23362
23367
|
code: parsePairingCode(readSingleOption(arguments_, "--code")),
|
|
23368
|
+
pairingId: arguments_.includes("--pairing") ? validatePairingId(readSingleOption(arguments_, "--pairing")) : "",
|
|
23363
23369
|
relayOrigin: parseRelayOrigin(readSingleOption(arguments_, "--url")),
|
|
23364
23370
|
workspace: arguments_.includes("--workspace") ? readSingleOption(arguments_, "--workspace") : loadDefaultWorkspace()
|
|
23365
23371
|
};
|
|
23366
23372
|
}
|
|
23373
|
+
function validatePairingId(value) {
|
|
23374
|
+
if (!IDENTIFIER_PATTERN5.test(value)) {
|
|
23375
|
+
throw new PairingClientError("invalid_pairing_id", "The Runtime pairing ID is invalid");
|
|
23376
|
+
}
|
|
23377
|
+
return value;
|
|
23378
|
+
}
|
|
23367
23379
|
function parseChannelArgument(arguments_) {
|
|
23368
23380
|
if (arguments_.length !== 2 || arguments_[0] !== "--channel") {
|
|
23369
23381
|
throw new PairingClientError("invalid_arguments", "Usage: mobius start --channel <channel-id>");
|
|
@@ -23420,17 +23432,17 @@ function readRelayError(value) {
|
|
|
23420
23432
|
}
|
|
23421
23433
|
return new PairingClientError("pairing_failed", "The relay could not pair this machine");
|
|
23422
23434
|
}
|
|
23423
|
-
function
|
|
23424
|
-
if (!isRecord7(value) || !hasExactKeys(value,
|
|
23435
|
+
function decodePairingResponse(value, credentialField, expectedKeys) {
|
|
23436
|
+
if (!isRecord7(value) || !hasExactKeys(value, expectedKeys)) {
|
|
23425
23437
|
throw new PairingClientError("invalid_relay_response", "The relay returned an invalid pairing response");
|
|
23426
23438
|
}
|
|
23427
23439
|
const channelId = value["channelId"];
|
|
23428
|
-
const
|
|
23440
|
+
const runtimeCredential = value[credentialField];
|
|
23429
23441
|
const runtimeId = value["runtimeId"];
|
|
23430
|
-
if (typeof channelId !== "string" || !IDENTIFIER_PATTERN5.test(channelId) || typeof
|
|
23442
|
+
if (typeof channelId !== "string" || !IDENTIFIER_PATTERN5.test(channelId) || typeof runtimeCredential !== "string" || !TOKEN_PATTERN2.test(runtimeCredential) || typeof runtimeId !== "string" || !IDENTIFIER_PATTERN5.test(runtimeId)) {
|
|
23431
23443
|
throw new PairingClientError("invalid_relay_response", "The relay returned an invalid pairing response");
|
|
23432
23444
|
}
|
|
23433
|
-
return { channelId,
|
|
23445
|
+
return { channelId, runtimeCredential, runtimeId };
|
|
23434
23446
|
}
|
|
23435
23447
|
async function claimPairing(options, defaultWorkspace, localAgents, installableAgents, signal) {
|
|
23436
23448
|
const agents = localAgents.map((agent) => ({
|
|
@@ -23440,7 +23452,7 @@ async function claimPairing(options, defaultWorkspace, localAgents, installableA
|
|
|
23440
23452
|
id: agent.id,
|
|
23441
23453
|
label: agent.label
|
|
23442
23454
|
}));
|
|
23443
|
-
const endpoint = mobiusApiUrl(options.relayOrigin, "/pairings/claim");
|
|
23455
|
+
const endpoint = options.pairingId.length > 0 ? mobiusApiUrl(options.relayOrigin, `/v1/runtime-pairings/${encodeURIComponent(options.pairingId)}/redeem`) : mobiusApiUrl(options.relayOrigin, "/pairings/claim");
|
|
23444
23456
|
let response;
|
|
23445
23457
|
try {
|
|
23446
23458
|
response = await fetch2(endpoint, {
|
|
@@ -23463,10 +23475,10 @@ async function claimPairing(options, defaultWorkspace, localAgents, installableA
|
|
|
23463
23475
|
if (!response.ok) {
|
|
23464
23476
|
throw readRelayError(value);
|
|
23465
23477
|
}
|
|
23466
|
-
return
|
|
23478
|
+
return options.pairingId.length > 0 ? decodePairingResponse(value, "runtimeCredential", REDEEM_RESPONSE_KEYS) : decodePairingResponse(value, "daemonToken", CLAIM_RESPONSE_KEYS);
|
|
23467
23479
|
}
|
|
23468
|
-
async function persistConnection(channelId, relayOrigin,
|
|
23469
|
-
const connectionsDirectory = join14(readStateDirectory(), "connections");
|
|
23480
|
+
async function persistConnection(channelId, relayOrigin, runtimeCredential, defaultWorkspace, environment) {
|
|
23481
|
+
const connectionsDirectory = join14(readStateDirectory(environment), "connections");
|
|
23470
23482
|
await mkdir3(connectionsDirectory, { mode: 448, recursive: true });
|
|
23471
23483
|
const connectionPath = join14(connectionsDirectory, `${channelId}.json`);
|
|
23472
23484
|
const temporaryPath = join14(connectionsDirectory, `.${channelId}.${randomUUID2()}.tmp`);
|
|
@@ -23476,8 +23488,8 @@ async function persistConnection(channelId, relayOrigin, daemonToken2, defaultWo
|
|
|
23476
23488
|
{
|
|
23477
23489
|
defaultWorkspace,
|
|
23478
23490
|
channelId,
|
|
23479
|
-
|
|
23480
|
-
|
|
23491
|
+
relayOrigin: relayOrigin.toString(),
|
|
23492
|
+
runtimeCredential
|
|
23481
23493
|
},
|
|
23482
23494
|
null,
|
|
23483
23495
|
2
|
|
@@ -23489,25 +23501,25 @@ async function persistConnection(channelId, relayOrigin, daemonToken2, defaultWo
|
|
|
23489
23501
|
return connectionPath;
|
|
23490
23502
|
}
|
|
23491
23503
|
function decodeSavedConnection(value, expectedChannelId) {
|
|
23492
|
-
if (!isRecord7(value) || !hasExactKeys(value, SAVED_CONNECTION_KEYS)) {
|
|
23504
|
+
if (!isRecord7(value) || !hasExactKeys(value, SAVED_CONNECTION_KEYS) && !hasExactKeys(value, LEGACY_SAVED_CONNECTION_KEYS)) {
|
|
23493
23505
|
throw new PairingClientError("invalid_connection", "The saved connection is invalid");
|
|
23494
23506
|
}
|
|
23495
23507
|
const defaultWorkspace = value["defaultWorkspace"];
|
|
23496
23508
|
const channelId = value["channelId"];
|
|
23497
|
-
const
|
|
23509
|
+
const runtimeCredential = value["runtimeCredential"] ?? value["daemonToken"];
|
|
23498
23510
|
const relayOrigin = value["relayOrigin"];
|
|
23499
|
-
if (typeof defaultWorkspace !== "string" || typeof channelId !== "string" || channelId !== expectedChannelId || !IDENTIFIER_PATTERN5.test(channelId) || typeof
|
|
23511
|
+
if (typeof defaultWorkspace !== "string" || typeof channelId !== "string" || channelId !== expectedChannelId || !IDENTIFIER_PATTERN5.test(channelId) || typeof runtimeCredential !== "string" || !TOKEN_PATTERN2.test(runtimeCredential) || typeof relayOrigin !== "string") {
|
|
23500
23512
|
throw new PairingClientError("invalid_connection", "The saved connection is invalid");
|
|
23501
23513
|
}
|
|
23502
23514
|
return {
|
|
23503
23515
|
defaultWorkspace,
|
|
23504
23516
|
channelId,
|
|
23505
|
-
|
|
23506
|
-
|
|
23517
|
+
relayOrigin: parseRelayOrigin(relayOrigin),
|
|
23518
|
+
runtimeCredential
|
|
23507
23519
|
};
|
|
23508
23520
|
}
|
|
23509
|
-
async function readSavedConnection(channelId) {
|
|
23510
|
-
const connectionPath = join14(readStateDirectory(), "connections", `${channelId}.json`);
|
|
23521
|
+
async function readSavedConnection(channelId, environment) {
|
|
23522
|
+
const connectionPath = join14(readStateDirectory(environment), "connections", `${channelId}.json`);
|
|
23511
23523
|
let encoded = "";
|
|
23512
23524
|
const handle = await open4(connectionPath, "r");
|
|
23513
23525
|
try {
|
|
@@ -23529,8 +23541,8 @@ async function readSavedConnection(channelId) {
|
|
|
23529
23541
|
return {
|
|
23530
23542
|
defaultWorkspace: await resolveDefaultWorkspace(connection.defaultWorkspace),
|
|
23531
23543
|
channelId: connection.channelId,
|
|
23532
|
-
|
|
23533
|
-
|
|
23544
|
+
relayOrigin: connection.relayOrigin,
|
|
23545
|
+
runtimeCredential: connection.runtimeCredential
|
|
23534
23546
|
};
|
|
23535
23547
|
}
|
|
23536
23548
|
function assertInventoryUsable(inventory) {
|
|
@@ -23538,15 +23550,12 @@ function assertInventoryUsable(inventory) {
|
|
|
23538
23550
|
throw new PairingClientError("no_supported_agent", "No supported Agent is available for local discovery");
|
|
23539
23551
|
}
|
|
23540
23552
|
}
|
|
23541
|
-
function buildPairedDaemon(connection, connectionPath, inventory) {
|
|
23553
|
+
function buildPairedDaemon(connection, connectionPath, inventory, environment) {
|
|
23542
23554
|
assertInventoryUsable(inventory);
|
|
23543
|
-
const relayUrl = mobiusApiUrl(
|
|
23544
|
-
connection.relayOrigin,
|
|
23545
|
-
`/channels/${encodeURIComponent(connection.channelId)}/daemon`
|
|
23546
|
-
);
|
|
23555
|
+
const relayUrl = mobiusApiUrl(connection.relayOrigin, "/v1/runtimes/connect");
|
|
23547
23556
|
relayUrl.protocol = relayUrl.protocol === "https:" ? "wss:" : "ws:";
|
|
23548
|
-
relayUrl.searchParams.set("token", connection.
|
|
23549
|
-
const auditDatabasePath = join14(readStateDirectory(), "audit", `${connection.channelId}.sqlite`);
|
|
23557
|
+
relayUrl.searchParams.set("token", connection.runtimeCredential);
|
|
23558
|
+
const auditDatabasePath = join14(readStateDirectory(environment), "audit", `${connection.channelId}.sqlite`);
|
|
23550
23559
|
return {
|
|
23551
23560
|
channelUrl: new URL(`/channels/${encodeURIComponent(connection.channelId)}`, connection.relayOrigin).toString(),
|
|
23552
23561
|
config: createDaemonConfig(
|
|
@@ -23562,12 +23571,11 @@ function buildPairedDaemon(connection, connectionPath, inventory) {
|
|
|
23562
23571
|
connectionPath
|
|
23563
23572
|
};
|
|
23564
23573
|
}
|
|
23565
|
-
async function
|
|
23566
|
-
const options = parsePairCommand(arguments_);
|
|
23574
|
+
async function pairRuntime(options, signal, environment) {
|
|
23567
23575
|
const defaultWorkspace = await resolveDefaultWorkspace(options.workspace);
|
|
23568
23576
|
const inventory = await discoverLocalAgentInventory({
|
|
23569
23577
|
defaultWorkspace,
|
|
23570
|
-
environment
|
|
23578
|
+
environment,
|
|
23571
23579
|
signal
|
|
23572
23580
|
});
|
|
23573
23581
|
assertInventoryUsable(inventory);
|
|
@@ -23576,32 +23584,88 @@ async function pairLocalMachine(arguments_, signal = new AbortController().signa
|
|
|
23576
23584
|
const connectionPath = await persistConnection(
|
|
23577
23585
|
claim2.channelId,
|
|
23578
23586
|
options.relayOrigin,
|
|
23579
|
-
claim2.
|
|
23580
|
-
defaultWorkspace
|
|
23587
|
+
claim2.runtimeCredential,
|
|
23588
|
+
defaultWorkspace,
|
|
23589
|
+
environment
|
|
23581
23590
|
);
|
|
23582
23591
|
return buildPairedDaemon(
|
|
23583
23592
|
{
|
|
23584
23593
|
defaultWorkspace,
|
|
23585
23594
|
channelId: claim2.channelId,
|
|
23586
|
-
|
|
23587
|
-
|
|
23595
|
+
relayOrigin: options.relayOrigin,
|
|
23596
|
+
runtimeCredential: claim2.runtimeCredential
|
|
23588
23597
|
},
|
|
23589
23598
|
connectionPath,
|
|
23590
|
-
inventory
|
|
23599
|
+
inventory,
|
|
23600
|
+
environment
|
|
23591
23601
|
);
|
|
23592
23602
|
}
|
|
23593
|
-
async function
|
|
23594
|
-
|
|
23595
|
-
|
|
23596
|
-
|
|
23603
|
+
async function pairLocalMachine(arguments_, signal = new AbortController().signal) {
|
|
23604
|
+
return await pairRuntime(parsePairCommand(arguments_), signal, process.env);
|
|
23605
|
+
}
|
|
23606
|
+
async function resumeMobiusRuntime(channelId, signal, environment = process.env) {
|
|
23607
|
+
const normalizedChannelId = validatePairingId(channelId);
|
|
23608
|
+
const connectionPath = join14(readStateDirectory(environment), "connections", `${normalizedChannelId}.json`);
|
|
23609
|
+
const connection = await readSavedConnection(normalizedChannelId, environment);
|
|
23597
23610
|
const inventory = await discoverLocalAgentInventory({
|
|
23598
23611
|
defaultWorkspace: connection.defaultWorkspace,
|
|
23599
|
-
environment
|
|
23612
|
+
environment,
|
|
23600
23613
|
signal
|
|
23601
23614
|
});
|
|
23602
|
-
return buildPairedDaemon(connection, connectionPath, inventory);
|
|
23615
|
+
return buildPairedDaemon(connection, connectionPath, inventory, environment);
|
|
23616
|
+
}
|
|
23617
|
+
async function resumeLocalMachine(arguments_, signal = new AbortController().signal) {
|
|
23618
|
+
return await resumeMobiusRuntime(parseChannelArgument(arguments_), signal, process.env);
|
|
23603
23619
|
}
|
|
23604
23620
|
|
|
23621
|
+
// src/audit/audit-store.ts
|
|
23622
|
+
import { mkdirSync } from "node:fs";
|
|
23623
|
+
import { dirname as dirname3 } from "node:path";
|
|
23624
|
+
import { DatabaseSync } from "node:sqlite";
|
|
23625
|
+
var AuditStore = class {
|
|
23626
|
+
database;
|
|
23627
|
+
constructor(path2) {
|
|
23628
|
+
mkdirSync(dirname3(path2), { recursive: true });
|
|
23629
|
+
this.database = new DatabaseSync(path2);
|
|
23630
|
+
this.database.exec(`
|
|
23631
|
+
CREATE TABLE IF NOT EXISTS audit_events (
|
|
23632
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
23633
|
+
recorded_at TEXT NOT NULL,
|
|
23634
|
+
event TEXT NOT NULL,
|
|
23635
|
+
command_id TEXT NOT NULL,
|
|
23636
|
+
session_id TEXT NOT NULL,
|
|
23637
|
+
details_json TEXT NOT NULL DEFAULT '{}'
|
|
23638
|
+
) STRICT
|
|
23639
|
+
`);
|
|
23640
|
+
const columns = this.database.prepare("PRAGMA table_info(audit_events)").all();
|
|
23641
|
+
if (!columns.some((column) => Reflect.get(column, "name") === "details_json")) {
|
|
23642
|
+
this.database.exec("ALTER TABLE audit_events ADD COLUMN details_json TEXT NOT NULL DEFAULT '{}'");
|
|
23643
|
+
}
|
|
23644
|
+
}
|
|
23645
|
+
record(record2) {
|
|
23646
|
+
this.database.prepare(
|
|
23647
|
+
`
|
|
23648
|
+
INSERT INTO audit_events (
|
|
23649
|
+
recorded_at,
|
|
23650
|
+
event,
|
|
23651
|
+
command_id,
|
|
23652
|
+
session_id,
|
|
23653
|
+
details_json
|
|
23654
|
+
) VALUES (?, ?, ?, ?, ?)
|
|
23655
|
+
`
|
|
23656
|
+
).run(
|
|
23657
|
+
(/* @__PURE__ */ new Date()).toISOString(),
|
|
23658
|
+
record2.event,
|
|
23659
|
+
record2.commandId,
|
|
23660
|
+
record2.sessionId,
|
|
23661
|
+
JSON.stringify(record2.details ?? {})
|
|
23662
|
+
);
|
|
23663
|
+
}
|
|
23664
|
+
close() {
|
|
23665
|
+
this.database.close();
|
|
23666
|
+
}
|
|
23667
|
+
};
|
|
23668
|
+
|
|
23605
23669
|
// src/run-daemon.ts
|
|
23606
23670
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
23607
23671
|
import { basename as basename4, join as join16 } from "node:path";
|
|
@@ -26992,14 +27056,19 @@ var AgentCatalogPublishError = class extends Error {
|
|
|
26992
27056
|
this.name = "AgentCatalogPublishError";
|
|
26993
27057
|
}
|
|
26994
27058
|
};
|
|
26995
|
-
function catalogEndpoint(
|
|
26996
|
-
const endpoint = new URL(relayUrl);
|
|
27059
|
+
function catalogEndpoint(config2) {
|
|
27060
|
+
const endpoint = new URL(config2.relayUrl);
|
|
26997
27061
|
endpoint.protocol = endpoint.protocol === "wss:" ? "https:" : "http:";
|
|
26998
|
-
if (
|
|
26999
|
-
|
|
27062
|
+
if (endpoint.pathname.endsWith("/daemon")) {
|
|
27063
|
+
endpoint.pathname = `${endpoint.pathname.slice(0, -"/daemon".length)}/catalog`;
|
|
27064
|
+
return endpoint;
|
|
27000
27065
|
}
|
|
27001
|
-
|
|
27002
|
-
|
|
27066
|
+
const runtimePath = "/v1/runtimes/connect";
|
|
27067
|
+
if (endpoint.pathname.endsWith(runtimePath)) {
|
|
27068
|
+
endpoint.pathname = `${endpoint.pathname.slice(0, -runtimePath.length)}/channels/${encodeURIComponent(config2.channelId)}/catalog`;
|
|
27069
|
+
return endpoint;
|
|
27070
|
+
}
|
|
27071
|
+
throw new AgentCatalogPublishError("The Relay URL is invalid");
|
|
27003
27072
|
}
|
|
27004
27073
|
async function publishAgentCatalog(config2, signal) {
|
|
27005
27074
|
signal.throwIfAborted();
|
|
@@ -27008,7 +27077,7 @@ async function publishAgentCatalog(config2, signal) {
|
|
|
27008
27077
|
let response;
|
|
27009
27078
|
const catalog = config2.catalog.read();
|
|
27010
27079
|
try {
|
|
27011
|
-
response = await fetch3(catalogEndpoint(config2
|
|
27080
|
+
response = await fetch3(catalogEndpoint(config2), {
|
|
27012
27081
|
body: JSON.stringify({
|
|
27013
27082
|
agents: catalog.agents.map((agent) => ({
|
|
27014
27083
|
agentPath: agent.agentIdentity.requestedPath,
|
|
@@ -29064,10 +29133,17 @@ function parseReference(resource) {
|
|
|
29064
29133
|
function downloadUrl(config2, command, reference) {
|
|
29065
29134
|
const url2 = new URL(config2.relayUrl);
|
|
29066
29135
|
url2.protocol = url2.protocol === "wss:" ? "https:" : "http:";
|
|
29067
|
-
|
|
29068
|
-
|
|
29136
|
+
let channelPath = "";
|
|
29137
|
+
if (url2.pathname.endsWith("/daemon")) {
|
|
29138
|
+
channelPath = url2.pathname.slice(0, -"/daemon".length);
|
|
29139
|
+
} else {
|
|
29140
|
+
const runtimePath = "/v1/runtimes/connect";
|
|
29141
|
+
if (!url2.pathname.endsWith(runtimePath)) {
|
|
29142
|
+
throw new PromptArtifactError("artifact_endpoint_invalid", "The Relay artifact endpoint is invalid");
|
|
29143
|
+
}
|
|
29144
|
+
channelPath = `${url2.pathname.slice(0, -runtimePath.length)}/channels/${encodeURIComponent(config2.channelId)}`;
|
|
29069
29145
|
}
|
|
29070
|
-
url2.pathname = `${
|
|
29146
|
+
url2.pathname = `${channelPath}/sessions/${encodeURIComponent(command.sessionId)}/commands/${encodeURIComponent(command.commandId)}/artifacts/${encodeURIComponent(reference.artifactId)}/content`;
|
|
29071
29147
|
url2.searchParams.delete("liveness");
|
|
29072
29148
|
return url2;
|
|
29073
29149
|
}
|
|
@@ -29217,17 +29293,27 @@ import { WebSocket } from "undici";
|
|
|
29217
29293
|
var CONNECTION_TIMEOUT_MILLISECONDS = 15e3;
|
|
29218
29294
|
var RelayConnectionError = class extends Error {
|
|
29219
29295
|
code;
|
|
29220
|
-
|
|
29296
|
+
diagnosticDetails;
|
|
29297
|
+
constructor(code, message, diagnosticDetails = {}, options = {}) {
|
|
29221
29298
|
super(message, options);
|
|
29222
29299
|
this.code = code;
|
|
29300
|
+
this.diagnosticDetails = diagnosticDetails;
|
|
29223
29301
|
this.name = "RelayConnectionError";
|
|
29224
29302
|
}
|
|
29225
29303
|
};
|
|
29226
29304
|
function connectRelay(url2, signal) {
|
|
29227
29305
|
signal.throwIfAborted();
|
|
29228
29306
|
return new Promise((resolve5, reject) => {
|
|
29229
|
-
const
|
|
29230
|
-
|
|
29307
|
+
const endpoint = new URL(url2);
|
|
29308
|
+
const headers = {};
|
|
29309
|
+
if (endpoint.pathname.endsWith("/v1/runtimes/connect")) {
|
|
29310
|
+
const runtimeCredential = endpoint.searchParams.get("token") ?? "";
|
|
29311
|
+
endpoint.searchParams.delete("token");
|
|
29312
|
+
headers["authorization"] = `Bearer ${runtimeCredential}`;
|
|
29313
|
+
}
|
|
29314
|
+
const socket = new WebSocket(endpoint, {
|
|
29315
|
+
dispatcher: environmentProxyDispatcher,
|
|
29316
|
+
headers
|
|
29231
29317
|
});
|
|
29232
29318
|
let finished = false;
|
|
29233
29319
|
const timeout = setTimeout(onTimeout, CONNECTION_TIMEOUT_MILLISECONDS);
|
|
@@ -29249,16 +29335,34 @@ function connectRelay(url2, signal) {
|
|
|
29249
29335
|
};
|
|
29250
29336
|
const onAbort = () => {
|
|
29251
29337
|
fail(
|
|
29252
|
-
new RelayConnectionError(
|
|
29253
|
-
|
|
29254
|
-
|
|
29338
|
+
new RelayConnectionError(
|
|
29339
|
+
"relay_connection_cancelled",
|
|
29340
|
+
"Relay connection was cancelled",
|
|
29341
|
+
{
|
|
29342
|
+
cancellation: "requested"
|
|
29343
|
+
},
|
|
29344
|
+
{ cause: signal.reason }
|
|
29345
|
+
)
|
|
29255
29346
|
);
|
|
29256
29347
|
};
|
|
29257
|
-
const onClose = () => {
|
|
29258
|
-
fail(
|
|
29348
|
+
const onClose = (event) => {
|
|
29349
|
+
fail(
|
|
29350
|
+
new RelayConnectionError("relay_connection_closed", "Relay connection closed before it was ready", {
|
|
29351
|
+
closeCode: event.code.toString(),
|
|
29352
|
+
closeReason: event.reason
|
|
29353
|
+
})
|
|
29354
|
+
);
|
|
29259
29355
|
};
|
|
29260
|
-
const onError = () => {
|
|
29261
|
-
|
|
29356
|
+
const onError = (event) => {
|
|
29357
|
+
const cause = Reflect.get(event, "error");
|
|
29358
|
+
fail(
|
|
29359
|
+
new RelayConnectionError(
|
|
29360
|
+
"relay_connection_failed",
|
|
29361
|
+
"Relay connection failed",
|
|
29362
|
+
{},
|
|
29363
|
+
{ cause: cause ?? event }
|
|
29364
|
+
)
|
|
29365
|
+
);
|
|
29262
29366
|
};
|
|
29263
29367
|
const onOpen = () => {
|
|
29264
29368
|
if (finished) {
|
|
@@ -29269,7 +29373,11 @@ function connectRelay(url2, signal) {
|
|
|
29269
29373
|
resolve5(socket);
|
|
29270
29374
|
};
|
|
29271
29375
|
function onTimeout() {
|
|
29272
|
-
fail(
|
|
29376
|
+
fail(
|
|
29377
|
+
new RelayConnectionError("relay_connection_timeout", "Relay connection timed out", {
|
|
29378
|
+
timeoutMilliseconds: CONNECTION_TIMEOUT_MILLISECONDS.toString()
|
|
29379
|
+
})
|
|
29380
|
+
);
|
|
29273
29381
|
}
|
|
29274
29382
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
29275
29383
|
socket.addEventListener("close", onClose, { once: true });
|
|
@@ -29281,6 +29389,142 @@ function connectRelay(url2, signal) {
|
|
|
29281
29389
|
});
|
|
29282
29390
|
}
|
|
29283
29391
|
|
|
29392
|
+
// src/relay-connection/diagnostics.ts
|
|
29393
|
+
var MAX_DIAGNOSTIC_TEXT_LENGTH = 256;
|
|
29394
|
+
function sanitizeDiagnosticText(value) {
|
|
29395
|
+
const printable = Array.from(value, (character) => {
|
|
29396
|
+
const code = character.charCodeAt(0);
|
|
29397
|
+
return code < 32 || code === 127 ? " " : character;
|
|
29398
|
+
}).join("");
|
|
29399
|
+
const withoutSecrets = printable.replace(/([?&](?:access_token|authorization|credential|key|secret|token)=)[^&\s]*/giu, "$1[redacted]").replace(/(?:https?|wss?):\/\/[^\s)]+/giu, (encodedUrl) => {
|
|
29400
|
+
try {
|
|
29401
|
+
return new URL(encodedUrl).origin;
|
|
29402
|
+
} catch {
|
|
29403
|
+
return "[redacted-url]";
|
|
29404
|
+
}
|
|
29405
|
+
});
|
|
29406
|
+
return withoutSecrets.slice(0, MAX_DIAGNOSTIC_TEXT_LENGTH);
|
|
29407
|
+
}
|
|
29408
|
+
function addFailureDetails(details, failure) {
|
|
29409
|
+
let current = failure;
|
|
29410
|
+
for (let depth = 0; depth < 3 && current instanceof Error; depth += 1) {
|
|
29411
|
+
const prefix = depth === 0 ? "failure" : `cause${depth.toString()}`;
|
|
29412
|
+
details[`${prefix}Name`] = current.name;
|
|
29413
|
+
details[`${prefix}Message`] = sanitizeDiagnosticText(current.message);
|
|
29414
|
+
const code = Reflect.get(current, "code");
|
|
29415
|
+
if (typeof code === "string" || typeof code === "number") {
|
|
29416
|
+
details[`${prefix}Code`] = String(code);
|
|
29417
|
+
}
|
|
29418
|
+
const statusCode = Reflect.get(current, "statusCode");
|
|
29419
|
+
if (typeof statusCode === "string" || typeof statusCode === "number") {
|
|
29420
|
+
details[`${prefix}StatusCode`] = String(statusCode);
|
|
29421
|
+
}
|
|
29422
|
+
const diagnosticDetails = Reflect.get(current, "diagnosticDetails");
|
|
29423
|
+
if (typeof diagnosticDetails === "object" && diagnosticDetails !== null) {
|
|
29424
|
+
for (const [key, value] of Object.entries(diagnosticDetails)) {
|
|
29425
|
+
if (typeof value === "string") {
|
|
29426
|
+
details[key] = sanitizeDiagnosticText(value);
|
|
29427
|
+
}
|
|
29428
|
+
}
|
|
29429
|
+
}
|
|
29430
|
+
current = current.cause;
|
|
29431
|
+
}
|
|
29432
|
+
}
|
|
29433
|
+
var RelayConnectionDiagnostics = class {
|
|
29434
|
+
#audit;
|
|
29435
|
+
#channelId;
|
|
29436
|
+
#lastClosedEpoch = 0;
|
|
29437
|
+
#lastFailedEpoch = 0;
|
|
29438
|
+
#relayOrigin;
|
|
29439
|
+
#route;
|
|
29440
|
+
constructor(audit, channelId, relayUrl) {
|
|
29441
|
+
this.#audit = audit;
|
|
29442
|
+
this.#channelId = channelId;
|
|
29443
|
+
this.#relayOrigin = relayUrl.origin;
|
|
29444
|
+
this.#route = describeEnvironmentProxyRoute(relayUrl);
|
|
29445
|
+
}
|
|
29446
|
+
catalogFailed = (failure) => {
|
|
29447
|
+
const details = { ...this.#route, relayOrigin: this.#relayOrigin };
|
|
29448
|
+
addFailureDetails(details, failure);
|
|
29449
|
+
this.#record("agent_catalog_publish_failed", "catalog", details, true);
|
|
29450
|
+
};
|
|
29451
|
+
connectionClosed = (connectionEpoch, code, reason, source) => {
|
|
29452
|
+
if (this.#lastClosedEpoch === connectionEpoch) {
|
|
29453
|
+
return;
|
|
29454
|
+
}
|
|
29455
|
+
this.#lastClosedEpoch = connectionEpoch;
|
|
29456
|
+
this.#record(
|
|
29457
|
+
"relay_connection_closed",
|
|
29458
|
+
connectionEpoch.toString(),
|
|
29459
|
+
{
|
|
29460
|
+
closeCode: code.toString(),
|
|
29461
|
+
closeReason: sanitizeDiagnosticText(reason),
|
|
29462
|
+
connectionEpoch: connectionEpoch.toString(),
|
|
29463
|
+
relayOrigin: this.#relayOrigin,
|
|
29464
|
+
source,
|
|
29465
|
+
...this.#route
|
|
29466
|
+
},
|
|
29467
|
+
true
|
|
29468
|
+
);
|
|
29469
|
+
};
|
|
29470
|
+
connectionFailed = (connectionEpoch, failure, source) => {
|
|
29471
|
+
if (this.#lastFailedEpoch === connectionEpoch) {
|
|
29472
|
+
return;
|
|
29473
|
+
}
|
|
29474
|
+
this.#lastFailedEpoch = connectionEpoch;
|
|
29475
|
+
const details = {
|
|
29476
|
+
connectionEpoch: connectionEpoch.toString(),
|
|
29477
|
+
relayOrigin: this.#relayOrigin,
|
|
29478
|
+
source,
|
|
29479
|
+
...this.#route
|
|
29480
|
+
};
|
|
29481
|
+
addFailureDetails(details, failure);
|
|
29482
|
+
this.#record("relay_connection_failed", connectionEpoch.toString(), details, true);
|
|
29483
|
+
};
|
|
29484
|
+
connectionOpened = (connectionEpoch) => {
|
|
29485
|
+
this.#record(
|
|
29486
|
+
"relay_connection_opened",
|
|
29487
|
+
connectionEpoch.toString(),
|
|
29488
|
+
{
|
|
29489
|
+
connectionEpoch: connectionEpoch.toString(),
|
|
29490
|
+
relayOrigin: this.#relayOrigin,
|
|
29491
|
+
...this.#route
|
|
29492
|
+
},
|
|
29493
|
+
false
|
|
29494
|
+
);
|
|
29495
|
+
};
|
|
29496
|
+
heartbeatTimedOut = (connectionEpoch, heartbeatId, timeoutMilliseconds) => {
|
|
29497
|
+
this.#record(
|
|
29498
|
+
"relay_heartbeat_timed_out",
|
|
29499
|
+
connectionEpoch.toString(),
|
|
29500
|
+
{
|
|
29501
|
+
connectionEpoch: connectionEpoch.toString(),
|
|
29502
|
+
heartbeatId,
|
|
29503
|
+
relayOrigin: this.#relayOrigin,
|
|
29504
|
+
timeoutMilliseconds: timeoutMilliseconds.toString(),
|
|
29505
|
+
...this.#route
|
|
29506
|
+
},
|
|
29507
|
+
true
|
|
29508
|
+
);
|
|
29509
|
+
};
|
|
29510
|
+
#record = (event, commandId, details, warning) => {
|
|
29511
|
+
this.#audit.record({
|
|
29512
|
+
commandId,
|
|
29513
|
+
details,
|
|
29514
|
+
event,
|
|
29515
|
+
sessionId: this.#channelId
|
|
29516
|
+
});
|
|
29517
|
+
const encoded = JSON.stringify({ channelId: this.#channelId, event, ...details });
|
|
29518
|
+
if (warning) {
|
|
29519
|
+
process.stderr.write(`Mobius relay diagnostic ${encoded}
|
|
29520
|
+
`);
|
|
29521
|
+
return;
|
|
29522
|
+
}
|
|
29523
|
+
process.stdout.write(`Mobius relay diagnostic ${encoded}
|
|
29524
|
+
`);
|
|
29525
|
+
};
|
|
29526
|
+
};
|
|
29527
|
+
|
|
29284
29528
|
// src/relay-connection/liveness.ts
|
|
29285
29529
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
29286
29530
|
var HEARTBEAT_ACK_TIMEOUT_MILLISECONDS = 1e4;
|
|
@@ -29357,9 +29601,10 @@ var RelayConnectionLiveness = class {
|
|
|
29357
29601
|
if (!(this.#state instanceof AwaitingHeartbeatAckState)) {
|
|
29358
29602
|
throw new Error("Heartbeat timeout occurred in an invalid state");
|
|
29359
29603
|
}
|
|
29604
|
+
const heartbeatId = this.#state.heartbeatId;
|
|
29360
29605
|
this.#state.timeout.cancel();
|
|
29361
29606
|
this.#state = new FinishedRelayLivenessState();
|
|
29362
|
-
this.#transport.unresponsive();
|
|
29607
|
+
this.#transport.unresponsive(heartbeatId, HEARTBEAT_ACK_TIMEOUT_MILLISECONDS);
|
|
29363
29608
|
};
|
|
29364
29609
|
#sendHeartbeat = () => {
|
|
29365
29610
|
if (this.#state instanceof WaitingForHeartbeatState) {
|
|
@@ -29408,6 +29653,7 @@ var SessionEventOutbox = class {
|
|
|
29408
29653
|
ordinal INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
29409
29654
|
event_id TEXT NOT NULL UNIQUE,
|
|
29410
29655
|
session_id TEXT NOT NULL,
|
|
29656
|
+
source_key TEXT NOT NULL,
|
|
29411
29657
|
envelope_json TEXT NOT NULL
|
|
29412
29658
|
) STRICT;
|
|
29413
29659
|
CREATE INDEX IF NOT EXISTS relay_session_event_outbox_order
|
|
@@ -29435,6 +29681,27 @@ var SessionEventOutbox = class {
|
|
|
29435
29681
|
)
|
|
29436
29682
|
) STRICT;
|
|
29437
29683
|
`);
|
|
29684
|
+
const outboxColumns = this.#database.prepare("PRAGMA table_info(relay_session_event_outbox)").all();
|
|
29685
|
+
const hasSourceKey = outboxColumns.some(
|
|
29686
|
+
(column) => typeof column === "object" && column !== null && Reflect.get(column, "name") === "source_key"
|
|
29687
|
+
);
|
|
29688
|
+
if (!hasSourceKey) {
|
|
29689
|
+
this.#database.exec(`
|
|
29690
|
+
ALTER TABLE relay_session_event_outbox
|
|
29691
|
+
ADD COLUMN source_key TEXT NOT NULL DEFAULT '';
|
|
29692
|
+
`);
|
|
29693
|
+
}
|
|
29694
|
+
this.#database.exec(`
|
|
29695
|
+
UPDATE relay_session_event_outbox
|
|
29696
|
+
SET source_key = CASE json_extract(envelope_json, '$.source.kind')
|
|
29697
|
+
WHEN 'attempt' THEN
|
|
29698
|
+
'attempt:' || session_id || ':' || json_extract(envelope_json, '$.source.attemptId')
|
|
29699
|
+
ELSE 'runtime:' || session_id
|
|
29700
|
+
END
|
|
29701
|
+
WHERE source_key = '';
|
|
29702
|
+
CREATE INDEX IF NOT EXISTS relay_session_event_outbox_source_order
|
|
29703
|
+
ON relay_session_event_outbox(source_key, ordinal);
|
|
29704
|
+
`);
|
|
29438
29705
|
}
|
|
29439
29706
|
claimExecution = (commandId, sessionId, commandJson) => {
|
|
29440
29707
|
this.#database.exec("BEGIN IMMEDIATE");
|
|
@@ -29492,12 +29759,10 @@ var SessionEventOutbox = class {
|
|
|
29492
29759
|
const state = typeof row === "object" && row !== null ? Reflect.get(row, "state") : void 0;
|
|
29493
29760
|
return state === "succeeded" || state === "failed" || state === "cancelled";
|
|
29494
29761
|
};
|
|
29495
|
-
acknowledge = (acknowledgement) =>
|
|
29496
|
-
|
|
29497
|
-
`DELETE FROM relay_session_event_outbox
|
|
29762
|
+
acknowledge = (acknowledgement) => this.#database.prepare(
|
|
29763
|
+
`DELETE FROM relay_session_event_outbox
|
|
29498
29764
|
WHERE event_id = ? AND session_id = ?`
|
|
29499
|
-
|
|
29500
|
-
};
|
|
29765
|
+
).run(acknowledgement.eventId, acknowledgement.sessionId).changes === 1;
|
|
29501
29766
|
quarantine = (rejection) => {
|
|
29502
29767
|
this.#database.exec("BEGIN IMMEDIATE");
|
|
29503
29768
|
try {
|
|
@@ -29564,9 +29829,9 @@ var SessionEventOutbox = class {
|
|
|
29564
29829
|
).run(sourceKey, nextSourceSeq + 1);
|
|
29565
29830
|
this.#database.prepare(
|
|
29566
29831
|
`INSERT INTO relay_session_event_outbox (
|
|
29567
|
-
event_id, session_id, envelope_json
|
|
29568
|
-
) VALUES (?, ?, ?)`
|
|
29569
|
-
).run(envelope2.eventId, envelope2.sessionId, encoded);
|
|
29832
|
+
event_id, session_id, source_key, envelope_json
|
|
29833
|
+
) VALUES (?, ?, ?, ?)`
|
|
29834
|
+
).run(envelope2.eventId, envelope2.sessionId, sourceKey, encoded);
|
|
29570
29835
|
this.#database.exec("COMMIT");
|
|
29571
29836
|
return encoded;
|
|
29572
29837
|
} catch (error61) {
|
|
@@ -29579,6 +29844,27 @@ var SessionEventOutbox = class {
|
|
|
29579
29844
|
FROM relay_session_event_outbox
|
|
29580
29845
|
ORDER BY ordinal`
|
|
29581
29846
|
).all().map(readPendingEnvelope);
|
|
29847
|
+
pendingBatch = (limit) => {
|
|
29848
|
+
if (!Number.isSafeInteger(limit) || limit < 1) {
|
|
29849
|
+
throw new Error("Session event replay batch limit must be a positive integer");
|
|
29850
|
+
}
|
|
29851
|
+
return this.#database.prepare(
|
|
29852
|
+
`WITH ranked_events AS (
|
|
29853
|
+
SELECT ordinal,
|
|
29854
|
+
ROW_NUMBER() OVER (
|
|
29855
|
+
PARTITION BY source_key
|
|
29856
|
+
ORDER BY ordinal
|
|
29857
|
+
) AS source_position
|
|
29858
|
+
FROM relay_session_event_outbox
|
|
29859
|
+
)
|
|
29860
|
+
SELECT outbox.envelope_json
|
|
29861
|
+
FROM ranked_events AS ranked
|
|
29862
|
+
INNER JOIN relay_session_event_outbox AS outbox
|
|
29863
|
+
ON outbox.ordinal = ranked.ordinal
|
|
29864
|
+
ORDER BY ranked.source_position, outbox.ordinal
|
|
29865
|
+
LIMIT ?`
|
|
29866
|
+
).all(limit).map(readPendingEnvelope);
|
|
29867
|
+
};
|
|
29582
29868
|
};
|
|
29583
29869
|
function isDaemonExecutionState(value) {
|
|
29584
29870
|
return value === "cancelled" || value === "dispatching" || value === "failed" || value === "running" || value === "succeeded";
|
|
@@ -29595,7 +29881,7 @@ var systemDaemonTimer = new SystemDaemonTimer();
|
|
|
29595
29881
|
|
|
29596
29882
|
// src/run-daemon.ts
|
|
29597
29883
|
var RELAY_RECONNECT_DELAY_MILLISECONDS = 500;
|
|
29598
|
-
var
|
|
29884
|
+
var AGENT_ACTIVITY_COALESCING_MILLISECONDS = 100;
|
|
29599
29885
|
var ACTIVE_TURN_RECONCILIATION_TIMEOUT_MILLISECONDS = 2e4;
|
|
29600
29886
|
var ACTIVE_TURN_RECONCILIATION_CLOSE_CODE = 4009;
|
|
29601
29887
|
function botIpcSocketPath() {
|
|
@@ -29604,54 +29890,6 @@ function botIpcSocketPath() {
|
|
|
29604
29890
|
}
|
|
29605
29891
|
return join16("/tmp", `mobius-bot-${randomUUID6()}.sock`);
|
|
29606
29892
|
}
|
|
29607
|
-
var RelayConnectionRefreshState = class {
|
|
29608
|
-
};
|
|
29609
|
-
var ScheduledRelayConnectionRefreshState = class extends RelayConnectionRefreshState {
|
|
29610
|
-
};
|
|
29611
|
-
var DueRelayConnectionRefreshState = class extends RelayConnectionRefreshState {
|
|
29612
|
-
};
|
|
29613
|
-
var FinishedRelayConnectionRefreshState = class extends RelayConnectionRefreshState {
|
|
29614
|
-
};
|
|
29615
|
-
var RelayConnectionRefresh = class {
|
|
29616
|
-
#isSafe;
|
|
29617
|
-
#refresh;
|
|
29618
|
-
#scheduledTask;
|
|
29619
|
-
#state = new ScheduledRelayConnectionRefreshState();
|
|
29620
|
-
constructor(timer, isSafe, refresh) {
|
|
29621
|
-
this.#isSafe = isSafe;
|
|
29622
|
-
this.#refresh = refresh;
|
|
29623
|
-
this.#scheduledTask = timer.schedule(this.#request, RELAY_CONNECTION_REFRESH_MILLISECONDS);
|
|
29624
|
-
}
|
|
29625
|
-
afterTurnFinished = () => {
|
|
29626
|
-
if (this.#state instanceof DueRelayConnectionRefreshState) {
|
|
29627
|
-
this.#request();
|
|
29628
|
-
return;
|
|
29629
|
-
}
|
|
29630
|
-
if (this.#state instanceof ScheduledRelayConnectionRefreshState || this.#state instanceof FinishedRelayConnectionRefreshState) {
|
|
29631
|
-
return;
|
|
29632
|
-
}
|
|
29633
|
-
throw new Error("Unknown relay connection refresh state");
|
|
29634
|
-
};
|
|
29635
|
-
close = () => {
|
|
29636
|
-
this.#scheduledTask.cancel();
|
|
29637
|
-
this.#state = new FinishedRelayConnectionRefreshState();
|
|
29638
|
-
};
|
|
29639
|
-
#request = () => {
|
|
29640
|
-
if (this.#state instanceof FinishedRelayConnectionRefreshState) {
|
|
29641
|
-
return;
|
|
29642
|
-
}
|
|
29643
|
-
if (!(this.#state instanceof ScheduledRelayConnectionRefreshState) && !(this.#state instanceof DueRelayConnectionRefreshState)) {
|
|
29644
|
-
throw new Error("Unknown relay connection refresh state");
|
|
29645
|
-
}
|
|
29646
|
-
if (!this.#isSafe()) {
|
|
29647
|
-
this.#state = new DueRelayConnectionRefreshState();
|
|
29648
|
-
return;
|
|
29649
|
-
}
|
|
29650
|
-
this.#state = new FinishedRelayConnectionRefreshState();
|
|
29651
|
-
this.#scheduledTask.cancel();
|
|
29652
|
-
this.#refresh();
|
|
29653
|
-
};
|
|
29654
|
-
};
|
|
29655
29893
|
var DaemonAcpObserver = class {
|
|
29656
29894
|
#record;
|
|
29657
29895
|
constructor(record2) {
|
|
@@ -29848,10 +30086,17 @@ var ActiveTurnReconciliationError = class extends Error {
|
|
|
29848
30086
|
function activeTurnReconciliationEndpoint(config2, turn) {
|
|
29849
30087
|
const endpoint = new URL(config2.relayUrl);
|
|
29850
30088
|
endpoint.protocol = endpoint.protocol === "wss:" ? "https:" : "http:";
|
|
29851
|
-
|
|
29852
|
-
|
|
30089
|
+
let channelPath = "";
|
|
30090
|
+
if (endpoint.pathname.endsWith("/daemon")) {
|
|
30091
|
+
channelPath = endpoint.pathname.slice(0, -"/daemon".length);
|
|
30092
|
+
} else {
|
|
30093
|
+
const runtimePath = "/v1/runtimes/connect";
|
|
30094
|
+
if (!endpoint.pathname.endsWith(runtimePath)) {
|
|
30095
|
+
throw new ActiveTurnReconciliationError("The Relay URL is invalid");
|
|
30096
|
+
}
|
|
30097
|
+
channelPath = `${endpoint.pathname.slice(0, -runtimePath.length)}/channels/${encodeURIComponent(config2.channelId)}`;
|
|
29853
30098
|
}
|
|
29854
|
-
endpoint.pathname = `${
|
|
30099
|
+
endpoint.pathname = `${channelPath}/sessions/${encodeURIComponent(turn.command.sessionId)}/attempts/${encodeURIComponent(turn.attemptId)}/reconciliation`;
|
|
29855
30100
|
endpoint.searchParams.delete("liveness");
|
|
29856
30101
|
return endpoint;
|
|
29857
30102
|
}
|
|
@@ -29909,7 +30154,8 @@ var DaemonSessionEventConnectionState = class {
|
|
|
29909
30154
|
};
|
|
29910
30155
|
var DisconnectedDaemonSessionEventState = class extends DaemonSessionEventConnectionState {
|
|
29911
30156
|
disconnect = () => this;
|
|
29912
|
-
|
|
30157
|
+
matches = () => false;
|
|
30158
|
+
send = () => false;
|
|
29913
30159
|
};
|
|
29914
30160
|
var ConnectedDaemonSessionEventState = class extends DaemonSessionEventConnectionState {
|
|
29915
30161
|
#socket;
|
|
@@ -29918,38 +30164,155 @@ var ConnectedDaemonSessionEventState = class extends DaemonSessionEventConnectio
|
|
|
29918
30164
|
this.#socket = socket;
|
|
29919
30165
|
}
|
|
29920
30166
|
disconnect = (socket) => socket === this.#socket ? new DisconnectedDaemonSessionEventState() : this;
|
|
30167
|
+
matches = (socket) => socket === this.#socket;
|
|
29921
30168
|
send = (message) => {
|
|
29922
|
-
if (this.#socket.readyState
|
|
29923
|
-
|
|
29924
|
-
|
|
29925
|
-
|
|
29926
|
-
|
|
30169
|
+
if (this.#socket.readyState !== WebSocket2.OPEN) {
|
|
30170
|
+
return false;
|
|
30171
|
+
}
|
|
30172
|
+
try {
|
|
30173
|
+
this.#socket.send(message);
|
|
30174
|
+
return true;
|
|
30175
|
+
} catch {
|
|
30176
|
+
return false;
|
|
29927
30177
|
}
|
|
29928
30178
|
};
|
|
29929
30179
|
};
|
|
30180
|
+
var SESSION_EVENT_REPLAY_WINDOW = 4;
|
|
29930
30181
|
var DaemonSessionEventPublisher = class {
|
|
29931
30182
|
#outbox;
|
|
30183
|
+
#inFlightEventIds = /* @__PURE__ */ new Set();
|
|
29932
30184
|
#connection = new DisconnectedDaemonSessionEventState();
|
|
29933
30185
|
constructor(outbox) {
|
|
29934
30186
|
this.#outbox = outbox;
|
|
29935
30187
|
}
|
|
29936
30188
|
connect = (socket) => {
|
|
29937
|
-
|
|
29938
|
-
this.#
|
|
29939
|
-
|
|
29940
|
-
connection.send(pending);
|
|
29941
|
-
}
|
|
30189
|
+
this.#connection = new ConnectedDaemonSessionEventState(socket);
|
|
30190
|
+
this.#inFlightEventIds.clear();
|
|
30191
|
+
this.#publishNextBatch();
|
|
29942
30192
|
};
|
|
29943
30193
|
disconnect = (socket) => {
|
|
30194
|
+
if (!this.#connection.matches(socket)) {
|
|
30195
|
+
return;
|
|
30196
|
+
}
|
|
29944
30197
|
this.#connection = this.#connection.disconnect(socket);
|
|
30198
|
+
this.#inFlightEventIds.clear();
|
|
29945
30199
|
};
|
|
29946
30200
|
recordAttempt = (sessionId, payload, attemptId) => {
|
|
29947
|
-
this.#
|
|
30201
|
+
this.#outbox.enqueue(sessionId, payload, attemptId);
|
|
30202
|
+
this.#publishNextBatch();
|
|
29948
30203
|
};
|
|
29949
30204
|
recordRuntime = (sessionId, payload) => {
|
|
29950
|
-
this.#
|
|
30205
|
+
this.#outbox.enqueue(sessionId, payload);
|
|
30206
|
+
this.#publishNextBatch();
|
|
30207
|
+
};
|
|
30208
|
+
acknowledge = (acknowledgement) => {
|
|
30209
|
+
if (!this.#outbox.acknowledge(acknowledgement)) {
|
|
30210
|
+
return;
|
|
30211
|
+
}
|
|
30212
|
+
this.#inFlightEventIds.delete(acknowledgement.eventId);
|
|
30213
|
+
this.#publishNextBatch();
|
|
30214
|
+
};
|
|
30215
|
+
quarantine = (rejection) => {
|
|
30216
|
+
const quarantined = this.#outbox.quarantine(rejection);
|
|
30217
|
+
if (!quarantined) {
|
|
30218
|
+
return false;
|
|
30219
|
+
}
|
|
30220
|
+
this.#inFlightEventIds.delete(rejection.eventId);
|
|
30221
|
+
this.#publishNextBatch();
|
|
30222
|
+
return true;
|
|
30223
|
+
};
|
|
30224
|
+
#publishNextBatch = () => {
|
|
30225
|
+
if (this.#inFlightEventIds.size > 0) {
|
|
30226
|
+
return;
|
|
30227
|
+
}
|
|
30228
|
+
const batch = this.#outbox.pendingBatch(SESSION_EVENT_REPLAY_WINDOW);
|
|
30229
|
+
for (const encoded of batch) {
|
|
30230
|
+
const envelope2 = decodeSessionEventEnvelope(encoded);
|
|
30231
|
+
if (!this.#connection.send(encoded)) {
|
|
30232
|
+
this.#inFlightEventIds.clear();
|
|
30233
|
+
return;
|
|
30234
|
+
}
|
|
30235
|
+
this.#inFlightEventIds.add(envelope2.eventId);
|
|
30236
|
+
}
|
|
29951
30237
|
};
|
|
29952
30238
|
};
|
|
30239
|
+
var CoalescingDaemonActivityPublisher = class {
|
|
30240
|
+
#pending = /* @__PURE__ */ new Map();
|
|
30241
|
+
#publish;
|
|
30242
|
+
#publishedActivityIds = /* @__PURE__ */ new Set();
|
|
30243
|
+
#scheduled = /* @__PURE__ */ new Map();
|
|
30244
|
+
#timer;
|
|
30245
|
+
constructor(timer, publish) {
|
|
30246
|
+
this.#publish = publish;
|
|
30247
|
+
this.#timer = timer;
|
|
30248
|
+
}
|
|
30249
|
+
flush = () => {
|
|
30250
|
+
const activities = [...this.#pending.values()].sort((left, right) => left.sequence - right.sequence);
|
|
30251
|
+
for (const task of this.#scheduled.values()) {
|
|
30252
|
+
task.cancel();
|
|
30253
|
+
}
|
|
30254
|
+
this.#pending.clear();
|
|
30255
|
+
this.#scheduled.clear();
|
|
30256
|
+
for (const activity of activities) {
|
|
30257
|
+
this.#publish(activity);
|
|
30258
|
+
}
|
|
30259
|
+
};
|
|
30260
|
+
record = (activity) => {
|
|
30261
|
+
if (activity.status !== "pending" && activity.status !== "running") {
|
|
30262
|
+
this.#discardPending(activity.activityId);
|
|
30263
|
+
this.#publishedActivityIds.add(activity.activityId);
|
|
30264
|
+
this.#publish(activity);
|
|
30265
|
+
return Promise.resolve();
|
|
30266
|
+
}
|
|
30267
|
+
if (!this.#publishedActivityIds.has(activity.activityId)) {
|
|
30268
|
+
this.#publishedActivityIds.add(activity.activityId);
|
|
30269
|
+
this.#publish(activity);
|
|
30270
|
+
return Promise.resolve();
|
|
30271
|
+
}
|
|
30272
|
+
this.#pending.set(activity.activityId, activity);
|
|
30273
|
+
if (!this.#scheduled.has(activity.activityId)) {
|
|
30274
|
+
this.#scheduled.set(
|
|
30275
|
+
activity.activityId,
|
|
30276
|
+
this.#timer.schedule(
|
|
30277
|
+
() => this.#flushActivity(activity.activityId),
|
|
30278
|
+
AGENT_ACTIVITY_COALESCING_MILLISECONDS
|
|
30279
|
+
)
|
|
30280
|
+
);
|
|
30281
|
+
}
|
|
30282
|
+
return Promise.resolve();
|
|
30283
|
+
};
|
|
30284
|
+
#discardPending = (activityId) => {
|
|
30285
|
+
this.#scheduled.get(activityId)?.cancel();
|
|
30286
|
+
this.#scheduled.delete(activityId);
|
|
30287
|
+
this.#pending.delete(activityId);
|
|
30288
|
+
};
|
|
30289
|
+
#flushActivity = (activityId) => {
|
|
30290
|
+
const activity = this.#pending.get(activityId);
|
|
30291
|
+
this.#discardPending(activityId);
|
|
30292
|
+
if (activity !== void 0) {
|
|
30293
|
+
this.#publish(activity);
|
|
30294
|
+
}
|
|
30295
|
+
};
|
|
30296
|
+
};
|
|
30297
|
+
function encodeDaemonActivity(command, activity) {
|
|
30298
|
+
return encodeAgentActivityEvent({
|
|
30299
|
+
activityId: activity.activityId,
|
|
30300
|
+
activityType: activity.activityType,
|
|
30301
|
+
commandId: command.commandId,
|
|
30302
|
+
content: activity.content,
|
|
30303
|
+
detail: activity.detail,
|
|
30304
|
+
input: activity.input,
|
|
30305
|
+
kind: AGENT_ACTIVITY_KIND,
|
|
30306
|
+
locations: activity.locations,
|
|
30307
|
+
name: activity.name,
|
|
30308
|
+
output: activity.output,
|
|
30309
|
+
sequence: activity.sequence,
|
|
30310
|
+
sessionId: command.sessionId,
|
|
30311
|
+
status: activity.status,
|
|
30312
|
+
title: activity.title,
|
|
30313
|
+
toolKind: activity.toolKind
|
|
30314
|
+
});
|
|
30315
|
+
}
|
|
29953
30316
|
function reportFailure(publisher, command, code, message, attemptId) {
|
|
29954
30317
|
publisher.recordAttempt(
|
|
29955
30318
|
command.sessionId,
|
|
@@ -30126,7 +30489,10 @@ async function executeSessionConfigure(publisher, socket, command, config2, dire
|
|
|
30126
30489
|
reportOperationFailure(socket, command.requestId, command.sessionId, error61);
|
|
30127
30490
|
}
|
|
30128
30491
|
}
|
|
30129
|
-
async function executePrompt(outbox, publisher, command, config2, directories, audit, sessions, attemptId, signal) {
|
|
30492
|
+
async function executePrompt(outbox, publisher, command, config2, directories, audit, sessions, attemptId, timer, signal) {
|
|
30493
|
+
const activities = new CoalescingDaemonActivityPublisher(timer, (activity) => {
|
|
30494
|
+
publisher.recordAttempt(command.sessionId, encodeDaemonActivity(command, activity), attemptId);
|
|
30495
|
+
});
|
|
30130
30496
|
try {
|
|
30131
30497
|
const resolved = await resolveSessionDirectories(
|
|
30132
30498
|
directories,
|
|
@@ -30183,30 +30549,7 @@ async function executePrompt(outbox, publisher, command, config2, directories, a
|
|
|
30183
30549
|
cwd: resolved.cwd,
|
|
30184
30550
|
disposition: command.disposition,
|
|
30185
30551
|
images: attachments.images,
|
|
30186
|
-
onActivity:
|
|
30187
|
-
publisher.recordAttempt(
|
|
30188
|
-
command.sessionId,
|
|
30189
|
-
encodeAgentActivityEvent({
|
|
30190
|
-
activityId: activity.activityId,
|
|
30191
|
-
activityType: activity.activityType,
|
|
30192
|
-
commandId: command.commandId,
|
|
30193
|
-
content: activity.content,
|
|
30194
|
-
detail: activity.detail,
|
|
30195
|
-
input: activity.input,
|
|
30196
|
-
kind: AGENT_ACTIVITY_KIND,
|
|
30197
|
-
locations: activity.locations,
|
|
30198
|
-
name: activity.name,
|
|
30199
|
-
output: activity.output,
|
|
30200
|
-
sequence: activity.sequence,
|
|
30201
|
-
sessionId: command.sessionId,
|
|
30202
|
-
status: activity.status,
|
|
30203
|
-
title: activity.title,
|
|
30204
|
-
toolKind: activity.toolKind
|
|
30205
|
-
}),
|
|
30206
|
-
attemptId
|
|
30207
|
-
);
|
|
30208
|
-
return Promise.resolve();
|
|
30209
|
-
},
|
|
30552
|
+
onActivity: activities.record,
|
|
30210
30553
|
onText: (text, messageId) => {
|
|
30211
30554
|
publisher.recordAttempt(
|
|
30212
30555
|
command.sessionId,
|
|
@@ -30229,6 +30572,7 @@ async function executePrompt(outbox, publisher, command, config2, directories, a
|
|
|
30229
30572
|
});
|
|
30230
30573
|
stopReason = result.stopReason;
|
|
30231
30574
|
}
|
|
30575
|
+
activities.flush();
|
|
30232
30576
|
publisher.recordAttempt(
|
|
30233
30577
|
command.sessionId,
|
|
30234
30578
|
encodeSessionCompletedEvent({
|
|
@@ -30246,6 +30590,7 @@ async function executePrompt(outbox, publisher, command, config2, directories, a
|
|
|
30246
30590
|
sessionId: command.sessionId
|
|
30247
30591
|
});
|
|
30248
30592
|
} catch (error61) {
|
|
30593
|
+
activities.flush();
|
|
30249
30594
|
audit.record({
|
|
30250
30595
|
commandId: command.commandId,
|
|
30251
30596
|
event: "session_failed",
|
|
@@ -30284,7 +30629,10 @@ function botExecutionAgent(agent, contextHandle, socketPath) {
|
|
|
30284
30629
|
agent.capabilities
|
|
30285
30630
|
);
|
|
30286
30631
|
}
|
|
30287
|
-
async function executeBotPrompt(outbox, publisher, command, config2, directories, audit, sessions, grants, contexts, applicator, ipc, attemptId, signal) {
|
|
30632
|
+
async function executeBotPrompt(outbox, publisher, command, config2, directories, audit, sessions, grants, contexts, applicator, ipc, attemptId, timer, signal) {
|
|
30633
|
+
const activities = new CoalescingDaemonActivityPublisher(timer, (activity) => {
|
|
30634
|
+
publisher.recordAttempt(command.sessionId, encodeDaemonActivity(command, activity), attemptId);
|
|
30635
|
+
});
|
|
30288
30636
|
try {
|
|
30289
30637
|
const deadline = Date.parse(command.executionDeadlineAt);
|
|
30290
30638
|
const remaining = deadline - Date.now();
|
|
@@ -30368,30 +30716,7 @@ async function executeBotPrompt(outbox, publisher, command, config2, directories
|
|
|
30368
30716
|
commandId: command.commandId,
|
|
30369
30717
|
cwd: resolved.cwd,
|
|
30370
30718
|
images: attachments.images,
|
|
30371
|
-
onActivity:
|
|
30372
|
-
publisher.recordAttempt(
|
|
30373
|
-
command.sessionId,
|
|
30374
|
-
encodeAgentActivityEvent({
|
|
30375
|
-
activityId: activity.activityId,
|
|
30376
|
-
activityType: activity.activityType,
|
|
30377
|
-
commandId: command.commandId,
|
|
30378
|
-
content: activity.content,
|
|
30379
|
-
detail: activity.detail,
|
|
30380
|
-
input: activity.input,
|
|
30381
|
-
kind: AGENT_ACTIVITY_KIND,
|
|
30382
|
-
locations: activity.locations,
|
|
30383
|
-
name: activity.name,
|
|
30384
|
-
output: activity.output,
|
|
30385
|
-
sequence: activity.sequence,
|
|
30386
|
-
sessionId: command.sessionId,
|
|
30387
|
-
status: activity.status,
|
|
30388
|
-
title: activity.title,
|
|
30389
|
-
toolKind: activity.toolKind
|
|
30390
|
-
}),
|
|
30391
|
-
attemptId
|
|
30392
|
-
);
|
|
30393
|
-
return Promise.resolve();
|
|
30394
|
-
},
|
|
30719
|
+
onActivity: activities.record,
|
|
30395
30720
|
onText: (text, messageId) => {
|
|
30396
30721
|
publisher.recordAttempt(
|
|
30397
30722
|
command.sessionId,
|
|
@@ -30411,6 +30736,7 @@ async function executeBotPrompt(outbox, publisher, command, config2, directories
|
|
|
30411
30736
|
sessionId: command.sessionId,
|
|
30412
30737
|
signal: executionSignal
|
|
30413
30738
|
});
|
|
30739
|
+
activities.flush();
|
|
30414
30740
|
publisher.recordAttempt(
|
|
30415
30741
|
command.sessionId,
|
|
30416
30742
|
encodeSessionCompletedEvent({
|
|
@@ -30435,6 +30761,7 @@ async function executeBotPrompt(outbox, publisher, command, config2, directories
|
|
|
30435
30761
|
lease.release();
|
|
30436
30762
|
}
|
|
30437
30763
|
} catch (error61) {
|
|
30764
|
+
activities.flush();
|
|
30438
30765
|
const failure = Date.parse(command.executionDeadlineAt) <= Date.now() ? new LocalBotAuthorizationError("turn_expired", "The Bot execution deadline has expired", {
|
|
30439
30766
|
cause: error61
|
|
30440
30767
|
}) : error61;
|
|
@@ -30474,15 +30801,15 @@ function waitForRelayRetry(signal, timer) {
|
|
|
30474
30801
|
signal.addEventListener("abort", finish, { once: true });
|
|
30475
30802
|
});
|
|
30476
30803
|
}
|
|
30477
|
-
async function publishAgentCatalogUntilAvailable(config2, signal, timer) {
|
|
30804
|
+
async function publishAgentCatalogUntilAvailable(config2, diagnostics, signal, timer) {
|
|
30478
30805
|
try {
|
|
30479
30806
|
await publishAgentCatalog(config2, signal);
|
|
30480
30807
|
return;
|
|
30481
|
-
} catch {
|
|
30808
|
+
} catch (error61) {
|
|
30482
30809
|
if (signal.aborted) {
|
|
30483
30810
|
return;
|
|
30484
30811
|
}
|
|
30485
|
-
|
|
30812
|
+
diagnostics.catalogFailed(error61);
|
|
30486
30813
|
}
|
|
30487
30814
|
while (!signal.aborted) {
|
|
30488
30815
|
await waitForRelayRetry(signal, timer);
|
|
@@ -30496,11 +30823,12 @@ async function publishAgentCatalogUntilAvailable(config2, signal, timer) {
|
|
|
30496
30823
|
}
|
|
30497
30824
|
}
|
|
30498
30825
|
}
|
|
30499
|
-
async function runRelayConnection(config2, directories, audit, sessions, installations, installationObserver, nativeSessions, outbox, publisher, grants, contexts, applicator, ipc, activeTurns, activeAttemptIds, onConnected, signal, timer) {
|
|
30826
|
+
async function runRelayConnection(config2, directories, audit, sessions, installations, installationObserver, nativeSessions, outbox, publisher, grants, contexts, applicator, ipc, activeTurns, activeAttemptIds, connectionEpoch, diagnostics, onConnected, signal, timer) {
|
|
30500
30827
|
const relayUrl = new URL(config2.relayUrl);
|
|
30501
30828
|
relayUrl.searchParams.set("liveness", "1");
|
|
30502
30829
|
const socket = await connectRelay(relayUrl, signal);
|
|
30503
30830
|
const reconnectingTurns = [...activeTurns.values()];
|
|
30831
|
+
diagnostics.connectionOpened(connectionEpoch);
|
|
30504
30832
|
onConnected();
|
|
30505
30833
|
const connectionController = new AbortController();
|
|
30506
30834
|
const nativeRequests = /* @__PURE__ */ new Map();
|
|
@@ -30509,7 +30837,6 @@ async function runRelayConnection(config2, directories, audit, sessions, install
|
|
|
30509
30837
|
await new Promise((resolve5, reject) => {
|
|
30510
30838
|
let finished = false;
|
|
30511
30839
|
const cleanup = () => {
|
|
30512
|
-
connectionRefresh.close();
|
|
30513
30840
|
connectionLiveness.close();
|
|
30514
30841
|
signal.removeEventListener("abort", stop);
|
|
30515
30842
|
socket.removeEventListener("close", handleClose);
|
|
@@ -30527,61 +30854,70 @@ async function runRelayConnection(config2, directories, audit, sessions, install
|
|
|
30527
30854
|
cleanup();
|
|
30528
30855
|
resolve5();
|
|
30529
30856
|
};
|
|
30530
|
-
const rejectConnection = () => {
|
|
30857
|
+
const rejectConnection = (failure) => {
|
|
30531
30858
|
if (finished) {
|
|
30532
30859
|
return;
|
|
30533
30860
|
}
|
|
30534
30861
|
finished = true;
|
|
30535
30862
|
cleanup();
|
|
30536
|
-
reject(
|
|
30863
|
+
reject(failure);
|
|
30537
30864
|
};
|
|
30538
30865
|
const stop = () => {
|
|
30866
|
+
diagnostics.connectionClosed(connectionEpoch, 1e3, "Daemon stopped", "daemon_stop");
|
|
30539
30867
|
closeRelaySocket(socket, 1e3, "Daemon stopped");
|
|
30540
30868
|
resolveConnection();
|
|
30541
30869
|
};
|
|
30542
|
-
const
|
|
30543
|
-
|
|
30544
|
-
|
|
30870
|
+
const failAndClose = (failure, code, reason, source) => {
|
|
30871
|
+
diagnostics.connectionFailed(connectionEpoch, failure, source);
|
|
30872
|
+
diagnostics.connectionClosed(connectionEpoch, code, reason, source);
|
|
30873
|
+
closeRelaySocket(socket, code, reason);
|
|
30874
|
+
rejectConnection(failure);
|
|
30545
30875
|
};
|
|
30546
30876
|
const connectionLiveness = new RelayConnectionLiveness(timer, {
|
|
30547
30877
|
send: (message) => send(socket, message),
|
|
30548
|
-
unresponsive: () => {
|
|
30878
|
+
unresponsive: (heartbeatId, timeoutMilliseconds) => {
|
|
30879
|
+
diagnostics.heartbeatTimedOut(connectionEpoch, heartbeatId, timeoutMilliseconds);
|
|
30880
|
+
diagnostics.connectionClosed(
|
|
30881
|
+
connectionEpoch,
|
|
30882
|
+
4001,
|
|
30883
|
+
"Relay liveness check timed out",
|
|
30884
|
+
"heartbeat_timeout"
|
|
30885
|
+
);
|
|
30549
30886
|
closeRelaySocket(socket, 4001, "Relay liveness check timed out");
|
|
30550
30887
|
resolveConnection();
|
|
30551
30888
|
}
|
|
30552
30889
|
});
|
|
30553
|
-
const connectionRefresh = new RelayConnectionRefresh(
|
|
30554
|
-
timer,
|
|
30555
|
-
() => activeTurns.size === 0,
|
|
30556
|
-
refreshConnection
|
|
30557
|
-
);
|
|
30558
30890
|
signal.addEventListener("abort", stop, { once: true });
|
|
30559
|
-
const handleClose = () => {
|
|
30891
|
+
const handleClose = (event) => {
|
|
30892
|
+
diagnostics.connectionClosed(connectionEpoch, event.code, event.reason, "relay");
|
|
30560
30893
|
resolveConnection();
|
|
30561
30894
|
};
|
|
30562
|
-
const handleError = () => {
|
|
30895
|
+
const handleError = (event) => {
|
|
30563
30896
|
if (signal.aborted) {
|
|
30564
30897
|
resolveConnection();
|
|
30565
30898
|
return;
|
|
30566
30899
|
}
|
|
30567
|
-
|
|
30900
|
+
const cause = Reflect.get(event, "error");
|
|
30901
|
+
const failure = new Error("Relay WebSocket emitted an error", { cause: cause ?? event });
|
|
30902
|
+
diagnostics.connectionFailed(connectionEpoch, failure, "socket_error");
|
|
30903
|
+
diagnostics.connectionClosed(connectionEpoch, 1006, "WebSocket error", "socket_error");
|
|
30904
|
+
rejectConnection(failure);
|
|
30568
30905
|
};
|
|
30569
30906
|
const handleMessage = (event) => {
|
|
30570
30907
|
const encoded = event.data;
|
|
30571
30908
|
if (typeof encoded !== "string") {
|
|
30572
|
-
|
|
30573
|
-
rejectConnection();
|
|
30909
|
+
failAndClose(new Error("Relay sent a non-text message"), 4003, "Text message required", "protocol");
|
|
30574
30910
|
return;
|
|
30575
30911
|
}
|
|
30576
30912
|
try {
|
|
30577
30913
|
const kind = decodeMessageKind(encoded);
|
|
30578
30914
|
if (kind === SESSION_EVENT_ACK_KIND) {
|
|
30579
|
-
|
|
30915
|
+
publisher.acknowledge(decodeSessionEventAcknowledgement(encoded));
|
|
30580
30916
|
return;
|
|
30581
30917
|
}
|
|
30582
30918
|
if (kind === SESSION_EVENT_REJECTED_KIND) {
|
|
30583
30919
|
const rejection = decodeSessionEventRejection(encoded);
|
|
30584
|
-
if (
|
|
30920
|
+
if (publisher.quarantine(rejection)) {
|
|
30585
30921
|
audit.record({
|
|
30586
30922
|
commandId: rejection.eventId,
|
|
30587
30923
|
details: { code: rejection.code },
|
|
@@ -30960,6 +31296,7 @@ async function runRelayConnection(config2, directories, audit, sessions, install
|
|
|
30960
31296
|
applicator,
|
|
30961
31297
|
ipc,
|
|
30962
31298
|
receipt2.attemptId,
|
|
31299
|
+
timer,
|
|
30963
31300
|
turn2.signal
|
|
30964
31301
|
).catch((error61) => {
|
|
30965
31302
|
console.error("Mobius daemon Bot turn failed", error61);
|
|
@@ -30969,7 +31306,6 @@ async function runRelayConnection(config2, directories, audit, sessions, install
|
|
|
30969
31306
|
activeAttemptIds.delete(command2.sessionId);
|
|
30970
31307
|
}
|
|
30971
31308
|
turn2.finish();
|
|
30972
|
-
connectionRefresh.afterTurnFinished();
|
|
30973
31309
|
});
|
|
30974
31310
|
return;
|
|
30975
31311
|
}
|
|
@@ -30998,6 +31334,7 @@ async function runRelayConnection(config2, directories, audit, sessions, install
|
|
|
30998
31334
|
audit,
|
|
30999
31335
|
sessions,
|
|
31000
31336
|
receipt.attemptId,
|
|
31337
|
+
timer,
|
|
31001
31338
|
turn.signal
|
|
31002
31339
|
).catch((error61) => {
|
|
31003
31340
|
console.error("Mobius daemon turn failed", error61);
|
|
@@ -31007,12 +31344,15 @@ async function runRelayConnection(config2, directories, audit, sessions, install
|
|
|
31007
31344
|
activeAttemptIds.delete(command.sessionId);
|
|
31008
31345
|
}
|
|
31009
31346
|
turn.finish();
|
|
31010
|
-
connectionRefresh.afterTurnFinished();
|
|
31011
31347
|
});
|
|
31012
31348
|
} catch (error61) {
|
|
31013
31349
|
console.error("Mobius daemon rejected a message", error61);
|
|
31014
|
-
|
|
31015
|
-
|
|
31350
|
+
failAndClose(
|
|
31351
|
+
new Error("Relay sent an invalid protocol message", { cause: error61 }),
|
|
31352
|
+
4008,
|
|
31353
|
+
"Invalid protocol message",
|
|
31354
|
+
"protocol"
|
|
31355
|
+
);
|
|
31016
31356
|
}
|
|
31017
31357
|
};
|
|
31018
31358
|
socket.addEventListener("close", handleClose);
|
|
@@ -31024,21 +31364,18 @@ async function runRelayConnection(config2, directories, audit, sessions, install
|
|
|
31024
31364
|
return;
|
|
31025
31365
|
}
|
|
31026
31366
|
publisher.connect(socket);
|
|
31027
|
-
void Promise.all(reconnectingTurns.map((turn) => turn.completion)).then(
|
|
31028
|
-
connectionRefresh.afterTurnFinished
|
|
31029
|
-
);
|
|
31030
31367
|
},
|
|
31031
31368
|
(error61) => {
|
|
31032
31369
|
if (finished) {
|
|
31033
31370
|
return;
|
|
31034
31371
|
}
|
|
31035
31372
|
console.error("Mobius daemon could not reconcile active Agent turns", error61);
|
|
31036
|
-
|
|
31037
|
-
|
|
31373
|
+
failAndClose(
|
|
31374
|
+
new Error("Active Agent turn reconciliation failed", { cause: error61 }),
|
|
31038
31375
|
ACTIVE_TURN_RECONCILIATION_CLOSE_CODE,
|
|
31039
|
-
"Active Agent turn reconciliation failed"
|
|
31376
|
+
"Active Agent turn reconciliation failed",
|
|
31377
|
+
"reconciliation"
|
|
31040
31378
|
);
|
|
31041
|
-
rejectConnection();
|
|
31042
31379
|
}
|
|
31043
31380
|
);
|
|
31044
31381
|
if (signal.aborted) {
|
|
@@ -31082,6 +31419,7 @@ async function runDaemon(config2, audit, signal, timer = systemDaemonTimer) {
|
|
|
31082
31419
|
const sessions = new AcpSessionManager(new DaemonAcpObserver(recordSessionEvent), sessionStore);
|
|
31083
31420
|
const directories = new WorkspaceDirectoryService(config2.bindingId, config2.defaultWorkspace);
|
|
31084
31421
|
const installationObserver = new DaemonInstallationObserver();
|
|
31422
|
+
const diagnostics = new RelayConnectionDiagnostics(audit, config2.channelId, config2.relayUrl);
|
|
31085
31423
|
const nativeSessions = new NativeSessionService(config2, directories, sessions, audit);
|
|
31086
31424
|
const installations = new AgentInstallationManager(
|
|
31087
31425
|
specifications,
|
|
@@ -31089,7 +31427,7 @@ async function runDaemon(config2, audit, signal, timer = systemDaemonTimer) {
|
|
|
31089
31427
|
installationObserver,
|
|
31090
31428
|
installationAudit
|
|
31091
31429
|
);
|
|
31092
|
-
let
|
|
31430
|
+
let connectionEpoch = 0;
|
|
31093
31431
|
let connectionAnnounced = false;
|
|
31094
31432
|
const announceConnection = () => {
|
|
31095
31433
|
if (connectionAnnounced) {
|
|
@@ -31105,10 +31443,11 @@ async function runDaemon(config2, audit, signal, timer = systemDaemonTimer) {
|
|
|
31105
31443
|
`
|
|
31106
31444
|
);
|
|
31107
31445
|
};
|
|
31108
|
-
const catalogPublication = publishAgentCatalogUntilAvailable(config2, signal, timer);
|
|
31446
|
+
const catalogPublication = publishAgentCatalogUntilAvailable(config2, diagnostics, signal, timer);
|
|
31109
31447
|
try {
|
|
31110
31448
|
await ipc.listen(signal);
|
|
31111
31449
|
while (!signal.aborted) {
|
|
31450
|
+
connectionEpoch += 1;
|
|
31112
31451
|
try {
|
|
31113
31452
|
await runRelayConnection(
|
|
31114
31453
|
config2,
|
|
@@ -31126,19 +31465,17 @@ async function runDaemon(config2, audit, signal, timer = systemDaemonTimer) {
|
|
|
31126
31465
|
ipc,
|
|
31127
31466
|
activeTurns,
|
|
31128
31467
|
activeAttemptIds,
|
|
31468
|
+
connectionEpoch,
|
|
31469
|
+
diagnostics,
|
|
31129
31470
|
announceConnection,
|
|
31130
31471
|
signal,
|
|
31131
31472
|
timer
|
|
31132
31473
|
);
|
|
31133
|
-
|
|
31134
|
-
} catch {
|
|
31474
|
+
} catch (error61) {
|
|
31135
31475
|
if (signal.aborted) {
|
|
31136
31476
|
return;
|
|
31137
31477
|
}
|
|
31138
|
-
|
|
31139
|
-
console.warn("Mobius daemon is waiting for the relay");
|
|
31140
|
-
retryAnnounced = true;
|
|
31141
|
-
}
|
|
31478
|
+
diagnostics.connectionFailed(connectionEpoch, error61, "connection");
|
|
31142
31479
|
}
|
|
31143
31480
|
await waitForRelayRetry(signal, timer);
|
|
31144
31481
|
}
|
|
@@ -31176,6 +31513,69 @@ async function runDaemon(config2, audit, signal, timer = systemDaemonTimer) {
|
|
|
31176
31513
|
}
|
|
31177
31514
|
}
|
|
31178
31515
|
|
|
31516
|
+
// src/runtime.ts
|
|
31517
|
+
var APP_CREDENTIAL_PATTERN = /^mapp_[a-zA-Z0-9_-]{22}_[a-zA-Z0-9_-]{43}$/;
|
|
31518
|
+
var CONVERSATION_CREDENTIAL_PATTERN = /^mconv_[a-zA-Z0-9_-]{22}_[a-zA-Z0-9_-]{43}$/;
|
|
31519
|
+
var RUNTIME_CREDENTIAL_PATTERN = /^[a-zA-Z0-9_-]{32,256}$/;
|
|
31520
|
+
var MobiusRuntimeConfigurationError = class extends Error {
|
|
31521
|
+
code = "runtime_configuration_invalid";
|
|
31522
|
+
constructor(message, options = {}) {
|
|
31523
|
+
super(message, options);
|
|
31524
|
+
this.name = "MobiusRuntimeConfigurationError";
|
|
31525
|
+
}
|
|
31526
|
+
};
|
|
31527
|
+
var MobiusRuntimeState = class {
|
|
31528
|
+
};
|
|
31529
|
+
var IdleMobiusRuntimeState = class extends MobiusRuntimeState {
|
|
31530
|
+
};
|
|
31531
|
+
var RunningMobiusRuntimeState = class extends MobiusRuntimeState {
|
|
31532
|
+
};
|
|
31533
|
+
var FinishedMobiusRuntimeState = class extends MobiusRuntimeState {
|
|
31534
|
+
};
|
|
31535
|
+
function validateRuntimeRelay(relayUrl) {
|
|
31536
|
+
if (relayUrl.protocol !== "ws:" && relayUrl.protocol !== "wss:") {
|
|
31537
|
+
throw new MobiusRuntimeConfigurationError("The Runtime relay URL must use WebSocket transport");
|
|
31538
|
+
}
|
|
31539
|
+
const credential = relayUrl.searchParams.get("token") ?? "";
|
|
31540
|
+
if (credential.length === 0) {
|
|
31541
|
+
throw new MobiusRuntimeConfigurationError("The Runtime relay credential is required");
|
|
31542
|
+
}
|
|
31543
|
+
if (APP_CREDENTIAL_PATTERN.test(credential)) {
|
|
31544
|
+
throw new MobiusRuntimeConfigurationError("An AppCredential cannot be held by a local Runtime");
|
|
31545
|
+
}
|
|
31546
|
+
if (CONVERSATION_CREDENTIAL_PATTERN.test(credential)) {
|
|
31547
|
+
throw new MobiusRuntimeConfigurationError("A ConversationAccessToken cannot be held by a local Runtime");
|
|
31548
|
+
}
|
|
31549
|
+
if (!RUNTIME_CREDENTIAL_PATTERN.test(credential)) {
|
|
31550
|
+
throw new MobiusRuntimeConfigurationError("The Runtime relay credential is invalid");
|
|
31551
|
+
}
|
|
31552
|
+
}
|
|
31553
|
+
function validateRuntimeConfiguration(config2) {
|
|
31554
|
+
validateRuntimeRelay(config2.relayUrl);
|
|
31555
|
+
}
|
|
31556
|
+
var MobiusRuntimeEngine = class {
|
|
31557
|
+
#config;
|
|
31558
|
+
#state = new IdleMobiusRuntimeState();
|
|
31559
|
+
constructor(config2) {
|
|
31560
|
+
validateRuntimeConfiguration(config2);
|
|
31561
|
+
this.#config = config2;
|
|
31562
|
+
}
|
|
31563
|
+
run = async (signal) => {
|
|
31564
|
+
if (!(this.#state instanceof IdleMobiusRuntimeState)) {
|
|
31565
|
+
throw new MobiusRuntimeConfigurationError("A Mobius Runtime engine can only be started once");
|
|
31566
|
+
}
|
|
31567
|
+
signal.throwIfAborted();
|
|
31568
|
+
this.#state = new RunningMobiusRuntimeState();
|
|
31569
|
+
const audit = new AuditStore(this.#config.auditDatabasePath);
|
|
31570
|
+
try {
|
|
31571
|
+
await runDaemon(this.#config, audit, signal);
|
|
31572
|
+
} finally {
|
|
31573
|
+
audit.close();
|
|
31574
|
+
this.#state = new FinishedMobiusRuntimeState();
|
|
31575
|
+
}
|
|
31576
|
+
};
|
|
31577
|
+
};
|
|
31578
|
+
|
|
31179
31579
|
// src/cli.ts
|
|
31180
31580
|
var shutdown = new AbortController();
|
|
31181
31581
|
process.once("SIGINT", () => shutdown.abort());
|
|
@@ -31183,7 +31583,7 @@ process.once("SIGTERM", () => shutdown.abort());
|
|
|
31183
31583
|
var USAGE = `Mobius CLI
|
|
31184
31584
|
|
|
31185
31585
|
Usage:
|
|
31186
|
-
mobius pair --url <relay-url> --code <pairing-code> [--workspace <directory>]
|
|
31586
|
+
mobius pair --url <relay-url> --pairing <runtime-pairing-id> --code <pairing-code> [--workspace <directory>]
|
|
31187
31587
|
mobius start --channel <channel-id>
|
|
31188
31588
|
mobius agents [--json]
|
|
31189
31589
|
mobius bot message send --target <current|alias> --text <message>
|
|
@@ -31256,12 +31656,7 @@ async function main() {
|
|
|
31256
31656
|
process.stdout.write(`Default workspace: ${config2.defaultWorkspace}
|
|
31257
31657
|
|
|
31258
31658
|
`);
|
|
31259
|
-
|
|
31260
|
-
try {
|
|
31261
|
-
await runDaemon(config2, audit, shutdown.signal);
|
|
31262
|
-
} finally {
|
|
31263
|
-
audit.close();
|
|
31264
|
-
}
|
|
31659
|
+
await new MobiusRuntimeEngine(config2).run(shutdown.signal);
|
|
31265
31660
|
}
|
|
31266
31661
|
try {
|
|
31267
31662
|
await main();
|