openmausbot 0.1.60 → 0.1.62
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/README.md +71 -8
- package/dist/assets/index-BSI5e_fY.js +264 -0
- package/dist/assets/{index-BIzf_lw1.js → index-DTeYtYoC.js} +1 -1
- package/dist/index.html +1 -1
- package/dist-server/container-mcp.js +16 -1
- package/dist-server/drivers/agents-proxy.js +8 -4
- package/dist-server/index.js +463 -134
- package/dist-server/openmausbot.js +33585 -14506
- package/dist-server/pair-cli.js +33585 -14506
- package/dist-server/server/browser-bundle-release.js +56 -0
- package/dist-server/server/browser-engine-release.js +13 -5
- package/dist-server/server/browser-engine.js +114 -15
- package/dist-server/server/cli-api-setup.js +144 -0
- package/dist-server/server/cli-phone-setup.js +130 -0
- package/dist-server/server/cli-prompts.js +185 -0
- package/dist-server/server/cli-setup.js +303 -0
- package/dist-server/server/cli.js +375 -70
- package/dist-server/server/config.js +25 -1
- package/dist-server/server/default-model-selection.js +21 -0
- package/dist-server/server/drivers/agents-proxy.js +13 -4
- package/dist-server/server/drivers/claude.js +26 -1
- package/dist-server/server/drivers/openai-compat.js +4 -2
- package/dist-server/server/index.js +117 -80
- package/dist-server/server/recall-disclosure.js +45 -0
- package/dist-server/server/request-auth.js +6 -0
- package/dist-server/server/screen-frame-gate.js +36 -1
- package/dist-server/server/screen-frame-source.js +57 -0
- package/dist-server/server/sessions.js +52 -4
- package/dist-server/vps-container-mcp.js +16 -1
- package/package.json +1 -1
- package/dist/assets/index-Tha1umgl.js +0 -264
package/dist-server/index.js
CHANGED
|
@@ -6,9 +6,9 @@ var __export = (target, all2) => {
|
|
|
6
6
|
|
|
7
7
|
// server/index.ts
|
|
8
8
|
import { createHash as createHash25, randomBytes as randomBytes7, randomUUID as randomUUID17, timingSafeEqual as timingSafeEqual4 } from "node:crypto";
|
|
9
|
-
import { existsSync as existsSync25, readFileSync as readFileSync38, rmSync as
|
|
9
|
+
import { existsSync as existsSync25, readFileSync as readFileSync38, rmSync as rmSync7, unlinkSync as unlinkSync9 } from "node:fs";
|
|
10
10
|
import { createServer as createServer3 } from "node:http";
|
|
11
|
-
import { extname as extname5, join as
|
|
11
|
+
import { extname as extname5, join as join50 } from "node:path";
|
|
12
12
|
|
|
13
13
|
// node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
|
|
14
14
|
var external_exports = {};
|
|
@@ -15651,6 +15651,15 @@ import { readFileSync as readFileSync3, mkdirSync, existsSync as existsSync2, re
|
|
|
15651
15651
|
import { homedir as homedir2 } from "node:os";
|
|
15652
15652
|
import { join as join3 } from "node:path";
|
|
15653
15653
|
|
|
15654
|
+
// server/contracts.ts
|
|
15655
|
+
var EFFORT_LEVELS = ["none", "low", "medium", "high", "xhigh", "max"];
|
|
15656
|
+
function isEffortLevel(value) {
|
|
15657
|
+
return typeof value === "string" && EFFORT_LEVELS.includes(value);
|
|
15658
|
+
}
|
|
15659
|
+
var eventCounter = 0;
|
|
15660
|
+
var newEventId = () => `ev-${Date.now().toString(36)}-${(eventCounter++).toString(36)}`;
|
|
15661
|
+
var newId = () => crypto.randomUUID();
|
|
15662
|
+
|
|
15654
15663
|
// server/mcp-registry.ts
|
|
15655
15664
|
var MAX_MCP_SERVERS = 20;
|
|
15656
15665
|
var MAX_ARGS = 64;
|
|
@@ -15933,7 +15942,19 @@ var instanceConfigSchema = external_exports.object({
|
|
|
15933
15942
|
config: external_exports.json().optional()
|
|
15934
15943
|
});
|
|
15935
15944
|
var instanceConfigMapSchema = external_exports.record(external_exports.string(), instanceConfigSchema);
|
|
15945
|
+
var defaultModelSelectionSchema = external_exports.object({
|
|
15946
|
+
instanceId: external_exports.string().trim().min(1),
|
|
15947
|
+
model: external_exports.string().trim().min(1),
|
|
15948
|
+
effort: external_exports.enum(EFFORT_LEVELS).optional()
|
|
15949
|
+
});
|
|
15936
15950
|
var appConfigSchema = external_exports.object({
|
|
15951
|
+
defaultModelSelection: defaultModelSelectionSchema.optional(),
|
|
15952
|
+
/** CLI-only launch preferences. Never enable remote access implicitly. */
|
|
15953
|
+
cliStartup: external_exports.object({
|
|
15954
|
+
access: external_exports.enum(["local", "tunnel", "tailscale", "public-url"]),
|
|
15955
|
+
publicUrl: external_exports.string().url().optional(),
|
|
15956
|
+
phone: external_exports.enum(["ios", "android"]).optional()
|
|
15957
|
+
}).optional(),
|
|
15937
15958
|
xai: external_exports.object({ key: optionalText, url: optionalText }).optional(),
|
|
15938
15959
|
/** `model` seeds the default selection; `provider` pins an OpenRouter
|
|
15939
15960
|
* upstream (e.g. "fireworks"). Both are non-secret and optional. */
|
|
@@ -15970,7 +15991,7 @@ var appConfigSchema = external_exports.object({
|
|
|
15970
15991
|
var storedAppConfigSchema = appConfigSchema.extend({
|
|
15971
15992
|
browserProfiles: storedBrowserProfilesSchema.optional()
|
|
15972
15993
|
});
|
|
15973
|
-
var appConfigPatchSchema = appConfigSchema.omit({ instances: true, mcpServers: true });
|
|
15994
|
+
var appConfigPatchSchema = appConfigSchema.omit({ instances: true, mcpServers: true, cliStartup: true });
|
|
15974
15995
|
var jsonObjectSchema = external_exports.record(external_exports.string(), external_exports.json());
|
|
15975
15996
|
function browserProfilePartitionId(profile) {
|
|
15976
15997
|
return profile.partitionId ?? profile.id;
|
|
@@ -16170,6 +16191,10 @@ function saveConfig(patch) {
|
|
|
16170
16191
|
}
|
|
16171
16192
|
if (checkedPatch.vps !== void 0) disk.vps = normalizeVpsConfig(checkedPatch.vps);
|
|
16172
16193
|
if (checkedPatch.language !== void 0) disk.language = checkedPatch.language;
|
|
16194
|
+
if (checkedPatch.defaultModelSelection !== void 0) {
|
|
16195
|
+
disk.defaultModelSelection = checkedPatch.defaultModelSelection;
|
|
16196
|
+
}
|
|
16197
|
+
if (checkedPatch.cliStartup !== void 0) disk.cliStartup = checkedPatch.cliStartup;
|
|
16173
16198
|
if (checkedPatch.mcpServers !== void 0) {
|
|
16174
16199
|
disk.mcpServers = jsonObjectSchema.parse(checkedPatch.mcpServers);
|
|
16175
16200
|
}
|
|
@@ -16289,6 +16314,7 @@ function instanceConfigs(cfg2) {
|
|
|
16289
16314
|
const current2 = typeof raw === "object" && raw !== null && !Array.isArray(raw) ? raw : {};
|
|
16290
16315
|
const merged = { ...current2 };
|
|
16291
16316
|
for (const [k, v] of Object.entries(defaults)) {
|
|
16317
|
+
if (k === "provider" && typeof merged[k] === "string") continue;
|
|
16292
16318
|
if (typeof merged[k] !== "string" || !merged[k].trim()) merged[k] = v;
|
|
16293
16319
|
}
|
|
16294
16320
|
entry.config = merged;
|
|
@@ -28129,15 +28155,6 @@ function buildNotification(kind, bot, threadId, detail, extra) {
|
|
|
28129
28155
|
return notification;
|
|
28130
28156
|
}
|
|
28131
28157
|
|
|
28132
|
-
// server/contracts.ts
|
|
28133
|
-
var EFFORT_LEVELS = ["none", "low", "medium", "high", "xhigh", "max"];
|
|
28134
|
-
function isEffortLevel(value) {
|
|
28135
|
-
return typeof value === "string" && EFFORT_LEVELS.includes(value);
|
|
28136
|
-
}
|
|
28137
|
-
var eventCounter = 0;
|
|
28138
|
-
var newEventId = () => `ev-${Date.now().toString(36)}-${(eventCounter++).toString(36)}`;
|
|
28139
|
-
var newId = () => crypto.randomUUID();
|
|
28140
|
-
|
|
28141
28158
|
// server/drivers/retry.ts
|
|
28142
28159
|
var RETRY_MAX_ATTEMPTS = 3;
|
|
28143
28160
|
var BACKOFF_BASE_MS = [1e3, 3e3, 8e3];
|
|
@@ -30848,6 +30865,10 @@ function claudeSignedIn(cli, env, run3 = execCli) {
|
|
|
30848
30865
|
});
|
|
30849
30866
|
});
|
|
30850
30867
|
}
|
|
30868
|
+
function claudeAuthFailure(frame, text6) {
|
|
30869
|
+
if (frame.is_api_error_message !== true && typeof frame.error !== "string") return false;
|
|
30870
|
+
return frame.error === "authentication_failed" || classifyError({ text: text6 }).reason === "auth";
|
|
30871
|
+
}
|
|
30851
30872
|
function claudeEnvironment(model, source = process.env) {
|
|
30852
30873
|
const env = { ...source, PATH: augmentedPath(), NPM_CONFIG_LOGLEVEL: "error" };
|
|
30853
30874
|
delete env.CLAUDECODE;
|
|
@@ -31562,6 +31583,11 @@ var ClaudeDriver = {
|
|
|
31562
31583
|
case "assistant": {
|
|
31563
31584
|
const msg = o.message ?? {};
|
|
31564
31585
|
const text6 = firstText(msg.content);
|
|
31586
|
+
if (claudeAuthFailure(o, text6)) {
|
|
31587
|
+
if (session.turn) session.turn.authFailed = true;
|
|
31588
|
+
emit({ ...base2(threadId, currentTurnId()), type: "runtime.error", message: text6, setup: true });
|
|
31589
|
+
break;
|
|
31590
|
+
}
|
|
31565
31591
|
if (text6.trim()) {
|
|
31566
31592
|
if (!session.turn?.sawStreamDelta) {
|
|
31567
31593
|
emit({ ...base2(threadId, currentTurnId()), type: "content.delta", streamKind: "assistant_text", delta: text6 });
|
|
@@ -31596,7 +31622,7 @@ var ClaudeDriver = {
|
|
|
31596
31622
|
if (o.origin?.kind === "task-notification") break;
|
|
31597
31623
|
settle(
|
|
31598
31624
|
o.is_error !== true,
|
|
31599
|
-
o.stop_reason ?? o.terminal_reason ?? null,
|
|
31625
|
+
session.turn?.authFailed ? "auth_required" : o.stop_reason ?? o.terminal_reason ?? null,
|
|
31600
31626
|
o.total_cost_usd ?? null,
|
|
31601
31627
|
o.usage ? {
|
|
31602
31628
|
input: (o.usage.input_tokens || 0) + (o.usage.cache_read_input_tokens || 0) + (o.usage.cache_creation_input_tokens || 0),
|
|
@@ -41630,7 +41656,9 @@ function decodeConfig5(raw) {
|
|
|
41630
41656
|
apiKeyEnv: typeof config2.apiKeyEnv === "string" && config2.apiKeyEnv ? config2.apiKeyEnv : "OPENAI_COMPAT_API_KEY",
|
|
41631
41657
|
key: typeof config2.key === "string" && config2.key ? config2.key : void 0,
|
|
41632
41658
|
model: typeof config2.model === "string" && config2.model ? config2.model : process.env.OPENAI_COMPAT_MODEL || void 0,
|
|
41633
|
-
|
|
41659
|
+
// An explicit empty override disables inherited routing for an isolated
|
|
41660
|
+
// connection (CLI setup uses this). Absent still inherits the global pin.
|
|
41661
|
+
provider: typeof config2.provider === "string" ? config2.provider || void 0 : process.env.OPENAI_COMPAT_PROVIDER || void 0
|
|
41634
41662
|
};
|
|
41635
41663
|
}
|
|
41636
41664
|
var OpenAICompatDriver = {
|
|
@@ -44165,6 +44193,26 @@ function mirrorActivity(bus2, from, channel, name2, ok, notify2) {
|
|
|
44165
44193
|
markMirrored(bus2, channel, notify2);
|
|
44166
44194
|
}
|
|
44167
44195
|
|
|
44196
|
+
// server/recall-disclosure.ts
|
|
44197
|
+
var announced = /* @__PURE__ */ new Map();
|
|
44198
|
+
function claimRecallCrossings(roomThreadId, sources) {
|
|
44199
|
+
const seen = announced.get(roomThreadId) ?? /* @__PURE__ */ new Set();
|
|
44200
|
+
const threadIds = [];
|
|
44201
|
+
let count = 0;
|
|
44202
|
+
for (const threadId of sources) {
|
|
44203
|
+
if (seen.has(threadId)) continue;
|
|
44204
|
+
if (!threadIds.includes(threadId)) threadIds.push(threadId);
|
|
44205
|
+
count += 1;
|
|
44206
|
+
}
|
|
44207
|
+
if (!threadIds.length) return { threadIds: [], count: 0 };
|
|
44208
|
+
for (const threadId of threadIds) seen.add(threadId);
|
|
44209
|
+
announced.set(roomThreadId, seen);
|
|
44210
|
+
return { threadIds, count };
|
|
44211
|
+
}
|
|
44212
|
+
function recallCrossingLabel(botName, count) {
|
|
44213
|
+
return `${botName} recalled ${count} message${count === 1 ? "" : "s"} from its private chat with you`;
|
|
44214
|
+
}
|
|
44215
|
+
|
|
44168
44216
|
// server/replies.ts
|
|
44169
44217
|
var MAX_REPLY_EXCERPT = 900;
|
|
44170
44218
|
function replyExcerpt(text6, limit = MAX_REPLY_EXCERPT) {
|
|
@@ -45207,6 +45255,22 @@ var ProviderRegistry = class {
|
|
|
45207
45255
|
}
|
|
45208
45256
|
};
|
|
45209
45257
|
|
|
45258
|
+
// server/default-model-selection.ts
|
|
45259
|
+
function selectDefaultModelSelection(instances, preferred) {
|
|
45260
|
+
if (preferred) {
|
|
45261
|
+
const instance = instances.find((candidate) => candidate.instanceId === preferred.instanceId);
|
|
45262
|
+
if (instance?.snapshot.state !== "available" || instance.snapshot.authenticated === false || !(instance.models.default === preferred.model || instance.models.options.some((model) => model.id === preferred.model))) {
|
|
45263
|
+
return { instanceId: "", model: "" };
|
|
45264
|
+
}
|
|
45265
|
+
const selection = { ...preferred };
|
|
45266
|
+
if (selection.effort && !instance.capabilities?.effortLevels?.includes(selection.effort)) delete selection.effort;
|
|
45267
|
+
return selection;
|
|
45268
|
+
}
|
|
45269
|
+
const available = instances.filter((instance) => instance.snapshot.state === "available");
|
|
45270
|
+
const pick2 = available.find((instance) => instance.driverKind === "claudeAgent") ?? available[0];
|
|
45271
|
+
return { instanceId: pick2?.instanceId ?? "", model: pick2?.models.default ?? "" };
|
|
45272
|
+
}
|
|
45273
|
+
|
|
45210
45274
|
// server/room-post-budget.ts
|
|
45211
45275
|
var SENDER_WINDOW_MS = 6e4;
|
|
45212
45276
|
var SENDER_MAX = 10;
|
|
@@ -49596,8 +49660,12 @@ var CalendarCallManager = class {
|
|
|
49596
49660
|
// server/browser-engine.ts
|
|
49597
49661
|
import { spawn as spawn4 } from "node:child_process";
|
|
49598
49662
|
import { createHash as createHash16, randomBytes as randomBytes4, randomUUID as randomUUID12 } from "node:crypto";
|
|
49599
|
-
import { chmodSync as chmodSync5, existsSync as existsSync18, mkdirSync as mkdirSync20, readFileSync as readFileSync31, renameSync as renameSync5, statSync as statSync8, writeFileSync as writeFileSync15 } from "node:fs";
|
|
49600
|
-
import {
|
|
49663
|
+
import { chmodSync as chmodSync5, existsSync as existsSync18, mkdirSync as mkdirSync20, readFileSync as readFileSync31, renameSync as renameSync5, rmSync as rmSync6, statSync as statSync8, writeFileSync as writeFileSync15 } from "node:fs";
|
|
49664
|
+
import { tmpdir as tmpdir6 } from "node:os";
|
|
49665
|
+
import { delimiter as delimiter4, join as join41, resolve as resolve10 } from "node:path";
|
|
49666
|
+
|
|
49667
|
+
// server/browser-bundle-release.ts
|
|
49668
|
+
import { join as join40 } from "node:path";
|
|
49601
49669
|
|
|
49602
49670
|
// server/browser-engine-release.ts
|
|
49603
49671
|
var AGENT_BROWSER_VERSION = "0.36.0";
|
|
@@ -49628,17 +49696,77 @@ var RELEASES2 = /* @__PURE__ */ new Map([
|
|
|
49628
49696
|
],
|
|
49629
49697
|
[
|
|
49630
49698
|
"win32-x64",
|
|
49631
|
-
{
|
|
49699
|
+
{
|
|
49700
|
+
target: "win32-x64",
|
|
49701
|
+
version: "0.36.0-omb.1",
|
|
49702
|
+
asset: "agent-browser-win32-x64-0.36.0-omb.1.exe",
|
|
49703
|
+
url: "https://github.com/milind-soni/OpenMausBot/releases/download/browser-engine-v0.36.0-omb.1/agent-browser-win32-x64-0.36.0-omb.1.exe",
|
|
49704
|
+
sha256: "33bee834f6a6072ec8688b0914726e0262874d758f69f27e8baf7eaac6b5ed15",
|
|
49705
|
+
bytes: 13806080
|
|
49706
|
+
}
|
|
49632
49707
|
]
|
|
49633
49708
|
]);
|
|
49634
49709
|
function agentBrowserReleaseUrl(asset) {
|
|
49635
|
-
return `https://github.com/vercel-labs/agent-browser/releases/download/v${AGENT_BROWSER_VERSION}/${asset.asset}`;
|
|
49710
|
+
return asset.url ?? `https://github.com/vercel-labs/agent-browser/releases/download/v${AGENT_BROWSER_VERSION}/${asset.asset}`;
|
|
49711
|
+
}
|
|
49712
|
+
function agentBrowserReleaseVersion(asset) {
|
|
49713
|
+
return asset?.version ?? AGENT_BROWSER_VERSION;
|
|
49636
49714
|
}
|
|
49637
49715
|
function resolveAgentBrowserReleaseAsset(platform = process.platform, arch = process.arch, musl = false) {
|
|
49638
49716
|
const key3 = platform === "linux" && musl ? `linux-musl-${arch}` : `${platform}-${arch}`;
|
|
49639
49717
|
return RELEASES2.get(key3) ?? null;
|
|
49640
49718
|
}
|
|
49641
49719
|
|
|
49720
|
+
// server/browser-bundle-release.ts
|
|
49721
|
+
var CHROME_VERSION = "152.0.7977.82";
|
|
49722
|
+
var CHROME_ASSETS = {
|
|
49723
|
+
"darwin-arm64": { platform: "mac-arm64", bytes: 97760996, sha256: "1615f063c894aa824fd55c89f8f05e9904f63cce0c95d0cbce7394d77884fba5", executableSha256: "ac18537fb2bc2b72b0440893b6ea5d4ef3bacc6220b1e9a1693c73f8b1045fbe" },
|
|
49724
|
+
"darwin-x64": { platform: "mac-x64", bytes: 102895824, sha256: "c903707292c6aaed7c6e572c0bb5e6645454e45d9e0e6db27e876b18e9165f31", executableSha256: "2faefd55d4e47764956e31e338d362a1e794037fa772a426ffd510f122f33f19" },
|
|
49725
|
+
"linux-x64": { platform: "linux64", bytes: 119454769, sha256: "0ca12ea26b502a83e32db334a17883c315348845efc071d908de7a6d94a97eff", executableSha256: "1301a5024fef1d58c4cfbde5d44794cb18c5b2cbbe4f3c64f81cc4eaa683c45f" },
|
|
49726
|
+
"win32-x64": { platform: "win64", bytes: 119768220, sha256: "86fb1fa7fbbda65f6f1572062c358803e61a6e6d9489e6619b37a61d609fa845", executableSha256: "ec0744f041cb6c35c439c85a5ac42d77b507838cd59cac005e25114025336c98" }
|
|
49727
|
+
};
|
|
49728
|
+
function browserBundleSpec(target) {
|
|
49729
|
+
if (!Object.hasOwn(CHROME_ASSETS, target)) throw new Error(`Unsupported desktop browser target: ${target}`);
|
|
49730
|
+
const pinned = CHROME_ASSETS[target];
|
|
49731
|
+
const [platform, arch] = target.split("-");
|
|
49732
|
+
const engine = resolveAgentBrowserReleaseAsset(platform, arch);
|
|
49733
|
+
const suffix = platform === "win32" ? ".exe" : "";
|
|
49734
|
+
const directory = `chrome-headless-shell-${pinned.platform}`;
|
|
49735
|
+
return {
|
|
49736
|
+
schemaVersion: 1,
|
|
49737
|
+
target,
|
|
49738
|
+
engine: {
|
|
49739
|
+
version: agentBrowserReleaseVersion(engine),
|
|
49740
|
+
asset: engine.asset,
|
|
49741
|
+
url: agentBrowserReleaseUrl(engine),
|
|
49742
|
+
bytes: engine.bytes,
|
|
49743
|
+
sha256: engine.sha256,
|
|
49744
|
+
executable: `agent-browser${suffix}`
|
|
49745
|
+
},
|
|
49746
|
+
chrome: {
|
|
49747
|
+
version: CHROME_VERSION,
|
|
49748
|
+
asset: `${directory}.zip`,
|
|
49749
|
+
url: `https://storage.googleapis.com/chrome-for-testing-public/${CHROME_VERSION}/${pinned.platform}/${directory}.zip`,
|
|
49750
|
+
bytes: pinned.bytes,
|
|
49751
|
+
sha256: pinned.sha256,
|
|
49752
|
+
executableSha256: pinned.executableSha256,
|
|
49753
|
+
executable: `chrome/${directory}/chrome-headless-shell${suffix}`,
|
|
49754
|
+
license: `chrome/${directory}/LICENSE.headless_shell`,
|
|
49755
|
+
about: `chrome/${directory}/ABOUT`
|
|
49756
|
+
}
|
|
49757
|
+
};
|
|
49758
|
+
}
|
|
49759
|
+
function browserBundlePaths(bundleDirectory, target) {
|
|
49760
|
+
const spec = browserBundleSpec(target);
|
|
49761
|
+
return {
|
|
49762
|
+
directory: bundleDirectory,
|
|
49763
|
+
manifest: join40(bundleDirectory, "manifest.json"),
|
|
49764
|
+
engine: join40(bundleDirectory, spec.engine.executable),
|
|
49765
|
+
chrome: join40(bundleDirectory, spec.chrome.executable),
|
|
49766
|
+
licenses: join40(bundleDirectory, "licenses")
|
|
49767
|
+
};
|
|
49768
|
+
}
|
|
49769
|
+
|
|
49642
49770
|
// server/browser-engine.ts
|
|
49643
49771
|
var ENGINE_DIR = "tools/agent-browser";
|
|
49644
49772
|
var KEY_FILE = "browser-engine-key";
|
|
@@ -49649,8 +49777,9 @@ function executableName(platform = process.platform) {
|
|
|
49649
49777
|
function isMusl(platform = process.platform, exists = existsSync18) {
|
|
49650
49778
|
return platform === "linux" && (exists("/lib/ld-musl-x86_64.so.1") || exists("/lib/ld-musl-aarch64.so.1"));
|
|
49651
49779
|
}
|
|
49652
|
-
function pinnedBinaryPath(dataDir = DATA_DIR, platform = process.platform) {
|
|
49653
|
-
|
|
49780
|
+
function pinnedBinaryPath(dataDir = DATA_DIR, platform = process.platform, arch = process.arch) {
|
|
49781
|
+
const version2 = agentBrowserReleaseVersion(resolveAgentBrowserReleaseAsset(platform, arch));
|
|
49782
|
+
return join41(dataDir, ENGINE_DIR, version2, executableName(platform));
|
|
49654
49783
|
}
|
|
49655
49784
|
function onPath(env, platform, exists) {
|
|
49656
49785
|
const pathValue = platform === "win32" ? Object.entries(env).findLast(([key3]) => key3.toUpperCase() === "PATH")?.[1] : env.PATH;
|
|
@@ -49662,13 +49791,27 @@ function onPath(env, platform, exists) {
|
|
|
49662
49791
|
}
|
|
49663
49792
|
return null;
|
|
49664
49793
|
}
|
|
49794
|
+
function packagedBrowser(options) {
|
|
49795
|
+
const resources = (options.env ?? process.env).OMB_RESOURCES_PATH;
|
|
49796
|
+
if (!resources) return null;
|
|
49797
|
+
try {
|
|
49798
|
+
return browserBundlePaths(join41(resolve10(resources), "browser-engine"), `${options.platform ?? process.platform}-${options.arch ?? process.arch}`);
|
|
49799
|
+
} catch {
|
|
49800
|
+
return null;
|
|
49801
|
+
}
|
|
49802
|
+
}
|
|
49803
|
+
function completePackage(bundle, exists) {
|
|
49804
|
+
return [bundle.manifest, bundle.engine, bundle.chrome, bundle.licenses].every(exists);
|
|
49805
|
+
}
|
|
49665
49806
|
function resolveAgentBrowserBinary(options = {}) {
|
|
49666
49807
|
const env = options.env ?? process.env;
|
|
49667
49808
|
const platform = options.platform ?? process.platform;
|
|
49668
49809
|
const exists = options.exists ?? existsSync18;
|
|
49669
49810
|
const override = env.OMB_AGENT_BROWSER_PATH?.trim();
|
|
49670
49811
|
if (override) return resolve10(override) && exists(resolve10(override)) ? resolve10(override) : null;
|
|
49671
|
-
const
|
|
49812
|
+
const bundle = packagedBrowser(options);
|
|
49813
|
+
if (bundle && exists(bundle.directory)) return completePackage(bundle, exists) ? bundle.engine : null;
|
|
49814
|
+
const pinned = pinnedBinaryPath(options.dataDir, platform, options.arch);
|
|
49672
49815
|
if (exists(pinned)) return pinned;
|
|
49673
49816
|
return onPath(env, platform, exists);
|
|
49674
49817
|
}
|
|
@@ -49676,11 +49819,11 @@ async function installAgentBrowserBinary(options = {}) {
|
|
|
49676
49819
|
const platform = options.platform ?? process.platform;
|
|
49677
49820
|
const asset = options.asset ?? resolveAgentBrowserReleaseAsset(platform, options.arch ?? process.arch, options.musl ?? isMusl(platform));
|
|
49678
49821
|
if (!asset) throw new Error(`agent-browser publishes no build for ${platform}-${options.arch ?? process.arch}.`);
|
|
49679
|
-
const destination = pinnedBinaryPath(options.dataDir, platform);
|
|
49680
|
-
const directory =
|
|
49822
|
+
const destination = pinnedBinaryPath(options.dataDir, platform, options.arch);
|
|
49823
|
+
const directory = join41(destination, "..");
|
|
49681
49824
|
mkdirSync20(directory, { recursive: true, mode: 448 });
|
|
49682
49825
|
const url2 = agentBrowserReleaseUrl(asset);
|
|
49683
|
-
options.log?.(`downloading agent-browser ${
|
|
49826
|
+
options.log?.(`downloading agent-browser ${agentBrowserReleaseVersion(asset)} (${Math.round(asset.bytes / 1024 / 1024)} MB, digest pinned)`);
|
|
49684
49827
|
const controller = new AbortController();
|
|
49685
49828
|
const timer = setTimeout(() => controller.abort(), DOWNLOAD_TIMEOUT_MS2);
|
|
49686
49829
|
timer.unref?.();
|
|
@@ -49702,6 +49845,11 @@ async function installAgentBrowserBinary(options = {}) {
|
|
|
49702
49845
|
return destination;
|
|
49703
49846
|
}
|
|
49704
49847
|
function ensureChrome(binaryPath, options = {}) {
|
|
49848
|
+
const bundle = packagedBrowser(options);
|
|
49849
|
+
if (!options.withDeps && bundle && resolve10(binaryPath) === bundle.engine && completePackage(bundle, existsSync18)) {
|
|
49850
|
+
options.log?.("agent-browser: the bundled browser is ready; no download needed");
|
|
49851
|
+
return Promise.resolve();
|
|
49852
|
+
}
|
|
49705
49853
|
const args = ["install", ...options.withDeps ? ["--with-deps"] : []];
|
|
49706
49854
|
return new Promise((done, fail2) => {
|
|
49707
49855
|
const child = spawn4(binaryPath, args, { env: options.env ?? process.env, stdio: ["ignore", "pipe", "pipe"], windowsHide: true });
|
|
@@ -49724,7 +49872,7 @@ function ensureChrome(binaryPath, options = {}) {
|
|
|
49724
49872
|
});
|
|
49725
49873
|
}
|
|
49726
49874
|
function browserEngineEncryptionKey(dataDir = DATA_DIR) {
|
|
49727
|
-
const file2 =
|
|
49875
|
+
const file2 = join41(dataDir, KEY_FILE);
|
|
49728
49876
|
try {
|
|
49729
49877
|
const existing = readFileSync31(file2, "utf8").trim();
|
|
49730
49878
|
if (/^[0-9a-f]{64}$/u.test(existing)) return existing;
|
|
@@ -49738,14 +49886,28 @@ function browserEngineEncryptionKey(dataDir = DATA_DIR) {
|
|
|
49738
49886
|
}
|
|
49739
49887
|
function browserEngineStatus(options = {}) {
|
|
49740
49888
|
const binaryPath = resolveAgentBrowserBinary(options);
|
|
49741
|
-
|
|
49889
|
+
const bundle = packagedBrowser(options);
|
|
49890
|
+
if (binaryPath) {
|
|
49891
|
+
const platform2 = options.platform ?? process.platform;
|
|
49892
|
+
const managed = binaryPath === bundle?.engine || binaryPath === pinnedBinaryPath(options.dataDir, platform2, options.arch);
|
|
49893
|
+
const version2 = managed ? agentBrowserReleaseVersion(resolveAgentBrowserReleaseAsset(platform2, options.arch)) : AGENT_BROWSER_VERSION;
|
|
49894
|
+
return { kind: "ready", binaryPath, version: version2 };
|
|
49895
|
+
}
|
|
49896
|
+
if (bundle && (options.exists ?? existsSync18)(bundle.directory)) {
|
|
49897
|
+
return { kind: "unavailable", reason: "The desktop browser bundle is incomplete. Reinstall or update OpenMausBot to repair it.", installable: false };
|
|
49898
|
+
}
|
|
49742
49899
|
const platform = options.platform ?? process.platform;
|
|
49743
|
-
const
|
|
49744
|
-
|
|
49900
|
+
const arch = options.arch ?? process.arch;
|
|
49901
|
+
const asset = resolveAgentBrowserReleaseAsset(platform, arch, isMusl(platform));
|
|
49902
|
+
return asset ? { kind: "unavailable", reason: "agent-browser is not installed on this machine yet", installable: true } : { kind: "unavailable", reason: `agent-browser publishes no build for ${platform}-${arch}`, installable: false };
|
|
49745
49903
|
}
|
|
49746
49904
|
function agentBrowserIntegration(input) {
|
|
49747
49905
|
const env = {
|
|
49748
49906
|
AGENT_BROWSER_SESSION: input.session,
|
|
49907
|
+
// The MCP server invokes child CLI commands without forwarding its own
|
|
49908
|
+
// global flags. The environment keeps page-provided tools disabled in
|
|
49909
|
+
// those commands too, and avoids changing browser launch settings later.
|
|
49910
|
+
AGENT_BROWSER_NO_WEBMCP: "1",
|
|
49749
49911
|
// This is a restore *name*, not a boolean. "1" would give every bot
|
|
49750
49912
|
// the same saved cookies despite using different daemon sessions.
|
|
49751
49913
|
AGENT_BROWSER_RESTORE: input.session,
|
|
@@ -49753,10 +49915,53 @@ function agentBrowserIntegration(input) {
|
|
|
49753
49915
|
AGENT_BROWSER_ENCRYPTION_KEY: input.encryptionKey
|
|
49754
49916
|
};
|
|
49755
49917
|
if (input.headless !== false) env.AGENT_BROWSER_HEADLESS = "1";
|
|
49756
|
-
const
|
|
49757
|
-
|
|
49918
|
+
const sourceEnv = input.env ?? process.env;
|
|
49919
|
+
for (const name2 of ["PATH", "AGENT_BROWSER_EXECUTABLE_PATH"]) {
|
|
49920
|
+
if (sourceEnv[name2]) env[name2] = sourceEnv[name2];
|
|
49921
|
+
}
|
|
49922
|
+
const bundle = packagedBrowser({ env: sourceEnv });
|
|
49923
|
+
if (!env.AGENT_BROWSER_EXECUTABLE_PATH && bundle && resolve10(input.binaryPath) === bundle.engine && completePackage(bundle, existsSync18)) {
|
|
49924
|
+
env.AGENT_BROWSER_EXECUTABLE_PATH = bundle.chrome;
|
|
49925
|
+
}
|
|
49758
49926
|
return { command: input.binaryPath, args: ["mcp", "--tools", "core", "--no-webmcp"], env };
|
|
49759
49927
|
}
|
|
49928
|
+
var FRAME_TIMEOUT_MS = 1e4;
|
|
49929
|
+
function agentBrowserFrame(input) {
|
|
49930
|
+
const file2 = join41(tmpdir6(), `openmausbot-browser-${randomUUID12()}.png`);
|
|
49931
|
+
return new Promise((settle, fail2) => {
|
|
49932
|
+
const child = spawn4(input.binaryPath, ["screenshot", file2], {
|
|
49933
|
+
env: { ...process.env, ...input.env },
|
|
49934
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
49935
|
+
windowsHide: true
|
|
49936
|
+
});
|
|
49937
|
+
let stderr = "";
|
|
49938
|
+
child.stderr?.on("data", (chunk) => {
|
|
49939
|
+
if (stderr.length < 2e3) stderr += String(chunk);
|
|
49940
|
+
});
|
|
49941
|
+
const timer = setTimeout(() => {
|
|
49942
|
+
child.kill("SIGKILL");
|
|
49943
|
+
fail2(new Error("the browser did not return a picture in time"));
|
|
49944
|
+
}, input.timeoutMs ?? FRAME_TIMEOUT_MS);
|
|
49945
|
+
const done = (error51) => {
|
|
49946
|
+
clearTimeout(timer);
|
|
49947
|
+
try {
|
|
49948
|
+
if (error51) {
|
|
49949
|
+
fail2(error51);
|
|
49950
|
+
return;
|
|
49951
|
+
}
|
|
49952
|
+
settle({ png: readFileSync31(file2).toString("base64"), format: "png" });
|
|
49953
|
+
} catch {
|
|
49954
|
+
fail2(new Error("the browser reported a picture it did not write"));
|
|
49955
|
+
} finally {
|
|
49956
|
+
rmSync6(file2, { force: true });
|
|
49957
|
+
}
|
|
49958
|
+
};
|
|
49959
|
+
child.on("error", (error51) => done(error51 instanceof Error ? error51 : new Error(String(error51))));
|
|
49960
|
+
child.on("close", (code) => {
|
|
49961
|
+
done(code === 0 ? null : new Error(`the browser could not be pictured${stderr.trim() ? `: ${stderr.trim().slice(0, 200)}` : ""}`));
|
|
49962
|
+
});
|
|
49963
|
+
});
|
|
49964
|
+
}
|
|
49760
49965
|
function browserSessionId(botId, partitionId) {
|
|
49761
49966
|
if (partitionId === "guest") return `guest-${randomUUID12()}`;
|
|
49762
49967
|
const raw = partitionId || `bot-${botId}`;
|
|
@@ -49811,6 +50016,49 @@ async function captureOutsideHumanControl(control, capture) {
|
|
|
49811
50016
|
return { png, mime: format === "jpeg" ? "image/jpeg" : "image/png" };
|
|
49812
50017
|
}
|
|
49813
50018
|
|
|
50019
|
+
// server/screen-frame-source.ts
|
|
50020
|
+
function createScreenFrameSource(input) {
|
|
50021
|
+
let current2 = null;
|
|
50022
|
+
let lastAt = 0;
|
|
50023
|
+
let attemptedSurface;
|
|
50024
|
+
let last = null;
|
|
50025
|
+
let lastSurface;
|
|
50026
|
+
const selectedSurface = () => input.captures[source.surface] ? source.surface : input.captures.computer ? "computer" : "browser";
|
|
50027
|
+
const source = {
|
|
50028
|
+
surface: input.captures.computer ? "computer" : "browser",
|
|
50029
|
+
get last() {
|
|
50030
|
+
return lastSurface === selectedSurface() ? last : null;
|
|
50031
|
+
},
|
|
50032
|
+
async capture(fresh = false) {
|
|
50033
|
+
while (current2) await current2;
|
|
50034
|
+
if (input.control().held) return;
|
|
50035
|
+
const surface = selectedSurface();
|
|
50036
|
+
if (!fresh && attemptedSurface === surface && Date.now() - lastAt < input.minGapMs) return;
|
|
50037
|
+
attemptedSurface = surface;
|
|
50038
|
+
current2 = (async () => {
|
|
50039
|
+
try {
|
|
50040
|
+
const capture = input.captures[surface];
|
|
50041
|
+
if (!capture) return;
|
|
50042
|
+
const frame = await captureOutsideHumanControl(input.control, capture);
|
|
50043
|
+
if (!frame || surface !== selectedSurface()) return;
|
|
50044
|
+
last = frame;
|
|
50045
|
+
lastSurface = surface;
|
|
50046
|
+
input.onFrame(frame);
|
|
50047
|
+
} catch {
|
|
50048
|
+
} finally {
|
|
50049
|
+
lastAt = Date.now();
|
|
50050
|
+
}
|
|
50051
|
+
})();
|
|
50052
|
+
try {
|
|
50053
|
+
await current2;
|
|
50054
|
+
} finally {
|
|
50055
|
+
current2 = null;
|
|
50056
|
+
}
|
|
50057
|
+
}
|
|
50058
|
+
};
|
|
50059
|
+
return source;
|
|
50060
|
+
}
|
|
50061
|
+
|
|
49814
50062
|
// server/screen-frame-gate.ts
|
|
49815
50063
|
import { createHash as createHash17 } from "node:crypto";
|
|
49816
50064
|
var SCREEN_TOUCHING_TOOLS = /* @__PURE__ */ new Set([
|
|
@@ -49824,7 +50072,8 @@ var SCREEN_TOUCHING_TOOLS = /* @__PURE__ */ new Set([
|
|
|
49824
50072
|
"open_url",
|
|
49825
50073
|
"browser_click",
|
|
49826
50074
|
"browser_fill",
|
|
49827
|
-
// built-in browser
|
|
50075
|
+
// built-in browser: the Electron surface's names, kept because a bot on an
|
|
50076
|
+
// older remote harness can still call them
|
|
49828
50077
|
"browser_navigate",
|
|
49829
50078
|
"browser_type",
|
|
49830
50079
|
"browser_press",
|
|
@@ -49835,6 +50084,20 @@ var SCREEN_TOUCHING_TOOLS = /* @__PURE__ */ new Set([
|
|
|
49835
50084
|
"browser_back",
|
|
49836
50085
|
"browser_forward",
|
|
49837
50086
|
"browser_screenshot",
|
|
50087
|
+
// built-in browser: agent-browser's names. The engine swap replaced the
|
|
50088
|
+
// Electron surface without renaming these here, so every browser turn
|
|
50089
|
+
// stopped counting as screen work and no page ever reached the transcript.
|
|
50090
|
+
// Read-only tools stay out by the same rule as everywhere else in this
|
|
50091
|
+
// list: agent_browser_snapshot, _read and _get_text return text, and the
|
|
50092
|
+
// waits change nothing the action before them did not already count.
|
|
50093
|
+
"agent_browser_open",
|
|
50094
|
+
"agent_browser_click",
|
|
50095
|
+
"agent_browser_fill",
|
|
50096
|
+
"agent_browser_type",
|
|
50097
|
+
"agent_browser_press",
|
|
50098
|
+
"agent_browser_select",
|
|
50099
|
+
"agent_browser_check",
|
|
50100
|
+
"agent_browser_screenshot",
|
|
49838
50101
|
// Cua Driver (local Mac, Local VM, VPS)
|
|
49839
50102
|
"double_click",
|
|
49840
50103
|
"right_click",
|
|
@@ -49855,6 +50118,14 @@ function screenFrameHash(png) {
|
|
|
49855
50118
|
function settledFrameIsNews(shownFrameHash, png) {
|
|
49856
50119
|
return shownFrameHash === void 0 || screenFrameHash(png) !== shownFrameHash;
|
|
49857
50120
|
}
|
|
50121
|
+
function screenSurfaceForTool(toolName) {
|
|
50122
|
+
const name2 = toolName.toLowerCase();
|
|
50123
|
+
if (name2.startsWith("mcp__computer__") || name2.startsWith("computer_")) return "computer";
|
|
50124
|
+
if (name2.startsWith("mcp__browser__")) return "browser";
|
|
50125
|
+
const bare = name2.replace(/^mcp__.+?__/, "");
|
|
50126
|
+
if (bare === "browser_click" || bare === "browser_fill") return "computer";
|
|
50127
|
+
return bare.startsWith("agent_browser_") || bare.startsWith("browser_") ? "browser" : "computer";
|
|
50128
|
+
}
|
|
49858
50129
|
|
|
49859
50130
|
// server/routine-requests.ts
|
|
49860
50131
|
import { createHash as createHash18 } from "node:crypto";
|
|
@@ -50862,13 +51133,13 @@ function profileRevision(bot) {
|
|
|
50862
51133
|
import { appendFile as appendFile2 } from "node:fs/promises";
|
|
50863
51134
|
import { createHash as createHash20, randomUUID as randomUUID13 } from "node:crypto";
|
|
50864
51135
|
import { closeSync as closeSync8, existsSync as existsSync19, fstatSync as fstatSync3, mkdirSync as mkdirSync21, openSync as openSync8, readSync as readSync3 } from "node:fs";
|
|
50865
|
-
import { join as
|
|
51136
|
+
import { join as join42 } from "node:path";
|
|
50866
51137
|
var FILE_NAME2 = "history.ndjson";
|
|
50867
51138
|
var ONE_LINER = 200;
|
|
50868
51139
|
var MAX_HISTORY_READ_BYTES = 8 * 1024 * 1024;
|
|
50869
51140
|
var writeQueues2 = /* @__PURE__ */ new Map();
|
|
50870
51141
|
function historyFile(botId) {
|
|
50871
|
-
return
|
|
51142
|
+
return join42(botFolder(botId), FILE_NAME2);
|
|
50872
51143
|
}
|
|
50873
51144
|
function oneLiner(value) {
|
|
50874
51145
|
return value.replace(/\s+/g, " ").trim().slice(0, ONE_LINER);
|
|
@@ -51265,8 +51536,8 @@ function parseBotDirectory(value) {
|
|
|
51265
51536
|
}
|
|
51266
51537
|
async function readBounded(response, maxBytes) {
|
|
51267
51538
|
const oversized = () => new Error("The bot directory response is too large");
|
|
51268
|
-
const
|
|
51269
|
-
if (
|
|
51539
|
+
const announced2 = Number(response.headers.get("content-length") ?? 0);
|
|
51540
|
+
if (announced2 > maxBytes) throw oversized();
|
|
51270
51541
|
if (!response.body) {
|
|
51271
51542
|
const raw = await response.text();
|
|
51272
51543
|
if (Buffer.byteLength(raw) > maxBytes) throw oversized();
|
|
@@ -51368,7 +51639,7 @@ function matchDirectoryBots(profile, bots, limit = 5) {
|
|
|
51368
51639
|
|
|
51369
51640
|
// server/project-scout.ts
|
|
51370
51641
|
import { readFileSync as readFileSync32, readdirSync as readdirSync6, statSync as statSync9 } from "node:fs";
|
|
51371
|
-
import { basename as basename5, join as
|
|
51642
|
+
import { basename as basename5, join as join43 } from "node:path";
|
|
51372
51643
|
|
|
51373
51644
|
// shared/import-name.ts
|
|
51374
51645
|
function takeImportName(value, taken, maxLength = 100) {
|
|
@@ -51572,7 +51843,7 @@ function isDir(path) {
|
|
|
51572
51843
|
}
|
|
51573
51844
|
}
|
|
51574
51845
|
function packageJson(cwd) {
|
|
51575
|
-
const raw = readText2(
|
|
51846
|
+
const raw = readText2(join43(cwd, "package.json"));
|
|
51576
51847
|
if (!raw) return { deps: /* @__PURE__ */ new Set() };
|
|
51577
51848
|
try {
|
|
51578
51849
|
const parsed = JSON.parse(raw);
|
|
@@ -51595,7 +51866,7 @@ function packageJson(cwd) {
|
|
|
51595
51866
|
}
|
|
51596
51867
|
}
|
|
51597
51868
|
function readme(cwd) {
|
|
51598
|
-
const raw = readText2(
|
|
51869
|
+
const raw = readText2(join43(cwd, "README.md")) ?? readText2(join43(cwd, "readme.md")) ?? readText2(join43(cwd, "README"));
|
|
51599
51870
|
if (!raw) return {};
|
|
51600
51871
|
let title;
|
|
51601
51872
|
let summary;
|
|
@@ -51669,12 +51940,12 @@ function scoutProject(cwd) {
|
|
|
51669
51940
|
const pkg = packageJson(cwd);
|
|
51670
51941
|
const md = readme(cwd);
|
|
51671
51942
|
const entries = new Set(listNames(cwd));
|
|
51672
|
-
const pythonDeps = `${readText2(
|
|
51673
|
-
${readText2(
|
|
51943
|
+
const pythonDeps = `${readText2(join43(cwd, "requirements.txt")) ?? ""}
|
|
51944
|
+
${readText2(join43(cwd, "pyproject.toml")) ?? ""}`.toLowerCase();
|
|
51674
51945
|
const hasDep = (dep) => pkg.deps.has(dep) || pythonDeps.length > 1 && pythonDeps.includes(dep);
|
|
51675
51946
|
const pathEvidence = (path) => {
|
|
51676
|
-
if (path.includes("/")) return isDir(
|
|
51677
|
-
if (entries.has(path)) return isDir(
|
|
51947
|
+
if (path.includes("/")) return isDir(join43(cwd, path)) && listNames(join43(cwd, path)).length > 0 ? `${path}/` : null;
|
|
51948
|
+
if (entries.has(path)) return isDir(join43(cwd, path)) ? `${path}/` : path;
|
|
51678
51949
|
return null;
|
|
51679
51950
|
};
|
|
51680
51951
|
const signals = [];
|
|
@@ -51687,7 +51958,7 @@ ${readText2(join42(cwd, "pyproject.toml")) ?? ""}`.toLowerCase();
|
|
|
51687
51958
|
}
|
|
51688
51959
|
if (detector.role === "docs" && evidence.length > 0) {
|
|
51689
51960
|
const hasMkdocs = evidence.includes("mkdocs.yml");
|
|
51690
|
-
const hasMarkdown = listNames(
|
|
51961
|
+
const hasMarkdown = listNames(join43(cwd, "docs")).some((name2) => name2.endsWith(".md"));
|
|
51691
51962
|
if (!hasMkdocs && !hasMarkdown) continue;
|
|
51692
51963
|
}
|
|
51693
51964
|
if (evidence.length > 0) signals.push({ role: detector.role, evidence: evidence.slice(0, 6) });
|
|
@@ -52182,8 +52453,8 @@ async function fetchJson(url2, maxBytes, fetcher) {
|
|
|
52182
52453
|
const error51 = Object.assign(new Error(`GitHub returned HTTP ${response.status}`), { status: response.status });
|
|
52183
52454
|
throw error51;
|
|
52184
52455
|
}
|
|
52185
|
-
const
|
|
52186
|
-
if (
|
|
52456
|
+
const announced2 = Number(response.headers.get("content-length") ?? 0);
|
|
52457
|
+
if (announced2 > maxBytes) throw new Error("The remote team file is too large");
|
|
52187
52458
|
const raw = await response.text();
|
|
52188
52459
|
if (Buffer.byteLength(raw) > maxBytes) throw new Error("The remote team file is too large");
|
|
52189
52460
|
try {
|
|
@@ -52199,8 +52470,8 @@ async function fetchText2(url2, maxBytes, fetcher) {
|
|
|
52199
52470
|
signal: AbortSignal.timeout(1e4)
|
|
52200
52471
|
});
|
|
52201
52472
|
if (!response.ok) throw Object.assign(new Error(`GitHub returned HTTP ${response.status}`), { status: response.status });
|
|
52202
|
-
const
|
|
52203
|
-
if (
|
|
52473
|
+
const announced2 = Number(response.headers.get("content-length") ?? 0);
|
|
52474
|
+
if (announced2 > maxBytes) throw new Error("The remote team file is too large");
|
|
52204
52475
|
const raw = await response.text();
|
|
52205
52476
|
if (Buffer.byteLength(raw) > maxBytes) throw new Error("The remote team file is too large");
|
|
52206
52477
|
return raw;
|
|
@@ -52277,7 +52548,7 @@ async function fetchGithubTeam(input, fetcher = fetch) {
|
|
|
52277
52548
|
|
|
52278
52549
|
// server/thread-events.ts
|
|
52279
52550
|
import { closeSync as closeSync9, fstatSync as fstatSync4, openSync as openSync9, readSync as readSync4 } from "node:fs";
|
|
52280
|
-
import { join as
|
|
52551
|
+
import { join as join44 } from "node:path";
|
|
52281
52552
|
var DEFAULT_LIMIT = 300;
|
|
52282
52553
|
var MAX_LIMIT = 2e3;
|
|
52283
52554
|
var READ_CHUNK = 64 * 1024;
|
|
@@ -52411,8 +52682,8 @@ function readThreadEvents(input) {
|
|
|
52411
52682
|
assertThreadId(threadId);
|
|
52412
52683
|
const requested = input.limit ?? DEFAULT_LIMIT;
|
|
52413
52684
|
const limit = Number.isFinite(requested) ? Math.max(1, Math.min(Math.trunc(requested), MAX_LIMIT)) : DEFAULT_LIMIT;
|
|
52414
|
-
const runtime = readRecentLines(
|
|
52415
|
-
const native = readRecentLines(
|
|
52685
|
+
const runtime = readRecentLines(join44(eventsDir, `${threadId}.ndjson`), limit, isRuntimeEvent);
|
|
52686
|
+
const native = readRecentLines(join44(nativeDir, `${threadId}.ndjson`), limit, isNativeRecord);
|
|
52416
52687
|
const merged = [
|
|
52417
52688
|
...runtime.lines.map((data) => ({ kind: "runtime", at: data.createdAt, data })),
|
|
52418
52689
|
...native.lines.map((data) => ({ kind: "native", at: data.at, data }))
|
|
@@ -52589,7 +52860,7 @@ function memberTurnSelection(selection) {
|
|
|
52589
52860
|
// server/webhooks.ts
|
|
52590
52861
|
import { createHash as createHash21, randomBytes as randomBytes5, randomUUID as randomUUID14, timingSafeEqual } from "node:crypto";
|
|
52591
52862
|
import { mkdirSync as mkdirSync22, readFileSync as readFileSync33 } from "node:fs";
|
|
52592
|
-
import { dirname as dirname13, join as
|
|
52863
|
+
import { dirname as dirname13, join as join45 } from "node:path";
|
|
52593
52864
|
var MAX_DELIVERIES = 2e3;
|
|
52594
52865
|
var MAX_ATTEMPTS = 2e3;
|
|
52595
52866
|
var MAX_EVENT_CHARS = 48e3;
|
|
@@ -52776,7 +53047,7 @@ var WebhookManager = class {
|
|
|
52776
53047
|
rate = /* @__PURE__ */ new Map();
|
|
52777
53048
|
constructor(options) {
|
|
52778
53049
|
this.options = options;
|
|
52779
|
-
this.file = options.file ??
|
|
53050
|
+
this.file = options.file ?? join45(DATA_DIR, "webhooks.json");
|
|
52780
53051
|
this.now = options.now ?? Date.now;
|
|
52781
53052
|
try {
|
|
52782
53053
|
const parsed = webhookFileSchema.safeParse(parseJson(readFileSync33(this.file, "utf8")));
|
|
@@ -53040,7 +53311,7 @@ var WebhookManager = class {
|
|
|
53040
53311
|
|
|
53041
53312
|
// server/skill-library.ts
|
|
53042
53313
|
import { existsSync as existsSync20, readFileSync as readFileSync34, readdirSync as readdirSync7 } from "node:fs";
|
|
53043
|
-
import { basename as basename6, join as
|
|
53314
|
+
import { basename as basename6, join as join46 } from "node:path";
|
|
53044
53315
|
var SAFE_ID = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
53045
53316
|
function strings(value) {
|
|
53046
53317
|
return Array.isArray(value) && value.every((item) => typeof item === "string" && item.trim()) ? value.map((item) => item.trim()) : null;
|
|
@@ -53069,19 +53340,19 @@ function parseSkillManifest(value, directory) {
|
|
|
53069
53340
|
};
|
|
53070
53341
|
}
|
|
53071
53342
|
function loadSkillDirectory(directory) {
|
|
53072
|
-
const manifestPath2 =
|
|
53073
|
-
const skillPath =
|
|
53343
|
+
const manifestPath2 = join46(directory, "manifest.json");
|
|
53344
|
+
const skillPath = join46(directory, "SKILL.md");
|
|
53074
53345
|
if (!existsSync20(manifestPath2) || !existsSync20(skillPath)) return null;
|
|
53075
53346
|
const manifest = parseSkillManifest(JSON.parse(readFileSync34(manifestPath2, "utf8")), directory);
|
|
53076
53347
|
const instructions = readFileSync34(skillPath, "utf8").trim();
|
|
53077
53348
|
if (!instructions.startsWith("---")) throw new Error(`${skillPath} has no skill frontmatter`);
|
|
53078
53349
|
return { manifest, instructions, directory };
|
|
53079
53350
|
}
|
|
53080
|
-
function loadBundledSkills(root = process.env.OMB_SKILLS_DIR ||
|
|
53351
|
+
function loadBundledSkills(root = process.env.OMB_SKILLS_DIR || join46(process.cwd(), "skills")) {
|
|
53081
53352
|
if (!existsSync20(root)) return [];
|
|
53082
53353
|
const skills = [];
|
|
53083
53354
|
for (const name2 of readdirSync7(root).sort()) {
|
|
53084
|
-
const directory =
|
|
53355
|
+
const directory = join46(root, name2);
|
|
53085
53356
|
const skill = loadSkillDirectory(directory);
|
|
53086
53357
|
if (skill) skills.push(skill);
|
|
53087
53358
|
}
|
|
@@ -53098,7 +53369,7 @@ function loadUserSkills(root) {
|
|
|
53098
53369
|
const skills = [];
|
|
53099
53370
|
for (const name2 of names) {
|
|
53100
53371
|
try {
|
|
53101
|
-
const skill = loadSkillDirectory(
|
|
53372
|
+
const skill = loadSkillDirectory(join46(root, name2));
|
|
53102
53373
|
if (skill) skills.push(skill);
|
|
53103
53374
|
} catch {
|
|
53104
53375
|
}
|
|
@@ -53802,7 +54073,7 @@ function createGracefulShutdown({
|
|
|
53802
54073
|
import { createHash as createHash22, randomUUID as randomUUID15 } from "node:crypto";
|
|
53803
54074
|
import { existsSync as existsSync21, linkSync as linkSync4, mkdirSync as mkdirSync23, readFileSync as readFileSync35, renameSync as renameSync6, unlinkSync as unlinkSync8, writeFileSync as writeFileSync16 } from "node:fs";
|
|
53804
54075
|
import { hostname as hostname4 } from "node:os";
|
|
53805
|
-
import { join as
|
|
54076
|
+
import { join as join47 } from "node:path";
|
|
53806
54077
|
var LEASE_NAME = "openmausbot-server.lease";
|
|
53807
54078
|
var DELEGATED_CHILD_DIR = ".openmausbot-server-child";
|
|
53808
54079
|
var CHILD_LEASE_ENV = "OPENMAUSBOT_INTERNAL_DATA_DIR_LEASE";
|
|
@@ -53961,10 +54232,10 @@ function prepareDataDir(dataDir, legacyDataDir) {
|
|
|
53961
54232
|
} catch (error51) {
|
|
53962
54233
|
throw leaseError("OpenMausBot cannot create its data directory.", error51);
|
|
53963
54234
|
}
|
|
53964
|
-
return
|
|
54235
|
+
return join47(dataDir, LEASE_NAME);
|
|
53965
54236
|
}
|
|
53966
54237
|
function assertNoLiveDelegatedChild(dataDir) {
|
|
53967
|
-
const childLeasePath =
|
|
54238
|
+
const childLeasePath = join47(dataDir, DELEGATED_CHILD_DIR, LEASE_NAME);
|
|
53968
54239
|
const child = readOwner(childLeasePath);
|
|
53969
54240
|
if (!child) return;
|
|
53970
54241
|
if (child.host !== hostname4()) {
|
|
@@ -54006,13 +54277,13 @@ function validateChildDelegation(dataDir, encoded) {
|
|
|
54006
54277
|
const capability = parseCapability(encoded);
|
|
54007
54278
|
const invalid = () => leaseError("The OpenMausBot desktop lease delegation is invalid; refusing to start to protect its state.");
|
|
54008
54279
|
if (!capability) throw invalid();
|
|
54009
|
-
const parentLeasePath =
|
|
54280
|
+
const parentLeasePath = join47(dataDir, LEASE_NAME);
|
|
54010
54281
|
const matchesLiveParent = (owner2) => Boolean(owner2 && owner2.pid === capability.pid && owner2.token === capability.token && owner2.host === hostname4() && processIsAlive2(owner2.pid));
|
|
54011
54282
|
if (!matchesLiveParent(readOwner(parentLeasePath))) {
|
|
54012
54283
|
throw invalid();
|
|
54013
54284
|
}
|
|
54014
54285
|
const childLease = acquireDataDirLeaseInternal(
|
|
54015
|
-
|
|
54286
|
+
join47(dataDir, DELEGATED_CHILD_DIR),
|
|
54016
54287
|
{ guardDelegatedChild: false }
|
|
54017
54288
|
);
|
|
54018
54289
|
if (!matchesLiveParent(readOwner(parentLeasePath))) {
|
|
@@ -54111,14 +54382,14 @@ function acquireDataDirLeaseForProcess(dataDir, environment = process.env) {
|
|
|
54111
54382
|
|
|
54112
54383
|
// server/enterprise.ts
|
|
54113
54384
|
import { existsSync as existsSync22 } from "node:fs";
|
|
54114
|
-
import { join as
|
|
54385
|
+
import { join as join48 } from "node:path";
|
|
54115
54386
|
import { pathToFileURL } from "node:url";
|
|
54116
54387
|
var layerSchema = external_exports.object({
|
|
54117
54388
|
customer: external_exports.string().min(1),
|
|
54118
54389
|
features: external_exports.array(external_exports.string().min(1)),
|
|
54119
54390
|
expiresAt: external_exports.string().nullable()
|
|
54120
54391
|
});
|
|
54121
|
-
var DEFAULT_DIR =
|
|
54392
|
+
var DEFAULT_DIR = join48(SERVER_ROOT, "..", "enterprise");
|
|
54122
54393
|
var current = { edition: "oss", features: [] };
|
|
54123
54394
|
function oss(notice) {
|
|
54124
54395
|
current = notice ? { edition: "oss", features: [], notice } : { edition: "oss", features: [] };
|
|
@@ -54127,7 +54398,7 @@ function oss(notice) {
|
|
|
54127
54398
|
async function loadEnterpriseLayer(options = {}) {
|
|
54128
54399
|
const dir = options.dir ?? process.env.OMB_ENTERPRISE_DIR ?? DEFAULT_DIR;
|
|
54129
54400
|
const licenseKey = options.licenseKey ?? process.env.OMB_LICENSE_KEY;
|
|
54130
|
-
const entry = [
|
|
54401
|
+
const entry = [join48(dir, "server", "index.ts"), join48(dir, "server", "index.js")].find((candidate) => existsSync22(candidate));
|
|
54131
54402
|
if (!entry) {
|
|
54132
54403
|
return oss(
|
|
54133
54404
|
licenseKey ? `OMB_LICENSE_KEY is set but no enterprise layer exists at ${dir}` : void 0
|
|
@@ -54559,6 +54830,7 @@ function resolveRequestAuth(req, options) {
|
|
|
54559
54830
|
if (!session.scopes.includes(needed)) {
|
|
54560
54831
|
return deny(403, `forbidden: this session lacks the ${needed} scope`);
|
|
54561
54832
|
}
|
|
54833
|
+
if (via !== "ticket") options.sessions.renew(session.id);
|
|
54562
54834
|
return { auth: { kind: "session", session, via, scopes: session.scopes }, status: 401, error: "" };
|
|
54563
54835
|
}
|
|
54564
54836
|
const proxied = isProxied(req);
|
|
@@ -54594,7 +54866,17 @@ var SCOPES = ["admin", "client"];
|
|
|
54594
54866
|
var PAIRING_CODE_ALPHABET = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
|
|
54595
54867
|
var PAIRING_CODE_LENGTH = 12;
|
|
54596
54868
|
var PAIRING_CODE_TTL_MS = 5 * 6e4;
|
|
54597
|
-
|
|
54869
|
+
function daysMs(value, fallbackDays) {
|
|
54870
|
+
const days = Number(value);
|
|
54871
|
+
const ok = Number.isInteger(days) && days >= 1 && days <= 3650;
|
|
54872
|
+
return (ok ? days : fallbackDays) * 24 * 60 * 6e4;
|
|
54873
|
+
}
|
|
54874
|
+
var SESSION_TTL_MS = daysMs(process.env.OMB_SESSION_TTL_DAYS, 30);
|
|
54875
|
+
var SESSION_MAX_AGE_MS = daysMs(process.env.OMB_SESSION_MAX_DAYS, 180);
|
|
54876
|
+
var SESSION_RENEW_WHEN_LEFT_MS = SESSION_TTL_MS / 2;
|
|
54877
|
+
function cookieMaxAgeSeconds(session, now = Date.now()) {
|
|
54878
|
+
return Math.max(1, Math.floor((session.expiresAt - now) / 1e3));
|
|
54879
|
+
}
|
|
54598
54880
|
var STREAM_TICKET_TTL_MS = 5 * 6e4;
|
|
54599
54881
|
var LOCKOUT = { failures: 10, windowMs: 6e4, lockMs: 6e4 };
|
|
54600
54882
|
var EXCHANGE_REPLAY_MS = 6e4;
|
|
@@ -54793,7 +55075,9 @@ var SessionRegistry = class {
|
|
|
54793
55075
|
scopes: [...pairing.scopes],
|
|
54794
55076
|
createdAt: now,
|
|
54795
55077
|
lastSeenAt: now,
|
|
54796
|
-
|
|
55078
|
+
// The absolute cap applies from the first term, so a TTL configured
|
|
55079
|
+
// longer than the cap does not hand out a session the cap forbids.
|
|
55080
|
+
expiresAt: now + Math.min(SESSION_TTL_MS, SESSION_MAX_AGE_MS)
|
|
54797
55081
|
};
|
|
54798
55082
|
this.sessions.push(record2);
|
|
54799
55083
|
this.lastSeenWrites.set(record2.id, now);
|
|
@@ -54817,6 +55101,25 @@ var SessionRegistry = class {
|
|
|
54817
55101
|
}
|
|
54818
55102
|
return record2;
|
|
54819
55103
|
}
|
|
55104
|
+
/** Sliding expiry. Called by the request gate once a request has passed
|
|
55105
|
+
* the origin and scope checks (so a rejected request never extends
|
|
55106
|
+
* anything): a still-valid session with half its term or less left is
|
|
55107
|
+
* renewed for the full term, capped at SESSION_MAX_AGE_MS from pairing.
|
|
55108
|
+
* Nothing expired is revived. Writes at most once per half-term, so it
|
|
55109
|
+
* adds nothing to the last-seen traffic. Returns whether it renewed. */
|
|
55110
|
+
renew(sessionId) {
|
|
55111
|
+
const record2 = this.sessions.find((s) => s.id === sessionId);
|
|
55112
|
+
const now = this.now();
|
|
55113
|
+
if (!record2 || record2.expiresAt <= now) return false;
|
|
55114
|
+
if (record2.expiresAt - now > SESSION_RENEW_WHEN_LEFT_MS) return false;
|
|
55115
|
+
const next = Math.min(now + SESSION_TTL_MS, record2.createdAt + SESSION_MAX_AGE_MS);
|
|
55116
|
+
if (next <= record2.expiresAt) return false;
|
|
55117
|
+
record2.expiresAt = next;
|
|
55118
|
+
record2.lastSeenAt = now;
|
|
55119
|
+
this.lastSeenWrites.set(record2.id, now);
|
|
55120
|
+
this.persist();
|
|
55121
|
+
return true;
|
|
55122
|
+
}
|
|
54820
55123
|
/** Still valid right now (prunes expiry first). */
|
|
54821
55124
|
isLive(sessionId) {
|
|
54822
55125
|
this.prune();
|
|
@@ -54859,7 +55162,7 @@ var SessionRegistry = class {
|
|
|
54859
55162
|
|
|
54860
55163
|
// server/brand.ts
|
|
54861
55164
|
import { existsSync as existsSync24, readFileSync as readFileSync37 } from "node:fs";
|
|
54862
|
-
import { join as
|
|
55165
|
+
import { join as join49 } from "node:path";
|
|
54863
55166
|
var HEX_COLOUR = /^#[0-9a-fA-F]{6}$/;
|
|
54864
55167
|
var MAX_LOGO_CHARS = 3e5;
|
|
54865
55168
|
var brandSchema = external_exports.object({
|
|
@@ -54876,7 +55179,7 @@ var brandSchema = external_exports.object({
|
|
|
54876
55179
|
}).strict();
|
|
54877
55180
|
var DEFAULT_BRAND = { name: "OpenMausBot" };
|
|
54878
55181
|
function brandFile() {
|
|
54879
|
-
return process.env.OMB_BRAND_FILE ||
|
|
55182
|
+
return process.env.OMB_BRAND_FILE || join49(DATA_DIR, "brand.json");
|
|
54880
55183
|
}
|
|
54881
55184
|
function loadBrand(options = {}) {
|
|
54882
55185
|
const file2 = options.file ?? brandFile();
|
|
@@ -57372,7 +57675,7 @@ function releaseDataDirLeaseAtExit() {
|
|
|
57372
57675
|
}
|
|
57373
57676
|
process.once("exit", releaseDataDirLeaseAtExit);
|
|
57374
57677
|
var ENVIRONMENT_ID = loadEnvironmentId(DATA_DIR);
|
|
57375
|
-
var sessions = new SessionRegistry({ file:
|
|
57678
|
+
var sessions = new SessionRegistry({ file: join50(DATA_DIR, "sessions.json") });
|
|
57376
57679
|
var SESSION_COOKIE = sessionCookieName(PORT, ENVIRONMENT_ID);
|
|
57377
57680
|
var DESKTOP_MANAGED = process.env.OMB_DESKTOP_PARENT === "1";
|
|
57378
57681
|
var desktopMutationToken = DESKTOP_MANAGED ? "" : void 0;
|
|
@@ -57382,7 +57685,7 @@ var cfg = loadConfig();
|
|
|
57382
57685
|
var registry2 = new ProviderRegistry(BUILT_IN_DRIVERS);
|
|
57383
57686
|
await registry2.load(instanceConfigs(cfg));
|
|
57384
57687
|
var bundledSkills = loadBundledSkills();
|
|
57385
|
-
var availableSkills = () => mergeSkills(bundledSkills, loadUserSkills(
|
|
57688
|
+
var availableSkills = () => mergeSkills(bundledSkills, loadUserSkills(join50(DATA_DIR, "skills")));
|
|
57386
57689
|
var utilityParentPort = process.parentPort;
|
|
57387
57690
|
function postDesktopPrivateMessage(message3) {
|
|
57388
57691
|
if (!utilityParentPort) return false;
|
|
@@ -57408,7 +57711,7 @@ function applyDesktopMutationTokenMessage(raw) {
|
|
|
57408
57711
|
return true;
|
|
57409
57712
|
}
|
|
57410
57713
|
var browserCleanup = new BrowserCleanupCoordinator({
|
|
57411
|
-
file:
|
|
57714
|
+
file: join50(DATA_DIR, "browser-cleanups.json"),
|
|
57412
57715
|
send: (request) => {
|
|
57413
57716
|
const status = browserEngineStatus();
|
|
57414
57717
|
const sessions2 = request.type === "openmausbot:browser-bot-deleted" && request.botId ? [browserSessionId(request.botId, "")] : request.partitionId ? [browserSessionId("", request.partitionId)] : [];
|
|
@@ -57765,10 +58068,7 @@ function askBotAndWait(targetBotId, message3, depth, fromBotId) {
|
|
|
57765
58068
|
});
|
|
57766
58069
|
}
|
|
57767
58070
|
async function defaultSelection() {
|
|
57768
|
-
|
|
57769
|
-
const available = described.filter((d) => d.snapshot.state === "available");
|
|
57770
|
-
const pick2 = available.find((d) => d.driverKind === "claudeAgent") ?? available[0];
|
|
57771
|
-
return { instanceId: pick2?.instanceId ?? "", model: pick2?.models.default ?? "" };
|
|
58071
|
+
return selectDefaultModelSelection(await registry2.describe(), cfg.defaultModelSelection);
|
|
57772
58072
|
}
|
|
57773
58073
|
function checkedModelSelection(raw, current2, requireAvailableModel = false) {
|
|
57774
58074
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
@@ -58538,12 +58838,16 @@ function broadcast(payload) {
|
|
|
58538
58838
|
data: ${JSON.stringify({ ...payload, seq: seq2 })}
|
|
58539
58839
|
|
|
58540
58840
|
`;
|
|
58541
|
-
|
|
58841
|
+
const clientFrame = kind === "config" ? `id: ${STREAM_ID}:${seq2}
|
|
58842
|
+
data: ${JSON.stringify({ ...configForAccess(payload, false), seq: seq2 })}
|
|
58843
|
+
|
|
58844
|
+
` : frame;
|
|
58845
|
+
replayBuffer.push({ seq: seq2, kind, frame: kind === "screen" ? null : frame, clientFrame: kind === "screen" ? null : clientFrame });
|
|
58542
58846
|
if (replayBuffer.length > REPLAY_MAX) replayBuffer.shift();
|
|
58543
58847
|
for (const client of [...sseClients]) {
|
|
58544
58848
|
if (!wants(client, kind)) continue;
|
|
58545
58849
|
try {
|
|
58546
|
-
client.res.write(frame);
|
|
58850
|
+
client.res.write(client.admin ? frame : clientFrame);
|
|
58547
58851
|
} catch {
|
|
58548
58852
|
sseClients.delete(client);
|
|
58549
58853
|
}
|
|
@@ -59042,7 +59346,7 @@ bus.subscribe((event) => {
|
|
|
59042
59346
|
if (bot) {
|
|
59043
59347
|
const touches = screenTouchingTool(toolName);
|
|
59044
59348
|
if (touches || /computer|screenshot|click|type_text|press_key|scroll|open_url|wait_for|browser_/i.test(toolName)) {
|
|
59045
|
-
pokeScreenPoller(bot.id, touches);
|
|
59349
|
+
pokeScreenPoller(bot.id, touches, screenSurfaceForTool(toolName));
|
|
59046
59350
|
}
|
|
59047
59351
|
}
|
|
59048
59352
|
}
|
|
@@ -59606,45 +59910,29 @@ function drainQueuedSends() {
|
|
|
59606
59910
|
var screenPollers = /* @__PURE__ */ new Map();
|
|
59607
59911
|
var SCREEN_POLL_MS = 6e3;
|
|
59608
59912
|
var SCREEN_MIN_GAP_MS = 3e3;
|
|
59609
|
-
function startScreenPoller(botId,
|
|
59913
|
+
function startScreenPoller(botId, captures, { screenIsTheWork = false } = {}) {
|
|
59914
|
+
if (!captures.computer && !captures.browser) return;
|
|
59610
59915
|
if (screenPollers.has(botId)) return;
|
|
59611
|
-
|
|
59612
|
-
|
|
59613
|
-
|
|
59916
|
+
const entry = Object.assign(createScreenFrameSource({
|
|
59917
|
+
captures,
|
|
59918
|
+
control: () => ({
|
|
59919
|
+
held: computerControl.snapshot(botId).held,
|
|
59920
|
+
revision: computerControlRevision.get(botId) ?? 0
|
|
59921
|
+
}),
|
|
59922
|
+
onFrame: (frame) => broadcast({ kind: "screen", botId, ...frame }),
|
|
59923
|
+
minGapMs: SCREEN_MIN_GAP_MS
|
|
59924
|
+
}), {
|
|
59614
59925
|
timer: null,
|
|
59615
|
-
capture: () => {
|
|
59616
|
-
if (computerControl.snapshot(botId).held) return Promise.resolve();
|
|
59617
|
-
if (!current2 && Date.now() - lastAt < SCREEN_MIN_GAP_MS) return Promise.resolve();
|
|
59618
|
-
current2 ??= (async () => {
|
|
59619
|
-
try {
|
|
59620
|
-
const frame = await captureOutsideHumanControl(
|
|
59621
|
-
() => ({
|
|
59622
|
-
held: computerControl.snapshot(botId).held,
|
|
59623
|
-
revision: computerControlRevision.get(botId) ?? 0
|
|
59624
|
-
}),
|
|
59625
|
-
capture
|
|
59626
|
-
);
|
|
59627
|
-
if (!frame) return;
|
|
59628
|
-
entry.last = frame;
|
|
59629
|
-
broadcast({ kind: "screen", botId, ...frame });
|
|
59630
|
-
} catch {
|
|
59631
|
-
} finally {
|
|
59632
|
-
lastAt = Date.now();
|
|
59633
|
-
current2 = null;
|
|
59634
|
-
}
|
|
59635
|
-
})();
|
|
59636
|
-
return current2;
|
|
59637
|
-
},
|
|
59638
|
-
last: null,
|
|
59639
59926
|
touched: screenIsTheWork
|
|
59640
|
-
};
|
|
59927
|
+
});
|
|
59641
59928
|
entry.timer = setInterval(() => void entry.capture(), SCREEN_POLL_MS);
|
|
59642
59929
|
screenPollers.set(botId, entry);
|
|
59643
59930
|
}
|
|
59644
|
-
function pokeScreenPoller(botId, touches) {
|
|
59931
|
+
function pokeScreenPoller(botId, touches, surface) {
|
|
59645
59932
|
const entry = screenPollers.get(botId);
|
|
59646
59933
|
if (!entry) return;
|
|
59647
59934
|
if (touches) entry.touched = true;
|
|
59935
|
+
if (touches && surface) entry.surface = surface;
|
|
59648
59936
|
void entry.capture();
|
|
59649
59937
|
}
|
|
59650
59938
|
function stopScreenPoller(botId) {
|
|
@@ -59666,7 +59954,7 @@ async function finalScreenFrame(botId, threadId) {
|
|
|
59666
59954
|
if (entry.timer) clearInterval(entry.timer);
|
|
59667
59955
|
screenPollers.delete(botId);
|
|
59668
59956
|
if (!entry.touched) return null;
|
|
59669
|
-
await entry.capture();
|
|
59957
|
+
await entry.capture(true);
|
|
59670
59958
|
const frame = entry.last;
|
|
59671
59959
|
if (!frame || !settledFrameIsNews(shownScreenHash(botId, threadId), frame.png)) return null;
|
|
59672
59960
|
settledScreenHashes.set(botId, screenFrameHash(frame.png));
|
|
@@ -59837,6 +60125,7 @@ async function startTurn(botId, text6, opts) {
|
|
|
59837
60125
|
}
|
|
59838
60126
|
const wants2 = plan.computer;
|
|
59839
60127
|
let previewCapture = null;
|
|
60128
|
+
let browserCapture = null;
|
|
59840
60129
|
let computerKind = null;
|
|
59841
60130
|
let autoVpsProblem = null;
|
|
59842
60131
|
if (wants2 === "vm") {
|
|
@@ -60005,6 +60294,10 @@ async function startTurn(botId, text6, opts) {
|
|
|
60005
60294
|
const selectedProfile = liveBot.browserProfile;
|
|
60006
60295
|
browser = browserIntegration(bot.id, selectedProfile);
|
|
60007
60296
|
if (browser) integrations.browser = browser.integration;
|
|
60297
|
+
if (browser) {
|
|
60298
|
+
const frame = { binaryPath: browser.integration.command, env: browser.integration.env };
|
|
60299
|
+
browserCapture = () => agentBrowserFrame(frame);
|
|
60300
|
+
}
|
|
60008
60301
|
}
|
|
60009
60302
|
await pendingCancelledProviderHandshakes.waitForClear(threadId);
|
|
60010
60303
|
if (!markDirectTurnDispatching(bot.id, dispatchClaimId, threadId)) {
|
|
@@ -60066,8 +60359,12 @@ async function startTurn(botId, text6, opts) {
|
|
|
60066
60359
|
if (!isExternalContextMarker(task2.lastInstanceId) || task2.lastInstanceId === externalContextMarker) {
|
|
60067
60360
|
store.markTaskDispatched(bot.id, threadId, instanceId);
|
|
60068
60361
|
}
|
|
60069
|
-
if (previewCapture && store.bot(bot.id)?.busy) {
|
|
60070
|
-
startScreenPoller(
|
|
60362
|
+
if ((previewCapture || browserCapture) && store.bot(bot.id)?.busy) {
|
|
60363
|
+
startScreenPoller(
|
|
60364
|
+
bot.id,
|
|
60365
|
+
{ ...previewCapture ? { computer: previewCapture } : {}, ...browserCapture ? { browser: browserCapture } : {} },
|
|
60366
|
+
{ screenIsTheWork: instance.driverKind === "boxAgent" }
|
|
60367
|
+
);
|
|
60071
60368
|
}
|
|
60072
60369
|
if (store.bot(bot.id)?.busy !== true) {
|
|
60073
60370
|
revokeInternalCapabilityGeneration(threadId, dispatchClaimId);
|
|
@@ -62286,6 +62583,15 @@ function configStatus() {
|
|
|
62286
62583
|
browserProfiles: cfg.browserProfiles ?? []
|
|
62287
62584
|
};
|
|
62288
62585
|
}
|
|
62586
|
+
function configForAccess(status, admin) {
|
|
62587
|
+
if (admin) return status;
|
|
62588
|
+
return {
|
|
62589
|
+
...status,
|
|
62590
|
+
vps: { configured: status.vps.configured, sshAlias: "" },
|
|
62591
|
+
profile: { name: status.profile.name, email: "" },
|
|
62592
|
+
browserProfiles: status.browserProfiles.map((profile) => Object.fromEntries(Object.entries(profile).filter(([key3]) => key3 !== "partitionId")))
|
|
62593
|
+
};
|
|
62594
|
+
}
|
|
62289
62595
|
function mcpServerResponse() {
|
|
62290
62596
|
return { servers: listMcpServers(cfg.mcpServers) };
|
|
62291
62597
|
}
|
|
@@ -62332,7 +62638,7 @@ var claudeUpdatesInFlight = /* @__PURE__ */ new Set();
|
|
|
62332
62638
|
function serveStatic(res, path) {
|
|
62333
62639
|
if (!STATIC_DIR) return false;
|
|
62334
62640
|
const safe = path === "/" ? "/index.html" : path.replace(/\.\./g, "");
|
|
62335
|
-
const file2 =
|
|
62641
|
+
const file2 = join50(STATIC_DIR, safe);
|
|
62336
62642
|
try {
|
|
62337
62643
|
const data = readFileSync38(file2);
|
|
62338
62644
|
res.writeHead(200, { "content-type": MIME[extname5(file2)] ?? "application/octet-stream" });
|
|
@@ -62340,7 +62646,7 @@ function serveStatic(res, path) {
|
|
|
62340
62646
|
return true;
|
|
62341
62647
|
} catch {
|
|
62342
62648
|
try {
|
|
62343
|
-
const data = readFileSync38(
|
|
62649
|
+
const data = readFileSync38(join50(STATIC_DIR, "index.html"));
|
|
62344
62650
|
res.writeHead(200, { "content-type": "text/html" });
|
|
62345
62651
|
res.end(data);
|
|
62346
62652
|
return true;
|
|
@@ -62388,7 +62694,12 @@ function readBody(req, limit = 1e6) {
|
|
|
62388
62694
|
});
|
|
62389
62695
|
}
|
|
62390
62696
|
var handleRequest = async (req, res) => {
|
|
62391
|
-
|
|
62697
|
+
let url2;
|
|
62698
|
+
try {
|
|
62699
|
+
url2 = new URL(req.url ?? "/", `http://localhost:${PORT}`);
|
|
62700
|
+
} catch {
|
|
62701
|
+
return json3(res, 400, { error: "invalid request URL" });
|
|
62702
|
+
}
|
|
62392
62703
|
const path = url2.pathname;
|
|
62393
62704
|
const method = req.method ?? "GET";
|
|
62394
62705
|
let m = null;
|
|
@@ -62414,7 +62725,7 @@ var handleRequest = async (req, res) => {
|
|
|
62414
62725
|
const environment = environmentDescriptor({ environmentId: ENVIRONMENT_ID, desktopManaged: DESKTOP_MANAGED });
|
|
62415
62726
|
if (wantsCookie) {
|
|
62416
62727
|
const secure = requestOrigin(req)?.startsWith("https://") === true;
|
|
62417
|
-
res.setHeader("set-cookie", serializeSessionCookie(SESSION_COOKIE, result.token, { secure, maxAgeSeconds:
|
|
62728
|
+
res.setHeader("set-cookie", serializeSessionCookie(SESSION_COOKIE, result.token, { secure, maxAgeSeconds: cookieMaxAgeSeconds(result.session) }));
|
|
62418
62729
|
return json3(res, 200, { session: result.session, environment });
|
|
62419
62730
|
}
|
|
62420
62731
|
return json3(res, 200, { token: result.token, session: result.session, environment });
|
|
@@ -62427,6 +62738,13 @@ var handleRequest = async (req, res) => {
|
|
|
62427
62738
|
loopbackMutationToken: desktopMutationToken,
|
|
62428
62739
|
companionMutationToken
|
|
62429
62740
|
});
|
|
62741
|
+
if (gate.auth?.kind === "session" && gate.auth.via === "cookie") {
|
|
62742
|
+
const presented = parseCookies(req.headers.cookie).get(SESSION_COOKIE);
|
|
62743
|
+
if (presented) {
|
|
62744
|
+
const secure = requestOrigin(req)?.startsWith("https://") === true;
|
|
62745
|
+
res.setHeader("set-cookie", serializeSessionCookie(SESSION_COOKIE, presented, { secure, maxAgeSeconds: cookieMaxAgeSeconds(gate.auth.session) }));
|
|
62746
|
+
}
|
|
62747
|
+
}
|
|
62430
62748
|
if (method === "GET" && path === "/api/health" && !gate.auth) {
|
|
62431
62749
|
return json3(res, 200, { app: "openmausbot" });
|
|
62432
62750
|
}
|
|
@@ -62472,7 +62790,7 @@ var handleRequest = async (req, res) => {
|
|
|
62472
62790
|
hint: base2 ? null : "this server has no public address to put in a link: set OMB_PUBLIC_URL, or open /pair on the address you use and type the code"
|
|
62473
62791
|
});
|
|
62474
62792
|
}
|
|
62475
|
-
if (method === "GET" && path === "/api/auth/pairing") return json3(res, 200, { pairings: sessions.openPairings() });
|
|
62793
|
+
if (method === "GET" && path === "/api/auth/pairing") return json3(res, 200, { pairings: sessions.openPairings(), publicUrl: PUBLIC_URL });
|
|
62476
62794
|
m = path.match(/^\/api\/auth\/pairing\/([\w-]+)$/);
|
|
62477
62795
|
if (m && method === "DELETE") {
|
|
62478
62796
|
const cancelled = sessions.cancelPairing(m[1]);
|
|
@@ -62697,6 +63015,16 @@ var handleRequest = async (req, res) => {
|
|
|
62697
63015
|
});
|
|
62698
63016
|
return json3(res, 201, proposed);
|
|
62699
63017
|
}
|
|
63018
|
+
const discloseRecall = (bot, roomThreadId, sourceThreadIds) => {
|
|
63019
|
+
const crossing = claimRecallCrossings(roomThreadId, sourceThreadIds);
|
|
63020
|
+
if (!crossing.count) return;
|
|
63021
|
+
store.appendMessage(roomThreadId, {
|
|
63022
|
+
role: "bot",
|
|
63023
|
+
kind: "activity",
|
|
63024
|
+
from: { botId: bot.id, name: bot.name, color: bot.color },
|
|
63025
|
+
tool: { name: recallCrossingLabel(bot.name, crossing.count), ok: true }
|
|
63026
|
+
});
|
|
63027
|
+
};
|
|
62700
63028
|
if (method === "GET" && path === "/api/internal/session-search") {
|
|
62701
63029
|
const fromBotId = String(url2.searchParams.get("fromBotId") ?? "");
|
|
62702
63030
|
const from = store.bot(fromBotId);
|
|
@@ -62710,11 +63038,16 @@ var handleRequest = async (req, res) => {
|
|
|
62710
63038
|
const rawLimit = Number(url2.searchParams.get("limit"));
|
|
62711
63039
|
const limit = Number.isFinite(rawLimit) && rawLimit > 0 ? Math.min(Math.trunc(rawLimit), 25) : 12;
|
|
62712
63040
|
const ownThreads = [.../* @__PURE__ */ new Set([from.threadId, ...(from.tasks ?? []).map((task2) => task2.threadId)])];
|
|
63041
|
+
const inRoom = Boolean(store.groupByThread(fromThreadId));
|
|
62713
63042
|
const hits = recallMessages(q, ownThreads, limit).map((hit) => ({
|
|
62714
63043
|
...hit,
|
|
62715
63044
|
task: store.taskByThread(from.id, hit.threadId)?.title,
|
|
62716
|
-
current: hit.threadId === fromThreadId
|
|
63045
|
+
current: hit.threadId === fromThreadId,
|
|
63046
|
+
crossed: inRoom && hit.threadId !== fromThreadId
|
|
62717
63047
|
}));
|
|
63048
|
+
if (inRoom) {
|
|
63049
|
+
discloseRecall(from, fromThreadId, hits.filter((hit) => hit.crossed).map((hit) => hit.threadId));
|
|
63050
|
+
}
|
|
62718
63051
|
return json3(res, 200, { hits });
|
|
62719
63052
|
}
|
|
62720
63053
|
if (method === "GET" && path === "/api/internal/session-read") {
|
|
@@ -62731,8 +63064,12 @@ var handleRequest = async (req, res) => {
|
|
|
62731
63064
|
const own3 = threadId === from.threadId || Boolean(store.taskByThread(from.id, threadId));
|
|
62732
63065
|
const message3 = own3 ? readMessageText(threadId, messageId) : null;
|
|
62733
63066
|
if (!message3) return json3(res, 404, { error: "no such message in your conversations" });
|
|
63067
|
+
const readInRoom = Boolean(store.groupByThread(fromThreadId));
|
|
63068
|
+
const readCrossed = readInRoom && threadId !== fromThreadId;
|
|
63069
|
+
if (readCrossed) discloseRecall(from, fromThreadId, [threadId]);
|
|
62734
63070
|
return json3(res, 200, {
|
|
62735
63071
|
...message3,
|
|
63072
|
+
crossed: readCrossed,
|
|
62736
63073
|
text: message3.text.length > SESSION_READ_MAX_CHARS ? `${message3.text.slice(0, SESSION_READ_MAX_CHARS)}\u2026` : message3.text,
|
|
62737
63074
|
task: store.taskByThread(from.id, threadId)?.title
|
|
62738
63075
|
});
|
|
@@ -63421,7 +63758,7 @@ var handleRequest = async (req, res) => {
|
|
|
63421
63758
|
return webhooks.remove(webhookMatch[1]) ? json3(res, 200, { ok: true }) : json3(res, 404, { error: "no such webhook" });
|
|
63422
63759
|
}
|
|
63423
63760
|
if (method === "GET" && path === "/api/events") {
|
|
63424
|
-
const client = { res, screens: url2.searchParams.get("screens") !== "off" };
|
|
63761
|
+
const client = { res, admin: auth.scopes.includes("admin"), screens: url2.searchParams.get("screens") !== "off" };
|
|
63425
63762
|
if (auth.kind === "session") client.sessionId = auth.session.id;
|
|
63426
63763
|
res.writeHead(200, {
|
|
63427
63764
|
"content-type": "text/event-stream",
|
|
@@ -63447,7 +63784,8 @@ var handleRequest = async (req, res) => {
|
|
|
63447
63784
|
);
|
|
63448
63785
|
if (resumed) {
|
|
63449
63786
|
for (const buffered of replayBuffer) {
|
|
63450
|
-
|
|
63787
|
+
const frame = client.admin ? buffered.frame : buffered.clientFrame;
|
|
63788
|
+
if (buffered.seq > since && frame && wants(client, buffered.kind)) res.write(frame);
|
|
63451
63789
|
}
|
|
63452
63790
|
}
|
|
63453
63791
|
sseClients.add(client);
|
|
@@ -64288,7 +64626,7 @@ data: ${JSON.stringify({ kind: "ping" })}
|
|
|
64288
64626
|
for (const threadId of threadIds) {
|
|
64289
64627
|
for (const dir of [EVENTS_DIR, NATIVE_DIR]) {
|
|
64290
64628
|
try {
|
|
64291
|
-
unlinkSync9(
|
|
64629
|
+
unlinkSync9(join50(dir, `${threadId}.ndjson`));
|
|
64292
64630
|
} catch {
|
|
64293
64631
|
}
|
|
64294
64632
|
}
|
|
@@ -65010,7 +65348,7 @@ data: ${JSON.stringify({ kind: "ping" })}
|
|
|
65010
65348
|
}
|
|
65011
65349
|
for (const dir of [EVENTS_DIR, NATIVE_DIR]) {
|
|
65012
65350
|
try {
|
|
65013
|
-
unlinkSync9(
|
|
65351
|
+
unlinkSync9(join50(dir, `${bot.threadId}.ndjson`));
|
|
65014
65352
|
} catch {
|
|
65015
65353
|
}
|
|
65016
65354
|
}
|
|
@@ -66099,16 +66437,7 @@ data: ${JSON.stringify({ kind: "ping" })}
|
|
|
66099
66437
|
}
|
|
66100
66438
|
}
|
|
66101
66439
|
if (method === "GET" && path === "/api/config") {
|
|
66102
|
-
|
|
66103
|
-
if (auth.kind === "session" && !auth.scopes.includes("admin")) {
|
|
66104
|
-
return json3(res, 200, {
|
|
66105
|
-
...status,
|
|
66106
|
-
vps: { configured: status.vps.configured, sshAlias: "" },
|
|
66107
|
-
profile: { name: status.profile.name, email: "" },
|
|
66108
|
-
browserProfiles: status.browserProfiles.map((profile) => Object.fromEntries(Object.entries(profile).filter(([key3]) => key3 !== "partitionId")))
|
|
66109
|
-
});
|
|
66110
|
-
}
|
|
66111
|
-
return json3(res, 200, status);
|
|
66440
|
+
return json3(res, 200, configForAccess(configStatus(), auth.scopes.includes("admin")));
|
|
66112
66441
|
}
|
|
66113
66442
|
if ((method === "PUT" || method === "PATCH") && path === "/api/config") {
|
|
66114
66443
|
const body = await readBody(req);
|
|
@@ -66767,7 +67096,7 @@ server.listen(PORT, "127.0.0.1", () => {
|
|
|
66767
67096
|
var TUNNEL_SOCKET = process.env.OMB_TUNNEL_SOCKET?.trim() || null;
|
|
66768
67097
|
var tunnelListener = null;
|
|
66769
67098
|
if (TUNNEL_SOCKET) {
|
|
66770
|
-
if (process.platform !== "win32")
|
|
67099
|
+
if (process.platform !== "win32") rmSync7(TUNNEL_SOCKET, { force: true });
|
|
66771
67100
|
tunnelListener = createServer3(handleRequest);
|
|
66772
67101
|
tunnelListener.listen(TUNNEL_SOCKET, () => {
|
|
66773
67102
|
console.log(`openmausbot tunnel listener on ${TUNNEL_SOCKET}`);
|