motebit 1.4.4 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +2165 -353
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -980,7 +980,7 @@ var init_routing = __esm({
|
|
|
980
980
|
function isTokenAudience(value) {
|
|
981
981
|
return typeof value === "string" && ALL_TOKEN_AUDIENCES.includes(value);
|
|
982
982
|
}
|
|
983
|
-
var SYNC_AUDIENCE, DEVICE_AUTH_AUDIENCE, PAIR_AUDIENCE, ROTATE_KEY_AUDIENCE, PUSH_REGISTER_AUDIENCE, TASK_SUBMIT_AUDIENCE, ADMIN_QUERY_AUDIENCE, PROPOSAL_AUDIENCE, ACCOUNT_BALANCE_AUDIENCE, ACCOUNT_DEPOSIT_AUDIENCE, ACCOUNT_WITHDRAW_AUDIENCE, ACCOUNT_WITHDRAWALS_AUDIENCE, ACCOUNT_CHECKOUT_AUDIENCE, BROWSER_SANDBOX_GRANT_AUDIENCE, BROWSER_SANDBOX_AUDIENCE, ALL_TOKEN_AUDIENCES;
|
|
983
|
+
var SYNC_AUDIENCE, DEVICE_AUTH_AUDIENCE, PAIR_AUDIENCE, ROTATE_KEY_AUDIENCE, PUSH_REGISTER_AUDIENCE, TASK_SUBMIT_AUDIENCE, ADMIN_QUERY_AUDIENCE, PROPOSAL_AUDIENCE, ACCOUNT_BALANCE_AUDIENCE, ACCOUNT_DEPOSIT_AUDIENCE, ACCOUNT_WITHDRAW_AUDIENCE, ACCOUNT_WITHDRAWALS_AUDIENCE, ACCOUNT_CHECKOUT_AUDIENCE, BROWSER_SANDBOX_GRANT_AUDIENCE, BROWSER_SANDBOX_AUDIENCE, RUNTIME_ATTACH_AUDIENCE, ALL_TOKEN_AUDIENCES;
|
|
984
984
|
var init_audience = __esm({
|
|
985
985
|
"../../packages/protocol/dist/audience.js"() {
|
|
986
986
|
"use strict";
|
|
@@ -1000,6 +1000,7 @@ var init_audience = __esm({
|
|
|
1000
1000
|
ACCOUNT_CHECKOUT_AUDIENCE = "account:checkout";
|
|
1001
1001
|
BROWSER_SANDBOX_GRANT_AUDIENCE = "browser-sandbox-grant";
|
|
1002
1002
|
BROWSER_SANDBOX_AUDIENCE = "browser-sandbox";
|
|
1003
|
+
RUNTIME_ATTACH_AUDIENCE = "runtime:attach";
|
|
1003
1004
|
ALL_TOKEN_AUDIENCES = Object.freeze([
|
|
1004
1005
|
"sync",
|
|
1005
1006
|
"device:auth",
|
|
@@ -1016,7 +1017,8 @@ var init_audience = __esm({
|
|
|
1016
1017
|
"account:withdrawals",
|
|
1017
1018
|
"account:checkout",
|
|
1018
1019
|
"browser-sandbox-grant",
|
|
1019
|
-
"browser-sandbox"
|
|
1020
|
+
"browser-sandbox",
|
|
1021
|
+
"runtime:attach"
|
|
1020
1022
|
]);
|
|
1021
1023
|
}
|
|
1022
1024
|
});
|
|
@@ -4064,6 +4066,7 @@ __export(dist_exports, {
|
|
|
4064
4066
|
RESEARCH_TASK_SHAPE: () => RESEARCH_TASK_SHAPE,
|
|
4065
4067
|
RISK_LABELS: () => RISK_LABELS,
|
|
4066
4068
|
ROTATE_KEY_AUDIENCE: () => ROTATE_KEY_AUDIENCE,
|
|
4069
|
+
RUNTIME_ATTACH_AUDIENCE: () => RUNTIME_ATTACH_AUDIENCE,
|
|
4067
4070
|
RUNTIME_RETENTION_REGISTRY: () => RUNTIME_RETENTION_REGISTRY,
|
|
4068
4071
|
RegulatoryRiskSemiring: () => RegulatoryRiskSemiring,
|
|
4069
4072
|
RelationType: () => RelationType,
|
|
@@ -8738,6 +8741,8 @@ __export(dist_exports2, {
|
|
|
8738
8741
|
SKILL_SIGNATURE_SUITE: () => SKILL_SIGNATURE_SUITE,
|
|
8739
8742
|
TOOL_INVOCATION_RECEIPT_SUITE: () => TOOL_INVOCATION_RECEIPT_SUITE,
|
|
8740
8743
|
WITNESS_OMISSION_DISPUTE_WINDOW_MS: () => WITNESS_OMISSION_DISPUTE_WINDOW_MS,
|
|
8744
|
+
agentCommandAudience: () => agentCommandAudience,
|
|
8745
|
+
agentCommandPayload: () => agentCommandPayload,
|
|
8741
8746
|
base58btcDecode: () => base58btcDecode,
|
|
8742
8747
|
base58btcEncode: () => base58btcEncode,
|
|
8743
8748
|
bytesToHex: () => bytesToHex4,
|
|
@@ -8788,6 +8793,7 @@ __export(dist_exports2, {
|
|
|
8788
8793
|
resolveTreeHashVersion: () => resolveTreeHashVersion,
|
|
8789
8794
|
sha256: () => sha2562,
|
|
8790
8795
|
signAdjudicatorVote: () => signAdjudicatorVote,
|
|
8796
|
+
signAgentCommandEnvelope: () => signAgentCommandEnvelope,
|
|
8791
8797
|
signApprovalDecision: () => signApprovalDecision,
|
|
8792
8798
|
signBalanceWaiver: () => signBalanceWaiver,
|
|
8793
8799
|
signBySuite: () => signBySuite,
|
|
@@ -8830,6 +8836,7 @@ __export(dist_exports2, {
|
|
|
8830
8836
|
toBase64Url: () => toBase64Url,
|
|
8831
8837
|
verify: () => verify,
|
|
8832
8838
|
verifyAdjudicatorVote: () => verifyAdjudicatorVote,
|
|
8839
|
+
verifyAgentCommandEnvelope: () => verifyAgentCommandEnvelope,
|
|
8833
8840
|
verifyAgentSettlementAnchor: () => verifyAgentSettlementAnchor,
|
|
8834
8841
|
verifyApprovalDecision: () => verifyApprovalDecision,
|
|
8835
8842
|
verifyBalanceWaiver: () => verifyBalanceWaiver,
|
|
@@ -11747,6 +11754,9 @@ function createSWU(Point2, opts) {
|
|
|
11747
11754
|
let map;
|
|
11748
11755
|
return (scalars) => (map || (map = mapToCurveSimpleSWU(Point2.Fp, opts)))(scalars[0]);
|
|
11749
11756
|
}
|
|
11757
|
+
function assertNeverSuite(suite) {
|
|
11758
|
+
throw new Error(`unsupported cryptosuite: ${String(suite)}`);
|
|
11759
|
+
}
|
|
11750
11760
|
async function verifyBySuite(suite, canonicalBytes, signatureBytes, publicKeyBytes) {
|
|
11751
11761
|
switch (suite) {
|
|
11752
11762
|
case "motebit-jcs-ed25519-b64-v1":
|
|
@@ -11759,6 +11769,8 @@ async function verifyBySuite(suite, canonicalBytes, signatureBytes, publicKeyByt
|
|
|
11759
11769
|
} catch {
|
|
11760
11770
|
return false;
|
|
11761
11771
|
}
|
|
11772
|
+
default:
|
|
11773
|
+
return false;
|
|
11762
11774
|
}
|
|
11763
11775
|
}
|
|
11764
11776
|
async function signBySuite(suite, canonicalBytes, privateKeyBytes) {
|
|
@@ -11769,6 +11781,8 @@ async function signBySuite(suite, canonicalBytes, privateKeyBytes) {
|
|
|
11769
11781
|
case "motebit-concat-ed25519-hex-v1":
|
|
11770
11782
|
case "eddsa-jcs-2022":
|
|
11771
11783
|
return signAsync(canonicalBytes, privateKeyBytes);
|
|
11784
|
+
default:
|
|
11785
|
+
return assertNeverSuite(suite);
|
|
11772
11786
|
}
|
|
11773
11787
|
}
|
|
11774
11788
|
async function ed25519Sign(message2, privateKey) {
|
|
@@ -11793,6 +11807,8 @@ async function getPublicKeyBySuite(privateKey, suite) {
|
|
|
11793
11807
|
case "motebit-concat-ed25519-hex-v1":
|
|
11794
11808
|
case "eddsa-jcs-2022":
|
|
11795
11809
|
return getPublicKeyAsync(privateKey);
|
|
11810
|
+
default:
|
|
11811
|
+
return assertNeverSuite(suite);
|
|
11796
11812
|
}
|
|
11797
11813
|
}
|
|
11798
11814
|
function verifyP256EcdsaSha256(publicKeyCompressedHex, messageBytes, signatureDerBytes) {
|
|
@@ -12463,9 +12479,11 @@ async function signRequestEnvelope(payload, fields, identityPrivateKey) {
|
|
|
12463
12479
|
async function verifyRequestEnvelope(envelope, registeredPublicKey, options) {
|
|
12464
12480
|
if (envelope.suite !== SIGNED_REQUEST_ENVELOPE_SUITE) return false;
|
|
12465
12481
|
if (options?.expectedAud !== void 0 && envelope.aud !== options.expectedAud) return false;
|
|
12466
|
-
|
|
12467
|
-
|
|
12468
|
-
|
|
12482
|
+
const checkFreshness = options?.checkFreshness ?? true;
|
|
12483
|
+
if (checkFreshness) {
|
|
12484
|
+
const now = options?.now ?? Date.now();
|
|
12485
|
+
const windowMs = options?.windowMs ?? 3e5;
|
|
12486
|
+
if (Math.abs(now - envelope.ts) > windowMs) return false;
|
|
12469
12487
|
}
|
|
12470
12488
|
if (options?.payload !== void 0) {
|
|
12471
12489
|
const recomputed = await canonicalSha256(options.payload);
|
|
@@ -13555,11 +13573,11 @@ async function signVerifiableCredential(unsignedVC, privateKey, publicKey) {
|
|
|
13555
13573
|
);
|
|
13556
13574
|
return { ...unsignedVC, proof };
|
|
13557
13575
|
}
|
|
13558
|
-
async function verifyVerifiableCredential(vc) {
|
|
13559
|
-
|
|
13560
|
-
|
|
13561
|
-
|
|
13562
|
-
|
|
13576
|
+
async function verifyVerifiableCredential(vc, options) {
|
|
13577
|
+
const now = options?.now ?? Date.now();
|
|
13578
|
+
if (vc.validUntil && now > new Date(vc.validUntil).getTime()) return false;
|
|
13579
|
+
if (vc.validFrom && now < new Date(vc.validFrom).getTime()) return false;
|
|
13580
|
+
if (vc.credentialStatus && options?.isRevoked?.(vc.credentialStatus.id) === true) return false;
|
|
13563
13581
|
return verifyDataIntegritySigning(vc, vc.proof);
|
|
13564
13582
|
}
|
|
13565
13583
|
async function signVerifiablePresentation(unsignedVP, privateKey, publicKey) {
|
|
@@ -14552,6 +14570,49 @@ async function verifyAlternativePeeringArtifact(artifact, cert, ctx, errors) {
|
|
|
14552
14570
|
if (!ok) errors.push("peering artifact signature does not verify against cert issuer pubkey");
|
|
14553
14571
|
return ok;
|
|
14554
14572
|
}
|
|
14573
|
+
function agentCommandAudience(targetMotebitId) {
|
|
14574
|
+
return `agent-command/${targetMotebitId}`;
|
|
14575
|
+
}
|
|
14576
|
+
function agentCommandPayload(command2, args) {
|
|
14577
|
+
return { command: command2, args: args ?? null };
|
|
14578
|
+
}
|
|
14579
|
+
async function signAgentCommandEnvelope(opts) {
|
|
14580
|
+
return signRequestEnvelope(
|
|
14581
|
+
agentCommandPayload(opts.command, opts.args),
|
|
14582
|
+
{
|
|
14583
|
+
motebit_id: opts.motebitId,
|
|
14584
|
+
ts: (opts.now ?? Date.now)(),
|
|
14585
|
+
aud: agentCommandAudience(opts.motebitId),
|
|
14586
|
+
...opts.nonce !== void 0 ? { nonce: opts.nonce } : {}
|
|
14587
|
+
},
|
|
14588
|
+
opts.identityPrivateKey
|
|
14589
|
+
);
|
|
14590
|
+
}
|
|
14591
|
+
async function verifyAgentCommandEnvelope(opts) {
|
|
14592
|
+
const candidate = opts.envelope;
|
|
14593
|
+
if (candidate == null || typeof candidate !== "object" || typeof candidate.signature !== "string" || typeof candidate.motebit_id !== "string") {
|
|
14594
|
+
return {
|
|
14595
|
+
ok: false,
|
|
14596
|
+
reason: "command_request rejected: missing signed-request-envelope@1.0 (unsigned remote commands are not accepted)"
|
|
14597
|
+
};
|
|
14598
|
+
}
|
|
14599
|
+
if (candidate.motebit_id !== opts.motebitId) {
|
|
14600
|
+
return {
|
|
14601
|
+
ok: false,
|
|
14602
|
+
reason: "command_request rejected: envelope identity is not this agent's identity"
|
|
14603
|
+
};
|
|
14604
|
+
}
|
|
14605
|
+
const key = typeof opts.identityPublicKey === "string" ? hexToBytes5(opts.identityPublicKey) : opts.identityPublicKey;
|
|
14606
|
+
const valid = await verifyRequestEnvelope(candidate, key, {
|
|
14607
|
+
payload: agentCommandPayload(opts.command, opts.args),
|
|
14608
|
+
expectedAud: agentCommandAudience(opts.motebitId),
|
|
14609
|
+
...opts.now !== void 0 ? { now: opts.now } : {}
|
|
14610
|
+
});
|
|
14611
|
+
return valid ? { ok: true } : {
|
|
14612
|
+
ok: false,
|
|
14613
|
+
reason: "command_request rejected: envelope verification failed (signature, audience, freshness, or payload digest)"
|
|
14614
|
+
};
|
|
14615
|
+
}
|
|
14555
14616
|
function parseYamlValue(raw) {
|
|
14556
14617
|
const trimmed = raw.trim();
|
|
14557
14618
|
if (trimmed === "null") return null;
|
|
@@ -15436,15 +15497,24 @@ async function verifySkillEnvelopeArtifact(envelope) {
|
|
|
15436
15497
|
}
|
|
15437
15498
|
async function verifyCredential(vc, clockSkewSeconds = DEFAULT_CLOCK_SKEW_SECONDS, hardwareAttestationVerifiers) {
|
|
15438
15499
|
const errors = [];
|
|
15500
|
+
const skewMs = clockSkewSeconds * 1e3;
|
|
15439
15501
|
let expired = false;
|
|
15440
15502
|
if (vc.validUntil) {
|
|
15441
15503
|
const expiresAt = new Date(vc.validUntil).getTime();
|
|
15442
|
-
const skewMs = clockSkewSeconds * 1e3;
|
|
15443
15504
|
if (Date.now() > expiresAt + skewMs) {
|
|
15444
15505
|
expired = true;
|
|
15445
15506
|
errors.push({ message: "Credential has expired", path: "validUntil" });
|
|
15446
15507
|
}
|
|
15447
15508
|
}
|
|
15509
|
+
let notYetValid = false;
|
|
15510
|
+
if (vc.validFrom) {
|
|
15511
|
+
const activeAt = new Date(vc.validFrom).getTime();
|
|
15512
|
+
if (Date.now() + skewMs < activeAt) {
|
|
15513
|
+
notYetValid = true;
|
|
15514
|
+
errors.push({ message: "Credential is not yet valid", path: "validFrom" });
|
|
15515
|
+
}
|
|
15516
|
+
}
|
|
15517
|
+
const revocationUnchecked = vc.credentialStatus !== void 0;
|
|
15448
15518
|
const proofValid = await verifyDataIntegrity(vc, vc.proof);
|
|
15449
15519
|
if (!proofValid) {
|
|
15450
15520
|
errors.push({ message: "Credential proof verification failed", path: "proof" });
|
|
@@ -15468,11 +15538,13 @@ async function verifyCredential(vc, clockSkewSeconds = DEFAULT_CLOCK_SKEW_SECOND
|
|
|
15468
15538
|
}
|
|
15469
15539
|
return {
|
|
15470
15540
|
type: "credential",
|
|
15471
|
-
valid: proofValid && !expired,
|
|
15541
|
+
valid: proofValid && !expired && !notYetValid,
|
|
15472
15542
|
credential: vc,
|
|
15473
15543
|
issuer: issuerDid,
|
|
15474
15544
|
subject: subjectId,
|
|
15475
15545
|
expired,
|
|
15546
|
+
...notYetValid ? { not_yet_valid: true } : {},
|
|
15547
|
+
...revocationUnchecked ? { revocation_unchecked: true } : {},
|
|
15476
15548
|
...hardwareAttestation && { hardware_attestation: hardwareAttestation },
|
|
15477
15549
|
...errors.length > 0 ? { errors } : {}
|
|
15478
15550
|
};
|
|
@@ -18975,7 +19047,7 @@ var init_config2 = __esm({
|
|
|
18975
19047
|
"src/config.ts"() {
|
|
18976
19048
|
"use strict";
|
|
18977
19049
|
init_esm_shims();
|
|
18978
|
-
VERSION = true ? "1.
|
|
19050
|
+
VERSION = true ? "1.5.0" : "0.0.0-dev";
|
|
18979
19051
|
CONFIG_DIR = process.env["MOTEBIT_CONFIG_DIR"] ?? path2.join(os.homedir(), ".motebit");
|
|
18980
19052
|
CONFIG_PATH = path2.join(CONFIG_DIR, "config.json");
|
|
18981
19053
|
RELAY_DIR = path2.join(CONFIG_DIR, "relay");
|
|
@@ -27002,7 +27074,7 @@ function createRelayReceiptExchange(config) {
|
|
|
27002
27074
|
if (closed)
|
|
27003
27075
|
break;
|
|
27004
27076
|
if (!response.ok) {
|
|
27005
|
-
await
|
|
27077
|
+
await sleep3(pollRetryDelayMs, pollAbortController.signal);
|
|
27006
27078
|
continue;
|
|
27007
27079
|
}
|
|
27008
27080
|
const text = await response.text();
|
|
@@ -27051,13 +27123,13 @@ function createRelayReceiptExchange(config) {
|
|
|
27051
27123
|
break;
|
|
27052
27124
|
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
27053
27125
|
if (!msg.includes("abort")) {
|
|
27054
|
-
await
|
|
27126
|
+
await sleep3(pollRetryDelayMs, pollAbortController.signal);
|
|
27055
27127
|
}
|
|
27056
27128
|
}
|
|
27057
27129
|
}
|
|
27058
27130
|
pollAbortController = null;
|
|
27059
27131
|
}
|
|
27060
|
-
async function
|
|
27132
|
+
async function sleep3(ms, signal) {
|
|
27061
27133
|
return new Promise((resolve14) => {
|
|
27062
27134
|
const timer = setTimeout(resolve14, ms);
|
|
27063
27135
|
signal?.addEventListener("abort", () => {
|
|
@@ -46380,6 +46452,7 @@ __export(dist_exports6, {
|
|
|
46380
46452
|
SANDBOX_TOKEN_REFRESH_MARGIN_MS: () => SANDBOX_TOKEN_REFRESH_MARGIN_MS,
|
|
46381
46453
|
SimpleToolRegistry: () => SimpleToolRegistry,
|
|
46382
46454
|
SovereignTierRequiredError: () => SovereignTierRequiredError,
|
|
46455
|
+
agentCommandAudience: () => agentCommandAudience,
|
|
46383
46456
|
bindSlabControllerToRenderer: () => bindSlabControllerToRenderer,
|
|
46384
46457
|
buildPrecisionContext: () => buildPrecisionContext,
|
|
46385
46458
|
buildUserInputAuditDetail: () => buildUserInputAuditDetail,
|
|
@@ -46419,6 +46492,7 @@ __export(dist_exports6, {
|
|
|
46419
46492
|
submitP2pDelegation: () => submitP2pDelegation,
|
|
46420
46493
|
summarizeGradientHistory: () => summarizeGradientHistory,
|
|
46421
46494
|
urlAuditDetail: () => urlAuditDetail,
|
|
46495
|
+
verifyAgentCommandEnvelope: () => verifyAgentCommandEnvelope,
|
|
46422
46496
|
verifyGrantForTurn: () => verifyGrantForTurn
|
|
46423
46497
|
});
|
|
46424
46498
|
var init_dist22 = __esm({
|
|
@@ -46460,6 +46534,7 @@ var init_dist22 = __esm({
|
|
|
46460
46534
|
init_relay_delegation();
|
|
46461
46535
|
init_invoke_capability();
|
|
46462
46536
|
init_grant_verifier();
|
|
46537
|
+
init_dist5();
|
|
46463
46538
|
}
|
|
46464
46539
|
});
|
|
46465
46540
|
|
|
@@ -67341,7 +67416,7 @@ var init_execution_receipt = __esm({
|
|
|
67341
67416
|
DeviceIdSchema = z2.string().min(1).describe("Device identity \u2014 UUIDv7 string for the signing device. Brand-checked at the type layer; the wire shape is a non-empty string.");
|
|
67342
67417
|
IntentOriginSchema = z2.enum(["user-tap", "ai-loop", "scheduled", "agent-to-agent"]).describe("How this task was authorized for invocation. `user-tap` = explicit affordance (surface determinism); `ai-loop` = model-mediated delegation; `scheduled` = cron; `agent-to-agent` = another motebit initiated it. Signature-bound \u2014 tampering breaks verification.");
|
|
67343
67418
|
SuiteSchema = z2.literal("motebit-jcs-ed25519-b64-v1").describe("Cryptosuite identifier. Always `motebit-jcs-ed25519-b64-v1` for ExecutionReceipt today: JCS canonicalization (RFC 8785), Ed25519 signature, base64url-encoded signature, hex-encoded public key. See @motebit/protocol SUITE_REGISTRY.");
|
|
67344
|
-
StatusSchema = z2.enum(["completed", "failed", "denied"]).describe("Terminal state of the task. `completed` = result produced and signed. `failed` = execution
|
|
67419
|
+
StatusSchema = z2.enum(["completed", "failed", "denied"]).describe("Terminal state of the task. `completed` = result produced and signed. `failed` = the execution interior did not yield its outcome (crashes, timeouts, and the worker's own principled refusals all included). `denied` = the governance boundary refused: a policy or budget gate blocked the task's actions and no permitted work completed. The discriminator is who refused \u2014 the boundary (`denied`) or the interior (`failed`). See execution-ledger-v1 \xA711.1 Status semantics.");
|
|
67345
67420
|
ExecutionReceiptSchema = z2.lazy(() => z2.object({
|
|
67346
67421
|
task_id: z2.string().min(1).describe("Task identifier \u2014 UUID assigned at submission time; stable across retries."),
|
|
67347
67422
|
motebit_id: MotebitIdSchema,
|
|
@@ -70572,10 +70647,177 @@ var init_goal_tools = __esm({
|
|
|
70572
70647
|
});
|
|
70573
70648
|
|
|
70574
70649
|
// ../../packages/tools/dist/builtins/computer.js
|
|
70650
|
+
var POINT_SCHEMA, TARGET_HINT_SCHEMA, MODIFIERS_SCHEMA, computerDefinition;
|
|
70575
70651
|
var init_computer = __esm({
|
|
70576
70652
|
"../../packages/tools/dist/builtins/computer.js"() {
|
|
70577
70653
|
"use strict";
|
|
70578
70654
|
init_esm_shims();
|
|
70655
|
+
POINT_SCHEMA = {
|
|
70656
|
+
type: "object",
|
|
70657
|
+
properties: {
|
|
70658
|
+
x: { type: "integer", description: "Logical pixel X." },
|
|
70659
|
+
y: { type: "integer", description: "Logical pixel Y." }
|
|
70660
|
+
},
|
|
70661
|
+
required: ["x", "y"],
|
|
70662
|
+
additionalProperties: false
|
|
70663
|
+
};
|
|
70664
|
+
TARGET_HINT_SCHEMA = {
|
|
70665
|
+
type: "object",
|
|
70666
|
+
properties: {
|
|
70667
|
+
role: { type: "string", description: "Accessibility role. Examples: 'button', 'link'." },
|
|
70668
|
+
label: { type: "string", description: "Accessible label or visible text." },
|
|
70669
|
+
source: {
|
|
70670
|
+
type: "string",
|
|
70671
|
+
description: "Source: 'accessibility', 'dom', 'vision', 'user_annotation'."
|
|
70672
|
+
}
|
|
70673
|
+
},
|
|
70674
|
+
required: ["source"],
|
|
70675
|
+
additionalProperties: true
|
|
70676
|
+
};
|
|
70677
|
+
MODIFIERS_SCHEMA = {
|
|
70678
|
+
type: "array",
|
|
70679
|
+
items: { type: "string", enum: ["cmd", "ctrl", "alt", "shift"] },
|
|
70680
|
+
description: "Modifier keys held during the action."
|
|
70681
|
+
};
|
|
70682
|
+
computerDefinition = {
|
|
70683
|
+
name: "computer",
|
|
70684
|
+
mode: "pixels",
|
|
70685
|
+
description: "Observe or act on the user's computer \u2014 screenshot, click, type, scroll, navigate. Available on desktop (drives the user's OS via xcap+enigo) and on web (drives a cloud-browser sandbox via Playwright). On the cloud-browser surface, use `navigate` to reach a URL \u2014 there's no address bar for `key`/`type` to drive. Every observation and action emits a signed receipt and flows through the governance gate. See spec/computer-use-v1.md.",
|
|
70686
|
+
inputSchema: {
|
|
70687
|
+
type: "object",
|
|
70688
|
+
properties: {
|
|
70689
|
+
session_id: {
|
|
70690
|
+
type: "string",
|
|
70691
|
+
description: "Open computer-use session identifier. Optional \u2014 if omitted, the runtime's default session for this motebit is used."
|
|
70692
|
+
},
|
|
70693
|
+
action: {
|
|
70694
|
+
description: "Action to perform. Must be a discriminated variant with a `kind` field.",
|
|
70695
|
+
oneOf: [
|
|
70696
|
+
{
|
|
70697
|
+
type: "object",
|
|
70698
|
+
properties: {
|
|
70699
|
+
kind: { type: "string", enum: ["screenshot"] }
|
|
70700
|
+
},
|
|
70701
|
+
required: ["kind"],
|
|
70702
|
+
additionalProperties: false,
|
|
70703
|
+
description: "Capture the primary display's current frame."
|
|
70704
|
+
},
|
|
70705
|
+
{
|
|
70706
|
+
type: "object",
|
|
70707
|
+
properties: {
|
|
70708
|
+
kind: { type: "string", enum: ["cursor_position"] }
|
|
70709
|
+
},
|
|
70710
|
+
required: ["kind"],
|
|
70711
|
+
additionalProperties: false,
|
|
70712
|
+
description: "Read the current cursor coordinates."
|
|
70713
|
+
},
|
|
70714
|
+
{
|
|
70715
|
+
type: "object",
|
|
70716
|
+
properties: {
|
|
70717
|
+
kind: { type: "string", enum: ["click"] },
|
|
70718
|
+
target: POINT_SCHEMA,
|
|
70719
|
+
button: { type: "string", enum: ["left", "right", "middle"] },
|
|
70720
|
+
modifiers: MODIFIERS_SCHEMA,
|
|
70721
|
+
target_hint: TARGET_HINT_SCHEMA
|
|
70722
|
+
},
|
|
70723
|
+
required: ["kind", "target"],
|
|
70724
|
+
additionalProperties: false,
|
|
70725
|
+
description: "Single mouse click at `target`."
|
|
70726
|
+
},
|
|
70727
|
+
{
|
|
70728
|
+
type: "object",
|
|
70729
|
+
properties: {
|
|
70730
|
+
kind: { type: "string", enum: ["double_click"] },
|
|
70731
|
+
target: POINT_SCHEMA,
|
|
70732
|
+
button: { type: "string", enum: ["left", "right", "middle"] },
|
|
70733
|
+
modifiers: MODIFIERS_SCHEMA,
|
|
70734
|
+
target_hint: TARGET_HINT_SCHEMA
|
|
70735
|
+
},
|
|
70736
|
+
required: ["kind", "target"],
|
|
70737
|
+
additionalProperties: false,
|
|
70738
|
+
description: "Double click at `target`."
|
|
70739
|
+
},
|
|
70740
|
+
{
|
|
70741
|
+
type: "object",
|
|
70742
|
+
properties: {
|
|
70743
|
+
kind: { type: "string", enum: ["mouse_move"] },
|
|
70744
|
+
target: POINT_SCHEMA,
|
|
70745
|
+
target_hint: TARGET_HINT_SCHEMA
|
|
70746
|
+
},
|
|
70747
|
+
required: ["kind", "target"],
|
|
70748
|
+
additionalProperties: false,
|
|
70749
|
+
description: "Move cursor to `target` without clicking."
|
|
70750
|
+
},
|
|
70751
|
+
{
|
|
70752
|
+
type: "object",
|
|
70753
|
+
properties: {
|
|
70754
|
+
kind: { type: "string", enum: ["drag"] },
|
|
70755
|
+
from: POINT_SCHEMA,
|
|
70756
|
+
to: POINT_SCHEMA,
|
|
70757
|
+
button: { type: "string", enum: ["left", "right", "middle"] },
|
|
70758
|
+
modifiers: MODIFIERS_SCHEMA,
|
|
70759
|
+
duration_ms: { type: "integer", minimum: 0 },
|
|
70760
|
+
target_hint: TARGET_HINT_SCHEMA
|
|
70761
|
+
},
|
|
70762
|
+
required: ["kind", "from", "to"],
|
|
70763
|
+
additionalProperties: false,
|
|
70764
|
+
description: "Press at `from`, move to `to`, release."
|
|
70765
|
+
},
|
|
70766
|
+
{
|
|
70767
|
+
type: "object",
|
|
70768
|
+
properties: {
|
|
70769
|
+
kind: { type: "string", enum: ["type"] },
|
|
70770
|
+
text: { type: "string", description: "Text to type." },
|
|
70771
|
+
per_char_delay_ms: { type: "integer", minimum: 0 }
|
|
70772
|
+
},
|
|
70773
|
+
required: ["kind", "text"],
|
|
70774
|
+
additionalProperties: false,
|
|
70775
|
+
description: "Keyboard text input."
|
|
70776
|
+
},
|
|
70777
|
+
{
|
|
70778
|
+
type: "object",
|
|
70779
|
+
properties: {
|
|
70780
|
+
kind: { type: "string", enum: ["key"] },
|
|
70781
|
+
key: {
|
|
70782
|
+
type: "string",
|
|
70783
|
+
description: "Key combination. Example: 'cmd+c', 'escape'."
|
|
70784
|
+
}
|
|
70785
|
+
},
|
|
70786
|
+
required: ["kind", "key"],
|
|
70787
|
+
additionalProperties: false,
|
|
70788
|
+
description: "Keyboard combination."
|
|
70789
|
+
},
|
|
70790
|
+
{
|
|
70791
|
+
type: "object",
|
|
70792
|
+
properties: {
|
|
70793
|
+
kind: { type: "string", enum: ["scroll"] },
|
|
70794
|
+
target: POINT_SCHEMA,
|
|
70795
|
+
dx: { type: "integer", description: "Scroll wheel delta X." },
|
|
70796
|
+
dy: { type: "integer", description: "Scroll wheel delta Y." }
|
|
70797
|
+
},
|
|
70798
|
+
required: ["kind", "target", "dx", "dy"],
|
|
70799
|
+
additionalProperties: false,
|
|
70800
|
+
description: "Scroll at `target` by `(dx, dy)` wheel deltas."
|
|
70801
|
+
},
|
|
70802
|
+
{
|
|
70803
|
+
type: "object",
|
|
70804
|
+
properties: {
|
|
70805
|
+
kind: { type: "string", enum: ["navigate"] },
|
|
70806
|
+
url: {
|
|
70807
|
+
type: "string",
|
|
70808
|
+
description: "Target URL. Hostnames without a scheme (e.g. 'example.com') are normalized to 'https://example.com'."
|
|
70809
|
+
}
|
|
70810
|
+
},
|
|
70811
|
+
required: ["kind", "url"],
|
|
70812
|
+
additionalProperties: false,
|
|
70813
|
+
description: "Navigate the active browser context to a URL. Cloud-browser-only \u2014 use this when you need to reach a specific page on the cloud-browser surface (no address bar exists in the headless viewport for `key`/`type` to drive). Not available on the desktop surface (the user controls which app is focused there)."
|
|
70814
|
+
}
|
|
70815
|
+
]
|
|
70816
|
+
}
|
|
70817
|
+
},
|
|
70818
|
+
required: ["action"]
|
|
70819
|
+
}
|
|
70820
|
+
};
|
|
70579
70821
|
}
|
|
70580
70822
|
});
|
|
70581
70823
|
|
|
@@ -71103,8 +71345,8 @@ function buildCliSkillSelectorHook(getHardwareAttestationScore) {
|
|
|
71103
71345
|
}
|
|
71104
71346
|
};
|
|
71105
71347
|
}
|
|
71106
|
-
function mapNodePlatformToSkillPlatform(
|
|
71107
|
-
switch (
|
|
71348
|
+
function mapNodePlatformToSkillPlatform(nodePlatform2) {
|
|
71349
|
+
switch (nodePlatform2) {
|
|
71108
71350
|
case "darwin":
|
|
71109
71351
|
return "macos";
|
|
71110
71352
|
case "linux":
|
|
@@ -71895,6 +72137,134 @@ var init_idempotency = __esm({
|
|
|
71895
72137
|
}
|
|
71896
72138
|
});
|
|
71897
72139
|
|
|
72140
|
+
// ../../services/relay/dist/loop-supervisor.js
|
|
72141
|
+
function superviseInterval(supervisor, name, intervalMs, tick, opts = {}) {
|
|
72142
|
+
supervisor?.register(name, intervalMs);
|
|
72143
|
+
return setInterval(() => {
|
|
72144
|
+
if (opts.isFrozen?.()) {
|
|
72145
|
+
supervisor?.markSkip(name);
|
|
72146
|
+
return;
|
|
72147
|
+
}
|
|
72148
|
+
if (supervisor === void 0) {
|
|
72149
|
+
void Promise.resolve().then(tick).catch(() => {
|
|
72150
|
+
});
|
|
72151
|
+
return;
|
|
72152
|
+
}
|
|
72153
|
+
supervisor.markStart(name);
|
|
72154
|
+
Promise.resolve().then(tick).then(() => supervisor.markOk(name)).catch((err2) => supervisor.markError(name, err2));
|
|
72155
|
+
}, intervalMs);
|
|
72156
|
+
}
|
|
72157
|
+
var LoopSupervisor;
|
|
72158
|
+
var init_loop_supervisor = __esm({
|
|
72159
|
+
"../../services/relay/dist/loop-supervisor.js"() {
|
|
72160
|
+
"use strict";
|
|
72161
|
+
init_esm_shims();
|
|
72162
|
+
LoopSupervisor = class {
|
|
72163
|
+
loops = /* @__PURE__ */ new Map();
|
|
72164
|
+
now;
|
|
72165
|
+
staleFactor;
|
|
72166
|
+
constructor(opts = {}) {
|
|
72167
|
+
this.now = opts.now ?? (() => Date.now());
|
|
72168
|
+
this.staleFactor = opts.staleFactor ?? 3;
|
|
72169
|
+
}
|
|
72170
|
+
/** Register a loop. Idempotent — re-registering preserves existing counters. */
|
|
72171
|
+
register(name, intervalMs) {
|
|
72172
|
+
if (this.loops.has(name))
|
|
72173
|
+
return;
|
|
72174
|
+
this.loops.set(name, {
|
|
72175
|
+
name,
|
|
72176
|
+
intervalMs,
|
|
72177
|
+
registeredAt: this.now(),
|
|
72178
|
+
lastStartAt: null,
|
|
72179
|
+
lastOkAt: null,
|
|
72180
|
+
lastErrorAt: null,
|
|
72181
|
+
lastError: null,
|
|
72182
|
+
lastSkipAt: null,
|
|
72183
|
+
tickCount: 0,
|
|
72184
|
+
okCount: 0,
|
|
72185
|
+
errorCount: 0,
|
|
72186
|
+
skipCount: 0,
|
|
72187
|
+
running: false
|
|
72188
|
+
});
|
|
72189
|
+
}
|
|
72190
|
+
markStart(name) {
|
|
72191
|
+
const r2 = this.loops.get(name);
|
|
72192
|
+
if (!r2)
|
|
72193
|
+
return;
|
|
72194
|
+
r2.lastStartAt = this.now();
|
|
72195
|
+
r2.tickCount += 1;
|
|
72196
|
+
r2.running = true;
|
|
72197
|
+
}
|
|
72198
|
+
markOk(name) {
|
|
72199
|
+
const r2 = this.loops.get(name);
|
|
72200
|
+
if (!r2)
|
|
72201
|
+
return;
|
|
72202
|
+
r2.lastOkAt = this.now();
|
|
72203
|
+
r2.okCount += 1;
|
|
72204
|
+
r2.running = false;
|
|
72205
|
+
}
|
|
72206
|
+
markError(name, err2) {
|
|
72207
|
+
const r2 = this.loops.get(name);
|
|
72208
|
+
if (!r2)
|
|
72209
|
+
return;
|
|
72210
|
+
r2.lastErrorAt = this.now();
|
|
72211
|
+
r2.lastError = err2 instanceof Error ? err2.message : String(err2);
|
|
72212
|
+
r2.errorCount += 1;
|
|
72213
|
+
r2.running = false;
|
|
72214
|
+
}
|
|
72215
|
+
markSkip(name) {
|
|
72216
|
+
const r2 = this.loops.get(name);
|
|
72217
|
+
if (!r2)
|
|
72218
|
+
return;
|
|
72219
|
+
r2.lastSkipAt = this.now();
|
|
72220
|
+
r2.skipCount += 1;
|
|
72221
|
+
}
|
|
72222
|
+
/** Compute the derived status for a record at time `now`. */
|
|
72223
|
+
statusOf(r2, now) {
|
|
72224
|
+
const freshness = r2.intervalMs * this.staleFactor;
|
|
72225
|
+
if (r2.running && r2.lastStartAt != null && now - r2.lastStartAt > freshness)
|
|
72226
|
+
return "hung";
|
|
72227
|
+
const lastHealthy = Math.max(r2.lastOkAt ?? 0, r2.lastSkipAt ?? 0);
|
|
72228
|
+
if (lastHealthy > 0) {
|
|
72229
|
+
if (now - lastHealthy <= freshness)
|
|
72230
|
+
return "ok";
|
|
72231
|
+
if (r2.lastErrorAt != null && r2.lastErrorAt >= lastHealthy)
|
|
72232
|
+
return "erroring";
|
|
72233
|
+
return "stale";
|
|
72234
|
+
}
|
|
72235
|
+
if (r2.lastErrorAt != null)
|
|
72236
|
+
return "erroring";
|
|
72237
|
+
if (now - r2.registeredAt <= freshness)
|
|
72238
|
+
return "idle";
|
|
72239
|
+
return "stale";
|
|
72240
|
+
}
|
|
72241
|
+
snapshot() {
|
|
72242
|
+
const now = this.now();
|
|
72243
|
+
return [...this.loops.values()].map((r2) => ({
|
|
72244
|
+
name: r2.name,
|
|
72245
|
+
interval_ms: r2.intervalMs,
|
|
72246
|
+
registered_at: r2.registeredAt,
|
|
72247
|
+
last_start_at: r2.lastStartAt,
|
|
72248
|
+
last_ok_at: r2.lastOkAt,
|
|
72249
|
+
last_error_at: r2.lastErrorAt,
|
|
72250
|
+
last_error: r2.lastError,
|
|
72251
|
+
last_skip_at: r2.lastSkipAt,
|
|
72252
|
+
tick_count: r2.tickCount,
|
|
72253
|
+
ok_count: r2.okCount,
|
|
72254
|
+
error_count: r2.errorCount,
|
|
72255
|
+
skip_count: r2.skipCount,
|
|
72256
|
+
running: r2.running,
|
|
72257
|
+
status: this.statusOf(r2, now)
|
|
72258
|
+
})).sort((a3, b3) => a3.name.localeCompare(b3.name));
|
|
72259
|
+
}
|
|
72260
|
+
/** True if any registered loop is not `ok`/`idle` — the alerting signal. */
|
|
72261
|
+
anyUnhealthy() {
|
|
72262
|
+
return this.snapshot().some((l6) => l6.status === "stale" || l6.status === "erroring" || l6.status === "hung");
|
|
72263
|
+
}
|
|
72264
|
+
};
|
|
72265
|
+
}
|
|
72266
|
+
});
|
|
72267
|
+
|
|
71898
72268
|
// ../../services/relay/dist/horizon.js
|
|
71899
72269
|
function truncateExecutionLedgersBeforeHorizon(db, horizonTs) {
|
|
71900
72270
|
const result = db.prepare("DELETE FROM relay_execution_ledgers WHERE created_at < ?").run(horizonTs);
|
|
@@ -72134,16 +72504,17 @@ async function advanceRevocationHorizon(db, ctx) {
|
|
|
72134
72504
|
const horizonTs = Date.now() - REVOCATION_TTL_MS;
|
|
72135
72505
|
return advanceRelayHorizon(db, "relay_revocation_events", horizonTs, ctx);
|
|
72136
72506
|
}
|
|
72137
|
-
function startRevocationHorizonLoop(db, ctx, intervalMs = DEFAULT_REVOCATION_HORIZON_INTERVAL_MS, isFrozen) {
|
|
72138
|
-
return
|
|
72139
|
-
|
|
72140
|
-
|
|
72141
|
-
|
|
72507
|
+
function startRevocationHorizonLoop(db, ctx, intervalMs = DEFAULT_REVOCATION_HORIZON_INTERVAL_MS, isFrozen, supervisor) {
|
|
72508
|
+
return superviseInterval(supervisor, "revocation-horizon", intervalMs, async () => {
|
|
72509
|
+
try {
|
|
72510
|
+
await advanceRevocationHorizon(db, ctx);
|
|
72511
|
+
} catch (err2) {
|
|
72142
72512
|
logger9.error("horizon.revocation.loop_tick_failed", {
|
|
72143
72513
|
error: err2 instanceof Error ? err2.message : String(err2)
|
|
72144
72514
|
});
|
|
72145
|
-
|
|
72146
|
-
|
|
72515
|
+
throw err2;
|
|
72516
|
+
}
|
|
72517
|
+
}, { ...isFrozen ? { isFrozen } : {} });
|
|
72147
72518
|
}
|
|
72148
72519
|
var logger9, HORIZON_CERT_SUITE, DEFAULT_WITNESS_SOLICITATION_TIMEOUT_MS, MAX_HORIZON_ADVANCE_ATTEMPTS, HORIZON_RETRY_BACKOFF_MS, PEER_FRESHNESS_FLOOR_MS, STORE_TRUNCATE_REGISTRY, advanceLocks, DEFAULT_REVOCATION_HORIZON_INTERVAL_MS;
|
|
72149
72520
|
var init_horizon = __esm({
|
|
@@ -72156,6 +72527,7 @@ var init_horizon = __esm({
|
|
|
72156
72527
|
init_dist30();
|
|
72157
72528
|
init_federation();
|
|
72158
72529
|
init_logger();
|
|
72530
|
+
init_loop_supervisor();
|
|
72159
72531
|
logger9 = createLogger({ service: "relay", module: "horizon" });
|
|
72160
72532
|
HORIZON_CERT_SUITE = "motebit-jcs-ed25519-b64-v1";
|
|
72161
72533
|
DEFAULT_WITNESS_SOLICITATION_TIMEOUT_MS = 1e4;
|
|
@@ -72174,134 +72546,6 @@ var init_horizon = __esm({
|
|
|
72174
72546
|
}
|
|
72175
72547
|
});
|
|
72176
72548
|
|
|
72177
|
-
// ../../services/relay/dist/loop-supervisor.js
|
|
72178
|
-
function superviseInterval(supervisor, name, intervalMs, tick, opts = {}) {
|
|
72179
|
-
supervisor?.register(name, intervalMs);
|
|
72180
|
-
return setInterval(() => {
|
|
72181
|
-
if (opts.isFrozen?.()) {
|
|
72182
|
-
supervisor?.markSkip(name);
|
|
72183
|
-
return;
|
|
72184
|
-
}
|
|
72185
|
-
if (supervisor === void 0) {
|
|
72186
|
-
void Promise.resolve().then(tick).catch(() => {
|
|
72187
|
-
});
|
|
72188
|
-
return;
|
|
72189
|
-
}
|
|
72190
|
-
supervisor.markStart(name);
|
|
72191
|
-
Promise.resolve().then(tick).then(() => supervisor.markOk(name)).catch((err2) => supervisor.markError(name, err2));
|
|
72192
|
-
}, intervalMs);
|
|
72193
|
-
}
|
|
72194
|
-
var LoopSupervisor;
|
|
72195
|
-
var init_loop_supervisor = __esm({
|
|
72196
|
-
"../../services/relay/dist/loop-supervisor.js"() {
|
|
72197
|
-
"use strict";
|
|
72198
|
-
init_esm_shims();
|
|
72199
|
-
LoopSupervisor = class {
|
|
72200
|
-
loops = /* @__PURE__ */ new Map();
|
|
72201
|
-
now;
|
|
72202
|
-
staleFactor;
|
|
72203
|
-
constructor(opts = {}) {
|
|
72204
|
-
this.now = opts.now ?? (() => Date.now());
|
|
72205
|
-
this.staleFactor = opts.staleFactor ?? 3;
|
|
72206
|
-
}
|
|
72207
|
-
/** Register a loop. Idempotent — re-registering preserves existing counters. */
|
|
72208
|
-
register(name, intervalMs) {
|
|
72209
|
-
if (this.loops.has(name))
|
|
72210
|
-
return;
|
|
72211
|
-
this.loops.set(name, {
|
|
72212
|
-
name,
|
|
72213
|
-
intervalMs,
|
|
72214
|
-
registeredAt: this.now(),
|
|
72215
|
-
lastStartAt: null,
|
|
72216
|
-
lastOkAt: null,
|
|
72217
|
-
lastErrorAt: null,
|
|
72218
|
-
lastError: null,
|
|
72219
|
-
lastSkipAt: null,
|
|
72220
|
-
tickCount: 0,
|
|
72221
|
-
okCount: 0,
|
|
72222
|
-
errorCount: 0,
|
|
72223
|
-
skipCount: 0,
|
|
72224
|
-
running: false
|
|
72225
|
-
});
|
|
72226
|
-
}
|
|
72227
|
-
markStart(name) {
|
|
72228
|
-
const r2 = this.loops.get(name);
|
|
72229
|
-
if (!r2)
|
|
72230
|
-
return;
|
|
72231
|
-
r2.lastStartAt = this.now();
|
|
72232
|
-
r2.tickCount += 1;
|
|
72233
|
-
r2.running = true;
|
|
72234
|
-
}
|
|
72235
|
-
markOk(name) {
|
|
72236
|
-
const r2 = this.loops.get(name);
|
|
72237
|
-
if (!r2)
|
|
72238
|
-
return;
|
|
72239
|
-
r2.lastOkAt = this.now();
|
|
72240
|
-
r2.okCount += 1;
|
|
72241
|
-
r2.running = false;
|
|
72242
|
-
}
|
|
72243
|
-
markError(name, err2) {
|
|
72244
|
-
const r2 = this.loops.get(name);
|
|
72245
|
-
if (!r2)
|
|
72246
|
-
return;
|
|
72247
|
-
r2.lastErrorAt = this.now();
|
|
72248
|
-
r2.lastError = err2 instanceof Error ? err2.message : String(err2);
|
|
72249
|
-
r2.errorCount += 1;
|
|
72250
|
-
r2.running = false;
|
|
72251
|
-
}
|
|
72252
|
-
markSkip(name) {
|
|
72253
|
-
const r2 = this.loops.get(name);
|
|
72254
|
-
if (!r2)
|
|
72255
|
-
return;
|
|
72256
|
-
r2.lastSkipAt = this.now();
|
|
72257
|
-
r2.skipCount += 1;
|
|
72258
|
-
}
|
|
72259
|
-
/** Compute the derived status for a record at time `now`. */
|
|
72260
|
-
statusOf(r2, now) {
|
|
72261
|
-
const freshness = r2.intervalMs * this.staleFactor;
|
|
72262
|
-
if (r2.running && r2.lastStartAt != null && now - r2.lastStartAt > freshness)
|
|
72263
|
-
return "hung";
|
|
72264
|
-
const lastHealthy = Math.max(r2.lastOkAt ?? 0, r2.lastSkipAt ?? 0);
|
|
72265
|
-
if (lastHealthy > 0) {
|
|
72266
|
-
if (now - lastHealthy <= freshness)
|
|
72267
|
-
return "ok";
|
|
72268
|
-
if (r2.lastErrorAt != null && r2.lastErrorAt >= lastHealthy)
|
|
72269
|
-
return "erroring";
|
|
72270
|
-
return "stale";
|
|
72271
|
-
}
|
|
72272
|
-
if (r2.lastErrorAt != null)
|
|
72273
|
-
return "erroring";
|
|
72274
|
-
if (now - r2.registeredAt <= freshness)
|
|
72275
|
-
return "idle";
|
|
72276
|
-
return "stale";
|
|
72277
|
-
}
|
|
72278
|
-
snapshot() {
|
|
72279
|
-
const now = this.now();
|
|
72280
|
-
return [...this.loops.values()].map((r2) => ({
|
|
72281
|
-
name: r2.name,
|
|
72282
|
-
interval_ms: r2.intervalMs,
|
|
72283
|
-
registered_at: r2.registeredAt,
|
|
72284
|
-
last_start_at: r2.lastStartAt,
|
|
72285
|
-
last_ok_at: r2.lastOkAt,
|
|
72286
|
-
last_error_at: r2.lastErrorAt,
|
|
72287
|
-
last_error: r2.lastError,
|
|
72288
|
-
last_skip_at: r2.lastSkipAt,
|
|
72289
|
-
tick_count: r2.tickCount,
|
|
72290
|
-
ok_count: r2.okCount,
|
|
72291
|
-
error_count: r2.errorCount,
|
|
72292
|
-
skip_count: r2.skipCount,
|
|
72293
|
-
running: r2.running,
|
|
72294
|
-
status: this.statusOf(r2, now)
|
|
72295
|
-
})).sort((a3, b3) => a3.name.localeCompare(b3.name));
|
|
72296
|
-
}
|
|
72297
|
-
/** True if any registered loop is not `ok`/`idle` — the alerting signal. */
|
|
72298
|
-
anyUnhealthy() {
|
|
72299
|
-
return this.snapshot().some((l6) => l6.status === "stale" || l6.status === "erroring" || l6.status === "hung");
|
|
72300
|
-
}
|
|
72301
|
-
};
|
|
72302
|
-
}
|
|
72303
|
-
});
|
|
72304
|
-
|
|
72305
72549
|
// ../../services/relay/dist/anchoring.js
|
|
72306
72550
|
function createAnchoringTables(db) {
|
|
72307
72551
|
db.exec(`
|
|
@@ -72420,43 +72664,40 @@ async function submitAnchorOnChain(db, batchId, submitter) {
|
|
|
72420
72664
|
return false;
|
|
72421
72665
|
}
|
|
72422
72666
|
}
|
|
72423
|
-
function startBatchAnchorLoop(db, relayIdentity, config = {}, isFrozen) {
|
|
72667
|
+
function startBatchAnchorLoop(db, relayIdentity, config = {}, isFrozen, supervisor) {
|
|
72424
72668
|
const maxSize = config.batchMaxSize ?? DEFAULT_BATCH_MAX_SIZE;
|
|
72425
72669
|
const intervalMs = config.batchIntervalMs ?? DEFAULT_BATCH_INTERVAL_MS;
|
|
72426
72670
|
const checkIntervalMs = Math.min(6e4, intervalMs);
|
|
72427
72671
|
const submitter = config.submitter;
|
|
72428
|
-
return
|
|
72429
|
-
|
|
72430
|
-
|
|
72431
|
-
|
|
72432
|
-
|
|
72433
|
-
|
|
72434
|
-
|
|
72435
|
-
|
|
72436
|
-
|
|
72437
|
-
|
|
72672
|
+
return superviseInterval(supervisor, "federation-anchor", checkIntervalMs, async () => {
|
|
72673
|
+
try {
|
|
72674
|
+
const countRow = db.prepare("SELECT COUNT(*) as cnt FROM relay_federation_settlements WHERE anchor_batch_id IS NULL AND record_json IS NOT NULL").get();
|
|
72675
|
+
if (countRow.cnt === 0)
|
|
72676
|
+
return;
|
|
72677
|
+
let batch = null;
|
|
72678
|
+
if (countRow.cnt >= maxSize) {
|
|
72679
|
+
batch = await cutBatch(db, relayIdentity, maxSize);
|
|
72680
|
+
} else {
|
|
72681
|
+
const oldest = db.prepare("SELECT MIN(settled_at) as oldest FROM relay_federation_settlements WHERE anchor_batch_id IS NULL AND record_json IS NOT NULL").get();
|
|
72682
|
+
if (oldest.oldest != null && Date.now() - oldest.oldest >= intervalMs) {
|
|
72438
72683
|
batch = await cutBatch(db, relayIdentity, maxSize);
|
|
72439
|
-
} else {
|
|
72440
|
-
const oldest = db.prepare("SELECT MIN(settled_at) as oldest FROM relay_federation_settlements WHERE anchor_batch_id IS NULL AND record_json IS NOT NULL").get();
|
|
72441
|
-
if (oldest.oldest != null && Date.now() - oldest.oldest >= intervalMs) {
|
|
72442
|
-
batch = await cutBatch(db, relayIdentity, maxSize);
|
|
72443
|
-
}
|
|
72444
|
-
}
|
|
72445
|
-
if (batch && submitter) {
|
|
72446
|
-
await submitAnchorOnChain(db, batch.batch_id, submitter);
|
|
72447
72684
|
}
|
|
72448
|
-
|
|
72449
|
-
|
|
72450
|
-
|
|
72451
|
-
|
|
72452
|
-
|
|
72685
|
+
}
|
|
72686
|
+
if (batch && submitter) {
|
|
72687
|
+
await submitAnchorOnChain(db, batch.batch_id, submitter);
|
|
72688
|
+
}
|
|
72689
|
+
if (submitter) {
|
|
72690
|
+
const failedBatches = db.prepare("SELECT batch_id FROM relay_anchor_batches WHERE status = 'signed' AND tx_hash IS NULL").all();
|
|
72691
|
+
for (const fb of failedBatches) {
|
|
72692
|
+
await submitAnchorOnChain(db, fb.batch_id, submitter);
|
|
72453
72693
|
}
|
|
72454
|
-
} catch (err2) {
|
|
72455
|
-
const message2 = err2 instanceof Error ? err2.message : String(err2);
|
|
72456
|
-
logger10.error("anchoring.loop_error", { error: message2 });
|
|
72457
72694
|
}
|
|
72458
|
-
}
|
|
72459
|
-
|
|
72695
|
+
} catch (err2) {
|
|
72696
|
+
const message2 = err2 instanceof Error ? err2.message : String(err2);
|
|
72697
|
+
logger10.error("anchoring.loop_error", { error: message2 });
|
|
72698
|
+
throw err2;
|
|
72699
|
+
}
|
|
72700
|
+
}, { ...isFrozen ? { isFrozen } : {} });
|
|
72460
72701
|
}
|
|
72461
72702
|
async function getSettlementProof(db, settlementId) {
|
|
72462
72703
|
const settlement = db.prepare(`SELECT settlement_id, settled_at, record_json, anchor_batch_id
|
|
@@ -72644,43 +72885,40 @@ function chainAnchorFromRow(row) {
|
|
|
72644
72885
|
anchored_at: row.anchored_at
|
|
72645
72886
|
};
|
|
72646
72887
|
}
|
|
72647
|
-
function startAgentSettlementAnchorLoop(db, relayIdentity, config = {}, isFrozen) {
|
|
72888
|
+
function startAgentSettlementAnchorLoop(db, relayIdentity, config = {}, isFrozen, supervisor) {
|
|
72648
72889
|
const maxSize = config.batchMaxSize ?? DEFAULT_BATCH_MAX_SIZE;
|
|
72649
72890
|
const intervalMs = config.batchIntervalMs ?? DEFAULT_BATCH_INTERVAL_MS;
|
|
72650
72891
|
const checkIntervalMs = Math.min(6e4, intervalMs);
|
|
72651
72892
|
const submitter = config.submitter;
|
|
72652
|
-
return
|
|
72653
|
-
|
|
72654
|
-
|
|
72655
|
-
|
|
72656
|
-
|
|
72657
|
-
|
|
72658
|
-
|
|
72659
|
-
|
|
72660
|
-
|
|
72661
|
-
|
|
72893
|
+
return superviseInterval(supervisor, "agent-settlement-anchor", checkIntervalMs, async () => {
|
|
72894
|
+
try {
|
|
72895
|
+
const countRow = db.prepare("SELECT COUNT(*) as cnt FROM relay_settlements WHERE anchor_batch_id IS NULL AND signature IS NOT NULL").get();
|
|
72896
|
+
if (countRow.cnt === 0)
|
|
72897
|
+
return;
|
|
72898
|
+
let batch = null;
|
|
72899
|
+
if (countRow.cnt >= maxSize) {
|
|
72900
|
+
batch = await cutAgentSettlementBatch(db, relayIdentity, maxSize);
|
|
72901
|
+
} else {
|
|
72902
|
+
const oldest = db.prepare("SELECT MIN(settled_at) as oldest FROM relay_settlements WHERE anchor_batch_id IS NULL AND signature IS NOT NULL").get();
|
|
72903
|
+
if (oldest.oldest != null && Date.now() - oldest.oldest >= intervalMs) {
|
|
72662
72904
|
batch = await cutAgentSettlementBatch(db, relayIdentity, maxSize);
|
|
72663
|
-
} else {
|
|
72664
|
-
const oldest = db.prepare("SELECT MIN(settled_at) as oldest FROM relay_settlements WHERE anchor_batch_id IS NULL AND signature IS NOT NULL").get();
|
|
72665
|
-
if (oldest.oldest != null && Date.now() - oldest.oldest >= intervalMs) {
|
|
72666
|
-
batch = await cutAgentSettlementBatch(db, relayIdentity, maxSize);
|
|
72667
|
-
}
|
|
72668
72905
|
}
|
|
72669
|
-
|
|
72670
|
-
|
|
72671
|
-
|
|
72672
|
-
|
|
72673
|
-
|
|
72674
|
-
|
|
72675
|
-
|
|
72676
|
-
|
|
72906
|
+
}
|
|
72907
|
+
if (batch && submitter) {
|
|
72908
|
+
await submitAgentAnchorOnChain(db, batch.batch_id, submitter);
|
|
72909
|
+
}
|
|
72910
|
+
if (submitter) {
|
|
72911
|
+
const failedBatches = db.prepare("SELECT batch_id FROM relay_agent_anchor_batches WHERE status = 'signed' AND tx_hash IS NULL").all();
|
|
72912
|
+
for (const fb of failedBatches) {
|
|
72913
|
+
await submitAgentAnchorOnChain(db, fb.batch_id, submitter);
|
|
72677
72914
|
}
|
|
72678
|
-
} catch (err2) {
|
|
72679
|
-
const message2 = err2 instanceof Error ? err2.message : String(err2);
|
|
72680
|
-
logger10.error("anchoring.agent_loop_error", { error: message2 });
|
|
72681
72915
|
}
|
|
72682
|
-
}
|
|
72683
|
-
|
|
72916
|
+
} catch (err2) {
|
|
72917
|
+
const message2 = err2 instanceof Error ? err2.message : String(err2);
|
|
72918
|
+
logger10.error("anchoring.agent_loop_error", { error: message2 });
|
|
72919
|
+
throw err2;
|
|
72920
|
+
}
|
|
72921
|
+
}, { ...isFrozen ? { isFrozen } : {} });
|
|
72684
72922
|
}
|
|
72685
72923
|
async function getAgentSettlementProof(db, settlementId) {
|
|
72686
72924
|
const settlement = db.prepare(`SELECT settlement_id, settled_at, record_json, anchor_batch_id
|
|
@@ -72738,6 +72976,7 @@ var init_anchoring = __esm({
|
|
|
72738
72976
|
init_esm_shims();
|
|
72739
72977
|
init_dist6();
|
|
72740
72978
|
init_logger();
|
|
72979
|
+
init_loop_supervisor();
|
|
72741
72980
|
AGENT_SETTLEMENT_ANCHOR_SUITE2 = "motebit-jcs-ed25519-hex-v1";
|
|
72742
72981
|
FEDERATION_SETTLEMENT_ANCHOR_SUITE2 = "motebit-jcs-ed25519-hex-v1";
|
|
72743
72982
|
AGENT_SETTLEMENT_TREE_HASH_VERSION = "merkle-sha256-rfc6962-v2";
|
|
@@ -72868,43 +73107,40 @@ async function submitCredentialAnchorOnChain(db, batchId, submitter) {
|
|
|
72868
73107
|
return false;
|
|
72869
73108
|
}
|
|
72870
73109
|
}
|
|
72871
|
-
function startCredentialAnchorLoop(db, relayIdentity, config = {}, isFrozen) {
|
|
73110
|
+
function startCredentialAnchorLoop(db, relayIdentity, config = {}, isFrozen, supervisor) {
|
|
72872
73111
|
const maxSize = config.batchMaxSize ?? DEFAULT_BATCH_MAX_SIZE2;
|
|
72873
73112
|
const intervalMs = config.batchIntervalMs ?? DEFAULT_BATCH_INTERVAL_MS2;
|
|
72874
73113
|
const submitter = config.submitter;
|
|
72875
73114
|
const checkIntervalMs = Math.min(6e4, intervalMs);
|
|
72876
|
-
return
|
|
72877
|
-
|
|
72878
|
-
|
|
72879
|
-
|
|
72880
|
-
|
|
72881
|
-
|
|
72882
|
-
|
|
72883
|
-
|
|
72884
|
-
|
|
72885
|
-
|
|
73115
|
+
return superviseInterval(supervisor, "credential-anchor", checkIntervalMs, async () => {
|
|
73116
|
+
try {
|
|
73117
|
+
const countRow = db.prepare("SELECT COUNT(*) as cnt FROM relay_credentials WHERE anchor_batch_id IS NULL").get();
|
|
73118
|
+
if (countRow.cnt === 0)
|
|
73119
|
+
return;
|
|
73120
|
+
let batch = null;
|
|
73121
|
+
if (countRow.cnt >= maxSize) {
|
|
73122
|
+
batch = await cutCredentialBatch(db, relayIdentity, maxSize);
|
|
73123
|
+
} else {
|
|
73124
|
+
const oldest = db.prepare("SELECT MIN(issued_at) as oldest FROM relay_credentials WHERE anchor_batch_id IS NULL").get();
|
|
73125
|
+
if (oldest.oldest != null && Date.now() - oldest.oldest >= intervalMs) {
|
|
72886
73126
|
batch = await cutCredentialBatch(db, relayIdentity, maxSize);
|
|
72887
|
-
} else {
|
|
72888
|
-
const oldest = db.prepare("SELECT MIN(issued_at) as oldest FROM relay_credentials WHERE anchor_batch_id IS NULL").get();
|
|
72889
|
-
if (oldest.oldest != null && Date.now() - oldest.oldest >= intervalMs) {
|
|
72890
|
-
batch = await cutCredentialBatch(db, relayIdentity, maxSize);
|
|
72891
|
-
}
|
|
72892
|
-
}
|
|
72893
|
-
if (batch && submitter) {
|
|
72894
|
-
await submitCredentialAnchorOnChain(db, batch.batch_id, submitter);
|
|
72895
73127
|
}
|
|
72896
|
-
|
|
72897
|
-
|
|
72898
|
-
|
|
72899
|
-
|
|
72900
|
-
|
|
73128
|
+
}
|
|
73129
|
+
if (batch && submitter) {
|
|
73130
|
+
await submitCredentialAnchorOnChain(db, batch.batch_id, submitter);
|
|
73131
|
+
}
|
|
73132
|
+
if (submitter) {
|
|
73133
|
+
const failedBatches = db.prepare("SELECT batch_id FROM relay_credential_anchor_batches WHERE status = 'signed' AND tx_hash IS NULL").all();
|
|
73134
|
+
for (const fb of failedBatches) {
|
|
73135
|
+
await submitCredentialAnchorOnChain(db, fb.batch_id, submitter);
|
|
72901
73136
|
}
|
|
72902
|
-
} catch (err2) {
|
|
72903
|
-
const message2 = err2 instanceof Error ? err2.message : String(err2);
|
|
72904
|
-
logger11.error("credential_anchoring.loop_error", { error: message2 });
|
|
72905
73137
|
}
|
|
72906
|
-
}
|
|
72907
|
-
|
|
73138
|
+
} catch (err2) {
|
|
73139
|
+
const message2 = err2 instanceof Error ? err2.message : String(err2);
|
|
73140
|
+
logger11.error("credential_anchoring.loop_error", { error: message2 });
|
|
73141
|
+
throw err2;
|
|
73142
|
+
}
|
|
73143
|
+
}, { ...isFrozen ? { isFrozen } : {} });
|
|
72908
73144
|
}
|
|
72909
73145
|
async function getCredentialAnchorProof(db, credentialId) {
|
|
72910
73146
|
const credential = db.prepare(`SELECT credential_id, credential_json, issued_at, anchor_batch_id
|
|
@@ -73041,6 +73277,7 @@ var init_credential_anchoring = __esm({
|
|
|
73041
73277
|
init_esm_shims();
|
|
73042
73278
|
init_dist6();
|
|
73043
73279
|
init_logger();
|
|
73280
|
+
init_loop_supervisor();
|
|
73044
73281
|
logger11 = createLogger({ service: "relay", module: "credential-anchoring" });
|
|
73045
73282
|
CREDENTIAL_TREE_HASH_VERSION = "merkle-sha256-rfc6962-v2";
|
|
73046
73283
|
DEFAULT_BATCH_MAX_SIZE2 = 50;
|
|
@@ -74043,18 +74280,19 @@ function createTaskRouter(deps) {
|
|
|
74043
74280
|
return [];
|
|
74044
74281
|
}
|
|
74045
74282
|
try {
|
|
74283
|
+
const discoverBody = await signDiscoverBody({
|
|
74284
|
+
query: { capability: requiredCaps[0], limit: 20 },
|
|
74285
|
+
hop_count: 0,
|
|
74286
|
+
max_hops: 1,
|
|
74287
|
+
// Only one hop for task routing candidates
|
|
74288
|
+
visited,
|
|
74289
|
+
query_id: queryId,
|
|
74290
|
+
origin_relay: relayIdentity.relayMotebitId
|
|
74291
|
+
}, relayIdentity);
|
|
74046
74292
|
const resp = await fetch(`${peer.endpoint_url}/federation/v1/discover`, {
|
|
74047
74293
|
method: "POST",
|
|
74048
74294
|
headers: { "Content-Type": "application/json", "X-Correlation-ID": queryId },
|
|
74049
|
-
body: JSON.stringify(
|
|
74050
|
-
query: { capability: requiredCaps[0], limit: 20 },
|
|
74051
|
-
hop_count: 0,
|
|
74052
|
-
max_hops: 1,
|
|
74053
|
-
// Only one hop for task routing candidates
|
|
74054
|
-
visited,
|
|
74055
|
-
query_id: queryId,
|
|
74056
|
-
origin_relay: relayIdentity.relayMotebitId
|
|
74057
|
-
}),
|
|
74295
|
+
body: JSON.stringify(discoverBody),
|
|
74058
74296
|
signal: AbortSignal.timeout(5e3)
|
|
74059
74297
|
});
|
|
74060
74298
|
if (!resp.ok)
|
|
@@ -74456,6 +74694,7 @@ var init_task_routing = __esm({
|
|
|
74456
74694
|
init_dist34();
|
|
74457
74695
|
init_dist5();
|
|
74458
74696
|
init_dist6();
|
|
74697
|
+
init_federation();
|
|
74459
74698
|
init_dist35();
|
|
74460
74699
|
init_logger();
|
|
74461
74700
|
logger12 = createLogger({ service: "relay", module: "task-routing" });
|
|
@@ -76162,6 +76401,7 @@ __export(federation_exports, {
|
|
|
76162
76401
|
registerFederationRoutes: () => registerFederationRoutes,
|
|
76163
76402
|
sendHeartbeats: () => sendHeartbeats,
|
|
76164
76403
|
setRevocationAnchorSubmitter: () => setRevocationAnchorSubmitter,
|
|
76404
|
+
signDiscoverBody: () => signDiscoverBody,
|
|
76165
76405
|
startHeartbeatLoop: () => startHeartbeatLoop,
|
|
76166
76406
|
startSettlementRetryLoop: () => startSettlementRetryLoop
|
|
76167
76407
|
});
|
|
@@ -76447,15 +76687,15 @@ async function initRelayIdentity(db, passphrase, logger39) {
|
|
|
76447
76687
|
did
|
|
76448
76688
|
};
|
|
76449
76689
|
}
|
|
76450
|
-
function createFederationQueryCache() {
|
|
76690
|
+
function createFederationQueryCache(supervisor) {
|
|
76451
76691
|
const cache2 = /* @__PURE__ */ new Map();
|
|
76452
|
-
const pruneInterval =
|
|
76692
|
+
const pruneInterval = superviseInterval(supervisor, "federation-query-cache-prune", FEDERATION_QUERY_TTL_MS, () => {
|
|
76453
76693
|
const cutoff = Date.now() - FEDERATION_QUERY_TTL_MS;
|
|
76454
76694
|
for (const [id, ts] of cache2) {
|
|
76455
76695
|
if (ts < cutoff)
|
|
76456
76696
|
cache2.delete(id);
|
|
76457
76697
|
}
|
|
76458
|
-
}
|
|
76698
|
+
});
|
|
76459
76699
|
return { cache: cache2, pruneInterval };
|
|
76460
76700
|
}
|
|
76461
76701
|
async function sendHeartbeats(db, relayIdentity) {
|
|
@@ -76573,21 +76813,18 @@ async function processSettlementRetries(db, relayIdentity, onRetryExhausted, ret
|
|
|
76573
76813
|
function startSettlementRetryLoop(db, relayIdentity, intervalMs = 3e4, onRetryExhausted, isFrozen, retryPolicy = DEFAULT_RETRY_POLICY, supervisor) {
|
|
76574
76814
|
return superviseInterval(supervisor, "settlement-retry", intervalMs, () => processSettlementRetries(db, relayIdentity, onRetryExhausted, retryPolicy), { isFrozen });
|
|
76575
76815
|
}
|
|
76576
|
-
function startHeartbeatLoop(db, relayIdentity, intervalMs = 6e4, isFrozen) {
|
|
76577
|
-
return
|
|
76578
|
-
if (isFrozen?.())
|
|
76579
|
-
return;
|
|
76580
|
-
void sendHeartbeats(db, relayIdentity);
|
|
76581
|
-
}, intervalMs);
|
|
76816
|
+
function startHeartbeatLoop(db, relayIdentity, intervalMs = 6e4, isFrozen, supervisor) {
|
|
76817
|
+
return superviseInterval(supervisor, "federation-heartbeat", intervalMs, () => sendHeartbeats(db, relayIdentity), { ...isFrozen ? { isFrozen } : {} });
|
|
76582
76818
|
}
|
|
76583
76819
|
async function verifyPeerSignature(db, peerId, signatureHex, payloadBytes, allowedStates = ["active"], timestamp) {
|
|
76584
|
-
if (timestamp
|
|
76585
|
-
|
|
76586
|
-
|
|
76587
|
-
|
|
76588
|
-
|
|
76589
|
-
|
|
76590
|
-
|
|
76820
|
+
if (timestamp == null || !Number.isFinite(timestamp)) {
|
|
76821
|
+
throw new HTTPException6(400, { message: "Federation request timestamp required" });
|
|
76822
|
+
}
|
|
76823
|
+
const drift = Math.abs(Date.now() - timestamp);
|
|
76824
|
+
if (drift > FEDERATION_TIMESTAMP_DRIFT_MS) {
|
|
76825
|
+
throw new HTTPException6(400, {
|
|
76826
|
+
message: "Request timestamp outside acceptable drift (\xB15min)"
|
|
76827
|
+
});
|
|
76591
76828
|
}
|
|
76592
76829
|
const stateList = allowedStates.map(() => "?").join(", ");
|
|
76593
76830
|
const peer = db.prepare(`SELECT public_key FROM relay_peers WHERE peer_relay_id = ? AND state IN (${stateList})`).get(peerId, ...allowedStates);
|
|
@@ -76600,6 +76837,31 @@ async function verifyPeerSignature(db, peerId, signatureHex, payloadBytes, allow
|
|
|
76600
76837
|
}
|
|
76601
76838
|
return peer.public_key;
|
|
76602
76839
|
}
|
|
76840
|
+
async function signDiscoverBody(body, relayIdentity) {
|
|
76841
|
+
const signed = {
|
|
76842
|
+
...body,
|
|
76843
|
+
sender_relay: relayIdentity.relayMotebitId,
|
|
76844
|
+
timestamp: Date.now()
|
|
76845
|
+
};
|
|
76846
|
+
const sig = await ed25519Sign(new TextEncoder().encode(canonicalJson(signed)), relayIdentity.privateKey);
|
|
76847
|
+
return { ...signed, signature: bytesToHex4(sig) };
|
|
76848
|
+
}
|
|
76849
|
+
async function verifyDiscoverSender(db, body, requireSignature) {
|
|
76850
|
+
if (body.signature == null) {
|
|
76851
|
+
if (requireSignature) {
|
|
76852
|
+
throw new HTTPException6(403, { message: "Unsigned discovery request rejected" });
|
|
76853
|
+
}
|
|
76854
|
+
return null;
|
|
76855
|
+
}
|
|
76856
|
+
if (typeof body.sender_relay !== "string") {
|
|
76857
|
+
throw new HTTPException6(400, { message: "Signed discovery request missing sender_relay" });
|
|
76858
|
+
}
|
|
76859
|
+
if (typeof body.timestamp !== "number") {
|
|
76860
|
+
throw new HTTPException6(400, { message: "Signed discovery request missing timestamp" });
|
|
76861
|
+
}
|
|
76862
|
+
const { signature, ...payload } = body;
|
|
76863
|
+
return verifyPeerSignature(db, body.sender_relay, signature, new TextEncoder().encode(canonicalJson(payload)), ["active"], body.timestamp);
|
|
76864
|
+
}
|
|
76603
76865
|
function registerFederationRoutes(deps) {
|
|
76604
76866
|
const { db, app, relayIdentity, federationConfig, federationQueryCache } = deps;
|
|
76605
76867
|
const peerLimiter = new FixedWindowLimiter(30, 6e4);
|
|
@@ -76614,6 +76876,7 @@ function registerFederationRoutes(deps) {
|
|
|
76614
76876
|
}
|
|
76615
76877
|
const federationEnabled = federationConfig?.enabled !== void 0 ? federationConfig.enabled : federationConfig?.endpointUrl != null;
|
|
76616
76878
|
const maxPeers = federationConfig?.maxPeers ?? 50;
|
|
76879
|
+
const requireDiscoverSignature = federationConfig?.requireDiscoverSignature ?? false;
|
|
76617
76880
|
function checkFederationEnabled() {
|
|
76618
76881
|
if (!federationEnabled) {
|
|
76619
76882
|
throw new HTTPException6(403, { message: "Federation is disabled on this relay" });
|
|
@@ -77029,7 +77292,14 @@ function registerFederationRoutes(deps) {
|
|
|
77029
77292
|
if (!federationEnabled) {
|
|
77030
77293
|
return c3.json({ agents: [] });
|
|
77031
77294
|
}
|
|
77032
|
-
|
|
77295
|
+
const verifiedSender = await verifyDiscoverSender(db, body, requireDiscoverSignature);
|
|
77296
|
+
if (verifiedSender == null) {
|
|
77297
|
+
logger17.warn("federation.discover.unsigned", {
|
|
77298
|
+
origin_relay: body.origin_relay,
|
|
77299
|
+
query_id: body.query_id
|
|
77300
|
+
});
|
|
77301
|
+
}
|
|
77302
|
+
checkPeerLimit(verifiedSender ?? body.origin_relay);
|
|
77033
77303
|
if (federationQueryCache.has(body.query_id))
|
|
77034
77304
|
return c3.json({ agents: [] });
|
|
77035
77305
|
federationQueryCache.set(body.query_id, Date.now());
|
|
@@ -77052,20 +77322,21 @@ function registerFederationRoutes(deps) {
|
|
|
77052
77322
|
const peers = db.prepare("SELECT peer_relay_id, endpoint_url FROM relay_peers WHERE state = 'active'").all();
|
|
77053
77323
|
const forwardPromises = peers.filter((p5) => !visitedSet.has(p5.peer_relay_id)).map(async (peer) => {
|
|
77054
77324
|
try {
|
|
77325
|
+
const forwardBody = await signDiscoverBody({
|
|
77326
|
+
query: body.query,
|
|
77327
|
+
hop_count: body.hop_count + 1,
|
|
77328
|
+
max_hops: body.max_hops,
|
|
77329
|
+
visited,
|
|
77330
|
+
query_id: body.query_id,
|
|
77331
|
+
origin_relay: body.origin_relay
|
|
77332
|
+
}, relayIdentity);
|
|
77055
77333
|
const resp = await fetch(`${peer.endpoint_url}/federation/v1/discover`, {
|
|
77056
77334
|
method: "POST",
|
|
77057
77335
|
headers: {
|
|
77058
77336
|
"Content-Type": "application/json",
|
|
77059
77337
|
"X-Correlation-ID": body.query_id
|
|
77060
77338
|
},
|
|
77061
|
-
body: JSON.stringify(
|
|
77062
|
-
query: body.query,
|
|
77063
|
-
hop_count: body.hop_count + 1,
|
|
77064
|
-
max_hops: body.max_hops,
|
|
77065
|
-
visited,
|
|
77066
|
-
query_id: body.query_id,
|
|
77067
|
-
origin_relay: body.origin_relay
|
|
77068
|
-
}),
|
|
77339
|
+
body: JSON.stringify(forwardBody),
|
|
77069
77340
|
signal: AbortSignal.timeout(5e3)
|
|
77070
77341
|
});
|
|
77071
77342
|
if (!resp.ok)
|
|
@@ -77201,7 +77472,7 @@ var init_federation = __esm({
|
|
|
77201
77472
|
init_retry_policy();
|
|
77202
77473
|
init_dist30();
|
|
77203
77474
|
FEDERATION_SUITE = "motebit-concat-ed25519-hex-v1";
|
|
77204
|
-
RELAY_SPEC_VERSION = "motebit/relay-federation@1.
|
|
77475
|
+
RELAY_SPEC_VERSION = "motebit/relay-federation@1.3";
|
|
77205
77476
|
logger17 = createLogger({ service: "relay", module: "federation" });
|
|
77206
77477
|
REVOCATION_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
77207
77478
|
PBKDF2_ITERATIONS = 6e5;
|
|
@@ -77561,10 +77832,12 @@ function startDepositDetector(config) {
|
|
|
77561
77832
|
chain: config.chain,
|
|
77562
77833
|
error: err2 instanceof Error ? err2.message : String(err2)
|
|
77563
77834
|
});
|
|
77835
|
+
throw err2;
|
|
77564
77836
|
}
|
|
77565
77837
|
};
|
|
77566
|
-
void tick()
|
|
77567
|
-
|
|
77838
|
+
void tick().catch(() => {
|
|
77839
|
+
});
|
|
77840
|
+
return superviseInterval(config.supervisor, "deposit-detector", intervalMs, tick);
|
|
77568
77841
|
}
|
|
77569
77842
|
var logger18, TRANSFER_TOPIC, USDC_CONTRACTS, DEFAULT_RPC_URLS, CONFIRMATIONS_BY_CHAIN, USDC_ONCHAIN_TO_MICRO, SqliteDepositDetectorStore;
|
|
77570
77843
|
var init_deposit_detector = __esm({
|
|
@@ -77575,6 +77848,7 @@ var init_deposit_detector = __esm({
|
|
|
77575
77848
|
init_dist38();
|
|
77576
77849
|
init_account_store_sqlite();
|
|
77577
77850
|
init_logger();
|
|
77851
|
+
init_loop_supervisor();
|
|
77578
77852
|
logger18 = createLogger({ service: "deposit-detector" });
|
|
77579
77853
|
TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
|
|
77580
77854
|
USDC_CONTRACTS = {
|
|
@@ -80814,18 +81088,19 @@ async function runIdentityLogAnchorTick(db, relayIdentity, config = {}) {
|
|
|
80814
81088
|
await submitIdentityLogAnchorOnChain(db, anchor.anchor_id, submitter);
|
|
80815
81089
|
}
|
|
80816
81090
|
}
|
|
80817
|
-
function startIdentityLogAnchorLoop(db, relayIdentity, config = {}, isFrozen) {
|
|
81091
|
+
function startIdentityLogAnchorLoop(db, relayIdentity, config = {}, isFrozen, supervisor) {
|
|
80818
81092
|
const intervalMs = config.intervalMs ?? DEFAULT_IDENTITY_ANCHOR_INTERVAL_MS;
|
|
80819
81093
|
const checkIntervalMs = Math.min(IDENTITY_ANCHOR_CHECK_INTERVAL_MS, intervalMs);
|
|
80820
|
-
return
|
|
80821
|
-
|
|
80822
|
-
|
|
80823
|
-
|
|
81094
|
+
return superviseInterval(supervisor, "identity-log-anchor", checkIntervalMs, async () => {
|
|
81095
|
+
try {
|
|
81096
|
+
await runIdentityLogAnchorTick(db, relayIdentity, config);
|
|
81097
|
+
} catch (err2) {
|
|
80824
81098
|
logger33.error("identity_log.anchor_loop_error", {
|
|
80825
81099
|
error: err2 instanceof Error ? err2.message : String(err2)
|
|
80826
81100
|
});
|
|
80827
|
-
|
|
80828
|
-
|
|
81101
|
+
throw err2;
|
|
81102
|
+
}
|
|
81103
|
+
}, { ...isFrozen ? { isFrozen } : {} });
|
|
80829
81104
|
}
|
|
80830
81105
|
var logger33, IDENTITY_LOG_TREE_HASH_VERSION, DEFAULT_IDENTITY_ANCHOR_INTERVAL_MS, IDENTITY_ANCHOR_CHECK_INTERVAL_MS;
|
|
80831
81106
|
var init_identity_log_anchoring = __esm({
|
|
@@ -80834,6 +81109,7 @@ var init_identity_log_anchoring = __esm({
|
|
|
80834
81109
|
init_esm_shims();
|
|
80835
81110
|
init_dist6();
|
|
80836
81111
|
init_logger();
|
|
81112
|
+
init_loop_supervisor();
|
|
80837
81113
|
init_identity_log();
|
|
80838
81114
|
init_identity_transparency();
|
|
80839
81115
|
logger33 = createLogger({ service: "relay", module: "identity-log-anchoring" });
|
|
@@ -81815,13 +82091,16 @@ async function runDeferredOrchestrationCycle(deps) {
|
|
|
81815
82091
|
}
|
|
81816
82092
|
function startDeferredOrchestrationWorker(deps) {
|
|
81817
82093
|
const intervalMs = deps.intervalMs ?? ORCHESTRATION_WORKER_INTERVAL_MS;
|
|
81818
|
-
const handle =
|
|
81819
|
-
|
|
82094
|
+
const handle = superviseInterval(deps.supervisor, "dispute-orchestration", intervalMs, async () => {
|
|
82095
|
+
try {
|
|
82096
|
+
await runDeferredOrchestrationCycle(deps);
|
|
82097
|
+
} catch (err2) {
|
|
81820
82098
|
logger36.warn("dispute.orchestration.cycle_failed", {
|
|
81821
82099
|
error: err2 instanceof Error ? err2.message : String(err2)
|
|
81822
82100
|
});
|
|
81823
|
-
|
|
81824
|
-
|
|
82101
|
+
throw err2;
|
|
82102
|
+
}
|
|
82103
|
+
});
|
|
81825
82104
|
if (typeof handle.unref === "function")
|
|
81826
82105
|
handle.unref();
|
|
81827
82106
|
return handle;
|
|
@@ -82359,6 +82638,7 @@ var init_disputes = __esm({
|
|
|
82359
82638
|
init_dist30();
|
|
82360
82639
|
init_accounts();
|
|
82361
82640
|
init_logger();
|
|
82641
|
+
init_loop_supervisor();
|
|
82362
82642
|
init_dispute_orchestration();
|
|
82363
82643
|
logger36 = createLogger({ service: "relay", module: "disputes" });
|
|
82364
82644
|
EVIDENCE_WINDOW_MS = 48 * 60 * 60 * 1e3;
|
|
@@ -84351,6 +84631,1441 @@ async function consumeStream(stream, runtime, opts) {
|
|
|
84351
84631
|
}
|
|
84352
84632
|
}
|
|
84353
84633
|
|
|
84634
|
+
// src/runtime-host.ts
|
|
84635
|
+
init_esm_shims();
|
|
84636
|
+
|
|
84637
|
+
// ../../packages/runtime-host/dist/index.js
|
|
84638
|
+
init_esm_shims();
|
|
84639
|
+
|
|
84640
|
+
// ../../packages/runtime-host/dist/protocol.js
|
|
84641
|
+
init_esm_shims();
|
|
84642
|
+
var RUNTIME_HOST_PROTOCOL_VERSION = 1;
|
|
84643
|
+
var MAX_FRAME_BYTES = 4 * 1024 * 1024;
|
|
84644
|
+
function encodeFrame(message2) {
|
|
84645
|
+
return `${JSON.stringify(message2)}
|
|
84646
|
+
`;
|
|
84647
|
+
}
|
|
84648
|
+
var JsonLineDecoder = class {
|
|
84649
|
+
buffer = "";
|
|
84650
|
+
// Streaming decoder: a multibyte UTF-8 character split across two
|
|
84651
|
+
// socket chunks must not corrupt — `stream: true` holds the partial
|
|
84652
|
+
// sequence until its continuation bytes arrive.
|
|
84653
|
+
utf8 = new TextDecoder();
|
|
84654
|
+
push(data) {
|
|
84655
|
+
this.buffer += typeof data === "string" ? data : this.utf8.decode(data, { stream: true });
|
|
84656
|
+
if (this.buffer.length > MAX_FRAME_BYTES) {
|
|
84657
|
+
throw new Error(`frame exceeds ${MAX_FRAME_BYTES} bytes`);
|
|
84658
|
+
}
|
|
84659
|
+
const frames = [];
|
|
84660
|
+
let newline = this.buffer.indexOf("\n");
|
|
84661
|
+
while (newline !== -1) {
|
|
84662
|
+
const line = this.buffer.slice(0, newline);
|
|
84663
|
+
this.buffer = this.buffer.slice(newline + 1);
|
|
84664
|
+
if (line.trim().length > 0) {
|
|
84665
|
+
let parsed;
|
|
84666
|
+
try {
|
|
84667
|
+
parsed = JSON.parse(line);
|
|
84668
|
+
} catch (err2) {
|
|
84669
|
+
throw new Error(`malformed frame: ${err2 instanceof Error ? err2.message : String(err2)}`, {
|
|
84670
|
+
cause: err2
|
|
84671
|
+
});
|
|
84672
|
+
}
|
|
84673
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
84674
|
+
throw new Error("malformed frame: not a JSON object");
|
|
84675
|
+
}
|
|
84676
|
+
frames.push(parsed);
|
|
84677
|
+
}
|
|
84678
|
+
newline = this.buffer.indexOf("\n");
|
|
84679
|
+
}
|
|
84680
|
+
return frames;
|
|
84681
|
+
}
|
|
84682
|
+
};
|
|
84683
|
+
|
|
84684
|
+
// ../../packages/runtime-host/dist/paths-shared.js
|
|
84685
|
+
init_esm_shims();
|
|
84686
|
+
function isWindowsPipePath(path19) {
|
|
84687
|
+
return path19.startsWith("\\\\.\\pipe\\");
|
|
84688
|
+
}
|
|
84689
|
+
|
|
84690
|
+
// ../../packages/runtime-host/dist/lockfile.js
|
|
84691
|
+
init_esm_shims();
|
|
84692
|
+
async function readLockfile(platform, path19) {
|
|
84693
|
+
const raw = await platform.readFile(path19);
|
|
84694
|
+
if (raw === null)
|
|
84695
|
+
return null;
|
|
84696
|
+
try {
|
|
84697
|
+
const parsed = JSON.parse(raw);
|
|
84698
|
+
if (parsed === null || typeof parsed !== "object")
|
|
84699
|
+
return null;
|
|
84700
|
+
const record = parsed;
|
|
84701
|
+
if (typeof record.pid !== "number" || typeof record.bound_at !== "number" || typeof record.protocol_version !== "number") {
|
|
84702
|
+
return null;
|
|
84703
|
+
}
|
|
84704
|
+
return {
|
|
84705
|
+
pid: record.pid,
|
|
84706
|
+
bound_at: record.bound_at,
|
|
84707
|
+
protocol_version: record.protocol_version
|
|
84708
|
+
};
|
|
84709
|
+
} catch {
|
|
84710
|
+
return null;
|
|
84711
|
+
}
|
|
84712
|
+
}
|
|
84713
|
+
async function writeLockfile(platform, path19, record) {
|
|
84714
|
+
await platform.writeFile(path19, `${JSON.stringify(record)}
|
|
84715
|
+
`);
|
|
84716
|
+
}
|
|
84717
|
+
async function removeLockfile(platform, path19, pid) {
|
|
84718
|
+
const current = await readLockfile(platform, path19);
|
|
84719
|
+
if (current !== null && current.pid !== pid)
|
|
84720
|
+
return;
|
|
84721
|
+
try {
|
|
84722
|
+
await platform.removeFile(path19);
|
|
84723
|
+
} catch {
|
|
84724
|
+
}
|
|
84725
|
+
}
|
|
84726
|
+
|
|
84727
|
+
// ../../packages/runtime-host/dist/server.js
|
|
84728
|
+
init_esm_shims();
|
|
84729
|
+
init_dist5();
|
|
84730
|
+
init_dist();
|
|
84731
|
+
var CoordinatorAlreadyBoundError = class extends Error {
|
|
84732
|
+
constructor(socketPath) {
|
|
84733
|
+
super(`another process already binds ${socketPath}`);
|
|
84734
|
+
this.name = "CoordinatorAlreadyBoundError";
|
|
84735
|
+
}
|
|
84736
|
+
};
|
|
84737
|
+
function parseTokenClaims(token) {
|
|
84738
|
+
const dot = token.indexOf(".");
|
|
84739
|
+
if (dot <= 0)
|
|
84740
|
+
return null;
|
|
84741
|
+
try {
|
|
84742
|
+
const payload = JSON.parse(new TextDecoder().decode(fromBase64Url(token.slice(0, dot))));
|
|
84743
|
+
if (payload === null || typeof payload !== "object")
|
|
84744
|
+
return null;
|
|
84745
|
+
const record = payload;
|
|
84746
|
+
if (typeof record.mid !== "string" || typeof record.did !== "string" || typeof record.aud !== "string") {
|
|
84747
|
+
return null;
|
|
84748
|
+
}
|
|
84749
|
+
return { mid: record.mid, did: record.did, aud: record.aud };
|
|
84750
|
+
} catch {
|
|
84751
|
+
return null;
|
|
84752
|
+
}
|
|
84753
|
+
}
|
|
84754
|
+
var RuntimeHostServer = class _RuntimeHostServer {
|
|
84755
|
+
listener;
|
|
84756
|
+
opts;
|
|
84757
|
+
pid;
|
|
84758
|
+
connections = /* @__PURE__ */ new Set();
|
|
84759
|
+
/** capability → the connection contributing it (last registrar wins). */
|
|
84760
|
+
bridged = /* @__PURE__ */ new Map();
|
|
84761
|
+
bridgeInflight = /* @__PURE__ */ new Map();
|
|
84762
|
+
capabilityListeners = /* @__PURE__ */ new Set();
|
|
84763
|
+
bridgeCounter = 0;
|
|
84764
|
+
closed = false;
|
|
84765
|
+
constructor(listener, opts, pid) {
|
|
84766
|
+
this.listener = listener;
|
|
84767
|
+
this.opts = opts;
|
|
84768
|
+
this.pid = pid;
|
|
84769
|
+
}
|
|
84770
|
+
/**
|
|
84771
|
+
* Bind the canonical endpoint and become the coordinator. Throws
|
|
84772
|
+
* `CoordinatorAlreadyBoundError` when another listener holds it —
|
|
84773
|
+
* the caller attaches instead. The caller is responsible for
|
|
84774
|
+
* clearing a *verified-stale* socket file first (the election does
|
|
84775
|
+
* this); bind never unlinks.
|
|
84776
|
+
*/
|
|
84777
|
+
static async bind(opts) {
|
|
84778
|
+
const pid = opts.pid ?? opts.platform.pid;
|
|
84779
|
+
const now = opts.now ?? (() => Date.now());
|
|
84780
|
+
const bound = await opts.platform.bind(opts.socketPath);
|
|
84781
|
+
if (bound === "in_use")
|
|
84782
|
+
throw new CoordinatorAlreadyBoundError(opts.socketPath);
|
|
84783
|
+
const host = new _RuntimeHostServer(bound, opts, pid);
|
|
84784
|
+
bound.onConnection((conn) => host.onConnection(conn));
|
|
84785
|
+
await writeLockfile(opts.platform, opts.lockfilePath, {
|
|
84786
|
+
pid,
|
|
84787
|
+
bound_at: now(),
|
|
84788
|
+
protocol_version: RUNTIME_HOST_PROTOCOL_VERSION
|
|
84789
|
+
});
|
|
84790
|
+
return host;
|
|
84791
|
+
}
|
|
84792
|
+
get attachedCount() {
|
|
84793
|
+
let count2 = 0;
|
|
84794
|
+
for (const conn of this.connections)
|
|
84795
|
+
if (conn.authenticated)
|
|
84796
|
+
count2 += 1;
|
|
84797
|
+
return count2;
|
|
84798
|
+
}
|
|
84799
|
+
/** The organs currently contributed by attached frontends. */
|
|
84800
|
+
get bridgedCapabilities() {
|
|
84801
|
+
return [...this.bridged.keys()];
|
|
84802
|
+
}
|
|
84803
|
+
/**
|
|
84804
|
+
* Subscribe to changes in the bridged-organ set — fired after a
|
|
84805
|
+
* frontend registers capabilities and after a contributing frontend
|
|
84806
|
+
* disconnects. The listener receives the CURRENT full set (idempotent
|
|
84807
|
+
* sync semantics, never deltas: last-registrar-wins and multi-frontend
|
|
84808
|
+
* churn collapse to "reconcile against this"). Returns unsubscribe.
|
|
84809
|
+
*/
|
|
84810
|
+
onBridgedCapabilitiesChanged(listener) {
|
|
84811
|
+
this.capabilityListeners.add(listener);
|
|
84812
|
+
return () => this.capabilityListeners.delete(listener);
|
|
84813
|
+
}
|
|
84814
|
+
/**
|
|
84815
|
+
* Listener faults must not tear down the coordinator or the frame
|
|
84816
|
+
* that triggered the change — log and keep serving.
|
|
84817
|
+
*/
|
|
84818
|
+
notifyCapabilitiesChanged() {
|
|
84819
|
+
const capabilities = this.bridgedCapabilities;
|
|
84820
|
+
for (const listener of this.capabilityListeners) {
|
|
84821
|
+
try {
|
|
84822
|
+
listener(capabilities);
|
|
84823
|
+
} catch (err2) {
|
|
84824
|
+
this.opts.logger?.warn("runtime-host: bridged-capabilities listener failed", {
|
|
84825
|
+
error: err2 instanceof Error ? err2.message : String(err2)
|
|
84826
|
+
});
|
|
84827
|
+
}
|
|
84828
|
+
}
|
|
84829
|
+
}
|
|
84830
|
+
/** Push an event to every attached frontend subscribed to `channel`. */
|
|
84831
|
+
publishEvent(channel, payload) {
|
|
84832
|
+
for (const conn of this.connections) {
|
|
84833
|
+
if (conn.authenticated && conn.channels.has(channel)) {
|
|
84834
|
+
this.send(conn, { t: "event", channel, payload });
|
|
84835
|
+
}
|
|
84836
|
+
}
|
|
84837
|
+
}
|
|
84838
|
+
/**
|
|
84839
|
+
* Invoke an organ a frontend contributed — the bridging half of the
|
|
84840
|
+
* election doctrine. Streams the frontend's chunks; throws honestly
|
|
84841
|
+
* when no attached frontend contributes the capability or the
|
|
84842
|
+
* contributor disconnects mid-stream (fail-loud, never silent retry
|
|
84843
|
+
* across the authority boundary).
|
|
84844
|
+
*/
|
|
84845
|
+
async *invokeBridged(capability, prompt2, options) {
|
|
84846
|
+
const conn = this.bridged.get(capability);
|
|
84847
|
+
if (conn === void 0 || conn.conn.destroyed) {
|
|
84848
|
+
throw new Error(`no attached frontend contributes capability "${capability}"`);
|
|
84849
|
+
}
|
|
84850
|
+
this.bridgeCounter += 1;
|
|
84851
|
+
const id = `bridge-${this.bridgeCounter}`;
|
|
84852
|
+
const state = { conn, queue: [], done: false, error: null, wake: null };
|
|
84853
|
+
this.bridgeInflight.set(id, state);
|
|
84854
|
+
conn.bridgeIds.add(id);
|
|
84855
|
+
this.send(conn, { t: "bridge_invoke", id, capability, prompt: prompt2, options });
|
|
84856
|
+
try {
|
|
84857
|
+
for (; ; ) {
|
|
84858
|
+
while (state.queue.length > 0)
|
|
84859
|
+
yield state.queue.shift();
|
|
84860
|
+
if (state.error !== null)
|
|
84861
|
+
throw state.error;
|
|
84862
|
+
if (state.done)
|
|
84863
|
+
return;
|
|
84864
|
+
await new Promise((resolve14) => {
|
|
84865
|
+
state.wake = resolve14;
|
|
84866
|
+
});
|
|
84867
|
+
state.wake = null;
|
|
84868
|
+
}
|
|
84869
|
+
} finally {
|
|
84870
|
+
this.bridgeInflight.delete(id);
|
|
84871
|
+
conn.bridgeIds.delete(id);
|
|
84872
|
+
}
|
|
84873
|
+
}
|
|
84874
|
+
/** Stop coordinating: drop connections, release the socket + lock. */
|
|
84875
|
+
async close() {
|
|
84876
|
+
if (this.closed)
|
|
84877
|
+
return;
|
|
84878
|
+
this.closed = true;
|
|
84879
|
+
for (const conn of this.connections)
|
|
84880
|
+
this.destroyConnection(conn);
|
|
84881
|
+
await this.listener.close();
|
|
84882
|
+
await this.opts.platform.removeSocketFile(this.opts.socketPath);
|
|
84883
|
+
await removeLockfile(this.opts.platform, this.opts.lockfilePath, this.pid);
|
|
84884
|
+
}
|
|
84885
|
+
onConnection(frameConn) {
|
|
84886
|
+
const conn = {
|
|
84887
|
+
conn: frameConn,
|
|
84888
|
+
decoder: new JsonLineDecoder(),
|
|
84889
|
+
authenticated: false,
|
|
84890
|
+
channels: /* @__PURE__ */ new Set(),
|
|
84891
|
+
capabilities: /* @__PURE__ */ new Set(),
|
|
84892
|
+
inflight: /* @__PURE__ */ new Map(),
|
|
84893
|
+
bridgeIds: /* @__PURE__ */ new Set(),
|
|
84894
|
+
handshakeTimer: null
|
|
84895
|
+
};
|
|
84896
|
+
this.connections.add(conn);
|
|
84897
|
+
conn.handshakeTimer = setTimeout(() => {
|
|
84898
|
+
if (!conn.authenticated)
|
|
84899
|
+
this.destroyConnection(conn);
|
|
84900
|
+
}, this.opts.handshakeTimeoutMs ?? 5e3);
|
|
84901
|
+
frameConn.onData((data) => {
|
|
84902
|
+
let frames;
|
|
84903
|
+
try {
|
|
84904
|
+
frames = conn.decoder.push(data);
|
|
84905
|
+
} catch (err2) {
|
|
84906
|
+
this.opts.logger?.warn("runtime-host: destroying connection on malformed frame", {
|
|
84907
|
+
error: err2 instanceof Error ? err2.message : String(err2)
|
|
84908
|
+
});
|
|
84909
|
+
this.destroyConnection(conn);
|
|
84910
|
+
return;
|
|
84911
|
+
}
|
|
84912
|
+
for (const frame of frames) {
|
|
84913
|
+
void this.onMessage(conn, frame);
|
|
84914
|
+
}
|
|
84915
|
+
});
|
|
84916
|
+
frameConn.onClose(() => this.destroyConnection(conn));
|
|
84917
|
+
}
|
|
84918
|
+
async onMessage(conn, message2) {
|
|
84919
|
+
if (!conn.authenticated) {
|
|
84920
|
+
if (message2.t !== "hello") {
|
|
84921
|
+
this.destroyConnection(conn);
|
|
84922
|
+
return;
|
|
84923
|
+
}
|
|
84924
|
+
await this.onHello(conn, message2);
|
|
84925
|
+
return;
|
|
84926
|
+
}
|
|
84927
|
+
switch (message2.t) {
|
|
84928
|
+
case "hello":
|
|
84929
|
+
this.destroyConnection(conn);
|
|
84930
|
+
return;
|
|
84931
|
+
case "subscribe":
|
|
84932
|
+
if (typeof message2.channel === "string")
|
|
84933
|
+
conn.channels.add(message2.channel);
|
|
84934
|
+
return;
|
|
84935
|
+
case "unsubscribe":
|
|
84936
|
+
if (typeof message2.channel === "string")
|
|
84937
|
+
conn.channels.delete(message2.channel);
|
|
84938
|
+
return;
|
|
84939
|
+
case "register_capabilities": {
|
|
84940
|
+
if (!Array.isArray(message2.capabilities) || message2.capabilities.some((c3) => typeof c3 !== "string")) {
|
|
84941
|
+
this.destroyConnection(conn);
|
|
84942
|
+
return;
|
|
84943
|
+
}
|
|
84944
|
+
for (const capability of conn.capabilities) {
|
|
84945
|
+
if (this.bridged.get(capability) === conn)
|
|
84946
|
+
this.bridged.delete(capability);
|
|
84947
|
+
}
|
|
84948
|
+
conn.capabilities = new Set(message2.capabilities);
|
|
84949
|
+
for (const capability of conn.capabilities) {
|
|
84950
|
+
this.bridged.set(capability, conn);
|
|
84951
|
+
}
|
|
84952
|
+
this.notifyCapabilitiesChanged();
|
|
84953
|
+
return;
|
|
84954
|
+
}
|
|
84955
|
+
case "bridge_chunk":
|
|
84956
|
+
case "bridge_end":
|
|
84957
|
+
case "bridge_error": {
|
|
84958
|
+
const state = this.bridgeInflight.get(message2.id);
|
|
84959
|
+
if (state === void 0 || state.conn !== conn)
|
|
84960
|
+
return;
|
|
84961
|
+
if (message2.t === "bridge_chunk")
|
|
84962
|
+
state.queue.push(message2.chunk);
|
|
84963
|
+
else if (message2.t === "bridge_end")
|
|
84964
|
+
state.done = true;
|
|
84965
|
+
else
|
|
84966
|
+
state.error = new Error(message2.message);
|
|
84967
|
+
state.wake?.();
|
|
84968
|
+
return;
|
|
84969
|
+
}
|
|
84970
|
+
case "invoke":
|
|
84971
|
+
if (typeof message2.id !== "string" || typeof message2.capability !== "string" || typeof message2.prompt !== "string") {
|
|
84972
|
+
this.destroyConnection(conn);
|
|
84973
|
+
return;
|
|
84974
|
+
}
|
|
84975
|
+
await this.runStream(conn, message2.id, (ctx) => this.opts.onInvoke(message2.capability, message2.prompt, message2.options, ctx));
|
|
84976
|
+
return;
|
|
84977
|
+
case "chat": {
|
|
84978
|
+
if (typeof message2.id !== "string" || typeof message2.text !== "string") {
|
|
84979
|
+
this.destroyConnection(conn);
|
|
84980
|
+
return;
|
|
84981
|
+
}
|
|
84982
|
+
const onChat = this.opts.onChat;
|
|
84983
|
+
if (onChat === void 0) {
|
|
84984
|
+
this.send(conn, {
|
|
84985
|
+
t: "invoke_error",
|
|
84986
|
+
id: message2.id,
|
|
84987
|
+
message: "this coordinator does not proxy chat"
|
|
84988
|
+
});
|
|
84989
|
+
return;
|
|
84990
|
+
}
|
|
84991
|
+
await this.runStream(conn, message2.id, (ctx) => onChat(message2.text, message2.options, ctx));
|
|
84992
|
+
return;
|
|
84993
|
+
}
|
|
84994
|
+
case "resolve_approval": {
|
|
84995
|
+
if (typeof message2.id !== "string" || typeof message2.approved !== "boolean" || typeof message2.approver_id !== "string") {
|
|
84996
|
+
this.destroyConnection(conn);
|
|
84997
|
+
return;
|
|
84998
|
+
}
|
|
84999
|
+
const onResolveApproval = this.opts.onResolveApproval;
|
|
85000
|
+
if (onResolveApproval === void 0) {
|
|
85001
|
+
this.send(conn, {
|
|
85002
|
+
t: "invoke_error",
|
|
85003
|
+
id: message2.id,
|
|
85004
|
+
message: "this coordinator does not proxy approval resolution"
|
|
85005
|
+
});
|
|
85006
|
+
return;
|
|
85007
|
+
}
|
|
85008
|
+
await this.runStream(conn, message2.id, (ctx) => onResolveApproval(message2.approved, message2.approver_id, ctx));
|
|
85009
|
+
return;
|
|
85010
|
+
}
|
|
85011
|
+
default:
|
|
85012
|
+
this.destroyConnection(conn);
|
|
85013
|
+
}
|
|
85014
|
+
}
|
|
85015
|
+
refuse(conn, reason, detail) {
|
|
85016
|
+
this.send(conn, { t: "refuse", reason, detail });
|
|
85017
|
+
conn.conn.end();
|
|
85018
|
+
}
|
|
85019
|
+
async onHello(conn, hello) {
|
|
85020
|
+
if (typeof hello.protocol_version !== "number" || typeof hello.token !== "string") {
|
|
85021
|
+
this.refuse(conn, "malformed_hello", "hello requires protocol_version and token");
|
|
85022
|
+
return;
|
|
85023
|
+
}
|
|
85024
|
+
if (hello.protocol_version !== RUNTIME_HOST_PROTOCOL_VERSION) {
|
|
85025
|
+
this.refuse(conn, "version_skew", `coordinator speaks runtime-host protocol v${RUNTIME_HOST_PROTOCOL_VERSION}, attacher sent v${hello.protocol_version}`);
|
|
85026
|
+
return;
|
|
85027
|
+
}
|
|
85028
|
+
const claims = parseTokenClaims(hello.token);
|
|
85029
|
+
if (claims === null) {
|
|
85030
|
+
this.refuse(conn, "auth_failed", "attach token is malformed");
|
|
85031
|
+
return;
|
|
85032
|
+
}
|
|
85033
|
+
if (claims.aud !== RUNTIME_ATTACH_AUDIENCE) {
|
|
85034
|
+
this.refuse(conn, "auth_failed", `attach token audience is "${claims.aud}", expected "${RUNTIME_ATTACH_AUDIENCE}"`);
|
|
85035
|
+
return;
|
|
85036
|
+
}
|
|
85037
|
+
if (claims.mid !== this.opts.motebitId) {
|
|
85038
|
+
this.refuse(conn, "auth_failed", "attach token is for a different motebit identity");
|
|
85039
|
+
return;
|
|
85040
|
+
}
|
|
85041
|
+
let publicKey;
|
|
85042
|
+
try {
|
|
85043
|
+
publicKey = await this.opts.resolveDevicePublicKey(claims.did);
|
|
85044
|
+
} catch (err2) {
|
|
85045
|
+
this.opts.logger?.warn("runtime-host: device key resolution failed", {
|
|
85046
|
+
error: err2 instanceof Error ? err2.message : String(err2)
|
|
85047
|
+
});
|
|
85048
|
+
publicKey = null;
|
|
85049
|
+
}
|
|
85050
|
+
if (publicKey === null) {
|
|
85051
|
+
this.refuse(conn, "auth_failed", `unknown device "${claims.did}"`);
|
|
85052
|
+
return;
|
|
85053
|
+
}
|
|
85054
|
+
const payload = await verifySignedToken(hello.token, publicKey);
|
|
85055
|
+
if (payload === null) {
|
|
85056
|
+
this.refuse(conn, "auth_failed", "attach token signature invalid or token expired");
|
|
85057
|
+
return;
|
|
85058
|
+
}
|
|
85059
|
+
conn.authenticated = true;
|
|
85060
|
+
if (conn.handshakeTimer !== null) {
|
|
85061
|
+
clearTimeout(conn.handshakeTimer);
|
|
85062
|
+
conn.handshakeTimer = null;
|
|
85063
|
+
}
|
|
85064
|
+
this.send(conn, {
|
|
85065
|
+
t: "hello_ack",
|
|
85066
|
+
protocol_version: RUNTIME_HOST_PROTOCOL_VERSION,
|
|
85067
|
+
coordinator_pid: this.pid
|
|
85068
|
+
});
|
|
85069
|
+
}
|
|
85070
|
+
/** Drive one handler stream onto the wire: chunk* then end, or invoke_error. */
|
|
85071
|
+
async runStream(conn, id, start) {
|
|
85072
|
+
const controller = new AbortController();
|
|
85073
|
+
conn.inflight.set(id, controller);
|
|
85074
|
+
try {
|
|
85075
|
+
for await (const chunk of start({ signal: controller.signal })) {
|
|
85076
|
+
if (controller.signal.aborted || conn.conn.destroyed)
|
|
85077
|
+
return;
|
|
85078
|
+
this.send(conn, { t: "chunk", id, chunk });
|
|
85079
|
+
}
|
|
85080
|
+
if (!conn.conn.destroyed)
|
|
85081
|
+
this.send(conn, { t: "end", id });
|
|
85082
|
+
} catch (err2) {
|
|
85083
|
+
if (!conn.conn.destroyed) {
|
|
85084
|
+
this.send(conn, {
|
|
85085
|
+
t: "invoke_error",
|
|
85086
|
+
id,
|
|
85087
|
+
message: err2 instanceof Error ? err2.message : String(err2)
|
|
85088
|
+
});
|
|
85089
|
+
}
|
|
85090
|
+
} finally {
|
|
85091
|
+
conn.inflight.delete(id);
|
|
85092
|
+
}
|
|
85093
|
+
}
|
|
85094
|
+
send(conn, message2) {
|
|
85095
|
+
if (conn.conn.destroyed)
|
|
85096
|
+
return;
|
|
85097
|
+
conn.conn.send(encodeFrame(message2));
|
|
85098
|
+
}
|
|
85099
|
+
destroyConnection(conn) {
|
|
85100
|
+
if (!this.connections.has(conn))
|
|
85101
|
+
return;
|
|
85102
|
+
this.connections.delete(conn);
|
|
85103
|
+
if (conn.handshakeTimer !== null)
|
|
85104
|
+
clearTimeout(conn.handshakeTimer);
|
|
85105
|
+
for (const controller of conn.inflight.values())
|
|
85106
|
+
controller.abort();
|
|
85107
|
+
conn.inflight.clear();
|
|
85108
|
+
for (const id of conn.bridgeIds) {
|
|
85109
|
+
const state = this.bridgeInflight.get(id);
|
|
85110
|
+
if (state !== void 0 && state.error === null && !state.done) {
|
|
85111
|
+
state.error = new Error("bridged frontend disconnected mid-invocation");
|
|
85112
|
+
state.wake?.();
|
|
85113
|
+
}
|
|
85114
|
+
}
|
|
85115
|
+
conn.bridgeIds.clear();
|
|
85116
|
+
let removedOrgans = 0;
|
|
85117
|
+
for (const capability of conn.capabilities) {
|
|
85118
|
+
if (this.bridged.get(capability) === conn) {
|
|
85119
|
+
this.bridged.delete(capability);
|
|
85120
|
+
removedOrgans += 1;
|
|
85121
|
+
}
|
|
85122
|
+
}
|
|
85123
|
+
conn.conn.destroy();
|
|
85124
|
+
if (removedOrgans > 0)
|
|
85125
|
+
this.notifyCapabilitiesChanged();
|
|
85126
|
+
}
|
|
85127
|
+
};
|
|
85128
|
+
|
|
85129
|
+
// ../../packages/runtime-host/dist/client.js
|
|
85130
|
+
init_esm_shims();
|
|
85131
|
+
init_dist5();
|
|
85132
|
+
init_dist();
|
|
85133
|
+
var AttachRefusedError = class extends Error {
|
|
85134
|
+
reason;
|
|
85135
|
+
detail;
|
|
85136
|
+
constructor(reason, detail) {
|
|
85137
|
+
super(`attach refused (${reason}): ${detail}`);
|
|
85138
|
+
this.reason = reason;
|
|
85139
|
+
this.detail = detail;
|
|
85140
|
+
this.name = "AttachRefusedError";
|
|
85141
|
+
}
|
|
85142
|
+
};
|
|
85143
|
+
var CoordinatorUnreachableError = class extends Error {
|
|
85144
|
+
constructor(socketPath, cause) {
|
|
85145
|
+
super(`no coordinator reachable at ${socketPath}`, { cause });
|
|
85146
|
+
this.name = "CoordinatorUnreachableError";
|
|
85147
|
+
}
|
|
85148
|
+
};
|
|
85149
|
+
async function mintAttachToken(identity, privateKey, opts = {}) {
|
|
85150
|
+
const now = opts.now ?? (() => Date.now());
|
|
85151
|
+
const issuedAt = now();
|
|
85152
|
+
return createSignedToken({
|
|
85153
|
+
mid: identity.motebitId,
|
|
85154
|
+
did: identity.deviceId,
|
|
85155
|
+
iat: issuedAt,
|
|
85156
|
+
exp: issuedAt + (opts.ttlMs ?? 3e4),
|
|
85157
|
+
// Web Crypto — available in node ≥ 20 AND the desktop webview;
|
|
85158
|
+
// this module must never import node:crypto.
|
|
85159
|
+
jti: globalThis.crypto.randomUUID(),
|
|
85160
|
+
aud: RUNTIME_ATTACH_AUDIENCE
|
|
85161
|
+
}, privateKey);
|
|
85162
|
+
}
|
|
85163
|
+
var RuntimeHostClient = class _RuntimeHostClient {
|
|
85164
|
+
conn;
|
|
85165
|
+
coordinatorPid;
|
|
85166
|
+
inflight = /* @__PURE__ */ new Map();
|
|
85167
|
+
eventHandlers = /* @__PURE__ */ new Map();
|
|
85168
|
+
closeHandlers = /* @__PURE__ */ new Set();
|
|
85169
|
+
capabilityHandlers = /* @__PURE__ */ new Map();
|
|
85170
|
+
bridgeAborts = /* @__PURE__ */ new Map();
|
|
85171
|
+
requestCounter = 0;
|
|
85172
|
+
closed = false;
|
|
85173
|
+
constructor(conn, coordinatorPid) {
|
|
85174
|
+
this.conn = conn;
|
|
85175
|
+
this.coordinatorPid = coordinatorPid;
|
|
85176
|
+
}
|
|
85177
|
+
/**
|
|
85178
|
+
* Connect + handshake. Resolves to an attached client, or throws
|
|
85179
|
+
* `CoordinatorUnreachableError` (nothing listening — caller should
|
|
85180
|
+
* run the election) / `AttachRefusedError` (a live coordinator said
|
|
85181
|
+
* no — caller must NOT try to bind over it).
|
|
85182
|
+
*/
|
|
85183
|
+
static async attach(opts) {
|
|
85184
|
+
const conn = await opts.platform.connect(opts.socketPath, opts.connectTimeoutMs ?? 2e3);
|
|
85185
|
+
if (conn === null) {
|
|
85186
|
+
throw new CoordinatorUnreachableError(opts.socketPath, new Error("connect failed"));
|
|
85187
|
+
}
|
|
85188
|
+
conn.send(encodeFrame({
|
|
85189
|
+
t: "hello",
|
|
85190
|
+
protocol_version: opts.protocolVersion ?? RUNTIME_HOST_PROTOCOL_VERSION,
|
|
85191
|
+
token: opts.token
|
|
85192
|
+
}));
|
|
85193
|
+
const decoder2 = new JsonLineDecoder();
|
|
85194
|
+
const { first, rest } = await new Promise((resolve14, reject) => {
|
|
85195
|
+
let settled = false;
|
|
85196
|
+
const timer = setTimeout(() => {
|
|
85197
|
+
settle();
|
|
85198
|
+
conn.destroy();
|
|
85199
|
+
reject(new CoordinatorUnreachableError(opts.socketPath, new Error("handshake timeout")));
|
|
85200
|
+
}, opts.handshakeTimeoutMs ?? 3e3);
|
|
85201
|
+
const settle = () => {
|
|
85202
|
+
settled = true;
|
|
85203
|
+
clearTimeout(timer);
|
|
85204
|
+
};
|
|
85205
|
+
conn.onData((data) => {
|
|
85206
|
+
if (settled)
|
|
85207
|
+
return;
|
|
85208
|
+
let frames;
|
|
85209
|
+
try {
|
|
85210
|
+
frames = decoder2.push(data);
|
|
85211
|
+
} catch (err2) {
|
|
85212
|
+
settle();
|
|
85213
|
+
conn.destroy();
|
|
85214
|
+
reject(new Error(`malformed coordinator frame: ${err2 instanceof Error ? err2.message : String(err2)}`, { cause: err2 }));
|
|
85215
|
+
return;
|
|
85216
|
+
}
|
|
85217
|
+
if (frames.length > 0) {
|
|
85218
|
+
settle();
|
|
85219
|
+
resolve14({
|
|
85220
|
+
first: frames[0],
|
|
85221
|
+
rest: frames.slice(1)
|
|
85222
|
+
});
|
|
85223
|
+
}
|
|
85224
|
+
});
|
|
85225
|
+
conn.onClose(() => {
|
|
85226
|
+
if (settled)
|
|
85227
|
+
return;
|
|
85228
|
+
settle();
|
|
85229
|
+
conn.destroy();
|
|
85230
|
+
reject(new CoordinatorUnreachableError(opts.socketPath, new Error("closed during handshake")));
|
|
85231
|
+
});
|
|
85232
|
+
});
|
|
85233
|
+
if (first.t === "refuse") {
|
|
85234
|
+
conn.destroy();
|
|
85235
|
+
throw new AttachRefusedError(first.reason, first.detail);
|
|
85236
|
+
}
|
|
85237
|
+
if (first.t !== "hello_ack") {
|
|
85238
|
+
conn.destroy();
|
|
85239
|
+
throw new Error(`unexpected first coordinator frame: ${first.t}`);
|
|
85240
|
+
}
|
|
85241
|
+
const client = new _RuntimeHostClient(conn, first.coordinator_pid);
|
|
85242
|
+
client.wire(decoder2);
|
|
85243
|
+
for (const frame of rest)
|
|
85244
|
+
client.onMessage(frame);
|
|
85245
|
+
if (opts.capabilities !== void 0)
|
|
85246
|
+
client.setBridgedCapabilities(opts.capabilities);
|
|
85247
|
+
return client;
|
|
85248
|
+
}
|
|
85249
|
+
/**
|
|
85250
|
+
* Proxy a typed capability invocation to the coordinator. Yields the
|
|
85251
|
+
* coordinator's streamed chunks; throws on `invoke_error` or if the
|
|
85252
|
+
* coordinator goes away mid-stream (fail-loud, per doctrine).
|
|
85253
|
+
*/
|
|
85254
|
+
async *invoke(capability, prompt2, options) {
|
|
85255
|
+
yield* this.stream((id) => ({ t: "invoke", id, capability, prompt: prompt2, options }));
|
|
85256
|
+
}
|
|
85257
|
+
/**
|
|
85258
|
+
* Proxy one conversational turn (`sendMessageStreaming`) to the
|
|
85259
|
+
* coordinator. A turn that pauses on `approval_request` ends its
|
|
85260
|
+
* stream; continue it with `resolveApproval`.
|
|
85261
|
+
*/
|
|
85262
|
+
async *chat(text, options) {
|
|
85263
|
+
yield* this.stream((id) => ({ t: "chat", id, text, options }));
|
|
85264
|
+
}
|
|
85265
|
+
/**
|
|
85266
|
+
* Resolve a pending approval on the coordinator
|
|
85267
|
+
* (`resolveApprovalVote`); yields the continuation turn's chunks.
|
|
85268
|
+
*/
|
|
85269
|
+
async *resolveApproval(approved, approverId) {
|
|
85270
|
+
yield* this.stream((id) => ({
|
|
85271
|
+
t: "resolve_approval",
|
|
85272
|
+
id,
|
|
85273
|
+
approved,
|
|
85274
|
+
approver_id: approverId
|
|
85275
|
+
}));
|
|
85276
|
+
}
|
|
85277
|
+
/**
|
|
85278
|
+
* Contribute this frontend's organs. Replaces the previous set, both
|
|
85279
|
+
* locally and on the coordinator (idempotent).
|
|
85280
|
+
*/
|
|
85281
|
+
setBridgedCapabilities(handlers) {
|
|
85282
|
+
if (this.closed)
|
|
85283
|
+
throw new Error("runtime-host client is closed");
|
|
85284
|
+
this.capabilityHandlers = new Map(Object.entries(handlers));
|
|
85285
|
+
this.conn.send(encodeFrame({
|
|
85286
|
+
t: "register_capabilities",
|
|
85287
|
+
capabilities: [...this.capabilityHandlers.keys()]
|
|
85288
|
+
}));
|
|
85289
|
+
}
|
|
85290
|
+
/** Subscribe to a coordinator event channel. Returns the unsubscriber. */
|
|
85291
|
+
subscribe(channel, handler) {
|
|
85292
|
+
let handlers = this.eventHandlers.get(channel);
|
|
85293
|
+
if (handlers === void 0) {
|
|
85294
|
+
handlers = /* @__PURE__ */ new Set();
|
|
85295
|
+
this.eventHandlers.set(channel, handlers);
|
|
85296
|
+
this.conn.send(encodeFrame({ t: "subscribe", channel }));
|
|
85297
|
+
}
|
|
85298
|
+
handlers.add(handler);
|
|
85299
|
+
return () => {
|
|
85300
|
+
const current = this.eventHandlers.get(channel);
|
|
85301
|
+
if (current === void 0)
|
|
85302
|
+
return;
|
|
85303
|
+
current.delete(handler);
|
|
85304
|
+
if (current.size === 0) {
|
|
85305
|
+
this.eventHandlers.delete(channel);
|
|
85306
|
+
if (!this.closed)
|
|
85307
|
+
this.conn.send(encodeFrame({ t: "unsubscribe", channel }));
|
|
85308
|
+
}
|
|
85309
|
+
};
|
|
85310
|
+
}
|
|
85311
|
+
/** Fires once when the coordinator connection ends — the re-elect signal. */
|
|
85312
|
+
onClose(handler) {
|
|
85313
|
+
this.closeHandlers.add(handler);
|
|
85314
|
+
return () => this.closeHandlers.delete(handler);
|
|
85315
|
+
}
|
|
85316
|
+
close() {
|
|
85317
|
+
this.teardown(null);
|
|
85318
|
+
}
|
|
85319
|
+
async *stream(buildFrame) {
|
|
85320
|
+
if (this.closed)
|
|
85321
|
+
throw new Error("runtime-host client is closed");
|
|
85322
|
+
this.requestCounter += 1;
|
|
85323
|
+
const id = `req-${this.requestCounter}-${globalThis.crypto.randomUUID().slice(0, 8)}`;
|
|
85324
|
+
const state = { queue: [], done: false, error: null, wake: null };
|
|
85325
|
+
this.inflight.set(id, state);
|
|
85326
|
+
this.conn.send(encodeFrame(buildFrame(id)));
|
|
85327
|
+
try {
|
|
85328
|
+
for (; ; ) {
|
|
85329
|
+
while (state.queue.length > 0)
|
|
85330
|
+
yield state.queue.shift();
|
|
85331
|
+
if (state.error !== null)
|
|
85332
|
+
throw state.error;
|
|
85333
|
+
if (state.done)
|
|
85334
|
+
return;
|
|
85335
|
+
await new Promise((resolve14) => {
|
|
85336
|
+
state.wake = resolve14;
|
|
85337
|
+
});
|
|
85338
|
+
state.wake = null;
|
|
85339
|
+
}
|
|
85340
|
+
} finally {
|
|
85341
|
+
this.inflight.delete(id);
|
|
85342
|
+
}
|
|
85343
|
+
}
|
|
85344
|
+
wire(decoder2) {
|
|
85345
|
+
this.conn.onData((data) => {
|
|
85346
|
+
let frames;
|
|
85347
|
+
try {
|
|
85348
|
+
frames = decoder2.push(data);
|
|
85349
|
+
} catch (err2) {
|
|
85350
|
+
this.teardown(new Error(`malformed coordinator frame: ${err2 instanceof Error ? err2.message : String(err2)}`, { cause: err2 }));
|
|
85351
|
+
return;
|
|
85352
|
+
}
|
|
85353
|
+
for (const frame of frames)
|
|
85354
|
+
this.onMessage(frame);
|
|
85355
|
+
});
|
|
85356
|
+
this.conn.onClose(() => this.teardown(new Error("coordinator connection closed")));
|
|
85357
|
+
}
|
|
85358
|
+
onMessage(message2) {
|
|
85359
|
+
switch (message2.t) {
|
|
85360
|
+
case "chunk": {
|
|
85361
|
+
const state = this.inflight.get(message2.id);
|
|
85362
|
+
if (state !== void 0) {
|
|
85363
|
+
state.queue.push(message2.chunk);
|
|
85364
|
+
state.wake?.();
|
|
85365
|
+
}
|
|
85366
|
+
return;
|
|
85367
|
+
}
|
|
85368
|
+
case "end": {
|
|
85369
|
+
const state = this.inflight.get(message2.id);
|
|
85370
|
+
if (state !== void 0) {
|
|
85371
|
+
state.done = true;
|
|
85372
|
+
state.wake?.();
|
|
85373
|
+
}
|
|
85374
|
+
return;
|
|
85375
|
+
}
|
|
85376
|
+
case "invoke_error": {
|
|
85377
|
+
const state = this.inflight.get(message2.id);
|
|
85378
|
+
if (state !== void 0) {
|
|
85379
|
+
state.error = new Error(message2.message);
|
|
85380
|
+
state.wake?.();
|
|
85381
|
+
}
|
|
85382
|
+
return;
|
|
85383
|
+
}
|
|
85384
|
+
case "event": {
|
|
85385
|
+
const handlers = this.eventHandlers.get(message2.channel);
|
|
85386
|
+
if (handlers !== void 0) {
|
|
85387
|
+
for (const handler of handlers)
|
|
85388
|
+
handler(message2.payload);
|
|
85389
|
+
}
|
|
85390
|
+
return;
|
|
85391
|
+
}
|
|
85392
|
+
case "bridge_invoke": {
|
|
85393
|
+
if (typeof message2.id === "string" && typeof message2.capability === "string" && typeof message2.prompt === "string") {
|
|
85394
|
+
void this.runBridgedInvocation(message2.id, message2.capability, message2.prompt, message2.options);
|
|
85395
|
+
}
|
|
85396
|
+
return;
|
|
85397
|
+
}
|
|
85398
|
+
default:
|
|
85399
|
+
return;
|
|
85400
|
+
}
|
|
85401
|
+
}
|
|
85402
|
+
/** Answer a coordinator-initiated bridged invocation. */
|
|
85403
|
+
async runBridgedInvocation(id, capability, prompt2, options) {
|
|
85404
|
+
const handler = this.capabilityHandlers.get(capability);
|
|
85405
|
+
if (handler === void 0) {
|
|
85406
|
+
this.conn.send(encodeFrame({
|
|
85407
|
+
t: "bridge_error",
|
|
85408
|
+
id,
|
|
85409
|
+
message: `capability "${capability}" is not contributed by this frontend`
|
|
85410
|
+
}));
|
|
85411
|
+
return;
|
|
85412
|
+
}
|
|
85413
|
+
const controller = new AbortController();
|
|
85414
|
+
this.bridgeAborts.set(id, controller);
|
|
85415
|
+
try {
|
|
85416
|
+
for await (const chunk of handler(prompt2, options, { signal: controller.signal })) {
|
|
85417
|
+
if (controller.signal.aborted || this.closed)
|
|
85418
|
+
return;
|
|
85419
|
+
this.conn.send(encodeFrame({ t: "bridge_chunk", id, chunk }));
|
|
85420
|
+
}
|
|
85421
|
+
if (!this.closed)
|
|
85422
|
+
this.conn.send(encodeFrame({ t: "bridge_end", id }));
|
|
85423
|
+
} catch (err2) {
|
|
85424
|
+
if (!this.closed) {
|
|
85425
|
+
this.conn.send(encodeFrame({
|
|
85426
|
+
t: "bridge_error",
|
|
85427
|
+
id,
|
|
85428
|
+
message: err2 instanceof Error ? err2.message : String(err2)
|
|
85429
|
+
}));
|
|
85430
|
+
}
|
|
85431
|
+
} finally {
|
|
85432
|
+
this.bridgeAborts.delete(id);
|
|
85433
|
+
}
|
|
85434
|
+
}
|
|
85435
|
+
teardown(cause) {
|
|
85436
|
+
if (this.closed)
|
|
85437
|
+
return;
|
|
85438
|
+
this.closed = true;
|
|
85439
|
+
for (const state of this.inflight.values()) {
|
|
85440
|
+
if (!state.done && state.error === null) {
|
|
85441
|
+
state.error = cause ?? new Error("runtime-host client closed");
|
|
85442
|
+
}
|
|
85443
|
+
state.wake?.();
|
|
85444
|
+
}
|
|
85445
|
+
for (const controller of this.bridgeAborts.values())
|
|
85446
|
+
controller.abort();
|
|
85447
|
+
this.bridgeAborts.clear();
|
|
85448
|
+
this.conn.destroy();
|
|
85449
|
+
for (const handler of this.closeHandlers)
|
|
85450
|
+
handler();
|
|
85451
|
+
this.closeHandlers.clear();
|
|
85452
|
+
}
|
|
85453
|
+
};
|
|
85454
|
+
|
|
85455
|
+
// ../../packages/runtime-host/dist/safe-options.js
|
|
85456
|
+
init_esm_shims();
|
|
85457
|
+
function pickSafeChatOptions(options) {
|
|
85458
|
+
if (options === void 0)
|
|
85459
|
+
return void 0;
|
|
85460
|
+
const picked = {};
|
|
85461
|
+
if (typeof options["delegationScope"] === "string") {
|
|
85462
|
+
picked.delegationScope = options["delegationScope"];
|
|
85463
|
+
}
|
|
85464
|
+
if (typeof options["suppressHistory"] === "boolean") {
|
|
85465
|
+
picked.suppressHistory = options["suppressHistory"];
|
|
85466
|
+
}
|
|
85467
|
+
return picked;
|
|
85468
|
+
}
|
|
85469
|
+
function pickSafeInvokeOptions(options) {
|
|
85470
|
+
const picked = {};
|
|
85471
|
+
if (options === void 0)
|
|
85472
|
+
return picked;
|
|
85473
|
+
if (typeof options["targetWorkerId"] === "string") {
|
|
85474
|
+
picked.targetWorkerId = options["targetWorkerId"];
|
|
85475
|
+
}
|
|
85476
|
+
if (typeof options["acknowledgeNoHistoryRisk"] === "boolean") {
|
|
85477
|
+
picked.acknowledgeNoHistoryRisk = options["acknowledgeNoHistoryRisk"];
|
|
85478
|
+
}
|
|
85479
|
+
return picked;
|
|
85480
|
+
}
|
|
85481
|
+
|
|
85482
|
+
// ../../packages/runtime-host/dist/bridged-tools.js
|
|
85483
|
+
init_esm_shims();
|
|
85484
|
+
var AI_LOOP_EXCLUDED_ORGANS = /* @__PURE__ */ new Map([
|
|
85485
|
+
[
|
|
85486
|
+
"se_attestation",
|
|
85487
|
+
"hardware-attestation minting is a user-initiated identity affordance (docs/doctrine/hardware-attestation.md), never a model-chosen act"
|
|
85488
|
+
]
|
|
85489
|
+
]);
|
|
85490
|
+
function isToolResultShaped(value) {
|
|
85491
|
+
return typeof value === "object" && value !== null && typeof value.ok === "boolean";
|
|
85492
|
+
}
|
|
85493
|
+
function bridgedToolRegistry(server, definitions) {
|
|
85494
|
+
for (const capability of Object.keys(definitions)) {
|
|
85495
|
+
const exclusion = AI_LOOP_EXCLUDED_ORGANS.get(capability);
|
|
85496
|
+
if (exclusion !== void 0) {
|
|
85497
|
+
throw new Error(`bridged organ "${capability}" must not surface to the AI loop as a tool: ${exclusion}`);
|
|
85498
|
+
}
|
|
85499
|
+
}
|
|
85500
|
+
const exposed = () => server.bridgedCapabilities.filter((capability) => definitions[capability] !== void 0).map((capability) => ({ capability, definition: definitions[capability] }));
|
|
85501
|
+
return {
|
|
85502
|
+
list() {
|
|
85503
|
+
return exposed().map((e2) => e2.definition);
|
|
85504
|
+
},
|
|
85505
|
+
async execute(name, args) {
|
|
85506
|
+
const entry = exposed().find((e2) => e2.definition.name === name);
|
|
85507
|
+
if (entry === void 0) {
|
|
85508
|
+
return { ok: false, error: `no attached frontend currently contributes tool "${name}"` };
|
|
85509
|
+
}
|
|
85510
|
+
const chunks = [];
|
|
85511
|
+
try {
|
|
85512
|
+
for await (const chunk of server.invokeBridged(entry.capability, "", args)) {
|
|
85513
|
+
chunks.push(chunk);
|
|
85514
|
+
}
|
|
85515
|
+
} catch (err2) {
|
|
85516
|
+
return { ok: false, error: err2 instanceof Error ? err2.message : String(err2) };
|
|
85517
|
+
}
|
|
85518
|
+
const data = chunks.length === 1 ? chunks[0] : chunks;
|
|
85519
|
+
if (isToolResultShaped(data))
|
|
85520
|
+
return data;
|
|
85521
|
+
return { ok: true, data };
|
|
85522
|
+
},
|
|
85523
|
+
register() {
|
|
85524
|
+
throw new Error("bridged tool registry is a read-only view over attached frontends' organs");
|
|
85525
|
+
}
|
|
85526
|
+
};
|
|
85527
|
+
}
|
|
85528
|
+
var BRIDGED_ORGAN_TOOL_SOURCE = "runtime-host:bridged-organs";
|
|
85529
|
+
function wireBridgedOrganTools(server, host, definitions) {
|
|
85530
|
+
const registry = bridgedToolRegistry(server, definitions);
|
|
85531
|
+
const sync = () => {
|
|
85532
|
+
host.unregisterExternalTools(BRIDGED_ORGAN_TOOL_SOURCE);
|
|
85533
|
+
host.registerExternalTools(BRIDGED_ORGAN_TOOL_SOURCE, registry);
|
|
85534
|
+
};
|
|
85535
|
+
const unsubscribe = server.onBridgedCapabilitiesChanged(sync);
|
|
85536
|
+
sync();
|
|
85537
|
+
return unsubscribe;
|
|
85538
|
+
}
|
|
85539
|
+
|
|
85540
|
+
// ../../packages/runtime-host/dist/election.js
|
|
85541
|
+
init_esm_shims();
|
|
85542
|
+
var sleep = (ms) => new Promise((resolve14) => setTimeout(resolve14, ms));
|
|
85543
|
+
async function probeSocketLive(platform, socketPath, timeoutMs) {
|
|
85544
|
+
const conn = await platform.connect(socketPath, timeoutMs);
|
|
85545
|
+
if (conn === null)
|
|
85546
|
+
return false;
|
|
85547
|
+
conn.destroy();
|
|
85548
|
+
return true;
|
|
85549
|
+
}
|
|
85550
|
+
async function acquireTakeoverMutex(platform, mutexDir, pid, probePid) {
|
|
85551
|
+
for (let i3 = 0; i3 < 2; i3 += 1) {
|
|
85552
|
+
if (await platform.mkdirExclusive(mutexDir) === "created") {
|
|
85553
|
+
await platform.writeFile(`${mutexDir}/pid`, String(pid));
|
|
85554
|
+
return true;
|
|
85555
|
+
}
|
|
85556
|
+
const holderRaw = await platform.readFile(`${mutexDir}/pid`);
|
|
85557
|
+
if (holderRaw === null) {
|
|
85558
|
+
return false;
|
|
85559
|
+
}
|
|
85560
|
+
const holderPid = Number(holderRaw.trim());
|
|
85561
|
+
if (Number.isInteger(holderPid) && holderPid > 0 && await probePid(holderPid))
|
|
85562
|
+
return false;
|
|
85563
|
+
await platform.removeDir(mutexDir);
|
|
85564
|
+
}
|
|
85565
|
+
return false;
|
|
85566
|
+
}
|
|
85567
|
+
async function releaseTakeoverMutex(platform, mutexDir) {
|
|
85568
|
+
await platform.removeDir(mutexDir);
|
|
85569
|
+
}
|
|
85570
|
+
async function electRuntimeHost(opts) {
|
|
85571
|
+
const platform = opts.platform;
|
|
85572
|
+
const maxAttempts = opts.maxAttempts ?? 4;
|
|
85573
|
+
const retryDelayMs = opts.retryDelayMs ?? 150;
|
|
85574
|
+
const probePid = opts.probePid ?? ((pid) => platform.isPidAlive(pid));
|
|
85575
|
+
let lastError = null;
|
|
85576
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
85577
|
+
if (await probeSocketLive(platform, opts.socketPath, retryDelayMs * 2)) {
|
|
85578
|
+
let token;
|
|
85579
|
+
try {
|
|
85580
|
+
token = await opts.mintToken();
|
|
85581
|
+
} catch (err2) {
|
|
85582
|
+
throw new Error(`a coordinator is live at ${opts.socketPath} but minting the attach token failed: ${err2 instanceof Error ? err2.message : String(err2)}`, { cause: err2 });
|
|
85583
|
+
}
|
|
85584
|
+
try {
|
|
85585
|
+
const client = await RuntimeHostClient.attach({
|
|
85586
|
+
platform,
|
|
85587
|
+
socketPath: opts.socketPath,
|
|
85588
|
+
token,
|
|
85589
|
+
handshakeTimeoutMs: opts.handshakeTimeoutMs
|
|
85590
|
+
});
|
|
85591
|
+
return { role: "frontend", client };
|
|
85592
|
+
} catch (err2) {
|
|
85593
|
+
if (err2 instanceof AttachRefusedError)
|
|
85594
|
+
throw err2;
|
|
85595
|
+
if (!(err2 instanceof CoordinatorUnreachableError)) {
|
|
85596
|
+
throw new Error(`runtime-host attach failed: ${err2 instanceof Error ? err2.message : String(err2)}`, { cause: err2 });
|
|
85597
|
+
}
|
|
85598
|
+
lastError = err2;
|
|
85599
|
+
}
|
|
85600
|
+
}
|
|
85601
|
+
const lock = await readLockfile(platform, opts.lockfilePath);
|
|
85602
|
+
if (lock !== null && await probePid(lock.pid) && attempt < maxAttempts) {
|
|
85603
|
+
await sleep(retryDelayMs);
|
|
85604
|
+
continue;
|
|
85605
|
+
}
|
|
85606
|
+
const mutexDir = `${opts.lockfilePath}.takeover`;
|
|
85607
|
+
const pid = opts.pid ?? platform.pid;
|
|
85608
|
+
if (!await acquireTakeoverMutex(platform, mutexDir, pid, probePid)) {
|
|
85609
|
+
await sleep(retryDelayMs);
|
|
85610
|
+
continue;
|
|
85611
|
+
}
|
|
85612
|
+
try {
|
|
85613
|
+
if (await probeSocketLive(platform, opts.socketPath, retryDelayMs * 2)) {
|
|
85614
|
+
continue;
|
|
85615
|
+
}
|
|
85616
|
+
if (!isWindowsPipePath(opts.socketPath)) {
|
|
85617
|
+
await platform.removeSocketFile(opts.socketPath);
|
|
85618
|
+
}
|
|
85619
|
+
const server = await RuntimeHostServer.bind(opts);
|
|
85620
|
+
return { role: "coordinator", server };
|
|
85621
|
+
} catch (err2) {
|
|
85622
|
+
if (err2 instanceof CoordinatorAlreadyBoundError) {
|
|
85623
|
+
lastError = err2;
|
|
85624
|
+
await sleep(retryDelayMs);
|
|
85625
|
+
continue;
|
|
85626
|
+
}
|
|
85627
|
+
throw err2;
|
|
85628
|
+
} finally {
|
|
85629
|
+
await releaseTakeoverMutex(platform, mutexDir);
|
|
85630
|
+
}
|
|
85631
|
+
}
|
|
85632
|
+
throw new Error(`runtime-host election did not converge after ${maxAttempts} attempts: ${lastError instanceof Error ? lastError.message : String(lastError)}`, { cause: lastError });
|
|
85633
|
+
}
|
|
85634
|
+
|
|
85635
|
+
// ../../packages/runtime-host/dist/node.js
|
|
85636
|
+
init_esm_shims();
|
|
85637
|
+
|
|
85638
|
+
// ../../packages/runtime-host/dist/node-platform.js
|
|
85639
|
+
init_esm_shims();
|
|
85640
|
+
import { chmodSync, mkdirSync as mkdirSync4, readFileSync as readFileSync4, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
85641
|
+
import { connect, createServer } from "net";
|
|
85642
|
+
import { dirname as dirname4 } from "path";
|
|
85643
|
+
function isWindowsPipe(path19) {
|
|
85644
|
+
return path19.startsWith("\\\\.\\pipe\\");
|
|
85645
|
+
}
|
|
85646
|
+
function wrapSocket(socket) {
|
|
85647
|
+
return {
|
|
85648
|
+
send: (data) => {
|
|
85649
|
+
if (!socket.destroyed)
|
|
85650
|
+
socket.write(data);
|
|
85651
|
+
},
|
|
85652
|
+
onData: (cb) => {
|
|
85653
|
+
socket.on("data", cb);
|
|
85654
|
+
},
|
|
85655
|
+
onClose: (cb) => {
|
|
85656
|
+
socket.on("close", cb);
|
|
85657
|
+
socket.on("error", () => socket.destroy());
|
|
85658
|
+
},
|
|
85659
|
+
end: () => socket.end(),
|
|
85660
|
+
destroy: () => socket.destroy(),
|
|
85661
|
+
get destroyed() {
|
|
85662
|
+
return socket.destroyed;
|
|
85663
|
+
}
|
|
85664
|
+
};
|
|
85665
|
+
}
|
|
85666
|
+
var NodeFrameListener = class {
|
|
85667
|
+
server;
|
|
85668
|
+
constructor(server) {
|
|
85669
|
+
this.server = server;
|
|
85670
|
+
}
|
|
85671
|
+
onConnection(cb) {
|
|
85672
|
+
this.server.on("connection", (socket) => cb(wrapSocket(socket)));
|
|
85673
|
+
}
|
|
85674
|
+
close() {
|
|
85675
|
+
return new Promise((resolve14) => {
|
|
85676
|
+
this.server.close(() => resolve14());
|
|
85677
|
+
});
|
|
85678
|
+
}
|
|
85679
|
+
};
|
|
85680
|
+
function nodePlatform(overrides = {}) {
|
|
85681
|
+
return {
|
|
85682
|
+
pid: overrides.pid ?? process.pid,
|
|
85683
|
+
connect(socketPath, timeoutMs) {
|
|
85684
|
+
return new Promise((resolve14) => {
|
|
85685
|
+
const socket = connect(socketPath);
|
|
85686
|
+
const timer = setTimeout(() => {
|
|
85687
|
+
socket.destroy();
|
|
85688
|
+
resolve14(null);
|
|
85689
|
+
}, timeoutMs);
|
|
85690
|
+
socket.once("connect", () => {
|
|
85691
|
+
clearTimeout(timer);
|
|
85692
|
+
resolve14(wrapSocket(socket));
|
|
85693
|
+
});
|
|
85694
|
+
socket.once("error", () => {
|
|
85695
|
+
clearTimeout(timer);
|
|
85696
|
+
resolve14(null);
|
|
85697
|
+
});
|
|
85698
|
+
});
|
|
85699
|
+
},
|
|
85700
|
+
bind(socketPath) {
|
|
85701
|
+
return new Promise((resolve14, reject) => {
|
|
85702
|
+
const server = createServer();
|
|
85703
|
+
const onError = (err2) => {
|
|
85704
|
+
server.removeListener("listening", onListening);
|
|
85705
|
+
if (err2.code === "EADDRINUSE")
|
|
85706
|
+
resolve14("in_use");
|
|
85707
|
+
else
|
|
85708
|
+
reject(new Error(`runtime-host bind failed: ${err2.message}`, { cause: err2 }));
|
|
85709
|
+
};
|
|
85710
|
+
const onListening = () => {
|
|
85711
|
+
server.removeListener("error", onError);
|
|
85712
|
+
if (!isWindowsPipe(socketPath)) {
|
|
85713
|
+
try {
|
|
85714
|
+
chmodSync(socketPath, 384);
|
|
85715
|
+
} catch (err2) {
|
|
85716
|
+
server.close();
|
|
85717
|
+
reject(new Error(`runtime-host bind failed: could not restrict socket permissions: ${err2 instanceof Error ? err2.message : String(err2)}`, { cause: err2 }));
|
|
85718
|
+
return;
|
|
85719
|
+
}
|
|
85720
|
+
}
|
|
85721
|
+
resolve14(new NodeFrameListener(server));
|
|
85722
|
+
};
|
|
85723
|
+
server.once("error", onError);
|
|
85724
|
+
server.once("listening", onListening);
|
|
85725
|
+
server.listen(socketPath);
|
|
85726
|
+
});
|
|
85727
|
+
},
|
|
85728
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
85729
|
+
async removeSocketFile(socketPath) {
|
|
85730
|
+
if (isWindowsPipe(socketPath))
|
|
85731
|
+
return;
|
|
85732
|
+
try {
|
|
85733
|
+
rmSync2(socketPath, { force: true });
|
|
85734
|
+
} catch {
|
|
85735
|
+
}
|
|
85736
|
+
},
|
|
85737
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
85738
|
+
async readFile(path19) {
|
|
85739
|
+
try {
|
|
85740
|
+
return readFileSync4(path19, "utf8");
|
|
85741
|
+
} catch {
|
|
85742
|
+
return null;
|
|
85743
|
+
}
|
|
85744
|
+
},
|
|
85745
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
85746
|
+
async writeFile(path19, content) {
|
|
85747
|
+
mkdirSync4(dirname4(path19), { recursive: true, mode: 448 });
|
|
85748
|
+
writeFileSync4(path19, content, { mode: 384 });
|
|
85749
|
+
},
|
|
85750
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
85751
|
+
async removeFile(path19) {
|
|
85752
|
+
rmSync2(path19, { force: true });
|
|
85753
|
+
},
|
|
85754
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
85755
|
+
async mkdirExclusive(path19) {
|
|
85756
|
+
mkdirSync4(dirname4(path19), { recursive: true, mode: 448 });
|
|
85757
|
+
try {
|
|
85758
|
+
mkdirSync4(path19);
|
|
85759
|
+
return "created";
|
|
85760
|
+
} catch {
|
|
85761
|
+
return "exists";
|
|
85762
|
+
}
|
|
85763
|
+
},
|
|
85764
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
85765
|
+
async removeDir(path19) {
|
|
85766
|
+
try {
|
|
85767
|
+
rmSync2(path19, { recursive: true, force: true });
|
|
85768
|
+
} catch {
|
|
85769
|
+
}
|
|
85770
|
+
},
|
|
85771
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
85772
|
+
async isPidAlive(pid) {
|
|
85773
|
+
if (!Number.isInteger(pid) || pid <= 0)
|
|
85774
|
+
return false;
|
|
85775
|
+
try {
|
|
85776
|
+
process.kill(pid, 0);
|
|
85777
|
+
return true;
|
|
85778
|
+
} catch (err2) {
|
|
85779
|
+
return err2.code === "EPERM";
|
|
85780
|
+
}
|
|
85781
|
+
}
|
|
85782
|
+
};
|
|
85783
|
+
}
|
|
85784
|
+
|
|
85785
|
+
// ../../packages/runtime-host/dist/paths.js
|
|
85786
|
+
init_esm_shims();
|
|
85787
|
+
import { createHash } from "crypto";
|
|
85788
|
+
import { homedir as homedir3 } from "os";
|
|
85789
|
+
import { join as join7 } from "path";
|
|
85790
|
+
function defaultRuntimeHostPaths(home = homedir3(), platform = process.platform) {
|
|
85791
|
+
const dir = join7(home, ".motebit");
|
|
85792
|
+
if (platform === "win32") {
|
|
85793
|
+
const tag2 = createHash("sha256").update(dir).digest("hex").slice(0, 16);
|
|
85794
|
+
return {
|
|
85795
|
+
socketPath: `\\\\.\\pipe\\motebit-runtime-${tag2}`,
|
|
85796
|
+
lockfilePath: join7(dir, "runtime.lock")
|
|
85797
|
+
};
|
|
85798
|
+
}
|
|
85799
|
+
return {
|
|
85800
|
+
socketPath: join7(dir, "runtime.sock"),
|
|
85801
|
+
lockfilePath: join7(dir, "runtime.lock")
|
|
85802
|
+
};
|
|
85803
|
+
}
|
|
85804
|
+
|
|
85805
|
+
// ../../packages/tools/dist/web-safe.js
|
|
85806
|
+
init_esm_shims();
|
|
85807
|
+
init_web_search();
|
|
85808
|
+
init_read_url();
|
|
85809
|
+
init_recall_memories();
|
|
85810
|
+
init_current_time();
|
|
85811
|
+
init_list_events();
|
|
85812
|
+
init_rewrite_memory();
|
|
85813
|
+
init_search_conversations();
|
|
85814
|
+
init_self_reflect();
|
|
85815
|
+
init_web_search();
|
|
85816
|
+
init_read_url();
|
|
85817
|
+
init_recall_memories();
|
|
85818
|
+
init_current_time();
|
|
85819
|
+
init_recall_self();
|
|
85820
|
+
init_list_events();
|
|
85821
|
+
init_self_reflect();
|
|
85822
|
+
init_search_provider();
|
|
85823
|
+
init_brave_search();
|
|
85824
|
+
init_duckduckgo();
|
|
85825
|
+
init_proxy_search();
|
|
85826
|
+
init_tavily_search();
|
|
85827
|
+
init_biased_search();
|
|
85828
|
+
init_goal_tools();
|
|
85829
|
+
init_computer();
|
|
85830
|
+
init_request_control();
|
|
85831
|
+
init_read_page();
|
|
85832
|
+
init_registry4();
|
|
85833
|
+
|
|
85834
|
+
// src/runtime-host.ts
|
|
85835
|
+
init_identity();
|
|
85836
|
+
function requireRuntime(ref) {
|
|
85837
|
+
if (ref.current === null) {
|
|
85838
|
+
throw new Error("coordinator runtime is still starting \u2014 retry shortly");
|
|
85839
|
+
}
|
|
85840
|
+
return ref.current;
|
|
85841
|
+
}
|
|
85842
|
+
async function electCliRuntimeHost(deps) {
|
|
85843
|
+
const paths = deps.paths ?? defaultRuntimeHostPaths();
|
|
85844
|
+
const deviceId = deps.fullConfig.device_id;
|
|
85845
|
+
const devicePublicKey = deps.fullConfig.device_public_key;
|
|
85846
|
+
return electRuntimeHost({
|
|
85847
|
+
platform: nodePlatform(),
|
|
85848
|
+
socketPath: paths.socketPath,
|
|
85849
|
+
lockfilePath: paths.lockfilePath,
|
|
85850
|
+
motebitId: deps.motebitId,
|
|
85851
|
+
resolveDevicePublicKey: (did) => deviceId != null && did === deviceId && devicePublicKey != null && devicePublicKey !== "" ? fromHex3(devicePublicKey) : null,
|
|
85852
|
+
onInvoke: (capability, prompt2, options, ctx) => requireRuntime(deps.runtimeRef).invokeCapability(capability, prompt2, {
|
|
85853
|
+
...pickSafeInvokeOptions(options),
|
|
85854
|
+
signal: ctx.signal
|
|
85855
|
+
}),
|
|
85856
|
+
// sendMessageStreaming has no abort seam; a frontend disconnect
|
|
85857
|
+
// lets the turn run to completion on the coordinator (its memory
|
|
85858
|
+
// and receipts stay consistent), which is the correct authority
|
|
85859
|
+
// semantics — the renderer left, the act didn't.
|
|
85860
|
+
onChat: (text, options) => requireRuntime(deps.runtimeRef).sendMessageStreaming(
|
|
85861
|
+
text,
|
|
85862
|
+
void 0,
|
|
85863
|
+
pickSafeChatOptions(options)
|
|
85864
|
+
),
|
|
85865
|
+
onResolveApproval: (approved, approverId) => requireRuntime(deps.runtimeRef).resolveApprovalVote(approved, approverId),
|
|
85866
|
+
mintToken: async () => {
|
|
85867
|
+
if (deviceId == null || deviceId === "") {
|
|
85868
|
+
throw new Error(
|
|
85869
|
+
"no device_id in ~/.motebit/config.json \u2014 run `motebit` once to mint an identity"
|
|
85870
|
+
);
|
|
85871
|
+
}
|
|
85872
|
+
return mintAttachToken({ motebitId: deps.motebitId, deviceId }, await deps.loadPrivateKey());
|
|
85873
|
+
}
|
|
85874
|
+
});
|
|
85875
|
+
}
|
|
85876
|
+
function wireBridgedOrgans(server, runtime) {
|
|
85877
|
+
wireBridgedOrganTools(server, runtime, {
|
|
85878
|
+
computer_use: { ...computerDefinition, embodimentMode: "desktop_drive" }
|
|
85879
|
+
});
|
|
85880
|
+
}
|
|
85881
|
+
async function electCoordinatorRole(fullConfig, motebitId, runtimeRef) {
|
|
85882
|
+
let election;
|
|
85883
|
+
try {
|
|
85884
|
+
election = await electCliRuntimeHost({
|
|
85885
|
+
fullConfig,
|
|
85886
|
+
motebitId,
|
|
85887
|
+
loadPrivateKey: async () => (await loadActiveSigningKey(fullConfig, {
|
|
85888
|
+
promptLabel: "Passphrase (to verify the running coordinator): "
|
|
85889
|
+
})).privateKey,
|
|
85890
|
+
runtimeRef
|
|
85891
|
+
});
|
|
85892
|
+
} catch (err2) {
|
|
85893
|
+
console.error(
|
|
85894
|
+
`Runtime-host election failed: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
85895
|
+
);
|
|
85896
|
+
console.error(
|
|
85897
|
+
"A coordinator may already be running with an incompatible build or a locked signing key. Stop it and retry."
|
|
85898
|
+
);
|
|
85899
|
+
process.exit(1);
|
|
85900
|
+
}
|
|
85901
|
+
if (election.role === "frontend") {
|
|
85902
|
+
const pid = election.client.coordinatorPid;
|
|
85903
|
+
election.client.close();
|
|
85904
|
+
console.error(`Another motebit process is already coordinating this machine (pid ${pid}).`);
|
|
85905
|
+
console.error(
|
|
85906
|
+
"One runtime per machine: stop that process first, or run `motebit` (no arguments) to attach a rendering REPL to it."
|
|
85907
|
+
);
|
|
85908
|
+
process.exit(1);
|
|
85909
|
+
}
|
|
85910
|
+
return election.server;
|
|
85911
|
+
}
|
|
85912
|
+
|
|
85913
|
+
// src/attached-repl.ts
|
|
85914
|
+
init_esm_shims();
|
|
85915
|
+
init_colors();
|
|
85916
|
+
async function renderStream(stream) {
|
|
85917
|
+
let pendingApproval = null;
|
|
85918
|
+
try {
|
|
85919
|
+
for await (const raw of stream) {
|
|
85920
|
+
const chunk = raw;
|
|
85921
|
+
switch (chunk.type) {
|
|
85922
|
+
case "text":
|
|
85923
|
+
if (typeof chunk.text === "string") writeOutput(chunk.text);
|
|
85924
|
+
break;
|
|
85925
|
+
case "tool_status":
|
|
85926
|
+
if (chunk.status === "calling") {
|
|
85927
|
+
writeOutput(`
|
|
85928
|
+
${meta(`[${chunk.name ?? "tool"}]`)} `);
|
|
85929
|
+
}
|
|
85930
|
+
break;
|
|
85931
|
+
case "approval_request":
|
|
85932
|
+
pendingApproval = {
|
|
85933
|
+
name: chunk.name ?? "tool",
|
|
85934
|
+
args: chunk.args ?? {}
|
|
85935
|
+
};
|
|
85936
|
+
break;
|
|
85937
|
+
case "delegation_start":
|
|
85938
|
+
writeOutput(`
|
|
85939
|
+
${dim(`[delegating \xB7 ${chunk.tool ?? ""}]`)} `);
|
|
85940
|
+
break;
|
|
85941
|
+
case "delegation_complete":
|
|
85942
|
+
if (chunk.receipt?.task_id != null) {
|
|
85943
|
+
writeOutput(
|
|
85944
|
+
`
|
|
85945
|
+
${dim(`[receipt ${chunk.receipt.task_id.slice(0, 8)} \xB7 ${chunk.receipt.status ?? ""}]`)}
|
|
85946
|
+
`
|
|
85947
|
+
);
|
|
85948
|
+
}
|
|
85949
|
+
break;
|
|
85950
|
+
case "injection_warning":
|
|
85951
|
+
writeOutput(`
|
|
85952
|
+
${warn("\u26A0")} suspicious content in ${chunk.tool_name ?? "tool"} output
|
|
85953
|
+
`);
|
|
85954
|
+
break;
|
|
85955
|
+
case "invoke_error":
|
|
85956
|
+
writeOutput(
|
|
85957
|
+
`
|
|
85958
|
+
${warn(`[invoke failed${chunk.code != null ? ` \xB7 ${chunk.code}` : ""}]`)} ${chunk.message ?? ""}
|
|
85959
|
+
`
|
|
85960
|
+
);
|
|
85961
|
+
break;
|
|
85962
|
+
case "task_step_narration":
|
|
85963
|
+
if (typeof chunk.text === "string") writeOutput(dim(chunk.text));
|
|
85964
|
+
break;
|
|
85965
|
+
default:
|
|
85966
|
+
break;
|
|
85967
|
+
}
|
|
85968
|
+
}
|
|
85969
|
+
} catch (err2) {
|
|
85970
|
+
writeOutput(
|
|
85971
|
+
`
|
|
85972
|
+
${warn("[coordinator error]")} ${err2 instanceof Error ? err2.message : String(err2)}
|
|
85973
|
+
`
|
|
85974
|
+
);
|
|
85975
|
+
}
|
|
85976
|
+
return { pendingApproval };
|
|
85977
|
+
}
|
|
85978
|
+
async function renderTurn(client, motebitId, stream) {
|
|
85979
|
+
let current = stream;
|
|
85980
|
+
for (; ; ) {
|
|
85981
|
+
const { pendingApproval } = await renderStream(current);
|
|
85982
|
+
if (pendingApproval === null) return;
|
|
85983
|
+
const argsPreview = JSON.stringify(pendingApproval.args).slice(0, 120);
|
|
85984
|
+
const answer = await askQuestion(
|
|
85985
|
+
`
|
|
85986
|
+
${warn("?")} ${pendingApproval.name}(${argsPreview})
|
|
85987
|
+
Allow? (y/n) `
|
|
85988
|
+
);
|
|
85989
|
+
const approved = answer.trim().toLowerCase() === "y";
|
|
85990
|
+
current = client.resolveApproval(approved, motebitId);
|
|
85991
|
+
}
|
|
85992
|
+
}
|
|
85993
|
+
var ATTACHED_HELP = `
|
|
85994
|
+
Attached mode \u2014 the coordinator process owns the runtime; this terminal renders.
|
|
85995
|
+
<text> chat with your motebit (proxied to the coordinator)
|
|
85996
|
+
/invoke <cap> <text> invoke a capability deterministically
|
|
85997
|
+
/help this help
|
|
85998
|
+
/exit leave (the coordinator keeps running)
|
|
85999
|
+
Other slash commands need the runtime in-process: exit, stop the coordinator, and re-run \`motebit\`.
|
|
86000
|
+
`;
|
|
86001
|
+
async function runAttachedRepl(client, motebitId) {
|
|
86002
|
+
writeOutput(
|
|
86003
|
+
`
|
|
86004
|
+
${dim("Attached to the machine's coordinator runtime")} ${dim(`(pid ${client.coordinatorPid})`)}
|
|
86005
|
+
${dim("This terminal renders; the coordinator acts. /help for what's available.")}
|
|
86006
|
+
|
|
86007
|
+
`
|
|
86008
|
+
);
|
|
86009
|
+
let closed = false;
|
|
86010
|
+
client.onClose(() => {
|
|
86011
|
+
closed = true;
|
|
86012
|
+
writeOutput(
|
|
86013
|
+
`
|
|
86014
|
+
${warn("Coordinator exited.")} Run \`motebit\` again to take over as coordinator.
|
|
86015
|
+
`
|
|
86016
|
+
);
|
|
86017
|
+
destroyTerminal();
|
|
86018
|
+
process.exit(0);
|
|
86019
|
+
});
|
|
86020
|
+
for (; ; ) {
|
|
86021
|
+
if (closed) return;
|
|
86022
|
+
let line;
|
|
86023
|
+
try {
|
|
86024
|
+
line = await readInput(prompt("you>") + " ");
|
|
86025
|
+
} catch {
|
|
86026
|
+
break;
|
|
86027
|
+
}
|
|
86028
|
+
const trimmed = line.trim();
|
|
86029
|
+
if (trimmed === "") continue;
|
|
86030
|
+
if (trimmed === "/exit" || trimmed === "/quit" || trimmed === "exit" || trimmed === "quit") {
|
|
86031
|
+
writeOutput("Goodbye! (coordinator keeps running)\n");
|
|
86032
|
+
break;
|
|
86033
|
+
}
|
|
86034
|
+
if (trimmed === "/help") {
|
|
86035
|
+
writeOutput(ATTACHED_HELP);
|
|
86036
|
+
continue;
|
|
86037
|
+
}
|
|
86038
|
+
if (trimmed.startsWith("/invoke ")) {
|
|
86039
|
+
const rest = trimmed.slice("/invoke ".length).trim();
|
|
86040
|
+
const space = rest.indexOf(" ");
|
|
86041
|
+
const capability = space === -1 ? rest : rest.slice(0, space);
|
|
86042
|
+
const prompt2 = space === -1 ? "" : rest.slice(space + 1);
|
|
86043
|
+
if (capability === "") {
|
|
86044
|
+
writeOutput(`${warn("usage:")} /invoke <capability> <prompt>
|
|
86045
|
+
`);
|
|
86046
|
+
continue;
|
|
86047
|
+
}
|
|
86048
|
+
writeOutput(prompt("mote>") + " ");
|
|
86049
|
+
await renderTurn(client, motebitId, client.invoke(capability, prompt2));
|
|
86050
|
+
writeOutput("\n");
|
|
86051
|
+
continue;
|
|
86052
|
+
}
|
|
86053
|
+
if (trimmed.startsWith("/")) {
|
|
86054
|
+
writeOutput(
|
|
86055
|
+
`${dim(`${trimmed.split(" ")[0]} needs the runtime in-process \u2014 not available in attached mode.`)}
|
|
86056
|
+
${dim("Stop the coordinator and re-run `motebit` to use it.")}
|
|
86057
|
+
`
|
|
86058
|
+
);
|
|
86059
|
+
continue;
|
|
86060
|
+
}
|
|
86061
|
+
writeOutput(prompt("mote>") + " ");
|
|
86062
|
+
await renderTurn(client, motebitId, client.chat(trimmed));
|
|
86063
|
+
writeOutput("\n");
|
|
86064
|
+
}
|
|
86065
|
+
client.close();
|
|
86066
|
+
destroyTerminal();
|
|
86067
|
+
}
|
|
86068
|
+
|
|
84354
86069
|
// src/index.ts
|
|
84355
86070
|
init_colors();
|
|
84356
86071
|
|
|
@@ -84545,7 +86260,7 @@ init_dist6();
|
|
|
84545
86260
|
|
|
84546
86261
|
// ../../packages/mcp-server/dist/bootstrap-service.js
|
|
84547
86262
|
init_esm_shims();
|
|
84548
|
-
import { writeFileSync as
|
|
86263
|
+
import { writeFileSync as writeFileSync6 } from "fs";
|
|
84549
86264
|
import { resolve as resolve6 } from "path";
|
|
84550
86265
|
|
|
84551
86266
|
// ../../packages/core-identity/dist/node.js
|
|
@@ -84553,12 +86268,12 @@ init_esm_shims();
|
|
|
84553
86268
|
|
|
84554
86269
|
// ../../packages/core-identity/dist/file-stores.js
|
|
84555
86270
|
init_esm_shims();
|
|
84556
|
-
import { existsSync, readFileSync as
|
|
86271
|
+
import { existsSync, readFileSync as readFileSync5, writeFileSync as writeFileSync5, renameSync as renameSync2, chmodSync as chmodSync2 } from "fs";
|
|
84557
86272
|
|
|
84558
86273
|
// ../../packages/core-identity/dist/bootstrap-service.js
|
|
84559
86274
|
init_esm_shims();
|
|
84560
|
-
import { mkdirSync as
|
|
84561
|
-
import { join as
|
|
86275
|
+
import { mkdirSync as mkdirSync5, existsSync as existsSync2 } from "fs";
|
|
86276
|
+
import { join as join8 } from "path";
|
|
84562
86277
|
init_dist9();
|
|
84563
86278
|
init_dist8();
|
|
84564
86279
|
|
|
@@ -85338,7 +87053,7 @@ var McpServerAdapter = class _McpServerAdapter {
|
|
|
85338
87053
|
init_config2();
|
|
85339
87054
|
init_dist31();
|
|
85340
87055
|
init_node_fs();
|
|
85341
|
-
import { join as
|
|
87056
|
+
import { join as join9 } from "path";
|
|
85342
87057
|
|
|
85343
87058
|
// src/utils.ts
|
|
85344
87059
|
init_esm_shims();
|
|
@@ -87352,7 +89067,7 @@ Curiosity targets (${targets.length}):
|
|
|
87352
89067
|
}
|
|
87353
89068
|
case "skills": {
|
|
87354
89069
|
const registry = new SkillRegistry(
|
|
87355
|
-
new NodeFsSkillStorageAdapter({ root:
|
|
89070
|
+
new NodeFsSkillStorageAdapter({ root: join9(CONFIG_DIR, "skills") })
|
|
87356
89071
|
);
|
|
87357
89072
|
const records = await registry.list();
|
|
87358
89073
|
if (records.length === 0) {
|
|
@@ -87383,7 +89098,7 @@ Curiosity targets (${targets.length}):
|
|
|
87383
89098
|
break;
|
|
87384
89099
|
}
|
|
87385
89100
|
const registry = new SkillRegistry(
|
|
87386
|
-
new NodeFsSkillStorageAdapter({ root:
|
|
89101
|
+
new NodeFsSkillStorageAdapter({ root: join9(CONFIG_DIR, "skills") })
|
|
87387
89102
|
);
|
|
87388
89103
|
const record = await registry.get(name);
|
|
87389
89104
|
if (!record) {
|
|
@@ -87899,12 +89614,13 @@ init_config2();
|
|
|
87899
89614
|
init_runtime_factory();
|
|
87900
89615
|
async function handleDelegatePlan(config, motebitId, prompt2) {
|
|
87901
89616
|
const relayUrl = getRelayUrl(config);
|
|
89617
|
+
const runtimeRef = { current: null };
|
|
89618
|
+
const hostServer = await electCoordinatorRole(loadFullConfig(), motebitId, runtimeRef);
|
|
87902
89619
|
const authHeaders = await getRelayAuthHeaders(config, { aud: "task:submit", json: true });
|
|
87903
89620
|
const { createProvider: createProvider2, buildToolRegistry: buildToolRegistry2, buildStorageAdapters: buildStorageAdapters2, deriveGovernanceForRuntime: deriveGovernanceForRuntime2 } = await Promise.resolve().then(() => (init_runtime_factory(), runtime_factory_exports));
|
|
87904
89621
|
const { MotebitRuntime: MotebitRuntime2, NullRenderer: NullRenderer2, PLANNING_TASK_ROUTER: PLANNING_TASK_ROUTER2 } = await Promise.resolve().then(() => (init_dist22(), dist_exports6));
|
|
87905
89622
|
const dbPath = getDbPath(config.dbPath);
|
|
87906
89623
|
const moteDb = await openMotebitDatabase(dbPath);
|
|
87907
|
-
const runtimeRef = { current: null };
|
|
87908
89624
|
const provider = createProvider2(config);
|
|
87909
89625
|
const registry = buildToolRegistry2(config, runtimeRef, motebitId);
|
|
87910
89626
|
const storage = buildStorageAdapters2(moteDb);
|
|
@@ -88123,6 +89839,8 @@ Plan failed: ${chunk.reason}`);
|
|
|
88123
89839
|
} finally {
|
|
88124
89840
|
runtime.stop();
|
|
88125
89841
|
moteDb.close();
|
|
89842
|
+
await hostServer.close().catch(() => {
|
|
89843
|
+
});
|
|
88126
89844
|
}
|
|
88127
89845
|
}
|
|
88128
89846
|
async function handleDelegate(config) {
|
|
@@ -89459,7 +91177,7 @@ async function handleLogs(config) {
|
|
|
89459
91177
|
if (config.tail !== true) return;
|
|
89460
91178
|
let lastShown = initial.length > 0 ? initial[0].ran_at : 0;
|
|
89461
91179
|
while (true) {
|
|
89462
|
-
await
|
|
91180
|
+
await sleep2(POLL_INTERVAL_MS2);
|
|
89463
91181
|
const fresh = moteDb.goalOutcomeStore.listForGoal(goal.goal_id, 50);
|
|
89464
91182
|
const newer = [...fresh].filter((o5) => o5.ran_at > lastShown).reverse();
|
|
89465
91183
|
for (const o5 of newer) {
|
|
@@ -89484,7 +91202,7 @@ function printOutcome(ranAt, status, summary, errMsg) {
|
|
|
89484
91202
|
const body = summary ?? errMsg ?? "";
|
|
89485
91203
|
console.log(`${prefix} ${dim(when.padStart(10))} ${body}`);
|
|
89486
91204
|
}
|
|
89487
|
-
function
|
|
91205
|
+
function sleep2(ms) {
|
|
89488
91206
|
return new Promise((resolve14) => setTimeout(resolve14, ms));
|
|
89489
91207
|
}
|
|
89490
91208
|
|
|
@@ -93621,6 +95339,7 @@ async function reconcileTreasury(config) {
|
|
|
93621
95339
|
|
|
93622
95340
|
// ../../services/relay/dist/treasury-reconciliation.js
|
|
93623
95341
|
init_logger();
|
|
95342
|
+
init_loop_supervisor();
|
|
93624
95343
|
var logger19 = createLogger({ service: "treasury-reconciliation" });
|
|
93625
95344
|
var DEFAULT_INTERVAL_MS = 15 * 6e4;
|
|
93626
95345
|
var DEFAULT_CONFIRMATION_LAG_BUFFER_MS = 5 * 6e4;
|
|
@@ -93714,8 +95433,6 @@ function startTreasuryReconciliationLoop(config) {
|
|
|
93714
95433
|
confirmationLagBufferMs
|
|
93715
95434
|
});
|
|
93716
95435
|
const tick = async () => {
|
|
93717
|
-
if (config.isFrozen?.())
|
|
93718
|
-
return;
|
|
93719
95436
|
try {
|
|
93720
95437
|
await reconcileTreasury({
|
|
93721
95438
|
rpc: config.rpc,
|
|
@@ -93733,17 +95450,19 @@ function startTreasuryReconciliationLoop(config) {
|
|
|
93733
95450
|
chain: config.chain,
|
|
93734
95451
|
error: err2 instanceof Error ? err2.message : String(err2)
|
|
93735
95452
|
});
|
|
95453
|
+
throw err2;
|
|
93736
95454
|
}
|
|
93737
95455
|
};
|
|
93738
|
-
return
|
|
93739
|
-
|
|
93740
|
-
}
|
|
95456
|
+
return superviseInterval(config.supervisor, "treasury-reconciliation", intervalMs, tick, {
|
|
95457
|
+
...config.isFrozen ? { isFrozen: config.isFrozen } : {}
|
|
95458
|
+
});
|
|
93741
95459
|
}
|
|
93742
95460
|
|
|
93743
95461
|
// ../../services/relay/dist/solana-treasury-reconciliation.js
|
|
93744
95462
|
init_esm_shims();
|
|
93745
95463
|
init_dist39();
|
|
93746
95464
|
init_logger();
|
|
95465
|
+
init_loop_supervisor();
|
|
93747
95466
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
93748
95467
|
var logger20 = createLogger({ service: "solana-treasury-reconciliation" });
|
|
93749
95468
|
var DEFAULT_INTERVAL_MS2 = 15 * 6e4;
|
|
@@ -93803,8 +95522,6 @@ function startSolanaTreasuryReconciliationLoop(config) {
|
|
|
93803
95522
|
confirmationLagBufferMs
|
|
93804
95523
|
});
|
|
93805
95524
|
const tick = async () => {
|
|
93806
|
-
if (config.isFrozen?.())
|
|
93807
|
-
return;
|
|
93808
95525
|
try {
|
|
93809
95526
|
await reconciler.reconcile({
|
|
93810
95527
|
store,
|
|
@@ -93818,11 +95535,12 @@ function startSolanaTreasuryReconciliationLoop(config) {
|
|
|
93818
95535
|
chain,
|
|
93819
95536
|
error: err2 instanceof Error ? err2.message : String(err2)
|
|
93820
95537
|
});
|
|
95538
|
+
throw err2;
|
|
93821
95539
|
}
|
|
93822
95540
|
};
|
|
93823
|
-
return
|
|
93824
|
-
|
|
93825
|
-
}
|
|
95541
|
+
return superviseInterval(config.supervisor, "solana-treasury-reconciliation", intervalMs, tick, {
|
|
95542
|
+
...config.isFrozen ? { isFrozen: config.isFrozen } : {}
|
|
95543
|
+
});
|
|
93826
95544
|
}
|
|
93827
95545
|
|
|
93828
95546
|
// ../../services/relay/dist/index.js
|
|
@@ -99716,6 +101434,7 @@ var TaskQueue = class {
|
|
|
99716
101434
|
|
|
99717
101435
|
// ../../services/relay/dist/command-route.js
|
|
99718
101436
|
init_esm_shims();
|
|
101437
|
+
init_dist5();
|
|
99719
101438
|
import { HTTPException as HTTPException20 } from "hono/http-exception";
|
|
99720
101439
|
var RELAY_SIDE_COMMANDS = /* @__PURE__ */ new Set(["balance", "deposits", "discover", "proposals"]);
|
|
99721
101440
|
var RUNTIME_SIDE_COMMANDS = /* @__PURE__ */ new Set([
|
|
@@ -99750,6 +101469,22 @@ function registerCommandRoutes(deps) {
|
|
|
99750
101469
|
}
|
|
99751
101470
|
const command2 = body.command;
|
|
99752
101471
|
const args = typeof body.args === "string" ? body.args : void 0;
|
|
101472
|
+
const registered = db.prepare("SELECT public_key FROM agent_registry WHERE motebit_id = ?").get(motebitId);
|
|
101473
|
+
if (!registered || registered.public_key === "") {
|
|
101474
|
+
throw new HTTPException20(401, {
|
|
101475
|
+
message: "Unknown agent identity \u2014 no registered public key to verify the command against"
|
|
101476
|
+
});
|
|
101477
|
+
}
|
|
101478
|
+
const verdict = await verifyAgentCommandEnvelope({
|
|
101479
|
+
envelope: body.envelope,
|
|
101480
|
+
command: command2,
|
|
101481
|
+
args,
|
|
101482
|
+
motebitId,
|
|
101483
|
+
identityPublicKey: registered.public_key
|
|
101484
|
+
});
|
|
101485
|
+
if (!verdict.ok) {
|
|
101486
|
+
throw new HTTPException20(401, { message: verdict.reason });
|
|
101487
|
+
}
|
|
99753
101488
|
if (command2 in INFO_COMMANDS) {
|
|
99754
101489
|
return c3.json({ summary: INFO_COMMANDS[command2] });
|
|
99755
101490
|
}
|
|
@@ -99768,7 +101503,7 @@ function registerCommandRoutes(deps) {
|
|
|
99768
101503
|
return c3.json({ summary: "Agent not connected." }, 404);
|
|
99769
101504
|
}
|
|
99770
101505
|
try {
|
|
99771
|
-
const result = await forwardCommandToAgent(peers, command2, args);
|
|
101506
|
+
const result = await forwardCommandToAgent(peers, command2, args, body.envelope);
|
|
99772
101507
|
return c3.json(result);
|
|
99773
101508
|
} catch (err2) {
|
|
99774
101509
|
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
@@ -99795,13 +101530,14 @@ function handleCommandResponse(commandId, result) {
|
|
|
99795
101530
|
pendingCommands.delete(commandId);
|
|
99796
101531
|
pending.resolve(result);
|
|
99797
101532
|
}
|
|
99798
|
-
async function forwardCommandToAgent(peers, command2, args) {
|
|
101533
|
+
async function forwardCommandToAgent(peers, command2, args, envelope) {
|
|
99799
101534
|
const commandId = crypto.randomUUID();
|
|
99800
101535
|
const payload = JSON.stringify({
|
|
99801
101536
|
type: "command_request",
|
|
99802
101537
|
id: commandId,
|
|
99803
101538
|
command: command2,
|
|
99804
|
-
args
|
|
101539
|
+
args,
|
|
101540
|
+
envelope
|
|
99805
101541
|
});
|
|
99806
101542
|
return new Promise((resolve14, reject) => {
|
|
99807
101543
|
const timer = setTimeout(() => {
|
|
@@ -100380,7 +102116,7 @@ async function createSyncRelay(config) {
|
|
|
100380
102116
|
const taskQueue = new TaskQueue(moteDb.db);
|
|
100381
102117
|
const relayIdentity = await initRelayIdentity(moteDb.db, config.relayKeyPassphrase, createLogger({ service: "relay" }));
|
|
100382
102118
|
const taskRouter = createTaskRouter({ db: moteDb.db, relayIdentity, federationConfig });
|
|
100383
|
-
const { cache: federationQueryCache, pruneInterval: federationQueryPruneInterval } = createFederationQueryCache();
|
|
102119
|
+
const { cache: federationQueryCache, pruneInterval: federationQueryPruneInterval } = createFederationQueryCache(loopSupervisor);
|
|
100384
102120
|
const app = new Hono();
|
|
100385
102121
|
const { injectWebSocket, upgradeWebSocket } = createNodeWebSocket({ app });
|
|
100386
102122
|
const logger39 = createLogger({ service: "relay" });
|
|
@@ -100424,7 +102160,7 @@ async function createSyncRelay(config) {
|
|
|
100424
102160
|
if (STALE_ALLOCATION_HORIZON_MS < 3 * TASK_TTL_MS) {
|
|
100425
102161
|
throw new Error(`STALE_ALLOCATION_HORIZON_MS (${STALE_ALLOCATION_HORIZON_MS}ms) must be >= 3x TASK_TTL_MS (${TASK_TTL_MS}ms) \u2014 sweeping a live task's allocation opens a refund/settlement double-credit race`);
|
|
100426
102162
|
}
|
|
100427
|
-
const taskCleanupInterval =
|
|
102163
|
+
const taskCleanupInterval = superviseInterval(loopSupervisor, "task-cleanup", 6e4, () => {
|
|
100428
102164
|
const now = Date.now();
|
|
100429
102165
|
taskQueue.cleanup(now);
|
|
100430
102166
|
const evicted = taskQueue.evict(MAX_TASK_QUEUE_SIZE2);
|
|
@@ -100471,7 +102207,7 @@ async function createSyncRelay(config) {
|
|
|
100471
102207
|
}
|
|
100472
102208
|
} catch {
|
|
100473
102209
|
}
|
|
100474
|
-
}
|
|
102210
|
+
});
|
|
100475
102211
|
registerWebSocketRoutes({
|
|
100476
102212
|
app,
|
|
100477
102213
|
upgradeWebSocket,
|
|
@@ -100572,7 +102308,8 @@ async function createSyncRelay(config) {
|
|
|
100572
102308
|
});
|
|
100573
102309
|
const orchestrationWorkerInterval = startDeferredOrchestrationWorker2({
|
|
100574
102310
|
db: moteDb.db,
|
|
100575
|
-
relayIdentity
|
|
102311
|
+
relayIdentity,
|
|
102312
|
+
supervisor: loopSupervisor
|
|
100576
102313
|
});
|
|
100577
102314
|
logger39.info("dispute_orchestration_worker.started");
|
|
100578
102315
|
const { registerTransparencyRoutes: registerTransparencyRoutes2 } = await Promise.resolve().then(() => (init_transparency2(), transparency_exports));
|
|
@@ -100779,8 +102516,8 @@ async function createSyncRelay(config) {
|
|
|
100779
102516
|
isAgentRevoked
|
|
100780
102517
|
});
|
|
100781
102518
|
registerCommandRoutes({ app, db: moteDb.db, connections, logger: logger39 });
|
|
100782
|
-
const heartbeatInterval = startHeartbeatLoop(moteDb.db, relayIdentity, 6e4, () => getEmergencyFreeze());
|
|
100783
|
-
const revocationHorizonInterval = startRevocationHorizonLoop(moteDb.db, { relayIdentity, witnessSolicitationTimeoutMs: federationConfig?.witnessSolicitationTimeoutMs }, federationConfig?.revocationHorizonIntervalMs, () => getEmergencyFreeze());
|
|
102519
|
+
const heartbeatInterval = startHeartbeatLoop(moteDb.db, relayIdentity, 6e4, () => getEmergencyFreeze(), loopSupervisor);
|
|
102520
|
+
const revocationHorizonInterval = startRevocationHorizonLoop(moteDb.db, { relayIdentity, witnessSolicitationTimeoutMs: federationConfig?.witnessSolicitationTimeoutMs }, federationConfig?.revocationHorizonIntervalMs, () => getEmergencyFreeze(), loopSupervisor);
|
|
100784
102521
|
const settlementRetryInterval = startSettlementRetryLoop(
|
|
100785
102522
|
moteDb.db,
|
|
100786
102523
|
relayIdentity,
|
|
@@ -100874,16 +102611,17 @@ async function createSyncRelay(config) {
|
|
|
100874
102611
|
}
|
|
100875
102612
|
})();
|
|
100876
102613
|
}
|
|
100877
|
-
const batchAnchorInterval = startBatchAnchorLoop(moteDb.db, relayIdentity, { submitter: anchorSubmitter }, () => getEmergencyFreeze());
|
|
100878
|
-
const agentAnchorInterval = startAgentSettlementAnchorLoop(moteDb.db, relayIdentity, { submitter: anchorSubmitter }, () => getEmergencyFreeze());
|
|
102614
|
+
const batchAnchorInterval = startBatchAnchorLoop(moteDb.db, relayIdentity, { submitter: anchorSubmitter }, () => getEmergencyFreeze(), loopSupervisor);
|
|
102615
|
+
const agentAnchorInterval = startAgentSettlementAnchorLoop(moteDb.db, relayIdentity, { submitter: anchorSubmitter }, () => getEmergencyFreeze(), loopSupervisor);
|
|
100879
102616
|
credentialAnchorConfig.submitter = anchorSubmitter;
|
|
100880
|
-
const credentialAnchorInterval = startCredentialAnchorLoop(moteDb.db, relayIdentity, credentialAnchorConfig, () => getEmergencyFreeze());
|
|
102617
|
+
const credentialAnchorInterval = startCredentialAnchorLoop(moteDb.db, relayIdentity, credentialAnchorConfig, () => getEmergencyFreeze(), loopSupervisor);
|
|
100881
102618
|
const { startIdentityLogAnchorLoop: startIdentityLogAnchorLoop2 } = await Promise.resolve().then(() => (init_identity_log_anchoring(), identity_log_anchoring_exports));
|
|
100882
|
-
const identityLogAnchorInterval = startIdentityLogAnchorLoop2(moteDb.db, relayIdentity, { submitter: anchorSubmitter }, () => getEmergencyFreeze());
|
|
102619
|
+
const identityLogAnchorInterval = startIdentityLogAnchorLoop2(moteDb.db, relayIdentity, { submitter: anchorSubmitter }, () => getEmergencyFreeze(), loopSupervisor);
|
|
100883
102620
|
const depositDetectorChain = x402Config.network;
|
|
100884
102621
|
const depositDetectorInterval = startDepositDetector({
|
|
100885
102622
|
db: moteDb.db,
|
|
100886
|
-
chain: depositDetectorChain
|
|
102623
|
+
chain: depositDetectorChain,
|
|
102624
|
+
supervisor: loopSupervisor
|
|
100887
102625
|
});
|
|
100888
102626
|
let treasuryReconciliationInterval;
|
|
100889
102627
|
{
|
|
@@ -100901,7 +102639,8 @@ async function createSyncRelay(config) {
|
|
|
100901
102639
|
treasuryAddress: x402Config.payToAddress,
|
|
100902
102640
|
usdcContractAddress,
|
|
100903
102641
|
intervalMs,
|
|
100904
|
-
isFrozen: () => getEmergencyFreeze()
|
|
102642
|
+
isFrozen: () => getEmergencyFreeze(),
|
|
102643
|
+
supervisor: loopSupervisor
|
|
100905
102644
|
});
|
|
100906
102645
|
} else {
|
|
100907
102646
|
logger39.info("treasury-reconciliation.disabled", {
|
|
@@ -100933,7 +102672,8 @@ async function createSyncRelay(config) {
|
|
|
100933
102672
|
// negative-drift on every cycle.
|
|
100934
102673
|
...solanaUsdcMint ? { usdcMint: solanaUsdcMint } : {},
|
|
100935
102674
|
intervalMs: solanaReconciliationIntervalMs,
|
|
100936
|
-
isFrozen: () => getEmergencyFreeze()
|
|
102675
|
+
isFrozen: () => getEmergencyFreeze(),
|
|
102676
|
+
supervisor: loopSupervisor
|
|
100937
102677
|
});
|
|
100938
102678
|
} else {
|
|
100939
102679
|
logger39.info("solana-treasury-reconciliation.disabled", {
|
|
@@ -101855,13 +103595,13 @@ init_colors();
|
|
|
101855
103595
|
import {
|
|
101856
103596
|
appendFileSync,
|
|
101857
103597
|
existsSync as existsSync8,
|
|
101858
|
-
mkdirSync as
|
|
101859
|
-
readFileSync as
|
|
103598
|
+
mkdirSync as mkdirSync10,
|
|
103599
|
+
readFileSync as readFileSync10,
|
|
101860
103600
|
readdirSync as readdirSync3,
|
|
101861
103601
|
statSync as statSync4,
|
|
101862
|
-
writeFileSync as
|
|
103602
|
+
writeFileSync as writeFileSync14
|
|
101863
103603
|
} from "fs";
|
|
101864
|
-
import { isAbsolute as isAbsolute3, join as
|
|
103604
|
+
import { isAbsolute as isAbsolute3, join as join17, resolve as resolve10 } from "path";
|
|
101865
103605
|
import * as readline5 from "readline";
|
|
101866
103606
|
var DEFAULT_RELAY_URL = "https://relay.motebit.com";
|
|
101867
103607
|
var REGISTRY_ADDRESS_RE = /^did:key:z[1-9A-HJ-NP-Za-km-z]+\/[a-z0-9-]+@[^/]+$/;
|
|
@@ -101881,15 +103621,15 @@ function resolveRelayUrl() {
|
|
|
101881
103621
|
var SKILLS_DIR_NAME = "skills";
|
|
101882
103622
|
var AUDIT_LOG_NAME = "audit.log";
|
|
101883
103623
|
function getSkillsRoot() {
|
|
101884
|
-
return
|
|
103624
|
+
return join17(CONFIG_DIR, SKILLS_DIR_NAME);
|
|
101885
103625
|
}
|
|
101886
103626
|
function getAuditLogPath() {
|
|
101887
|
-
return
|
|
103627
|
+
return join17(getSkillsRoot(), AUDIT_LOG_NAME);
|
|
101888
103628
|
}
|
|
101889
103629
|
function makeAuditSink() {
|
|
101890
103630
|
return (event) => {
|
|
101891
103631
|
const root = getSkillsRoot();
|
|
101892
|
-
if (!existsSync8(root))
|
|
103632
|
+
if (!existsSync8(root)) mkdirSync10(root, { recursive: true });
|
|
101893
103633
|
appendFileSync(getAuditLogPath(), JSON.stringify(event) + "\n", "utf-8");
|
|
101894
103634
|
};
|
|
101895
103635
|
}
|
|
@@ -102227,7 +103967,7 @@ function parseAuditFilters(config) {
|
|
|
102227
103967
|
function readAuditEvents() {
|
|
102228
103968
|
const path19 = getAuditLogPath();
|
|
102229
103969
|
if (!existsSync8(path19)) return [];
|
|
102230
|
-
const text =
|
|
103970
|
+
const text = readFileSync10(path19, "utf-8");
|
|
102231
103971
|
const events = [];
|
|
102232
103972
|
for (const line of text.split("\n")) {
|
|
102233
103973
|
const trimmed = line.trim();
|
|
@@ -102386,14 +104126,14 @@ async function handleSkillsRunScript(config) {
|
|
|
102386
104126
|
}
|
|
102387
104127
|
moteDb.close();
|
|
102388
104128
|
if (!approved) process.exit(1);
|
|
102389
|
-
const { mkdtempSync, writeFileSync: writeBytes, chmodSync:
|
|
104129
|
+
const { mkdtempSync, writeFileSync: writeBytes, chmodSync: chmodSync3, rmSync: rmSync3 } = await import("fs");
|
|
102390
104130
|
const { tmpdir: tmpdir2 } = await import("os");
|
|
102391
104131
|
const { spawnSync } = await import("child_process");
|
|
102392
|
-
const tempDir = mkdtempSync(
|
|
102393
|
-
const tempPath =
|
|
104132
|
+
const tempDir = mkdtempSync(join17(tmpdir2(), "motebit-skill-script-"));
|
|
104133
|
+
const tempPath = join17(tempDir, scriptName);
|
|
102394
104134
|
try {
|
|
102395
104135
|
writeBytes(tempPath, Buffer.from(scriptBytes));
|
|
102396
|
-
|
|
104136
|
+
chmodSync3(tempPath, 448);
|
|
102397
104137
|
const interpreter = detectInterpreter(scriptBytes, scriptName);
|
|
102398
104138
|
const spawnArgs = interpreter.useShebang ? scriptArgs : [tempPath, ...scriptArgs];
|
|
102399
104139
|
const command2 = interpreter.useShebang ? tempPath : interpreter.command;
|
|
@@ -102408,7 +104148,7 @@ async function handleSkillsRunScript(config) {
|
|
|
102408
104148
|
process.exit(result.status ?? 0);
|
|
102409
104149
|
} finally {
|
|
102410
104150
|
try {
|
|
102411
|
-
|
|
104151
|
+
rmSync3(tempDir, { recursive: true, force: true });
|
|
102412
104152
|
} catch {
|
|
102413
104153
|
}
|
|
102414
104154
|
}
|
|
@@ -102460,17 +104200,17 @@ function collectAuxFiles2(skillDir) {
|
|
|
102460
104200
|
function walk2(current, base) {
|
|
102461
104201
|
const entries = readdirSync3(current, { withFileTypes: true });
|
|
102462
104202
|
for (const entry of entries) {
|
|
102463
|
-
const full =
|
|
104203
|
+
const full = join17(current, entry.name);
|
|
102464
104204
|
if (entry.isDirectory()) {
|
|
102465
104205
|
walk2(full, base);
|
|
102466
104206
|
} else if (entry.isFile()) {
|
|
102467
104207
|
const rel = full.slice(base.length + 1).split(/[\\/]+/).join("/");
|
|
102468
|
-
out[rel] =
|
|
104208
|
+
out[rel] = readFileSync10(full);
|
|
102469
104209
|
}
|
|
102470
104210
|
}
|
|
102471
104211
|
}
|
|
102472
104212
|
for (const subdir of AUX_DIRS2) {
|
|
102473
|
-
const subdirPath =
|
|
104213
|
+
const subdirPath = join17(skillDir, subdir);
|
|
102474
104214
|
if (existsSync8(subdirPath)) walk2(subdirPath, skillDir);
|
|
102475
104215
|
}
|
|
102476
104216
|
return out;
|
|
@@ -102518,12 +104258,12 @@ async function loadIdentityKey() {
|
|
|
102518
104258
|
};
|
|
102519
104259
|
}
|
|
102520
104260
|
async function signSkillDirectory(skillDir, privateKey, publicKey) {
|
|
102521
|
-
const skillMdPath =
|
|
104261
|
+
const skillMdPath = join17(skillDir, SKILL_MD_NAME);
|
|
102522
104262
|
if (!existsSync8(skillMdPath)) {
|
|
102523
104263
|
console.error(error(` No SKILL.md at ${skillMdPath}`));
|
|
102524
104264
|
process.exit(1);
|
|
102525
104265
|
}
|
|
102526
|
-
const text =
|
|
104266
|
+
const text = readFileSync10(skillMdPath, "utf-8");
|
|
102527
104267
|
const parsed = parseSkillFile(text);
|
|
102528
104268
|
const body = parsed.body;
|
|
102529
104269
|
const motebitNoSig = { ...parsed.manifest.motebit };
|
|
@@ -102560,9 +104300,9 @@ async function signSkillDirectory(skillDir, privateKey, publicKey) {
|
|
|
102560
104300
|
publicKey
|
|
102561
104301
|
);
|
|
102562
104302
|
const skillMdContent = serializeSkillFile(signedManifest, body);
|
|
102563
|
-
|
|
102564
|
-
|
|
102565
|
-
|
|
104303
|
+
writeFileSync14(skillMdPath, skillMdContent);
|
|
104304
|
+
writeFileSync14(
|
|
104305
|
+
join17(skillDir, SKILL_ENVELOPE_JSON_NAME),
|
|
102566
104306
|
JSON.stringify(signedEnvelope, null, 2) + "\n"
|
|
102567
104307
|
);
|
|
102568
104308
|
return { envelope: signedEnvelope, manifest: signedManifest, body };
|
|
@@ -102982,7 +104722,7 @@ init_esm_shims();
|
|
|
102982
104722
|
init_dist6();
|
|
102983
104723
|
init_dist30();
|
|
102984
104724
|
init_colors();
|
|
102985
|
-
import { readFileSync as
|
|
104725
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
102986
104726
|
import { resolve as resolve12 } from "path";
|
|
102987
104727
|
var KIND_KEYWORDS = /* @__PURE__ */ new Set(["receipt", "token", "listing"]);
|
|
102988
104728
|
function isVerifyKind(s3) {
|
|
@@ -102993,7 +104733,7 @@ async function verifyWire(kind, filePath, now = Date.now()) {
|
|
|
102993
104733
|
const absPath = resolve12(filePath);
|
|
102994
104734
|
let raw;
|
|
102995
104735
|
try {
|
|
102996
|
-
const text =
|
|
104736
|
+
const text = readFileSync12(absPath, "utf-8");
|
|
102997
104737
|
raw = JSON.parse(text);
|
|
102998
104738
|
checks.push({ name: "json", ok: true, detail: `parsed ${text.length} bytes` });
|
|
102999
104739
|
} catch (err2) {
|
|
@@ -103198,7 +104938,7 @@ init_esm_shims();
|
|
|
103198
104938
|
init_dist2();
|
|
103199
104939
|
init_dist32();
|
|
103200
104940
|
init_dist3();
|
|
103201
|
-
import { createHash as
|
|
104941
|
+
import { createHash as createHash3 } from "crypto";
|
|
103202
104942
|
var MAX_TOOL_CALLS_PER_RUN = 50;
|
|
103203
104943
|
var GoalScheduler = class _GoalScheduler {
|
|
103204
104944
|
constructor(runtime, goalStore, approvalStore, goalOutcomeStore, motebitId, denyAbove, defaultTtlMs = 36e5, goalWallClockMs = 10 * 60 * 1e3) {
|
|
@@ -104000,7 +105740,7 @@ var GoalScheduler = class _GoalScheduler {
|
|
|
104000
105740
|
}
|
|
104001
105741
|
};
|
|
104002
105742
|
function hashArgs(argsJson) {
|
|
104003
|
-
return
|
|
105743
|
+
return createHash3("sha256").update(argsJson).digest("hex");
|
|
104004
105744
|
}
|
|
104005
105745
|
function formatTimeAgo2(ms) {
|
|
104006
105746
|
if (ms < 6e4) return "just now";
|
|
@@ -104106,6 +105846,7 @@ async function handleRun(config) {
|
|
|
104106
105846
|
const motebitId = identity.motebit_id;
|
|
104107
105847
|
const runtimeRef = { current: null };
|
|
104108
105848
|
const toolRegistry = buildToolRegistry(config, runtimeRef, motebitId);
|
|
105849
|
+
const runtimeHostServer = await electCoordinatorRole(fullConfig, motebitId, runtimeRef);
|
|
104109
105850
|
const mcpServers = (fullConfig.mcp_servers ?? []).map((s3) => ({
|
|
104110
105851
|
...s3,
|
|
104111
105852
|
trusted: (fullConfig.mcp_trusted_servers ?? []).includes(s3.name)
|
|
@@ -104137,6 +105878,7 @@ async function handleRun(config) {
|
|
|
104137
105878
|
}
|
|
104138
105879
|
);
|
|
104139
105880
|
runtimeRef.current = runtime;
|
|
105881
|
+
wireBridgedOrgans(runtimeHostServer, runtime);
|
|
104140
105882
|
await runtime.init();
|
|
104141
105883
|
runtime.setLocalCapabilities([
|
|
104142
105884
|
DeviceCapability.StdioMcp,
|
|
@@ -104328,6 +106070,23 @@ Collaborative step ${stepId}... (proposal ${proposalId}...) ${stepStatus} by ${c
|
|
|
104328
106070
|
const cmdMsg = msg;
|
|
104329
106071
|
void (async () => {
|
|
104330
106072
|
try {
|
|
106073
|
+
const verdict = await verifyAgentCommandEnvelope({
|
|
106074
|
+
envelope: cmdMsg.envelope,
|
|
106075
|
+
command: cmdMsg.command,
|
|
106076
|
+
args: cmdMsg.args,
|
|
106077
|
+
motebitId,
|
|
106078
|
+
identityPublicKey: identity.identity.public_key
|
|
106079
|
+
});
|
|
106080
|
+
if (!verdict.ok) {
|
|
106081
|
+
wsAdapter.sendRaw(
|
|
106082
|
+
JSON.stringify({
|
|
106083
|
+
type: "command_response",
|
|
106084
|
+
id: cmdMsg.id,
|
|
106085
|
+
result: { summary: verdict.reason }
|
|
106086
|
+
})
|
|
106087
|
+
);
|
|
106088
|
+
return;
|
|
106089
|
+
}
|
|
104331
106090
|
const result = await executeCommand(runtime, cmdMsg.command, cmdMsg.args);
|
|
104332
106091
|
wsAdapter.sendRaw(
|
|
104333
106092
|
JSON.stringify({ type: "command_response", id: cmdMsg.id, result })
|
|
@@ -104521,6 +106280,8 @@ Agent task received: ${task.task_id.slice(0, 8)}... prompt: "${task.prompt.slice
|
|
|
104521
106280
|
}
|
|
104522
106281
|
scheduler.stop();
|
|
104523
106282
|
wsAdapter?.disconnect();
|
|
106283
|
+
void runtimeHostServer.close().catch(() => {
|
|
106284
|
+
});
|
|
104524
106285
|
runtime.stop();
|
|
104525
106286
|
moteDb.close();
|
|
104526
106287
|
if (privKeyBytes) secureErase(privKeyBytes);
|
|
@@ -104613,6 +106374,7 @@ async function handleServe(config) {
|
|
|
104613
106374
|
}
|
|
104614
106375
|
const runtimeRef = { current: null };
|
|
104615
106376
|
const toolRegistry = buildToolRegistry(config, runtimeRef, motebitId);
|
|
106377
|
+
const runtimeHostServer = await electCoordinatorRole(loadFullConfig(), motebitId, runtimeRef);
|
|
104616
106378
|
if (config.tools) {
|
|
104617
106379
|
const { pathToFileURL } = await import("url");
|
|
104618
106380
|
const resolved = path18.resolve(config.tools);
|
|
@@ -104663,6 +106425,7 @@ async function handleServe(config) {
|
|
|
104663
106425
|
}
|
|
104664
106426
|
);
|
|
104665
106427
|
runtimeRef.current = runtime;
|
|
106428
|
+
wireBridgedOrgans(runtimeHostServer, runtime);
|
|
104666
106429
|
await runtime.init();
|
|
104667
106430
|
const deps = {
|
|
104668
106431
|
motebitId,
|
|
@@ -104922,6 +106685,26 @@ async function handleServe(config) {
|
|
|
104922
106685
|
const cmdMsg = msg;
|
|
104923
106686
|
void (async () => {
|
|
104924
106687
|
try {
|
|
106688
|
+
const verdict = publicKeyHex == null || publicKeyHex === "" ? {
|
|
106689
|
+
ok: false,
|
|
106690
|
+
reason: "command_request rejected: no registered identity public key to verify against"
|
|
106691
|
+
} : await verifyAgentCommandEnvelope({
|
|
106692
|
+
envelope: cmdMsg.envelope,
|
|
106693
|
+
command: cmdMsg.command,
|
|
106694
|
+
args: cmdMsg.args,
|
|
106695
|
+
motebitId,
|
|
106696
|
+
identityPublicKey: publicKeyHex
|
|
106697
|
+
});
|
|
106698
|
+
if (!verdict.ok) {
|
|
106699
|
+
serveWsAdapter.sendRaw(
|
|
106700
|
+
JSON.stringify({
|
|
106701
|
+
type: "command_response",
|
|
106702
|
+
id: cmdMsg.id,
|
|
106703
|
+
result: { summary: verdict.reason }
|
|
106704
|
+
})
|
|
106705
|
+
);
|
|
106706
|
+
return;
|
|
106707
|
+
}
|
|
104925
106708
|
const result = await executeCommand(runtimeRef.current, cmdMsg.command, cmdMsg.args);
|
|
104926
106709
|
serveWsAdapter.sendRaw(
|
|
104927
106710
|
JSON.stringify({ type: "command_response", id: cmdMsg.id, result })
|
|
@@ -105103,6 +106886,8 @@ async function handleServe(config) {
|
|
|
105103
106886
|
}
|
|
105104
106887
|
serveWsAdapter?.disconnect();
|
|
105105
106888
|
await mcpServer.stop();
|
|
106889
|
+
await runtimeHostServer.close().catch(() => {
|
|
106890
|
+
});
|
|
105106
106891
|
runtime.stop();
|
|
105107
106892
|
moteDb.close();
|
|
105108
106893
|
} catch (err2) {
|
|
@@ -105120,7 +106905,7 @@ init_esm_shims();
|
|
|
105120
106905
|
import { spawn } from "child_process";
|
|
105121
106906
|
import { mkdtemp, writeFile as writeFile3, unlink } from "fs/promises";
|
|
105122
106907
|
import { tmpdir } from "os";
|
|
105123
|
-
import { join as
|
|
106908
|
+
import { join as join19 } from "path";
|
|
105124
106909
|
|
|
105125
106910
|
// ../../packages/voice/dist/index.js
|
|
105126
106911
|
init_esm_shims();
|
|
@@ -105396,8 +107181,8 @@ var SystemTTSProvider = class {
|
|
|
105396
107181
|
}
|
|
105397
107182
|
};
|
|
105398
107183
|
async function writeTempMp3(buf) {
|
|
105399
|
-
const dir = await mkdtemp(
|
|
105400
|
-
const path19 =
|
|
107184
|
+
const dir = await mkdtemp(join19(tmpdir(), "motebit-tts-"));
|
|
107185
|
+
const path19 = join19(dir, "out.mp3");
|
|
105401
107186
|
await writeFile3(path19, buf);
|
|
105402
107187
|
return path19;
|
|
105403
107188
|
}
|
|
@@ -105881,6 +107666,31 @@ async function main() {
|
|
|
105881
107666
|
current: null
|
|
105882
107667
|
};
|
|
105883
107668
|
const toolRegistry = buildToolRegistry(config, runtimeRef, motebitId);
|
|
107669
|
+
let election;
|
|
107670
|
+
try {
|
|
107671
|
+
election = await electCliRuntimeHost({
|
|
107672
|
+
fullConfig: reloadedConfig,
|
|
107673
|
+
motebitId,
|
|
107674
|
+
loadPrivateKey: () => privateKeyBytes !== void 0 ? Promise.resolve(privateKeyBytes) : Promise.reject(
|
|
107675
|
+
new Error("signing key unavailable \u2014 cannot attach to the running coordinator")
|
|
107676
|
+
),
|
|
107677
|
+
runtimeRef
|
|
107678
|
+
});
|
|
107679
|
+
} catch (err2) {
|
|
107680
|
+
console.error(
|
|
107681
|
+
`Runtime-host election failed: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
107682
|
+
);
|
|
107683
|
+
console.error(
|
|
107684
|
+
"Another motebit process may be coordinating with an incompatible build. Stop it and retry."
|
|
107685
|
+
);
|
|
107686
|
+
destroyTerminal();
|
|
107687
|
+
process.exit(1);
|
|
107688
|
+
}
|
|
107689
|
+
if (election.role === "frontend") {
|
|
107690
|
+
await runAttachedRepl(election.client, motebitId);
|
|
107691
|
+
return;
|
|
107692
|
+
}
|
|
107693
|
+
const runtimeHostServer = election.server;
|
|
105884
107694
|
const trustedServers = fullConfig.mcp_trusted_servers ?? [];
|
|
105885
107695
|
const mcpServers = (fullConfig.mcp_servers ?? []).map((s3) => ({
|
|
105886
107696
|
...s3,
|
|
@@ -106022,6 +107832,8 @@ async function main() {
|
|
|
106022
107832
|
}
|
|
106023
107833
|
const shutdown = async () => {
|
|
106024
107834
|
destroyTerminal();
|
|
107835
|
+
await runtimeHostServer.close().catch(() => {
|
|
107836
|
+
});
|
|
106025
107837
|
runtime.stop();
|
|
106026
107838
|
await Promise.allSettled(mcpAdapters.map((a3) => a3.disconnect()));
|
|
106027
107839
|
try {
|