@moxt-ai/mobius 0.0.29 → 0.0.31
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/bots/bot-approve-command.d.ts +2 -0
- package/dist/bots/bot-approve-command.d.ts.map +1 -0
- package/dist/bots/bot-approve-command.js +78 -0
- package/dist/bots/bot-approve-command.js.map +1 -0
- package/dist/bots/local-bot-grant-store.d.ts +7 -0
- package/dist/bots/local-bot-grant-store.d.ts.map +1 -1
- package/dist/bots/local-bot-grant-store.js +89 -0
- package/dist/bots/local-bot-grant-store.js.map +1 -1
- package/dist/cli.js +951 -731
- package/dist/cli.js.map +4 -4
- package/dist/index.js +115 -12
- package/dist/index.js.map +3 -3
- package/dist/run-daemon.d.ts.map +1 -1
- package/dist/run-daemon.js +18 -14
- package/dist/run-daemon.js.map +1 -1
- package/dist/runtime.js +115 -12
- package/dist/runtime.js.map +3 -3
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -11,7 +11,7 @@ import { mkdir as mkdir5 } from "node:fs/promises";
|
|
|
11
11
|
// package.json
|
|
12
12
|
var package_default = {
|
|
13
13
|
name: "@moxt-ai/mobius",
|
|
14
|
-
version: "0.0.
|
|
14
|
+
version: "0.0.31",
|
|
15
15
|
description: "Mobius CLI for pairing and running local AI agent services.",
|
|
16
16
|
type: "module",
|
|
17
17
|
bin: {
|
|
@@ -2743,166 +2743,9 @@ async function discoverLocalAgentInventory(options) {
|
|
|
2743
2743
|
return new LocalAgentInventory(discoveries);
|
|
2744
2744
|
}
|
|
2745
2745
|
|
|
2746
|
-
// src/bots/bot-
|
|
2747
|
-
import {
|
|
2748
|
-
import {
|
|
2749
|
-
var BOT_IPC_RESPONSE_LIMIT = 64 * 1024;
|
|
2750
|
-
var BOT_IPC_TIMEOUT_MILLISECONDS = 2e4;
|
|
2751
|
-
var BotMessageCommandError = class extends Error {
|
|
2752
|
-
code;
|
|
2753
|
-
constructor(code, message, options = {}) {
|
|
2754
|
-
super(message, options);
|
|
2755
|
-
this.code = code;
|
|
2756
|
-
this.name = "BotMessageCommandError";
|
|
2757
|
-
}
|
|
2758
|
-
};
|
|
2759
|
-
function isRecord4(value) {
|
|
2760
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2761
|
-
}
|
|
2762
|
-
function requiredEnvironment(environment, name) {
|
|
2763
|
-
const value = environment[name]?.trim();
|
|
2764
|
-
if (value === void 0 || value.length === 0) {
|
|
2765
|
-
throw new BotMessageCommandError(
|
|
2766
|
-
"execution_context_required",
|
|
2767
|
-
"This command is available only inside an active Mobius Bot execution"
|
|
2768
|
-
);
|
|
2769
|
-
}
|
|
2770
|
-
return value;
|
|
2771
|
-
}
|
|
2772
|
-
function option(arguments_, name) {
|
|
2773
|
-
const index = arguments_.indexOf(name);
|
|
2774
|
-
if (index < 0 || index + 1 >= arguments_.length || arguments_[index + 1]?.startsWith("--") === true) {
|
|
2775
|
-
throw new BotMessageCommandError(
|
|
2776
|
-
"invalid_request",
|
|
2777
|
-
"Usage: mobius bot message send --target <current|alias> --text <message>"
|
|
2778
|
-
);
|
|
2779
|
-
}
|
|
2780
|
-
const value = arguments_[index + 1];
|
|
2781
|
-
if (value === void 0) {
|
|
2782
|
-
throw new BotMessageCommandError("invalid_request", "The Bot message option is missing");
|
|
2783
|
-
}
|
|
2784
|
-
return value;
|
|
2785
|
-
}
|
|
2786
|
-
function validateArguments(arguments_) {
|
|
2787
|
-
if (arguments_.length !== 4) {
|
|
2788
|
-
throw new BotMessageCommandError(
|
|
2789
|
-
"invalid_request",
|
|
2790
|
-
"Usage: mobius bot message send --target <current|alias> --text <message>"
|
|
2791
|
-
);
|
|
2792
|
-
}
|
|
2793
|
-
const names = [arguments_[0], arguments_[2]];
|
|
2794
|
-
if (!names.includes("--target") || !names.includes("--text") || names[0] === names[1]) {
|
|
2795
|
-
throw new BotMessageCommandError(
|
|
2796
|
-
"invalid_request",
|
|
2797
|
-
"Usage: mobius bot message send --target <current|alias> --text <message>"
|
|
2798
|
-
);
|
|
2799
|
-
}
|
|
2800
|
-
}
|
|
2801
|
-
function decodeResponse(encoded) {
|
|
2802
|
-
let value;
|
|
2803
|
-
try {
|
|
2804
|
-
value = JSON.parse(encoded);
|
|
2805
|
-
} catch (cause) {
|
|
2806
|
-
throw new BotMessageCommandError("bot_service_unavailable", "The local Bot service returned invalid data", {
|
|
2807
|
-
cause
|
|
2808
|
-
});
|
|
2809
|
-
}
|
|
2810
|
-
if (!isRecord4(value) || typeof value["ok"] !== "boolean") {
|
|
2811
|
-
throw new BotMessageCommandError("bot_service_unavailable", "The local Bot service returned invalid data");
|
|
2812
|
-
}
|
|
2813
|
-
if (value["ok"] === true && typeof value["outboundMessageId"] === "string") {
|
|
2814
|
-
return value["outboundMessageId"];
|
|
2815
|
-
}
|
|
2816
|
-
const error61 = value["error"];
|
|
2817
|
-
if (value["ok"] === false && isRecord4(error61) && typeof error61["code"] === "string" && typeof error61["message"] === "string") {
|
|
2818
|
-
throw new BotMessageCommandError(error61["code"], error61["message"]);
|
|
2819
|
-
}
|
|
2820
|
-
throw new BotMessageCommandError("bot_service_unavailable", "The local Bot service returned invalid data");
|
|
2821
|
-
}
|
|
2822
|
-
async function requestLocalBotService(socketPath, payload, signal) {
|
|
2823
|
-
signal.throwIfAborted();
|
|
2824
|
-
return await new Promise((resolve5, reject) => {
|
|
2825
|
-
const socket = createConnection(socketPath);
|
|
2826
|
-
const chunks = [];
|
|
2827
|
-
let length = 0;
|
|
2828
|
-
let finished = false;
|
|
2829
|
-
const finish = () => {
|
|
2830
|
-
socket.removeAllListeners();
|
|
2831
|
-
socket.destroy();
|
|
2832
|
-
};
|
|
2833
|
-
const fail = (error61) => {
|
|
2834
|
-
if (finished) {
|
|
2835
|
-
return;
|
|
2836
|
-
}
|
|
2837
|
-
finished = true;
|
|
2838
|
-
finish();
|
|
2839
|
-
if (error61 instanceof BotMessageCommandError) {
|
|
2840
|
-
reject(error61);
|
|
2841
|
-
return;
|
|
2842
|
-
}
|
|
2843
|
-
reject(
|
|
2844
|
-
new BotMessageCommandError(
|
|
2845
|
-
"execution_context_required",
|
|
2846
|
-
"The active Mobius Bot execution is unavailable",
|
|
2847
|
-
{ cause: error61 }
|
|
2848
|
-
)
|
|
2849
|
-
);
|
|
2850
|
-
};
|
|
2851
|
-
const abort = () => fail(signal.reason);
|
|
2852
|
-
socket.setTimeout(BOT_IPC_TIMEOUT_MILLISECONDS, () => {
|
|
2853
|
-
fail(new BotMessageCommandError("bot_service_unavailable", "The local Bot service timed out"));
|
|
2854
|
-
});
|
|
2855
|
-
socket.once("error", fail);
|
|
2856
|
-
socket.once("connect", () => socket.end(`${payload}
|
|
2857
|
-
`));
|
|
2858
|
-
socket.on("data", (chunk) => {
|
|
2859
|
-
length += chunk.byteLength;
|
|
2860
|
-
if (length > BOT_IPC_RESPONSE_LIMIT) {
|
|
2861
|
-
fail(new BotMessageCommandError("bot_service_unavailable", "The local Bot response is too large"));
|
|
2862
|
-
return;
|
|
2863
|
-
}
|
|
2864
|
-
chunks.push(chunk);
|
|
2865
|
-
});
|
|
2866
|
-
socket.once("end", () => {
|
|
2867
|
-
if (finished) {
|
|
2868
|
-
return;
|
|
2869
|
-
}
|
|
2870
|
-
finished = true;
|
|
2871
|
-
signal.removeEventListener("abort", abort);
|
|
2872
|
-
try {
|
|
2873
|
-
resolve5(decodeResponse(Buffer.concat(chunks).toString("utf8")));
|
|
2874
|
-
} catch (error61) {
|
|
2875
|
-
reject(error61);
|
|
2876
|
-
} finally {
|
|
2877
|
-
finish();
|
|
2878
|
-
}
|
|
2879
|
-
});
|
|
2880
|
-
signal.addEventListener("abort", abort, { once: true });
|
|
2881
|
-
});
|
|
2882
|
-
}
|
|
2883
|
-
async function executeBotMessageCommand(arguments_, environment, signal) {
|
|
2884
|
-
validateArguments(arguments_);
|
|
2885
|
-
const contextHandle = requiredEnvironment(environment, "MOBIUS_BOT_CONTEXT");
|
|
2886
|
-
const socketPath = requiredEnvironment(environment, "MOBIUS_BOT_SOCKET");
|
|
2887
|
-
const target = option(arguments_, "--target");
|
|
2888
|
-
const text = option(arguments_, "--text");
|
|
2889
|
-
if (!/^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$/.test(target) || text.trim().length === 0 || text.length > 1e4) {
|
|
2890
|
-
throw new BotMessageCommandError("invalid_request", "The Bot outbound message is invalid");
|
|
2891
|
-
}
|
|
2892
|
-
return await requestLocalBotService(
|
|
2893
|
-
socketPath,
|
|
2894
|
-
JSON.stringify({
|
|
2895
|
-
contextHandle,
|
|
2896
|
-
idempotencyKey: `cli_${randomUUID()}`,
|
|
2897
|
-
target,
|
|
2898
|
-
text
|
|
2899
|
-
}),
|
|
2900
|
-
signal
|
|
2901
|
-
);
|
|
2902
|
-
}
|
|
2903
|
-
|
|
2904
|
-
// src/config.ts
|
|
2905
|
-
import { join as join14 } from "node:path";
|
|
2746
|
+
// src/bots/bot-approve-command.ts
|
|
2747
|
+
import { readdir as readdir2, realpath as realpath8 } from "node:fs/promises";
|
|
2748
|
+
import { dirname as dirname4, join as join17 } from "node:path";
|
|
2906
2749
|
|
|
2907
2750
|
// ../../node_modules/.pnpm/zod@4.5.4/node_modules/zod/v4/classic/external.js
|
|
2908
2751
|
var external_exports = {};
|
|
@@ -21770,6 +21613,174 @@ function date4(params) {
|
|
|
21770
21613
|
return _coercedDate(ZodDate, params);
|
|
21771
21614
|
}
|
|
21772
21615
|
|
|
21616
|
+
// src/authorization/workspace-directory.ts
|
|
21617
|
+
import { readdir, realpath as realpath6, stat as stat5 } from "node:fs/promises";
|
|
21618
|
+
import { homedir as homedir2 } from "node:os";
|
|
21619
|
+
import { basename, dirname as dirname3, isAbsolute as isAbsolute8, join as join14, normalize as normalize2, relative as relative4 } from "node:path";
|
|
21620
|
+
|
|
21621
|
+
// src/authorization/local-authorization-error.ts
|
|
21622
|
+
var LocalAuthorizationError = class extends Error {
|
|
21623
|
+
code = "local_authorization_failed";
|
|
21624
|
+
constructor(message, options = {}) {
|
|
21625
|
+
super(message, options);
|
|
21626
|
+
this.name = "LocalAuthorizationError";
|
|
21627
|
+
}
|
|
21628
|
+
};
|
|
21629
|
+
|
|
21630
|
+
// src/authorization/workspace-directory.ts
|
|
21631
|
+
var WorkspaceDirectoryListing = class {
|
|
21632
|
+
directoryId;
|
|
21633
|
+
directoryLabel;
|
|
21634
|
+
entries;
|
|
21635
|
+
parentDirectoryId;
|
|
21636
|
+
constructor(directoryId2, directoryLabel, entries, parentDirectoryId) {
|
|
21637
|
+
this.directoryId = directoryId2;
|
|
21638
|
+
this.directoryLabel = directoryLabel;
|
|
21639
|
+
this.entries = entries;
|
|
21640
|
+
this.parentDirectoryId = parentDirectoryId;
|
|
21641
|
+
}
|
|
21642
|
+
};
|
|
21643
|
+
function encodePath(path2) {
|
|
21644
|
+
const encoded = Buffer.from(path2, "utf8").toString("base64url");
|
|
21645
|
+
if (encoded.length === 0 || encoded.length > 2e3) {
|
|
21646
|
+
throw new LocalAuthorizationError("The local directory path is too long to select");
|
|
21647
|
+
}
|
|
21648
|
+
return `path-${encoded}`;
|
|
21649
|
+
}
|
|
21650
|
+
function decodePath(value, prefix) {
|
|
21651
|
+
const encoded = value.slice(prefix.length);
|
|
21652
|
+
if (encoded.length === 0 || encoded.length > 2e3 || !/^[a-zA-Z0-9_-]+$/.test(encoded)) {
|
|
21653
|
+
throw new LocalAuthorizationError("The directory identifier is invalid");
|
|
21654
|
+
}
|
|
21655
|
+
const decoded = Buffer.from(encoded, "base64url").toString("utf8");
|
|
21656
|
+
if (Buffer.from(decoded, "utf8").toString("base64url") !== encoded || decoded.includes("\0") || normalize2(decoded) !== decoded) {
|
|
21657
|
+
throw new LocalAuthorizationError("The directory identifier is invalid");
|
|
21658
|
+
}
|
|
21659
|
+
return decoded;
|
|
21660
|
+
}
|
|
21661
|
+
var WorkspaceDirectoryService = class {
|
|
21662
|
+
#bindingId;
|
|
21663
|
+
#defaultWorkspace;
|
|
21664
|
+
constructor(bindingId, defaultWorkspace) {
|
|
21665
|
+
this.#bindingId = bindingId;
|
|
21666
|
+
this.#defaultWorkspace = defaultWorkspace;
|
|
21667
|
+
}
|
|
21668
|
+
authorizeMachine = (bindingId) => {
|
|
21669
|
+
if (bindingId !== this.#bindingId) {
|
|
21670
|
+
throw new LocalAuthorizationError("The runtime binding is not authorized");
|
|
21671
|
+
}
|
|
21672
|
+
};
|
|
21673
|
+
requestedPath = (id) => {
|
|
21674
|
+
if (id === "default") {
|
|
21675
|
+
return this.#defaultWorkspace;
|
|
21676
|
+
}
|
|
21677
|
+
if (id === "home") {
|
|
21678
|
+
return homedir2();
|
|
21679
|
+
}
|
|
21680
|
+
if (id.startsWith("path-")) {
|
|
21681
|
+
const path2 = decodePath(id, "path-");
|
|
21682
|
+
if (isAbsolute8(path2)) {
|
|
21683
|
+
return path2;
|
|
21684
|
+
}
|
|
21685
|
+
}
|
|
21686
|
+
throw new LocalAuthorizationError("The directory identifier is invalid");
|
|
21687
|
+
};
|
|
21688
|
+
resolvePath = async (requested, signal) => {
|
|
21689
|
+
signal.throwIfAborted();
|
|
21690
|
+
if (!isAbsolute8(requested) || requested.includes("\0") || normalize2(requested) !== requested) {
|
|
21691
|
+
throw new LocalAuthorizationError("The local directory path is invalid");
|
|
21692
|
+
}
|
|
21693
|
+
try {
|
|
21694
|
+
const target = await realpath6(requested);
|
|
21695
|
+
signal.throwIfAborted();
|
|
21696
|
+
const metadata = await stat5(target);
|
|
21697
|
+
signal.throwIfAborted();
|
|
21698
|
+
if (!isAbsolute8(target) || normalize2(target) !== target || !metadata.isDirectory()) {
|
|
21699
|
+
throw new LocalAuthorizationError("The selected location is not a directory");
|
|
21700
|
+
}
|
|
21701
|
+
return target;
|
|
21702
|
+
} catch (cause) {
|
|
21703
|
+
signal.throwIfAborted();
|
|
21704
|
+
if (cause instanceof LocalAuthorizationError) {
|
|
21705
|
+
throw cause;
|
|
21706
|
+
}
|
|
21707
|
+
throw new LocalAuthorizationError("This directory is missing or unavailable to your user account", {
|
|
21708
|
+
cause
|
|
21709
|
+
});
|
|
21710
|
+
}
|
|
21711
|
+
};
|
|
21712
|
+
label = (path2) => {
|
|
21713
|
+
const suffix = relative4(homedir2(), path2);
|
|
21714
|
+
return suffix === "" ? "~" : !isAbsolute8(suffix) && suffix !== ".." && !suffix.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) ? `~/${suffix.replaceAll("\\", "/")}` : path2;
|
|
21715
|
+
};
|
|
21716
|
+
identify = async (bindingId, requestedPath, signal) => {
|
|
21717
|
+
this.authorizeMachine(bindingId);
|
|
21718
|
+
const path2 = await this.resolvePath(requestedPath, signal);
|
|
21719
|
+
return {
|
|
21720
|
+
path: path2,
|
|
21721
|
+
directoryId: encodePath(path2),
|
|
21722
|
+
name: basename(path2) || path2,
|
|
21723
|
+
label: this.label(path2)
|
|
21724
|
+
};
|
|
21725
|
+
};
|
|
21726
|
+
resolve = async (bindingId, id, signal) => {
|
|
21727
|
+
this.authorizeMachine(bindingId);
|
|
21728
|
+
return await this.resolvePath(this.requestedPath(id), signal);
|
|
21729
|
+
};
|
|
21730
|
+
list = async (bindingId, id, signal) => {
|
|
21731
|
+
const target = await this.resolve(bindingId, id, signal);
|
|
21732
|
+
try {
|
|
21733
|
+
const children = await readdir(target, { withFileTypes: true });
|
|
21734
|
+
signal.throwIfAborted();
|
|
21735
|
+
const entries = [];
|
|
21736
|
+
for (const child of children) {
|
|
21737
|
+
if (entries.length === 500) {
|
|
21738
|
+
break;
|
|
21739
|
+
}
|
|
21740
|
+
if (child.isFile() || child.isBlockDevice() || child.isCharacterDevice() || child.isFIFO() || child.isSocket()) {
|
|
21741
|
+
continue;
|
|
21742
|
+
}
|
|
21743
|
+
try {
|
|
21744
|
+
const path2 = await this.resolvePath(join14(target, child.name), signal);
|
|
21745
|
+
entries.push({ directoryId: encodePath(path2), name: child.name });
|
|
21746
|
+
} catch (cause) {
|
|
21747
|
+
signal.throwIfAborted();
|
|
21748
|
+
if (!(cause instanceof LocalAuthorizationError)) {
|
|
21749
|
+
throw cause;
|
|
21750
|
+
}
|
|
21751
|
+
}
|
|
21752
|
+
}
|
|
21753
|
+
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
21754
|
+
const parent = dirname3(target);
|
|
21755
|
+
return new WorkspaceDirectoryListing(
|
|
21756
|
+
id,
|
|
21757
|
+
this.label(target),
|
|
21758
|
+
entries,
|
|
21759
|
+
parent === target ? "" : encodePath(parent)
|
|
21760
|
+
);
|
|
21761
|
+
} catch (cause) {
|
|
21762
|
+
signal.throwIfAborted();
|
|
21763
|
+
if (cause instanceof LocalAuthorizationError) {
|
|
21764
|
+
throw cause;
|
|
21765
|
+
}
|
|
21766
|
+
throw new LocalAuthorizationError("This directory cannot be browsed by your user account", { cause });
|
|
21767
|
+
}
|
|
21768
|
+
};
|
|
21769
|
+
resolveMany = async (bindingId, ids, signal) => {
|
|
21770
|
+
const paths = /* @__PURE__ */ new Set();
|
|
21771
|
+
for (const id of ids) {
|
|
21772
|
+
paths.add(await this.resolve(bindingId, id, signal));
|
|
21773
|
+
}
|
|
21774
|
+
return [...paths];
|
|
21775
|
+
};
|
|
21776
|
+
};
|
|
21777
|
+
|
|
21778
|
+
// src/pairing/pair-local-machine.ts
|
|
21779
|
+
import { randomUUID } from "node:crypto";
|
|
21780
|
+
import { mkdir as mkdir3, open as open4, realpath as realpath7, rename as rename3, stat as stat6, writeFile as writeFile2 } from "node:fs/promises";
|
|
21781
|
+
import { hostname as hostname3 } from "node:os";
|
|
21782
|
+
import { basename as basename2, join as join16, resolve as resolve3 } from "node:path";
|
|
21783
|
+
|
|
21773
21784
|
// ../protocol/src/prompt-usage.ts
|
|
21774
21785
|
var TOKEN_COUNT = external_exports.number().int().nonnegative();
|
|
21775
21786
|
function normalizeQuotaTokenCount(value) {
|
|
@@ -22284,7 +22295,7 @@ var ProtocolMessageError = class extends Error {
|
|
|
22284
22295
|
this.name = "ProtocolMessageError";
|
|
22285
22296
|
}
|
|
22286
22297
|
};
|
|
22287
|
-
function
|
|
22298
|
+
function isRecord4(value) {
|
|
22288
22299
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22289
22300
|
}
|
|
22290
22301
|
function parseJson2(encoded) {
|
|
@@ -22296,7 +22307,7 @@ function parseJson2(encoded) {
|
|
|
22296
22307
|
cause: error61
|
|
22297
22308
|
});
|
|
22298
22309
|
}
|
|
22299
|
-
if (!
|
|
22310
|
+
if (!isRecord4(value)) {
|
|
22300
22311
|
throw new ProtocolMessageError("Message must be a JSON object");
|
|
22301
22312
|
}
|
|
22302
22313
|
return value;
|
|
@@ -22393,7 +22404,7 @@ function readActivityContent(value) {
|
|
|
22393
22404
|
}
|
|
22394
22405
|
function readActivityDetail(value) {
|
|
22395
22406
|
const field = value["detail"];
|
|
22396
|
-
if (!
|
|
22407
|
+
if (!isRecord4(field)) {
|
|
22397
22408
|
throw new ProtocolMessageError("Invalid activity detail field");
|
|
22398
22409
|
}
|
|
22399
22410
|
const kind = readString(field, "kind", 32);
|
|
@@ -22857,7 +22868,7 @@ var BotSessionPromptCommand = class {
|
|
|
22857
22868
|
this.replyRoute = replyRoute;
|
|
22858
22869
|
}
|
|
22859
22870
|
};
|
|
22860
|
-
function
|
|
22871
|
+
function isRecord5(value) {
|
|
22861
22872
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22862
22873
|
}
|
|
22863
22874
|
function parseJson3(encoded) {
|
|
@@ -22867,7 +22878,7 @@ function parseJson3(encoded) {
|
|
|
22867
22878
|
} catch (cause) {
|
|
22868
22879
|
throw new ProtocolMessageError("Message is not valid JSON", { cause });
|
|
22869
22880
|
}
|
|
22870
|
-
if (!
|
|
22881
|
+
if (!isRecord5(value)) {
|
|
22871
22882
|
throw new ProtocolMessageError("Message must be a JSON object");
|
|
22872
22883
|
}
|
|
22873
22884
|
return value;
|
|
@@ -22952,7 +22963,7 @@ function readTargets(value, key) {
|
|
|
22952
22963
|
const identifiers = /* @__PURE__ */ new Set();
|
|
22953
22964
|
const aliases = /* @__PURE__ */ new Set();
|
|
22954
22965
|
for (const candidate of field) {
|
|
22955
|
-
if (!
|
|
22966
|
+
if (!isRecord5(candidate)) {
|
|
22956
22967
|
throw new ProtocolMessageError(`Invalid Bot delivery targets: ${key}`);
|
|
22957
22968
|
}
|
|
22958
22969
|
assertExactKeys2(candidate, ["alias", "deliveryTargetId"]);
|
|
@@ -22968,7 +22979,7 @@ function readTargets(value, key) {
|
|
|
22968
22979
|
return Object.freeze(targets);
|
|
22969
22980
|
}
|
|
22970
22981
|
function readCompletionPolicy(value) {
|
|
22971
|
-
if (!
|
|
22982
|
+
if (!isRecord5(value)) {
|
|
22972
22983
|
throw new ProtocolMessageError("Invalid Bot completion policy");
|
|
22973
22984
|
}
|
|
22974
22985
|
const kind = readText2(value, "kind", 32, false);
|
|
@@ -22986,7 +22997,7 @@ function readCompletionPolicy(value) {
|
|
|
22986
22997
|
throw new ProtocolMessageError("Invalid Bot completion policy");
|
|
22987
22998
|
}
|
|
22988
22999
|
function readReplyRoute(value) {
|
|
22989
|
-
if (!
|
|
23000
|
+
if (!isRecord5(value)) {
|
|
22990
23001
|
throw new ProtocolMessageError("Invalid Bot reply route");
|
|
22991
23002
|
}
|
|
22992
23003
|
const kind = readText2(value, "kind", 32, false);
|
|
@@ -23437,10 +23448,14 @@ var encodeSessionEventEnvelope = (message) => encode4(message, eventEnvelopeSche
|
|
|
23437
23448
|
var decodeSessionEventAcknowledgement = (encoded) => decode5(encoded, eventAcknowledgementSchema);
|
|
23438
23449
|
var decodeSessionEventRejection = (encoded) => decode5(encoded, eventRejectionSchema);
|
|
23439
23450
|
|
|
23451
|
+
// src/pairing/pair-local-machine.ts
|
|
23452
|
+
import { fetch as fetch2 } from "undici";
|
|
23453
|
+
|
|
23440
23454
|
// src/config.ts
|
|
23455
|
+
import { join as join15 } from "node:path";
|
|
23441
23456
|
function loadDefaultWorkspace(environment = process.env) {
|
|
23442
23457
|
const { MOBIUS_DEFAULT_WORKSPACE: configuredWorkspace } = environment;
|
|
23443
|
-
return configuredWorkspace ??
|
|
23458
|
+
return configuredWorkspace ?? join15(readStateDirectory(environment), "workspace");
|
|
23444
23459
|
}
|
|
23445
23460
|
function createDaemonConfig(connection, agents = [], installableAgents = []) {
|
|
23446
23461
|
return {
|
|
@@ -23456,11 +23471,6 @@ function createDaemonConfig(connection, agents = [], installableAgents = []) {
|
|
|
23456
23471
|
}
|
|
23457
23472
|
|
|
23458
23473
|
// src/pairing/pair-local-machine.ts
|
|
23459
|
-
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
23460
|
-
import { mkdir as mkdir3, open as open4, realpath as realpath6, rename as rename3, stat as stat5, writeFile as writeFile2 } from "node:fs/promises";
|
|
23461
|
-
import { hostname as hostname3 } from "node:os";
|
|
23462
|
-
import { basename, join as join15, resolve as resolve3 } from "node:path";
|
|
23463
|
-
import { fetch as fetch2 } from "undici";
|
|
23464
23474
|
var PAIRING_TIMEOUT_MILLISECONDS = 2e4;
|
|
23465
23475
|
var PAIRING_CODE_PATTERN = /^[A-HJ-NP-Z2-9]{4}(?:-[A-HJ-NP-Z2-9]{4}){4}$/;
|
|
23466
23476
|
var IDENTIFIER_PATTERN5 = /^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$/;
|
|
@@ -23478,7 +23488,7 @@ var PairingClientError = class extends Error {
|
|
|
23478
23488
|
this.name = "PairingClientError";
|
|
23479
23489
|
}
|
|
23480
23490
|
};
|
|
23481
|
-
function
|
|
23491
|
+
function isRecord6(value) {
|
|
23482
23492
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
23483
23493
|
}
|
|
23484
23494
|
function hasExactKeys(value, expectedKeys) {
|
|
@@ -23571,8 +23581,8 @@ async function resolveDefaultWorkspace(path2) {
|
|
|
23571
23581
|
const requestedWorkspace = resolve3(path2);
|
|
23572
23582
|
try {
|
|
23573
23583
|
await mkdir3(requestedWorkspace, { recursive: true, mode: 448 });
|
|
23574
|
-
const defaultWorkspace = await
|
|
23575
|
-
const metadata = await
|
|
23584
|
+
const defaultWorkspace = await realpath7(requestedWorkspace);
|
|
23585
|
+
const metadata = await stat6(defaultWorkspace);
|
|
23576
23586
|
if (!metadata.isDirectory()) {
|
|
23577
23587
|
throw new PairingClientError("invalid_workspace", "The default workspace is not a directory");
|
|
23578
23588
|
}
|
|
@@ -23601,9 +23611,9 @@ async function readJsonResponse(response) {
|
|
|
23601
23611
|
}
|
|
23602
23612
|
}
|
|
23603
23613
|
function readRelayError(value) {
|
|
23604
|
-
if (
|
|
23614
|
+
if (isRecord6(value)) {
|
|
23605
23615
|
const error61 = value["error"];
|
|
23606
|
-
if (
|
|
23616
|
+
if (isRecord6(error61)) {
|
|
23607
23617
|
const code = error61["code"];
|
|
23608
23618
|
const message = error61["message"];
|
|
23609
23619
|
if (typeof code === "string" && typeof message === "string") {
|
|
@@ -23614,7 +23624,7 @@ function readRelayError(value) {
|
|
|
23614
23624
|
return new PairingClientError("pairing_failed", "The relay could not pair this machine");
|
|
23615
23625
|
}
|
|
23616
23626
|
function decodePairingResponse(value, credentialField, expectedKeys) {
|
|
23617
|
-
if (!
|
|
23627
|
+
if (!isRecord6(value) || !hasExactKeys(value, expectedKeys)) {
|
|
23618
23628
|
throw new PairingClientError("invalid_relay_response", "The relay returned an invalid pairing response");
|
|
23619
23629
|
}
|
|
23620
23630
|
const channelId = value["channelId"];
|
|
@@ -23642,7 +23652,7 @@ async function claimPairing(options, defaultWorkspace, localAgents, installableA
|
|
|
23642
23652
|
installableAgents,
|
|
23643
23653
|
machineName: hostname3(),
|
|
23644
23654
|
pairingCode: options.code,
|
|
23645
|
-
workspaceName:
|
|
23655
|
+
workspaceName: basename2(defaultWorkspace) || "Local workspace"
|
|
23646
23656
|
}),
|
|
23647
23657
|
dispatcher: environmentProxyDispatcher,
|
|
23648
23658
|
headers: { "content-type": "application/json" },
|
|
@@ -23659,10 +23669,10 @@ async function claimPairing(options, defaultWorkspace, localAgents, installableA
|
|
|
23659
23669
|
return options.pairingId.length > 0 ? decodePairingResponse(value, "runtimeCredential", REDEEM_RESPONSE_KEYS) : decodePairingResponse(value, "daemonToken", CLAIM_RESPONSE_KEYS);
|
|
23660
23670
|
}
|
|
23661
23671
|
async function persistConnection(channelId, relayOrigin, runtimeCredential2, defaultWorkspace, environment) {
|
|
23662
|
-
const connectionsDirectory =
|
|
23672
|
+
const connectionsDirectory = join16(readStateDirectory(environment), "connections");
|
|
23663
23673
|
await mkdir3(connectionsDirectory, { mode: 448, recursive: true });
|
|
23664
|
-
const connectionPath =
|
|
23665
|
-
const temporaryPath =
|
|
23674
|
+
const connectionPath = join16(connectionsDirectory, `${channelId}.json`);
|
|
23675
|
+
const temporaryPath = join16(connectionsDirectory, `.${channelId}.${randomUUID()}.tmp`);
|
|
23666
23676
|
await writeFile2(
|
|
23667
23677
|
temporaryPath,
|
|
23668
23678
|
`${JSON.stringify(
|
|
@@ -23682,7 +23692,7 @@ async function persistConnection(channelId, relayOrigin, runtimeCredential2, def
|
|
|
23682
23692
|
return connectionPath;
|
|
23683
23693
|
}
|
|
23684
23694
|
function decodeSavedConnection(value, expectedChannelId) {
|
|
23685
|
-
if (!
|
|
23695
|
+
if (!isRecord6(value) || !hasExactKeys(value, SAVED_CONNECTION_KEYS) && !hasExactKeys(value, LEGACY_SAVED_CONNECTION_KEYS)) {
|
|
23686
23696
|
throw new PairingClientError("invalid_connection", "The saved connection is invalid");
|
|
23687
23697
|
}
|
|
23688
23698
|
const defaultWorkspace = value["defaultWorkspace"];
|
|
@@ -23700,7 +23710,7 @@ function decodeSavedConnection(value, expectedChannelId) {
|
|
|
23700
23710
|
};
|
|
23701
23711
|
}
|
|
23702
23712
|
async function readSavedConnection(channelId, environment) {
|
|
23703
|
-
const connectionPath =
|
|
23713
|
+
const connectionPath = join16(readStateDirectory(environment), "connections", `${channelId}.json`);
|
|
23704
23714
|
let encoded = "";
|
|
23705
23715
|
const handle = await open4(connectionPath, "r");
|
|
23706
23716
|
try {
|
|
@@ -23736,7 +23746,7 @@ function buildPairedDaemon(connection, connectionPath, inventory, environment) {
|
|
|
23736
23746
|
const relayUrl = mobiusApiUrl(connection.relayOrigin, "/v1/runtimes/connect");
|
|
23737
23747
|
relayUrl.protocol = relayUrl.protocol === "https:" ? "wss:" : "ws:";
|
|
23738
23748
|
relayUrl.searchParams.set("token", connection.runtimeCredential);
|
|
23739
|
-
const auditDatabasePath =
|
|
23749
|
+
const auditDatabasePath = join16(readStateDirectory(environment), "audit", `${connection.channelId}.sqlite`);
|
|
23740
23750
|
return {
|
|
23741
23751
|
channelUrl: new URL(`/channels/${encodeURIComponent(connection.channelId)}`, connection.relayOrigin).toString(),
|
|
23742
23752
|
config: createDaemonConfig(
|
|
@@ -23786,7 +23796,7 @@ async function pairLocalMachine(arguments_, signal = new AbortController().signa
|
|
|
23786
23796
|
}
|
|
23787
23797
|
async function resumeMobiusRuntime(channelId, signal, environment = process.env) {
|
|
23788
23798
|
const normalizedChannelId = validatePairingId(channelId);
|
|
23789
|
-
const connectionPath =
|
|
23799
|
+
const connectionPath = join16(readStateDirectory(environment), "connections", `${normalizedChannelId}.json`);
|
|
23790
23800
|
const connection = await readSavedConnection(normalizedChannelId, environment);
|
|
23791
23801
|
const inventory = await discoverLocalAgentInventory({
|
|
23792
23802
|
defaultWorkspace: connection.defaultWorkspace,
|
|
@@ -23799,15 +23809,714 @@ async function resumeLocalMachine(arguments_, signal = new AbortController().sig
|
|
|
23799
23809
|
return await resumeMobiusRuntime(parseChannelArgument(arguments_), signal, process.env);
|
|
23800
23810
|
}
|
|
23801
23811
|
|
|
23812
|
+
// src/bots/local-bot-grant-store.ts
|
|
23813
|
+
import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
|
|
23814
|
+
import { stat as stat7 } from "node:fs/promises";
|
|
23815
|
+
import { DatabaseSync } from "node:sqlite";
|
|
23816
|
+
var LOCAL_BOT_GRANT_LIFETIME_MILLISECONDS = 90 * 24 * 60 * 60 * 1e3;
|
|
23817
|
+
var identifier2 = external_exports.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$/);
|
|
23818
|
+
var savedApprovalSchema = external_exports.object({
|
|
23819
|
+
local_grant_id: identifier2,
|
|
23820
|
+
bot_id: identifier2,
|
|
23821
|
+
binding_id: identifier2,
|
|
23822
|
+
binding_revision: external_exports.number().int().positive(),
|
|
23823
|
+
agent_ref: identifier2,
|
|
23824
|
+
workspace_ref: external_exports.string().min(1).max(2048),
|
|
23825
|
+
revocation_epoch: external_exports.number().int().nonnegative(),
|
|
23826
|
+
agent_identity_digest: external_exports.string().regex(/^[a-f0-9]{64}$/),
|
|
23827
|
+
workspace_identity_digest: external_exports.string().regex(/^[a-f0-9]{64}$/)
|
|
23828
|
+
}).readonly();
|
|
23829
|
+
var LocalBotAuthorizationError = class extends Error {
|
|
23830
|
+
code;
|
|
23831
|
+
constructor(code, message, options = {}) {
|
|
23832
|
+
super(message, options);
|
|
23833
|
+
this.code = code;
|
|
23834
|
+
this.name = "LocalBotAuthorizationError";
|
|
23835
|
+
}
|
|
23836
|
+
};
|
|
23837
|
+
var LocalBotGrant = class {
|
|
23838
|
+
agent;
|
|
23839
|
+
agentIdentityDigest;
|
|
23840
|
+
allowedDeliveryTargetIds;
|
|
23841
|
+
behaviorDigest;
|
|
23842
|
+
behaviorRevisionId;
|
|
23843
|
+
bindingId;
|
|
23844
|
+
bindingRevision;
|
|
23845
|
+
botId;
|
|
23846
|
+
expiresAt;
|
|
23847
|
+
localGrantId;
|
|
23848
|
+
revocationEpoch;
|
|
23849
|
+
workspaceIdentityDigest;
|
|
23850
|
+
workspacePath;
|
|
23851
|
+
workspaceRef;
|
|
23852
|
+
constructor(localGrantId, botId, behaviorRevisionId, behaviorDigest, bindingId, bindingRevision, agent, agentIdentityDigest, workspaceRef, workspacePath, workspaceIdentityDigest, allowedDeliveryTargetIds, expiresAt, revocationEpoch) {
|
|
23853
|
+
this.localGrantId = localGrantId;
|
|
23854
|
+
this.botId = botId;
|
|
23855
|
+
this.behaviorRevisionId = behaviorRevisionId;
|
|
23856
|
+
this.behaviorDigest = behaviorDigest;
|
|
23857
|
+
this.bindingId = bindingId;
|
|
23858
|
+
this.bindingRevision = bindingRevision;
|
|
23859
|
+
this.agent = agent;
|
|
23860
|
+
this.agentIdentityDigest = agentIdentityDigest;
|
|
23861
|
+
this.workspaceRef = workspaceRef;
|
|
23862
|
+
this.workspacePath = workspacePath;
|
|
23863
|
+
this.workspaceIdentityDigest = workspaceIdentityDigest;
|
|
23864
|
+
this.allowedDeliveryTargetIds = Object.freeze([...allowedDeliveryTargetIds]);
|
|
23865
|
+
this.expiresAt = expiresAt;
|
|
23866
|
+
this.revocationEpoch = revocationEpoch;
|
|
23867
|
+
}
|
|
23868
|
+
};
|
|
23869
|
+
function digest(value) {
|
|
23870
|
+
return createHash2("sha256").update(value, "utf8").digest("hex");
|
|
23871
|
+
}
|
|
23872
|
+
function agentDigest(agent) {
|
|
23873
|
+
return digest(
|
|
23874
|
+
JSON.stringify({
|
|
23875
|
+
adapter: agent.adapterIdentity,
|
|
23876
|
+
agent: agent.agentIdentity,
|
|
23877
|
+
agentVersion: agent.agentVersion,
|
|
23878
|
+
arguments: agent.arguments,
|
|
23879
|
+
id: agent.id
|
|
23880
|
+
})
|
|
23881
|
+
);
|
|
23882
|
+
}
|
|
23883
|
+
async function workspaceDigest(path2) {
|
|
23884
|
+
const metadata = await stat7(path2);
|
|
23885
|
+
if (!metadata.isDirectory()) {
|
|
23886
|
+
throw new LocalBotAuthorizationError("binding_incompatible", "The Bot workspace is not a directory");
|
|
23887
|
+
}
|
|
23888
|
+
return digest(
|
|
23889
|
+
JSON.stringify({
|
|
23890
|
+
changedAtMilliseconds: metadata.ctimeMs,
|
|
23891
|
+
device: metadata.dev,
|
|
23892
|
+
inode: metadata.ino,
|
|
23893
|
+
path: path2
|
|
23894
|
+
})
|
|
23895
|
+
);
|
|
23896
|
+
}
|
|
23897
|
+
function exactTargets(left, right) {
|
|
23898
|
+
if (left.length !== right.length) {
|
|
23899
|
+
return false;
|
|
23900
|
+
}
|
|
23901
|
+
const expected = [...left].sort();
|
|
23902
|
+
const actual = [...right].sort();
|
|
23903
|
+
return expected.every((target, index) => target === actual[index]);
|
|
23904
|
+
}
|
|
23905
|
+
function rowString(row, key) {
|
|
23906
|
+
const value = Reflect.get(row, key);
|
|
23907
|
+
if (typeof value !== "string") {
|
|
23908
|
+
throw new LocalBotAuthorizationError("local_grant_invalid", "The local Bot grant is invalid");
|
|
23909
|
+
}
|
|
23910
|
+
return value;
|
|
23911
|
+
}
|
|
23912
|
+
function rowInteger(row, key) {
|
|
23913
|
+
const value = Reflect.get(row, key);
|
|
23914
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value)) {
|
|
23915
|
+
throw new LocalBotAuthorizationError("local_grant_invalid", "The local Bot grant is invalid");
|
|
23916
|
+
}
|
|
23917
|
+
return value;
|
|
23918
|
+
}
|
|
23919
|
+
function parseTargets(encoded) {
|
|
23920
|
+
let value;
|
|
23921
|
+
try {
|
|
23922
|
+
value = JSON.parse(encoded);
|
|
23923
|
+
} catch (cause) {
|
|
23924
|
+
throw new LocalBotAuthorizationError("local_grant_invalid", "The local Bot grant is invalid", { cause });
|
|
23925
|
+
}
|
|
23926
|
+
if (!Array.isArray(value) || value.some((target) => typeof target !== "string")) {
|
|
23927
|
+
throw new LocalBotAuthorizationError("local_grant_invalid", "The local Bot grant is invalid");
|
|
23928
|
+
}
|
|
23929
|
+
return value.filter((target) => typeof target === "string");
|
|
23930
|
+
}
|
|
23931
|
+
var LocalBotGrantStore = class {
|
|
23932
|
+
#database;
|
|
23933
|
+
static containsBot = (path2, botId) => {
|
|
23934
|
+
const database = new DatabaseSync(path2, { readOnly: true });
|
|
23935
|
+
try {
|
|
23936
|
+
if (database.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'local_bot_grants'").get() === void 0) {
|
|
23937
|
+
return false;
|
|
23938
|
+
}
|
|
23939
|
+
return database.prepare("SELECT local_grant_id FROM local_bot_grants WHERE bot_id = ? LIMIT 1").get(botId) !== void 0;
|
|
23940
|
+
} finally {
|
|
23941
|
+
database.close();
|
|
23942
|
+
}
|
|
23943
|
+
};
|
|
23944
|
+
constructor(path2) {
|
|
23945
|
+
this.#database = new DatabaseSync(path2);
|
|
23946
|
+
this.#database.exec(`
|
|
23947
|
+
CREATE TABLE IF NOT EXISTS local_bot_grants (
|
|
23948
|
+
local_grant_id TEXT PRIMARY KEY,
|
|
23949
|
+
bot_id TEXT NOT NULL,
|
|
23950
|
+
behavior_revision_id TEXT NOT NULL,
|
|
23951
|
+
behavior_digest TEXT NOT NULL,
|
|
23952
|
+
binding_id TEXT NOT NULL UNIQUE,
|
|
23953
|
+
binding_revision INTEGER NOT NULL,
|
|
23954
|
+
agent_ref TEXT NOT NULL,
|
|
23955
|
+
agent_identity_digest TEXT NOT NULL,
|
|
23956
|
+
workspace_ref TEXT NOT NULL,
|
|
23957
|
+
workspace_path TEXT NOT NULL,
|
|
23958
|
+
workspace_identity_digest TEXT NOT NULL,
|
|
23959
|
+
allowed_delivery_target_ids_json TEXT NOT NULL,
|
|
23960
|
+
unattended_policy_json TEXT NOT NULL,
|
|
23961
|
+
created_at INTEGER NOT NULL,
|
|
23962
|
+
expires_at INTEGER NOT NULL,
|
|
23963
|
+
revocation_epoch INTEGER NOT NULL DEFAULT 0,
|
|
23964
|
+
revoked_at INTEGER
|
|
23965
|
+
) STRICT;
|
|
23966
|
+
CREATE TABLE IF NOT EXISTS local_bot_binding_resolutions (
|
|
23967
|
+
request_id TEXT PRIMARY KEY,
|
|
23968
|
+
binding_id TEXT NOT NULL,
|
|
23969
|
+
local_grant_id TEXT NOT NULL,
|
|
23970
|
+
resolved_at INTEGER NOT NULL,
|
|
23971
|
+
result TEXT NOT NULL CHECK (result IN ('approved', 'unavailable')),
|
|
23972
|
+
error_code TEXT,
|
|
23973
|
+
error_message TEXT
|
|
23974
|
+
) STRICT;
|
|
23975
|
+
`);
|
|
23976
|
+
}
|
|
23977
|
+
/** Only the local CLI may invoke this explicit approval transition. */
|
|
23978
|
+
reapprove = async (botId, catalog, directories, bindingAuthorityId, now, signal) => {
|
|
23979
|
+
signal.throwIfAborted();
|
|
23980
|
+
if (!identifier2.safeParse(botId).success) {
|
|
23981
|
+
throw new LocalBotAuthorizationError("invalid_request", "The Bot ID is invalid");
|
|
23982
|
+
}
|
|
23983
|
+
const row = this.#database.prepare("SELECT * FROM local_bot_grants WHERE bot_id = ? ORDER BY binding_revision DESC LIMIT 1").get(botId);
|
|
23984
|
+
if (row === void 0) {
|
|
23985
|
+
throw new LocalBotAuthorizationError("bot_not_found", "This machine has no saved approval for that Bot");
|
|
23986
|
+
}
|
|
23987
|
+
const parsed = savedApprovalSchema.safeParse(row);
|
|
23988
|
+
if (!parsed.success) {
|
|
23989
|
+
throw new LocalBotAuthorizationError("local_grant_invalid", "The saved Bot approval is invalid", {
|
|
23990
|
+
cause: parsed.error
|
|
23991
|
+
});
|
|
23992
|
+
}
|
|
23993
|
+
const saved = parsed.data;
|
|
23994
|
+
const agent = catalog.find(saved.agent_ref);
|
|
23995
|
+
if (!agent.capabilities.embeddedContextPrompt) {
|
|
23996
|
+
throw new LocalBotAuthorizationError("binding_incompatible", "The local Agent cannot apply Bot context");
|
|
23997
|
+
}
|
|
23998
|
+
const [adapterUnchanged, agentUnchanged] = await Promise.all([
|
|
23999
|
+
revalidateExecutable(agent.adapterIdentity),
|
|
24000
|
+
revalidateExecutable(agent.agentIdentity)
|
|
24001
|
+
]);
|
|
24002
|
+
signal.throwIfAborted();
|
|
24003
|
+
if (!adapterUnchanged || !agentUnchanged) {
|
|
24004
|
+
throw new LocalBotAuthorizationError(
|
|
24005
|
+
"binding_incompatible",
|
|
24006
|
+
"The local Agent executable changed before approval"
|
|
24007
|
+
);
|
|
24008
|
+
}
|
|
24009
|
+
const workspacePath = await directories.resolve(bindingAuthorityId, saved.workspace_ref, signal);
|
|
24010
|
+
const workspaceIdentityDigest = await workspaceDigest(workspacePath);
|
|
24011
|
+
signal.throwIfAborted();
|
|
24012
|
+
this.#database.exec("BEGIN IMMEDIATE");
|
|
24013
|
+
try {
|
|
24014
|
+
const result = this.#database.prepare(`UPDATE local_bot_grants
|
|
24015
|
+
SET agent_identity_digest = ?, workspace_identity_digest = ?, workspace_path = ?,
|
|
24016
|
+
revoked_at = NULL, expires_at = ?, revocation_epoch = revocation_epoch + 1
|
|
24017
|
+
WHERE local_grant_id = ? AND revocation_epoch = ?
|
|
24018
|
+
AND agent_identity_digest = ? AND workspace_identity_digest = ?
|
|
24019
|
+
AND binding_revision = (SELECT MAX(binding_revision) FROM local_bot_grants WHERE bot_id = ?)`).run(
|
|
24020
|
+
agentDigest(agent),
|
|
24021
|
+
workspaceIdentityDigest,
|
|
24022
|
+
workspacePath,
|
|
24023
|
+
now + LOCAL_BOT_GRANT_LIFETIME_MILLISECONDS,
|
|
24024
|
+
saved.local_grant_id,
|
|
24025
|
+
saved.revocation_epoch,
|
|
24026
|
+
saved.agent_identity_digest,
|
|
24027
|
+
saved.workspace_identity_digest,
|
|
24028
|
+
botId
|
|
24029
|
+
);
|
|
24030
|
+
if (result.changes !== 1) {
|
|
24031
|
+
throw new LocalBotAuthorizationError(
|
|
24032
|
+
"binding_not_approved",
|
|
24033
|
+
"The Bot binding changed during approval; run the command again"
|
|
24034
|
+
);
|
|
24035
|
+
}
|
|
24036
|
+
this.#database.prepare(`INSERT INTO audit_events (recorded_at, event, command_id, session_id, details_json)
|
|
24037
|
+
VALUES (?, 'bot_binding_locally_reapproved', ?, '', ?)`).run(
|
|
24038
|
+
new Date(now).toISOString(),
|
|
24039
|
+
saved.binding_id,
|
|
24040
|
+
JSON.stringify({ botId, bindingId: saved.binding_id, agentRef: saved.agent_ref })
|
|
24041
|
+
);
|
|
24042
|
+
this.#database.exec("COMMIT");
|
|
24043
|
+
} catch (cause) {
|
|
24044
|
+
this.#database.exec("ROLLBACK");
|
|
24045
|
+
throw cause;
|
|
24046
|
+
}
|
|
24047
|
+
return { bindingId: saved.binding_id, workspacePath, agentRef: saved.agent_ref };
|
|
24048
|
+
};
|
|
24049
|
+
approve = async (request, catalog, directories, bindingAuthorityId, now, signal) => {
|
|
24050
|
+
signal.throwIfAborted();
|
|
24051
|
+
const existing = this.#database.prepare(
|
|
24052
|
+
`SELECT grants.* FROM local_bot_binding_resolutions AS resolutions
|
|
24053
|
+
INNER JOIN local_bot_grants AS grants
|
|
24054
|
+
ON grants.local_grant_id = resolutions.local_grant_id
|
|
24055
|
+
WHERE resolutions.request_id = ? AND resolutions.binding_id = ?
|
|
24056
|
+
AND resolutions.result = 'approved'`
|
|
24057
|
+
).get(request.requestId, request.bindingId);
|
|
24058
|
+
if (existing !== void 0) {
|
|
24059
|
+
return new BotExecutionBindingApprovedEvent(
|
|
24060
|
+
request.requestId,
|
|
24061
|
+
request.botId,
|
|
24062
|
+
request.behaviorRevisionId,
|
|
24063
|
+
request.bindingId,
|
|
24064
|
+
request.bindingRevision,
|
|
24065
|
+
rowString(existing, "local_grant_id"),
|
|
24066
|
+
rowString(existing, "agent_identity_digest"),
|
|
24067
|
+
rowString(existing, "workspace_identity_digest")
|
|
24068
|
+
);
|
|
24069
|
+
}
|
|
24070
|
+
const resolved = await this.resolveProposal(request, catalog, directories, bindingAuthorityId, signal);
|
|
24071
|
+
const localGrantId = `lbg_${randomUUID2()}`;
|
|
24072
|
+
const expiresAt = now + LOCAL_BOT_GRANT_LIFETIME_MILLISECONDS;
|
|
24073
|
+
this.#database.exec("BEGIN IMMEDIATE");
|
|
24074
|
+
try {
|
|
24075
|
+
this.#database.prepare(
|
|
24076
|
+
`INSERT INTO local_bot_grants (
|
|
24077
|
+
local_grant_id, bot_id, behavior_revision_id, behavior_digest,
|
|
24078
|
+
binding_id, binding_revision, agent_ref,
|
|
24079
|
+
agent_identity_digest, workspace_ref, workspace_path,
|
|
24080
|
+
workspace_identity_digest, allowed_delivery_target_ids_json,
|
|
24081
|
+
unattended_policy_json, created_at, expires_at
|
|
24082
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
24083
|
+
).run(
|
|
24084
|
+
localGrantId,
|
|
24085
|
+
request.botId,
|
|
24086
|
+
request.behaviorRevisionId,
|
|
24087
|
+
request.behaviorDigest,
|
|
24088
|
+
request.bindingId,
|
|
24089
|
+
request.bindingRevision,
|
|
24090
|
+
request.agentRef,
|
|
24091
|
+
resolved.agentIdentityDigest,
|
|
24092
|
+
request.workspaceRef,
|
|
24093
|
+
resolved.workspacePath,
|
|
24094
|
+
resolved.workspaceIdentityDigest,
|
|
24095
|
+
JSON.stringify(request.allowedDeliveryTargets.map((target) => target.deliveryTargetId)),
|
|
24096
|
+
JSON.stringify({ localSideEffects: "owner-approval-required" }),
|
|
24097
|
+
now,
|
|
24098
|
+
expiresAt
|
|
24099
|
+
);
|
|
24100
|
+
this.#database.prepare(
|
|
24101
|
+
`INSERT INTO local_bot_binding_resolutions (
|
|
24102
|
+
request_id, binding_id, local_grant_id, resolved_at, result
|
|
24103
|
+
) VALUES (?, ?, ?, ?, 'approved')`
|
|
24104
|
+
).run(request.requestId, request.bindingId, localGrantId, now);
|
|
24105
|
+
this.#database.exec("COMMIT");
|
|
24106
|
+
} catch (cause) {
|
|
24107
|
+
this.#database.exec("ROLLBACK");
|
|
24108
|
+
throw cause;
|
|
24109
|
+
}
|
|
24110
|
+
signal.throwIfAborted();
|
|
24111
|
+
return new BotExecutionBindingApprovedEvent(
|
|
24112
|
+
request.requestId,
|
|
24113
|
+
request.botId,
|
|
24114
|
+
request.behaviorRevisionId,
|
|
24115
|
+
request.bindingId,
|
|
24116
|
+
request.bindingRevision,
|
|
24117
|
+
localGrantId,
|
|
24118
|
+
resolved.agentIdentityDigest,
|
|
24119
|
+
resolved.workspaceIdentityDigest
|
|
24120
|
+
);
|
|
24121
|
+
};
|
|
24122
|
+
resolveExecution = async (command, catalog, directories, bindingAuthorityId, now, signal) => {
|
|
24123
|
+
signal.throwIfAborted();
|
|
24124
|
+
const row = this.#database.prepare(
|
|
24125
|
+
`SELECT * FROM local_bot_grants
|
|
24126
|
+
WHERE local_grant_id = ? AND bot_id = ?
|
|
24127
|
+
AND behavior_revision_id = ? AND behavior_digest = ?
|
|
24128
|
+
AND binding_id = ? AND binding_revision = ?
|
|
24129
|
+
AND agent_ref = ?
|
|
24130
|
+
AND revoked_at IS NULL AND expires_at > ?`
|
|
24131
|
+
).get(
|
|
24132
|
+
command.localGrantId,
|
|
24133
|
+
command.botId,
|
|
24134
|
+
command.behaviorRevisionId,
|
|
24135
|
+
command.behaviorDigest,
|
|
24136
|
+
command.bindingId,
|
|
24137
|
+
command.bindingRevision,
|
|
24138
|
+
command.agentId,
|
|
24139
|
+
now
|
|
24140
|
+
);
|
|
24141
|
+
if (row === void 0) {
|
|
24142
|
+
throw new LocalBotAuthorizationError(
|
|
24143
|
+
"binding_not_approved",
|
|
24144
|
+
"The Bot execution binding is not locally approved"
|
|
24145
|
+
);
|
|
24146
|
+
}
|
|
24147
|
+
const approvedTargets = parseTargets(rowString(row, "allowed_delivery_target_ids_json"));
|
|
24148
|
+
const requestedTargets = command.allowedDeliveryTargets.map((target) => target.deliveryTargetId);
|
|
24149
|
+
if (!exactTargets(approvedTargets, requestedTargets)) {
|
|
24150
|
+
throw new LocalBotAuthorizationError(
|
|
24151
|
+
"binding_not_approved",
|
|
24152
|
+
"The Bot execution target policy changed after local approval"
|
|
24153
|
+
);
|
|
24154
|
+
}
|
|
24155
|
+
const agent = catalog.find(command.agentId);
|
|
24156
|
+
const [adapterUnchanged, agentUnchanged] = await Promise.all([
|
|
24157
|
+
revalidateExecutable(agent.adapterIdentity),
|
|
24158
|
+
revalidateExecutable(agent.agentIdentity)
|
|
24159
|
+
]);
|
|
24160
|
+
signal.throwIfAborted();
|
|
24161
|
+
const workspaceRef = rowString(row, "workspace_ref");
|
|
24162
|
+
const workspacePath = await directories.resolve(bindingAuthorityId, workspaceRef, signal);
|
|
24163
|
+
const currentAgentDigest = agentDigest(agent);
|
|
24164
|
+
const currentWorkspaceDigest = await workspaceDigest(workspacePath);
|
|
24165
|
+
signal.throwIfAborted();
|
|
24166
|
+
if (!adapterUnchanged || !agentUnchanged || currentAgentDigest !== rowString(row, "agent_identity_digest") || currentWorkspaceDigest !== rowString(row, "workspace_identity_digest") || workspacePath !== rowString(row, "workspace_path")) {
|
|
24167
|
+
this.revoke(rowString(row, "local_grant_id"), now);
|
|
24168
|
+
throw new LocalBotAuthorizationError(
|
|
24169
|
+
"binding_not_approved",
|
|
24170
|
+
"The Bot execution target changed and requires local approval"
|
|
24171
|
+
);
|
|
24172
|
+
}
|
|
24173
|
+
if (command.behaviorInstructions.length > 0 && !agent.capabilities.embeddedContextPrompt) {
|
|
24174
|
+
throw new LocalBotAuthorizationError(
|
|
24175
|
+
"binding_incompatible",
|
|
24176
|
+
"The local Agent cannot apply the approved Bot behavior"
|
|
24177
|
+
);
|
|
24178
|
+
}
|
|
24179
|
+
return new LocalBotGrant(
|
|
24180
|
+
rowString(row, "local_grant_id"),
|
|
24181
|
+
command.botId,
|
|
24182
|
+
command.behaviorRevisionId,
|
|
24183
|
+
command.behaviorDigest,
|
|
24184
|
+
command.bindingId,
|
|
24185
|
+
command.bindingRevision,
|
|
24186
|
+
agent,
|
|
24187
|
+
currentAgentDigest,
|
|
24188
|
+
workspaceRef,
|
|
24189
|
+
workspacePath,
|
|
24190
|
+
currentWorkspaceDigest,
|
|
24191
|
+
approvedTargets,
|
|
24192
|
+
rowInteger(row, "expires_at"),
|
|
24193
|
+
rowInteger(row, "revocation_epoch")
|
|
24194
|
+
);
|
|
24195
|
+
};
|
|
24196
|
+
recordUnavailable = (request, code, message, now) => {
|
|
24197
|
+
this.#database.prepare(
|
|
24198
|
+
`INSERT OR IGNORE INTO local_bot_binding_resolutions (
|
|
24199
|
+
request_id, binding_id, local_grant_id, resolved_at,
|
|
24200
|
+
result, error_code, error_message
|
|
24201
|
+
) VALUES (?, ?, '', ?, 'unavailable', ?, ?)`
|
|
24202
|
+
).run(request.requestId, request.bindingId, now, code, message);
|
|
24203
|
+
};
|
|
24204
|
+
close = () => {
|
|
24205
|
+
this.#database.close();
|
|
24206
|
+
};
|
|
24207
|
+
resolveProposal = async (request, catalog, directories, bindingAuthorityId, signal) => {
|
|
24208
|
+
const agent = catalog.find(request.agentRef);
|
|
24209
|
+
if (request.requiredCapabilities.includes("embedded-context") && !agent.capabilities.embeddedContextPrompt) {
|
|
24210
|
+
throw new LocalBotAuthorizationError(
|
|
24211
|
+
"binding_incompatible",
|
|
24212
|
+
"The local Agent cannot apply embedded Bot context"
|
|
24213
|
+
);
|
|
24214
|
+
}
|
|
24215
|
+
if (request.requiredCapabilities.some((capability) => capability !== "embedded-context")) {
|
|
24216
|
+
throw new LocalBotAuthorizationError(
|
|
24217
|
+
"binding_incompatible",
|
|
24218
|
+
"The Bot requires an unsupported local Agent capability"
|
|
24219
|
+
);
|
|
24220
|
+
}
|
|
24221
|
+
const [adapterUnchanged, agentUnchanged] = await Promise.all([
|
|
24222
|
+
revalidateExecutable(agent.adapterIdentity),
|
|
24223
|
+
revalidateExecutable(agent.agentIdentity)
|
|
24224
|
+
]);
|
|
24225
|
+
signal.throwIfAborted();
|
|
24226
|
+
if (!adapterUnchanged || !agentUnchanged) {
|
|
24227
|
+
throw new LocalBotAuthorizationError(
|
|
24228
|
+
"binding_incompatible",
|
|
24229
|
+
"The local Agent executable changed before approval"
|
|
24230
|
+
);
|
|
24231
|
+
}
|
|
24232
|
+
const workspacePath = await directories.resolve(bindingAuthorityId, request.workspaceRef, signal);
|
|
24233
|
+
return {
|
|
24234
|
+
agent,
|
|
24235
|
+
agentIdentityDigest: agentDigest(agent),
|
|
24236
|
+
workspaceIdentityDigest: await workspaceDigest(workspacePath),
|
|
24237
|
+
workspacePath
|
|
24238
|
+
};
|
|
24239
|
+
};
|
|
24240
|
+
revoke = (localGrantId, now) => {
|
|
24241
|
+
this.#database.prepare(
|
|
24242
|
+
`UPDATE local_bot_grants
|
|
24243
|
+
SET revoked_at = COALESCE(revoked_at, ?), revocation_epoch = revocation_epoch + 1
|
|
24244
|
+
WHERE local_grant_id = ? AND revoked_at IS NULL`
|
|
24245
|
+
).run(now, localGrantId);
|
|
24246
|
+
};
|
|
24247
|
+
};
|
|
24248
|
+
|
|
24249
|
+
// src/bots/bot-approve-command.ts
|
|
24250
|
+
var identifier3 = external_exports.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$/);
|
|
24251
|
+
var argumentsSchema = external_exports.union([
|
|
24252
|
+
external_exports.tuple([external_exports.literal("--bot"), identifier3]),
|
|
24253
|
+
external_exports.tuple([external_exports.literal("--bot"), identifier3, external_exports.literal("--channel"), identifier3])
|
|
24254
|
+
]);
|
|
24255
|
+
async function executeBotApproveCommand(arguments_, environment, parent) {
|
|
24256
|
+
const parsed = argumentsSchema.safeParse(arguments_);
|
|
24257
|
+
if (!parsed.success) {
|
|
24258
|
+
throw new LocalBotAuthorizationError(
|
|
24259
|
+
"invalid_arguments",
|
|
24260
|
+
"Usage: mobius bot approve --bot <bot-id> [--channel <channel-id>]"
|
|
24261
|
+
);
|
|
24262
|
+
}
|
|
24263
|
+
const [, botId, , requestedChannel] = parsed.data;
|
|
24264
|
+
const signal = AbortSignal.any([parent, AbortSignal.timeout(3e4)]);
|
|
24265
|
+
signal.throwIfAborted();
|
|
24266
|
+
try {
|
|
24267
|
+
const stateDirectory = await realpath8(readStateDirectory(environment));
|
|
24268
|
+
const auditDirectory = await realpath8(join17(stateDirectory, "audit"));
|
|
24269
|
+
if (dirname4(auditDirectory) !== stateDirectory) {
|
|
24270
|
+
throw new LocalBotAuthorizationError(
|
|
24271
|
+
"local_grant_invalid",
|
|
24272
|
+
"The local approval directory is outside the Mobius state directory"
|
|
24273
|
+
);
|
|
24274
|
+
}
|
|
24275
|
+
const channels = [];
|
|
24276
|
+
for (const entry of await readdir2(auditDirectory, { withFileTypes: true })) {
|
|
24277
|
+
signal.throwIfAborted();
|
|
24278
|
+
if (!entry.isFile() || !entry.name.endsWith(".sqlite")) {
|
|
24279
|
+
continue;
|
|
24280
|
+
}
|
|
24281
|
+
const channelId2 = entry.name.slice(0, -7);
|
|
24282
|
+
if (!identifier3.safeParse(channelId2).success || requestedChannel !== void 0 && requestedChannel !== channelId2) {
|
|
24283
|
+
continue;
|
|
24284
|
+
}
|
|
24285
|
+
const databasePath = await realpath8(join17(auditDirectory, entry.name));
|
|
24286
|
+
if (dirname4(databasePath) !== auditDirectory) {
|
|
24287
|
+
throw new LocalBotAuthorizationError(
|
|
24288
|
+
"local_grant_invalid",
|
|
24289
|
+
"The local approval database is outside the Mobius audit directory"
|
|
24290
|
+
);
|
|
24291
|
+
}
|
|
24292
|
+
if (LocalBotGrantStore.containsBot(databasePath, botId)) {
|
|
24293
|
+
channels.push(channelId2);
|
|
24294
|
+
}
|
|
24295
|
+
}
|
|
24296
|
+
const channelId = channels[0];
|
|
24297
|
+
if (channelId === void 0) {
|
|
24298
|
+
throw new LocalBotAuthorizationError(
|
|
24299
|
+
"bot_not_found",
|
|
24300
|
+
"No saved approval for this Bot was found. Run this command on the machine hosting the Bot."
|
|
24301
|
+
);
|
|
24302
|
+
}
|
|
24303
|
+
if (channels.length !== 1) {
|
|
24304
|
+
throw new LocalBotAuthorizationError(
|
|
24305
|
+
"binding_ambiguous",
|
|
24306
|
+
`This Bot has approvals in multiple local channels. Add --channel with the intended channel: ${channels.join(", ")}`
|
|
24307
|
+
);
|
|
24308
|
+
}
|
|
24309
|
+
const { config: config2 } = await resumeMobiusRuntime(channelId, signal, environment);
|
|
24310
|
+
signal.throwIfAborted();
|
|
24311
|
+
const grants = new LocalBotGrantStore(config2.auditDatabasePath);
|
|
24312
|
+
try {
|
|
24313
|
+
const approval = await grants.reapprove(
|
|
24314
|
+
botId,
|
|
24315
|
+
config2.catalog,
|
|
24316
|
+
new WorkspaceDirectoryService(config2.bindingId, config2.defaultWorkspace),
|
|
24317
|
+
config2.bindingId,
|
|
24318
|
+
Date.now(),
|
|
24319
|
+
signal
|
|
24320
|
+
);
|
|
24321
|
+
return `Approved Bot ${botId}
|
|
24322
|
+
Binding: ${approval.bindingId}
|
|
24323
|
+
Agent: ${approval.agentRef}
|
|
24324
|
+
Workspace: ${approval.workspacePath}
|
|
24325
|
+
Keep your daemon running and resend your message in the same Chat conversation.
|
|
24326
|
+
`;
|
|
24327
|
+
} finally {
|
|
24328
|
+
grants.close();
|
|
24329
|
+
}
|
|
24330
|
+
} catch (cause) {
|
|
24331
|
+
parent.throwIfAborted();
|
|
24332
|
+
if (signal.aborted) {
|
|
24333
|
+
throw new LocalBotAuthorizationError("approval_timeout", "Local Bot approval timed out", { cause });
|
|
24334
|
+
}
|
|
24335
|
+
if (cause instanceof LocalBotAuthorizationError) {
|
|
24336
|
+
throw cause;
|
|
24337
|
+
}
|
|
24338
|
+
if (cause instanceof Error && "code" in cause && cause.code === "ENOENT") {
|
|
24339
|
+
throw new LocalBotAuthorizationError(
|
|
24340
|
+
"bot_not_found",
|
|
24341
|
+
"No saved local approval or Runtime connection was found for this Bot",
|
|
24342
|
+
{ cause }
|
|
24343
|
+
);
|
|
24344
|
+
}
|
|
24345
|
+
throw new LocalBotAuthorizationError(
|
|
24346
|
+
"local_approval_failed",
|
|
24347
|
+
"Local Bot approval failed. Check the saved Runtime connection and local Agent installation.",
|
|
24348
|
+
{ cause }
|
|
24349
|
+
);
|
|
24350
|
+
}
|
|
24351
|
+
}
|
|
24352
|
+
|
|
24353
|
+
// src/bots/bot-message-command.ts
|
|
24354
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
24355
|
+
import { createConnection } from "node:net";
|
|
24356
|
+
var BOT_IPC_RESPONSE_LIMIT = 64 * 1024;
|
|
24357
|
+
var BOT_IPC_TIMEOUT_MILLISECONDS = 2e4;
|
|
24358
|
+
var BotMessageCommandError = class extends Error {
|
|
24359
|
+
code;
|
|
24360
|
+
constructor(code, message, options = {}) {
|
|
24361
|
+
super(message, options);
|
|
24362
|
+
this.code = code;
|
|
24363
|
+
this.name = "BotMessageCommandError";
|
|
24364
|
+
}
|
|
24365
|
+
};
|
|
24366
|
+
function isRecord7(value) {
|
|
24367
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
24368
|
+
}
|
|
24369
|
+
function requiredEnvironment(environment, name) {
|
|
24370
|
+
const value = environment[name]?.trim();
|
|
24371
|
+
if (value === void 0 || value.length === 0) {
|
|
24372
|
+
throw new BotMessageCommandError(
|
|
24373
|
+
"execution_context_required",
|
|
24374
|
+
"This command is available only inside an active Mobius Bot execution"
|
|
24375
|
+
);
|
|
24376
|
+
}
|
|
24377
|
+
return value;
|
|
24378
|
+
}
|
|
24379
|
+
function option(arguments_, name) {
|
|
24380
|
+
const index = arguments_.indexOf(name);
|
|
24381
|
+
if (index < 0 || index + 1 >= arguments_.length || arguments_[index + 1]?.startsWith("--") === true) {
|
|
24382
|
+
throw new BotMessageCommandError(
|
|
24383
|
+
"invalid_request",
|
|
24384
|
+
"Usage: mobius bot message send --target <current|alias> --text <message>"
|
|
24385
|
+
);
|
|
24386
|
+
}
|
|
24387
|
+
const value = arguments_[index + 1];
|
|
24388
|
+
if (value === void 0) {
|
|
24389
|
+
throw new BotMessageCommandError("invalid_request", "The Bot message option is missing");
|
|
24390
|
+
}
|
|
24391
|
+
return value;
|
|
24392
|
+
}
|
|
24393
|
+
function validateArguments(arguments_) {
|
|
24394
|
+
if (arguments_.length !== 4) {
|
|
24395
|
+
throw new BotMessageCommandError(
|
|
24396
|
+
"invalid_request",
|
|
24397
|
+
"Usage: mobius bot message send --target <current|alias> --text <message>"
|
|
24398
|
+
);
|
|
24399
|
+
}
|
|
24400
|
+
const names = [arguments_[0], arguments_[2]];
|
|
24401
|
+
if (!names.includes("--target") || !names.includes("--text") || names[0] === names[1]) {
|
|
24402
|
+
throw new BotMessageCommandError(
|
|
24403
|
+
"invalid_request",
|
|
24404
|
+
"Usage: mobius bot message send --target <current|alias> --text <message>"
|
|
24405
|
+
);
|
|
24406
|
+
}
|
|
24407
|
+
}
|
|
24408
|
+
function decodeResponse(encoded) {
|
|
24409
|
+
let value;
|
|
24410
|
+
try {
|
|
24411
|
+
value = JSON.parse(encoded);
|
|
24412
|
+
} catch (cause) {
|
|
24413
|
+
throw new BotMessageCommandError("bot_service_unavailable", "The local Bot service returned invalid data", {
|
|
24414
|
+
cause
|
|
24415
|
+
});
|
|
24416
|
+
}
|
|
24417
|
+
if (!isRecord7(value) || typeof value["ok"] !== "boolean") {
|
|
24418
|
+
throw new BotMessageCommandError("bot_service_unavailable", "The local Bot service returned invalid data");
|
|
24419
|
+
}
|
|
24420
|
+
if (value["ok"] === true && typeof value["outboundMessageId"] === "string") {
|
|
24421
|
+
return value["outboundMessageId"];
|
|
24422
|
+
}
|
|
24423
|
+
const error61 = value["error"];
|
|
24424
|
+
if (value["ok"] === false && isRecord7(error61) && typeof error61["code"] === "string" && typeof error61["message"] === "string") {
|
|
24425
|
+
throw new BotMessageCommandError(error61["code"], error61["message"]);
|
|
24426
|
+
}
|
|
24427
|
+
throw new BotMessageCommandError("bot_service_unavailable", "The local Bot service returned invalid data");
|
|
24428
|
+
}
|
|
24429
|
+
async function requestLocalBotService(socketPath, payload, signal) {
|
|
24430
|
+
signal.throwIfAborted();
|
|
24431
|
+
return await new Promise((resolve5, reject) => {
|
|
24432
|
+
const socket = createConnection(socketPath);
|
|
24433
|
+
const chunks = [];
|
|
24434
|
+
let length = 0;
|
|
24435
|
+
let finished = false;
|
|
24436
|
+
const finish = () => {
|
|
24437
|
+
socket.removeAllListeners();
|
|
24438
|
+
socket.destroy();
|
|
24439
|
+
};
|
|
24440
|
+
const fail = (error61) => {
|
|
24441
|
+
if (finished) {
|
|
24442
|
+
return;
|
|
24443
|
+
}
|
|
24444
|
+
finished = true;
|
|
24445
|
+
finish();
|
|
24446
|
+
if (error61 instanceof BotMessageCommandError) {
|
|
24447
|
+
reject(error61);
|
|
24448
|
+
return;
|
|
24449
|
+
}
|
|
24450
|
+
reject(
|
|
24451
|
+
new BotMessageCommandError(
|
|
24452
|
+
"execution_context_required",
|
|
24453
|
+
"The active Mobius Bot execution is unavailable",
|
|
24454
|
+
{ cause: error61 }
|
|
24455
|
+
)
|
|
24456
|
+
);
|
|
24457
|
+
};
|
|
24458
|
+
const abort = () => fail(signal.reason);
|
|
24459
|
+
socket.setTimeout(BOT_IPC_TIMEOUT_MILLISECONDS, () => {
|
|
24460
|
+
fail(new BotMessageCommandError("bot_service_unavailable", "The local Bot service timed out"));
|
|
24461
|
+
});
|
|
24462
|
+
socket.once("error", fail);
|
|
24463
|
+
socket.once("connect", () => socket.end(`${payload}
|
|
24464
|
+
`));
|
|
24465
|
+
socket.on("data", (chunk) => {
|
|
24466
|
+
length += chunk.byteLength;
|
|
24467
|
+
if (length > BOT_IPC_RESPONSE_LIMIT) {
|
|
24468
|
+
fail(new BotMessageCommandError("bot_service_unavailable", "The local Bot response is too large"));
|
|
24469
|
+
return;
|
|
24470
|
+
}
|
|
24471
|
+
chunks.push(chunk);
|
|
24472
|
+
});
|
|
24473
|
+
socket.once("end", () => {
|
|
24474
|
+
if (finished) {
|
|
24475
|
+
return;
|
|
24476
|
+
}
|
|
24477
|
+
finished = true;
|
|
24478
|
+
signal.removeEventListener("abort", abort);
|
|
24479
|
+
try {
|
|
24480
|
+
resolve5(decodeResponse(Buffer.concat(chunks).toString("utf8")));
|
|
24481
|
+
} catch (error61) {
|
|
24482
|
+
reject(error61);
|
|
24483
|
+
} finally {
|
|
24484
|
+
finish();
|
|
24485
|
+
}
|
|
24486
|
+
});
|
|
24487
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
24488
|
+
});
|
|
24489
|
+
}
|
|
24490
|
+
async function executeBotMessageCommand(arguments_, environment, signal) {
|
|
24491
|
+
validateArguments(arguments_);
|
|
24492
|
+
const contextHandle = requiredEnvironment(environment, "MOBIUS_BOT_CONTEXT");
|
|
24493
|
+
const socketPath = requiredEnvironment(environment, "MOBIUS_BOT_SOCKET");
|
|
24494
|
+
const target = option(arguments_, "--target");
|
|
24495
|
+
const text = option(arguments_, "--text");
|
|
24496
|
+
if (!/^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$/.test(target) || text.trim().length === 0 || text.length > 1e4) {
|
|
24497
|
+
throw new BotMessageCommandError("invalid_request", "The Bot outbound message is invalid");
|
|
24498
|
+
}
|
|
24499
|
+
return await requestLocalBotService(
|
|
24500
|
+
socketPath,
|
|
24501
|
+
JSON.stringify({
|
|
24502
|
+
contextHandle,
|
|
24503
|
+
idempotencyKey: `cli_${randomUUID3()}`,
|
|
24504
|
+
target,
|
|
24505
|
+
text
|
|
24506
|
+
}),
|
|
24507
|
+
signal
|
|
24508
|
+
);
|
|
24509
|
+
}
|
|
24510
|
+
|
|
23802
24511
|
// src/audit/audit-store.ts
|
|
23803
24512
|
import { mkdirSync } from "node:fs";
|
|
23804
|
-
import { dirname as
|
|
23805
|
-
import { DatabaseSync } from "node:sqlite";
|
|
24513
|
+
import { dirname as dirname5 } from "node:path";
|
|
24514
|
+
import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
|
|
23806
24515
|
var AuditStore = class {
|
|
23807
24516
|
database;
|
|
23808
24517
|
constructor(path2) {
|
|
23809
|
-
mkdirSync(
|
|
23810
|
-
this.database = new
|
|
24518
|
+
mkdirSync(dirname5(path2), { recursive: true });
|
|
24519
|
+
this.database = new DatabaseSync2(path2);
|
|
23811
24520
|
this.database.exec(`
|
|
23812
24521
|
CREATE TABLE IF NOT EXISTS audit_events (
|
|
23813
24522
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
@@ -23849,7 +24558,7 @@ var AuditStore = class {
|
|
|
23849
24558
|
|
|
23850
24559
|
// src/run-daemon.ts
|
|
23851
24560
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
23852
|
-
import { basename as basename5, join as
|
|
24561
|
+
import { basename as basename5, join as join18 } from "node:path";
|
|
23853
24562
|
import { styleText } from "node:util";
|
|
23854
24563
|
import { fetch as fetch7, WebSocket as WebSocket2 } from "undici";
|
|
23855
24564
|
|
|
@@ -23863,8 +24572,8 @@ import {
|
|
|
23863
24572
|
} from "@agentclientprotocol/sdk";
|
|
23864
24573
|
|
|
23865
24574
|
// src/authorization/local-raster.ts
|
|
23866
|
-
import { readFile, realpath as
|
|
23867
|
-
import { basename as
|
|
24575
|
+
import { readFile, realpath as realpath9, stat as stat8 } from "node:fs/promises";
|
|
24576
|
+
import { basename as basename3, isAbsolute as isAbsolute9, normalize as normalize3, relative as relative5, sep as sep4 } from "node:path";
|
|
23868
24577
|
import { fileURLToPath } from "node:url";
|
|
23869
24578
|
var MAX_LOCAL_RASTER_BYTES = 3e6;
|
|
23870
24579
|
var PNG_SIGNATURE = Uint8Array.of(137, 80, 78, 71);
|
|
@@ -23906,18 +24615,18 @@ function sniffRasterMime(bytes) {
|
|
|
23906
24615
|
return null;
|
|
23907
24616
|
}
|
|
23908
24617
|
function isInsideRoot(root, target) {
|
|
23909
|
-
const relation =
|
|
23910
|
-
return relation !== ".." && !relation.startsWith(`..${sep4}`) && !
|
|
24618
|
+
const relation = relative5(root, target);
|
|
24619
|
+
return relation !== ".." && !relation.startsWith(`..${sep4}`) && !isAbsolute9(relation);
|
|
23911
24620
|
}
|
|
23912
24621
|
async function resolveCanonicalPath(requested, signal) {
|
|
23913
24622
|
signal.throwIfAborted();
|
|
23914
|
-
if (!
|
|
24623
|
+
if (!isAbsolute9(requested) || requested.includes("\0") || normalize3(requested) !== requested) {
|
|
23915
24624
|
return null;
|
|
23916
24625
|
}
|
|
23917
24626
|
try {
|
|
23918
|
-
const target = await
|
|
24627
|
+
const target = await realpath9(requested);
|
|
23919
24628
|
signal.throwIfAborted();
|
|
23920
|
-
if (!
|
|
24629
|
+
if (!isAbsolute9(target) || normalize3(target) !== target) {
|
|
23921
24630
|
return null;
|
|
23922
24631
|
}
|
|
23923
24632
|
return target;
|
|
@@ -23963,7 +24672,7 @@ async function readAuthorizedRaster(uri, roots, signal) {
|
|
|
23963
24672
|
if (target === null) {
|
|
23964
24673
|
return null;
|
|
23965
24674
|
}
|
|
23966
|
-
const targetMetadata = await
|
|
24675
|
+
const targetMetadata = await stat8(target);
|
|
23967
24676
|
signal.throwIfAborted();
|
|
23968
24677
|
if (!targetMetadata.isFile() || targetMetadata.size < 1 || targetMetadata.size > MAX_LOCAL_RASTER_BYTES) {
|
|
23969
24678
|
return null;
|
|
@@ -23974,7 +24683,7 @@ async function readAuthorizedRaster(uri, roots, signal) {
|
|
|
23974
24683
|
if (directory === null) {
|
|
23975
24684
|
continue;
|
|
23976
24685
|
}
|
|
23977
|
-
const metadata = await
|
|
24686
|
+
const metadata = await stat8(directory);
|
|
23978
24687
|
signal.throwIfAborted();
|
|
23979
24688
|
if (metadata.isDirectory()) {
|
|
23980
24689
|
authorizedRoots.push(directory);
|
|
@@ -23992,7 +24701,7 @@ async function readAuthorizedRaster(uri, roots, signal) {
|
|
|
23992
24701
|
if (mimeType === null) {
|
|
23993
24702
|
return null;
|
|
23994
24703
|
}
|
|
23995
|
-
return new LocalRasterImage(
|
|
24704
|
+
return new LocalRasterImage(basename3(target) || "Image", mimeType, bytes);
|
|
23996
24705
|
}
|
|
23997
24706
|
|
|
23998
24707
|
// src/native-sessions/history-images.ts
|
|
@@ -25067,8 +25776,8 @@ var AcpHistoryReplay = class {
|
|
|
25067
25776
|
|
|
25068
25777
|
// src/acp/session-store.ts
|
|
25069
25778
|
import { mkdirSync as mkdirSync2 } from "node:fs";
|
|
25070
|
-
import { dirname as
|
|
25071
|
-
import { DatabaseSync as
|
|
25779
|
+
import { dirname as dirname6 } from "node:path";
|
|
25780
|
+
import { DatabaseSync as DatabaseSync3 } from "node:sqlite";
|
|
25072
25781
|
var AcpStoredSession = class {
|
|
25073
25782
|
additionalDirectories;
|
|
25074
25783
|
agentId;
|
|
@@ -25170,8 +25879,8 @@ var MemoryAcpSessionStore = class {
|
|
|
25170
25879
|
var SqliteAcpSessionStore = class {
|
|
25171
25880
|
#database;
|
|
25172
25881
|
constructor(path2) {
|
|
25173
|
-
mkdirSync2(
|
|
25174
|
-
this.#database = new
|
|
25882
|
+
mkdirSync2(dirname6(path2), { recursive: true });
|
|
25883
|
+
this.#database = new DatabaseSync3(path2);
|
|
25175
25884
|
this.#database.exec(`
|
|
25176
25885
|
CREATE TABLE IF NOT EXISTS acp_deleted_sessions (session_id TEXT PRIMARY KEY) STRICT;
|
|
25177
25886
|
CREATE TABLE IF NOT EXISTS acp_sessions (
|
|
@@ -28040,7 +28749,7 @@ var FixedInstallationExecutor = class {
|
|
|
28040
28749
|
};
|
|
28041
28750
|
|
|
28042
28751
|
// src/artifacts/runtime-artifact-uploader.ts
|
|
28043
|
-
import { createHash as
|
|
28752
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
28044
28753
|
|
|
28045
28754
|
// ../mobius-sdk/src/errors.ts
|
|
28046
28755
|
var MobiusError = class extends Error {
|
|
@@ -28367,7 +29076,7 @@ async function uploadRuntimeArtifact(config2, sessionId, image, signal) {
|
|
|
28367
29076
|
image.name,
|
|
28368
29077
|
image.mimeType,
|
|
28369
29078
|
image.bytes.byteLength,
|
|
28370
|
-
|
|
29079
|
+
createHash3("sha256").update(image.bytes).digest("hex")
|
|
28371
29080
|
);
|
|
28372
29081
|
} catch (cause) {
|
|
28373
29082
|
translateSdkError(cause);
|
|
@@ -28435,508 +29144,6 @@ async function uploadRuntimeArtifact(config2, sessionId, image, signal) {
|
|
|
28435
29144
|
}
|
|
28436
29145
|
}
|
|
28437
29146
|
|
|
28438
|
-
// src/authorization/local-authorization-error.ts
|
|
28439
|
-
var LocalAuthorizationError = class extends Error {
|
|
28440
|
-
code = "local_authorization_failed";
|
|
28441
|
-
constructor(message, options = {}) {
|
|
28442
|
-
super(message, options);
|
|
28443
|
-
this.name = "LocalAuthorizationError";
|
|
28444
|
-
}
|
|
28445
|
-
};
|
|
28446
|
-
|
|
28447
|
-
// src/authorization/workspace-directory.ts
|
|
28448
|
-
import { readdir, realpath as realpath8, stat as stat7 } from "node:fs/promises";
|
|
28449
|
-
import { homedir as homedir2 } from "node:os";
|
|
28450
|
-
import { basename as basename3, dirname as dirname5, isAbsolute as isAbsolute9, join as join16, normalize as normalize3, relative as relative5 } from "node:path";
|
|
28451
|
-
var WorkspaceDirectoryListing = class {
|
|
28452
|
-
directoryId;
|
|
28453
|
-
directoryLabel;
|
|
28454
|
-
entries;
|
|
28455
|
-
parentDirectoryId;
|
|
28456
|
-
constructor(directoryId2, directoryLabel, entries, parentDirectoryId) {
|
|
28457
|
-
this.directoryId = directoryId2;
|
|
28458
|
-
this.directoryLabel = directoryLabel;
|
|
28459
|
-
this.entries = entries;
|
|
28460
|
-
this.parentDirectoryId = parentDirectoryId;
|
|
28461
|
-
}
|
|
28462
|
-
};
|
|
28463
|
-
function encodePath(path2) {
|
|
28464
|
-
const encoded = Buffer.from(path2, "utf8").toString("base64url");
|
|
28465
|
-
if (encoded.length === 0 || encoded.length > 2e3) {
|
|
28466
|
-
throw new LocalAuthorizationError("The local directory path is too long to select");
|
|
28467
|
-
}
|
|
28468
|
-
return `path-${encoded}`;
|
|
28469
|
-
}
|
|
28470
|
-
function decodePath(value, prefix) {
|
|
28471
|
-
const encoded = value.slice(prefix.length);
|
|
28472
|
-
if (encoded.length === 0 || encoded.length > 2e3 || !/^[a-zA-Z0-9_-]+$/.test(encoded)) {
|
|
28473
|
-
throw new LocalAuthorizationError("The directory identifier is invalid");
|
|
28474
|
-
}
|
|
28475
|
-
const decoded = Buffer.from(encoded, "base64url").toString("utf8");
|
|
28476
|
-
if (Buffer.from(decoded, "utf8").toString("base64url") !== encoded || decoded.includes("\0") || normalize3(decoded) !== decoded) {
|
|
28477
|
-
throw new LocalAuthorizationError("The directory identifier is invalid");
|
|
28478
|
-
}
|
|
28479
|
-
return decoded;
|
|
28480
|
-
}
|
|
28481
|
-
var WorkspaceDirectoryService = class {
|
|
28482
|
-
#bindingId;
|
|
28483
|
-
#defaultWorkspace;
|
|
28484
|
-
constructor(bindingId, defaultWorkspace) {
|
|
28485
|
-
this.#bindingId = bindingId;
|
|
28486
|
-
this.#defaultWorkspace = defaultWorkspace;
|
|
28487
|
-
}
|
|
28488
|
-
authorizeMachine = (bindingId) => {
|
|
28489
|
-
if (bindingId !== this.#bindingId) {
|
|
28490
|
-
throw new LocalAuthorizationError("The runtime binding is not authorized");
|
|
28491
|
-
}
|
|
28492
|
-
};
|
|
28493
|
-
requestedPath = (id) => {
|
|
28494
|
-
if (id === "default") {
|
|
28495
|
-
return this.#defaultWorkspace;
|
|
28496
|
-
}
|
|
28497
|
-
if (id === "home") {
|
|
28498
|
-
return homedir2();
|
|
28499
|
-
}
|
|
28500
|
-
if (id.startsWith("path-")) {
|
|
28501
|
-
const path2 = decodePath(id, "path-");
|
|
28502
|
-
if (isAbsolute9(path2)) {
|
|
28503
|
-
return path2;
|
|
28504
|
-
}
|
|
28505
|
-
}
|
|
28506
|
-
throw new LocalAuthorizationError("The directory identifier is invalid");
|
|
28507
|
-
};
|
|
28508
|
-
resolvePath = async (requested, signal) => {
|
|
28509
|
-
signal.throwIfAborted();
|
|
28510
|
-
if (!isAbsolute9(requested) || requested.includes("\0") || normalize3(requested) !== requested) {
|
|
28511
|
-
throw new LocalAuthorizationError("The local directory path is invalid");
|
|
28512
|
-
}
|
|
28513
|
-
try {
|
|
28514
|
-
const target = await realpath8(requested);
|
|
28515
|
-
signal.throwIfAborted();
|
|
28516
|
-
const metadata = await stat7(target);
|
|
28517
|
-
signal.throwIfAborted();
|
|
28518
|
-
if (!isAbsolute9(target) || normalize3(target) !== target || !metadata.isDirectory()) {
|
|
28519
|
-
throw new LocalAuthorizationError("The selected location is not a directory");
|
|
28520
|
-
}
|
|
28521
|
-
return target;
|
|
28522
|
-
} catch (cause) {
|
|
28523
|
-
signal.throwIfAborted();
|
|
28524
|
-
if (cause instanceof LocalAuthorizationError) {
|
|
28525
|
-
throw cause;
|
|
28526
|
-
}
|
|
28527
|
-
throw new LocalAuthorizationError("This directory is missing or unavailable to your user account", {
|
|
28528
|
-
cause
|
|
28529
|
-
});
|
|
28530
|
-
}
|
|
28531
|
-
};
|
|
28532
|
-
label = (path2) => {
|
|
28533
|
-
const suffix = relative5(homedir2(), path2);
|
|
28534
|
-
return suffix === "" ? "~" : !isAbsolute9(suffix) && suffix !== ".." && !suffix.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) ? `~/${suffix.replaceAll("\\", "/")}` : path2;
|
|
28535
|
-
};
|
|
28536
|
-
identify = async (bindingId, requestedPath, signal) => {
|
|
28537
|
-
this.authorizeMachine(bindingId);
|
|
28538
|
-
const path2 = await this.resolvePath(requestedPath, signal);
|
|
28539
|
-
return {
|
|
28540
|
-
path: path2,
|
|
28541
|
-
directoryId: encodePath(path2),
|
|
28542
|
-
name: basename3(path2) || path2,
|
|
28543
|
-
label: this.label(path2)
|
|
28544
|
-
};
|
|
28545
|
-
};
|
|
28546
|
-
resolve = async (bindingId, id, signal) => {
|
|
28547
|
-
this.authorizeMachine(bindingId);
|
|
28548
|
-
return await this.resolvePath(this.requestedPath(id), signal);
|
|
28549
|
-
};
|
|
28550
|
-
list = async (bindingId, id, signal) => {
|
|
28551
|
-
const target = await this.resolve(bindingId, id, signal);
|
|
28552
|
-
try {
|
|
28553
|
-
const children = await readdir(target, { withFileTypes: true });
|
|
28554
|
-
signal.throwIfAborted();
|
|
28555
|
-
const entries = [];
|
|
28556
|
-
for (const child of children) {
|
|
28557
|
-
if (entries.length === 500) {
|
|
28558
|
-
break;
|
|
28559
|
-
}
|
|
28560
|
-
if (child.isFile() || child.isBlockDevice() || child.isCharacterDevice() || child.isFIFO() || child.isSocket()) {
|
|
28561
|
-
continue;
|
|
28562
|
-
}
|
|
28563
|
-
try {
|
|
28564
|
-
const path2 = await this.resolvePath(join16(target, child.name), signal);
|
|
28565
|
-
entries.push({ directoryId: encodePath(path2), name: child.name });
|
|
28566
|
-
} catch (cause) {
|
|
28567
|
-
signal.throwIfAborted();
|
|
28568
|
-
if (!(cause instanceof LocalAuthorizationError)) {
|
|
28569
|
-
throw cause;
|
|
28570
|
-
}
|
|
28571
|
-
}
|
|
28572
|
-
}
|
|
28573
|
-
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
28574
|
-
const parent = dirname5(target);
|
|
28575
|
-
return new WorkspaceDirectoryListing(
|
|
28576
|
-
id,
|
|
28577
|
-
this.label(target),
|
|
28578
|
-
entries,
|
|
28579
|
-
parent === target ? "" : encodePath(parent)
|
|
28580
|
-
);
|
|
28581
|
-
} catch (cause) {
|
|
28582
|
-
signal.throwIfAborted();
|
|
28583
|
-
if (cause instanceof LocalAuthorizationError) {
|
|
28584
|
-
throw cause;
|
|
28585
|
-
}
|
|
28586
|
-
throw new LocalAuthorizationError("This directory cannot be browsed by your user account", { cause });
|
|
28587
|
-
}
|
|
28588
|
-
};
|
|
28589
|
-
resolveMany = async (bindingId, ids, signal) => {
|
|
28590
|
-
const paths = /* @__PURE__ */ new Set();
|
|
28591
|
-
for (const id of ids) {
|
|
28592
|
-
paths.add(await this.resolve(bindingId, id, signal));
|
|
28593
|
-
}
|
|
28594
|
-
return [...paths];
|
|
28595
|
-
};
|
|
28596
|
-
};
|
|
28597
|
-
|
|
28598
|
-
// src/bots/local-bot-grant-store.ts
|
|
28599
|
-
import { createHash as createHash3, randomUUID as randomUUID3 } from "node:crypto";
|
|
28600
|
-
import { stat as stat8 } from "node:fs/promises";
|
|
28601
|
-
import { DatabaseSync as DatabaseSync3 } from "node:sqlite";
|
|
28602
|
-
var LOCAL_BOT_GRANT_LIFETIME_MILLISECONDS = 90 * 24 * 60 * 60 * 1e3;
|
|
28603
|
-
var LocalBotAuthorizationError = class extends Error {
|
|
28604
|
-
code;
|
|
28605
|
-
constructor(code, message, options = {}) {
|
|
28606
|
-
super(message, options);
|
|
28607
|
-
this.code = code;
|
|
28608
|
-
this.name = "LocalBotAuthorizationError";
|
|
28609
|
-
}
|
|
28610
|
-
};
|
|
28611
|
-
var LocalBotGrant = class {
|
|
28612
|
-
agent;
|
|
28613
|
-
agentIdentityDigest;
|
|
28614
|
-
allowedDeliveryTargetIds;
|
|
28615
|
-
behaviorDigest;
|
|
28616
|
-
behaviorRevisionId;
|
|
28617
|
-
bindingId;
|
|
28618
|
-
bindingRevision;
|
|
28619
|
-
botId;
|
|
28620
|
-
expiresAt;
|
|
28621
|
-
localGrantId;
|
|
28622
|
-
revocationEpoch;
|
|
28623
|
-
workspaceIdentityDigest;
|
|
28624
|
-
workspacePath;
|
|
28625
|
-
workspaceRef;
|
|
28626
|
-
constructor(localGrantId, botId, behaviorRevisionId, behaviorDigest, bindingId, bindingRevision, agent, agentIdentityDigest, workspaceRef, workspacePath, workspaceIdentityDigest, allowedDeliveryTargetIds, expiresAt, revocationEpoch) {
|
|
28627
|
-
this.localGrantId = localGrantId;
|
|
28628
|
-
this.botId = botId;
|
|
28629
|
-
this.behaviorRevisionId = behaviorRevisionId;
|
|
28630
|
-
this.behaviorDigest = behaviorDigest;
|
|
28631
|
-
this.bindingId = bindingId;
|
|
28632
|
-
this.bindingRevision = bindingRevision;
|
|
28633
|
-
this.agent = agent;
|
|
28634
|
-
this.agentIdentityDigest = agentIdentityDigest;
|
|
28635
|
-
this.workspaceRef = workspaceRef;
|
|
28636
|
-
this.workspacePath = workspacePath;
|
|
28637
|
-
this.workspaceIdentityDigest = workspaceIdentityDigest;
|
|
28638
|
-
this.allowedDeliveryTargetIds = Object.freeze([...allowedDeliveryTargetIds]);
|
|
28639
|
-
this.expiresAt = expiresAt;
|
|
28640
|
-
this.revocationEpoch = revocationEpoch;
|
|
28641
|
-
}
|
|
28642
|
-
};
|
|
28643
|
-
function digest(value) {
|
|
28644
|
-
return createHash3("sha256").update(value, "utf8").digest("hex");
|
|
28645
|
-
}
|
|
28646
|
-
function agentDigest(agent) {
|
|
28647
|
-
return digest(
|
|
28648
|
-
JSON.stringify({
|
|
28649
|
-
adapter: agent.adapterIdentity,
|
|
28650
|
-
agent: agent.agentIdentity,
|
|
28651
|
-
agentVersion: agent.agentVersion,
|
|
28652
|
-
arguments: agent.arguments,
|
|
28653
|
-
id: agent.id
|
|
28654
|
-
})
|
|
28655
|
-
);
|
|
28656
|
-
}
|
|
28657
|
-
async function workspaceDigest(path2) {
|
|
28658
|
-
const metadata = await stat8(path2);
|
|
28659
|
-
if (!metadata.isDirectory()) {
|
|
28660
|
-
throw new LocalBotAuthorizationError("binding_incompatible", "The Bot workspace is not a directory");
|
|
28661
|
-
}
|
|
28662
|
-
return digest(
|
|
28663
|
-
JSON.stringify({
|
|
28664
|
-
changedAtMilliseconds: metadata.ctimeMs,
|
|
28665
|
-
device: metadata.dev,
|
|
28666
|
-
inode: metadata.ino,
|
|
28667
|
-
path: path2
|
|
28668
|
-
})
|
|
28669
|
-
);
|
|
28670
|
-
}
|
|
28671
|
-
function exactTargets(left, right) {
|
|
28672
|
-
if (left.length !== right.length) {
|
|
28673
|
-
return false;
|
|
28674
|
-
}
|
|
28675
|
-
const expected = [...left].sort();
|
|
28676
|
-
const actual = [...right].sort();
|
|
28677
|
-
return expected.every((target, index) => target === actual[index]);
|
|
28678
|
-
}
|
|
28679
|
-
function rowString(row, key) {
|
|
28680
|
-
const value = Reflect.get(row, key);
|
|
28681
|
-
if (typeof value !== "string") {
|
|
28682
|
-
throw new LocalBotAuthorizationError("local_grant_invalid", "The local Bot grant is invalid");
|
|
28683
|
-
}
|
|
28684
|
-
return value;
|
|
28685
|
-
}
|
|
28686
|
-
function rowInteger(row, key) {
|
|
28687
|
-
const value = Reflect.get(row, key);
|
|
28688
|
-
if (typeof value !== "number" || !Number.isSafeInteger(value)) {
|
|
28689
|
-
throw new LocalBotAuthorizationError("local_grant_invalid", "The local Bot grant is invalid");
|
|
28690
|
-
}
|
|
28691
|
-
return value;
|
|
28692
|
-
}
|
|
28693
|
-
function parseTargets(encoded) {
|
|
28694
|
-
let value;
|
|
28695
|
-
try {
|
|
28696
|
-
value = JSON.parse(encoded);
|
|
28697
|
-
} catch (cause) {
|
|
28698
|
-
throw new LocalBotAuthorizationError("local_grant_invalid", "The local Bot grant is invalid", { cause });
|
|
28699
|
-
}
|
|
28700
|
-
if (!Array.isArray(value) || value.some((target) => typeof target !== "string")) {
|
|
28701
|
-
throw new LocalBotAuthorizationError("local_grant_invalid", "The local Bot grant is invalid");
|
|
28702
|
-
}
|
|
28703
|
-
return value.filter((target) => typeof target === "string");
|
|
28704
|
-
}
|
|
28705
|
-
var LocalBotGrantStore = class {
|
|
28706
|
-
#database;
|
|
28707
|
-
constructor(path2) {
|
|
28708
|
-
this.#database = new DatabaseSync3(path2);
|
|
28709
|
-
this.#database.exec(`
|
|
28710
|
-
CREATE TABLE IF NOT EXISTS local_bot_grants (
|
|
28711
|
-
local_grant_id TEXT PRIMARY KEY,
|
|
28712
|
-
bot_id TEXT NOT NULL,
|
|
28713
|
-
behavior_revision_id TEXT NOT NULL,
|
|
28714
|
-
behavior_digest TEXT NOT NULL,
|
|
28715
|
-
binding_id TEXT NOT NULL UNIQUE,
|
|
28716
|
-
binding_revision INTEGER NOT NULL,
|
|
28717
|
-
agent_ref TEXT NOT NULL,
|
|
28718
|
-
agent_identity_digest TEXT NOT NULL,
|
|
28719
|
-
workspace_ref TEXT NOT NULL,
|
|
28720
|
-
workspace_path TEXT NOT NULL,
|
|
28721
|
-
workspace_identity_digest TEXT NOT NULL,
|
|
28722
|
-
allowed_delivery_target_ids_json TEXT NOT NULL,
|
|
28723
|
-
unattended_policy_json TEXT NOT NULL,
|
|
28724
|
-
created_at INTEGER NOT NULL,
|
|
28725
|
-
expires_at INTEGER NOT NULL,
|
|
28726
|
-
revocation_epoch INTEGER NOT NULL DEFAULT 0,
|
|
28727
|
-
revoked_at INTEGER
|
|
28728
|
-
) STRICT;
|
|
28729
|
-
CREATE TABLE IF NOT EXISTS local_bot_binding_resolutions (
|
|
28730
|
-
request_id TEXT PRIMARY KEY,
|
|
28731
|
-
binding_id TEXT NOT NULL,
|
|
28732
|
-
local_grant_id TEXT NOT NULL,
|
|
28733
|
-
resolved_at INTEGER NOT NULL,
|
|
28734
|
-
result TEXT NOT NULL CHECK (result IN ('approved', 'unavailable')),
|
|
28735
|
-
error_code TEXT,
|
|
28736
|
-
error_message TEXT
|
|
28737
|
-
) STRICT;
|
|
28738
|
-
`);
|
|
28739
|
-
}
|
|
28740
|
-
approve = async (request, catalog, directories, bindingAuthorityId, now, signal) => {
|
|
28741
|
-
signal.throwIfAborted();
|
|
28742
|
-
const existing = this.#database.prepare(
|
|
28743
|
-
`SELECT grants.* FROM local_bot_binding_resolutions AS resolutions
|
|
28744
|
-
INNER JOIN local_bot_grants AS grants
|
|
28745
|
-
ON grants.local_grant_id = resolutions.local_grant_id
|
|
28746
|
-
WHERE resolutions.request_id = ? AND resolutions.binding_id = ?
|
|
28747
|
-
AND resolutions.result = 'approved'`
|
|
28748
|
-
).get(request.requestId, request.bindingId);
|
|
28749
|
-
if (existing !== void 0) {
|
|
28750
|
-
return new BotExecutionBindingApprovedEvent(
|
|
28751
|
-
request.requestId,
|
|
28752
|
-
request.botId,
|
|
28753
|
-
request.behaviorRevisionId,
|
|
28754
|
-
request.bindingId,
|
|
28755
|
-
request.bindingRevision,
|
|
28756
|
-
rowString(existing, "local_grant_id"),
|
|
28757
|
-
rowString(existing, "agent_identity_digest"),
|
|
28758
|
-
rowString(existing, "workspace_identity_digest")
|
|
28759
|
-
);
|
|
28760
|
-
}
|
|
28761
|
-
const resolved = await this.resolveProposal(request, catalog, directories, bindingAuthorityId, signal);
|
|
28762
|
-
const localGrantId = `lbg_${randomUUID3()}`;
|
|
28763
|
-
const expiresAt = now + LOCAL_BOT_GRANT_LIFETIME_MILLISECONDS;
|
|
28764
|
-
this.#database.exec("BEGIN IMMEDIATE");
|
|
28765
|
-
try {
|
|
28766
|
-
this.#database.prepare(
|
|
28767
|
-
`INSERT INTO local_bot_grants (
|
|
28768
|
-
local_grant_id, bot_id, behavior_revision_id, behavior_digest,
|
|
28769
|
-
binding_id, binding_revision, agent_ref,
|
|
28770
|
-
agent_identity_digest, workspace_ref, workspace_path,
|
|
28771
|
-
workspace_identity_digest, allowed_delivery_target_ids_json,
|
|
28772
|
-
unattended_policy_json, created_at, expires_at
|
|
28773
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
28774
|
-
).run(
|
|
28775
|
-
localGrantId,
|
|
28776
|
-
request.botId,
|
|
28777
|
-
request.behaviorRevisionId,
|
|
28778
|
-
request.behaviorDigest,
|
|
28779
|
-
request.bindingId,
|
|
28780
|
-
request.bindingRevision,
|
|
28781
|
-
request.agentRef,
|
|
28782
|
-
resolved.agentIdentityDigest,
|
|
28783
|
-
request.workspaceRef,
|
|
28784
|
-
resolved.workspacePath,
|
|
28785
|
-
resolved.workspaceIdentityDigest,
|
|
28786
|
-
JSON.stringify(request.allowedDeliveryTargets.map((target) => target.deliveryTargetId)),
|
|
28787
|
-
JSON.stringify({ localSideEffects: "owner-approval-required" }),
|
|
28788
|
-
now,
|
|
28789
|
-
expiresAt
|
|
28790
|
-
);
|
|
28791
|
-
this.#database.prepare(
|
|
28792
|
-
`INSERT INTO local_bot_binding_resolutions (
|
|
28793
|
-
request_id, binding_id, local_grant_id, resolved_at, result
|
|
28794
|
-
) VALUES (?, ?, ?, ?, 'approved')`
|
|
28795
|
-
).run(request.requestId, request.bindingId, localGrantId, now);
|
|
28796
|
-
this.#database.exec("COMMIT");
|
|
28797
|
-
} catch (cause) {
|
|
28798
|
-
this.#database.exec("ROLLBACK");
|
|
28799
|
-
throw cause;
|
|
28800
|
-
}
|
|
28801
|
-
signal.throwIfAborted();
|
|
28802
|
-
return new BotExecutionBindingApprovedEvent(
|
|
28803
|
-
request.requestId,
|
|
28804
|
-
request.botId,
|
|
28805
|
-
request.behaviorRevisionId,
|
|
28806
|
-
request.bindingId,
|
|
28807
|
-
request.bindingRevision,
|
|
28808
|
-
localGrantId,
|
|
28809
|
-
resolved.agentIdentityDigest,
|
|
28810
|
-
resolved.workspaceIdentityDigest
|
|
28811
|
-
);
|
|
28812
|
-
};
|
|
28813
|
-
resolveExecution = async (command, catalog, directories, bindingAuthorityId, now, signal) => {
|
|
28814
|
-
signal.throwIfAborted();
|
|
28815
|
-
const row = this.#database.prepare(
|
|
28816
|
-
`SELECT * FROM local_bot_grants
|
|
28817
|
-
WHERE local_grant_id = ? AND bot_id = ?
|
|
28818
|
-
AND behavior_revision_id = ? AND behavior_digest = ?
|
|
28819
|
-
AND binding_id = ? AND binding_revision = ?
|
|
28820
|
-
AND agent_ref = ?
|
|
28821
|
-
AND revoked_at IS NULL AND expires_at > ?`
|
|
28822
|
-
).get(
|
|
28823
|
-
command.localGrantId,
|
|
28824
|
-
command.botId,
|
|
28825
|
-
command.behaviorRevisionId,
|
|
28826
|
-
command.behaviorDigest,
|
|
28827
|
-
command.bindingId,
|
|
28828
|
-
command.bindingRevision,
|
|
28829
|
-
command.agentId,
|
|
28830
|
-
now
|
|
28831
|
-
);
|
|
28832
|
-
if (row === void 0) {
|
|
28833
|
-
throw new LocalBotAuthorizationError(
|
|
28834
|
-
"binding_not_approved",
|
|
28835
|
-
"The Bot execution binding is not locally approved"
|
|
28836
|
-
);
|
|
28837
|
-
}
|
|
28838
|
-
const approvedTargets = parseTargets(rowString(row, "allowed_delivery_target_ids_json"));
|
|
28839
|
-
const requestedTargets = command.allowedDeliveryTargets.map((target) => target.deliveryTargetId);
|
|
28840
|
-
if (!exactTargets(approvedTargets, requestedTargets)) {
|
|
28841
|
-
throw new LocalBotAuthorizationError(
|
|
28842
|
-
"binding_not_approved",
|
|
28843
|
-
"The Bot execution target policy changed after local approval"
|
|
28844
|
-
);
|
|
28845
|
-
}
|
|
28846
|
-
const agent = catalog.find(command.agentId);
|
|
28847
|
-
const [adapterUnchanged, agentUnchanged] = await Promise.all([
|
|
28848
|
-
revalidateExecutable(agent.adapterIdentity),
|
|
28849
|
-
revalidateExecutable(agent.agentIdentity)
|
|
28850
|
-
]);
|
|
28851
|
-
signal.throwIfAborted();
|
|
28852
|
-
const workspaceRef = rowString(row, "workspace_ref");
|
|
28853
|
-
const workspacePath = await directories.resolve(bindingAuthorityId, workspaceRef, signal);
|
|
28854
|
-
const currentAgentDigest = agentDigest(agent);
|
|
28855
|
-
const currentWorkspaceDigest = await workspaceDigest(workspacePath);
|
|
28856
|
-
signal.throwIfAborted();
|
|
28857
|
-
if (!adapterUnchanged || !agentUnchanged || currentAgentDigest !== rowString(row, "agent_identity_digest") || currentWorkspaceDigest !== rowString(row, "workspace_identity_digest") || workspacePath !== rowString(row, "workspace_path")) {
|
|
28858
|
-
this.revoke(rowString(row, "local_grant_id"), now);
|
|
28859
|
-
throw new LocalBotAuthorizationError(
|
|
28860
|
-
"binding_not_approved",
|
|
28861
|
-
"The Bot execution target changed and requires local approval"
|
|
28862
|
-
);
|
|
28863
|
-
}
|
|
28864
|
-
if (command.behaviorInstructions.length > 0 && !agent.capabilities.embeddedContextPrompt) {
|
|
28865
|
-
throw new LocalBotAuthorizationError(
|
|
28866
|
-
"binding_incompatible",
|
|
28867
|
-
"The local Agent cannot apply the approved Bot behavior"
|
|
28868
|
-
);
|
|
28869
|
-
}
|
|
28870
|
-
return new LocalBotGrant(
|
|
28871
|
-
rowString(row, "local_grant_id"),
|
|
28872
|
-
command.botId,
|
|
28873
|
-
command.behaviorRevisionId,
|
|
28874
|
-
command.behaviorDigest,
|
|
28875
|
-
command.bindingId,
|
|
28876
|
-
command.bindingRevision,
|
|
28877
|
-
agent,
|
|
28878
|
-
currentAgentDigest,
|
|
28879
|
-
workspaceRef,
|
|
28880
|
-
workspacePath,
|
|
28881
|
-
currentWorkspaceDigest,
|
|
28882
|
-
approvedTargets,
|
|
28883
|
-
rowInteger(row, "expires_at"),
|
|
28884
|
-
rowInteger(row, "revocation_epoch")
|
|
28885
|
-
);
|
|
28886
|
-
};
|
|
28887
|
-
recordUnavailable = (request, code, message, now) => {
|
|
28888
|
-
this.#database.prepare(
|
|
28889
|
-
`INSERT OR IGNORE INTO local_bot_binding_resolutions (
|
|
28890
|
-
request_id, binding_id, local_grant_id, resolved_at,
|
|
28891
|
-
result, error_code, error_message
|
|
28892
|
-
) VALUES (?, ?, '', ?, 'unavailable', ?, ?)`
|
|
28893
|
-
).run(request.requestId, request.bindingId, now, code, message);
|
|
28894
|
-
};
|
|
28895
|
-
close = () => {
|
|
28896
|
-
this.#database.close();
|
|
28897
|
-
};
|
|
28898
|
-
resolveProposal = async (request, catalog, directories, bindingAuthorityId, signal) => {
|
|
28899
|
-
const agent = catalog.find(request.agentRef);
|
|
28900
|
-
if (request.requiredCapabilities.includes("embedded-context") && !agent.capabilities.embeddedContextPrompt) {
|
|
28901
|
-
throw new LocalBotAuthorizationError(
|
|
28902
|
-
"binding_incompatible",
|
|
28903
|
-
"The local Agent cannot apply embedded Bot context"
|
|
28904
|
-
);
|
|
28905
|
-
}
|
|
28906
|
-
if (request.requiredCapabilities.some((capability) => capability !== "embedded-context")) {
|
|
28907
|
-
throw new LocalBotAuthorizationError(
|
|
28908
|
-
"binding_incompatible",
|
|
28909
|
-
"The Bot requires an unsupported local Agent capability"
|
|
28910
|
-
);
|
|
28911
|
-
}
|
|
28912
|
-
const [adapterUnchanged, agentUnchanged] = await Promise.all([
|
|
28913
|
-
revalidateExecutable(agent.adapterIdentity),
|
|
28914
|
-
revalidateExecutable(agent.agentIdentity)
|
|
28915
|
-
]);
|
|
28916
|
-
signal.throwIfAborted();
|
|
28917
|
-
if (!adapterUnchanged || !agentUnchanged) {
|
|
28918
|
-
throw new LocalBotAuthorizationError(
|
|
28919
|
-
"binding_incompatible",
|
|
28920
|
-
"The local Agent executable changed before approval"
|
|
28921
|
-
);
|
|
28922
|
-
}
|
|
28923
|
-
const workspacePath = await directories.resolve(bindingAuthorityId, request.workspaceRef, signal);
|
|
28924
|
-
return {
|
|
28925
|
-
agent,
|
|
28926
|
-
agentIdentityDigest: agentDigest(agent),
|
|
28927
|
-
workspaceIdentityDigest: await workspaceDigest(workspacePath),
|
|
28928
|
-
workspacePath
|
|
28929
|
-
};
|
|
28930
|
-
};
|
|
28931
|
-
revoke = (localGrantId, now) => {
|
|
28932
|
-
this.#database.prepare(
|
|
28933
|
-
`UPDATE local_bot_grants
|
|
28934
|
-
SET revoked_at = COALESCE(revoked_at, ?), revocation_epoch = revocation_epoch + 1
|
|
28935
|
-
WHERE local_grant_id = ? AND revoked_at IS NULL`
|
|
28936
|
-
).run(now, localGrantId);
|
|
28937
|
-
};
|
|
28938
|
-
};
|
|
28939
|
-
|
|
28940
29147
|
// src/bots/bot-context-applicator.ts
|
|
28941
29148
|
var AppliedBotContext = class {
|
|
28942
29149
|
resources;
|
|
@@ -29072,7 +29279,7 @@ var BotExecutionContextRegistry = class {
|
|
|
29072
29279
|
// src/bots/bot-outbound-ipc.ts
|
|
29073
29280
|
import { chmod as chmod2, mkdir as mkdir4, unlink } from "node:fs/promises";
|
|
29074
29281
|
import { createServer } from "node:net";
|
|
29075
|
-
import { dirname as
|
|
29282
|
+
import { dirname as dirname7, isAbsolute as isAbsolute10 } from "node:path";
|
|
29076
29283
|
import { fetch as fetch5 } from "undici";
|
|
29077
29284
|
var BOT_IPC_REQUEST_LIMIT = 32 * 1024;
|
|
29078
29285
|
var BOT_IPC_TIMEOUT_MILLISECONDS2 = 2e4;
|
|
@@ -29185,7 +29392,7 @@ var BotOutboundIpcServer = class {
|
|
|
29185
29392
|
listen = async (signal) => {
|
|
29186
29393
|
signal.throwIfAborted();
|
|
29187
29394
|
if (this.#usesFilesystemSocket) {
|
|
29188
|
-
await mkdir4(
|
|
29395
|
+
await mkdir4(dirname7(this.#socketPath), { mode: 448, recursive: true });
|
|
29189
29396
|
await unlink(this.#socketPath).catch((error61) => {
|
|
29190
29397
|
if (!isRecord12(error61) || error61["code"] !== "ENOENT") {
|
|
29191
29398
|
throw error61;
|
|
@@ -30491,7 +30698,7 @@ var RelayConnectionLiveness = class {
|
|
|
30491
30698
|
|
|
30492
30699
|
// src/relay-connection/session-event-outbox.ts
|
|
30493
30700
|
import { mkdirSync as mkdirSync3 } from "node:fs";
|
|
30494
|
-
import { dirname as
|
|
30701
|
+
import { dirname as dirname8 } from "node:path";
|
|
30495
30702
|
import { DatabaseSync as DatabaseSync4 } from "node:sqlite";
|
|
30496
30703
|
var DEFAULT_OUTBOX_LIMITS = {
|
|
30497
30704
|
criticalEventBytes: 136 * 1024 * 1024,
|
|
@@ -30559,7 +30766,7 @@ var SessionEventOutbox = class {
|
|
|
30559
30766
|
constructor(path2, limits = DEFAULT_OUTBOX_LIMITS) {
|
|
30560
30767
|
validateLimits(limits);
|
|
30561
30768
|
this.#limits = limits;
|
|
30562
|
-
mkdirSync3(
|
|
30769
|
+
mkdirSync3(dirname8(path2), { recursive: true });
|
|
30563
30770
|
this.#database = new DatabaseSync4(path2);
|
|
30564
30771
|
this.#database.exec(`
|
|
30565
30772
|
CREATE TABLE IF NOT EXISTS relay_session_event_outbox (
|
|
@@ -31110,7 +31317,7 @@ function botIpcSocketPath() {
|
|
|
31110
31317
|
if (process.platform === "win32") {
|
|
31111
31318
|
return `\\\\.\\pipe\\mobius-bot-${randomUUID6()}`;
|
|
31112
31319
|
}
|
|
31113
|
-
return
|
|
31320
|
+
return join18("/tmp", `mobius-bot-${randomUUID6()}.sock`);
|
|
31114
31321
|
}
|
|
31115
31322
|
var DaemonAcpObserver = class {
|
|
31116
31323
|
#config;
|
|
@@ -31780,7 +31987,7 @@ async function executeSessionConfigure(publisher, socket, command, config2, dire
|
|
|
31780
31987
|
reportOperationFailure(socket, command.requestId, command.sessionId, error61);
|
|
31781
31988
|
}
|
|
31782
31989
|
}
|
|
31783
|
-
async function executePrompt(outbox, publisher, command, config2, directories, audit, sessions, attemptId, timer, signal) {
|
|
31990
|
+
async function executePrompt(outbox, publisher, command, config2, directories, audit, sessions, attemptId, activateAttempt, timer, signal) {
|
|
31784
31991
|
const activities = new CoalescingDaemonActivityPublisher(timer, (activity) => {
|
|
31785
31992
|
publisher.recordAttempt(command.sessionId, encodeDaemonActivity(command, activity), attemptId);
|
|
31786
31993
|
});
|
|
@@ -31809,6 +32016,7 @@ async function executePrompt(outbox, publisher, command, config2, directories, a
|
|
|
31809
32016
|
}),
|
|
31810
32017
|
attemptId
|
|
31811
32018
|
);
|
|
32019
|
+
activateAttempt();
|
|
31812
32020
|
const attachments = await resolvePromptArtifacts(config2, command, signal);
|
|
31813
32021
|
let stopReason;
|
|
31814
32022
|
if (command.disposition === SessionPromptDisposition.Steer) {
|
|
@@ -31920,7 +32128,7 @@ function botExecutionAgent(agent, contextHandle, socketPath) {
|
|
|
31920
32128
|
agent.capabilities
|
|
31921
32129
|
);
|
|
31922
32130
|
}
|
|
31923
|
-
async function executeBotPrompt(outbox, publisher, command, config2, directories, audit, sessions, grants, contexts, applicator, ipc, attemptId, timer, signal) {
|
|
32131
|
+
async function executeBotPrompt(outbox, publisher, command, config2, directories, audit, sessions, grants, contexts, applicator, ipc, attemptId, activateAttempt, timer, signal) {
|
|
31924
32132
|
const activities = new CoalescingDaemonActivityPublisher(timer, (activity) => {
|
|
31925
32133
|
publisher.recordAttempt(command.sessionId, encodeDaemonActivity(command, activity), attemptId);
|
|
31926
32134
|
});
|
|
@@ -32001,6 +32209,7 @@ async function executeBotPrompt(outbox, publisher, command, config2, directories
|
|
|
32001
32209
|
}),
|
|
32002
32210
|
attemptId
|
|
32003
32211
|
);
|
|
32212
|
+
activateAttempt();
|
|
32004
32213
|
const result = await sessions.runPrompt({
|
|
32005
32214
|
additionalDirectories: resolved.additionalDirectories,
|
|
32006
32215
|
agent,
|
|
@@ -32194,11 +32403,13 @@ async function runRelayConnection(config2, directories, audit, sessions, install
|
|
|
32194
32403
|
}
|
|
32195
32404
|
const turn = new ActiveDaemonTurn(command, receipt.attemptId);
|
|
32196
32405
|
activeTurns.set(command.commandId, turn);
|
|
32197
|
-
|
|
32198
|
-
|
|
32199
|
-
|
|
32406
|
+
const activateAttempt = () => {
|
|
32407
|
+
if (trackActiveAttempt) {
|
|
32408
|
+
activeAttemptIds.set(command.sessionId, receipt.attemptId);
|
|
32409
|
+
}
|
|
32410
|
+
};
|
|
32200
32411
|
try {
|
|
32201
|
-
await execute(receipt.attemptId, turn.signal);
|
|
32412
|
+
await execute(receipt.attemptId, activateAttempt, turn.signal);
|
|
32202
32413
|
} finally {
|
|
32203
32414
|
activeTurns.delete(command.commandId);
|
|
32204
32415
|
if (activeAttemptIds.get(command.sessionId) === receipt.attemptId) {
|
|
@@ -32283,7 +32494,7 @@ async function runRelayConnection(config2, directories, audit, sessions, install
|
|
|
32283
32494
|
delivery,
|
|
32284
32495
|
command,
|
|
32285
32496
|
true,
|
|
32286
|
-
(attemptId, executionSignal) => executeBotPrompt(
|
|
32497
|
+
(attemptId, activateAttempt, executionSignal) => executeBotPrompt(
|
|
32287
32498
|
outbox,
|
|
32288
32499
|
publisher,
|
|
32289
32500
|
command,
|
|
@@ -32296,6 +32507,7 @@ async function runRelayConnection(config2, directories, audit, sessions, install
|
|
|
32296
32507
|
applicator,
|
|
32297
32508
|
ipc,
|
|
32298
32509
|
attemptId,
|
|
32510
|
+
activateAttempt,
|
|
32299
32511
|
timer,
|
|
32300
32512
|
executionSignal
|
|
32301
32513
|
)
|
|
@@ -32308,7 +32520,7 @@ async function runRelayConnection(config2, directories, audit, sessions, install
|
|
|
32308
32520
|
delivery,
|
|
32309
32521
|
command,
|
|
32310
32522
|
command.disposition !== SessionPromptDisposition.Steer,
|
|
32311
|
-
(attemptId, executionSignal) => executePrompt(
|
|
32523
|
+
(attemptId, activateAttempt, executionSignal) => executePrompt(
|
|
32312
32524
|
outbox,
|
|
32313
32525
|
publisher,
|
|
32314
32526
|
command,
|
|
@@ -32317,6 +32529,7 @@ async function runRelayConnection(config2, directories, audit, sessions, install
|
|
|
32317
32529
|
audit,
|
|
32318
32530
|
sessions,
|
|
32319
32531
|
attemptId,
|
|
32532
|
+
activateAttempt,
|
|
32320
32533
|
timer,
|
|
32321
32534
|
executionSignal
|
|
32322
32535
|
)
|
|
@@ -32818,7 +33031,6 @@ async function runRelayConnection(config2, directories, audit, sessions, install
|
|
|
32818
33031
|
}
|
|
32819
33032
|
const turn2 = new ActiveDaemonTurn(command2, receipt2.attemptId);
|
|
32820
33033
|
activeTurns.set(command2.commandId, turn2);
|
|
32821
|
-
activeAttemptIds.set(command2.sessionId, receipt2.attemptId);
|
|
32822
33034
|
void executeBotPrompt(
|
|
32823
33035
|
outbox,
|
|
32824
33036
|
publisher,
|
|
@@ -32832,6 +33044,7 @@ async function runRelayConnection(config2, directories, audit, sessions, install
|
|
|
32832
33044
|
applicator,
|
|
32833
33045
|
ipc,
|
|
32834
33046
|
receipt2.attemptId,
|
|
33047
|
+
() => activeAttemptIds.set(command2.sessionId, receipt2.attemptId),
|
|
32835
33048
|
timer,
|
|
32836
33049
|
turn2.signal
|
|
32837
33050
|
).catch((error61) => {
|
|
@@ -32858,9 +33071,6 @@ async function runRelayConnection(config2, directories, audit, sessions, install
|
|
|
32858
33071
|
}
|
|
32859
33072
|
const turn = new ActiveDaemonTurn(command, receipt.attemptId);
|
|
32860
33073
|
activeTurns.set(command.commandId, turn);
|
|
32861
|
-
if (command.disposition !== SessionPromptDisposition.Steer) {
|
|
32862
|
-
activeAttemptIds.set(command.sessionId, receipt.attemptId);
|
|
32863
|
-
}
|
|
32864
33074
|
void executePrompt(
|
|
32865
33075
|
outbox,
|
|
32866
33076
|
publisher,
|
|
@@ -32870,6 +33080,11 @@ async function runRelayConnection(config2, directories, audit, sessions, install
|
|
|
32870
33080
|
audit,
|
|
32871
33081
|
sessions,
|
|
32872
33082
|
receipt.attemptId,
|
|
33083
|
+
() => {
|
|
33084
|
+
if (command.disposition !== SessionPromptDisposition.Steer) {
|
|
33085
|
+
activeAttemptIds.set(command.sessionId, receipt.attemptId);
|
|
33086
|
+
}
|
|
33087
|
+
},
|
|
32873
33088
|
timer,
|
|
32874
33089
|
turn.signal
|
|
32875
33090
|
).catch((error61) => {
|
|
@@ -33128,6 +33343,7 @@ Usage:
|
|
|
33128
33343
|
mobius pair --url <relay-url> --pairing <runtime-pairing-id> --code <pairing-code> [--workspace <directory>]
|
|
33129
33344
|
mobius start --channel <channel-id>
|
|
33130
33345
|
mobius agents [--json]
|
|
33346
|
+
mobius bot approve --bot <bot-id> [--channel <channel-id>]
|
|
33131
33347
|
mobius bot message send --target <current|alias> --text <message>
|
|
33132
33348
|
|
|
33133
33349
|
Commands:
|
|
@@ -33135,7 +33351,7 @@ Commands:
|
|
|
33135
33351
|
pair Pair this machine and start its local daemon
|
|
33136
33352
|
start Start the local daemon for a saved channel
|
|
33137
33353
|
agents Show detected, missing, and integration-ready Agents
|
|
33138
|
-
bot
|
|
33354
|
+
bot Approve a saved Bot locally or send a message from an active execution
|
|
33139
33355
|
|
|
33140
33356
|
Pairing grants this browser access to the machine with your user account's permissions.
|
|
33141
33357
|
New conversations default to ~/.mobius/workspace; choose other directories in the browser.
|
|
@@ -33166,6 +33382,10 @@ async function main() {
|
|
|
33166
33382
|
return;
|
|
33167
33383
|
}
|
|
33168
33384
|
if (command === "bot") {
|
|
33385
|
+
if (process.argv[3] === "approve") {
|
|
33386
|
+
process.stdout.write(await executeBotApproveCommand(process.argv.slice(4), process.env, shutdown.signal));
|
|
33387
|
+
return;
|
|
33388
|
+
}
|
|
33169
33389
|
if (process.argv[3] !== "message" || process.argv[4] !== "send") {
|
|
33170
33390
|
throw new BotMessageCommandError(
|
|
33171
33391
|
"invalid_request",
|
|
@@ -33211,7 +33431,7 @@ try {
|
|
|
33211
33431
|
} catch (error61) {
|
|
33212
33432
|
if (!shutdown.signal.aborted) {
|
|
33213
33433
|
const message = error61 instanceof Error ? error61.message : "Unknown failure";
|
|
33214
|
-
const code = error61 instanceof BotMessageCommandError ? `${error61.code}: ` : "";
|
|
33434
|
+
const code = error61 instanceof BotMessageCommandError || error61 instanceof LocalBotAuthorizationError ? `${error61.code}: ` : "";
|
|
33215
33435
|
process.stderr.write(`Mobius daemon failed: ${code}${message}
|
|
33216
33436
|
`);
|
|
33217
33437
|
process.exitCode = 1;
|