openmausbot 0.1.76 → 0.1.77
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/assets/{index-C6blGSq5.js → index-DeK-z9ld.js} +1 -1
- package/dist/assets/index-XdHJ-9CF.css +1 -0
- package/dist/assets/index-lhqE1umn.js +305 -0
- package/dist/index.html +2 -2
- package/dist-server/container-mcp.js +5 -1
- package/dist-server/drivers/agents-proxy.js +52 -1
- package/dist-server/enterprise/server/index.js +387 -29
- package/dist-server/index.js +1377 -459
- package/dist-server/openmausbot.js +725 -522
- package/dist-server/pair-cli.js +725 -522
- package/dist-server/server/agent-tool-policy.js +1 -0
- package/dist-server/server/browser-live.js +3 -0
- package/dist-server/server/config.js +16 -0
- package/dist-server/server/drivers/acp/core.js +3 -1
- package/dist-server/server/drivers/agents-proxy.js +58 -2
- package/dist-server/server/drivers/boxagent.js +1 -1
- package/dist-server/server/drivers/claude.js +3 -2
- package/dist-server/server/drivers/codex.js +3 -1
- package/dist-server/server/drivers/pi.js +4 -0
- package/dist-server/server/enterprise.js +38 -0
- package/dist-server/server/environment.js +3 -0
- package/dist-server/server/fleet-agent.js +57 -13
- package/dist-server/server/fleet-cli.js +248 -31
- package/dist-server/server/fleet.js +88 -14
- package/dist-server/server/hosted-contract.js +16 -0
- package/dist-server/server/index.js +671 -100
- package/dist-server/server/message-file.js +4 -0
- package/dist-server/server/provider-key-check.js +18 -1
- package/dist-server/server/request-auth.js +14 -5
- package/dist-server/server/sessions.js +115 -8
- package/dist-server/server/shared-computer-control.js +24 -0
- package/dist-server/server/shared-computers.js +131 -0
- package/dist-server/server/steer-queue.js +2 -0
- package/dist-server/server/store.js +20 -7
- package/dist-server/server/team-computers.js +108 -0
- package/dist-server/server/tool-summary.js +53 -1
- package/dist-server/server/workspace-backup.js +1 -0
- package/dist-server/shared/approval-mode.js +4 -1
- package/dist-server/shared/team-computer.js +1 -0
- package/dist-server/vps-container-mcp.js +5 -1
- package/enterprise/README.md +21 -3
- package/enterprise/server/index.js +387 -29
- package/package.json +1 -1
- package/dist/assets/index-BtT047dk.css +0 -1
- package/dist/assets/index-fKXFpB_J.js +0 -305
package/dist-server/index.js
CHANGED
|
@@ -5,8 +5,8 @@ var __export = (target, all2) => {
|
|
|
5
5
|
};
|
|
6
6
|
|
|
7
7
|
// server/index.ts
|
|
8
|
-
import { createHash as createHash29, randomBytes as randomBytes9, randomUUID as
|
|
9
|
-
import { existsSync as
|
|
8
|
+
import { createHash as createHash29, randomBytes as randomBytes9, randomUUID as randomUUID27, timingSafeEqual as timingSafeEqual5 } from "node:crypto";
|
|
9
|
+
import { existsSync as existsSync31, readFileSync as readFileSync43, rmSync as rmSync8, unlinkSync as unlinkSync13 } from "node:fs";
|
|
10
10
|
import { createServer as createServer3 } from "node:http";
|
|
11
11
|
import { extname as extname5, join as join61 } from "node:path";
|
|
12
12
|
|
|
@@ -14524,12 +14524,173 @@ function date4(params) {
|
|
|
14524
14524
|
// node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
|
|
14525
14525
|
config(en_default());
|
|
14526
14526
|
|
|
14527
|
+
// server/shared-computers.ts
|
|
14528
|
+
import { randomUUID, timingSafeEqual } from "node:crypto";
|
|
14529
|
+
var folder = external_exports.object({ id: external_exports.string().uuid(), name: external_exports.string().min(1).max(120), write: external_exports.boolean() }).strict();
|
|
14530
|
+
var sharedComputerRegistration = external_exports.object({
|
|
14531
|
+
id: external_exports.string().uuid(),
|
|
14532
|
+
name: external_exports.string().min(1).max(120),
|
|
14533
|
+
environmentId: external_exports.string().uuid(),
|
|
14534
|
+
folders: external_exports.array(folder).max(20),
|
|
14535
|
+
terminal: external_exports.boolean(),
|
|
14536
|
+
computer: external_exports.boolean()
|
|
14537
|
+
}).strict();
|
|
14538
|
+
var sharedComputerOperation = external_exports.object({
|
|
14539
|
+
computer_id: external_exports.string().uuid(),
|
|
14540
|
+
action: external_exports.enum(["list_files", "read_file", "write_file", "run_command", "computer_tools", "computer_call"]),
|
|
14541
|
+
folder_id: external_exports.string().uuid().optional(),
|
|
14542
|
+
path: external_exports.string().max(2048).optional(),
|
|
14543
|
+
content: external_exports.string().max(35e4).optional(),
|
|
14544
|
+
encoding: external_exports.enum(["utf8", "base64"]).optional(),
|
|
14545
|
+
expected_sha256: external_exports.string().regex(/^[a-f0-9]{64}$/).optional(),
|
|
14546
|
+
command: external_exports.string().max(8e3).optional(),
|
|
14547
|
+
tool_name: external_exports.string().max(100).optional(),
|
|
14548
|
+
arguments: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
|
|
14549
|
+
}).strict();
|
|
14550
|
+
var failure = (message3, status = 409) => Object.assign(new Error(message3), { status });
|
|
14551
|
+
var SharedComputers = class {
|
|
14552
|
+
computers = /* @__PURE__ */ new Map();
|
|
14553
|
+
sessionLive;
|
|
14554
|
+
constructor(sessionLive) {
|
|
14555
|
+
this.sessionLive = sessionLive;
|
|
14556
|
+
}
|
|
14557
|
+
register(registration, owner2, secret) {
|
|
14558
|
+
if (!/^[a-f0-9]{64}$/.test(secret)) throw failure("Invalid computer credential", 400);
|
|
14559
|
+
const old = this.computers.get(registration.id);
|
|
14560
|
+
if (old) {
|
|
14561
|
+
this.authorize(registration.id, owner2, secret);
|
|
14562
|
+
old.registration = registration;
|
|
14563
|
+
old.seen = Date.now();
|
|
14564
|
+
} else {
|
|
14565
|
+
for (const [id, entry] of this.computers) if (!this.online(entry) && entry.jobs.size === 0) this.computers.delete(id);
|
|
14566
|
+
if (this.computers.size >= 20) throw failure("Too many shared computers");
|
|
14567
|
+
this.computers.set(registration.id, { registration, owner: owner2, secret, seen: Date.now(), jobs: /* @__PURE__ */ new Map() });
|
|
14568
|
+
}
|
|
14569
|
+
}
|
|
14570
|
+
online(entry) {
|
|
14571
|
+
return this.sessionLive(entry.owner) && Date.now() - entry.seen < 4e4;
|
|
14572
|
+
}
|
|
14573
|
+
authorize(id, owner2, secret) {
|
|
14574
|
+
const entry = this.computers.get(id);
|
|
14575
|
+
if (!entry || entry.owner !== owner2 || !this.sessionLive(owner2) || !/^[a-f0-9]{64}$/.test(secret) || !timingSafeEqual(Buffer.from(secret), Buffer.from(entry.secret))) throw failure("Shared computer is disconnected or not authorized", 403);
|
|
14576
|
+
return entry;
|
|
14577
|
+
}
|
|
14578
|
+
list() {
|
|
14579
|
+
return [...this.computers.values()].filter((entry) => this.online(entry)).map((entry) => entry.registration);
|
|
14580
|
+
}
|
|
14581
|
+
async poll(id, owner2, secret) {
|
|
14582
|
+
const entry = this.authorize(id, owner2, secret);
|
|
14583
|
+
if (entry.wake) throw failure("A computer poll is already running");
|
|
14584
|
+
entry.seen = Date.now();
|
|
14585
|
+
const next = () => {
|
|
14586
|
+
for (const job of entry.jobs.values()) {
|
|
14587
|
+
if (!job.active()) {
|
|
14588
|
+
this.finish(entry, job, failure("The requesting turn ended"));
|
|
14589
|
+
continue;
|
|
14590
|
+
}
|
|
14591
|
+
if (!job.sent) {
|
|
14592
|
+
job.sent = true;
|
|
14593
|
+
return { id: job.id, operation: job.operation };
|
|
14594
|
+
}
|
|
14595
|
+
}
|
|
14596
|
+
return null;
|
|
14597
|
+
};
|
|
14598
|
+
const queued = next();
|
|
14599
|
+
if (queued) return queued;
|
|
14600
|
+
await new Promise((resolve16) => {
|
|
14601
|
+
const timer = setTimeout(() => {
|
|
14602
|
+
entry.wake = void 0;
|
|
14603
|
+
resolve16();
|
|
14604
|
+
}, 2e4);
|
|
14605
|
+
entry.wake = () => {
|
|
14606
|
+
clearTimeout(timer);
|
|
14607
|
+
entry.wake = void 0;
|
|
14608
|
+
resolve16();
|
|
14609
|
+
};
|
|
14610
|
+
});
|
|
14611
|
+
this.authorize(id, owner2, secret);
|
|
14612
|
+
entry.seen = Date.now();
|
|
14613
|
+
return next();
|
|
14614
|
+
}
|
|
14615
|
+
liveJob(id, owner2, secret, jobId) {
|
|
14616
|
+
const entry = this.authorize(id, owner2, secret);
|
|
14617
|
+
entry.seen = Date.now();
|
|
14618
|
+
const job = entry.jobs.get(jobId);
|
|
14619
|
+
return job?.sent === true && job.active();
|
|
14620
|
+
}
|
|
14621
|
+
complete(id, owner2, secret, jobId, result) {
|
|
14622
|
+
const entry = this.authorize(id, owner2, secret);
|
|
14623
|
+
const job = entry.jobs.get(jobId);
|
|
14624
|
+
if (!job || !job.sent) throw failure("This computer operation expired; it will not be replayed");
|
|
14625
|
+
this.finish(entry, job, job.active() ? null : failure("The requesting turn ended"), result);
|
|
14626
|
+
}
|
|
14627
|
+
disconnect(id, owner2, secret) {
|
|
14628
|
+
const entry = this.authorize(id, owner2, secret);
|
|
14629
|
+
this.computers.delete(id);
|
|
14630
|
+
entry.wake?.();
|
|
14631
|
+
for (const job of entry.jobs.values()) this.finish(entry, job, failure("Computer disconnected. An in-flight action may have completed; inspect before retrying."));
|
|
14632
|
+
}
|
|
14633
|
+
request(operation, active) {
|
|
14634
|
+
const entry = this.computers.get(operation.computer_id);
|
|
14635
|
+
if (!entry || !this.online(entry)) return Promise.reject(failure("This shared computer is offline. Keep its desktop app open; do not substitute files on the server."));
|
|
14636
|
+
if (entry.jobs.size >= 1) return Promise.reject(failure("This shared computer is busy. Wait for its current action to finish."));
|
|
14637
|
+
if (!active()) return Promise.reject(failure("The requesting turn ended", 401));
|
|
14638
|
+
return new Promise((resolve16, reject) => {
|
|
14639
|
+
const id = randomUUID();
|
|
14640
|
+
const job = { id, operation, active, sent: false, resolve: resolve16, reject, timer: setTimeout(() => this.finish(entry, job, failure("Computer action timed out. Its outcome is unknown; inspect before retrying.")), 45e3) };
|
|
14641
|
+
entry.jobs.set(id, job);
|
|
14642
|
+
entry.wake?.();
|
|
14643
|
+
});
|
|
14644
|
+
}
|
|
14645
|
+
close() {
|
|
14646
|
+
for (const entry of this.computers.values()) {
|
|
14647
|
+
entry.wake?.();
|
|
14648
|
+
for (const job of entry.jobs.values()) this.finish(entry, job, failure("Workspace stopped. Inspect any in-flight action before retrying."));
|
|
14649
|
+
}
|
|
14650
|
+
this.computers.clear();
|
|
14651
|
+
}
|
|
14652
|
+
finish(entry, job, error51, result) {
|
|
14653
|
+
clearTimeout(job.timer);
|
|
14654
|
+
entry.jobs.delete(job.id);
|
|
14655
|
+
if (error51) job.reject(error51);
|
|
14656
|
+
else job.resolve(result);
|
|
14657
|
+
}
|
|
14658
|
+
};
|
|
14659
|
+
|
|
14660
|
+
// server/shared-computer-control.ts
|
|
14661
|
+
var SharedComputerControl = class {
|
|
14662
|
+
leases = /* @__PURE__ */ new Map();
|
|
14663
|
+
resources;
|
|
14664
|
+
held;
|
|
14665
|
+
constructor(resources, held) {
|
|
14666
|
+
this.resources = resources;
|
|
14667
|
+
this.held = held;
|
|
14668
|
+
}
|
|
14669
|
+
acquire(id) {
|
|
14670
|
+
const owner2 = { threadId: `shared:${id}`, generation: id };
|
|
14671
|
+
if (this.held() || !this.resources.claim("computer:host", owner2)) {
|
|
14672
|
+
this.release(id);
|
|
14673
|
+
throw Object.assign(new Error("This computer is in use locally or held by a person. Wait, then observe it again before acting."), { status: 409 });
|
|
14674
|
+
}
|
|
14675
|
+
clearTimeout(this.leases.get(id));
|
|
14676
|
+
this.leases.set(id, setTimeout(() => this.release(id), 4e4));
|
|
14677
|
+
}
|
|
14678
|
+
release(id) {
|
|
14679
|
+
clearTimeout(this.leases.get(id));
|
|
14680
|
+
this.leases.delete(id);
|
|
14681
|
+
this.resources.release({ threadId: `shared:${id}`, generation: id });
|
|
14682
|
+
}
|
|
14683
|
+
close() {
|
|
14684
|
+
for (const id of this.leases.keys()) this.release(id);
|
|
14685
|
+
}
|
|
14686
|
+
};
|
|
14687
|
+
|
|
14527
14688
|
// server/room-handoffs.ts
|
|
14528
|
-
import { randomUUID as
|
|
14689
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
14529
14690
|
import { readFileSync } from "node:fs";
|
|
14530
14691
|
|
|
14531
14692
|
// server/atomic.ts
|
|
14532
|
-
import { randomUUID } from "node:crypto";
|
|
14693
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
14533
14694
|
import { closeSync, fsyncSync, openSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
|
|
14534
14695
|
var RENAME_RETRY_DELAYS_MS = [5, 10, 20, 40, 80];
|
|
14535
14696
|
var RETRYABLE_RENAME_CODES = /* @__PURE__ */ new Set(["EPERM", "EACCES", "EBUSY"]);
|
|
@@ -14549,7 +14710,7 @@ function renameWithRetry(tmp, path, rename3 = renameSync) {
|
|
|
14549
14710
|
}
|
|
14550
14711
|
}
|
|
14551
14712
|
function writeFileAtomic(path, data, options = {}) {
|
|
14552
|
-
const tmp = `${path}.${process.pid}.${
|
|
14713
|
+
const tmp = `${path}.${process.pid}.${randomUUID2()}.tmp`;
|
|
14553
14714
|
let fd = null;
|
|
14554
14715
|
try {
|
|
14555
14716
|
fd = openSync(tmp, "w", options.mode);
|
|
@@ -14681,7 +14842,7 @@ var RoomHandoffs = class {
|
|
|
14681
14842
|
}
|
|
14682
14843
|
const node2 = {
|
|
14683
14844
|
...target,
|
|
14684
|
-
id:
|
|
14845
|
+
id: randomUUID3(),
|
|
14685
14846
|
rootId: parent.rootId,
|
|
14686
14847
|
parentId: parent.id,
|
|
14687
14848
|
key: key3,
|
|
@@ -14840,7 +15001,9 @@ function isApprovalMode(value) {
|
|
|
14840
15001
|
return typeof value === "string" && APPROVAL_MODES.includes(value);
|
|
14841
15002
|
}
|
|
14842
15003
|
function approvalModeFor(bot) {
|
|
14843
|
-
|
|
15004
|
+
const grant = bot.approvalGrant;
|
|
15005
|
+
const otherThread = grant && typeof grant === "object" && "threadOnly" in grant && grant.threadOnly === true && "threadId" in grant && typeof grant.threadId === "string" && typeof bot.threadId === "string" && grant.threadId !== bot.threadId;
|
|
15006
|
+
if (grant && !otherThread) return "ask";
|
|
14844
15007
|
if (isApprovalMode(bot.approvalMode)) return bot.approvalMode;
|
|
14845
15008
|
return bot.autoApprove === true ? "auto" : "ask";
|
|
14846
15009
|
}
|
|
@@ -15644,7 +15807,7 @@ async function updateClaudeCli(cli, env, execute2 = execCli) {
|
|
|
15644
15807
|
}
|
|
15645
15808
|
|
|
15646
15809
|
// server/claude-accounts.ts
|
|
15647
|
-
import { randomUUID as
|
|
15810
|
+
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
15648
15811
|
import { realpathSync as realpathSync2 } from "node:fs";
|
|
15649
15812
|
import { join as join11 } from "node:path";
|
|
15650
15813
|
|
|
@@ -16019,7 +16182,11 @@ var featureConfigSchema = external_exports.object({
|
|
|
16019
16182
|
showToolCalls: external_exports.boolean().optional(),
|
|
16020
16183
|
/** Experimental built-in browser. Off until explicitly enabled; each bot
|
|
16021
16184
|
* also has its own switch. */
|
|
16022
|
-
browser: external_exports.boolean().optional()
|
|
16185
|
+
browser: external_exports.boolean().optional(),
|
|
16186
|
+
/** Opt-in computer sharing (a desktop lending folders, a terminal or
|
|
16187
|
+
* computer control to a workspace). Off until explicitly enabled; there is
|
|
16188
|
+
* no Settings toggle — see sharedComputersEnabled. */
|
|
16189
|
+
sharedComputers: external_exports.boolean().optional()
|
|
16023
16190
|
});
|
|
16024
16191
|
var onboardingConfigSchema = external_exports.object({
|
|
16025
16192
|
/** ISO timestamp of finishing (or skipping to the end of) the welcome flow. */
|
|
@@ -16219,6 +16386,9 @@ function showToolCallsEnabled(cfg2) {
|
|
|
16219
16386
|
function builtInBrowserEnabled(cfg2) {
|
|
16220
16387
|
return cfg2.features?.browser === true;
|
|
16221
16388
|
}
|
|
16389
|
+
function sharedComputersEnabled(cfg2) {
|
|
16390
|
+
return cfg2.features?.sharedComputers === true;
|
|
16391
|
+
}
|
|
16222
16392
|
var FLEET_NEUTRAL_KEYS = /* @__PURE__ */ new Set([
|
|
16223
16393
|
"profile",
|
|
16224
16394
|
"language",
|
|
@@ -16571,8 +16741,8 @@ function classifyResumeFailure(state) {
|
|
|
16571
16741
|
if (state.attempted && state.rejected) return "before-accept";
|
|
16572
16742
|
return "unknown";
|
|
16573
16743
|
}
|
|
16574
|
-
function mayReplay(
|
|
16575
|
-
return
|
|
16744
|
+
function mayReplay(failure5) {
|
|
16745
|
+
return failure5 === "before-accept";
|
|
16576
16746
|
}
|
|
16577
16747
|
function recoveryPromptFor(input) {
|
|
16578
16748
|
if (!mayReplay(input.failure)) return { text: input.currentText, replayed: false };
|
|
@@ -16582,7 +16752,7 @@ function recoveryPromptFor(input) {
|
|
|
16582
16752
|
}
|
|
16583
16753
|
|
|
16584
16754
|
// server/drivers/claude-login-auth.ts
|
|
16585
|
-
import { randomUUID as
|
|
16755
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
16586
16756
|
import { realpathSync } from "node:fs";
|
|
16587
16757
|
import { homedir as homedir3 } from "node:os";
|
|
16588
16758
|
import { basename as basename2, dirname as dirname2, isAbsolute, join as join4, resolve } from "node:path";
|
|
@@ -16653,7 +16823,7 @@ var ClaudeLoginController = class {
|
|
|
16653
16823
|
const flow3 = {
|
|
16654
16824
|
status: {
|
|
16655
16825
|
phase: "waiting",
|
|
16656
|
-
flowId:
|
|
16826
|
+
flowId: randomUUID4(),
|
|
16657
16827
|
authorizationUrl: null,
|
|
16658
16828
|
expiresAt: new Date(Date.now() + (this.options.lifetimeMs ?? 15 * 6e4)).toISOString()
|
|
16659
16829
|
},
|
|
@@ -17823,6 +17993,41 @@ function redactSecrets(input, depth = 0) {
|
|
|
17823
17993
|
}
|
|
17824
17994
|
|
|
17825
17995
|
// server/tool-summary.ts
|
|
17996
|
+
function toolDetailPreview(value) {
|
|
17997
|
+
if (value === void 0) return void 0;
|
|
17998
|
+
let budget = 200;
|
|
17999
|
+
let textBudget = 256e3;
|
|
18000
|
+
const bounded2 = (item, depth = 0) => {
|
|
18001
|
+
if (--budget < 0 || depth > 6) return "[additional data omitted]";
|
|
18002
|
+
if (typeof item === "string") {
|
|
18003
|
+
if (item.length > textBudget) return "[large content omitted]";
|
|
18004
|
+
if (/^data:[^,\s]+;base64,/i.test(item)) return "[binary content omitted]";
|
|
18005
|
+
if (/^\s*[[{]/.test(item)) {
|
|
18006
|
+
try {
|
|
18007
|
+
return bounded2(JSON.parse(item), depth + 1);
|
|
18008
|
+
} catch {
|
|
18009
|
+
}
|
|
18010
|
+
}
|
|
18011
|
+
textBudget -= item.length;
|
|
18012
|
+
return item;
|
|
18013
|
+
}
|
|
18014
|
+
if (typeof item === "bigint") return String(item);
|
|
18015
|
+
if (item === null || typeof item !== "object") return item;
|
|
18016
|
+
if (Array.isArray(item)) return item.slice(0, 40).map((child) => bounded2(child, depth + 1)).concat(item.length > 40 ? ["[additional items omitted]"] : []);
|
|
18017
|
+
const entries = Object.entries(item);
|
|
18018
|
+
const result = /* @__PURE__ */ Object.create(null);
|
|
18019
|
+
for (const [key3, child] of entries.slice(0, 40)) {
|
|
18020
|
+
result[key3] = /token|secret|password|passwd|cookie|authorization|api.?key|(^|[_.-])keys?$/i.test(key3) ? "[redacted]" : /^(data|base64|blob)$/i.test(key3) && typeof child === "string" ? "[binary content omitted]" : bounded2(child, depth + 1);
|
|
18021
|
+
}
|
|
18022
|
+
if (entries.length > 40) result["\u2026"] = "[additional fields omitted]";
|
|
18023
|
+
return result;
|
|
18024
|
+
};
|
|
18025
|
+
const safe = redactSecrets(bounded2(value));
|
|
18026
|
+
const text8 = typeof safe === "string" ? safe : JSON.stringify(safe, null, 2);
|
|
18027
|
+
if (!text8?.trim() || text8 === "{}" || text8 === "[]") return void 0;
|
|
18028
|
+
return text8.length > 6e3 ? `${text8.slice(0, 6e3)}
|
|
18029
|
+
[\u2026 preview shortened]` : text8;
|
|
18030
|
+
}
|
|
17826
18031
|
var QUESTION_LIMIT = 300;
|
|
17827
18032
|
var LIMIT = 200;
|
|
17828
18033
|
function fieldsOf(input) {
|
|
@@ -19246,7 +19451,8 @@ var ClaudeDriver = {
|
|
|
19246
19451
|
itemType: "tool",
|
|
19247
19452
|
itemId: b2.id,
|
|
19248
19453
|
title: b2.name,
|
|
19249
|
-
summary: commandSummary(b2.input)
|
|
19454
|
+
summary: commandSummary(b2.input),
|
|
19455
|
+
input: toolDetailPreview(b2.input)
|
|
19250
19456
|
});
|
|
19251
19457
|
}
|
|
19252
19458
|
}
|
|
@@ -19264,7 +19470,7 @@ var ClaudeDriver = {
|
|
|
19264
19470
|
case "user":
|
|
19265
19471
|
for (const b2 of Array.isArray(o.message?.content) ? o.message.content : []) {
|
|
19266
19472
|
if (b2.type === "tool_result") {
|
|
19267
|
-
emit({ ...base2(threadId, currentTurnId()), type: "item.completed", itemType: "tool", itemId: b2.tool_use_id, ok: !b2.is_error });
|
|
19473
|
+
emit({ ...base2(threadId, currentTurnId()), type: "item.completed", itemType: "tool", itemId: b2.tool_use_id, ok: !b2.is_error, output: toolDetailPreview(b2.content) });
|
|
19268
19474
|
}
|
|
19269
19475
|
}
|
|
19270
19476
|
break;
|
|
@@ -19662,7 +19868,7 @@ function assertSeparateClaudeAccount(map3, id, entry) {
|
|
|
19662
19868
|
}
|
|
19663
19869
|
function newClaudeAccount(cfg2, body) {
|
|
19664
19870
|
const instances = persistableInstanceConfigs(cfg2);
|
|
19665
|
-
const instanceId = `claude-${
|
|
19871
|
+
const instanceId = `claude-${randomUUID5()}`;
|
|
19666
19872
|
const existing = Object.values(instances).find((entry2) => entry2.driver === "claudeAgent");
|
|
19667
19873
|
const cli = existing ? rawConfig(existing).cli : void 0;
|
|
19668
19874
|
const entry = {
|
|
@@ -19699,7 +19905,7 @@ function claudeAccountInfo(instanceId, entry, cli) {
|
|
|
19699
19905
|
}
|
|
19700
19906
|
|
|
19701
19907
|
// server/browser-lifecycle-cleanup.ts
|
|
19702
|
-
import { randomUUID as
|
|
19908
|
+
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
19703
19909
|
import { readFileSync as readFileSync6 } from "node:fs";
|
|
19704
19910
|
var BOT_ID = /^[A-Za-z0-9_-]{1,120}$/;
|
|
19705
19911
|
var PROFILE_ID = /^[a-z0-9_-]{1,40}$/;
|
|
@@ -19829,7 +20035,7 @@ var BrowserCleanupCoordinator = class {
|
|
|
19829
20035
|
const existing = [...this.#pending.values()].find((request3) => request3.kind === kind && request3.id === id);
|
|
19830
20036
|
if (existing) return existing;
|
|
19831
20037
|
if (this.#pending.size >= MAX_PENDING) throw new Error("too many pending browser data cleanups");
|
|
19832
|
-
const request2 = kind === "bot" ? { requestId:
|
|
20038
|
+
const request2 = kind === "bot" ? { requestId: randomUUID6(), kind, id, phase: "prepared" } : { requestId: randomUUID6(), kind, id, partitionId, phase: "prepared" };
|
|
19833
20039
|
this.#pending.set(request2.requestId, request2);
|
|
19834
20040
|
try {
|
|
19835
20041
|
this.#save();
|
|
@@ -20366,7 +20572,7 @@ function validateBotCwd(input) {
|
|
|
20366
20572
|
}
|
|
20367
20573
|
|
|
20368
20574
|
// server/attachments.ts
|
|
20369
|
-
import { createHash as createHash4, randomUUID as
|
|
20575
|
+
import { createHash as createHash4, randomUUID as randomUUID7 } from "node:crypto";
|
|
20370
20576
|
import {
|
|
20371
20577
|
createReadStream,
|
|
20372
20578
|
linkSync,
|
|
@@ -20665,9 +20871,9 @@ async function saveFile(chunks, originalName, mime, options = {}) {
|
|
|
20665
20871
|
}
|
|
20666
20872
|
return { path: existing, name: name2, mime: normalized, bytes: bytes2 };
|
|
20667
20873
|
}
|
|
20668
|
-
const id = uploadId ??
|
|
20874
|
+
const id = uploadId ?? randomUUID7();
|
|
20669
20875
|
const path = join14(ATTACHMENTS_DIR, `${id}${extension2}`);
|
|
20670
|
-
const partialPath = join14(ATTACHMENTS_DIR, `.openmaus-upload-${id}-${
|
|
20876
|
+
const partialPath = join14(ATTACHMENTS_DIR, `.openmaus-upload-${id}-${randomUUID7()}.partial`);
|
|
20671
20877
|
const reservation = new AttachmentReservation();
|
|
20672
20878
|
if (expectedBytes) reservation.reserve(expectedBytes);
|
|
20673
20879
|
activePartials.add(partialPath);
|
|
@@ -20740,10 +20946,10 @@ function saveImage(bytes, mime, requestedUploadId) {
|
|
|
20740
20946
|
}
|
|
20741
20947
|
const reservation = new AttachmentReservation();
|
|
20742
20948
|
reservation.reserve(bytes.byteLength);
|
|
20743
|
-
const id = uploadId ??
|
|
20949
|
+
const id = uploadId ?? randomUUID7();
|
|
20744
20950
|
const name2 = `${id}${ext}`;
|
|
20745
20951
|
const path = join14(ATTACHMENTS_DIR, name2);
|
|
20746
|
-
const partialPath = join14(ATTACHMENTS_DIR, `.openmaus-upload-${id}-${
|
|
20952
|
+
const partialPath = join14(ATTACHMENTS_DIR, `.openmaus-upload-${id}-${randomUUID7()}.partial`);
|
|
20747
20953
|
activePartials.add(partialPath);
|
|
20748
20954
|
let partialCleanupFailed = false;
|
|
20749
20955
|
try {
|
|
@@ -27993,6 +28199,14 @@ function mimeFor(path) {
|
|
|
27993
28199
|
return "image/gif";
|
|
27994
28200
|
case ".webp":
|
|
27995
28201
|
return "image/webp";
|
|
28202
|
+
case ".mp4":
|
|
28203
|
+
return "video/mp4";
|
|
28204
|
+
case ".m4v":
|
|
28205
|
+
return "video/x-m4v";
|
|
28206
|
+
case ".webm":
|
|
28207
|
+
return "video/webm";
|
|
28208
|
+
case ".mov":
|
|
28209
|
+
return "video/quicktime";
|
|
27996
28210
|
case ".doc":
|
|
27997
28211
|
return "application/msword";
|
|
27998
28212
|
case ".docx":
|
|
@@ -28440,7 +28654,7 @@ function roomTurnTimeoutMessage(botName, minutes) {
|
|
|
28440
28654
|
import { createHash as createHash6 } from "node:crypto";
|
|
28441
28655
|
|
|
28442
28656
|
// server/environment.ts
|
|
28443
|
-
import { randomUUID as
|
|
28657
|
+
import { randomUUID as randomUUID8 } from "node:crypto";
|
|
28444
28658
|
import {
|
|
28445
28659
|
closeSync as closeSync3,
|
|
28446
28660
|
fsyncSync as fsyncSync2,
|
|
@@ -28479,8 +28693,8 @@ function loadEnvironmentId(dataDir) {
|
|
|
28479
28693
|
if (existing) return existing;
|
|
28480
28694
|
const directory = dirname5(file2);
|
|
28481
28695
|
mkdirSync4(directory, { recursive: true, mode: 448 });
|
|
28482
|
-
const id =
|
|
28483
|
-
const temporaryFile = join15(directory, `.environment-id.${process.pid}.${
|
|
28696
|
+
const id = randomUUID8();
|
|
28697
|
+
const temporaryFile = join15(directory, `.environment-id.${process.pid}.${randomUUID8()}.tmp`);
|
|
28484
28698
|
let descriptor;
|
|
28485
28699
|
try {
|
|
28486
28700
|
descriptor = openSync3(temporaryFile, "wx", 384);
|
|
@@ -28524,6 +28738,9 @@ function environmentDescriptor(input) {
|
|
|
28524
28738
|
version: serverVersion(),
|
|
28525
28739
|
capabilities: {
|
|
28526
28740
|
remoteSessions: true,
|
|
28741
|
+
// Never advertise a protocol this server would refuse: the routes are
|
|
28742
|
+
// gone unless features.sharedComputers is on, so the capability is too.
|
|
28743
|
+
...input.sharedComputers === true ? { sharedComputers: true } : {},
|
|
28527
28744
|
selfUpdate: input.desktopManaged ? "desktop-managed" : "operator",
|
|
28528
28745
|
emailSignIn: input.emailSignIn === true
|
|
28529
28746
|
}
|
|
@@ -28531,7 +28748,7 @@ function environmentDescriptor(input) {
|
|
|
28531
28748
|
}
|
|
28532
28749
|
|
|
28533
28750
|
// server/box-create-idempotency.ts
|
|
28534
|
-
import { createHash as createHash5, randomUUID as
|
|
28751
|
+
import { createHash as createHash5, randomUUID as randomUUID9 } from "node:crypto";
|
|
28535
28752
|
import { linkSync as linkSync3, mkdirSync as mkdirSync5, readFileSync as readFileSync10, unlinkSync as unlinkSync5, writeFileSync as writeFileSync6 } from "node:fs";
|
|
28536
28753
|
import { join as join16 } from "node:path";
|
|
28537
28754
|
var FILE = join16(DATA_DIR, "box-create-requests.json");
|
|
@@ -28687,7 +28904,7 @@ function claimReaperAuthority(expected) {
|
|
|
28687
28904
|
const candidate = {
|
|
28688
28905
|
version: 1,
|
|
28689
28906
|
pid: process.pid,
|
|
28690
|
-
token:
|
|
28907
|
+
token: randomUUID9(),
|
|
28691
28908
|
createdAt: Date.now(),
|
|
28692
28909
|
targetToken: expected.token
|
|
28693
28910
|
};
|
|
@@ -28723,7 +28940,7 @@ function acquireJournalLock() {
|
|
|
28723
28940
|
const owner2 = {
|
|
28724
28941
|
version: 1,
|
|
28725
28942
|
pid: process.pid,
|
|
28726
|
-
token:
|
|
28943
|
+
token: randomUUID9(),
|
|
28727
28944
|
createdAt: Date.now()
|
|
28728
28945
|
};
|
|
28729
28946
|
const candidate = `${LOCK_FILE}.candidate-${owner2.pid}-${owner2.token}`;
|
|
@@ -28795,7 +29012,7 @@ function beginBoxCreate(botId, requestBody) {
|
|
|
28795
29012
|
const request2 = {
|
|
28796
29013
|
botId,
|
|
28797
29014
|
requestBody,
|
|
28798
|
-
idempotencyKey:
|
|
29015
|
+
idempotencyKey: randomUUID9(),
|
|
28799
29016
|
createdAt: now
|
|
28800
29017
|
};
|
|
28801
29018
|
if (requests.length >= MAX_REQUESTS) throw recoveryStateError("full");
|
|
@@ -28842,7 +29059,7 @@ function adoptResolvedBox(botId, boxId) {
|
|
|
28842
29059
|
const adopted = sameBox ? { ...sameBox, resolved: true } : {
|
|
28843
29060
|
botId,
|
|
28844
29061
|
requestBody: JSON.stringify({ adopted: "legacy-name", boxId }),
|
|
28845
|
-
idempotencyKey:
|
|
29062
|
+
idempotencyKey: randomUUID9(),
|
|
28846
29063
|
createdAt: Date.now(),
|
|
28847
29064
|
boxId,
|
|
28848
29065
|
resolved: true
|
|
@@ -29761,6 +29978,101 @@ async function screenshotBox(cfg2, botId, knownBoxId) {
|
|
|
29761
29978
|
return { png: data, format: "jpeg" };
|
|
29762
29979
|
}
|
|
29763
29980
|
|
|
29981
|
+
// server/team-computers.ts
|
|
29982
|
+
import { randomUUID as randomUUID10 } from "node:crypto";
|
|
29983
|
+
import { existsSync as existsSync5, lstatSync, readFileSync as readFileSync11 } from "node:fs";
|
|
29984
|
+
var teamComputerCreate = external_exports.object({
|
|
29985
|
+
name: external_exports.string().trim().min(1).max(60).refine((value) => [...value].every((character) => character.charCodeAt(0) >= 32 && character.charCodeAt(0) !== 127)),
|
|
29986
|
+
acknowledgeCost: external_exports.literal(true),
|
|
29987
|
+
// Clients may keep this id across a lost create response. It is not a Box id.
|
|
29988
|
+
requestId: external_exports.string().uuid()
|
|
29989
|
+
}).strict();
|
|
29990
|
+
var teamComputerAssignment = external_exports.object({
|
|
29991
|
+
section: external_exports.string().trim().max(60).nullable(),
|
|
29992
|
+
acknowledgeSharedAccess: external_exports.literal(true)
|
|
29993
|
+
}).strict();
|
|
29994
|
+
var entrySchema = external_exports.object({
|
|
29995
|
+
id: external_exports.string().uuid(),
|
|
29996
|
+
name: teamComputerCreate.shape.name,
|
|
29997
|
+
section: external_exports.string().trim().max(60).nullable(),
|
|
29998
|
+
createdAt: external_exports.number().finite().nonnegative(),
|
|
29999
|
+
problem: external_exports.string().max(500).optional()
|
|
30000
|
+
}).strict();
|
|
30001
|
+
var fileSchema = external_exports.object({ version: external_exports.literal(1), environmentId: external_exports.string().uuid(), computers: external_exports.array(entrySchema).max(100) }).strict();
|
|
30002
|
+
var teamComputerOwner = (id) => `computer_${id}`;
|
|
30003
|
+
var failure2 = (message3, status = 409) => Object.assign(new Error(message3), { status });
|
|
30004
|
+
var TeamComputers = class {
|
|
30005
|
+
entries = [];
|
|
30006
|
+
problem;
|
|
30007
|
+
file;
|
|
30008
|
+
environmentId;
|
|
30009
|
+
constructor(file2, environmentId) {
|
|
30010
|
+
this.file = file2;
|
|
30011
|
+
this.environmentId = environmentId;
|
|
30012
|
+
try {
|
|
30013
|
+
if (!existsSync5(file2)) return;
|
|
30014
|
+
const stat6 = lstatSync(file2);
|
|
30015
|
+
if (!stat6.isFile() || stat6.isSymbolicLink() || stat6.size > 1e5) throw new Error("unsafe registry file");
|
|
30016
|
+
const saved = fileSchema.parse(JSON.parse(readFileSync11(file2, "utf8")));
|
|
30017
|
+
if (saved.environmentId !== environmentId) throw new Error("registry belongs to another workspace; its computers were not transferred");
|
|
30018
|
+
if (new Set(saved.computers.map((entry) => entry.id)).size !== saved.computers.length) throw new Error("duplicate computer identity");
|
|
30019
|
+
const sections = saved.computers.flatMap((entry) => entry.section === null ? [] : [entry.section]);
|
|
30020
|
+
if (new Set(sections).size !== sections.length) throw new Error("a team has more than one computer");
|
|
30021
|
+
this.entries = saved.computers;
|
|
30022
|
+
} catch (error51) {
|
|
30023
|
+
this.problem = `Team computer registry could not be loaded: ${error51 instanceof Error ? error51.message : String(error51)}`;
|
|
30024
|
+
}
|
|
30025
|
+
}
|
|
30026
|
+
list() {
|
|
30027
|
+
if (this.problem) throw failure2(this.problem, 503);
|
|
30028
|
+
return this.entries.map((entry) => ({ ...entry }));
|
|
30029
|
+
}
|
|
30030
|
+
get(id) {
|
|
30031
|
+
return this.list().find((entry) => entry.id === id);
|
|
30032
|
+
}
|
|
30033
|
+
forSection(section2) {
|
|
30034
|
+
return this.list().find((entry) => entry.section !== null && entry.section === (section2?.trim() || ""));
|
|
30035
|
+
}
|
|
30036
|
+
forBot(bot) {
|
|
30037
|
+
return bot.computer === void 0 && bot.cloudBackend !== "vps" ? this.forSection(bot.section) : void 0;
|
|
30038
|
+
}
|
|
30039
|
+
create(name2, id = randomUUID10()) {
|
|
30040
|
+
const entries = this.list();
|
|
30041
|
+
const existing = entries.find((entry2) => entry2.id === id);
|
|
30042
|
+
if (existing) {
|
|
30043
|
+
if (existing.name !== name2) throw failure2("This creation id already names a different computer");
|
|
30044
|
+
return existing;
|
|
30045
|
+
}
|
|
30046
|
+
if (entries.length >= 100) throw failure2("This workspace has reached its team computer limit");
|
|
30047
|
+
const entry = entrySchema.parse({ id, name: name2, section: null, createdAt: Date.now() });
|
|
30048
|
+
this.save([...entries, entry]);
|
|
30049
|
+
return { ...entry };
|
|
30050
|
+
}
|
|
30051
|
+
assign(id, section2) {
|
|
30052
|
+
section2 = entrySchema.shape.section.parse(section2);
|
|
30053
|
+
const entry = this.get(id);
|
|
30054
|
+
if (!entry) throw failure2("No such team computer", 404);
|
|
30055
|
+
if (entry.section !== null && section2 !== null && section2 !== entry.section) throw failure2("Unassign this computer before moving it to another team");
|
|
30056
|
+
if (section2 !== null && this.list().some((other) => other.id !== id && other.section === section2)) throw failure2("This team already has a computer; unassign it first");
|
|
30057
|
+
return this.patch(id, { section: section2 });
|
|
30058
|
+
}
|
|
30059
|
+
setProblem(id, problem) {
|
|
30060
|
+
return this.patch(id, { problem: problem?.slice(0, 500) });
|
|
30061
|
+
}
|
|
30062
|
+
patch(id, patch) {
|
|
30063
|
+
const entries = this.list();
|
|
30064
|
+
const entry = entries.find((candidate) => candidate.id === id);
|
|
30065
|
+
if (!entry) throw failure2("No such team computer", 404);
|
|
30066
|
+
const next = entrySchema.parse({ ...entry, ...patch });
|
|
30067
|
+
this.save(entries.map((candidate) => candidate.id === id ? next : candidate));
|
|
30068
|
+
return { ...next };
|
|
30069
|
+
}
|
|
30070
|
+
save(entries) {
|
|
30071
|
+
writeFileAtomic(this.file, JSON.stringify({ version: 1, environmentId: this.environmentId, computers: entries }), { mode: 384 });
|
|
30072
|
+
this.entries = entries;
|
|
30073
|
+
}
|
|
30074
|
+
};
|
|
30075
|
+
|
|
29764
30076
|
// server/cloud-backend.ts
|
|
29765
30077
|
var CLOUD_BACKEND_CHANGE_ERROR = "stop the active turn before changing the cloud backend";
|
|
29766
30078
|
var BOX_ACCOUNT_RESOURCES_ERROR = "remove this installation's cloud computers before changing or clearing the Box account";
|
|
@@ -29781,7 +30093,7 @@ function vpsAliasResourceChangeError(instanceCount) {
|
|
|
29781
30093
|
}
|
|
29782
30094
|
|
|
29783
30095
|
// server/composio.ts
|
|
29784
|
-
import { createHash as createHash7, randomUUID as
|
|
30096
|
+
import { createHash as createHash7, randomUUID as randomUUID11 } from "node:crypto";
|
|
29785
30097
|
|
|
29786
30098
|
// shared/connector-availability.ts
|
|
29787
30099
|
var MANAGED_CUSTOM_AUTH_TOOLKITS = /* @__PURE__ */ new Set(["twitter", "x"]);
|
|
@@ -30076,13 +30388,13 @@ async function prepareProjectSession(apiKey, current2, knownAuthConfigs) {
|
|
|
30076
30388
|
if (existing && supportsMultiAccount(existing) && (sessionCoversAuthConfigs(existing, authConfigs) || authConfigUpgradeAttempted.has(authConfigsKey(existing.session_id, authConfigs)))) {
|
|
30077
30389
|
return {
|
|
30078
30390
|
apiKey: trimmed,
|
|
30079
|
-
userId: existing.config?.user_id ?? current2.userId ?? `openmausbot_${
|
|
30391
|
+
userId: existing.config?.user_id ?? current2.userId ?? `openmausbot_${randomUUID11()}`,
|
|
30080
30392
|
sessionId: existing.session_id
|
|
30081
30393
|
};
|
|
30082
30394
|
}
|
|
30083
30395
|
priorUserId = existing?.config?.user_id ?? priorUserId;
|
|
30084
30396
|
}
|
|
30085
|
-
const userId = priorUserId ?? `openmausbot_${
|
|
30397
|
+
const userId = priorUserId ?? `openmausbot_${randomUUID11()}`;
|
|
30086
30398
|
const sessionRequest = {
|
|
30087
30399
|
user_id: userId,
|
|
30088
30400
|
manage_connections: {
|
|
@@ -30741,9 +31053,9 @@ import {
|
|
|
30741
31053
|
closeSync as closeSync4,
|
|
30742
31054
|
constants as constants2,
|
|
30743
31055
|
fstatSync,
|
|
30744
|
-
lstatSync,
|
|
31056
|
+
lstatSync as lstatSync2,
|
|
30745
31057
|
openSync as openSync4,
|
|
30746
|
-
readFileSync as
|
|
31058
|
+
readFileSync as readFileSync12
|
|
30747
31059
|
} from "node:fs";
|
|
30748
31060
|
import { homedir as homedir8 } from "node:os";
|
|
30749
31061
|
import { dirname as dirname6, join as join17 } from "node:path";
|
|
@@ -30894,14 +31206,14 @@ function privateCache(path) {
|
|
|
30894
31206
|
if (uid === void 0) return null;
|
|
30895
31207
|
let descriptor = null;
|
|
30896
31208
|
try {
|
|
30897
|
-
const parentStatus =
|
|
30898
|
-
const fileStatus =
|
|
31209
|
+
const parentStatus = lstatSync2(dirname6(path));
|
|
31210
|
+
const fileStatus = lstatSync2(path);
|
|
30899
31211
|
if (parentStatus.isSymbolicLink() || !parentStatus.isDirectory() || parentStatus.uid !== uid || (parentStatus.mode & 511) !== 448) return null;
|
|
30900
31212
|
if (fileStatus.isSymbolicLink() || !fileStatus.isFile() || fileStatus.uid !== uid || (fileStatus.mode & 511) !== 384 || fileStatus.size > MAX_CACHE_BYTES) return null;
|
|
30901
31213
|
descriptor = openSync4(path, constants2.O_RDONLY | (constants2.O_NOFOLLOW ?? 0));
|
|
30902
31214
|
const openedStatus = fstatSync(descriptor);
|
|
30903
31215
|
if (!openedStatus.isFile() || openedStatus.uid !== uid || (openedStatus.mode & 511) !== 384 || openedStatus.dev !== fileStatus.dev || openedStatus.ino !== fileStatus.ino || openedStatus.size > MAX_CACHE_BYTES) return null;
|
|
30904
|
-
const contents =
|
|
31216
|
+
const contents = readFileSync12(descriptor);
|
|
30905
31217
|
return contents.length <= MAX_CACHE_BYTES ? contents : null;
|
|
30906
31218
|
} catch {
|
|
30907
31219
|
return null;
|
|
@@ -31128,7 +31440,7 @@ var ComputerControl = class {
|
|
|
31128
31440
|
};
|
|
31129
31441
|
|
|
31130
31442
|
// server/engine-install.ts
|
|
31131
|
-
import { existsSync as
|
|
31443
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync6 } from "node:fs";
|
|
31132
31444
|
import { join as join18 } from "node:path";
|
|
31133
31445
|
import { stripVTControlCharacters as stripVTControlCharacters2 } from "node:util";
|
|
31134
31446
|
var MAX_OUTPUT2 = 64 * 1024;
|
|
@@ -31235,7 +31547,7 @@ function runNpm(args, env, cwd, timeoutMs) {
|
|
|
31235
31547
|
}
|
|
31236
31548
|
|
|
31237
31549
|
// server/usage-ledger.ts
|
|
31238
|
-
import { readFileSync as
|
|
31550
|
+
import { readFileSync as readFileSync13 } from "node:fs";
|
|
31239
31551
|
import { appendFile as appendFile2, mkdir as mkdir2 } from "node:fs/promises";
|
|
31240
31552
|
import { join as join19 } from "node:path";
|
|
31241
31553
|
|
|
@@ -31310,7 +31622,7 @@ function readUsage(dataDir, range) {
|
|
|
31310
31622
|
for (const key3 of monthsBetween(range.from, range.to)) {
|
|
31311
31623
|
let text8;
|
|
31312
31624
|
try {
|
|
31313
|
-
text8 =
|
|
31625
|
+
text8 = readFileSync13(join19(dataDir, DIR, `${key3}.jsonl`), "utf8");
|
|
31314
31626
|
} catch {
|
|
31315
31627
|
continue;
|
|
31316
31628
|
}
|
|
@@ -31474,6 +31786,7 @@ function modelIds(body) {
|
|
|
31474
31786
|
return ids;
|
|
31475
31787
|
}
|
|
31476
31788
|
async function checkProviderKey(input, fetchImpl = fetch, timeoutMs = 1e4) {
|
|
31789
|
+
if (!input.key.trim()) return { ok: false, reason: "rejected" };
|
|
31477
31790
|
let url2;
|
|
31478
31791
|
try {
|
|
31479
31792
|
url2 = new URL(providerModelsUrl(input.provider, input.url));
|
|
@@ -31483,6 +31796,8 @@ async function checkProviderKey(input, fetchImpl = fetch, timeoutMs = 1e4) {
|
|
|
31483
31796
|
if (url2.protocol !== "https:" && !(url2.protocol === "http:" && isLoopback(url2.hostname))) {
|
|
31484
31797
|
return { ok: false, reason: "unexpected" };
|
|
31485
31798
|
}
|
|
31799
|
+
const authenticate = input.provider === "openaiCompat" && url2.origin === "https://openrouter.ai" && url2.pathname === "/api/v1/models";
|
|
31800
|
+
if (authenticate) url2.pathname = "/api/v1/key";
|
|
31486
31801
|
const headers = input.provider === "anthropic" ? { "x-api-key": input.key, "anthropic-version": "2023-06-01" } : { authorization: `Bearer ${input.key}` };
|
|
31487
31802
|
const controller = new AbortController();
|
|
31488
31803
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
@@ -31491,7 +31806,15 @@ async function checkProviderKey(input, fetchImpl = fetch, timeoutMs = 1e4) {
|
|
|
31491
31806
|
if (response.status === 401 || response.status === 403) return { ok: false, reason: "rejected", status: response.status };
|
|
31492
31807
|
if (!response.ok) return { ok: false, reason: "unexpected", status: response.status };
|
|
31493
31808
|
const body = await response.json().catch(() => null);
|
|
31494
|
-
return { ok:
|
|
31809
|
+
if (!body || typeof body !== "object") return { ok: false, reason: "unexpected", status: response.status };
|
|
31810
|
+
if (authenticate) {
|
|
31811
|
+
const data = body.data;
|
|
31812
|
+
if (!data || typeof data !== "object" || Array.isArray(data) || typeof data.is_free_tier !== "boolean") {
|
|
31813
|
+
return { ok: false, reason: "unexpected", status: response.status };
|
|
31814
|
+
}
|
|
31815
|
+
return { ok: true, check: "authentication", models: [] };
|
|
31816
|
+
}
|
|
31817
|
+
return { ok: true, check: "models", models: modelIds(body) };
|
|
31495
31818
|
} catch {
|
|
31496
31819
|
return { ok: false, reason: "unreachable" };
|
|
31497
31820
|
} finally {
|
|
@@ -31500,7 +31823,7 @@ async function checkProviderKey(input, fetchImpl = fetch, timeoutMs = 1e4) {
|
|
|
31500
31823
|
}
|
|
31501
31824
|
|
|
31502
31825
|
// server/enterprise.ts
|
|
31503
|
-
import { existsSync as
|
|
31826
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
31504
31827
|
import { join as join20 } from "node:path";
|
|
31505
31828
|
import { pathToFileURL } from "node:url";
|
|
31506
31829
|
var layerSchema = external_exports.object({
|
|
@@ -31510,14 +31833,38 @@ var layerSchema = external_exports.object({
|
|
|
31510
31833
|
});
|
|
31511
31834
|
var DEFAULT_DIR = join20(SERVER_ROOT, "..", "enterprise");
|
|
31512
31835
|
var current = { edition: "oss", features: [] };
|
|
31836
|
+
var workspaceAccessFactory;
|
|
31837
|
+
function hostedWorkspaceConfigured(env = process.env) {
|
|
31838
|
+
return env.OMB_ADMIN_URL !== void 0 || env.OMB_ADMIN_WORKSPACE !== void 0 || env.OMB_ADMIN_MEMBERSHIP !== void 0;
|
|
31839
|
+
}
|
|
31840
|
+
function hostedWorkspaceConfiguration(env = process.env) {
|
|
31841
|
+
try {
|
|
31842
|
+
const origin = (raw) => {
|
|
31843
|
+
if (!raw || raw !== raw.trim()) throw new Error("missing origin");
|
|
31844
|
+
const url2 = new URL(raw);
|
|
31845
|
+
if (url2.protocol !== "https:" || raw !== url2.origin && raw !== `${url2.origin}/`) throw new Error("invalid origin");
|
|
31846
|
+
return url2;
|
|
31847
|
+
};
|
|
31848
|
+
if (!/^[a-z][a-z0-9-]{1,30}$/.test(env.OMB_ADMIN_WORKSPACE ?? "")) return null;
|
|
31849
|
+
if (env.OMB_ADMIN_MEMBERSHIP !== void 0 && !["local", "portal"].includes(env.OMB_ADMIN_MEMBERSHIP)) return null;
|
|
31850
|
+
return { admin: origin(env.OMB_ADMIN_URL), tenant: origin(env.OMB_PUBLIC_URL), workspace: env.OMB_ADMIN_WORKSPACE, portalMembership: env.OMB_ADMIN_MEMBERSHIP === "portal" };
|
|
31851
|
+
} catch {
|
|
31852
|
+
return null;
|
|
31853
|
+
}
|
|
31854
|
+
}
|
|
31855
|
+
function createWorkspaceAccess(options) {
|
|
31856
|
+
if (!hostedWorkspaceConfigured(options.env)) return null;
|
|
31857
|
+
return workspaceAccessFactory?.({ ...options, entitled: () => entitled("admin") }) ?? null;
|
|
31858
|
+
}
|
|
31513
31859
|
function oss(notice) {
|
|
31514
31860
|
current = notice ? { edition: "oss", features: [], notice } : { edition: "oss", features: [] };
|
|
31515
31861
|
return current;
|
|
31516
31862
|
}
|
|
31517
31863
|
async function loadEnterpriseLayer(options = {}) {
|
|
31864
|
+
workspaceAccessFactory = void 0;
|
|
31518
31865
|
const dir = options.dir ?? process.env.OMB_ENTERPRISE_DIR ?? DEFAULT_DIR;
|
|
31519
31866
|
const licenseKey = options.licenseKey ?? process.env.OMB_LICENSE_KEY;
|
|
31520
|
-
const entry = [join20(dir, "server", "index.ts"), join20(dir, "server", "index.js")].find((candidate) =>
|
|
31867
|
+
const entry = [join20(dir, "server", "index.ts"), join20(dir, "server", "index.js")].find((candidate) => existsSync7(candidate));
|
|
31521
31868
|
if (!entry) {
|
|
31522
31869
|
return oss(
|
|
31523
31870
|
licenseKey ? `OMB_LICENSE_KEY is set but no enterprise layer exists at ${dir}` : void 0
|
|
@@ -31525,6 +31872,8 @@ async function loadEnterpriseLayer(options = {}) {
|
|
|
31525
31872
|
}
|
|
31526
31873
|
try {
|
|
31527
31874
|
const loaded = await import(pathToFileURL(entry).href);
|
|
31875
|
+
const access2 = Reflect.get(Object(loaded), "createWorkspaceAccess");
|
|
31876
|
+
if (typeof access2 === "function") workspaceAccessFactory = access2;
|
|
31528
31877
|
const register = Reflect.get(Object(loaded), "register");
|
|
31529
31878
|
if (typeof register !== "function") throw new Error(`${entry} does not export register()`);
|
|
31530
31879
|
if (!licenseKey) return oss("enterprise layer present but OMB_LICENSE_KEY is not set");
|
|
@@ -31618,13 +31967,13 @@ function assertWithinBudget(cfg2, dataDir, now = /* @__PURE__ */ new Date(), isE
|
|
|
31618
31967
|
}
|
|
31619
31968
|
|
|
31620
31969
|
// server/fleet-client.ts
|
|
31621
|
-
import { existsSync as
|
|
31970
|
+
import { existsSync as existsSync8 } from "node:fs";
|
|
31622
31971
|
import { request } from "node:http";
|
|
31623
31972
|
function fleetSocketPath(env = process.env) {
|
|
31624
31973
|
return env.OMB_FLEET_SOCKET?.trim() || "/run/openmausbot/fleet.sock";
|
|
31625
31974
|
}
|
|
31626
31975
|
function fleetAvailable(socketPath = fleetSocketPath()) {
|
|
31627
|
-
return
|
|
31976
|
+
return existsSync8(socketPath);
|
|
31628
31977
|
}
|
|
31629
31978
|
function fleetRequest(socketPath, method, path, body, timeoutMs = 15 * 6e4) {
|
|
31630
31979
|
return new Promise((resolve16, reject) => {
|
|
@@ -31659,6 +32008,15 @@ function fleetRequest(socketPath, method, path, body, timeoutMs = 15 * 6e4) {
|
|
|
31659
32008
|
});
|
|
31660
32009
|
}
|
|
31661
32010
|
|
|
32011
|
+
// server/hosted-contract.ts
|
|
32012
|
+
var HOSTED_CONTRACT_VERSION = 1;
|
|
32013
|
+
var HOSTED_CONTRACT_HEADER = "x-omb-hosted-contract-version";
|
|
32014
|
+
var HOSTED_CONTRACT_METADATA = Object.freeze({
|
|
32015
|
+
contractVersion: HOSTED_CONTRACT_VERSION,
|
|
32016
|
+
supportedContractVersions: Object.freeze([HOSTED_CONTRACT_VERSION]),
|
|
32017
|
+
legacyPolicy: "legacy-v1"
|
|
32018
|
+
});
|
|
32019
|
+
|
|
31662
32020
|
// server/notify.ts
|
|
31663
32021
|
function summarize(text8, max = 140) {
|
|
31664
32022
|
const line = text8.replace(/```[\s\S]*?```/g, " ").replace(/\s+/g, " ").trim();
|
|
@@ -32414,7 +32772,8 @@ function createAcpDriver(support11) {
|
|
|
32414
32772
|
itemType: "tool",
|
|
32415
32773
|
itemId: u2.toolCallId,
|
|
32416
32774
|
title: String(u2.rawInput?.command ?? u2.title ?? "tool").slice(0, 80),
|
|
32417
|
-
summary: commandSummary(u2.rawInput)
|
|
32775
|
+
summary: commandSummary(u2.rawInput),
|
|
32776
|
+
input: toolDetailPreview(u2.rawInput)
|
|
32418
32777
|
});
|
|
32419
32778
|
break;
|
|
32420
32779
|
}
|
|
@@ -32425,7 +32784,8 @@ function createAcpDriver(support11) {
|
|
|
32425
32784
|
type: "item.completed",
|
|
32426
32785
|
itemType: "tool",
|
|
32427
32786
|
itemId: u2.toolCallId,
|
|
32428
|
-
ok: u2.status !== "failed"
|
|
32787
|
+
ok: u2.status !== "failed",
|
|
32788
|
+
output: toolDetailPreview(u2.rawOutput ?? u2.content)
|
|
32429
32789
|
});
|
|
32430
32790
|
}
|
|
32431
32791
|
break;
|
|
@@ -32715,7 +33075,7 @@ ${turn.text}` : turn.text;
|
|
|
32715
33075
|
}
|
|
32716
33076
|
|
|
32717
33077
|
// server/drivers/antigravity-acp.ts
|
|
32718
|
-
import { createHash as createHash9, randomUUID as
|
|
33078
|
+
import { createHash as createHash9, randomUUID as randomUUID12 } from "node:crypto";
|
|
32719
33079
|
import { mkdir as mkdir4, mkdtemp as mkdtemp2, readFile as readFile2, rm as rm2, stat as stat4 } from "node:fs/promises";
|
|
32720
33080
|
import { request as httpRequest } from "node:http";
|
|
32721
33081
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
@@ -33100,10 +33460,10 @@ async function validateAntigravityRuntime(runtime, expectedVersion) {
|
|
|
33100
33460
|
const profileDirectory = await mkdtemp2(join21(tmpdir3(), "openmaus-antigravity-verify-"));
|
|
33101
33461
|
let client;
|
|
33102
33462
|
let failed = false;
|
|
33103
|
-
let
|
|
33463
|
+
let failure5;
|
|
33104
33464
|
try {
|
|
33105
33465
|
const profile = await prepareAntigravityProfile({
|
|
33106
|
-
instanceId: `verify-${
|
|
33466
|
+
instanceId: `verify-${randomUUID12()}`,
|
|
33107
33467
|
runtime,
|
|
33108
33468
|
profileDirectory,
|
|
33109
33469
|
// Google's Windows one-file executable expands a large Python runtime
|
|
@@ -33118,7 +33478,7 @@ async function validateAntigravityRuntime(runtime, expectedVersion) {
|
|
|
33118
33478
|
}
|
|
33119
33479
|
} catch (error51) {
|
|
33120
33480
|
failed = true;
|
|
33121
|
-
|
|
33481
|
+
failure5 = error51;
|
|
33122
33482
|
}
|
|
33123
33483
|
let stopped = !client;
|
|
33124
33484
|
try {
|
|
@@ -33129,7 +33489,7 @@ async function validateAntigravityRuntime(runtime, expectedVersion) {
|
|
|
33129
33489
|
} catch (error51) {
|
|
33130
33490
|
if (!failed) {
|
|
33131
33491
|
failed = true;
|
|
33132
|
-
|
|
33492
|
+
failure5 = error51;
|
|
33133
33493
|
}
|
|
33134
33494
|
}
|
|
33135
33495
|
if (stopped) {
|
|
@@ -33137,7 +33497,7 @@ async function validateAntigravityRuntime(runtime, expectedVersion) {
|
|
|
33137
33497
|
console.warn(`antigravity: could not remove verification profile ${profileDirectory}: ${error51 instanceof Error ? error51.message : String(error51)}`);
|
|
33138
33498
|
});
|
|
33139
33499
|
}
|
|
33140
|
-
if (failed) throw
|
|
33500
|
+
if (failed) throw failure5;
|
|
33141
33501
|
}
|
|
33142
33502
|
var AntigravityAuthController = class {
|
|
33143
33503
|
flow = null;
|
|
@@ -33176,7 +33536,7 @@ var AntigravityAuthController = class {
|
|
|
33176
33536
|
return { phase: "succeeded", flowId: null, authorizationUrl: null, expiresAt: null };
|
|
33177
33537
|
}
|
|
33178
33538
|
const flow3 = {
|
|
33179
|
-
id:
|
|
33539
|
+
id: randomUUID12(),
|
|
33180
33540
|
client,
|
|
33181
33541
|
completed: authenticated,
|
|
33182
33542
|
pending: { redirectUri: outcome.request.redirectUri, state: outcome.request.state },
|
|
@@ -33214,7 +33574,7 @@ var AntigravityAuthController = class {
|
|
|
33214
33574
|
};
|
|
33215
33575
|
|
|
33216
33576
|
// server/drivers/antigravity-runtime.ts
|
|
33217
|
-
import { createHash as createHash10, randomUUID as
|
|
33577
|
+
import { createHash as createHash10, randomUUID as randomUUID13 } from "node:crypto";
|
|
33218
33578
|
import { createReadStream as createReadStream2, createWriteStream } from "node:fs";
|
|
33219
33579
|
import {
|
|
33220
33580
|
access,
|
|
@@ -33539,7 +33899,7 @@ async function installOnce2(options) {
|
|
|
33539
33899
|
} catch (error51) {
|
|
33540
33900
|
if (error51 instanceof Error && error51.message.includes("Antigravity needs")) throw error51;
|
|
33541
33901
|
}
|
|
33542
|
-
const staging = await mkdtemp3(join22(versions, `${STAGING_PREFIX}${
|
|
33902
|
+
const staging = await mkdtemp3(join22(versions, `${STAGING_PREFIX}${randomUUID13()}-`));
|
|
33543
33903
|
let failed = false;
|
|
33544
33904
|
try {
|
|
33545
33905
|
const archivePath = join22(staging, "download.zip");
|
|
@@ -33843,7 +34203,7 @@ var BoxAgentDriver = {
|
|
|
33843
34203
|
const model = turn.model || MODELS.default;
|
|
33844
34204
|
const prompt = [
|
|
33845
34205
|
turn.system,
|
|
33846
|
-
"You are working on
|
|
34206
|
+
"You are working on the assigned cloud computer \u2014 use its desktop, Chrome, and shell within the access described above.",
|
|
33847
34207
|
"",
|
|
33848
34208
|
turn.text
|
|
33849
34209
|
].filter((s3) => s3 !== void 0).join("\n");
|
|
@@ -33998,11 +34358,11 @@ var BoxAgentDriver = {
|
|
|
33998
34358
|
};
|
|
33999
34359
|
|
|
34000
34360
|
// server/drivers/codex.ts
|
|
34001
|
-
import { existsSync as
|
|
34361
|
+
import { existsSync as existsSync9 } from "node:fs";
|
|
34002
34362
|
import { homedir as homedir13 } from "node:os";
|
|
34003
34363
|
|
|
34004
34364
|
// server/drivers/codex-catalog.ts
|
|
34005
|
-
import { readdirSync as readdirSync3, readFileSync as
|
|
34365
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync14 } from "node:fs";
|
|
34006
34366
|
import { homedir as homedir10 } from "node:os";
|
|
34007
34367
|
import { basename as basename5, join as join23 } from "node:path";
|
|
34008
34368
|
var STATIC_CODEX_MODELS = {
|
|
@@ -34199,7 +34559,7 @@ function parseCodexToml(text8) {
|
|
|
34199
34559
|
}
|
|
34200
34560
|
function readText(path) {
|
|
34201
34561
|
try {
|
|
34202
|
-
return
|
|
34562
|
+
return readFileSync14(path, "utf8");
|
|
34203
34563
|
} catch {
|
|
34204
34564
|
return null;
|
|
34205
34565
|
}
|
|
@@ -34335,7 +34695,7 @@ async function readCodexModelCatalog(env = process.env, fetchImpl = fetch, cli)
|
|
|
34335
34695
|
|
|
34336
34696
|
// server/drivers/codex-instructions.ts
|
|
34337
34697
|
import { createHash as createHash11 } from "node:crypto";
|
|
34338
|
-
import { mkdirSync as mkdirSync7, readFileSync as
|
|
34698
|
+
import { mkdirSync as mkdirSync7, readFileSync as readFileSync15 } from "node:fs";
|
|
34339
34699
|
import { join as join24 } from "node:path";
|
|
34340
34700
|
var digest = (value) => createHash11("sha256").update(value).digest("hex");
|
|
34341
34701
|
function codexDeveloperInstructions(config2, botInstructions) {
|
|
@@ -34356,7 +34716,7 @@ async function syncCodexInstructions(key3, nativeThreadId, instructions, resumed
|
|
|
34356
34716
|
const fingerprint = digest(instructions);
|
|
34357
34717
|
let previous2;
|
|
34358
34718
|
try {
|
|
34359
|
-
previous2 =
|
|
34719
|
+
previous2 = readFileSync15(path, "utf8");
|
|
34360
34720
|
} catch (error51) {
|
|
34361
34721
|
if (error51.code !== "ENOENT") throw error51;
|
|
34362
34722
|
}
|
|
@@ -34387,7 +34747,7 @@ async function syncCodexInstructions(key3, nativeThreadId, instructions, resumed
|
|
|
34387
34747
|
}
|
|
34388
34748
|
|
|
34389
34749
|
// server/drivers/codex-device-auth.ts
|
|
34390
|
-
import { randomUUID as
|
|
34750
|
+
import { randomUUID as randomUUID14 } from "node:crypto";
|
|
34391
34751
|
import { realpathSync as realpathSync4 } from "node:fs";
|
|
34392
34752
|
import { homedir as homedir11 } from "node:os";
|
|
34393
34753
|
import { basename as basename6, dirname as dirname9, isAbsolute as isAbsolute8, join as join25, resolve as resolve9 } from "node:path";
|
|
@@ -34452,7 +34812,7 @@ var CodexDeviceAuthController = class {
|
|
|
34452
34812
|
const flow3 = {
|
|
34453
34813
|
status: {
|
|
34454
34814
|
phase: "waiting",
|
|
34455
|
-
flowId:
|
|
34815
|
+
flowId: randomUUID14(),
|
|
34456
34816
|
authorizationUrl: null,
|
|
34457
34817
|
expiresAt: new Date(Date.now() + (this.options.lifetimeMs ?? 15 * 6e4)).toISOString()
|
|
34458
34818
|
},
|
|
@@ -34802,7 +35162,7 @@ function codexPredatesAstra(version2) {
|
|
|
34802
35162
|
function codexUpdateCommand(cli, platform = process.platform) {
|
|
34803
35163
|
if (cli === "codex") return "codex update";
|
|
34804
35164
|
const trimmed = cli.trim();
|
|
34805
|
-
const tokens = trimmed.includes(" ") &&
|
|
35165
|
+
const tokens = trimmed.includes(" ") && existsSync9(trimmed) ? [trimmed] : splitCliString(trimmed);
|
|
34806
35166
|
const quote = platform === "win32" ? (token) => `'${token.replaceAll("'", "''")}'` : (token) => `'${token.replaceAll("'", `'\\''`)}'`;
|
|
34807
35167
|
const command = (tokens.length > 0 ? tokens : [trimmed]).map(quote).join(" ");
|
|
34808
35168
|
return platform === "win32" ? `& ${command} update` : `${command} update`;
|
|
@@ -35378,7 +35738,8 @@ var CodexDriver = {
|
|
|
35378
35738
|
itemType: "tool",
|
|
35379
35739
|
itemId: item.id,
|
|
35380
35740
|
title,
|
|
35381
|
-
summary: item.type === "commandExecution" ? commandSummary({ command: item.command }) : void 0
|
|
35741
|
+
summary: item.type === "commandExecution" ? commandSummary({ command: item.command }) : void 0,
|
|
35742
|
+
input: toolDetailPreview(item.type === "commandExecution" ? { command: item.command, cwd: item.cwd } : item.type === "mcpToolCall" ? item.arguments : item.type === "fileChange" ? item.changes : item.query)
|
|
35382
35743
|
});
|
|
35383
35744
|
}
|
|
35384
35745
|
break;
|
|
@@ -35411,7 +35772,8 @@ var CodexDriver = {
|
|
|
35411
35772
|
type: "item.completed",
|
|
35412
35773
|
itemType: "tool",
|
|
35413
35774
|
itemId: item.id,
|
|
35414
|
-
ok: item.status !== "failed" && item.status !== "declined"
|
|
35775
|
+
ok: item.status !== "failed" && item.status !== "declined",
|
|
35776
|
+
output: toolDetailPreview(item.type === "commandExecution" ? { output: item.aggregatedOutput, exitCode: item.exitCode } : item.type === "mcpToolCall" ? item.error ?? item.result : item.type === "fileChange" ? item.changes : item.action)
|
|
35415
35777
|
});
|
|
35416
35778
|
} else if (item.type === "reasoning") {
|
|
35417
35779
|
emit({ ...base2(threadId, turnId), type: "item.updated", itemType: "reasoning", tokens: null });
|
|
@@ -35630,10 +35992,10 @@ var CodexDriver = {
|
|
|
35630
35992
|
await startTurn2();
|
|
35631
35993
|
}
|
|
35632
35994
|
} catch (e) {
|
|
35633
|
-
const
|
|
35995
|
+
const failure5 = e instanceof Error ? e : { text: String(e) };
|
|
35634
35996
|
const message3 = e instanceof Error ? e.message : String(e);
|
|
35635
35997
|
const needsAuth = /(?:\b401\b|unauthorized|missing bearer|authentication required)/i.test(message3);
|
|
35636
|
-
const verdict = classifyError(
|
|
35998
|
+
const verdict = classifyError(failure5);
|
|
35637
35999
|
if (!state.settled && !needsAuth && verdict.transient && attempt < RETRY_MAX_ATTEMPTS - 1 && state.sawStreamDelta === false) {
|
|
35638
36000
|
const delayMs = computeBackoff(attempt);
|
|
35639
36001
|
attempt++;
|
|
@@ -36041,7 +36403,7 @@ var GrokDriver = {
|
|
|
36041
36403
|
};
|
|
36042
36404
|
|
|
36043
36405
|
// server/drivers/acp/grok.ts
|
|
36044
|
-
import { existsSync as
|
|
36406
|
+
import { existsSync as existsSync10, readFileSync as readFileSync16, writeFileSync as writeFileSync7 } from "node:fs";
|
|
36045
36407
|
import { homedir as homedir14 } from "node:os";
|
|
36046
36408
|
import { join as join27 } from "node:path";
|
|
36047
36409
|
var STATIC_GROK_MODELS = {
|
|
@@ -36067,7 +36429,7 @@ function readGrokModelCatalog(env = process.env) {
|
|
|
36067
36429
|
const path = join27(grokHome(env), "config.toml");
|
|
36068
36430
|
let text8 = "";
|
|
36069
36431
|
try {
|
|
36070
|
-
text8 =
|
|
36432
|
+
text8 = readFileSync16(path, "utf8");
|
|
36071
36433
|
} catch {
|
|
36072
36434
|
return STATIC_GROK_MODELS;
|
|
36073
36435
|
}
|
|
@@ -36140,7 +36502,7 @@ function ensureGrokInjectSlug(modelId, env = process.env) {
|
|
|
36140
36502
|
const path = join27(grokHome(env), "config.toml");
|
|
36141
36503
|
let text8 = "";
|
|
36142
36504
|
try {
|
|
36143
|
-
text8 =
|
|
36505
|
+
text8 = readFileSync16(path, "utf8");
|
|
36144
36506
|
} catch {
|
|
36145
36507
|
text8 = "";
|
|
36146
36508
|
}
|
|
@@ -36269,7 +36631,7 @@ var support2 = {
|
|
|
36269
36631
|
// an unauthenticated CLI is a user action, not something to paper over.
|
|
36270
36632
|
pickAuthMethod: (methods) => methods.some((m2) => m2.id === "cached_token") ? "cached_token" : null,
|
|
36271
36633
|
authFailure: "fail",
|
|
36272
|
-
isAuthenticated: () =>
|
|
36634
|
+
isAuthenticated: () => existsSync10(join27(homedir14(), ".grok", "auth.json")),
|
|
36273
36635
|
// `--append-system-prompt`/`--rules` are accepted by the CLI but do NOT
|
|
36274
36636
|
// reach the agent-stdio system prompt (verified against 1.0.0), so the
|
|
36275
36637
|
// persona is prepended codex-style.
|
|
@@ -36280,13 +36642,13 @@ ${turn.text}` : turn.text
|
|
|
36280
36642
|
var GrokAgentDriver = createAcpDriver(support2);
|
|
36281
36643
|
|
|
36282
36644
|
// server/drivers/acp/gemini.ts
|
|
36283
|
-
import { existsSync as
|
|
36645
|
+
import { existsSync as existsSync11, readFileSync as readFileSync17 } from "node:fs";
|
|
36284
36646
|
import { homedir as homedir15 } from "node:os";
|
|
36285
36647
|
import { join as join28 } from "node:path";
|
|
36286
36648
|
var AUTH_PREFERENCE = ["gemini-api-key", "oauth-personal", "vertex-ai"];
|
|
36287
36649
|
function liveOauthCredential() {
|
|
36288
36650
|
try {
|
|
36289
|
-
const creds = JSON.parse(
|
|
36651
|
+
const creds = JSON.parse(readFileSync17(join28(homedir15(), ".gemini", "oauth_creds.json"), "utf8"));
|
|
36290
36652
|
if (typeof creds.access_token !== "string" || creds.access_token.trim().length === 0) return false;
|
|
36291
36653
|
const hasRefresh = typeof creds.refresh_token === "string" && creds.refresh_token.trim().length > 0;
|
|
36292
36654
|
if (creds.expiry_date !== void 0) {
|
|
@@ -36300,7 +36662,7 @@ function liveOauthCredential() {
|
|
|
36300
36662
|
}
|
|
36301
36663
|
var nonBlank = (value) => Boolean(value?.trim());
|
|
36302
36664
|
function geminiIsAuthenticated(env) {
|
|
36303
|
-
return nonBlank(env.GEMINI_API_KEY) || nonBlank(env.GOOGLE_API_KEY) ||
|
|
36665
|
+
return nonBlank(env.GEMINI_API_KEY) || nonBlank(env.GOOGLE_API_KEY) || existsSync11(join28(homedir15(), ".gemini", "oauth_creds.json")) && liveOauthCredential();
|
|
36304
36666
|
}
|
|
36305
36667
|
var support3 = {
|
|
36306
36668
|
driverKind: "geminiAgent",
|
|
@@ -36333,7 +36695,7 @@ ${turn.text}` : turn.text
|
|
|
36333
36695
|
var GeminiAgentDriver = createAcpDriver(support3);
|
|
36334
36696
|
|
|
36335
36697
|
// server/drivers/acp/kimi.ts
|
|
36336
|
-
import { existsSync as
|
|
36698
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync8, readFileSync as readFileSync18, writeFileSync as writeFileSync8 } from "node:fs";
|
|
36337
36699
|
import { homedir as homedir16 } from "node:os";
|
|
36338
36700
|
import { join as join29 } from "node:path";
|
|
36339
36701
|
var STATIC_KIMI_MODELS = {
|
|
@@ -36645,7 +37007,7 @@ function ensureKimiInjectAlias(modelId, env = process.env) {
|
|
|
36645
37007
|
const path = join29(dataRoot, "config.toml");
|
|
36646
37008
|
let text8 = "";
|
|
36647
37009
|
try {
|
|
36648
|
-
text8 =
|
|
37010
|
+
text8 = readFileSync18(path, "utf8");
|
|
36649
37011
|
} catch {
|
|
36650
37012
|
text8 = "";
|
|
36651
37013
|
}
|
|
@@ -36714,7 +37076,7 @@ function readKimiModelCatalog(env) {
|
|
|
36714
37076
|
const dataRoot = kimiDataRoot(env);
|
|
36715
37077
|
let text8 = "";
|
|
36716
37078
|
try {
|
|
36717
|
-
text8 =
|
|
37079
|
+
text8 = readFileSync18(join29(dataRoot, "config.toml"), "utf8");
|
|
36718
37080
|
} catch {
|
|
36719
37081
|
return STATIC_KIMI_MODELS;
|
|
36720
37082
|
}
|
|
@@ -36799,7 +37161,7 @@ var support4 = {
|
|
|
36799
37161
|
authFailure: "continue",
|
|
36800
37162
|
// Match the child CLI's own data-root precedence. A custom instance HOME or
|
|
36801
37163
|
// KIMI_CODE_HOME must not be checked against the server user's home instead.
|
|
36802
|
-
isAuthenticated: (env) =>
|
|
37164
|
+
isAuthenticated: (env) => existsSync12(credentialsPath(env)),
|
|
36803
37165
|
buildPromptText: (turn) => turn.system ? `${turn.system}
|
|
36804
37166
|
|
|
36805
37167
|
${turn.text}` : turn.text
|
|
@@ -36807,7 +37169,7 @@ ${turn.text}` : turn.text
|
|
|
36807
37169
|
var KimiAgentDriver = createAcpDriver(support4);
|
|
36808
37170
|
|
|
36809
37171
|
// server/drivers/acp/droid.ts
|
|
36810
|
-
import { existsSync as
|
|
37172
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync9, readFileSync as readFileSync19, writeFileSync as writeFileSync9 } from "node:fs";
|
|
36811
37173
|
import { homedir as homedir17 } from "node:os";
|
|
36812
37174
|
import { join as join30 } from "node:path";
|
|
36813
37175
|
var AUTH_FILES = ["auth.v2.file", "auth.v2.loginkeychain", "auth.v2.keyring"];
|
|
@@ -36834,9 +37196,9 @@ function ensureDroidInjectModel(modelId, env = process.env) {
|
|
|
36834
37196
|
const path = join30(dir, "settings.json");
|
|
36835
37197
|
let settings = {};
|
|
36836
37198
|
try {
|
|
36837
|
-
settings = JSON.parse(
|
|
37199
|
+
settings = JSON.parse(readFileSync19(path, "utf8"));
|
|
36838
37200
|
} catch (error51) {
|
|
36839
|
-
if (
|
|
37201
|
+
if (existsSync13(path)) throw error51;
|
|
36840
37202
|
}
|
|
36841
37203
|
const custom2 = Array.isArray(settings.customModels) ? [...settings.customModels] : [];
|
|
36842
37204
|
const match = custom2.find(
|
|
@@ -36867,11 +37229,11 @@ function ensureDroidInjectModel(modelId, env = process.env) {
|
|
|
36867
37229
|
function applyDroidLocalAuthEnv(env, modelId) {
|
|
36868
37230
|
if (!decodeInjectId(modelId)) return;
|
|
36869
37231
|
if (env.FACTORY_API_KEY?.trim()) return;
|
|
36870
|
-
if (authFilePaths(env).some(
|
|
37232
|
+
if (authFilePaths(env).some(existsSync13)) return;
|
|
36871
37233
|
env.FACTORY_API_KEY = "openmausbot-local";
|
|
36872
37234
|
}
|
|
36873
37235
|
function readSettings(env) {
|
|
36874
|
-
return JSON.parse(
|
|
37236
|
+
return JSON.parse(readFileSync19(join30(factoryHome(env), ".factory", "settings.json"), "utf8"));
|
|
36875
37237
|
}
|
|
36876
37238
|
async function resolveModels(env) {
|
|
36877
37239
|
let catalog = MODELS3;
|
|
@@ -36950,7 +37312,7 @@ var support5 = {
|
|
|
36950
37312
|
// The signed-in CLI is the primary source; FACTORY_API_KEY is the fallback
|
|
36951
37313
|
// droid itself advertises, and it is checked last so an ambient key can
|
|
36952
37314
|
// never be what makes an otherwise logged-out instance look ready.
|
|
36953
|
-
isAuthenticated: (env) => authFilePaths(env).some(
|
|
37315
|
+
isAuthenticated: (env) => authFilePaths(env).some(existsSync13) || Boolean(env.FACTORY_API_KEY),
|
|
36954
37316
|
resolveModels,
|
|
36955
37317
|
resolveTurnModel: (model, env) => model ? ensureDroidInjectModel(model, env) : model,
|
|
36956
37318
|
applyTurnEnv: (env, { requestedModel }) => {
|
|
@@ -37243,7 +37605,7 @@ function createCursorAgentDriver(run3 = execCli) {
|
|
|
37243
37605
|
var CursorAgentDriver = createCursorAgentDriver();
|
|
37244
37606
|
|
|
37245
37607
|
// server/drivers/acp/opencode-go.ts
|
|
37246
|
-
import { existsSync as
|
|
37608
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync10, readFileSync as readFileSync20, writeFileSync as writeFileSync10 } from "node:fs";
|
|
37247
37609
|
import { homedir as homedir18 } from "node:os";
|
|
37248
37610
|
import { join as join31 } from "node:path";
|
|
37249
37611
|
var STATIC_MODELS = {
|
|
@@ -37382,9 +37744,9 @@ function ensureOpenCodeInjectModel(modelId, env = process.env) {
|
|
|
37382
37744
|
mkdirSync10(dir, { recursive: true });
|
|
37383
37745
|
const path = join31(dir, "opencode.json");
|
|
37384
37746
|
let config2 = { $schema: "https://opencode.ai/config.json" };
|
|
37385
|
-
if (
|
|
37747
|
+
if (existsSync14(path)) {
|
|
37386
37748
|
try {
|
|
37387
|
-
config2 = JSON.parse(
|
|
37749
|
+
config2 = JSON.parse(readFileSync20(path, "utf8"));
|
|
37388
37750
|
} catch {
|
|
37389
37751
|
}
|
|
37390
37752
|
}
|
|
@@ -37435,7 +37797,7 @@ function hasStoredOpenCodeAuth(env) {
|
|
|
37435
37797
|
if (env.OPENCODE_AUTH_CONTENT) candidates.push(env.OPENCODE_AUTH_CONTENT);
|
|
37436
37798
|
for (const path of storedAuthPaths(env)) {
|
|
37437
37799
|
try {
|
|
37438
|
-
candidates.push(
|
|
37800
|
+
candidates.push(readFileSync20(path, "utf8"));
|
|
37439
37801
|
} catch {
|
|
37440
37802
|
}
|
|
37441
37803
|
}
|
|
@@ -37518,7 +37880,7 @@ function createOpenCodeDriver(loadCatalog = discoverOpenCodeModels) {
|
|
|
37518
37880
|
var OpenCodeDriver = createOpenCodeDriver();
|
|
37519
37881
|
|
|
37520
37882
|
// server/drivers/acp/qwen.ts
|
|
37521
|
-
import { chmodSync as chmodSync2, existsSync as
|
|
37883
|
+
import { chmodSync as chmodSync2, existsSync as existsSync15, mkdirSync as mkdirSync11, readFileSync as readFileSync21, writeFileSync as writeFileSync11 } from "node:fs";
|
|
37522
37884
|
import { createHash as createHash12 } from "node:crypto";
|
|
37523
37885
|
import { homedir as homedir19 } from "node:os";
|
|
37524
37886
|
import { join as join32 } from "node:path";
|
|
@@ -37538,7 +37900,7 @@ var PROTOCOLS = /* @__PURE__ */ new Set(["openai", "anthropic", "gemini", "verte
|
|
|
37538
37900
|
function readQwenRoutes(env) {
|
|
37539
37901
|
let settings;
|
|
37540
37902
|
try {
|
|
37541
|
-
settings = JSON.parse(
|
|
37903
|
+
settings = JSON.parse(readFileSync21(join32(qwenHome(env), "settings.json"), "utf8"));
|
|
37542
37904
|
} catch {
|
|
37543
37905
|
return [];
|
|
37544
37906
|
}
|
|
@@ -37624,9 +37986,9 @@ function ensureQwenInjectModel(modelId, env = process.env) {
|
|
|
37624
37986
|
mkdirSync11(dir, { recursive: true });
|
|
37625
37987
|
const path = join32(dir, "settings.json");
|
|
37626
37988
|
let settings = {};
|
|
37627
|
-
if (
|
|
37989
|
+
if (existsSync15(path)) {
|
|
37628
37990
|
try {
|
|
37629
|
-
settings = JSON.parse(
|
|
37991
|
+
settings = JSON.parse(readFileSync21(path, "utf8"));
|
|
37630
37992
|
} catch {
|
|
37631
37993
|
}
|
|
37632
37994
|
}
|
|
@@ -37761,7 +38123,7 @@ var CustomAcpDriver = {
|
|
|
37761
38123
|
|
|
37762
38124
|
// server/drivers/acp/hermes.ts
|
|
37763
38125
|
import { spawn as spawn2 } from "node:child_process";
|
|
37764
|
-
import { mkdirSync as mkdirSync12, readFileSync as
|
|
38126
|
+
import { mkdirSync as mkdirSync12, readFileSync as readFileSync22, writeFileSync as writeFileSync12 } from "node:fs";
|
|
37765
38127
|
import { homedir as homedir20 } from "node:os";
|
|
37766
38128
|
import { join as join33 } from "node:path";
|
|
37767
38129
|
|
|
@@ -44112,7 +44474,7 @@ function ensureHermesInjectProvider(modelId, env = process.env) {
|
|
|
44112
44474
|
const path = join33(dir, "config.yaml");
|
|
44113
44475
|
let text8 = "";
|
|
44114
44476
|
try {
|
|
44115
|
-
text8 =
|
|
44477
|
+
text8 = readFileSync22(path, "utf8");
|
|
44116
44478
|
} catch {
|
|
44117
44479
|
text8 = "";
|
|
44118
44480
|
}
|
|
@@ -44188,13 +44550,13 @@ function hermesConfiguredModel(env = process.env) {
|
|
|
44188
44550
|
const dir = hermesHome(env);
|
|
44189
44551
|
let secrets = "";
|
|
44190
44552
|
try {
|
|
44191
|
-
secrets =
|
|
44553
|
+
secrets = readFileSync22(join33(dir, ".env"), "utf8");
|
|
44192
44554
|
} catch {
|
|
44193
44555
|
}
|
|
44194
44556
|
const hasHostedProviderKey = HERMES_HOSTED_PROVIDER_KEYS.some((name2) => nonEmptyDotenvValue(secrets, name2));
|
|
44195
44557
|
let configuredDefault = null;
|
|
44196
44558
|
try {
|
|
44197
|
-
configuredDefault = hermesConfigDefault(
|
|
44559
|
+
configuredDefault = hermesConfigDefault(readFileSync22(join33(dir, "config.yaml"), "utf8"));
|
|
44198
44560
|
} catch {
|
|
44199
44561
|
}
|
|
44200
44562
|
const configuredProvider = configuredDefault?.provider.toLowerCase() ?? "";
|
|
@@ -44503,7 +44865,7 @@ var OpenAICompatDriver = {
|
|
|
44503
44865
|
};
|
|
44504
44866
|
|
|
44505
44867
|
// server/drivers/pi.ts
|
|
44506
|
-
import { chmodSync as chmodSync3, existsSync as
|
|
44868
|
+
import { chmodSync as chmodSync3, existsSync as existsSync16, mkdirSync as mkdirSync13, mkdtempSync as mkdtempSync2, readFileSync as readFileSync23, rmSync as rmSync2, writeFileSync as writeFileSync13 } from "node:fs";
|
|
44507
44869
|
import { homedir as homedir21, tmpdir as tmpdir4 } from "node:os";
|
|
44508
44870
|
import { join as join34 } from "node:path";
|
|
44509
44871
|
var DRIVER_KIND6 = "piAgent";
|
|
@@ -44513,7 +44875,7 @@ var NODE_ENV_FLAG2 = { ELECTRON_RUN_AS_NODE: "1" };
|
|
|
44513
44875
|
function readPiPromptImages(turn) {
|
|
44514
44876
|
return (turn.images ?? []).map((image) => ({
|
|
44515
44877
|
type: "image",
|
|
44516
|
-
data:
|
|
44878
|
+
data: readFileSync23(image.path).toString("base64"),
|
|
44517
44879
|
mimeType: image.mime
|
|
44518
44880
|
}));
|
|
44519
44881
|
}
|
|
@@ -44629,9 +44991,9 @@ function ensurePiInjectModel(modelId, env = process.env) {
|
|
|
44629
44991
|
mkdirSync13(dir, { recursive: true });
|
|
44630
44992
|
const path = join34(dir, "models.json");
|
|
44631
44993
|
let root = { providers: {} };
|
|
44632
|
-
if (
|
|
44994
|
+
if (existsSync16(path)) {
|
|
44633
44995
|
try {
|
|
44634
|
-
const parsed = JSON.parse(
|
|
44996
|
+
const parsed = JSON.parse(readFileSync23(path, "utf8"));
|
|
44635
44997
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
44636
44998
|
root = parsed;
|
|
44637
44999
|
} else {
|
|
@@ -44683,7 +45045,7 @@ function ensurePiInjectModel(modelId, env = process.env) {
|
|
|
44683
45045
|
function readPiDefaultModel(env) {
|
|
44684
45046
|
const home = env.HOME || env.USERPROFILE || homedir21();
|
|
44685
45047
|
try {
|
|
44686
|
-
const s3 = JSON.parse(
|
|
45048
|
+
const s3 = JSON.parse(readFileSync23(`${home}/.pi/agent/settings.json`, "utf8"));
|
|
44687
45049
|
return s3.defaultProvider && s3.defaultModel ? `${s3.defaultProvider}/${s3.defaultModel}` : "";
|
|
44688
45050
|
} catch {
|
|
44689
45051
|
return "";
|
|
@@ -44960,7 +45322,9 @@ var PiDriver = {
|
|
|
44960
45322
|
type: "item.started",
|
|
44961
45323
|
itemType: "tool",
|
|
44962
45324
|
itemId: evt.toolCallId,
|
|
44963
|
-
title: String(evt.toolName ?? "tool").slice(0, 80)
|
|
45325
|
+
title: String(evt.toolName ?? "tool").slice(0, 80),
|
|
45326
|
+
summary: commandSummary(evt.args),
|
|
45327
|
+
input: toolDetailPreview(evt.args)
|
|
44964
45328
|
});
|
|
44965
45329
|
return;
|
|
44966
45330
|
}
|
|
@@ -44970,7 +45334,8 @@ var PiDriver = {
|
|
|
44970
45334
|
type: "item.completed",
|
|
44971
45335
|
itemType: "tool",
|
|
44972
45336
|
itemId: evt.toolCallId,
|
|
44973
|
-
ok: !evt.isError
|
|
45337
|
+
ok: !evt.isError,
|
|
45338
|
+
output: toolDetailPreview(evt.result)
|
|
44974
45339
|
});
|
|
44975
45340
|
return;
|
|
44976
45341
|
}
|
|
@@ -45183,7 +45548,7 @@ ${turn.text}` : turn.text;
|
|
|
45183
45548
|
};
|
|
45184
45549
|
|
|
45185
45550
|
// server/drivers/minimax.ts
|
|
45186
|
-
import { readFileSync as
|
|
45551
|
+
import { readFileSync as readFileSync24 } from "node:fs";
|
|
45187
45552
|
import { homedir as homedir22 } from "node:os";
|
|
45188
45553
|
import { join as join35 } from "node:path";
|
|
45189
45554
|
var DRIVER_KIND7 = "minimax";
|
|
@@ -45211,7 +45576,7 @@ function normalizedApiUrl(value) {
|
|
|
45211
45576
|
}
|
|
45212
45577
|
function loadLocalMiniMaxConfig(home = homedir22()) {
|
|
45213
45578
|
try {
|
|
45214
|
-
const raw = localConfigSchema.parse(JSON.parse(
|
|
45579
|
+
const raw = localConfigSchema.parse(JSON.parse(readFileSync24(join35(home, ".mmx", "config.json"), "utf8")));
|
|
45215
45580
|
const configuredUrl = raw.base_url?.trim() ? raw.base_url : raw.region === "cn" ? CN_URL : DEFAULT_URL2;
|
|
45216
45581
|
return {
|
|
45217
45582
|
apiKey: raw.api_key?.trim() ?? "",
|
|
@@ -45302,11 +45667,11 @@ var BUILT_IN_DRIVERS = [
|
|
|
45302
45667
|
|
|
45303
45668
|
// server/store.ts
|
|
45304
45669
|
import { createHash as createHash14 } from "node:crypto";
|
|
45305
|
-
import { existsSync as
|
|
45670
|
+
import { existsSync as existsSync20, readFileSync as readFileSync28, mkdirSync as mkdirSync18, rmSync as rmSync4, unlinkSync as unlinkSync6 } from "node:fs";
|
|
45306
45671
|
import { join as join40 } from "node:path";
|
|
45307
45672
|
|
|
45308
45673
|
// server/section-context.ts
|
|
45309
|
-
import { existsSync as
|
|
45674
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync14, readFileSync as readFileSync25 } from "node:fs";
|
|
45310
45675
|
import { join as join36 } from "node:path";
|
|
45311
45676
|
var SECTION_CONTEXT_MAX_BYTES = 24e3;
|
|
45312
45677
|
var SECTION_CONTEXTS_FILE = join36(DATA_DIR, "section-contexts.json");
|
|
@@ -45327,9 +45692,9 @@ function sectionContextLabel(section2) {
|
|
|
45327
45692
|
return sectionContextKey(section2) || "General";
|
|
45328
45693
|
}
|
|
45329
45694
|
function loadFile(forWrite = false) {
|
|
45330
|
-
if (!
|
|
45695
|
+
if (!existsSync17(SECTION_CONTEXTS_FILE)) return emptyFile();
|
|
45331
45696
|
try {
|
|
45332
|
-
const input = JSON.parse(
|
|
45697
|
+
const input = JSON.parse(readFileSync25(SECTION_CONTEXTS_FILE, "utf8"));
|
|
45333
45698
|
const candidate = sectionContextFileSchema.safeParse(input);
|
|
45334
45699
|
if (!candidate.success) throw new Error("Invalid team instructions file");
|
|
45335
45700
|
const contexts = /* @__PURE__ */ Object.create(null);
|
|
@@ -45487,7 +45852,7 @@ function peerAllowKey(action, targetId) {
|
|
|
45487
45852
|
}
|
|
45488
45853
|
|
|
45489
45854
|
// server/message-db.ts
|
|
45490
|
-
import { chmodSync as chmodSync4, closeSync as closeSync6, existsSync as
|
|
45855
|
+
import { chmodSync as chmodSync4, closeSync as closeSync6, existsSync as existsSync18, mkdirSync as mkdirSync16, openSync as openSync6, readFileSync as readFileSync26, renameSync as renameSync3 } from "node:fs";
|
|
45491
45856
|
import { join as join38 } from "node:path";
|
|
45492
45857
|
import { DatabaseSync } from "node:sqlite";
|
|
45493
45858
|
|
|
@@ -45615,7 +45980,7 @@ function ensureRecallIndex(db2) {
|
|
|
45615
45980
|
}
|
|
45616
45981
|
var UPSERT_MESSAGE = "INSERT INTO messages (thread_id, id, at, role, kind, text, json) VALUES (?, ?, ?, ?, ?, ?, ?) ON CONFLICT(thread_id, id) DO UPDATE SET at = excluded.at, role = excluded.role, kind = excluded.kind, text = excluded.text, json = excluded.json";
|
|
45617
45982
|
function db() {
|
|
45618
|
-
if (handle && handlePath === DB_FILE() &&
|
|
45983
|
+
if (handle && handlePath === DB_FILE() && existsSync18(DB_FILE())) return handle;
|
|
45619
45984
|
try {
|
|
45620
45985
|
handle?.close();
|
|
45621
45986
|
} catch {
|
|
@@ -45696,7 +46061,7 @@ function importLegacy(threadId, legacyFile) {
|
|
|
45696
46061
|
let activeLeafId = null;
|
|
45697
46062
|
let raw;
|
|
45698
46063
|
try {
|
|
45699
|
-
raw = JSON.parse(
|
|
46064
|
+
raw = JSON.parse(readFileSync26(legacyFile, "utf8"));
|
|
45700
46065
|
} catch {
|
|
45701
46066
|
return { messages, activeLeafId };
|
|
45702
46067
|
}
|
|
@@ -45891,7 +46256,7 @@ function closeMessageDb() {
|
|
|
45891
46256
|
}
|
|
45892
46257
|
|
|
45893
46258
|
// server/workspace.ts
|
|
45894
|
-
import { existsSync as
|
|
46259
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync17, readdirSync as readdirSync4, readFileSync as readFileSync27, statSync as statSync5 } from "node:fs";
|
|
45895
46260
|
import { join as join39 } from "node:path";
|
|
45896
46261
|
var WORKSPACES_DIR = join39(DATA_DIR, "workspaces");
|
|
45897
46262
|
var TASK_WORKSPACES_DIR = join39(DATA_DIR, "task-workspaces");
|
|
@@ -45915,7 +46280,7 @@ function ensureWorkspace(botId) {
|
|
|
45915
46280
|
const dir = join39(WORKSPACES_DIR, botId);
|
|
45916
46281
|
mkdirSync17(join39(dir, "memory"), { recursive: true, mode: 448 });
|
|
45917
46282
|
const memoryFile = join39(dir, "MEMORY.md");
|
|
45918
|
-
if (!
|
|
46283
|
+
if (!existsSync19(memoryFile)) writeFileAtomic(memoryFile, MEMORY_SEED, { mode: 384 });
|
|
45919
46284
|
return dir;
|
|
45920
46285
|
}
|
|
45921
46286
|
function workspaceDir(botId) {
|
|
@@ -45938,7 +46303,7 @@ function memoryOverBudget(text8) {
|
|
|
45938
46303
|
function loadMemory(botId) {
|
|
45939
46304
|
let raw;
|
|
45940
46305
|
try {
|
|
45941
|
-
raw =
|
|
46306
|
+
raw = readFileSync27(join39(workspaceDir(botId), "MEMORY.md"), "utf8");
|
|
45942
46307
|
} catch {
|
|
45943
46308
|
return null;
|
|
45944
46309
|
}
|
|
@@ -45961,7 +46326,7 @@ var MEMORY_FILE_MAX_BYTES = 256 * 1024;
|
|
|
45961
46326
|
function readMemoryFile(botId) {
|
|
45962
46327
|
let raw;
|
|
45963
46328
|
try {
|
|
45964
|
-
raw =
|
|
46329
|
+
raw = readFileSync27(join39(workspaceDir(botId), "MEMORY.md"), "utf8");
|
|
45965
46330
|
} catch {
|
|
45966
46331
|
return { text: "", truncated: false };
|
|
45967
46332
|
}
|
|
@@ -45977,7 +46342,7 @@ function indexWrittenMemoryFile(botId, relativePath) {
|
|
|
45977
46342
|
try {
|
|
45978
46343
|
const path = join39(workspaceDir(botId), relativePath);
|
|
45979
46344
|
const stat6 = statSync5(path);
|
|
45980
|
-
indexMemoryFile(botId, relativePath,
|
|
46345
|
+
indexMemoryFile(botId, relativePath, readFileSync27(path, "utf8"), { mtimeMs: stat6.mtimeMs, bytes: stat6.size });
|
|
45981
46346
|
} catch {
|
|
45982
46347
|
}
|
|
45983
46348
|
}
|
|
@@ -46005,7 +46370,7 @@ function syncMemoryIndex(botId) {
|
|
|
46005
46370
|
indexed.delete(file2.path);
|
|
46006
46371
|
if (known && known.bytes === file2.bytes && known.mtimeMs === Math.trunc(file2.mtimeMs)) continue;
|
|
46007
46372
|
try {
|
|
46008
|
-
const text8 =
|
|
46373
|
+
const text8 = readFileSync27(join39(workspaceDir(botId), file2.path), "utf8");
|
|
46009
46374
|
indexMemoryFile(botId, file2.path, file2.path === "MEMORY.md" && text8 === MEMORY_SEED ? "" : text8, file2);
|
|
46010
46375
|
} catch {
|
|
46011
46376
|
}
|
|
@@ -46066,7 +46431,7 @@ function updateMemory(botId, update, opts = {}) {
|
|
|
46066
46431
|
}
|
|
46067
46432
|
const text8 = update.text === void 0 ? void 0 : redactSecretsInText(update.text);
|
|
46068
46433
|
const dir = ensureWorkspace(botId);
|
|
46069
|
-
const raw =
|
|
46434
|
+
const raw = readFileSync27(join39(dir, "MEMORY.md"), "utf8");
|
|
46070
46435
|
const current2 = raw === MEMORY_SEED ? "" : raw;
|
|
46071
46436
|
const today = memoryDate(opts.now);
|
|
46072
46437
|
const appendEntry = (base2, entry2) => `${base2}${base2 && !base2.endsWith("\n") ? "\n" : ""}${entry2}
|
|
@@ -46144,7 +46509,7 @@ function appendMemoryLog(botId, text8, opts = {}) {
|
|
|
46144
46509
|
const path = join39(dir, file2);
|
|
46145
46510
|
let current2 = "";
|
|
46146
46511
|
try {
|
|
46147
|
-
current2 =
|
|
46512
|
+
current2 = readFileSync27(path, "utf8");
|
|
46148
46513
|
} catch {
|
|
46149
46514
|
}
|
|
46150
46515
|
writeFileAtomic(path, `${current2}${current2 && !current2.endsWith("\n") ? "\n" : ""}${line}
|
|
@@ -46170,7 +46535,7 @@ function listMemoryLogs(botId) {
|
|
|
46170
46535
|
function readMemoryLog(botId, name2) {
|
|
46171
46536
|
if (!LOG_FILE_NAME.test(name2)) return null;
|
|
46172
46537
|
try {
|
|
46173
|
-
return
|
|
46538
|
+
return readFileSync27(join39(workspaceDir(botId), "memory", MEMORY_LOG_DIR, name2), "utf8");
|
|
46174
46539
|
} catch {
|
|
46175
46540
|
return null;
|
|
46176
46541
|
}
|
|
@@ -46204,7 +46569,7 @@ function writeMemoryTopic(botId, name2, text8) {
|
|
|
46204
46569
|
function readMemoryTopic(botId, name2) {
|
|
46205
46570
|
if (!isMemoryTopicName(name2)) return null;
|
|
46206
46571
|
try {
|
|
46207
|
-
return
|
|
46572
|
+
return readFileSync27(join39(workspaceDir(botId), "memory", name2), "utf8");
|
|
46208
46573
|
} catch {
|
|
46209
46574
|
return null;
|
|
46210
46575
|
}
|
|
@@ -46519,12 +46884,12 @@ var Store = class {
|
|
|
46519
46884
|
this.defaultSelection = defaultSelection2;
|
|
46520
46885
|
mkdirSync18(DATA_DIR, { recursive: true });
|
|
46521
46886
|
try {
|
|
46522
|
-
this.bots = JSON.parse(
|
|
46887
|
+
this.bots = JSON.parse(readFileSync28(BOTS_FILE, "utf8"));
|
|
46523
46888
|
} catch {
|
|
46524
46889
|
this.bots = [];
|
|
46525
46890
|
}
|
|
46526
46891
|
try {
|
|
46527
|
-
this.groups = JSON.parse(
|
|
46892
|
+
this.groups = JSON.parse(readFileSync28(GROUPS_FILE, "utf8"));
|
|
46528
46893
|
} catch {
|
|
46529
46894
|
this.groups = [];
|
|
46530
46895
|
}
|
|
@@ -46545,7 +46910,7 @@ var Store = class {
|
|
|
46545
46910
|
b2.soulHash = soulHash(b2.soul);
|
|
46546
46911
|
botsMigrated = true;
|
|
46547
46912
|
}
|
|
46548
|
-
if (!
|
|
46913
|
+
if (!existsSync20(soulFile(b2.id))) {
|
|
46549
46914
|
try {
|
|
46550
46915
|
writeSoulMirror(b2.id, b2.soul);
|
|
46551
46916
|
} catch (e) {
|
|
@@ -46576,15 +46941,25 @@ var Store = class {
|
|
|
46576
46941
|
botsMigrated = true;
|
|
46577
46942
|
}
|
|
46578
46943
|
if (b2.approvalGrant !== void 0) {
|
|
46579
|
-
b2.
|
|
46580
|
-
|
|
46581
|
-
|
|
46582
|
-
|
|
46583
|
-
|
|
46584
|
-
|
|
46585
|
-
task2.autoApprove = false;
|
|
46944
|
+
const threadOnly = b2.approvalGrant.threadOnly === true;
|
|
46945
|
+
if (threadOnly) {
|
|
46946
|
+
const target = b2.tasks?.find((task2) => task2.threadId === b2.approvalGrant?.threadId);
|
|
46947
|
+
if (target) {
|
|
46948
|
+
target.approvalMode = "ask";
|
|
46949
|
+
target.autoApprove = false;
|
|
46586
46950
|
}
|
|
46587
46951
|
}
|
|
46952
|
+
if (!threadOnly) {
|
|
46953
|
+
b2.approvalMode = "ask";
|
|
46954
|
+
b2.autoApprove = false;
|
|
46955
|
+
for (const task2 of b2.tasks ?? []) {
|
|
46956
|
+
if (task2.approvalMode === "full" || task2.approvalMode === "custom") {
|
|
46957
|
+
task2.approvalMode = "ask";
|
|
46958
|
+
task2.autoApprove = false;
|
|
46959
|
+
}
|
|
46960
|
+
}
|
|
46961
|
+
}
|
|
46962
|
+
delete b2.approvalGrant;
|
|
46588
46963
|
botsMigrated = true;
|
|
46589
46964
|
}
|
|
46590
46965
|
const avatar = botAvatarProfile(b2);
|
|
@@ -46727,7 +47102,7 @@ var Store = class {
|
|
|
46727
47102
|
]);
|
|
46728
47103
|
for (const threadId of knownThreads) {
|
|
46729
47104
|
const legacyFile = messagesFile(threadId);
|
|
46730
|
-
if (
|
|
47105
|
+
if (existsSync20(legacyFile)) readThread(threadId, legacyFile);
|
|
46731
47106
|
}
|
|
46732
47107
|
this.registeringInitialSections = false;
|
|
46733
47108
|
}
|
|
@@ -47683,6 +48058,7 @@ var Store = class {
|
|
|
47683
48058
|
return {
|
|
47684
48059
|
...bot,
|
|
47685
48060
|
threadId: task2.threadId,
|
|
48061
|
+
approvalGrant: bot.approvalGrant?.threadOnly && bot.approvalGrant.threadId !== threadId ? void 0 : bot.approvalGrant,
|
|
47686
48062
|
modelSelection: structuredClone(task2.modelSelection ?? bot.modelSelection),
|
|
47687
48063
|
resumeCursors: structuredClone(task2.resumeCursors),
|
|
47688
48064
|
approvalMode: task2.approvalMode ?? (task2.autoApprove === void 0 ? bot.approvalMode : void 0),
|
|
@@ -47976,7 +48352,7 @@ ${message3.text}`;
|
|
|
47976
48352
|
}
|
|
47977
48353
|
|
|
47978
48354
|
// server/delegations.ts
|
|
47979
|
-
import { readFileSync as
|
|
48355
|
+
import { readFileSync as readFileSync29 } from "node:fs";
|
|
47980
48356
|
import { join as join41 } from "node:path";
|
|
47981
48357
|
|
|
47982
48358
|
// server/peer-approval.ts
|
|
@@ -48187,7 +48563,7 @@ function _loadPending() {
|
|
|
48187
48563
|
pendingDelegations.clear();
|
|
48188
48564
|
let backfilled = false;
|
|
48189
48565
|
try {
|
|
48190
|
-
const raw = JSON.parse(
|
|
48566
|
+
const raw = JSON.parse(readFileSync29(DELEGATIONS_FILE, "utf8"));
|
|
48191
48567
|
const now = Date.now();
|
|
48192
48568
|
for (const [threadId, list3] of Object.entries(raw)) {
|
|
48193
48569
|
if (!Array.isArray(list3)) continue;
|
|
@@ -48226,7 +48602,7 @@ function _loadPending() {
|
|
|
48226
48602
|
if (backfilled) savePending();
|
|
48227
48603
|
receipts = [];
|
|
48228
48604
|
try {
|
|
48229
|
-
const rawReceipts = JSON.parse(
|
|
48605
|
+
const rawReceipts = JSON.parse(readFileSync29(RECEIPTS_FILE, "utf8"));
|
|
48230
48606
|
if (Array.isArray(rawReceipts)) {
|
|
48231
48607
|
const now = Date.now();
|
|
48232
48608
|
const loaded = [];
|
|
@@ -48731,7 +49107,8 @@ function queueSteeredMessage(botId, threadId, text8, options = {}) {
|
|
|
48731
49107
|
replyToId: options.replyToId,
|
|
48732
49108
|
sendId: options.sendId,
|
|
48733
49109
|
reason: options.reason,
|
|
48734
|
-
unattended: options.unattended
|
|
49110
|
+
unattended: options.unattended,
|
|
49111
|
+
peerAsk: options.peerAsk
|
|
48735
49112
|
};
|
|
48736
49113
|
saveChatFollowup({ id, kind: "bot", ownerId: botId, threadId, payload: item });
|
|
48737
49114
|
entry.items.push(item);
|
|
@@ -48771,7 +49148,8 @@ function drainSteeredMessages(store2, run3, isBlocked) {
|
|
|
48771
49148
|
text: item.text,
|
|
48772
49149
|
replyToId: item.replyToId,
|
|
48773
49150
|
sendId: item.sendId,
|
|
48774
|
-
queueId: item.messageId
|
|
49151
|
+
queueId: item.messageId,
|
|
49152
|
+
peerAsk: item.peerAsk
|
|
48775
49153
|
})
|
|
48776
49154
|
);
|
|
48777
49155
|
}
|
|
@@ -49657,7 +50035,7 @@ function buildTurnContext(input) {
|
|
|
49657
50035
|
}
|
|
49658
50036
|
|
|
49659
50037
|
// server/turn-images.ts
|
|
49660
|
-
import { lstatSync as
|
|
50038
|
+
import { lstatSync as lstatSync3, realpathSync as realpathSync5, statSync as statSync6 } from "node:fs";
|
|
49661
50039
|
import { dirname as dirname10, extname as extname4, resolve as resolve11 } from "node:path";
|
|
49662
50040
|
var TURN_IMAGE_MAX_COUNT = 4;
|
|
49663
50041
|
var TURN_IMAGE_MAX_BYTES = TURN_IMAGE_MAX_COUNT * IMAGE_MAX_BYTES2;
|
|
@@ -49720,7 +50098,7 @@ function ownedImage(path, attachmentsDir) {
|
|
|
49720
50098
|
const expected = resolve11(attachmentsDir, name2);
|
|
49721
50099
|
if (resolve11(path) !== expected) return null;
|
|
49722
50100
|
try {
|
|
49723
|
-
const entry =
|
|
50101
|
+
const entry = lstatSync3(expected);
|
|
49724
50102
|
if (!entry.isFile() || entry.isSymbolicLink() || entry.size <= 0 || entry.size > IMAGE_MAX_BYTES2) return null;
|
|
49725
50103
|
const canonicalRoot = realpathSync5(attachmentsDir);
|
|
49726
50104
|
const canonicalFile = realpathSync5(expected);
|
|
@@ -49857,7 +50235,7 @@ function overlaps(a, b2) {
|
|
|
49857
50235
|
// server/memory-store.ts
|
|
49858
50236
|
import { spawn as spawn3 } from "node:child_process";
|
|
49859
50237
|
import { createHash as createHash15 } from "node:crypto";
|
|
49860
|
-
import { existsSync as
|
|
50238
|
+
import { existsSync as existsSync21, lstatSync as lstatSync4, mkdirSync as mkdirSync19, readdirSync as readdirSync5, readFileSync as readFileSync30, realpathSync as realpathSync7, unlinkSync as unlinkSync7 } from "node:fs";
|
|
49861
50239
|
import { dirname as dirname11, join as join44, sep as sep3 } from "node:path";
|
|
49862
50240
|
var MEMORY_INDEX = "MEMORY.md";
|
|
49863
50241
|
var MemoryStoreError = class extends Error {
|
|
@@ -49912,13 +50290,13 @@ function resolveMemoryPath(botId, path) {
|
|
|
49912
50290
|
return absolute;
|
|
49913
50291
|
}
|
|
49914
50292
|
let ancestor = dirname11(absolute);
|
|
49915
|
-
while (!
|
|
50293
|
+
while (!existsSync21(ancestor) && ancestor !== root) ancestor = dirname11(ancestor);
|
|
49916
50294
|
const realAncestor = realpathSync7(ancestor);
|
|
49917
50295
|
if (realAncestor !== realRoot && !realAncestor.startsWith(realRoot + sep3)) {
|
|
49918
50296
|
throw new MemoryStoreError("path", 400, `memory path escapes the workspace: ${JSON.stringify(path)}`);
|
|
49919
50297
|
}
|
|
49920
50298
|
try {
|
|
49921
|
-
if (
|
|
50299
|
+
if (lstatSync4(absolute).isSymbolicLink()) {
|
|
49922
50300
|
throw new MemoryStoreError("path", 400, `memory file is a link, not a file: ${JSON.stringify(path)}`);
|
|
49923
50301
|
}
|
|
49924
50302
|
} catch (error51) {
|
|
@@ -49963,7 +50341,7 @@ function listMarkdown(dir, prefix) {
|
|
|
49963
50341
|
for (const name2 of names) {
|
|
49964
50342
|
if (!isMemoryTopicName(name2)) continue;
|
|
49965
50343
|
try {
|
|
49966
|
-
const info =
|
|
50344
|
+
const info = lstatSync4(join44(dir, name2));
|
|
49967
50345
|
if (!info.isFile()) continue;
|
|
49968
50346
|
files.push({ path: `${prefix}/${name2}`, name: name2, bytes: info.size, modifiedAt: Math.round(info.mtimeMs) });
|
|
49969
50347
|
} catch {
|
|
@@ -49976,7 +50354,7 @@ function memoryOverview(botId) {
|
|
|
49976
50354
|
const root = workspaceDir(botId);
|
|
49977
50355
|
let raw = "";
|
|
49978
50356
|
try {
|
|
49979
|
-
raw =
|
|
50357
|
+
raw = readFileSync30(resolveMemoryPath(botId, MEMORY_INDEX), "utf8");
|
|
49980
50358
|
} catch (error51) {
|
|
49981
50359
|
if (error51 instanceof MemoryStoreError) throw error51;
|
|
49982
50360
|
raw = "";
|
|
@@ -49992,7 +50370,7 @@ function memoryOverview(botId) {
|
|
|
49992
50370
|
function readMemoryDoc(botId, path) {
|
|
49993
50371
|
const absolute = resolveMemoryPath(botId, path);
|
|
49994
50372
|
try {
|
|
49995
|
-
const text8 =
|
|
50373
|
+
const text8 = readFileSync30(absolute, "utf8");
|
|
49996
50374
|
return { path, text: text8, hash: hashMemoryText(text8), exists: true };
|
|
49997
50375
|
} catch {
|
|
49998
50376
|
return { path, text: "", hash: hashMemoryText(""), exists: false };
|
|
@@ -50082,7 +50460,7 @@ async function openMemoryLocation(botId, target, platform = process.platform, ru
|
|
|
50082
50460
|
}
|
|
50083
50461
|
|
|
50084
50462
|
// server/memory-journal.ts
|
|
50085
|
-
import { randomUUID as
|
|
50463
|
+
import { randomUUID as randomUUID15 } from "node:crypto";
|
|
50086
50464
|
import { closeSync as closeSync7, fstatSync as fstatSync2, mkdirSync as mkdirSync20, openSync as openSync7, readSync as readSync3 } from "node:fs";
|
|
50087
50465
|
import { appendFile as appendFile3 } from "node:fs/promises";
|
|
50088
50466
|
import { join as join45 } from "node:path";
|
|
@@ -50280,7 +50658,7 @@ function recordMemoryChange(botId, change) {
|
|
|
50280
50658
|
}
|
|
50281
50659
|
}
|
|
50282
50660
|
const entry = {
|
|
50283
|
-
id:
|
|
50661
|
+
id: randomUUID15(),
|
|
50284
50662
|
at: Date.now(),
|
|
50285
50663
|
botId,
|
|
50286
50664
|
path: change.path,
|
|
@@ -50396,17 +50774,17 @@ function endMemoryTurn(threadId) {
|
|
|
50396
50774
|
}
|
|
50397
50775
|
|
|
50398
50776
|
// server/skills.ts
|
|
50399
|
-
import { createHash as createHash16, randomUUID as
|
|
50777
|
+
import { createHash as createHash16, randomUUID as randomUUID16 } from "node:crypto";
|
|
50400
50778
|
import {
|
|
50401
50779
|
closeSync as closeSync8,
|
|
50402
50780
|
constants as constants4,
|
|
50403
|
-
existsSync as
|
|
50781
|
+
existsSync as existsSync22,
|
|
50404
50782
|
fstatSync as fstatSync3,
|
|
50405
|
-
lstatSync as
|
|
50783
|
+
lstatSync as lstatSync5,
|
|
50406
50784
|
mkdirSync as mkdirSync21,
|
|
50407
50785
|
openSync as openSync8,
|
|
50408
50786
|
readdirSync as readdirSync6,
|
|
50409
|
-
readFileSync as
|
|
50787
|
+
readFileSync as readFileSync31,
|
|
50410
50788
|
readlinkSync,
|
|
50411
50789
|
realpathSync as realpathSync8,
|
|
50412
50790
|
renameSync as renameSync4,
|
|
@@ -50550,7 +50928,7 @@ function skillsDir(botId) {
|
|
|
50550
50928
|
}
|
|
50551
50929
|
function directoryEntryState(path) {
|
|
50552
50930
|
try {
|
|
50553
|
-
const stat6 =
|
|
50931
|
+
const stat6 = lstatSync5(path);
|
|
50554
50932
|
return stat6.isDirectory() && !stat6.isSymbolicLink() ? "directory" : "unsafe";
|
|
50555
50933
|
} catch {
|
|
50556
50934
|
return "missing";
|
|
@@ -50593,7 +50971,7 @@ function skillTarget(root, name2, entry) {
|
|
|
50593
50971
|
}
|
|
50594
50972
|
function entryExistsWithoutFollowing(path) {
|
|
50595
50973
|
try {
|
|
50596
|
-
|
|
50974
|
+
lstatSync5(path);
|
|
50597
50975
|
return true;
|
|
50598
50976
|
} catch {
|
|
50599
50977
|
return false;
|
|
@@ -50636,7 +51014,7 @@ function managedLinksPath(botId) {
|
|
|
50636
51014
|
}
|
|
50637
51015
|
function readManagedLinks(botId) {
|
|
50638
51016
|
try {
|
|
50639
|
-
const parsed = JSON.parse(
|
|
51017
|
+
const parsed = JSON.parse(readFileSync31(managedLinksPath(botId), "utf8"));
|
|
50640
51018
|
const result = managedLinksSchema.safeParse(parsed);
|
|
50641
51019
|
return result.success ? result.data.filter(isSkillName) : [];
|
|
50642
51020
|
} catch {
|
|
@@ -50650,7 +51028,7 @@ function writeManagedLinks(botId, names) {
|
|
|
50650
51028
|
}
|
|
50651
51029
|
function manifestFromFile(path) {
|
|
50652
51030
|
try {
|
|
50653
|
-
const parsed = JSON.parse(
|
|
51031
|
+
const parsed = JSON.parse(readFileSync31(path, "utf8"));
|
|
50654
51032
|
const result = skillManifestSchema.safeParse(parsed);
|
|
50655
51033
|
if (!result.success) return null;
|
|
50656
51034
|
const manifest = {};
|
|
@@ -50664,11 +51042,11 @@ function manifestFromFile(path) {
|
|
|
50664
51042
|
}
|
|
50665
51043
|
function readManifest(botId) {
|
|
50666
51044
|
const securePath = manifestPath(botId);
|
|
50667
|
-
if (
|
|
51045
|
+
if (existsSync22(securePath)) return manifestFromFile(securePath) ?? {};
|
|
50668
51046
|
const legacyRoot = existingSkillsRoot(botId);
|
|
50669
51047
|
if (!legacyRoot) return {};
|
|
50670
51048
|
const legacyPath = join46(legacyRoot, "skills.json");
|
|
50671
|
-
if (!
|
|
51049
|
+
if (!existsSync22(legacyPath)) return {};
|
|
50672
51050
|
const legacy = manifestFromFile(legacyPath) ?? {};
|
|
50673
51051
|
const migrated = {};
|
|
50674
51052
|
for (const [name2, entry] of Object.entries(legacy)) {
|
|
@@ -50692,7 +51070,7 @@ function comparablePath(path) {
|
|
|
50692
51070
|
}
|
|
50693
51071
|
function nativeLinkPointsToSkill(link2, target) {
|
|
50694
51072
|
try {
|
|
50695
|
-
if (!
|
|
51073
|
+
if (!lstatSync5(link2).isSymbolicLink()) return false;
|
|
50696
51074
|
try {
|
|
50697
51075
|
return comparablePath(realpathSync8(link2)) === comparablePath(realpathSync8(target));
|
|
50698
51076
|
} catch {
|
|
@@ -50706,7 +51084,7 @@ function nativeLinkPointsToSkill(link2, target) {
|
|
|
50706
51084
|
}
|
|
50707
51085
|
function nativeLinkDirectlyTargetsOwnedSkill(link2, root, name2, revisionWasManaged, targetBaseDirectory = dirname12(link2)) {
|
|
50708
51086
|
try {
|
|
50709
|
-
if (!
|
|
51087
|
+
if (!lstatSync5(link2).isSymbolicLink()) return false;
|
|
50710
51088
|
const rawTarget = readlinkSync(link2);
|
|
50711
51089
|
const resolvedTarget = resolve13(targetBaseDirectory, rawTarget.replace(/^\\\\\?\\/, ""));
|
|
50712
51090
|
const insideSkills = relative4(join46(root, "skills"), resolvedTarget).replaceAll("\\", "/");
|
|
@@ -50722,7 +51100,7 @@ function removeOwnedNativeLink(botId, link2, root, name2, revisionWasManaged, be
|
|
|
50722
51100
|
} catch {
|
|
50723
51101
|
return "retry";
|
|
50724
51102
|
}
|
|
50725
|
-
const quarantined = join46(quarantineDir,
|
|
51103
|
+
const quarantined = join46(quarantineDir, randomUUID16());
|
|
50726
51104
|
beforeRemove?.(link2);
|
|
50727
51105
|
try {
|
|
50728
51106
|
renameSync4(link2, quarantined);
|
|
@@ -50860,8 +51238,8 @@ function skillContentMatches(botId, name2, entry) {
|
|
|
50860
51238
|
const directory = skillDirectory(botId, name2, entry);
|
|
50861
51239
|
if (!directory) return false;
|
|
50862
51240
|
const file2 = join46(directory, "SKILL.md");
|
|
50863
|
-
if (!
|
|
50864
|
-
return createHash16("sha256").update(
|
|
51241
|
+
if (!lstatSync5(file2).isFile()) return false;
|
|
51242
|
+
return createHash16("sha256").update(readFileSync31(file2)).digest("hex") === entry.sha256;
|
|
50865
51243
|
} catch {
|
|
50866
51244
|
return false;
|
|
50867
51245
|
}
|
|
@@ -50890,12 +51268,12 @@ function readSkillFile(botId, name2) {
|
|
|
50890
51268
|
let descriptor = null;
|
|
50891
51269
|
try {
|
|
50892
51270
|
const path = join46(directory, "SKILL.md");
|
|
50893
|
-
const before =
|
|
51271
|
+
const before = lstatSync5(path);
|
|
50894
51272
|
if (!before.isFile() || before.size > SKILL_FILE_MAX_BYTES) return null;
|
|
50895
51273
|
descriptor = openSync8(path, constants4.O_RDONLY | (process.platform === "win32" ? 0 : constants4.O_NOFOLLOW ?? 0));
|
|
50896
51274
|
const opened = fstatSync3(descriptor);
|
|
50897
51275
|
if (!opened.isFile() || opened.size > SKILL_FILE_MAX_BYTES || opened.dev !== before.dev || opened.ino !== before.ino) return null;
|
|
50898
|
-
const text8 =
|
|
51276
|
+
const text8 = readFileSync31(descriptor, "utf8");
|
|
50899
51277
|
return createHash16("sha256").update(text8).digest("hex") === entry.sha256 ? text8 : null;
|
|
50900
51278
|
} catch {
|
|
50901
51279
|
return null;
|
|
@@ -50966,9 +51344,9 @@ function removeReviewedRevisionsNamed(botId, name2) {
|
|
|
50966
51344
|
if (directoryEntryState(directory) !== "directory") continue;
|
|
50967
51345
|
try {
|
|
50968
51346
|
const file2 = join46(directory, "SKILL.md");
|
|
50969
|
-
const stat6 =
|
|
51347
|
+
const stat6 = lstatSync5(file2);
|
|
50970
51348
|
if (!stat6.isFile() || stat6.size > SKILL_FILE_MAX_BYTES) continue;
|
|
50971
|
-
const parsed = parseSkillMd(
|
|
51349
|
+
const parsed = parseSkillMd(readFileSync31(file2, "utf8"));
|
|
50972
51350
|
if ("error" in parsed || parsed.name !== name2) continue;
|
|
50973
51351
|
rmSync5(directory, { recursive: true, force: true });
|
|
50974
51352
|
} catch {
|
|
@@ -51021,9 +51399,9 @@ function stagedPath(botId) {
|
|
|
51021
51399
|
}
|
|
51022
51400
|
function readStaged(botId) {
|
|
51023
51401
|
const securePath = stagedPath(botId);
|
|
51024
|
-
if (
|
|
51402
|
+
if (existsSync22(securePath)) {
|
|
51025
51403
|
try {
|
|
51026
|
-
const parsed = JSON.parse(
|
|
51404
|
+
const parsed = JSON.parse(readFileSync31(securePath, "utf8"));
|
|
51027
51405
|
const result = stagedStoreSchema.safeParse(parsed);
|
|
51028
51406
|
if (result.success) return result.data;
|
|
51029
51407
|
} catch {
|
|
@@ -51033,7 +51411,7 @@ function readStaged(botId) {
|
|
|
51033
51411
|
const legacyRoot = existingSkillsRoot(botId);
|
|
51034
51412
|
if (!legacyRoot) return { writes: {} };
|
|
51035
51413
|
const legacyPath = join46(legacyRoot, "staged.json");
|
|
51036
|
-
if (!
|
|
51414
|
+
if (!existsSync22(legacyPath)) return { writes: {} };
|
|
51037
51415
|
const empty = { writes: {} };
|
|
51038
51416
|
writeStaged(botId, empty);
|
|
51039
51417
|
try {
|
|
@@ -51082,8 +51460,8 @@ function learnedSkillDirectoryMatches(directory, sha2564) {
|
|
|
51082
51460
|
const entries = readdirSync6(directory);
|
|
51083
51461
|
if (entries.length !== 1 || entries[0] !== "SKILL.md") return false;
|
|
51084
51462
|
const file2 = join46(directory, "SKILL.md");
|
|
51085
|
-
if (!
|
|
51086
|
-
return createHash16("sha256").update(
|
|
51463
|
+
if (!lstatSync5(file2).isFile()) return false;
|
|
51464
|
+
return createHash16("sha256").update(readFileSync31(file2)).digest("hex") === sha2564;
|
|
51087
51465
|
} catch {
|
|
51088
51466
|
return false;
|
|
51089
51467
|
}
|
|
@@ -51094,7 +51472,7 @@ function installedLearnedSkillMatches(botId, name2, entry) {
|
|
|
51094
51472
|
}
|
|
51095
51473
|
function directoryIdentity2(path) {
|
|
51096
51474
|
try {
|
|
51097
|
-
const stat6 =
|
|
51475
|
+
const stat6 = lstatSync5(path);
|
|
51098
51476
|
return stat6.isDirectory() && !stat6.isSymbolicLink() ? { dev: stat6.dev, ino: stat6.ino } : null;
|
|
51099
51477
|
} catch {
|
|
51100
51478
|
return null;
|
|
@@ -51125,7 +51503,7 @@ function publishReviewedRevision(botId, stageId, skillMd, sha2564) {
|
|
|
51125
51503
|
}
|
|
51126
51504
|
rmSync5(prepared, { recursive: true, force: true });
|
|
51127
51505
|
}
|
|
51128
|
-
const temporary = join46(preparedRoot, `.prepare-${revision}-${
|
|
51506
|
+
const temporary = join46(preparedRoot, `.prepare-${revision}-${randomUUID16()}`);
|
|
51129
51507
|
try {
|
|
51130
51508
|
mkdirSync21(temporary, { mode: 448 });
|
|
51131
51509
|
writeFileAtomic(join46(temporary, "SKILL.md"), skillMd, { mode: 384 });
|
|
@@ -51172,7 +51550,7 @@ function commitNewSkillFiles(botId, name2, files, commitManifest) {
|
|
|
51172
51550
|
const root = ensureSkillsRoot(botId);
|
|
51173
51551
|
if (!root) throw new Error("the workspace skills path must be a real directory, not a symlink or file");
|
|
51174
51552
|
const target = join46(root, name2);
|
|
51175
|
-
const staged = join46(root, `.install-${name2}-${
|
|
51553
|
+
const staged = join46(root, `.install-${name2}-${randomUUID16()}`);
|
|
51176
51554
|
if (entryExistsWithoutFollowing(target)) throw new Error(`skill path already exists: ${name2}`);
|
|
51177
51555
|
let published = false;
|
|
51178
51556
|
try {
|
|
@@ -51355,7 +51733,7 @@ function stageSkillWrite(botId, input) {
|
|
|
51355
51733
|
return { error: `skill "${parsed.name}" already matches the proposed SKILL.md` };
|
|
51356
51734
|
}
|
|
51357
51735
|
const entry = {
|
|
51358
|
-
id:
|
|
51736
|
+
id: randomUUID16(),
|
|
51359
51737
|
action: input.action,
|
|
51360
51738
|
name: parsed.name,
|
|
51361
51739
|
gist: gist || parsed.description.slice(0, STAGED_GIST_MAX),
|
|
@@ -51697,7 +52075,7 @@ function mentionPrompt(tagged) {
|
|
|
51697
52075
|
}
|
|
51698
52076
|
|
|
51699
52077
|
// server/local-computer.ts
|
|
51700
|
-
import { lstatSync as
|
|
52078
|
+
import { lstatSync as lstatSync6, readFileSync as readFileSync32, realpathSync as realpathSync9, statSync as statSync7 } from "node:fs";
|
|
51701
52079
|
import { homedir as homedir23 } from "node:os";
|
|
51702
52080
|
import { dirname as dirname13, isAbsolute as isAbsolute10, join as join47 } from "node:path";
|
|
51703
52081
|
var REQUIRED_LINUX_TOOLS = ["click", "get_window_state", "list_apps", "type_text"];
|
|
@@ -51857,8 +52235,8 @@ function validateLinuxDescriptorRuntime(descriptorFile, raw, {
|
|
|
51857
52235
|
isProcessAlive = processAlive
|
|
51858
52236
|
} = {}) {
|
|
51859
52237
|
try {
|
|
51860
|
-
const descriptorStat =
|
|
51861
|
-
const descriptorDirectoryStat =
|
|
52238
|
+
const descriptorStat = lstatSync6(descriptorFile);
|
|
52239
|
+
const descriptorDirectoryStat = lstatSync6(dirname13(descriptorFile));
|
|
51862
52240
|
if (!descriptorStat.isFile() || descriptorStat.isSymbolicLink() || !ownedPrivate(descriptorStat, uid) || !descriptorDirectoryStat.isDirectory() || descriptorDirectoryStat.isSymbolicLink() || !ownedPrivate(descriptorDirectoryStat, uid)) {
|
|
51863
52241
|
return false;
|
|
51864
52242
|
}
|
|
@@ -51868,8 +52246,8 @@ function validateLinuxDescriptorRuntime(descriptorFile, raw, {
|
|
|
51868
52246
|
const socketPath = daemon.socketPath;
|
|
51869
52247
|
const binaryStat = statSync7(binaryPath);
|
|
51870
52248
|
const currentFileIdentity = currentDriverFileIdentity(binaryPath);
|
|
51871
|
-
const socketStat =
|
|
51872
|
-
const socketDirectoryStat =
|
|
52249
|
+
const socketStat = lstatSync6(socketPath);
|
|
52250
|
+
const socketDirectoryStat = lstatSync6(dirname13(socketPath));
|
|
51873
52251
|
if (realpathSync9(binaryPath) !== binaryPath || !sameDriverFileIdentity(driver.fileIdentity, currentFileIdentity) || !binaryStat.isFile() || binaryStat.uid !== uid && binaryStat.uid !== 0 || (binaryStat.mode & 73) === 0 || (binaryStat.mode & 18) !== 0 || !socketStat.isSocket() || socketStat.isSymbolicLink() || !ownedPrivate(socketStat, uid) || !socketDirectoryStat.isDirectory() || socketDirectoryStat.isSymbolicLink() || !ownedPrivate(socketDirectoryStat, uid) || !isProcessAlive(raw.ownerPid) || !isProcessAlive(daemon.pid)) {
|
|
51874
52252
|
return false;
|
|
51875
52253
|
}
|
|
@@ -51892,7 +52270,7 @@ function readCuaConnection({
|
|
|
51892
52270
|
}
|
|
51893
52271
|
for (const file2 of new Set(candidates)) {
|
|
51894
52272
|
try {
|
|
51895
|
-
const raw = JSON.parse(
|
|
52273
|
+
const raw = JSON.parse(readFileSync32(file2, "utf8"));
|
|
51896
52274
|
if (platform === "linux") {
|
|
51897
52275
|
const decoded = decodeLinuxDescriptor(raw);
|
|
51898
52276
|
if (decoded && validateLinuxRuntime(file2, raw)) return decoded;
|
|
@@ -52819,14 +53197,14 @@ async function vpsComputerJoin(cfg2, botId, runner = defaultRunner) {
|
|
|
52819
53197
|
stdio: ["ignore", "ignore", "pipe"],
|
|
52820
53198
|
windowsHide: true
|
|
52821
53199
|
});
|
|
52822
|
-
let
|
|
53200
|
+
let failure5 = null;
|
|
52823
53201
|
let stderr = "";
|
|
52824
53202
|
child.stderr?.setEncoding("utf8");
|
|
52825
53203
|
child.stderr?.on("data", (chunk) => {
|
|
52826
53204
|
stderr = `${stderr}${chunk}`.slice(-1e3);
|
|
52827
53205
|
});
|
|
52828
53206
|
child.once("error", (error51) => {
|
|
52829
|
-
|
|
53207
|
+
failure5 = error51.message;
|
|
52830
53208
|
});
|
|
52831
53209
|
child.once("close", (code) => {
|
|
52832
53210
|
const active = desktopTunnels.get(botId);
|
|
@@ -52834,16 +53212,16 @@ async function vpsComputerJoin(cfg2, botId, runner = defaultRunner) {
|
|
|
52834
53212
|
clearTimeout(active.expiry);
|
|
52835
53213
|
desktopTunnels.delete(botId);
|
|
52836
53214
|
}
|
|
52837
|
-
if (!
|
|
53215
|
+
if (!failure5) failure5 = stderr.trim() || `SSH viewer tunnel exited ${code ?? "without a status"}`;
|
|
52838
53216
|
});
|
|
52839
53217
|
const deadline = Date.now() + 1e4;
|
|
52840
|
-
while (Date.now() < deadline && !
|
|
53218
|
+
while (Date.now() < deadline && !failure5) {
|
|
52841
53219
|
if (await loopbackAnswers(localPort)) break;
|
|
52842
53220
|
await new Promise((resolve16) => setTimeout(resolve16, 100));
|
|
52843
53221
|
}
|
|
52844
|
-
if (
|
|
53222
|
+
if (failure5 || !await loopbackAnswers(localPort)) {
|
|
52845
53223
|
if (child.exitCode === null && !child.killed) child.kill("SIGTERM");
|
|
52846
|
-
const detail =
|
|
53224
|
+
const detail = failure5 || stderr.trim() || "the SSH port forward did not become ready";
|
|
52847
53225
|
throw Object.assign(new Error(`Could not open the VPS live desktop: ${detail}`), { status: 502 });
|
|
52848
53226
|
}
|
|
52849
53227
|
const joinUrl = `http://127.0.0.1:${localPort}/vnc.html#autoconnect=true&resize=scale&password=${encodeURIComponent(connection.password)}`;
|
|
@@ -52965,8 +53343,8 @@ async function vpsComputerScreenshot(cfg2, botId, runner = defaultRunner) {
|
|
|
52965
53343
|
}
|
|
52966
53344
|
|
|
52967
53345
|
// server/routines.ts
|
|
52968
|
-
import { randomUUID as
|
|
52969
|
-
import { mkdirSync as mkdirSync22, readFileSync as
|
|
53346
|
+
import { randomUUID as randomUUID17 } from "node:crypto";
|
|
53347
|
+
import { mkdirSync as mkdirSync22, readFileSync as readFileSync33 } from "node:fs";
|
|
52970
53348
|
import { dirname as dirname14, join as join48 } from "node:path";
|
|
52971
53349
|
var persistedSourceThreadId = external_exports.string().trim().min(1).optional().catch(void 0);
|
|
52972
53350
|
var webhookRunReceiptSchema = external_exports.object({
|
|
@@ -53352,7 +53730,7 @@ var RoutineManager = class {
|
|
|
53352
53730
|
this.file = options.file ?? join48(DATA_DIR, "routines.json");
|
|
53353
53731
|
this.now = options.now ?? Date.now;
|
|
53354
53732
|
try {
|
|
53355
|
-
const disk = JSON.parse(
|
|
53733
|
+
const disk = JSON.parse(readFileSync33(this.file, "utf8"));
|
|
53356
53734
|
this.routines = Array.isArray(disk.routines) ? disk.routines.flatMap((routine) => {
|
|
53357
53735
|
const schedule2 = loadSchedule(routine.schedule);
|
|
53358
53736
|
if (!schedule2) return [];
|
|
@@ -53522,7 +53900,7 @@ var RoutineManager = class {
|
|
|
53522
53900
|
throw new Error("This interval has no future runs. Choose a later end date or turn it off.");
|
|
53523
53901
|
}
|
|
53524
53902
|
const routine = {
|
|
53525
|
-
id:
|
|
53903
|
+
id: randomUUID17(),
|
|
53526
53904
|
...clean4,
|
|
53527
53905
|
// Only a confirmed chat card supplies `request`; the public calendar
|
|
53528
53906
|
// API cannot choose an arbitrary transcript as a reporting target.
|
|
@@ -53723,7 +54101,7 @@ var RoutineManager = class {
|
|
|
53723
54101
|
throw Object.assign(new Error("The assigned MAUS no longer exists"), { status: 410 });
|
|
53724
54102
|
}
|
|
53725
54103
|
const run3 = {
|
|
53726
|
-
id:
|
|
54104
|
+
id: randomUUID17(),
|
|
53727
54105
|
routineId: input.webhookId,
|
|
53728
54106
|
routineName: input.webhookName,
|
|
53729
54107
|
prompt: input.prompt,
|
|
@@ -54107,7 +54485,7 @@ var RoutineManager = class {
|
|
|
54107
54485
|
}
|
|
54108
54486
|
}
|
|
54109
54487
|
const run3 = {
|
|
54110
|
-
id:
|
|
54488
|
+
id: randomUUID17(),
|
|
54111
54489
|
routineId: routine.id,
|
|
54112
54490
|
routineName: routine.name,
|
|
54113
54491
|
prompt: routine.prompt,
|
|
@@ -54235,8 +54613,8 @@ var RoutineManager = class {
|
|
|
54235
54613
|
};
|
|
54236
54614
|
|
|
54237
54615
|
// server/calendar-calls.ts
|
|
54238
|
-
import { randomUUID as
|
|
54239
|
-
import { mkdirSync as mkdirSync23, readFileSync as
|
|
54616
|
+
import { randomUUID as randomUUID18 } from "node:crypto";
|
|
54617
|
+
import { mkdirSync as mkdirSync23, readFileSync as readFileSync34 } from "node:fs";
|
|
54240
54618
|
import { dirname as dirname15, join as join49 } from "node:path";
|
|
54241
54619
|
var MAX_BOTS = 100;
|
|
54242
54620
|
var MAX_ATTACHMENTS2 = 50;
|
|
@@ -54268,7 +54646,7 @@ var callSchema = external_exports.object({
|
|
|
54268
54646
|
createdAt: external_exports.number().finite().nonnegative(),
|
|
54269
54647
|
updatedAt: external_exports.number().finite().nonnegative()
|
|
54270
54648
|
});
|
|
54271
|
-
var
|
|
54649
|
+
var fileSchema2 = external_exports.object({
|
|
54272
54650
|
version: external_exports.literal(1),
|
|
54273
54651
|
calls: external_exports.array(external_exports.unknown())
|
|
54274
54652
|
});
|
|
@@ -54349,7 +54727,7 @@ var CalendarCallManager = class {
|
|
|
54349
54727
|
this.onDue = options.onDue;
|
|
54350
54728
|
let migrated = false;
|
|
54351
54729
|
try {
|
|
54352
|
-
const parsed =
|
|
54730
|
+
const parsed = fileSchema2.safeParse(JSON.parse(readFileSync34(this.file, "utf8")));
|
|
54353
54731
|
this.calls = parsed.success ? parsed.data.calls.flatMap((candidate) => {
|
|
54354
54732
|
const call = callSchema.safeParse(candidate);
|
|
54355
54733
|
if (!call.success) return [];
|
|
@@ -54379,7 +54757,7 @@ var CalendarCallManager = class {
|
|
|
54379
54757
|
const clean4 = cleanInput(input, this.botExists);
|
|
54380
54758
|
const timestamp4 = this.now();
|
|
54381
54759
|
const call = {
|
|
54382
|
-
id:
|
|
54760
|
+
id: randomUUID18(),
|
|
54383
54761
|
...clean4,
|
|
54384
54762
|
nextRunAt: this.initialOccurrence(clean4.schedule, timestamp4),
|
|
54385
54763
|
createdAt: timestamp4,
|
|
@@ -54512,8 +54890,8 @@ var CalendarCallManager = class {
|
|
|
54512
54890
|
|
|
54513
54891
|
// server/browser-engine.ts
|
|
54514
54892
|
import { spawn as spawn5 } from "node:child_process";
|
|
54515
|
-
import { createHash as createHash18, randomBytes as randomBytes4, randomUUID as
|
|
54516
|
-
import { chmodSync as chmodSync5, constants as constants5, copyFileSync, existsSync as
|
|
54893
|
+
import { createHash as createHash18, randomBytes as randomBytes4, randomUUID as randomUUID19 } from "node:crypto";
|
|
54894
|
+
import { chmodSync as chmodSync5, constants as constants5, copyFileSync, existsSync as existsSync23, mkdirSync as mkdirSync24, readFileSync as readFileSync35, readdirSync as readdirSync7, renameSync as renameSync5, rmSync as rmSync6, statSync as statSync8, unlinkSync as unlinkSync9, writeFileSync as writeFileSync15 } from "node:fs";
|
|
54517
54895
|
import { homedir as homedir24, tmpdir as tmpdir6 } from "node:os";
|
|
54518
54896
|
import { delimiter as delimiter4, dirname as dirname16, join as join51, resolve as resolve14 } from "node:path";
|
|
54519
54897
|
|
|
@@ -54972,7 +55350,7 @@ function ensureManagedBrowserConfig(env) {
|
|
|
54972
55350
|
writeFileSync15(path, "{}\n", { flag: "wx", mode: 384 });
|
|
54973
55351
|
} catch (error51) {
|
|
54974
55352
|
if (error51.code !== "EEXIST") throw error51;
|
|
54975
|
-
if (
|
|
55353
|
+
if (readFileSync35(path, "utf8").trim() !== "{}") throw new Error("The managed browser configuration was changed. Restore it to an empty JSON object before connecting.");
|
|
54976
55354
|
}
|
|
54977
55355
|
return path;
|
|
54978
55356
|
}
|
|
@@ -55056,7 +55434,7 @@ async function prepareBrowserSessionState(binaryPath, session, options = {}) {
|
|
|
55056
55434
|
if (!pending) {
|
|
55057
55435
|
pending = (async () => {
|
|
55058
55436
|
const targetBase = join51(directory, `${restoreKey}-${session}`);
|
|
55059
|
-
const hasTarget = () =>
|
|
55437
|
+
const hasTarget = () => existsSync23(`${targetBase}.json.enc`) || existsSync23(`${targetBase}.json`);
|
|
55060
55438
|
if (hasTarget()) return;
|
|
55061
55439
|
check2();
|
|
55062
55440
|
if (!await closeBrowserSession(binaryPath, { ...env, AGENT_BROWSER_RESTORE: session }, options.timeoutMs)) {
|
|
@@ -55095,7 +55473,7 @@ async function prepareBrowserSessionState(binaryPath, session, options = {}) {
|
|
|
55095
55473
|
function executableName(platform = process.platform) {
|
|
55096
55474
|
return platform === "win32" ? "agent-browser.exe" : "agent-browser";
|
|
55097
55475
|
}
|
|
55098
|
-
function isMusl(platform = process.platform, exists =
|
|
55476
|
+
function isMusl(platform = process.platform, exists = existsSync23) {
|
|
55099
55477
|
return platform === "linux" && (exists("/lib/ld-musl-x86_64.so.1") || exists("/lib/ld-musl-aarch64.so.1"));
|
|
55100
55478
|
}
|
|
55101
55479
|
function pinnedBinaryPath(dataDir = DATA_DIR, platform = process.platform, arch = process.arch) {
|
|
@@ -55127,7 +55505,7 @@ function completePackage(bundle, exists) {
|
|
|
55127
55505
|
function resolveAgentBrowserBinary(options = {}) {
|
|
55128
55506
|
const env = options.env ?? process.env;
|
|
55129
55507
|
const platform = options.platform ?? process.platform;
|
|
55130
|
-
const exists = options.exists ??
|
|
55508
|
+
const exists = options.exists ?? existsSync23;
|
|
55131
55509
|
const override = env.OMB_AGENT_BROWSER_PATH?.trim();
|
|
55132
55510
|
if (override) return resolve14(override) && exists(resolve14(override)) ? resolve14(override) : null;
|
|
55133
55511
|
const bundle = packagedBrowser(options);
|
|
@@ -55167,7 +55545,7 @@ async function installAgentBrowserBinary(options = {}) {
|
|
|
55167
55545
|
}
|
|
55168
55546
|
function ensureChrome(binaryPath, options = {}) {
|
|
55169
55547
|
const bundle = packagedBrowser(options);
|
|
55170
|
-
if (!options.withDeps && bundle && resolve14(binaryPath) === bundle.engine && completePackage(bundle,
|
|
55548
|
+
if (!options.withDeps && bundle && resolve14(binaryPath) === bundle.engine && completePackage(bundle, existsSync23)) {
|
|
55171
55549
|
options.log?.("agent-browser: the bundled browser is ready; no download needed");
|
|
55172
55550
|
return Promise.resolve();
|
|
55173
55551
|
}
|
|
@@ -55195,7 +55573,7 @@ function ensureChrome(binaryPath, options = {}) {
|
|
|
55195
55573
|
function browserEngineEncryptionKey(dataDir = DATA_DIR) {
|
|
55196
55574
|
const file2 = join51(dataDir, KEY_FILE);
|
|
55197
55575
|
try {
|
|
55198
|
-
const existing =
|
|
55576
|
+
const existing = readFileSync35(file2, "utf8").trim();
|
|
55199
55577
|
if (/^[0-9a-f]{64}$/u.test(existing)) return existing;
|
|
55200
55578
|
} catch {
|
|
55201
55579
|
}
|
|
@@ -55214,7 +55592,7 @@ function browserEngineStatus(options = {}) {
|
|
|
55214
55592
|
const version2 = managed ? agentBrowserReleaseVersion(resolveAgentBrowserReleaseAsset(platform2, options.arch)) : AGENT_BROWSER_VERSION;
|
|
55215
55593
|
return { kind: "ready", binaryPath, version: version2 };
|
|
55216
55594
|
}
|
|
55217
|
-
if (bundle && (options.exists ??
|
|
55595
|
+
if (bundle && (options.exists ?? existsSync23)(bundle.directory)) {
|
|
55218
55596
|
return { kind: "unavailable", reason: "The desktop browser bundle is incomplete. Reinstall or update OpenMausBot to repair it.", installable: false };
|
|
55219
55597
|
}
|
|
55220
55598
|
const platform = options.platform ?? process.platform;
|
|
@@ -55247,14 +55625,14 @@ function agentBrowserIntegration(input) {
|
|
|
55247
55625
|
if (sourceEnv[name2]) env[name2] = sourceEnv[name2];
|
|
55248
55626
|
}
|
|
55249
55627
|
const bundle = packagedBrowser({ env: sourceEnv });
|
|
55250
|
-
if (!env.AGENT_BROWSER_EXECUTABLE_PATH && bundle && resolve14(input.binaryPath) === bundle.engine && completePackage(bundle,
|
|
55628
|
+
if (!env.AGENT_BROWSER_EXECUTABLE_PATH && bundle && resolve14(input.binaryPath) === bundle.engine && completePackage(bundle, existsSync23)) {
|
|
55251
55629
|
env.AGENT_BROWSER_EXECUTABLE_PATH = bundle.chrome;
|
|
55252
55630
|
}
|
|
55253
55631
|
return { command: input.binaryPath, args: ["mcp", "--tools", "core", "--no-webmcp"], env };
|
|
55254
55632
|
}
|
|
55255
55633
|
var FRAME_TIMEOUT_MS = 1e4;
|
|
55256
55634
|
function agentBrowserFrame(input) {
|
|
55257
|
-
const file2 = join51(tmpdir6(), `openmausbot-browser-${
|
|
55635
|
+
const file2 = join51(tmpdir6(), `openmausbot-browser-${randomUUID19()}.png`);
|
|
55258
55636
|
return new Promise((settle, fail2) => {
|
|
55259
55637
|
const child = spawn5(input.binaryPath, ["screenshot", file2], {
|
|
55260
55638
|
env: browserRuntimeEnv(input.env),
|
|
@@ -55276,7 +55654,7 @@ function agentBrowserFrame(input) {
|
|
|
55276
55654
|
fail2(error51);
|
|
55277
55655
|
return;
|
|
55278
55656
|
}
|
|
55279
|
-
settle({ png:
|
|
55657
|
+
settle({ png: readFileSync35(file2).toString("base64"), format: "png" });
|
|
55280
55658
|
} catch {
|
|
55281
55659
|
fail2(new Error("the browser reported a picture it did not write"));
|
|
55282
55660
|
} finally {
|
|
@@ -55290,7 +55668,7 @@ function agentBrowserFrame(input) {
|
|
|
55290
55668
|
});
|
|
55291
55669
|
}
|
|
55292
55670
|
function browserSessionId(botId, partitionId) {
|
|
55293
|
-
if (partitionId === "guest") return `guest-${
|
|
55671
|
+
if (partitionId === "guest") return `guest-${randomUUID19()}`;
|
|
55294
55672
|
const raw = partitionId || `bot-${botId}`;
|
|
55295
55673
|
return raw.replace(/[^A-Za-z0-9_.-]/gu, "_").slice(0, 96);
|
|
55296
55674
|
}
|
|
@@ -55337,7 +55715,7 @@ async function clearBrowserSessionState(binaryPath, session, options = {}) {
|
|
|
55337
55715
|
|
|
55338
55716
|
// server/browser-live.ts
|
|
55339
55717
|
import { execFile as execFile6 } from "node:child_process";
|
|
55340
|
-
import { randomUUID as
|
|
55718
|
+
import { randomUUID as randomUUID20 } from "node:crypto";
|
|
55341
55719
|
import { promisify as promisify3 } from "node:util";
|
|
55342
55720
|
var execute = promisify3(execFile6);
|
|
55343
55721
|
var MAX_FRAME = 3 * 1024 * 1024;
|
|
@@ -55678,7 +56056,7 @@ data: ${JSON.stringify(data)}
|
|
|
55678
56056
|
if (this.viewers.size >= 8 || [...this.viewers.values()].filter((v2) => v2.session === options.session).length >= 2) {
|
|
55679
56057
|
throw new BrowserLiveError("Too many browser views are open. Close another browser panel first.", 429);
|
|
55680
56058
|
}
|
|
55681
|
-
const viewer = { ...options, id:
|
|
56059
|
+
const viewer = { ...options, id: randomUUID20(), closed: false, blocked: false, pendingActions: 0, restarting: false, pressedKeys: /* @__PURE__ */ new Set(), pressedButtons: /* @__PURE__ */ new Set() };
|
|
55682
56060
|
if (!this.current(viewer)) throw new BrowserLiveError("This browser view is no longer available.", 409);
|
|
55683
56061
|
this.viewers.set(viewer.id, viewer);
|
|
55684
56062
|
options.res.once("close", () => this.close(viewer));
|
|
@@ -55853,6 +56231,9 @@ data: ${JSON.stringify(data)}
|
|
|
55853
56231
|
closeForSession(session) {
|
|
55854
56232
|
for (const viewer of this.viewers.values()) if (viewer.session === session) this.close(viewer);
|
|
55855
56233
|
}
|
|
56234
|
+
closeForOwner(owner2) {
|
|
56235
|
+
for (const viewer of this.viewers.values()) if (viewer.owner === owner2) this.close(viewer);
|
|
56236
|
+
}
|
|
55856
56237
|
closeForBot(botId) {
|
|
55857
56238
|
for (const viewer of this.viewers.values()) if (viewer.botId === botId) this.close(viewer);
|
|
55858
56239
|
}
|
|
@@ -57228,8 +57609,8 @@ function profileRevision(bot) {
|
|
|
57228
57609
|
|
|
57229
57610
|
// server/profile-versions.ts
|
|
57230
57611
|
import { appendFile as appendFile4 } from "node:fs/promises";
|
|
57231
|
-
import { createHash as createHash22, randomUUID as
|
|
57232
|
-
import { closeSync as closeSync9, existsSync as
|
|
57612
|
+
import { createHash as createHash22, randomUUID as randomUUID21 } from "node:crypto";
|
|
57613
|
+
import { closeSync as closeSync9, existsSync as existsSync24, fstatSync as fstatSync4, mkdirSync as mkdirSync25, openSync as openSync9, readSync as readSync4 } from "node:fs";
|
|
57233
57614
|
import { join as join52 } from "node:path";
|
|
57234
57615
|
var FILE_NAME2 = "history.ndjson";
|
|
57235
57616
|
var ONE_LINER = 200;
|
|
@@ -57245,11 +57626,11 @@ function rowFor(field, at2, actor, via, before, after) {
|
|
|
57245
57626
|
if (field === "soul") {
|
|
57246
57627
|
const b3 = Buffer.byteLength(before, "utf8");
|
|
57247
57628
|
const a2 = Buffer.byteLength(after, "utf8");
|
|
57248
|
-
return { id:
|
|
57629
|
+
return { id: randomUUID21(), at: at2, actor, via, field, summary: `soul: ${b3} \u2192 ${a2} bytes`, before, after };
|
|
57249
57630
|
}
|
|
57250
57631
|
const b2 = oneLiner(before);
|
|
57251
57632
|
const a = oneLiner(after);
|
|
57252
|
-
return { id:
|
|
57633
|
+
return { id: randomUUID21(), at: at2, actor, via, field, summary: `${field}: ${JSON.stringify(b2)} \u2192 ${JSON.stringify(a)}`, before: b2, after: a };
|
|
57253
57634
|
}
|
|
57254
57635
|
function withRestoreEligibility(row) {
|
|
57255
57636
|
const redacted = redactSecrets(row);
|
|
@@ -57261,7 +57642,7 @@ function withRestoreEligibility(row) {
|
|
|
57261
57642
|
};
|
|
57262
57643
|
}
|
|
57263
57644
|
async function writeRows(botId, rows) {
|
|
57264
|
-
if (!
|
|
57645
|
+
if (!existsSync24(botFolder(botId))) return;
|
|
57265
57646
|
const file2 = historyFile(botId);
|
|
57266
57647
|
mkdirSync25(botFolder(botId), { recursive: true, mode: 448 });
|
|
57267
57648
|
const text8 = rows.map((row) => JSON.stringify(withRestoreEligibility(row))).join("\n") + "\n";
|
|
@@ -57971,7 +58352,7 @@ function matchDirectoryBots(profile, bots, limit = 5) {
|
|
|
57971
58352
|
}
|
|
57972
58353
|
|
|
57973
58354
|
// server/project-scout.ts
|
|
57974
|
-
import { readFileSync as
|
|
58355
|
+
import { readFileSync as readFileSync36, readdirSync as readdirSync8, statSync as statSync9 } from "node:fs";
|
|
57975
58356
|
import { basename as basename7, join as join53 } from "node:path";
|
|
57976
58357
|
|
|
57977
58358
|
// shared/import-name.ts
|
|
@@ -58156,7 +58537,7 @@ var MAX_DIR_ENTRIES = 400;
|
|
|
58156
58537
|
function readText2(path) {
|
|
58157
58538
|
try {
|
|
58158
58539
|
if (statSync9(path).size > MAX_FILE_BYTES2) return null;
|
|
58159
|
-
return
|
|
58540
|
+
return readFileSync36(path, "utf8");
|
|
58160
58541
|
} catch {
|
|
58161
58542
|
return null;
|
|
58162
58543
|
}
|
|
@@ -59288,8 +59669,8 @@ function memberTurnSelection(selection) {
|
|
|
59288
59669
|
}
|
|
59289
59670
|
|
|
59290
59671
|
// server/webhooks.ts
|
|
59291
|
-
import { createHash as createHash24, randomBytes as randomBytes5, randomUUID as
|
|
59292
|
-
import { mkdirSync as mkdirSync26, readFileSync as
|
|
59672
|
+
import { createHash as createHash24, randomBytes as randomBytes5, randomUUID as randomUUID22, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
|
|
59673
|
+
import { mkdirSync as mkdirSync26, readFileSync as readFileSync37 } from "node:fs";
|
|
59293
59674
|
import { dirname as dirname17, join as join55 } from "node:path";
|
|
59294
59675
|
var MAX_DELIVERIES = 2e3;
|
|
59295
59676
|
var MAX_ATTEMPTS = 2e3;
|
|
@@ -59372,7 +59753,7 @@ function secretMatches(secret, expectedHex) {
|
|
|
59372
59753
|
if (!secret) return false;
|
|
59373
59754
|
const actual = Buffer.from(hashSecret(secret), "hex");
|
|
59374
59755
|
const expected = Buffer.from(expectedHex, "hex");
|
|
59375
|
-
return actual.length === expected.length &&
|
|
59756
|
+
return actual.length === expected.length && timingSafeEqual2(actual, expected);
|
|
59376
59757
|
}
|
|
59377
59758
|
function newEndpointId() {
|
|
59378
59759
|
return `wh_${randomBytes5(12).toString("base64url")}`;
|
|
@@ -59480,7 +59861,7 @@ var WebhookManager = class {
|
|
|
59480
59861
|
this.file = options.file ?? join55(DATA_DIR, "webhooks.json");
|
|
59481
59862
|
this.now = options.now ?? Date.now;
|
|
59482
59863
|
try {
|
|
59483
|
-
const parsed = webhookFileSchema.safeParse(parseJson(
|
|
59864
|
+
const parsed = webhookFileSchema.safeParse(parseJson(readFileSync37(this.file, "utf8")));
|
|
59484
59865
|
if (!parsed.success) throw parsed.error;
|
|
59485
59866
|
this.webhooks = parsed.data.webhooks;
|
|
59486
59867
|
this.deliveries = parsed.data.deliveries.slice(-MAX_DELIVERIES);
|
|
@@ -59503,7 +59884,7 @@ var WebhookManager = class {
|
|
|
59503
59884
|
const now = this.now();
|
|
59504
59885
|
const secret = newSecret();
|
|
59505
59886
|
const trigger = {
|
|
59506
|
-
id:
|
|
59887
|
+
id: randomUUID22(),
|
|
59507
59888
|
endpointId: newEndpointId(),
|
|
59508
59889
|
...clean4,
|
|
59509
59890
|
secretHash: hashSecret(secret),
|
|
@@ -59599,7 +59980,7 @@ var WebhookManager = class {
|
|
|
59599
59980
|
contentType: "application/json",
|
|
59600
59981
|
eventName: eventName2,
|
|
59601
59982
|
userAgent: "OpenMausBot webhook tester",
|
|
59602
|
-
deliveryId: `test-${
|
|
59983
|
+
deliveryId: `test-${randomUUID22()}`
|
|
59603
59984
|
});
|
|
59604
59985
|
}
|
|
59605
59986
|
recordRejected(endpointId, statusCode, reason, event = {}) {
|
|
@@ -59612,7 +59993,7 @@ var WebhookManager = class {
|
|
|
59612
59993
|
if (this.options.botState(trigger.botId) === "missing") fail(410, "The assigned MAUS no longer exists");
|
|
59613
59994
|
const allowed = trigger.eventTypes ?? [];
|
|
59614
59995
|
if (allowed.length > 0 && (!event.eventName || !allowed.includes(event.eventName))) {
|
|
59615
|
-
const deliveryId3 = String(event.deliveryId ?? "").trim().slice(0, 200) ||
|
|
59996
|
+
const deliveryId3 = String(event.deliveryId ?? "").trim().slice(0, 200) || randomUUID22();
|
|
59616
59997
|
this.appendAttempt(trigger, event, {
|
|
59617
59998
|
outcome: "ignored",
|
|
59618
59999
|
statusCode: 202,
|
|
@@ -59647,7 +60028,7 @@ var WebhookManager = class {
|
|
|
59647
60028
|
if (recent.length >= RATE_LIMIT) fail(429, "Webhook rate limit exceeded");
|
|
59648
60029
|
recent.push(now);
|
|
59649
60030
|
this.rate.set(trigger.endpointId, recent);
|
|
59650
|
-
const deliveryId2 = requestedDeliveryId ||
|
|
60031
|
+
const deliveryId2 = requestedDeliveryId || randomUUID22();
|
|
59651
60032
|
const run3 = this.options.enqueue({
|
|
59652
60033
|
webhookId: trigger.id,
|
|
59653
60034
|
webhookName: trigger.name,
|
|
@@ -59677,7 +60058,7 @@ var WebhookManager = class {
|
|
|
59677
60058
|
}
|
|
59678
60059
|
captureVerification(trigger, event) {
|
|
59679
60060
|
const receivedAt = this.now();
|
|
59680
|
-
const deliveryId2 = String(event.deliveryId ?? "").trim().slice(0, 200) ||
|
|
60061
|
+
const deliveryId2 = String(event.deliveryId ?? "").trim().slice(0, 200) || randomUUID22();
|
|
59681
60062
|
trigger.verificationPending = false;
|
|
59682
60063
|
trigger.verifiedAt = receivedAt;
|
|
59683
60064
|
trigger.lastReceivedAt = receivedAt;
|
|
@@ -59711,7 +60092,7 @@ var WebhookManager = class {
|
|
|
59711
60092
|
}
|
|
59712
60093
|
appendAttempt(trigger, event, details) {
|
|
59713
60094
|
const attempt = {
|
|
59714
|
-
id:
|
|
60095
|
+
id: randomUUID22(),
|
|
59715
60096
|
webhookId: trigger.id,
|
|
59716
60097
|
receivedAt: this.now(),
|
|
59717
60098
|
outcome: details.outcome,
|
|
@@ -59741,7 +60122,7 @@ var WebhookManager = class {
|
|
|
59741
60122
|
};
|
|
59742
60123
|
|
|
59743
60124
|
// server/skill-library.ts
|
|
59744
|
-
import { existsSync as
|
|
60125
|
+
import { existsSync as existsSync25, readFileSync as readFileSync38, readdirSync as readdirSync9 } from "node:fs";
|
|
59745
60126
|
import { basename as basename8, join as join56 } from "node:path";
|
|
59746
60127
|
var SAFE_ID = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
59747
60128
|
function strings(value) {
|
|
@@ -59773,14 +60154,14 @@ function parseSkillManifest(value, directory) {
|
|
|
59773
60154
|
function loadSkillDirectory(directory) {
|
|
59774
60155
|
const manifestPath2 = join56(directory, "manifest.json");
|
|
59775
60156
|
const skillPath = join56(directory, "SKILL.md");
|
|
59776
|
-
if (!
|
|
59777
|
-
const manifest = parseSkillManifest(JSON.parse(
|
|
59778
|
-
const instructions =
|
|
60157
|
+
if (!existsSync25(manifestPath2) || !existsSync25(skillPath)) return null;
|
|
60158
|
+
const manifest = parseSkillManifest(JSON.parse(readFileSync38(manifestPath2, "utf8")), directory);
|
|
60159
|
+
const instructions = readFileSync38(skillPath, "utf8").trim();
|
|
59779
60160
|
if (!instructions.startsWith("---")) throw new Error(`${skillPath} has no skill frontmatter`);
|
|
59780
60161
|
return { manifest, instructions, directory };
|
|
59781
60162
|
}
|
|
59782
60163
|
function loadBundledSkills(root = process.env.OMB_SKILLS_DIR || join56(process.cwd(), "skills")) {
|
|
59783
|
-
if (!
|
|
60164
|
+
if (!existsSync25(root)) return [];
|
|
59784
60165
|
const skills = [];
|
|
59785
60166
|
for (const name2 of readdirSync9(root).sort()) {
|
|
59786
60167
|
const directory = join56(root, name2);
|
|
@@ -59790,7 +60171,7 @@ function loadBundledSkills(root = process.env.OMB_SKILLS_DIR || join56(process.c
|
|
|
59790
60171
|
return skills;
|
|
59791
60172
|
}
|
|
59792
60173
|
function loadUserSkills(root) {
|
|
59793
|
-
if (!
|
|
60174
|
+
if (!existsSync25(root)) return [];
|
|
59794
60175
|
let names;
|
|
59795
60176
|
try {
|
|
59796
60177
|
names = readdirSync9(root).sort();
|
|
@@ -60606,8 +60987,8 @@ function createGracefulShutdown({
|
|
|
60606
60987
|
|
|
60607
60988
|
// electron/data-dir-lease.mjs
|
|
60608
60989
|
import { execFileSync } from "node:child_process";
|
|
60609
|
-
import { createHash as createHash25, randomUUID as
|
|
60610
|
-
import { existsSync as
|
|
60990
|
+
import { createHash as createHash25, randomUUID as randomUUID23 } from "node:crypto";
|
|
60991
|
+
import { existsSync as existsSync26, linkSync as linkSync4, mkdirSync as mkdirSync27, readFileSync as readFileSync39, renameSync as renameSync6, unlinkSync as unlinkSync10, writeFileSync as writeFileSync16 } from "node:fs";
|
|
60611
60992
|
import { hostname as hostname4, uptime } from "node:os";
|
|
60612
60993
|
import { join as join57 } from "node:path";
|
|
60613
60994
|
var LEASE_NAME = "openmausbot-server.lease";
|
|
@@ -60630,7 +61011,7 @@ var cachedBootSession;
|
|
|
60630
61011
|
function readBootSession() {
|
|
60631
61012
|
try {
|
|
60632
61013
|
if (process.platform === "linux") {
|
|
60633
|
-
return normalizeBootId(
|
|
61014
|
+
return normalizeBootId(readFileSync39("/proc/sys/kernel/random/boot_id", "utf8"));
|
|
60634
61015
|
}
|
|
60635
61016
|
if (process.platform === "darwin") {
|
|
60636
61017
|
return normalizeBootId(execFileSync("/usr/sbin/sysctl", ["-n", "kern.bootsessionuuid"], {
|
|
@@ -60677,7 +61058,7 @@ function isReaperOwner(value, targetToken) {
|
|
|
60677
61058
|
function parseRecord(path, invalidMessage, validate) {
|
|
60678
61059
|
let raw;
|
|
60679
61060
|
try {
|
|
60680
|
-
raw =
|
|
61061
|
+
raw = readFileSync39(path, "utf8");
|
|
60681
61062
|
} catch (error51) {
|
|
60682
61063
|
if (error51?.code === "ENOENT") return null;
|
|
60683
61064
|
throw leaseError("OpenMausBot cannot read the data-directory lease; refusing to start to protect its state.", error51);
|
|
@@ -60753,7 +61134,7 @@ function claimReaperAuthority2(leasePath, expected) {
|
|
|
60753
61134
|
version: 1,
|
|
60754
61135
|
pid: process.pid,
|
|
60755
61136
|
host: hostname4(),
|
|
60756
|
-
token:
|
|
61137
|
+
token: randomUUID23(),
|
|
60757
61138
|
createdAt: Date.now(),
|
|
60758
61139
|
boot: bootSession(),
|
|
60759
61140
|
uptime: uptimeMs(),
|
|
@@ -60800,7 +61181,7 @@ function prepareDataDir(dataDir, legacyDataDir) {
|
|
|
60800
61181
|
validateDataDir(dataDir);
|
|
60801
61182
|
if (legacyDataDir !== void 0) {
|
|
60802
61183
|
validateDataDir(legacyDataDir);
|
|
60803
|
-
if (!
|
|
61184
|
+
if (!existsSync26(dataDir) && existsSync26(legacyDataDir)) {
|
|
60804
61185
|
try {
|
|
60805
61186
|
renameSync6(legacyDataDir, dataDir);
|
|
60806
61187
|
} catch {
|
|
@@ -60885,7 +61266,7 @@ function acquireDataDirLeaseInternal(dataDir, options = {}) {
|
|
|
60885
61266
|
version: 1,
|
|
60886
61267
|
pid: process.pid,
|
|
60887
61268
|
host: hostname4(),
|
|
60888
|
-
token:
|
|
61269
|
+
token: randomUUID23(),
|
|
60889
61270
|
createdAt: Date.now(),
|
|
60890
61271
|
boot: bootSession(),
|
|
60891
61272
|
uptime: uptimeMs()
|
|
@@ -61000,8 +61381,8 @@ var WorkspaceBackupMaintenance = class {
|
|
|
61000
61381
|
};
|
|
61001
61382
|
|
|
61002
61383
|
// server/workspace-backup-http.ts
|
|
61003
|
-
import { randomUUID as
|
|
61004
|
-
import { createReadStream as createReadStream4, createWriteStream as createWriteStream3, existsSync as
|
|
61384
|
+
import { randomUUID as randomUUID25 } from "node:crypto";
|
|
61385
|
+
import { createReadStream as createReadStream4, createWriteStream as createWriteStream3, existsSync as existsSync28, lstatSync as lstatSync8, mkdirSync as mkdirSync29, readdirSync as readdirSync11, statSync as statSync10, unlinkSync as unlinkSync11 } from "node:fs";
|
|
61005
61386
|
import { join as join59 } from "node:path";
|
|
61006
61387
|
import { Transform as Transform2 } from "node:stream";
|
|
61007
61388
|
import { pipeline as pipeline3 } from "node:stream/promises";
|
|
@@ -61022,7 +61403,7 @@ var WORKSPACE_BACKUP_CLIENT_KEYS = [
|
|
|
61022
61403
|
];
|
|
61023
61404
|
|
|
61024
61405
|
// server/workspace-backup.ts
|
|
61025
|
-
import { createCipheriv, createDecipheriv, createHash as createHash26, randomBytes as randomBytes6, randomUUID as
|
|
61406
|
+
import { createCipheriv, createDecipheriv, createHash as createHash26, randomBytes as randomBytes6, randomUUID as randomUUID24, scrypt } from "node:crypto";
|
|
61026
61407
|
import {
|
|
61027
61408
|
chmodSync as chmodSync6,
|
|
61028
61409
|
closeSync as closeSync11,
|
|
@@ -61030,13 +61411,13 @@ import {
|
|
|
61030
61411
|
cpSync,
|
|
61031
61412
|
createReadStream as createReadStream3,
|
|
61032
61413
|
createWriteStream as createWriteStream2,
|
|
61033
|
-
existsSync as
|
|
61414
|
+
existsSync as existsSync27,
|
|
61034
61415
|
fstatSync as fstatSync6,
|
|
61035
61416
|
fsyncSync as fsyncSync3,
|
|
61036
|
-
lstatSync as
|
|
61417
|
+
lstatSync as lstatSync7,
|
|
61037
61418
|
mkdirSync as mkdirSync28,
|
|
61038
61419
|
openSync as openSync11,
|
|
61039
|
-
readFileSync as
|
|
61420
|
+
readFileSync as readFileSync40,
|
|
61040
61421
|
readdirSync as readdirSync10,
|
|
61041
61422
|
readSync as readSync6,
|
|
61042
61423
|
realpathSync as realpathSync10,
|
|
@@ -64068,6 +64449,7 @@ var EXCLUDED = /* @__PURE__ */ new Set([
|
|
|
64068
64449
|
"environment-id",
|
|
64069
64450
|
"sessions.json",
|
|
64070
64451
|
"tunnel-account.json",
|
|
64452
|
+
"team-computers.json",
|
|
64071
64453
|
"openmausbot-server.lease",
|
|
64072
64454
|
"box-create-requests.lock",
|
|
64073
64455
|
"messages.db-wal",
|
|
@@ -64092,32 +64474,32 @@ function preserved(name2) {
|
|
|
64092
64474
|
const folded = name2.toLowerCase();
|
|
64093
64475
|
return excluded(folded) && folded !== "messages.db-wal" && folded !== "messages.db-shm";
|
|
64094
64476
|
}
|
|
64095
|
-
function
|
|
64477
|
+
function folder2(path) {
|
|
64096
64478
|
mkdirSync28(path, { recursive: true, mode: 448 });
|
|
64097
|
-
const stat6 =
|
|
64479
|
+
const stat6 = lstatSync7(path);
|
|
64098
64480
|
if (!stat6.isDirectory() || stat6.isSymbolicLink()) throw new Error("The backup directory must be a real directory, not a symbolic link.");
|
|
64099
64481
|
chmodSync6(path, 448);
|
|
64100
64482
|
}
|
|
64101
64483
|
function backupRoot(dataDir) {
|
|
64102
64484
|
const root = resolve15(dataDir);
|
|
64103
64485
|
if (root === parse6(root).root) throw new Error("A filesystem root cannot be a workspace backup target.");
|
|
64104
|
-
const stat6 =
|
|
64486
|
+
const stat6 = lstatSync7(root);
|
|
64105
64487
|
if (!stat6.isDirectory() || stat6.isSymbolicLink()) throw new Error("The workspace data directory must not be a symbolic link.");
|
|
64106
64488
|
const path = join58(root, ".backups");
|
|
64107
|
-
|
|
64489
|
+
folder2(path);
|
|
64108
64490
|
return path;
|
|
64109
64491
|
}
|
|
64110
64492
|
function jobPath(dataDir, id) {
|
|
64111
64493
|
if (!ID.test(id)) throw new Error("Invalid workspace backup identifier.");
|
|
64112
64494
|
const path = join58(backupRoot(dataDir), id);
|
|
64113
64495
|
if (entryExists(path)) {
|
|
64114
|
-
const stat6 =
|
|
64496
|
+
const stat6 = lstatSync7(path);
|
|
64115
64497
|
if (!stat6.isDirectory() || stat6.isSymbolicLink()) throw new Error("Unsafe workspace backup job directory.");
|
|
64116
64498
|
}
|
|
64117
64499
|
return path;
|
|
64118
64500
|
}
|
|
64119
64501
|
function newJob(dataDir) {
|
|
64120
|
-
const id =
|
|
64502
|
+
const id = randomUUID24();
|
|
64121
64503
|
const directory = jobPath(dataDir, id);
|
|
64122
64504
|
mkdirSync28(directory, { mode: 448 });
|
|
64123
64505
|
return { id, directory };
|
|
@@ -64130,7 +64512,7 @@ function record2(value) {
|
|
|
64130
64512
|
}
|
|
64131
64513
|
function entryExists(path) {
|
|
64132
64514
|
try {
|
|
64133
|
-
|
|
64515
|
+
lstatSync7(path);
|
|
64134
64516
|
return true;
|
|
64135
64517
|
} catch (error51) {
|
|
64136
64518
|
if (error51.code === "ENOENT") return false;
|
|
@@ -64142,7 +64524,7 @@ function privateJson(path) {
|
|
|
64142
64524
|
try {
|
|
64143
64525
|
const stat6 = fstatSync6(fd);
|
|
64144
64526
|
if (!stat6.isFile() || stat6.size > MAX_METADATA_BYTES) throw new Error("Workspace backup metadata is too large or is not a regular file.");
|
|
64145
|
-
return JSON.parse(
|
|
64527
|
+
return JSON.parse(readFileSync40(fd, "utf8"));
|
|
64146
64528
|
} finally {
|
|
64147
64529
|
closeSync11(fd);
|
|
64148
64530
|
}
|
|
@@ -64157,7 +64539,7 @@ function privateMetadata(manifest) {
|
|
|
64157
64539
|
}
|
|
64158
64540
|
function assertLocalAuthOutsideSnapshot(dataDir) {
|
|
64159
64541
|
const configPath = join58(dataDir, "config.json");
|
|
64160
|
-
const config2 =
|
|
64542
|
+
const config2 = existsSync27(configPath) ? privateJson(configPath) : {};
|
|
64161
64543
|
const instances = record2(config2) && record2(config2.instances) ? config2.instances : {};
|
|
64162
64544
|
const fields = ["HOME", "USERPROFILE", "CLAUDE_CONFIG_DIR", "CODEX_HOME", "GROK_HOME", "KIMI_CODE_HOME", "HERMES_HOME", "FACTORY_HOME_OVERRIDE", "XDG_CONFIG_HOME", "XDG_DATA_HOME", "LOCALAPPDATA"];
|
|
64163
64545
|
const check2 = (value, field, home) => {
|
|
@@ -64214,7 +64596,7 @@ function copyRegular(source, destination) {
|
|
|
64214
64596
|
while (offset < count) offset += writeSync(output, chunk, offset, count - offset);
|
|
64215
64597
|
}
|
|
64216
64598
|
const after = fstatSync6(input);
|
|
64217
|
-
const current2 =
|
|
64599
|
+
const current2 = lstatSync7(source);
|
|
64218
64600
|
if (size !== before.size || after.mtimeMs !== before.mtimeMs || after.ctimeMs !== before.ctimeMs || current2.ino !== before.ino || current2.dev !== before.dev || current2.isSymbolicLink()) {
|
|
64219
64601
|
throw new Error("A workspace file changed during backup. Stop its writer and retry.");
|
|
64220
64602
|
}
|
|
@@ -64241,7 +64623,7 @@ function hashFile(path) {
|
|
|
64241
64623
|
}
|
|
64242
64624
|
}
|
|
64243
64625
|
function countJsonArray(path) {
|
|
64244
|
-
if (!
|
|
64626
|
+
if (!existsSync27(path)) return 0;
|
|
64245
64627
|
const value = privateJson(path);
|
|
64246
64628
|
if (!Array.isArray(value)) throw new Error("A workspace roster is not a valid JSON array.");
|
|
64247
64629
|
const ids = /* @__PURE__ */ new Set();
|
|
@@ -64254,7 +64636,7 @@ function countJsonArray(path) {
|
|
|
64254
64636
|
return value.length;
|
|
64255
64637
|
}
|
|
64256
64638
|
function databaseCounts(path) {
|
|
64257
|
-
if (!
|
|
64639
|
+
if (!existsSync27(path)) return { threads: 0, messages: 0 };
|
|
64258
64640
|
const db2 = new DatabaseSync2(path, { readOnly: true });
|
|
64259
64641
|
try {
|
|
64260
64642
|
const check2 = db2.prepare("PRAGMA quick_check").all();
|
|
@@ -64274,7 +64656,7 @@ async function createWorkspaceBackup(dataDir, options) {
|
|
|
64274
64656
|
const key3 = await passwordKey(options.password, salt);
|
|
64275
64657
|
const job = newJob(dataDir);
|
|
64276
64658
|
const snapshot2 = join58(job.directory, "snapshot");
|
|
64277
|
-
|
|
64659
|
+
folder2(join58(snapshot2, "data"));
|
|
64278
64660
|
const entries = [];
|
|
64279
64661
|
const warnings = ["Stop external editors and managed desktops before exporting; files written outside this server cannot be frozen by the backup gate.", "Conversation text and user files are not redacted and may contain secrets you pasted. Keep the encrypted backup private.", RESTORE_WARNING];
|
|
64280
64662
|
let bytes = 0;
|
|
@@ -64282,8 +64664,8 @@ async function createWorkspaceBackup(dataDir, options) {
|
|
|
64282
64664
|
try {
|
|
64283
64665
|
const root = realpathSync10(dataDir);
|
|
64284
64666
|
const sourceDb = join58(root, "messages.db");
|
|
64285
|
-
if (
|
|
64286
|
-
if (
|
|
64667
|
+
if (existsSync27(sourceDb)) {
|
|
64668
|
+
if (lstatSync7(sourceDb).isSymbolicLink()) throw new Error("The message database must not be a symbolic link.");
|
|
64287
64669
|
const db2 = new DatabaseSync2(sourceDb, { readOnly: true });
|
|
64288
64670
|
const completionPulse = setInterval(() => {
|
|
64289
64671
|
}, 25);
|
|
@@ -64308,7 +64690,7 @@ async function createWorkspaceBackup(dataDir, options) {
|
|
|
64308
64690
|
if (forbiddenArchivePath(path2)) throw new Error("A workspace filename conflicts with a protected authentication or runtime path.");
|
|
64309
64691
|
if (!validRelative(path2)) throw new Error("A workspace filename cannot be safely restored on supported platforms.");
|
|
64310
64692
|
const source = join58(directory, name2);
|
|
64311
|
-
const stat6 =
|
|
64693
|
+
const stat6 = lstatSync7(source);
|
|
64312
64694
|
if (stat6.isSymbolicLink()) {
|
|
64313
64695
|
let target;
|
|
64314
64696
|
try {
|
|
@@ -64329,7 +64711,7 @@ async function createWorkspaceBackup(dataDir, options) {
|
|
|
64329
64711
|
if (entries.length >= MAX_WORKSPACE_BACKUP_FILES) throw new Error("The workspace exceeds the 100,000-entry backup limit.");
|
|
64330
64712
|
const destination = join58(snapshot2, "data", path2);
|
|
64331
64713
|
if (stat6.isDirectory()) {
|
|
64332
|
-
|
|
64714
|
+
folder2(destination);
|
|
64333
64715
|
entries.push({ path: path2, type: "directory", size: 0, mode: 448 });
|
|
64334
64716
|
walk2(source, path2);
|
|
64335
64717
|
} else if (stat6.isFile()) {
|
|
@@ -64344,7 +64726,7 @@ async function createWorkspaceBackup(dataDir, options) {
|
|
|
64344
64726
|
}
|
|
64345
64727
|
writeJson(destination, value);
|
|
64346
64728
|
}
|
|
64347
|
-
copied = { size:
|
|
64729
|
+
copied = { size: lstatSync7(destination).size, mode: 384, sha256: hashFile(destination) };
|
|
64348
64730
|
} else copied = copyRegular(path2 === "messages.db" ? join58(snapshot2, "messages-snapshot.db") : source, destination);
|
|
64349
64731
|
bytes += copied.size;
|
|
64350
64732
|
if (bytes > MAX_WORKSPACE_BACKUP_BYTES) throw new Error("The workspace exceeds the 10 GB backup limit.");
|
|
@@ -64499,7 +64881,7 @@ function validateStaged(directory, expected) {
|
|
|
64499
64881
|
for (const name2 of readdirSync10(root)) {
|
|
64500
64882
|
const path = prefix ? `${prefix}/${name2}` : name2;
|
|
64501
64883
|
if (!declared.has(path)) throw new Error("Staged workspace contains files absent from its manifest.");
|
|
64502
|
-
const stat6 =
|
|
64884
|
+
const stat6 = lstatSync7(join58(root, name2));
|
|
64503
64885
|
if (stat6.isSymbolicLink()) throw new Error("Staged workspace contains a symbolic link.");
|
|
64504
64886
|
if (stat6.isDirectory()) inspect(join58(root, name2), path);
|
|
64505
64887
|
}
|
|
@@ -64507,7 +64889,7 @@ function validateStaged(directory, expected) {
|
|
|
64507
64889
|
inspect(join58(directory, "data"));
|
|
64508
64890
|
for (const entry of manifest.entries) {
|
|
64509
64891
|
const path = join58(directory, "data", entry.path);
|
|
64510
|
-
const stat6 =
|
|
64892
|
+
const stat6 = lstatSync7(path);
|
|
64511
64893
|
if (stat6.isSymbolicLink() || (entry.type === "file" ? !stat6.isFile() || stat6.size !== entry.size : !stat6.isDirectory())) {
|
|
64512
64894
|
throw new Error("Staged workspace files do not match the backup manifest.");
|
|
64513
64895
|
}
|
|
@@ -64520,11 +64902,11 @@ function validateStaged(directory, expected) {
|
|
|
64520
64902
|
}
|
|
64521
64903
|
const data = join58(directory, "data");
|
|
64522
64904
|
if (countJsonArray(join58(data, "bots.json")) !== manifest.summary.bots || countJsonArray(join58(data, "groups.json")) !== manifest.summary.groups) throw new Error("The backup roster does not match its summary.");
|
|
64523
|
-
if (
|
|
64905
|
+
if (existsSync27(join58(data, "config.json"))) {
|
|
64524
64906
|
const config2 = privateJson(join58(data, "config.json"));
|
|
64525
64907
|
if (JSON.stringify(config2) !== JSON.stringify(portableWorkspaceConfig(config2))) throw new Error("Workspace backup contains non-portable connection settings.");
|
|
64526
64908
|
}
|
|
64527
|
-
if (
|
|
64909
|
+
if (existsSync27(join58(data, "webhooks.json"))) {
|
|
64528
64910
|
const value = privateJson(join58(data, "webhooks.json"));
|
|
64529
64911
|
if (!record2(value) || Array.isArray(value.webhooks) && value.webhooks.some((webhook) => record2(webhook) && Object.hasOwn(webhook, "secretHash"))) throw new Error("Workspace backup contains webhook credentials.");
|
|
64530
64912
|
}
|
|
@@ -64539,7 +64921,7 @@ async function stageWorkspaceBackup(dataDir, archivePath, options) {
|
|
|
64539
64921
|
await decryptArchive(archivePath, plaintext, options.password);
|
|
64540
64922
|
const expected = await inspectTar(plaintext);
|
|
64541
64923
|
const staged = join58(job.directory, "staged");
|
|
64542
|
-
|
|
64924
|
+
folder2(staged);
|
|
64543
64925
|
await So({ file: plaintext, cwd: staged, strict: true, preservePaths: false, umask: 63, noChmod: true });
|
|
64544
64926
|
const manifest = validateStaged(staged, expected);
|
|
64545
64927
|
const versions = [manifest.summary.appVersion, options.currentAppVersion ?? ""].map((version2) => /^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/.exec(version2)?.slice(1).map(Number));
|
|
@@ -64566,8 +64948,8 @@ function readStagedWorkspaceBackup(dataDir, id) {
|
|
|
64566
64948
|
}
|
|
64567
64949
|
function commitPendingWorkspaceRestore(dataDir, id) {
|
|
64568
64950
|
const root = backupRoot(dataDir);
|
|
64569
|
-
if (
|
|
64570
|
-
if (
|
|
64951
|
+
if (existsSync27(join58(root, "pending-restore.json"))) throw new Error("A workspace restore is already waiting for restart.");
|
|
64952
|
+
if (existsSync27(join58(root, `safety-${id}`))) throw new Error("This restore was already attempted. Preview the backup again to create a fresh restore attempt.");
|
|
64571
64953
|
const metadata = readStagedWorkspaceBackup(dataDir, id);
|
|
64572
64954
|
const manifest = validateStaged(join58(jobPath(dataDir, id), "staged"));
|
|
64573
64955
|
prepareRestore(dataDir, id, manifest);
|
|
@@ -64577,7 +64959,7 @@ function commitPendingWorkspaceRestore(dataDir, id) {
|
|
|
64577
64959
|
function readPendingWorkspaceRestoreMetadata(dataDir) {
|
|
64578
64960
|
const root = backupRoot(dataDir);
|
|
64579
64961
|
const path = join58(root, "pending-restore.json");
|
|
64580
|
-
if (!
|
|
64962
|
+
if (!existsSync27(path)) return null;
|
|
64581
64963
|
const pending = privateJson(path);
|
|
64582
64964
|
if (!record2(pending) || typeof pending.id !== "string") throw new Error("Invalid pending workspace restore.");
|
|
64583
64965
|
return readStagedWorkspaceBackup(dataDir, pending.id);
|
|
@@ -64609,8 +64991,8 @@ function rollback(dataDir, journal) {
|
|
|
64609
64991
|
const root = backupRoot(dataDir);
|
|
64610
64992
|
const previousReceipt = join58(root, `safety-${journal.id}`, "previous-restore.json");
|
|
64611
64993
|
const last = join58(root, "last-restore.json");
|
|
64612
|
-
if (
|
|
64613
|
-
else if (
|
|
64994
|
+
if (existsSync27(previousReceipt)) writeJson(last, privateJson(previousReceipt));
|
|
64995
|
+
else if (existsSync27(last)) {
|
|
64614
64996
|
const value = privateJson(last);
|
|
64615
64997
|
if (record2(value) && value.id === journal.id) rmSync7(last);
|
|
64616
64998
|
}
|
|
@@ -64661,16 +65043,16 @@ function prepareRestore(dataDir, id, manifest) {
|
|
|
64661
65043
|
assertLocalAuthOutsideSnapshot(dataDir);
|
|
64662
65044
|
const job = jobPath(dataDir, id);
|
|
64663
65045
|
const prepared = join58(job, "apply", "data");
|
|
64664
|
-
if (
|
|
64665
|
-
|
|
64666
|
-
for (const entry of manifest.entries.filter((entry2) => entry2.type === "directory"))
|
|
65046
|
+
if (existsSync27(join58(job, "apply"))) rmSync7(join58(job, "apply"), { recursive: true, force: true });
|
|
65047
|
+
folder2(prepared);
|
|
65048
|
+
for (const entry of manifest.entries.filter((entry2) => entry2.type === "directory")) folder2(join58(prepared, entry.path));
|
|
64667
65049
|
for (const entry of manifest.entries) {
|
|
64668
65050
|
const destination = join58(prepared, entry.path);
|
|
64669
65051
|
if (entry.type === "file") copyRegular(join58(job, "staged", "data", entry.path), destination);
|
|
64670
65052
|
}
|
|
64671
65053
|
const changeJson = (name2, change) => {
|
|
64672
65054
|
const path = join58(prepared, name2);
|
|
64673
|
-
if (!
|
|
65055
|
+
if (!existsSync27(path)) return;
|
|
64674
65056
|
const value = privateJson(path);
|
|
64675
65057
|
change(value);
|
|
64676
65058
|
writeJson(path, value);
|
|
@@ -64680,22 +65062,22 @@ function prepareRestore(dataDir, id, manifest) {
|
|
|
64680
65062
|
}
|
|
64681
65063
|
const oldConfig = join58(dataDir, "config.json");
|
|
64682
65064
|
const newConfig = join58(prepared, "config.json");
|
|
64683
|
-
if (
|
|
64684
|
-
writeJson(newConfig, restoredWorkspaceConfig(
|
|
65065
|
+
if (existsSync27(oldConfig) || existsSync27(newConfig)) {
|
|
65066
|
+
writeJson(newConfig, restoredWorkspaceConfig(existsSync27(newConfig) ? privateJson(newConfig) : {}, existsSync27(oldConfig) ? privateJson(oldConfig) : {}));
|
|
64685
65067
|
}
|
|
64686
65068
|
const browserHomes = ["vm-home"];
|
|
64687
65069
|
const vmHomes = join58(dataDir, "vm-homes");
|
|
64688
65070
|
if (entryExists(vmHomes)) {
|
|
64689
|
-
if (!
|
|
65071
|
+
if (!lstatSync7(vmHomes).isDirectory()) throw new Error("Stop managed desktops and repair the VM home directory before restoring.");
|
|
64690
65072
|
for (const name2 of readdirSync10(vmHomes)) if (validRelative(name2)) browserHomes.push(`vm-homes/${name2}`);
|
|
64691
65073
|
}
|
|
64692
65074
|
for (const home of browserHomes) {
|
|
64693
65075
|
const homePath = join58(dataDir, home);
|
|
64694
65076
|
if (!entryExists(homePath)) continue;
|
|
64695
|
-
if (!
|
|
65077
|
+
if (!lstatSync7(homePath).isDirectory()) throw new Error("Stop managed desktops and repair the VM home directory before restoring.");
|
|
64696
65078
|
const source = join58(dataDir, home, ".browser-profiles");
|
|
64697
65079
|
if (!entryExists(source)) continue;
|
|
64698
|
-
|
|
65080
|
+
folder2(join58(prepared, home));
|
|
64699
65081
|
cpSync(source, join58(prepared, home, ".browser-profiles"), { recursive: true, dereference: false, verbatimSymlinks: true, errorOnExist: true, force: false });
|
|
64700
65082
|
}
|
|
64701
65083
|
changeJson("routines.json", (value) => {
|
|
@@ -64714,7 +65096,7 @@ function prepareRestore(dataDir, id, manifest) {
|
|
|
64714
65096
|
});
|
|
64715
65097
|
changeJson("webhooks.json", (value) => {
|
|
64716
65098
|
if (!record2(value)) throw new Error("Invalid webhook definitions in workspace backup.");
|
|
64717
|
-
const destination =
|
|
65099
|
+
const destination = existsSync27(join58(dataDir, "webhooks.json")) ? privateJson(join58(dataDir, "webhooks.json")) : {};
|
|
64718
65100
|
const oldHooks = record2(destination) && Array.isArray(destination.webhooks) ? destination.webhooks : [];
|
|
64719
65101
|
if (Array.isArray(value.webhooks)) {
|
|
64720
65102
|
for (const webhook of value.webhooks) if (record2(webhook)) {
|
|
@@ -64731,8 +65113,8 @@ function prepareRestore(dataDir, id, manifest) {
|
|
|
64731
65113
|
for (const call of value.calls) if (record2(call)) call.nextRunAt = null;
|
|
64732
65114
|
}
|
|
64733
65115
|
});
|
|
64734
|
-
if (
|
|
64735
|
-
if (
|
|
65116
|
+
if (existsSync27(join58(prepared, "delegations.json"))) writeJson(join58(prepared, "delegations.json"), {});
|
|
65117
|
+
if (existsSync27(join58(prepared, "browser-cleanups.json"))) writeJson(join58(prepared, "browser-cleanups.json"), []);
|
|
64736
65118
|
for (const entry of manifest.entries) {
|
|
64737
65119
|
if (/^messages-[^/]+\.json$/.test(entry.path)) changeJson(entry.path, (value) => {
|
|
64738
65120
|
if (Array.isArray(value)) for (const message3 of value) rebaseMessage(message3, manifest.sourceDataDir, resolve15(dataDir));
|
|
@@ -64740,7 +65122,7 @@ function prepareRestore(dataDir, id, manifest) {
|
|
|
64740
65122
|
});
|
|
64741
65123
|
}
|
|
64742
65124
|
const dbPath = join58(prepared, "messages.db");
|
|
64743
|
-
if (
|
|
65125
|
+
if (existsSync27(dbPath)) {
|
|
64744
65126
|
const db2 = new DatabaseSync2(dbPath);
|
|
64745
65127
|
try {
|
|
64746
65128
|
db2.exec("BEGIN");
|
|
@@ -64772,7 +65154,7 @@ function prepareRestore(dataDir, id, manifest) {
|
|
|
64772
65154
|
function readLastWorkspaceRestore(dataDir) {
|
|
64773
65155
|
const root = backupRoot(dataDir);
|
|
64774
65156
|
const path = join58(root, "last-restore.json");
|
|
64775
|
-
if (!
|
|
65157
|
+
if (!existsSync27(path)) return null;
|
|
64776
65158
|
const raw = privateJson(path);
|
|
64777
65159
|
if (!record2(raw) || raw.restored !== true || typeof raw.id !== "string" || !ID.test(raw.id)) throw new Error("Invalid workspace restore receipt.");
|
|
64778
65160
|
return raw;
|
|
@@ -64799,7 +65181,7 @@ function applyPendingWorkspaceRestore(dataDir) {
|
|
|
64799
65181
|
const root = backupRoot(dataDir);
|
|
64800
65182
|
const journalFile2 = join58(root, "restore-journal.json");
|
|
64801
65183
|
const pendingFile = join58(root, "pending-restore.json");
|
|
64802
|
-
if (
|
|
65184
|
+
if (existsSync27(journalFile2)) {
|
|
64803
65185
|
const previous2 = readJournal(journalFile2);
|
|
64804
65186
|
if (previous2.phase === "applying") {
|
|
64805
65187
|
rollback(dataDir, previous2);
|
|
@@ -64807,7 +65189,7 @@ function applyPendingWorkspaceRestore(dataDir) {
|
|
|
64807
65189
|
rmSync7(pendingFile, { force: true });
|
|
64808
65190
|
return { restored: false, rolledBack: true, id: previous2.id, safetyCopyPath: join58(root, `safety-${previous2.id}`) };
|
|
64809
65191
|
}
|
|
64810
|
-
if (previous2.phase === "committed" &&
|
|
65192
|
+
if (previous2.phase === "committed" && existsSync27(pendingFile)) {
|
|
64811
65193
|
const pending2 = privateJson(pendingFile);
|
|
64812
65194
|
if (record2(pending2) && pending2.id === previous2.id) {
|
|
64813
65195
|
return finishRestore(dataDir, previous2.id);
|
|
@@ -64820,8 +65202,8 @@ function applyPendingWorkspaceRestore(dataDir) {
|
|
|
64820
65202
|
const manifest = validateStaged(staged);
|
|
64821
65203
|
const prepared = prepareRestore(dataDir, pending.id, manifest);
|
|
64822
65204
|
const safetyCopyPath = join58(root, `safety-${pending.id}`);
|
|
64823
|
-
if (
|
|
64824
|
-
|
|
65205
|
+
if (existsSync27(safetyCopyPath)) throw new Error("A safety copy already exists for this restore. Original files have not been changed.");
|
|
65206
|
+
folder2(join58(safetyCopyPath, "data"));
|
|
64825
65207
|
const journal = {
|
|
64826
65208
|
id: pending.id,
|
|
64827
65209
|
phase: "applying",
|
|
@@ -64832,7 +65214,7 @@ function applyPendingWorkspaceRestore(dataDir) {
|
|
|
64832
65214
|
if (!validRelative(name2) || name2.includes("/") || preserved(name2)) throw new Error("Unsafe workspace restore target.");
|
|
64833
65215
|
}
|
|
64834
65216
|
writeJson(join58(safetyCopyPath, "manifest.json"), { id: pending.id, createdAt: (/* @__PURE__ */ new Date()).toISOString(), originalEntries: journal.existing });
|
|
64835
|
-
if (
|
|
65217
|
+
if (existsSync27(join58(root, "last-restore.json"))) writeJson(join58(safetyCopyPath, "previous-restore.json"), privateJson(join58(root, "last-restore.json")));
|
|
64836
65218
|
writeJson(journalFile2, journal);
|
|
64837
65219
|
try {
|
|
64838
65220
|
for (const name2 of journal.existing) renameSync7(join58(dataDir, name2), join58(safetyCopyPath, "data", name2));
|
|
@@ -64851,13 +65233,13 @@ function removeWorkspaceBackupJob(dataDir, id) {
|
|
|
64851
65233
|
const path = jobPath(dataDir, id);
|
|
64852
65234
|
const root = backupRoot(dataDir);
|
|
64853
65235
|
for (const name2 of ["pending-restore.json", "restore-journal.json", "last-restore.json"]) {
|
|
64854
|
-
if (!
|
|
65236
|
+
if (!existsSync27(join58(root, name2))) continue;
|
|
64855
65237
|
const value = privateJson(join58(root, name2));
|
|
64856
65238
|
if (record2(value) && value.id === id) throw new Error("This backup is still needed for restore or recovery and cannot be removed.");
|
|
64857
65239
|
}
|
|
64858
|
-
if (
|
|
65240
|
+
if (existsSync27(join58(root, `safety-${id}`))) throw new Error("This backup has a recovery copy and cannot be removed.");
|
|
64859
65241
|
if (!entryExists(path)) return;
|
|
64860
|
-
if (!
|
|
65242
|
+
if (!lstatSync7(path).isDirectory() || lstatSync7(path).isSymbolicLink()) throw new Error("Unsafe workspace backup cleanup target.");
|
|
64861
65243
|
rmSync7(path, { recursive: true, force: true });
|
|
64862
65244
|
}
|
|
64863
65245
|
|
|
@@ -64867,7 +65249,7 @@ var MAX_UPLOAD_BYTES = MAX_WORKSPACE_BACKUP_UPLOAD_BYTES;
|
|
|
64867
65249
|
var MAX_CLIENT_STATE_BYTES = 2 * 1024 ** 2;
|
|
64868
65250
|
var EXPIRES_MS = 60 * 6e4;
|
|
64869
65251
|
var passwordSchema = external_exports.string().min(12, "Use a password of at least 12 characters.").max(1024);
|
|
64870
|
-
var
|
|
65252
|
+
var failure3 = (message3, status = 400) => Object.assign(new Error(message3), { status });
|
|
64871
65253
|
function isWorkspaceBackupSessionControl(method, path) {
|
|
64872
65254
|
return method === "GET" && path === "/api/auth/sessions" || method === "POST" && path === "/api/auth/logout" || method === "DELETE" && /^\/api\/auth\/sessions\/[\w-]+$/.test(path);
|
|
64873
65255
|
}
|
|
@@ -64880,7 +65262,7 @@ function createWorkspaceBackupRoutes(options) {
|
|
|
64880
65262
|
};
|
|
64881
65263
|
const owner2 = (auth) => auth.kind === "session" ? auth.session.id : "local-owner";
|
|
64882
65264
|
const check2 = (req, auth) => {
|
|
64883
|
-
if (!options.authorized(req, auth)) throw
|
|
65265
|
+
if (!options.authorized(req, auth)) throw failure3("Your session changed. Reopen Backups and try again.", 403);
|
|
64884
65266
|
};
|
|
64885
65267
|
function remove(id, artifact) {
|
|
64886
65268
|
if (artifact.path) {
|
|
@@ -64899,7 +65281,7 @@ function createWorkspaceBackupRoutes(options) {
|
|
|
64899
65281
|
}
|
|
64900
65282
|
function reclaimAbandoned() {
|
|
64901
65283
|
const root = join59(options.dataDir, ".backups");
|
|
64902
|
-
if (!
|
|
65284
|
+
if (!existsSync28(root) || lstatSync8(root).isSymbolicLink()) return;
|
|
64903
65285
|
for (const entry of readdirSync11(root, { withFileTypes: true })) {
|
|
64904
65286
|
if (!entry.isDirectory() || !/^[a-f0-9-]{36}$/.test(entry.name)) continue;
|
|
64905
65287
|
if (statSync10(join59(root, entry.name)).mtimeMs + EXPIRES_MS > Date.now()) continue;
|
|
@@ -64909,7 +65291,7 @@ function createWorkspaceBackupRoutes(options) {
|
|
|
64909
65291
|
}
|
|
64910
65292
|
}
|
|
64911
65293
|
const uploads = join59(root, "uploads");
|
|
64912
|
-
if (!
|
|
65294
|
+
if (!existsSync28(uploads) || lstatSync8(uploads).isSymbolicLink()) return;
|
|
64913
65295
|
for (const entry of readdirSync11(uploads, { withFileTypes: true })) {
|
|
64914
65296
|
if (!entry.isFile() || !/^[a-f0-9-]{36}\.ombbackup$/.test(entry.name)) continue;
|
|
64915
65297
|
const file2 = join59(uploads, entry.name);
|
|
@@ -64939,12 +65321,12 @@ function createWorkspaceBackupRoutes(options) {
|
|
|
64939
65321
|
expire();
|
|
64940
65322
|
const artifact = typeof id === "string" ? artifacts.get(id) : void 0;
|
|
64941
65323
|
if (!artifact || artifact.owner !== owner2(auth) || artifact.kind !== kind) {
|
|
64942
|
-
throw
|
|
65324
|
+
throw failure3("This backup has expired or belongs to another session. Select the file again.", 404);
|
|
64943
65325
|
}
|
|
64944
65326
|
return artifact;
|
|
64945
65327
|
};
|
|
64946
65328
|
async function operate(work) {
|
|
64947
|
-
if (operating) throw
|
|
65329
|
+
if (operating) throw failure3("Another backup operation is in progress. Wait for it to finish.", 409);
|
|
64948
65330
|
operating = true;
|
|
64949
65331
|
try {
|
|
64950
65332
|
return await work();
|
|
@@ -64969,18 +65351,18 @@ function createWorkspaceBackupRoutes(options) {
|
|
|
64969
65351
|
if (method === "POST" && path === `${PREFIX}/client-state`) {
|
|
64970
65352
|
const body = external_exports.object({ restoreId: external_exports.string().uuid() }).parse(await options.readBody(req));
|
|
64971
65353
|
check2(req, auth);
|
|
64972
|
-
if (!options.restored.restored || body.restoreId !== options.restored.id) throw
|
|
65354
|
+
if (!options.restored.restored || body.restoreId !== options.restored.id) throw failure3("This restore has not completed. Restart OpenMausBot first.", 409);
|
|
64973
65355
|
json4(res, 200, { clientState: options.restored.clientState ?? {} });
|
|
64974
65356
|
return true;
|
|
64975
65357
|
}
|
|
64976
65358
|
if (method === "POST" && path === `${PREFIX}/export`) {
|
|
64977
65359
|
const body = external_exports.object({ password: passwordSchema, clientState: external_exports.record(external_exports.string(), external_exports.string()).default({}) }).parse(await options.readBody(req, MAX_CLIENT_STATE_BYTES + 8 * 1024));
|
|
64978
|
-
if (Buffer.byteLength(JSON.stringify(body.clientState)) > MAX_CLIENT_STATE_BYTES) throw
|
|
65360
|
+
if (Buffer.byteLength(JSON.stringify(body.clientState)) > MAX_CLIENT_STATE_BYTES) throw failure3("Saved drafts exceed the 2 MB backup preference limit.", 413);
|
|
64979
65361
|
const clientState = Object.fromEntries(Object.entries(body.clientState).filter(([key3]) => WORKSPACE_BACKUP_CLIENT_KEYS.includes(key3)));
|
|
64980
65362
|
const result = await operate(() => options.exclusive(async () => {
|
|
64981
65363
|
check2(req, auth);
|
|
64982
65364
|
replacePrevious(auth, ["download"]);
|
|
64983
|
-
if (artifacts.size >= 4) throw
|
|
65365
|
+
if (artifacts.size >= 4) throw failure3("There are already four backup files in progress. Restart or wait an hour before creating another.", 409);
|
|
64984
65366
|
return createWorkspaceBackup(options.dataDir, { password: body.password, clientState, appVersion: options.appVersion });
|
|
64985
65367
|
}));
|
|
64986
65368
|
try {
|
|
@@ -65009,25 +65391,25 @@ function createWorkspaceBackupRoutes(options) {
|
|
|
65009
65391
|
if (method === "POST" && path === `${PREFIX}/upload`) {
|
|
65010
65392
|
await operate(async () => {
|
|
65011
65393
|
const length = Number(req.headers["content-length"]);
|
|
65012
|
-
if (!Number.isSafeInteger(length) || length <= 0 || length > MAX_UPLOAD_BYTES) throw
|
|
65394
|
+
if (!Number.isSafeInteger(length) || length <= 0 || length > MAX_UPLOAD_BYTES) throw failure3("Select a backup containing no more than 10 GB of workspace data.", 413);
|
|
65013
65395
|
replacePrevious(auth, ["upload", "staged"]);
|
|
65014
|
-
if (artifacts.size >= 4) throw
|
|
65396
|
+
if (artifacts.size >= 4) throw failure3("There are already four backup files in progress. Restart or wait an hour before uploading another.", 409);
|
|
65015
65397
|
const root = join59(options.dataDir, ".backups");
|
|
65016
65398
|
mkdirSync29(root, { recursive: true, mode: 448 });
|
|
65017
|
-
if (
|
|
65399
|
+
if (lstatSync8(root).isSymbolicLink()) throw failure3("Backup storage must not be a symbolic link.");
|
|
65018
65400
|
const directory = join59(root, "uploads");
|
|
65019
65401
|
mkdirSync29(directory, { recursive: true, mode: 448 });
|
|
65020
|
-
if (
|
|
65021
|
-
const id =
|
|
65402
|
+
if (lstatSync8(directory).isSymbolicLink()) throw failure3("Backup storage must not be a symbolic link.");
|
|
65403
|
+
const id = randomUUID25();
|
|
65022
65404
|
const file2 = join59(directory, `${id}.ombbackup`);
|
|
65023
65405
|
let bytes = 0;
|
|
65024
65406
|
try {
|
|
65025
65407
|
const limit = new Transform2({ transform(chunk, _encoding, callback) {
|
|
65026
65408
|
bytes += chunk.length;
|
|
65027
|
-
callback(bytes > MAX_UPLOAD_BYTES ?
|
|
65409
|
+
callback(bytes > MAX_UPLOAD_BYTES ? failure3("The backup is too large.", 413) : null, chunk);
|
|
65028
65410
|
} });
|
|
65029
65411
|
await pipeline3(req.iterator({ destroyOnReturn: false }), limit, createWriteStream3(file2, { flags: "wx", mode: 384 }));
|
|
65030
|
-
if (bytes !== length) throw
|
|
65412
|
+
if (bytes !== length) throw failure3("The upload was incomplete. Select the file again.");
|
|
65031
65413
|
check2(req, auth);
|
|
65032
65414
|
artifacts.set(id, { owner: owner2(auth), kind: "upload", path: file2, expires: Date.now() + EXPIRES_MS });
|
|
65033
65415
|
json4(res, 200, { id });
|
|
@@ -65681,17 +66063,17 @@ function unavailable(error51) {
|
|
|
65681
66063
|
}
|
|
65682
66064
|
|
|
65683
66065
|
// server/provider-auth-sessions.ts
|
|
65684
|
-
var
|
|
66066
|
+
var failure4 = (message3, status) => Object.assign(new Error(message3), { status });
|
|
65685
66067
|
var ProviderAuthSessions = class {
|
|
65686
66068
|
flows = /* @__PURE__ */ new Map();
|
|
65687
66069
|
get active() {
|
|
65688
66070
|
return [...this.flows.values()].some((flow3) => flow3.busy || !flow3.revoked && flow3.expiresAt > Date.now());
|
|
65689
66071
|
}
|
|
65690
66072
|
async start(instance, owner2) {
|
|
65691
|
-
if (!instance.startAuthentication) throw
|
|
66073
|
+
if (!instance.startAuthentication) throw failure4("Account setup is unavailable for this provider.", 404);
|
|
65692
66074
|
const existing = this.flows.get(instance.instanceId);
|
|
65693
66075
|
if (existing && (existing.busy || existing.owner !== owner2 && existing.expiresAt > Date.now())) {
|
|
65694
|
-
throw
|
|
66076
|
+
throw failure4("A sign-in is already in progress. Finish or cancel it in the browser that started it, or wait for it to expire.", 409);
|
|
65695
66077
|
}
|
|
65696
66078
|
const resuming = existing && existing.owner === owner2 && existing.expiresAt > Date.now() && !existing.revoked;
|
|
65697
66079
|
const flow3 = resuming ? existing : { instance, owner: owner2, flowId: null, expiresAt: Date.now() + 15 * 6e4, busy: false, starting: false, revoked: false };
|
|
@@ -65702,7 +66084,7 @@ var ProviderAuthSessions = class {
|
|
|
65702
66084
|
const result = await instance.startAuthentication();
|
|
65703
66085
|
if (flow3.revoked || this.flows.get(instance.instanceId) !== flow3) {
|
|
65704
66086
|
await instance.cancelAuthentication?.();
|
|
65705
|
-
throw
|
|
66087
|
+
throw failure4("Sign-in cancelled because the session or provider changed.", 409);
|
|
65706
66088
|
}
|
|
65707
66089
|
flow3.flowId = result.flowId;
|
|
65708
66090
|
const expiry = Date.parse(result.expiresAt ?? "");
|
|
@@ -65720,22 +66102,22 @@ var ProviderAuthSessions = class {
|
|
|
65720
66102
|
owned(instanceId, owner2, flowId) {
|
|
65721
66103
|
const flow3 = this.flows.get(instanceId);
|
|
65722
66104
|
if (!flowId || !flow3 || flow3.revoked || flow3.owner !== owner2 || flow3.flowId !== flowId) {
|
|
65723
|
-
throw
|
|
66105
|
+
throw failure4("This sign-in is no longer available in this browser. Start a new sign-in.", 404);
|
|
65724
66106
|
}
|
|
65725
|
-
if (flow3.busy) throw
|
|
66107
|
+
if (flow3.busy) throw failure4("The sign-in is still being updated. Try again shortly.", 409);
|
|
65726
66108
|
return flow3;
|
|
65727
66109
|
}
|
|
65728
66110
|
async status(instanceId, owner2, flowId) {
|
|
65729
66111
|
const flow3 = this.owned(instanceId, owner2, flowId);
|
|
65730
|
-
if (!flow3.instance.getAuthentication) throw
|
|
66112
|
+
if (!flow3.instance.getAuthentication) throw failure4("Sign-in status is unavailable for this provider.", 404);
|
|
65731
66113
|
const status = await flow3.instance.getAuthentication(flowId);
|
|
65732
|
-
if (flow3.revoked) throw
|
|
66114
|
+
if (flow3.revoked) throw failure4("This sign-in session ended.", 404);
|
|
65733
66115
|
if (status.phase !== "waiting") flow3.expiresAt = 0;
|
|
65734
66116
|
return status;
|
|
65735
66117
|
}
|
|
65736
66118
|
async complete(instanceId, owner2, flowId, callbackUrl) {
|
|
65737
66119
|
const flow3 = this.owned(instanceId, owner2, flowId);
|
|
65738
|
-
if (!flow3.instance.completeAuthentication) throw
|
|
66120
|
+
if (!flow3.instance.completeAuthentication) throw failure4("This provider does not use a callback URL.", 400);
|
|
65739
66121
|
flow3.busy = true;
|
|
65740
66122
|
try {
|
|
65741
66123
|
await flow3.instance.completeAuthentication(flowId, callbackUrl);
|
|
@@ -65758,10 +66140,10 @@ var ProviderAuthSessions = class {
|
|
|
65758
66140
|
* admin is still completing must not be pulled away underneath them, and
|
|
65759
66141
|
* nobody may start one while the credential is being removed. */
|
|
65760
66142
|
async signOut(instance, owner2) {
|
|
65761
|
-
if (!instance.signOut) throw
|
|
66143
|
+
if (!instance.signOut) throw failure4("Sign-out is unavailable for this provider.", 404);
|
|
65762
66144
|
const existing = this.flows.get(instance.instanceId);
|
|
65763
66145
|
if (existing && (existing.busy || existing.owner !== owner2 && existing.expiresAt > Date.now() && !existing.revoked)) {
|
|
65764
|
-
throw
|
|
66146
|
+
throw failure4("A sign-in is in progress. Finish or cancel it in the browser that started it, or wait for it to expire.", 409);
|
|
65765
66147
|
}
|
|
65766
66148
|
const flow3 = { instance, owner: owner2, flowId: null, expiresAt: Date.now() + 6e4, busy: true, starting: false, revoked: false };
|
|
65767
66149
|
this.flows.set(instance.instanceId, flow3);
|
|
@@ -65796,7 +66178,7 @@ var ProviderAuthSessions = class {
|
|
|
65796
66178
|
};
|
|
65797
66179
|
|
|
65798
66180
|
// server/request-auth.ts
|
|
65799
|
-
import { timingSafeEqual as
|
|
66181
|
+
import { timingSafeEqual as timingSafeEqual3 } from "node:crypto";
|
|
65800
66182
|
import { isIP as isIP2 } from "node:net";
|
|
65801
66183
|
|
|
65802
66184
|
// companion/src/routes.ts
|
|
@@ -66063,6 +66445,11 @@ var CLIENT_ALLOW = [
|
|
|
66063
66445
|
{ methods: ["GET"], path: /^\/api\/auth\/session$/ },
|
|
66064
66446
|
{ methods: ["POST"], path: /^\/api\/auth\/stream-ticket$/ },
|
|
66065
66447
|
{ methods: ["POST"], path: /^\/api\/auth\/logout$/ },
|
|
66448
|
+
// Own outbound desktop connector, additionally bound to a private secret.
|
|
66449
|
+
// Only honoured while features.sharedComputers is on (see requiredScope):
|
|
66450
|
+
// with the feature off these paths are as unlisted as any other, so a
|
|
66451
|
+
// client session is refused exactly the way an unknown route refuses it.
|
|
66452
|
+
{ methods: ["POST"], path: /^\/api\/shared-computers\/(?:connect|[\w-]+\/(?:poll|lease|result|disconnect))$/, feature: "sharedComputers" },
|
|
66066
66453
|
// liveness, identity, the stream
|
|
66067
66454
|
{ methods: ["GET"], path: /^\/api\/health$/ },
|
|
66068
66455
|
{ methods: ["GET"], path: /^\/api\/edition$/ },
|
|
@@ -66127,9 +66514,10 @@ var CLIENT_ALLOW = [
|
|
|
66127
66514
|
// configured-or-not booleans; the handler strips the few identifying fields for clients
|
|
66128
66515
|
{ methods: ["GET"], path: /^\/api\/config$/ }
|
|
66129
66516
|
];
|
|
66130
|
-
function requiredScope(method, path) {
|
|
66517
|
+
function requiredScope(method, path, features = {}) {
|
|
66131
66518
|
const upper = method.toUpperCase();
|
|
66132
66519
|
for (const rule of CLIENT_ALLOW) {
|
|
66520
|
+
if (rule.feature && features[rule.feature] !== true) continue;
|
|
66133
66521
|
if (rule.path.test(path) && rule.methods.includes(upper)) return "client";
|
|
66134
66522
|
}
|
|
66135
66523
|
return "admin";
|
|
@@ -66157,7 +66545,7 @@ function secureTokenMatch(actual, expected) {
|
|
|
66157
66545
|
if (!actual || !expected) return false;
|
|
66158
66546
|
const left = Buffer.from(actual);
|
|
66159
66547
|
const right = Buffer.from(expected);
|
|
66160
|
-
return left.length === right.length &&
|
|
66548
|
+
return left.length === right.length && timingSafeEqual3(left, right);
|
|
66161
66549
|
}
|
|
66162
66550
|
function resolveRequestAuth(req, options) {
|
|
66163
66551
|
const method = req.method ?? "GET";
|
|
@@ -66180,13 +66568,16 @@ function resolveRequestAuth(req, options) {
|
|
|
66180
66568
|
}
|
|
66181
66569
|
if (session && via) {
|
|
66182
66570
|
if (via === "cookie" && !isSameOrigin(req)) return deny(403, "forbidden: cross-origin request");
|
|
66183
|
-
const needed = requiredScope(method, path);
|
|
66571
|
+
const needed = requiredScope(method, path, options.features ?? {});
|
|
66184
66572
|
if (!session.scopes.includes(needed)) {
|
|
66185
66573
|
return deny(403, `forbidden: this session lacks the ${needed} scope`);
|
|
66186
66574
|
}
|
|
66187
66575
|
if (via !== "ticket") options.sessions.renew(session.id);
|
|
66188
66576
|
return { auth: { kind: "session", session, via, scopes: session.scopes }, status: 401, error: "" };
|
|
66189
66577
|
}
|
|
66578
|
+
if (via) {
|
|
66579
|
+
return deny(401, "unauthorized: this session has expired or was revoked; pair this device again");
|
|
66580
|
+
}
|
|
66190
66581
|
const proxied = isProxied(req);
|
|
66191
66582
|
const loopback2 = !proxied && isLoopbackHost(headerValue(req.headers.host)) && isAllowedOrigin(headerValue(req.headers.origin));
|
|
66192
66583
|
if (loopback2) {
|
|
@@ -66200,9 +66591,6 @@ function resolveRequestAuth(req, options) {
|
|
|
66200
66591
|
}
|
|
66201
66592
|
return { auth: { kind: "loopback", scopes: LOOPBACK_SCOPES }, status: 401, error: "" };
|
|
66202
66593
|
}
|
|
66203
|
-
if (via) {
|
|
66204
|
-
return deny(401, "unauthorized: this session has expired or was revoked; pair this device again");
|
|
66205
|
-
}
|
|
66206
66594
|
if (proxied) {
|
|
66207
66595
|
return deny(403, "forbidden: this request came through a proxy (pair this device to use the server remotely)");
|
|
66208
66596
|
}
|
|
@@ -66213,8 +66601,8 @@ function resolveRequestAuth(req, options) {
|
|
|
66213
66601
|
}
|
|
66214
66602
|
|
|
66215
66603
|
// server/sessions.ts
|
|
66216
|
-
import { createHash as createHash27, randomBytes as randomBytes8, randomUUID as
|
|
66217
|
-
import {
|
|
66604
|
+
import { createHash as createHash27, randomBytes as randomBytes8, randomUUID as randomUUID26, timingSafeEqual as timingSafeEqual4 } from "node:crypto";
|
|
66605
|
+
import { closeSync as closeSync12, existsSync as existsSync29, fsyncSync as fsyncSync4, mkdirSync as mkdirSync30, openSync as openSync12, readFileSync as readFileSync41, unlinkSync as unlinkSync12 } from "node:fs";
|
|
66218
66606
|
import { dirname as dirname18 } from "node:path";
|
|
66219
66607
|
var SCOPES = ["admin", "client"];
|
|
66220
66608
|
var PAIRING_CODE_ALPHABET = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
|
|
@@ -66247,14 +66635,16 @@ var sessionSchema = external_exports.object({
|
|
|
66247
66635
|
expiresAt: external_exports.number(),
|
|
66248
66636
|
/** Set when the session came from an account sign-in rather than a code. */
|
|
66249
66637
|
userId: external_exports.string().max(256).optional(),
|
|
66250
|
-
email: external_exports.string().max(320).optional()
|
|
66638
|
+
email: external_exports.string().max(320).optional(),
|
|
66639
|
+
/** Set only by the internal verified-portal issuance path, never by email or pairing input. */
|
|
66640
|
+
membershipAuthority: external_exports.literal("portal").optional()
|
|
66251
66641
|
});
|
|
66252
|
-
var
|
|
66642
|
+
var fileSchema3 = external_exports.object({ version: external_exports.literal(1), sessions: external_exports.array(sessionSchema) });
|
|
66253
66643
|
var sha2562 = (value) => createHash27("sha256").update(value).digest("hex");
|
|
66254
66644
|
function sameDigest(a, b2) {
|
|
66255
66645
|
const left = Buffer.from(a, "hex");
|
|
66256
66646
|
const right = Buffer.from(b2, "hex");
|
|
66257
|
-
return left.length === right.length &&
|
|
66647
|
+
return left.length === right.length && timingSafeEqual4(left, right);
|
|
66258
66648
|
}
|
|
66259
66649
|
function generatePairingCode() {
|
|
66260
66650
|
const limit = Math.floor(256 / PAIRING_CODE_ALPHABET.length) * PAIRING_CODE_ALPHABET.length;
|
|
@@ -66296,34 +66686,69 @@ var SessionRegistry = class {
|
|
|
66296
66686
|
lastSeenWrites = /* @__PURE__ */ new Map();
|
|
66297
66687
|
now;
|
|
66298
66688
|
options;
|
|
66689
|
+
openMarker;
|
|
66690
|
+
closed = false;
|
|
66299
66691
|
// No parameter properties: the server runs this file under Node's
|
|
66300
66692
|
// strip-only TypeScript mode, which only erases types.
|
|
66301
66693
|
constructor(options) {
|
|
66302
66694
|
this.options = options;
|
|
66303
66695
|
this.now = options.now ?? Date.now;
|
|
66304
|
-
this.
|
|
66696
|
+
this.openMarker = `${options.file}.open`;
|
|
66697
|
+
const unclean = existsSync29(this.openMarker);
|
|
66698
|
+
try {
|
|
66699
|
+
mkdirSync30(dirname18(options.file), { recursive: true, mode: 448 });
|
|
66700
|
+
writeFileAtomic(this.openMarker, "Session registry is open.\n", { mode: 384 });
|
|
66701
|
+
this.syncDirectory();
|
|
66702
|
+
} catch {
|
|
66703
|
+
throw new Error("Could not establish safe session storage.");
|
|
66704
|
+
}
|
|
66705
|
+
this.load(!unclean);
|
|
66706
|
+
this.revalidateEmailSessions();
|
|
66305
66707
|
}
|
|
66306
|
-
load() {
|
|
66307
|
-
if (!
|
|
66708
|
+
load(restoreAccounts) {
|
|
66709
|
+
if (!existsSync29(this.options.file)) return;
|
|
66308
66710
|
let raw;
|
|
66309
66711
|
try {
|
|
66310
|
-
raw = JSON.parse(
|
|
66712
|
+
raw = JSON.parse(readFileSync41(this.options.file, "utf8"));
|
|
66311
66713
|
} catch {
|
|
66312
66714
|
return;
|
|
66313
66715
|
}
|
|
66314
|
-
const parsed =
|
|
66315
|
-
if (parsed.success) this.sessions = parsed.data.sessions;
|
|
66716
|
+
const parsed = fileSchema3.safeParse(raw);
|
|
66717
|
+
if (parsed.success) this.sessions = parsed.data.sessions.filter((session) => restoreAccounts || session.email === void 0 && session.userId === void 0);
|
|
66718
|
+
}
|
|
66719
|
+
syncDirectory() {
|
|
66720
|
+
if (process.platform === "win32") return;
|
|
66721
|
+
const fd = openSync12(dirname18(this.options.file), "r");
|
|
66722
|
+
try {
|
|
66723
|
+
fsyncSync4(fd);
|
|
66724
|
+
} finally {
|
|
66725
|
+
closeSync12(fd);
|
|
66726
|
+
}
|
|
66316
66727
|
}
|
|
66317
66728
|
/** Atomic write, owner-only, directory owner-only. */
|
|
66318
66729
|
persist() {
|
|
66730
|
+
if (this.closed) throw new Error("Session registry is closed.");
|
|
66319
66731
|
const dir = dirname18(this.options.file);
|
|
66320
66732
|
mkdirSync30(dir, { recursive: true, mode: 448 });
|
|
66321
|
-
|
|
66322
|
-
|
|
66323
|
-
|
|
66324
|
-
|
|
66733
|
+
writeFileAtomic(this.options.file, JSON.stringify({ version: 1, sessions: this.sessions }, null, 2) + "\n", { mode: 384 });
|
|
66734
|
+
this.syncDirectory();
|
|
66735
|
+
}
|
|
66736
|
+
/** Call after HTTP/stream shutdown. A failed close leaves the open marker,
|
|
66737
|
+
* so account sessions require fresh sign-in after restart. Paired devices
|
|
66738
|
+
* remain durable. Never remove the marker before the reduced set is saved. */
|
|
66739
|
+
close() {
|
|
66740
|
+
if (this.closed) return;
|
|
66741
|
+
try {
|
|
66742
|
+
this.revalidateEmailSessions();
|
|
66743
|
+
this.persist();
|
|
66744
|
+
unlinkSync12(this.openMarker);
|
|
66745
|
+
this.closed = true;
|
|
66746
|
+
} catch {
|
|
66747
|
+
throw new Error("Could not safely close session storage.");
|
|
66748
|
+
}
|
|
66325
66749
|
}
|
|
66326
66750
|
prune() {
|
|
66751
|
+
this.revalidateEmailSessions();
|
|
66327
66752
|
const now = this.now();
|
|
66328
66753
|
this.pairings = this.pairings.filter((p2) => p2.expiresAt > now);
|
|
66329
66754
|
this.replays = this.replays.filter((r) => r.expiresAt > now);
|
|
@@ -66356,7 +66781,7 @@ var SessionRegistry = class {
|
|
|
66356
66781
|
const code = generatePairingCode();
|
|
66357
66782
|
const scopes = input.scopes?.length ? [...new Set(input.scopes)] : [...SCOPES];
|
|
66358
66783
|
const pairing = {
|
|
66359
|
-
id:
|
|
66784
|
+
id: randomUUID26(),
|
|
66360
66785
|
codeHash: sha2562(code),
|
|
66361
66786
|
scopes,
|
|
66362
66787
|
label: (input.label ?? "").trim().slice(0, 80),
|
|
@@ -66428,7 +66853,7 @@ var SessionRegistry = class {
|
|
|
66428
66853
|
this.failures.delete(input.source);
|
|
66429
66854
|
const token = `omb_sess_${randomBytes8(32).toString("base64url")}`;
|
|
66430
66855
|
const record3 = {
|
|
66431
|
-
id:
|
|
66856
|
+
id: randomUUID26(),
|
|
66432
66857
|
tokenHash: sha2562(token),
|
|
66433
66858
|
label: (input.label.trim() || pairing.label || input.fallbackLabel?.trim() || "Unnamed device").slice(0, 80),
|
|
66434
66859
|
scopes: [...pairing.scopes],
|
|
@@ -66448,11 +66873,20 @@ var SessionRegistry = class {
|
|
|
66448
66873
|
/** A session from a verified account sign-in (server/account-signin.ts)
|
|
66449
66874
|
* rather than a pairing code: same token, same term, same gates. */
|
|
66450
66875
|
issue(input) {
|
|
66876
|
+
return this.issueAccount(input);
|
|
66877
|
+
}
|
|
66878
|
+
/** Internal hosted-bridge seam: call only after consuming a verified,
|
|
66879
|
+
* workspace-bound PKCE grant. No HTTP route accepts this marker as input. */
|
|
66880
|
+
issuePortal(input) {
|
|
66881
|
+
if (!/^[A-Za-z0-9_-]{43}$/.test(input.grant) || !input.email) throw new Error("A verified portal identity is required");
|
|
66882
|
+
return this.issueAccount({ label: "Hosted workspace", email: input.email, userId: `portal:${input.grant}`, scopes: input.scopes }, "portal");
|
|
66883
|
+
}
|
|
66884
|
+
issueAccount(input, membershipAuthority) {
|
|
66451
66885
|
this.prune();
|
|
66452
66886
|
const now = this.now();
|
|
66453
66887
|
const token = `omb_sess_${randomBytes8(32).toString("base64url")}`;
|
|
66454
66888
|
const record3 = {
|
|
66455
|
-
id:
|
|
66889
|
+
id: randomUUID26(),
|
|
66456
66890
|
tokenHash: sha2562(token),
|
|
66457
66891
|
label: (input.label.trim() || "Unnamed device").slice(0, 80),
|
|
66458
66892
|
scopes: [...new Set(input.scopes)],
|
|
@@ -66462,6 +66896,7 @@ var SessionRegistry = class {
|
|
|
66462
66896
|
};
|
|
66463
66897
|
if (input.userId) record3.userId = input.userId;
|
|
66464
66898
|
if (input.email) record3.email = input.email;
|
|
66899
|
+
if (membershipAuthority) record3.membershipAuthority = membershipAuthority;
|
|
66465
66900
|
this.sessions.push(record3);
|
|
66466
66901
|
this.lastSeenWrites.set(record3.id, now);
|
|
66467
66902
|
this.persist();
|
|
@@ -66479,8 +66914,41 @@ var SessionRegistry = class {
|
|
|
66479
66914
|
this.failures.delete(source);
|
|
66480
66915
|
}
|
|
66481
66916
|
// ── sessions ───────────────────────────────────────────────────────────
|
|
66917
|
+
/** Email membership is live, not a thirty-day grant. Losing any issued
|
|
66918
|
+
* scope revokes the token and its streams; signing in again obtains the
|
|
66919
|
+
* new role. Promotions never widen existing credentials. Pairing sessions
|
|
66920
|
+
* have no email and remain independent of the hosted sign-in list. */
|
|
66921
|
+
revalidateEmailSessions() {
|
|
66922
|
+
if (this.closed) throw new Error("Session registry is closed.");
|
|
66923
|
+
const eligible = this.sessions.filter((session) => session.email !== void 0 && !(this.options.portalMembership && session.membershipAuthority === "portal"));
|
|
66924
|
+
if (!eligible.length) return;
|
|
66925
|
+
let resolveScopes = this.options.emailScopes;
|
|
66926
|
+
try {
|
|
66927
|
+
if (this.options.emailScopesSnapshot) resolveScopes = this.options.emailScopesSnapshot();
|
|
66928
|
+
} catch {
|
|
66929
|
+
resolveScopes = void 0;
|
|
66930
|
+
}
|
|
66931
|
+
const revoked = eligible.filter((session) => {
|
|
66932
|
+
try {
|
|
66933
|
+
const allowed = resolveScopes?.(session.email);
|
|
66934
|
+
return !allowed || session.scopes.some((scope) => !allowed.includes(scope));
|
|
66935
|
+
} catch {
|
|
66936
|
+
return true;
|
|
66937
|
+
}
|
|
66938
|
+
});
|
|
66939
|
+
if (!revoked.length) return;
|
|
66940
|
+
const ids = new Set(revoked.map((session) => session.id));
|
|
66941
|
+
this.sessions = this.sessions.filter((session) => !ids.has(session.id));
|
|
66942
|
+
for (const session of revoked) this.forget(session.id);
|
|
66943
|
+
try {
|
|
66944
|
+
this.persist();
|
|
66945
|
+
} catch {
|
|
66946
|
+
console.error("Could not persist email session revocation.");
|
|
66947
|
+
}
|
|
66948
|
+
}
|
|
66482
66949
|
authenticate(token) {
|
|
66483
66950
|
if (!token) return null;
|
|
66951
|
+
this.revalidateEmailSessions();
|
|
66484
66952
|
const hash2 = sha2562(token);
|
|
66485
66953
|
const now = this.now();
|
|
66486
66954
|
const record3 = this.sessions.find((s3) => sameDigest(s3.tokenHash, hash2));
|
|
@@ -66500,6 +66968,7 @@ var SessionRegistry = class {
|
|
|
66500
66968
|
* Nothing expired is revived. Writes at most once per half-term, so it
|
|
66501
66969
|
* adds nothing to the last-seen traffic. Returns whether it renewed. */
|
|
66502
66970
|
renew(sessionId) {
|
|
66971
|
+
this.revalidateEmailSessions();
|
|
66503
66972
|
const record3 = this.sessions.find((s3) => s3.id === sessionId);
|
|
66504
66973
|
const now = this.now();
|
|
66505
66974
|
if (!record3 || record3.expiresAt <= now) return false;
|
|
@@ -66553,7 +67022,7 @@ var SessionRegistry = class {
|
|
|
66553
67022
|
};
|
|
66554
67023
|
|
|
66555
67024
|
// server/brand.ts
|
|
66556
|
-
import { existsSync as
|
|
67025
|
+
import { existsSync as existsSync30, readFileSync as readFileSync42 } from "node:fs";
|
|
66557
67026
|
import { join as join60 } from "node:path";
|
|
66558
67027
|
var HEX_COLOUR = /^#[0-9a-fA-F]{6}$/;
|
|
66559
67028
|
var MAX_LOGO_CHARS = 3e5;
|
|
@@ -66580,10 +67049,10 @@ function loadBrand(options = {}) {
|
|
|
66580
67049
|
const file2 = options.file ?? brandFile();
|
|
66581
67050
|
const isEntitled = options.isEntitled ?? entitled;
|
|
66582
67051
|
const fallback = (notice) => notice ? { brand: DEFAULT_BRAND, source: "default", file: file2, notice } : { brand: DEFAULT_BRAND, source: "default", file: file2 };
|
|
66583
|
-
if (!
|
|
67052
|
+
if (!existsSync30(file2)) return fallback();
|
|
66584
67053
|
let raw;
|
|
66585
67054
|
try {
|
|
66586
|
-
raw = JSON.parse(
|
|
67055
|
+
raw = JSON.parse(readFileSync42(file2, "utf8"));
|
|
66587
67056
|
} catch (error51) {
|
|
66588
67057
|
return fallback(`${file2} is not valid JSON (${error51 instanceof Error ? error51.message : String(error51)}); using the default brand`);
|
|
66589
67058
|
}
|
|
@@ -69094,7 +69563,7 @@ function releaseDataDirLeaseAtExit() {
|
|
|
69094
69563
|
}
|
|
69095
69564
|
process.once("exit", releaseDataDirLeaseAtExit);
|
|
69096
69565
|
var workspaceRestore = { restored: false };
|
|
69097
|
-
if (
|
|
69566
|
+
if (existsSync31(join61(DATA_DIR, ".backups"))) {
|
|
69098
69567
|
workspaceRestore = applyPendingWorkspaceRestore(DATA_DIR);
|
|
69099
69568
|
if (!workspaceRestore.restored && !workspaceRestore.rolledBack) {
|
|
69100
69569
|
workspaceRestore = readLastWorkspaceRestore(DATA_DIR) ?? workspaceRestore;
|
|
@@ -69102,8 +69571,22 @@ if (existsSync30(join61(DATA_DIR, ".backups"))) {
|
|
|
69102
69571
|
}
|
|
69103
69572
|
var workspaceMaintenance = new WorkspaceBackupMaintenance();
|
|
69104
69573
|
var ENVIRONMENT_ID = loadEnvironmentId(DATA_DIR);
|
|
69105
|
-
|
|
69574
|
+
function signInAllowList() {
|
|
69575
|
+
const current2 = loadConfig().signIn;
|
|
69576
|
+
return { admins: parseAllowList(current2?.admins?.join(",")), members: parseAllowList(current2?.members?.join(",")) };
|
|
69577
|
+
}
|
|
69578
|
+
var sessions = new SessionRegistry({
|
|
69579
|
+
file: join61(DATA_DIR, "sessions.json"),
|
|
69580
|
+
emailScopesSnapshot: () => {
|
|
69581
|
+
const membership = signInAllowList();
|
|
69582
|
+
return (email3) => allowedScopes(email3, membership);
|
|
69583
|
+
},
|
|
69584
|
+
portalMembership: hostedWorkspaceConfiguration()?.portalMembership === true
|
|
69585
|
+
});
|
|
69586
|
+
var sharedComputers = new SharedComputers((id) => sessions.isLive(id));
|
|
69106
69587
|
var SESSION_COOKIE = sessionCookieName(PORT, ENVIRONMENT_ID);
|
|
69588
|
+
var HOSTED_WORKSPACE = hostedWorkspaceConfigured();
|
|
69589
|
+
var workspaceAccess = null;
|
|
69107
69590
|
var DESKTOP_MANAGED = process.env.OMB_DESKTOP_PARENT === "1";
|
|
69108
69591
|
var desktopMutationToken = DESKTOP_MANAGED ? "" : void 0;
|
|
69109
69592
|
var companionMutationToken = DESKTOP_MANAGED ? "" : void 0;
|
|
@@ -69111,10 +69594,7 @@ var FALLBACK_PUBLIC_URL = process.env.OMB_PUBLIC_URL?.trim().replace(/\/+$/, "")
|
|
|
69111
69594
|
var cfg = loadConfig();
|
|
69112
69595
|
var customDomainVerifier = createCustomDomainVerifier({ environmentId: ENVIRONMENT_ID });
|
|
69113
69596
|
var emailSignIn = createEmailSignIn({
|
|
69114
|
-
allow:
|
|
69115
|
-
const current2 = loadConfig().signIn;
|
|
69116
|
-
return { admins: parseAllowList(current2?.admins?.join(",")), members: parseAllowList(current2?.members?.join(",")) };
|
|
69117
|
-
}
|
|
69597
|
+
allow: signInAllowList
|
|
69118
69598
|
});
|
|
69119
69599
|
var customDomainRevision = 0;
|
|
69120
69600
|
function savedCustomDomain() {
|
|
@@ -69216,7 +69696,7 @@ var INTERNAL_CAPABILITY_ORPHAN_MS = 30 * 24 * 60 * 6e4;
|
|
|
69216
69696
|
var internalCapabilities = /* @__PURE__ */ new Map();
|
|
69217
69697
|
var activeInternalGenerationByThread = /* @__PURE__ */ new Map();
|
|
69218
69698
|
var internalGenerationByProviderTurn = new ProviderTurnGenerationRegistry();
|
|
69219
|
-
function beginInternalCapabilityGeneration(threadId, generation =
|
|
69699
|
+
function beginInternalCapabilityGeneration(threadId, generation = randomUUID27()) {
|
|
69220
69700
|
const previous2 = activeInternalGenerationByThread.get(threadId);
|
|
69221
69701
|
if (previous2) revokeInternalCapabilityGeneration(threadId, previous2);
|
|
69222
69702
|
activeInternalGenerationByThread.set(threadId, generation);
|
|
@@ -69275,11 +69755,15 @@ function authorizedInternalCapability(header2) {
|
|
|
69275
69755
|
continue;
|
|
69276
69756
|
}
|
|
69277
69757
|
const expected = Buffer.from(`Bearer ${token}`);
|
|
69278
|
-
if (got.length === expected.length &&
|
|
69758
|
+
if (got.length === expected.length && timingSafeEqual5(got, expected)) return capability;
|
|
69279
69759
|
}
|
|
69280
69760
|
return null;
|
|
69281
69761
|
}
|
|
69282
69762
|
function internalCapabilityIsActive(capability) {
|
|
69763
|
+
if (capability.teamComputerId) {
|
|
69764
|
+
const pinned = teamComputerTurns.get(capability.threadId);
|
|
69765
|
+
if (pinned?.computerId !== capability.teamComputerId || pinned.owner.generation !== capability.generation || pinned.botId !== capability.botId) return false;
|
|
69766
|
+
}
|
|
69283
69767
|
if (capability.localVmTarget) {
|
|
69284
69768
|
const owner2 = localVmLeaseFor(capability.localVmTarget).current(localVmOwnerBusy);
|
|
69285
69769
|
if (localVmThreadTargets.get(capability.threadId) !== capability.localVmTarget || owner2?.threadId !== capability.threadId || owner2.botId !== capability.botId) return false;
|
|
@@ -69306,7 +69790,7 @@ var phoneSecretEnvelopeSchema = external_exports.object({
|
|
|
69306
69790
|
var agentsProxyPath = SPAWNED_PROXIES.agents;
|
|
69307
69791
|
var phoneProxyPath = SPAWNED_PROXIES.phone;
|
|
69308
69792
|
var AGENTS_NODE_FLAG = { ELECTRON_RUN_AS_NODE: "1" };
|
|
69309
|
-
function agentsIntegration(botId, threadId, depth, skillAuthoring, generation, roomHandoffId, roomCoordination = false) {
|
|
69793
|
+
function agentsIntegration(botId, threadId, depth, skillAuthoring, generation, roomHandoffId, roomCoordination = false, ownThreadCreation = false) {
|
|
69310
69794
|
const token = mintInternalCapability({
|
|
69311
69795
|
botId,
|
|
69312
69796
|
threadId,
|
|
@@ -69317,7 +69801,8 @@ function agentsIntegration(botId, threadId, depth, skillAuthoring, generation, r
|
|
|
69317
69801
|
createdBots: 0,
|
|
69318
69802
|
openedThreads: 0,
|
|
69319
69803
|
roomHandoffId,
|
|
69320
|
-
roomCoordination
|
|
69804
|
+
roomCoordination,
|
|
69805
|
+
ownThreadCreation
|
|
69321
69806
|
});
|
|
69322
69807
|
return {
|
|
69323
69808
|
command: process.execPath,
|
|
@@ -69330,7 +69815,11 @@ function agentsIntegration(botId, threadId, depth, skillAuthoring, generation, r
|
|
|
69330
69815
|
OMB_COMMS_TOKEN: token,
|
|
69331
69816
|
OMB_TURN_DEPTH: String(depth),
|
|
69332
69817
|
OMB_ROOM_TURN: roomCoordination ? "1" : "0",
|
|
69333
|
-
|
|
69818
|
+
OMB_OWN_THREAD_CREATION: ownThreadCreation ? "1" : "0",
|
|
69819
|
+
OMB_SKILL_AUTHORING_ENABLED: skillAuthoring ? "1" : "0",
|
|
69820
|
+
// The shared-computer tools are advertised only while the workspace
|
|
69821
|
+
// gate is on; the routes behind them refuse regardless.
|
|
69822
|
+
OMB_SHARED_COMPUTERS_ENABLED: sharedComputersEnabled(cfg) ? "1" : "0"
|
|
69334
69823
|
}
|
|
69335
69824
|
};
|
|
69336
69825
|
}
|
|
@@ -69360,8 +69849,10 @@ function settleDirectFollowup(generation) {
|
|
|
69360
69849
|
var directTurnBots = /* @__PURE__ */ new Map();
|
|
69361
69850
|
var providerFleetReloading = false;
|
|
69362
69851
|
var turnResources = new TurnResources();
|
|
69852
|
+
var sharedComputerControl = new SharedComputerControl(turnResources, () => store.bots.some((bot) => botComputerControlSnapshot(bot.id).held));
|
|
69363
69853
|
var turnResourceOwners = /* @__PURE__ */ new Map();
|
|
69364
69854
|
var turnComputerResources = /* @__PURE__ */ new Map();
|
|
69855
|
+
var teamComputerTurns = /* @__PURE__ */ new Map();
|
|
69365
69856
|
var settlingResourceOwners = /* @__PURE__ */ new Map();
|
|
69366
69857
|
function claimTurnResource(owner2, resource) {
|
|
69367
69858
|
if (!turnResources.claim(resource, owner2)) return false;
|
|
@@ -69374,6 +69865,11 @@ function releaseTurnResources(owner2) {
|
|
|
69374
69865
|
turnResources.release(owner2);
|
|
69375
69866
|
if (turnResourceOwners.get(owner2.threadId)?.generation === owner2.generation) turnResourceOwners.delete(owner2.threadId);
|
|
69376
69867
|
if (turnComputerResources.get(owner2.threadId)?.owner.generation === owner2.generation) turnComputerResources.delete(owner2.threadId);
|
|
69868
|
+
const teamTurn = teamComputerTurns.get(owner2.threadId);
|
|
69869
|
+
if (teamTurn?.owner.generation === owner2.generation) {
|
|
69870
|
+
stopScreenPoller(teamTurn.botId, owner2.threadId);
|
|
69871
|
+
teamComputerTurns.delete(owner2.threadId);
|
|
69872
|
+
}
|
|
69377
69873
|
}
|
|
69378
69874
|
function bindTurnComputer(owner2, resource, exclusive = false) {
|
|
69379
69875
|
if (exclusive && !claimTurnResource(owner2, resource)) {
|
|
@@ -69592,12 +70088,15 @@ function connectedAppsIntegration(botId, threadId, generation) {
|
|
|
69592
70088
|
});
|
|
69593
70089
|
}
|
|
69594
70090
|
var computerControlRevision = /* @__PURE__ */ new Map();
|
|
69595
|
-
var computerControl = new ComputerControl((
|
|
69596
|
-
|
|
69597
|
-
|
|
69598
|
-
|
|
70091
|
+
var computerControl = new ComputerControl((key3, snapshot2) => {
|
|
70092
|
+
const members = key3.startsWith("computer_") ? store.bots.filter((bot) => inheritedTeamComputer(bot)?.id === key3.slice("computer_".length)).map((bot) => bot.id) : [key3];
|
|
70093
|
+
for (const botId of members) {
|
|
70094
|
+
computerControlRevision.set(botId, (computerControlRevision.get(botId) ?? 0) + 1);
|
|
70095
|
+
if (snapshot2.held && /^[A-Za-z0-9_-]{1,120}$/.test(botId)) {
|
|
70096
|
+
postDesktopPrivateMessage({ type: "openmausbot:browser-control", botId, held: true });
|
|
70097
|
+
}
|
|
70098
|
+
broadcast({ kind: "computer-control", botId, held: snapshot2.held, helpReason: snapshot2.helpReason });
|
|
69599
70099
|
}
|
|
69600
|
-
broadcast({ kind: "computer-control", botId, held: snapshot2.held, helpReason: snapshot2.helpReason });
|
|
69601
70100
|
});
|
|
69602
70101
|
var controlLeaseIdSchema = external_exports.string().min(16).max(120).regex(/^[A-Za-z0-9_-]+$/);
|
|
69603
70102
|
var routineRequestSourceSchema = {
|
|
@@ -69626,6 +70125,7 @@ function controlIntegration(botId, threadId, generation, localVmTarget) {
|
|
|
69626
70125
|
depth: 0,
|
|
69627
70126
|
kind: "computer",
|
|
69628
70127
|
...localVmTarget ? { localVmTarget } : {},
|
|
70128
|
+
...teamComputerTurns.get(threadId) ? { teamComputerId: teamComputerTurns.get(threadId).computerId } : {},
|
|
69629
70129
|
skillAuthoring: false,
|
|
69630
70130
|
createdBots: 0,
|
|
69631
70131
|
openedThreads: 0
|
|
@@ -69823,6 +70323,7 @@ function checkedMemberIds(value) {
|
|
|
69823
70323
|
}
|
|
69824
70324
|
var bootSelection = { instanceId: "", model: "" };
|
|
69825
70325
|
var store = new Store(() => bootSelection);
|
|
70326
|
+
var teamComputers = new TeamComputers(join61(DATA_DIR, "team-computers.json"), ENVIRONMENT_ID);
|
|
69826
70327
|
var followupsReady = false;
|
|
69827
70328
|
var sendSequencer = new SendSequencer();
|
|
69828
70329
|
bootSelection = await defaultSelection();
|
|
@@ -69846,7 +70347,7 @@ var activeCoordinationForThread = (_threadId) => false;
|
|
|
69846
70347
|
var wireTask = ({ resumeCursors: _resumeCursors, lastInstanceId: _lastInstanceId, ...task2 }) => activeCoordinationForThread(task2.threadId) && !task2.busy ? { ...task2, busy: true, activity: "working" } : task2;
|
|
69847
70348
|
var wireBot = (bot) => {
|
|
69848
70349
|
const { resumeCursors: _resumeCursors, tasks, approvalGrant, lastProfileRequestId: _lastProfileRequestId, lastTeamSetupReceipt: _lastTeamSetupReceipt, ...rest } = bot;
|
|
69849
|
-
const visible = approvalGrant ? { ...rest, approvalMode: "ask", autoApprove: false } : rest;
|
|
70350
|
+
const visible = approvalGrant && !approvalGrant.threadOnly ? { ...rest, approvalMode: "ask", autoApprove: false } : rest;
|
|
69850
70351
|
return {
|
|
69851
70352
|
...visible,
|
|
69852
70353
|
...activeCoordinationForThread(bot.threadId) && !visible.busy ? { busy: true, activity: "working" } : {},
|
|
@@ -69856,7 +70357,7 @@ var wireBot = (bot) => {
|
|
|
69856
70357
|
};
|
|
69857
70358
|
var wireTrustedApprovalBot = (bot) => {
|
|
69858
70359
|
const { resumeCursors: _resumeCursors, tasks, approvalGrant: _approvalGrant, lastProfileRequestId: _lastProfileRequestId, lastTeamSetupReceipt: _lastTeamSetupReceipt, ...rest } = bot;
|
|
69859
|
-
return { ...rest, avatarUrl: rest.avatarUrl ?? null, ...tasks ? { tasks: tasks.map(wireTask) } : {} };
|
|
70360
|
+
return { ...rest, approvalMode: approvalModeFor(rest), avatarUrl: rest.avatarUrl ?? null, ...tasks ? { tasks: tasks.map(wireTask) } : {} };
|
|
69860
70361
|
};
|
|
69861
70362
|
function previewSystemPrompt(bot) {
|
|
69862
70363
|
const persona = [
|
|
@@ -69866,12 +70367,14 @@ function previewSystemPrompt(bot) {
|
|
|
69866
70367
|
].filter(Boolean).join(" ");
|
|
69867
70368
|
const instance = registry2.get(bot.modelSelection.instanceId);
|
|
69868
70369
|
const caps = instance?.adapter.capabilities;
|
|
69869
|
-
const
|
|
70370
|
+
const teamComputer = inheritedTeamComputer(bot);
|
|
70371
|
+
const previewComputer = teamComputer ? "cloud" : bot.computer;
|
|
70372
|
+
const computerPromptKind = previewComputer === "vm" ? caps?.computerMcp ? localVmMode(cfg) === "per-bot" ? "vm-private" : "vm-shared" : null : previewComputer === "cloud" ? instance?.driverKind === "boxAgent" ? "box-agent" : caps?.computerMcp ? bot.cloudBackend === "vps" ? "vps" : "box" : null : previewComputer === "local" ? caps?.localComputerMcp ? "local" : null : null;
|
|
69870
70373
|
const peers = reachablePeers(store.bots, bot);
|
|
69871
70374
|
const coordination = bot.chiefOfStaff ? chiefOfStaffSystemPrompt(bot.id, store.bots, true, openMausStatusSystemPrompt()) : peers.length > 0 ? peerRosterSystemPrompt(peers) : "";
|
|
69872
70375
|
const agentsMounted = caps?.agentsMcp === true;
|
|
69873
70376
|
const previewPlan = resolveSurface({
|
|
69874
|
-
destination:
|
|
70377
|
+
destination: previewComputer,
|
|
69875
70378
|
browserOn: caps?.browserMcp === true && builtInBrowserEnabled(cfg) && bot.browser !== false
|
|
69876
70379
|
});
|
|
69877
70380
|
const privateWorkspace = instance && !["grok", "boxAgent"].includes(instance.driverKind);
|
|
@@ -69885,6 +70388,7 @@ function previewSystemPrompt(bot) {
|
|
|
69885
70388
|
})
|
|
69886
70389
|
},
|
|
69887
70390
|
{ id: "computer", label: "Computer", text: computerPrompt(computerPromptKind) },
|
|
70391
|
+
{ id: "team-computer", label: "Team computer", text: teamComputerPrompt(teamComputer) },
|
|
69888
70392
|
{ id: "plan", label: "Surface", text: previewPlan.note },
|
|
69889
70393
|
{ id: "composio", label: "Connected apps", text: caps?.composioMcp && bot.composio !== false && configured(cfg) ? COMPOSIO_PROMPT : "" },
|
|
69890
70394
|
{ id: "mcp", label: "MCP servers", text: caps?.customMcp ? customMcpPrompt(Object.keys(customMcpServers(cfg, bot.mcpServers))) : "" },
|
|
@@ -69972,11 +70476,21 @@ var approvalModeForTurn = (bot, peerInitiated = false) => {
|
|
|
69972
70476
|
function handleDesktopTrustedApprovalMessage(raw) {
|
|
69973
70477
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return false;
|
|
69974
70478
|
const message3 = raw;
|
|
70479
|
+
const grantTarget = (bot) => bot.approvalGrant?.threadOnly ? store.projectBotForTask(bot.id, bot.approvalGrant.threadId) : bot;
|
|
70480
|
+
const grantBusy = (bot) => bot.approvalGrant?.threadOnly ? threadBusy(bot.id, bot.approvalGrant.threadId) : bot.busy;
|
|
70481
|
+
const grantSupported = (bot, mode2) => supportsApprovalMode(
|
|
70482
|
+
registry2.cliTarget(grantTarget(bot)?.modelSelection.instanceId ?? "")?.driverKind,
|
|
70483
|
+
mode2
|
|
70484
|
+
);
|
|
70485
|
+
const clearGrant = (bot) => store.patchBot(bot.id, {
|
|
70486
|
+
...!bot.approvalGrant?.threadOnly ? { approvalMode: "ask", autoApprove: false } : {},
|
|
70487
|
+
approvalGrant: void 0
|
|
70488
|
+
});
|
|
69975
70489
|
const threadCanReceiveGrant = (bot) => {
|
|
69976
70490
|
const threadId2 = bot.approvalGrant?.threadId;
|
|
69977
70491
|
if (!threadId2) return true;
|
|
69978
70492
|
const target = store.projectBotForTask(bot.id, threadId2);
|
|
69979
|
-
return Boolean(target && !threadBusy(bot.id, threadId2) && registry2.cliTarget(target.modelSelection.instanceId)?.driverKind === registry2.cliTarget(bot.modelSelection.instanceId)?.driverKind);
|
|
70493
|
+
return Boolean(target && !threadBusy(bot.id, threadId2) && (bot.approvalGrant?.threadOnly || registry2.cliTarget(target.modelSelection.instanceId)?.driverKind === registry2.cliTarget(bot.modelSelection.instanceId)?.driverKind));
|
|
69980
70494
|
};
|
|
69981
70495
|
if (message3.type === "approval-trusted-mode-commit") {
|
|
69982
70496
|
const requestId2 = typeof message3.requestId === "string" && /^[A-Za-z0-9_-]{1,80}$/.test(message3.requestId) ? message3.requestId : null;
|
|
@@ -69985,13 +70499,17 @@ function handleDesktopTrustedApprovalMessage(raw) {
|
|
|
69985
70499
|
if (!requestId2) return true;
|
|
69986
70500
|
if (!botId2 || !mode2) return true;
|
|
69987
70501
|
const bot = store.bot(botId2);
|
|
69988
|
-
if (bot?.approvalGrant?.requestId === requestId2 && bot.approvalGrant.mode === mode2 && bot.approvalGrant.phase === "committed" && bot.approvalMode === mode2 && !bot
|
|
70502
|
+
if (bot?.approvalGrant?.requestId === requestId2 && bot.approvalGrant.mode === mode2 && bot.approvalGrant.phase === "committed" && (bot.approvalGrant.threadOnly || bot.approvalMode === mode2) && !grantBusy(bot) && threadCanReceiveGrant(bot) && grantSupported(bot, mode2)) {
|
|
69989
70503
|
if (bot.approvalGrant.threadId) {
|
|
69990
70504
|
store.patchTask(botId2, bot.approvalGrant.threadId, { approvalMode: mode2, autoApprove: false });
|
|
69991
70505
|
}
|
|
69992
70506
|
store.patchBot(botId2, { approvalGrant: void 0 });
|
|
70507
|
+
postDesktopPrivateMessage({ type: "approval-trusted-mode-commit-result", requestId: requestId2, ok: true, bot: wireBot(store.bot(botId2)) });
|
|
69993
70508
|
} else if (bot?.approvalGrant?.requestId === requestId2) {
|
|
69994
|
-
|
|
70509
|
+
clearGrant(bot);
|
|
70510
|
+
postDesktopPrivateMessage({ type: "approval-trusted-mode-commit-result", requestId: requestId2, ok: false });
|
|
70511
|
+
} else {
|
|
70512
|
+
postDesktopPrivateMessage({ type: "approval-trusted-mode-commit-result", requestId: requestId2, ok: false });
|
|
69995
70513
|
}
|
|
69996
70514
|
return true;
|
|
69997
70515
|
}
|
|
@@ -70013,9 +70531,9 @@ function handleDesktopTrustedApprovalMessage(raw) {
|
|
|
70013
70531
|
return true;
|
|
70014
70532
|
}
|
|
70015
70533
|
const bot = store.bot(botId2);
|
|
70016
|
-
if (bot?.approvalGrant?.requestId === requestId2 && bot.approvalGrant.mode === mode2 && bot.approvalGrant.phase === "prepared" && bot.approvalMode === mode2) {
|
|
70017
|
-
if (!
|
|
70018
|
-
|
|
70534
|
+
if (bot?.approvalGrant?.requestId === requestId2 && bot.approvalGrant.mode === mode2 && bot.approvalGrant.phase === "prepared" && (bot.approvalGrant.threadOnly || bot.approvalMode === mode2)) {
|
|
70535
|
+
if (!grantSupported(bot, mode2)) {
|
|
70536
|
+
clearGrant(bot);
|
|
70019
70537
|
confirm(false, "This provider does not support the selected approval level");
|
|
70020
70538
|
return true;
|
|
70021
70539
|
}
|
|
@@ -70026,7 +70544,7 @@ function handleDesktopTrustedApprovalMessage(raw) {
|
|
|
70026
70544
|
return true;
|
|
70027
70545
|
}
|
|
70028
70546
|
if (bot?.approvalGrant?.requestId === requestId2) {
|
|
70029
|
-
|
|
70547
|
+
clearGrant(bot);
|
|
70030
70548
|
}
|
|
70031
70549
|
confirm(false, "The approval confirmation no longer matches this bot");
|
|
70032
70550
|
return true;
|
|
@@ -70049,10 +70567,10 @@ function handleDesktopTrustedApprovalMessage(raw) {
|
|
|
70049
70567
|
return true;
|
|
70050
70568
|
}
|
|
70051
70569
|
const bot = store.bot(botId2);
|
|
70052
|
-
if (bot?.approvalGrant?.requestId === requestId2 && bot.approvalGrant.mode === mode2 && bot.approvalGrant.phase === "confirmed" && bot.approvalMode === mode2) {
|
|
70053
|
-
if (bot
|
|
70054
|
-
|
|
70055
|
-
activate(false, bot
|
|
70570
|
+
if (bot?.approvalGrant?.requestId === requestId2 && bot.approvalGrant.mode === mode2 && bot.approvalGrant.phase === "confirmed" && (bot.approvalGrant.threadOnly || bot.approvalMode === mode2)) {
|
|
70571
|
+
if (grantBusy(bot) || !grantSupported(bot, mode2)) {
|
|
70572
|
+
clearGrant(bot);
|
|
70573
|
+
activate(false, grantBusy(bot) ? "Stop this bot's turn before changing its approval level" : "This provider does not support the selected approval level");
|
|
70056
70574
|
return true;
|
|
70057
70575
|
}
|
|
70058
70576
|
store.patchBot(botId2, { approvalGrant: { ...bot.approvalGrant, requestId: requestId2, mode: mode2, phase: "activated" } });
|
|
@@ -70060,7 +70578,7 @@ function handleDesktopTrustedApprovalMessage(raw) {
|
|
|
70060
70578
|
return true;
|
|
70061
70579
|
}
|
|
70062
70580
|
if (bot?.approvalGrant?.requestId === requestId2) {
|
|
70063
|
-
|
|
70581
|
+
clearGrant(bot);
|
|
70064
70582
|
}
|
|
70065
70583
|
activate(false, "The approval activation no longer matches this bot");
|
|
70066
70584
|
return true;
|
|
@@ -70083,13 +70601,13 @@ function handleDesktopTrustedApprovalMessage(raw) {
|
|
|
70083
70601
|
return true;
|
|
70084
70602
|
}
|
|
70085
70603
|
const bot = store.bot(botId2);
|
|
70086
|
-
if (bot?.approvalGrant?.requestId === requestId2 && bot.approvalGrant.mode === mode2 && bot.approvalGrant.phase === "activated" && bot.approvalMode === mode2 && !bot
|
|
70604
|
+
if (bot?.approvalGrant?.requestId === requestId2 && bot.approvalGrant.mode === mode2 && bot.approvalGrant.phase === "activated" && (bot.approvalGrant.threadOnly || bot.approvalMode === mode2) && !grantBusy(bot) && threadCanReceiveGrant(bot) && grantSupported(bot, mode2)) {
|
|
70087
70605
|
store.patchBot(botId2, { approvalGrant: { ...bot.approvalGrant, requestId: requestId2, mode: mode2, phase: "committed" } });
|
|
70088
70606
|
finalize2(true);
|
|
70089
70607
|
return true;
|
|
70090
70608
|
}
|
|
70091
70609
|
if (bot?.approvalGrant?.requestId === requestId2) {
|
|
70092
|
-
|
|
70610
|
+
clearGrant(bot);
|
|
70093
70611
|
}
|
|
70094
70612
|
finalize2(false, bot?.busy ? "Stop this bot's turn before changing its approval level" : "The approval finalization no longer matches this bot");
|
|
70095
70613
|
return true;
|
|
@@ -70121,6 +70639,38 @@ function handleDesktopTrustedApprovalMessage(raw) {
|
|
|
70121
70639
|
}
|
|
70122
70640
|
const currentMode = approvalModeFor(existing);
|
|
70123
70641
|
const threadId = message3.threadId;
|
|
70642
|
+
if (message3.threadOnly !== void 0 && typeof message3.threadOnly !== "boolean") {
|
|
70643
|
+
respond({ ok: false, error: "Invalid thread approval scope" });
|
|
70644
|
+
return true;
|
|
70645
|
+
}
|
|
70646
|
+
if (message3.threadOnly === true) {
|
|
70647
|
+
const target = typeof threadId === "string" ? store.projectBotForTask(botId, threadId) : null;
|
|
70648
|
+
if (!target || message3.modelSelection !== void 0 || message3.updateBotDefault !== void 0) {
|
|
70649
|
+
respond({ ok: false, error: "Choose an existing thread for this approval change" });
|
|
70650
|
+
return true;
|
|
70651
|
+
}
|
|
70652
|
+
const clearsOwnGrant = mode === "ask" && existing.approvalGrant?.threadOnly && existing.approvalGrant.threadId === threadId;
|
|
70653
|
+
if (existing.approvalGrant && !clearsOwnGrant || threadBusy(botId, target.threadId)) {
|
|
70654
|
+
respond({ ok: false, error: "Stop this thread and finish its pending approval change first" });
|
|
70655
|
+
return true;
|
|
70656
|
+
}
|
|
70657
|
+
if (!supportsApprovalMode(registry2.cliTarget(target.modelSelection.instanceId)?.driverKind, mode)) {
|
|
70658
|
+
respond({ ok: false, error: "This thread's provider does not support that approval level" });
|
|
70659
|
+
return true;
|
|
70660
|
+
}
|
|
70661
|
+
if (mode === "auto" && target.computer === "local" && approvalModeFor(target) !== "auto" && message3.acknowledgeLocalAuto !== true) {
|
|
70662
|
+
respond({ ok: false, error: "Auto mode on this computer requires confirming the warning" });
|
|
70663
|
+
return true;
|
|
70664
|
+
}
|
|
70665
|
+
if (mode === "full" || mode === "custom") {
|
|
70666
|
+
store.patchBot(botId, { approvalGrant: { requestId, mode, phase: "prepared", threadId: target.threadId, threadOnly: true } });
|
|
70667
|
+
} else {
|
|
70668
|
+
if (clearsOwnGrant) clearGrant(existing);
|
|
70669
|
+
store.patchTask(botId, target.threadId, { approvalMode: mode, autoApprove: mode === "auto", alwaysAllow: [] });
|
|
70670
|
+
}
|
|
70671
|
+
respond({ ok: true, bot: wireTrustedApprovalBot(store.bot(botId)) });
|
|
70672
|
+
return true;
|
|
70673
|
+
}
|
|
70124
70674
|
if (message3.modelSelection !== void 0) {
|
|
70125
70675
|
const target = typeof threadId === "string" ? store.projectBotForTask(botId, threadId) : null;
|
|
70126
70676
|
if (mode !== "ask" || !target || typeof message3.updateBotDefault !== "boolean") {
|
|
@@ -70546,7 +71096,7 @@ function publicGroupState(group) {
|
|
|
70546
71096
|
}
|
|
70547
71097
|
function beginGroupTurnOperation(groupId, threadId, botIds = []) {
|
|
70548
71098
|
const operation = {
|
|
70549
|
-
id:
|
|
71099
|
+
id: randomUUID27(),
|
|
70550
71100
|
threadId,
|
|
70551
71101
|
botIds: new Set(botIds),
|
|
70552
71102
|
cancelled: false,
|
|
@@ -70863,8 +71413,8 @@ function messageWindow(threadId, messageId, limit) {
|
|
|
70863
71413
|
return { messages: all2.slice(start, stop).map(slimMessage), hasMore: start > 0 };
|
|
70864
71414
|
}
|
|
70865
71415
|
var sseClients = /* @__PURE__ */ new Set();
|
|
70866
|
-
|
|
70867
|
-
|
|
71416
|
+
function closeSessionStreams(sessionId) {
|
|
71417
|
+
browserLive.closeForOwner(sessionId);
|
|
70868
71418
|
for (const client of sseClients) {
|
|
70869
71419
|
if (client.sessionId !== sessionId) continue;
|
|
70870
71420
|
sseClients.delete(client);
|
|
@@ -70873,8 +71423,12 @@ sessions.onSessionRevoked((sessionId) => {
|
|
|
70873
71423
|
} catch {
|
|
70874
71424
|
}
|
|
70875
71425
|
}
|
|
71426
|
+
}
|
|
71427
|
+
sessions.onSessionRevoked((sessionId) => {
|
|
71428
|
+
providerAuthSessions.revokeOwner(sessionId);
|
|
71429
|
+
closeSessionStreams(sessionId);
|
|
70876
71430
|
});
|
|
70877
|
-
var STREAM_ID =
|
|
71431
|
+
var STREAM_ID = randomUUID27().slice(0, 8);
|
|
70878
71432
|
var REPLAY_MAX = 500;
|
|
70879
71433
|
var configuredSseHeartbeatMs = Number(process.env.OMB_SSE_HEARTBEAT_MS);
|
|
70880
71434
|
var SSE_HEARTBEAT_MS = Number.isFinite(configuredSseHeartbeatMs) && configuredSseHeartbeatMs > 0 ? configuredSseHeartbeatMs : 15e3;
|
|
@@ -70890,6 +71444,7 @@ function cursorSeq(raw) {
|
|
|
70890
71444
|
return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : null;
|
|
70891
71445
|
}
|
|
70892
71446
|
function broadcast(payload) {
|
|
71447
|
+
sessions.revalidateEmailSessions();
|
|
70893
71448
|
const seq2 = ++lastSeq;
|
|
70894
71449
|
const kind = String(payload.kind ?? "");
|
|
70895
71450
|
const frame = `id: ${STREAM_ID}:${seq2}
|
|
@@ -71131,15 +71686,99 @@ var checkpointRestoreLeases = /* @__PURE__ */ new Set();
|
|
|
71131
71686
|
var LOCAL_VM_IDLE_MS = 8 * 60 * 6e4;
|
|
71132
71687
|
var LOCAL_VM_DESKTOP_WAIT_MS = 9e4;
|
|
71133
71688
|
var localVmIdles = /* @__PURE__ */ new Map();
|
|
71689
|
+
function inheritedTeamComputer(bot) {
|
|
71690
|
+
return teamComputers.forBot(bot);
|
|
71691
|
+
}
|
|
71692
|
+
function teamComputerPrompt(computer) {
|
|
71693
|
+
return computer ? `Your team shares the Box computer ${JSON.stringify(computer.name)}. Its desktop files and desktop browser logins are shared with other Auto bots in your team; only one turn may drive it at a time. The separate built-in Browser is not this desktop and does not automatically share its logins.` : "";
|
|
71694
|
+
}
|
|
71695
|
+
function botComputerControlKey(bot) {
|
|
71696
|
+
const computer = inheritedTeamComputer(bot);
|
|
71697
|
+
return computer ? teamComputerOwner(computer.id) : bot.id;
|
|
71698
|
+
}
|
|
71699
|
+
function botComputerControlSnapshot(botId, pinnedComputerId) {
|
|
71700
|
+
const own3 = computerControl.snapshot(botId);
|
|
71701
|
+
const bot = store.bot(botId);
|
|
71702
|
+
const key3 = pinnedComputerId ? teamComputerOwner(pinnedComputerId) : bot ? botComputerControlKey(bot) : botId;
|
|
71703
|
+
const shared = computerControl.snapshot(key3);
|
|
71704
|
+
return own3.held ? own3 : shared.held || shared.helpReason ? shared : own3;
|
|
71705
|
+
}
|
|
71706
|
+
function teamComputerInUse(computer) {
|
|
71707
|
+
computer = teamComputers.get(computer.id) ?? computer;
|
|
71708
|
+
return computerControl.snapshot(teamComputerOwner(computer.id)).held || [...teamComputerTurns.values()].some((turn) => turn.computerId === computer.id) || store.bots.some((bot) => computer.section !== null && sectionKey3(bot.section) === computer.section && (botHasActiveTurn(bot.id) || Boolean(routines?.activeRunForBot(bot.id)) || computerControl.snapshot(bot.id).held));
|
|
71709
|
+
}
|
|
71710
|
+
function assertTeamControlCanBeTaken(computerId) {
|
|
71711
|
+
if ([...teamComputerTurns.values()].some((turn) => turn.computerId === computerId && turn.remoteAgent)) {
|
|
71712
|
+
throw Object.assign(new Error("Stop the Computer engine's active turn before taking control of this shared desktop"), { status: 409 });
|
|
71713
|
+
}
|
|
71714
|
+
}
|
|
71715
|
+
function claimTeamComputerLifecycle(computer) {
|
|
71716
|
+
if (computerProviderConfigTransitions.has("box")) throw Object.assign(new Error(providerTransitionMessage("box")), { status: 409 });
|
|
71717
|
+
if (teamComputerInUse(computer)) throw Object.assign(new Error("This team computer is in use; stop the team's work and release computer control first"), { status: 409 });
|
|
71718
|
+
return claimBotComputerLifecycle(teamComputerOwner(computer.id));
|
|
71719
|
+
}
|
|
71720
|
+
function assertTeamComputerChangeIdle(before, after) {
|
|
71721
|
+
const previous2 = inheritedTeamComputer(before);
|
|
71722
|
+
const next = inheritedTeamComputer(after);
|
|
71723
|
+
if (previous2?.id === next?.id) return;
|
|
71724
|
+
if (botHasActiveTurn(before.id) || routines?.activeRunForBot(before.id) || botComputerControlSnapshot(before.id).held || [previous2, next].some((computer) => computer && (teamComputerInUse(computer) || boxLifecycleBusyBots.has(teamComputerOwner(computer.id))))) {
|
|
71725
|
+
throw Object.assign(new Error("Stop the affected team's work and release computer control before changing its computer access"), { status: 409 });
|
|
71726
|
+
}
|
|
71727
|
+
}
|
|
71728
|
+
async function teamComputersPayload() {
|
|
71729
|
+
const entries = teamComputers.list();
|
|
71730
|
+
const inventory = await listManagedBoxes(cfg, managedBoxOwners());
|
|
71731
|
+
return {
|
|
71732
|
+
configured: inventory.configured,
|
|
71733
|
+
...inventory.problem ? { problem: inventory.problem } : {},
|
|
71734
|
+
computers: entries.map((entry) => {
|
|
71735
|
+
const machine = inventory.instances.find((instance) => instance.ownerBotId === teamComputerOwner(entry.id));
|
|
71736
|
+
return {
|
|
71737
|
+
id: entry.id,
|
|
71738
|
+
name: entry.name,
|
|
71739
|
+
section: entry.section,
|
|
71740
|
+
held: computerControl.snapshot(teamComputerOwner(entry.id)).held,
|
|
71741
|
+
state: boxLifecycleBusyBots.has(teamComputerOwner(entry.id)) ? "working" : machine?.state ?? (inventory.available ? "missing" : "unavailable"),
|
|
71742
|
+
...entry.problem || inventory.problem ? { problem: entry.problem || inventory.problem } : {}
|
|
71743
|
+
};
|
|
71744
|
+
})
|
|
71745
|
+
};
|
|
71746
|
+
}
|
|
71747
|
+
async function attachTeamBox(computer, botId, owner2, canMount, remoteAgent) {
|
|
71748
|
+
if (!canMount) throw new Error("This model engine cannot use the team's Box computer; choose an engine with computer tools or an explicit bot destination");
|
|
71749
|
+
if (!boxConfigured(cfg)) throw new Error("The team's Box account is not configured; reconnect it in Settings");
|
|
71750
|
+
const ownerId = teamComputerOwner(computer.id);
|
|
71751
|
+
if (boxLifecycleBusyBots.has(ownerId)) throw new Error("The team computer is being changed; wait for it to finish");
|
|
71752
|
+
if (computerControl.snapshot(ownerId).held) throw new Error("Release human control of the team computer before starting another turn");
|
|
71753
|
+
bindTurnComputer(owner2, `computer:box-bot:${ownerId}`, true);
|
|
71754
|
+
teamComputerTurns.set(owner2.threadId, { owner: owner2, computerId: computer.id, botId, remoteAgent });
|
|
71755
|
+
let machine = await findBox(cfg, ownerId);
|
|
71756
|
+
if (!machine) throw new Error("The team's Box computer is missing; explicitly create or retry it from the Team map");
|
|
71757
|
+
bindTurnComputer(owner2, `computer:box:${machine.id}`, true);
|
|
71758
|
+
const action = boxTurnLifecycleAction({ explicitCloud: true, canMount: true, state: typeof machine.state === "string" ? machine.state : null });
|
|
71759
|
+
if (action === "wake") machine = await readyBox(cfg, ownerId);
|
|
71760
|
+
if (!machine || boxTurnLifecycleAction({ explicitCloud: true, canMount: true, state: typeof machine.state === "string" ? machine.state : null }) !== "attach") {
|
|
71761
|
+
throw new Error("The team computer is not ready; check it in the Team map");
|
|
71762
|
+
}
|
|
71763
|
+
if (turnResourceOwners.get(owner2.threadId)?.generation !== owner2.generation || !turnResources.owns(`computer:box:${machine.id}`, owner2)) throw new Error("This computer turn ended while its machine was starting");
|
|
71764
|
+
return {
|
|
71765
|
+
integration: { kind: "box", boxId: machine.id, token: cfg.box.token, control: controlIntegration(botId, owner2.threadId, owner2.generation) },
|
|
71766
|
+
capture: () => screenshotBox(cfg, ownerId, machine.id)
|
|
71767
|
+
};
|
|
71768
|
+
}
|
|
71134
71769
|
function managedBoxOwners() {
|
|
71135
|
-
return store.bots.map((bot) => ({
|
|
71770
|
+
return [...store.bots.map((bot) => ({
|
|
71136
71771
|
botId: bot.id,
|
|
71137
71772
|
name: bot.name,
|
|
71138
71773
|
// A machine is not safe to mutate while any app-level work or human
|
|
71139
71774
|
// control lease still names its owner. This is deliberately conservative
|
|
71140
71775
|
// across destination changes: an old Box may still contain valuable state.
|
|
71141
71776
|
inUse: bot.busy === true || hasDirectDispatch(bot.id) || activeGroupTurnForBot(bot.id) !== null || Boolean(routines?.activeRunForBot(bot.id)) || activeVpsThreads.has(bot.id) || computerControl.snapshot(bot.id).held
|
|
71142
|
-
}))
|
|
71777
|
+
})), ...teamComputers.list().map((computer) => ({
|
|
71778
|
+
botId: teamComputerOwner(computer.id),
|
|
71779
|
+
name: computer.name,
|
|
71780
|
+
inUse: teamComputerInUse(computer)
|
|
71781
|
+
}))];
|
|
71143
71782
|
}
|
|
71144
71783
|
function botHasActiveTurn(botId) {
|
|
71145
71784
|
const bot = store.bot(botId);
|
|
@@ -71172,6 +71811,7 @@ function providerOperationConflict(provider) {
|
|
|
71172
71811
|
}
|
|
71173
71812
|
function turnProvider(bot, runOn2) {
|
|
71174
71813
|
if (runOn2 === "cloud" || registry2.get(bot.modelSelection.instanceId)?.driverKind === "boxAgent") return "box";
|
|
71814
|
+
if (inheritedTeamComputer(bot)) return "box";
|
|
71175
71815
|
if (bot.computer !== void 0 && bot.computer !== "cloud") return null;
|
|
71176
71816
|
return bot.cloudBackend === "vps" ? "vps" : "box";
|
|
71177
71817
|
}
|
|
@@ -71188,6 +71828,8 @@ function claimBoxInventoryRequest(boxId) {
|
|
|
71188
71828
|
}
|
|
71189
71829
|
function claimManagedBoxMutation(instance) {
|
|
71190
71830
|
const ownerBotId = instance.ownerBotId;
|
|
71831
|
+
const teamComputer = teamComputers.list().find((computer) => teamComputerOwner(computer.id) === ownerBotId);
|
|
71832
|
+
if (teamComputer) return claimTeamComputerLifecycle(teamComputer);
|
|
71191
71833
|
if (!ownerBotId) {
|
|
71192
71834
|
if (orphanBoxLifecycleBusyIds.has(instance.boxId)) {
|
|
71193
71835
|
throw Object.assign(new Error("this cloud computer is being changed \u2014 wait for it to finish"), { status: 409 });
|
|
@@ -71387,7 +72029,7 @@ bus.subscribe((event) => {
|
|
|
71387
72029
|
const existing = store.messagesFor(event.threadId).find((m2) => m2.id === messageId)?.tool;
|
|
71388
72030
|
toolName = existing?.name ?? "tool";
|
|
71389
72031
|
store.patchMessage(event.threadId, messageId, {
|
|
71390
|
-
tool: { name: toolName, ok: event.ok,
|
|
72032
|
+
tool: { ...existing, name: toolName, ok: event.ok, output: event.output }
|
|
71391
72033
|
});
|
|
71392
72034
|
toolMessageByItem.delete(itemKey);
|
|
71393
72035
|
}
|
|
@@ -71406,7 +72048,7 @@ bus.subscribe((event) => {
|
|
|
71406
72048
|
const message3 = pushMessage({
|
|
71407
72049
|
role: "bot",
|
|
71408
72050
|
kind: "activity",
|
|
71409
|
-
tool: { name: name2, spoken: narrateTool(name2) ?? void 0, summary: event.summary }
|
|
72051
|
+
tool: { name: name2, spoken: narrateTool(name2) ?? void 0, summary: event.summary, input: event.input }
|
|
71410
72052
|
});
|
|
71411
72053
|
if (event.itemId) toolMessageByItem.set(`${event.threadId}:${event.itemId}`, message3.id);
|
|
71412
72054
|
}
|
|
@@ -71784,7 +72426,7 @@ function markTaskContextExternallyUpdated(bot, threadId) {
|
|
|
71784
72426
|
if (!task2) return;
|
|
71785
72427
|
store.patchTask(bot.id, threadId, {
|
|
71786
72428
|
resumeCursors: {},
|
|
71787
|
-
lastInstanceId: `${EXTERNAL_CONTEXT_MARKER_PREFIX}${
|
|
72429
|
+
lastInstanceId: `${EXTERNAL_CONTEXT_MARKER_PREFIX}${randomUUID27()}`,
|
|
71788
72430
|
unread: true
|
|
71789
72431
|
});
|
|
71790
72432
|
}
|
|
@@ -72010,9 +72652,9 @@ function drainQueuedSends() {
|
|
|
72010
72652
|
store,
|
|
72011
72653
|
(botId, threadId, prompt, userMessage, excludeIds, unattended) => (
|
|
72012
72654
|
// A plain attended turn — no automationSource, no comms depth: exactly
|
|
72013
|
-
// what typing the same words into an idle bot would run.
|
|
72014
|
-
//
|
|
72015
|
-
//
|
|
72655
|
+
// what typing the same words into an idle bot would run. Self-opened
|
|
72656
|
+
// work retains `unattended` and the message's bot-origin provenance
|
|
72657
|
+
// through the wait for a slot.
|
|
72016
72658
|
// Drain just appended the held lines; userMessage keeps startTurn
|
|
72017
72659
|
// from duplicating the last one, and excludeIds drops every drained
|
|
72018
72660
|
// line from the transcript-replay so they are not also in `prompt`.
|
|
@@ -72058,12 +72700,13 @@ async function startOrQueueDirectMessage(botId, threadId, text8, replyTo, sendId
|
|
|
72058
72700
|
}
|
|
72059
72701
|
var MAX_THREADS_OPENED_PER_TURN = 5;
|
|
72060
72702
|
async function startOrQueueOpenedThread(botId, threadId, text8, unattended) {
|
|
72703
|
+
const peerAsk = { botId, name: store.bot(botId).name, unattended: unattended || void 0 };
|
|
72061
72704
|
if (botAtThreadCapacity(botId) || activeGroupTurnForBot(botId)) {
|
|
72062
|
-
queueSteeredMessage(botId, threadId, text8, { reason: "capacity", unattended });
|
|
72705
|
+
queueSteeredMessage(botId, threadId, text8, { reason: "capacity", unattended, peerAsk });
|
|
72063
72706
|
return { state: "queued", position: queuedThreadPosition(botId, threadId) ?? 1 };
|
|
72064
72707
|
}
|
|
72065
72708
|
try {
|
|
72066
|
-
await startTurn(botId, text8, { threadId, unattended });
|
|
72709
|
+
await startTurn(botId, text8, { threadId, unattended, peerAsk });
|
|
72067
72710
|
return { state: "running" };
|
|
72068
72711
|
} catch (error51) {
|
|
72069
72712
|
const why = error51 instanceof Error ? error51.message : String(error51);
|
|
@@ -72098,7 +72741,7 @@ function startScreenPoller(botId, threadId, captures, { screenIsTheWork = false
|
|
|
72098
72741
|
browser: guarded(captures.browser, browserSession ? `browser:${browserSession}` : void 0)
|
|
72099
72742
|
},
|
|
72100
72743
|
control: () => ({
|
|
72101
|
-
held:
|
|
72744
|
+
held: botComputerControlSnapshot(botId, teamComputerTurns.get(threadId)?.computerId).held,
|
|
72102
72745
|
revision: computerControlRevision.get(botId) ?? 0
|
|
72103
72746
|
}),
|
|
72104
72747
|
onFrame: (frame) => broadcast({ kind: "screen", botId, threadId, ...frame }),
|
|
@@ -72238,7 +72881,7 @@ async function startTurn(botId, text8, opts) {
|
|
|
72238
72881
|
userMessage = edited;
|
|
72239
72882
|
}
|
|
72240
72883
|
if (!userMessage) {
|
|
72241
|
-
userMessage = opts?.cardContinuation ? { id: `card-${
|
|
72884
|
+
userMessage = opts?.cardContinuation ? { id: `card-${randomUUID27()}`, at: Date.now(), role: "user", kind: "text", text: text8 } : store.appendMessage(threadId, {
|
|
72242
72885
|
role: "user",
|
|
72243
72886
|
kind: "text",
|
|
72244
72887
|
text: text8,
|
|
@@ -72248,7 +72891,7 @@ async function startTurn(botId, text8, opts) {
|
|
|
72248
72891
|
});
|
|
72249
72892
|
}
|
|
72250
72893
|
if (!opts?.cardContinuation) {
|
|
72251
|
-
if (commsDepth === 0 && opts?.automationSource === void 0 && !opts?.unattended) {
|
|
72894
|
+
if (commsDepth === 0 && opts?.automationSource === void 0 && !opts?.unattended && !userMessage.peerAsk) {
|
|
72252
72895
|
personAskAt.set(threadId, userMessage.at);
|
|
72253
72896
|
if (task2.routineRunId) store.patchTask(bot.id, threadId, { routineRunId: void 0 });
|
|
72254
72897
|
} else {
|
|
@@ -72291,7 +72934,7 @@ async function startTurn(botId, text8, opts) {
|
|
|
72291
72934
|
const drift = checkSoulDrift(bot.id, bot.soul ?? "", bot.soulHash ?? "");
|
|
72292
72935
|
if (drift.drift !== Boolean(bot.soulDrift)) store.patchBot(bot.id, { soulDrift: drift.drift });
|
|
72293
72936
|
}
|
|
72294
|
-
const dispatchClaimId =
|
|
72937
|
+
const dispatchClaimId = randomUUID27();
|
|
72295
72938
|
const resourceOwner = { threadId, generation: dispatchClaimId };
|
|
72296
72939
|
turnResourceOwners.set(threadId, resourceOwner);
|
|
72297
72940
|
directTurnGenerationByThread.set(threadId, dispatchClaimId);
|
|
@@ -72347,13 +72990,14 @@ async function startTurn(botId, text8, opts) {
|
|
|
72347
72990
|
const checkpointCwd = cwd && cwd !== privateWorkspace ? cwd : void 0;
|
|
72348
72991
|
const dwebUrl = process.env.DWEB_URL?.trim();
|
|
72349
72992
|
if (dwebUrl) integrations.dweb = { url: dwebUrl };
|
|
72350
|
-
const
|
|
72993
|
+
const teamComputer = inheritedTeamComputer(bot);
|
|
72994
|
+
const cloudBackend = teamComputer || opts?.runOn === "cloud" || bot.cloudBackend !== "vps" ? "box" : "vps";
|
|
72351
72995
|
const mountsComputerMcp = instance.adapter.capabilities.computerMcp === true;
|
|
72352
72996
|
const mountsCloudComputer = mountsComputerMcp || instance.driverKind === "boxAgent";
|
|
72353
72997
|
const mountsLocalComputer = instance.adapter.capabilities.localComputerMcp === true;
|
|
72354
72998
|
const browserOn = builtInBrowserEnabled(cfg) && bot.browser !== false && instance.adapter.capabilities.browserMcp === true;
|
|
72355
72999
|
const plan = resolveSurface({
|
|
72356
|
-
destination: opts?.runOn === "cloud" ? "cloud" : bot.computer,
|
|
73000
|
+
destination: teamComputer || opts?.runOn === "cloud" ? "cloud" : bot.computer,
|
|
72357
73001
|
// cloud routine overrides the MAUS default
|
|
72358
73002
|
browserOn
|
|
72359
73003
|
});
|
|
@@ -72438,7 +73082,13 @@ async function startTurn(botId, text8, opts) {
|
|
|
72438
73082
|
}
|
|
72439
73083
|
}
|
|
72440
73084
|
}
|
|
72441
|
-
if (
|
|
73085
|
+
if (teamComputer) {
|
|
73086
|
+
const attached = await attachTeamBox(teamComputer, bot.id, resourceOwner, mountsCloudComputer, instance.driverKind === "boxAgent");
|
|
73087
|
+
integrations.computer = attached.integration;
|
|
73088
|
+
previewCapture = attached.capture;
|
|
73089
|
+
computerKind = "box";
|
|
73090
|
+
}
|
|
73091
|
+
if (!teamComputer && (wants2 === "cloud" || wants2 === void 0) && cloudBackend === "box" && boxConfigured(cfg)) {
|
|
72442
73092
|
if (wants2 === "cloud") bindTurnComputer(resourceOwner, `computer:box-bot:${bot.id}`, true);
|
|
72443
73093
|
if (!mountsCloudComputer && wants2 === "cloud") {
|
|
72444
73094
|
throw new Error("this model engine cannot use computer tools \u2014 choose Claude, an ACP engine, or the Computer engine");
|
|
@@ -72506,7 +73156,9 @@ async function startTurn(botId, text8, opts) {
|
|
|
72506
73156
|
}
|
|
72507
73157
|
const sectionPeers = reachablePeers(store.bots, bot);
|
|
72508
73158
|
if (agentsMounted) {
|
|
72509
|
-
|
|
73159
|
+
const origin = opts?.cardContinuation ? store.activePath(threadId).findLast((message3) => message3.role === "user" && message3.kind === "text") : userMessage;
|
|
73160
|
+
const ownThreadCreation = boundedCoordination && !opts?.coordination && Boolean(origin && !origin.peerAsk);
|
|
73161
|
+
integrations.agents = agentsIntegration(bot.id, threadId, commsDepth, skillAuthoring, dispatchClaimId, opts?.coordination?.id, boundedCoordination, ownThreadCreation);
|
|
72510
73162
|
}
|
|
72511
73163
|
const tagged = integrations.agents ? mentionedBots(
|
|
72512
73164
|
providerText,
|
|
@@ -72564,6 +73216,7 @@ async function startTurn(botId, text8, opts) {
|
|
|
72564
73216
|
{ id: "setup", label: "Setup", text: setupSystemPrompt(setupMode, { skills: skillAuthoring, cwd: liveBot?.cwd ?? bot.cwd }) },
|
|
72565
73217
|
{ id: "files", label: "File locations", text: worksInWorkspace && opts?.runOn !== "cloud" ? workspaceLocationsPrompt(bot.id, cwd, liveBot?.cwd ?? bot.cwd) : "" },
|
|
72566
73218
|
{ id: "computer", label: "Computer", text: computerPrompt(computerPromptKind) },
|
|
73219
|
+
{ id: "team-computer", label: "Team computer", text: teamComputerPrompt(teamComputer) },
|
|
72567
73220
|
{ id: "plan", label: "Surface", text: plan.note },
|
|
72568
73221
|
// gated on the integration, not the key: the hint only goes to a
|
|
72569
73222
|
// bot whose driver actually mounted the tools
|
|
@@ -72821,8 +73474,8 @@ async function stopBotForEmergencyApprovalDowngrade(botId) {
|
|
|
72821
73474
|
registry2.get(bot.modelSelection.instanceId)?.adapter.interruptTurn(groupTurn.threadId)
|
|
72822
73475
|
]);
|
|
72823
73476
|
closeOpenApprovals(groupTurn.threadId);
|
|
72824
|
-
const
|
|
72825
|
-
if (
|
|
73477
|
+
const failure5 = results.find((result) => result.status === "rejected");
|
|
73478
|
+
if (failure5) throw failure5.reason;
|
|
72826
73479
|
return;
|
|
72827
73480
|
}
|
|
72828
73481
|
await directStop;
|
|
@@ -73012,7 +73665,7 @@ async function deleteBotWithLifecycle(botId, revalidate = () => {
|
|
|
73012
73665
|
return deletionResponse(409, { error: "stop this bot's Local VM turn or setup action before deleting the bot" });
|
|
73013
73666
|
}
|
|
73014
73667
|
const vm = await containerComputerStatus(void 0, void 0, target);
|
|
73015
|
-
if (!vm.daemonUp &&
|
|
73668
|
+
if (!vm.daemonUp && existsSync31(target.workspaceDir)) {
|
|
73016
73669
|
return deletionResponse(409, {
|
|
73017
73670
|
error: "start the container runtime and delete this bot's Local VM before deleting the bot"
|
|
73018
73671
|
});
|
|
@@ -73110,7 +73763,7 @@ async function deleteBotWithLifecycle(botId, revalidate = () => {
|
|
|
73110
73763
|
}
|
|
73111
73764
|
for (const dir of [EVENTS_DIR, NATIVE_DIR]) {
|
|
73112
73765
|
try {
|
|
73113
|
-
|
|
73766
|
+
unlinkSync13(join61(dir, `${bot.threadId}.ndjson`));
|
|
73114
73767
|
} catch {
|
|
73115
73768
|
}
|
|
73116
73769
|
}
|
|
@@ -73234,6 +73887,12 @@ async function resolveAndSendTeamSetup(res, args, ownerReview) {
|
|
|
73234
73887
|
json3(res, 403, { error: "Approve team setup or deletion from the desktop app or a paired owner device. In a local browser, wait until every bot is idle." });
|
|
73235
73888
|
return true;
|
|
73236
73889
|
}
|
|
73890
|
+
if (args.behavior === "allow" && !card.answered && !card.dismissed) {
|
|
73891
|
+
for (const operation of card.teamSetupRequest.operations) {
|
|
73892
|
+
const before = store.bot(operation.botId);
|
|
73893
|
+
if (before && operation.action === "update") assertTeamComputerChangeIdle(before, { ...before, ...operation.fields });
|
|
73894
|
+
}
|
|
73895
|
+
}
|
|
73237
73896
|
const resumeGeneration = teamSetupResumeGenerations.get(args.threadId) ?? 0;
|
|
73238
73897
|
const resolved = await teamSetupRequests.resolve(args);
|
|
73239
73898
|
if (!resolved) return false;
|
|
@@ -73670,8 +74329,9 @@ async function runGroupMemberTurn(groupId, threadId, botId, hop, spoken = /* @__
|
|
|
73670
74329
|
groupSpeakers.set(threadId, roomSpeaker);
|
|
73671
74330
|
store.patchGroup(readyGroup.id, { busyBotId: bot.id });
|
|
73672
74331
|
orchestration?.onClaimed?.();
|
|
74332
|
+
const roomTeamComputer = inheritedTeamComputer(readyBot);
|
|
73673
74333
|
const roomPlan = resolveSurface({
|
|
73674
|
-
destination: readyBot.computer,
|
|
74334
|
+
destination: roomTeamComputer ? "cloud" : readyBot.computer,
|
|
73675
74335
|
browserOn: builtInBrowserEnabled(cfg) && readyBot.browser !== false && instance.adapter.capabilities.browserMcp === true
|
|
73676
74336
|
});
|
|
73677
74337
|
if (roomPlan.browser) {
|
|
@@ -73683,6 +74343,18 @@ async function runGroupMemberTurn(groupId, threadId, botId, hop, spoken = /* @__
|
|
|
73683
74343
|
if (isCancelled?.() || !browserReadyBot?.busy || groupSpeakers.get(threadId) !== roomSpeaker || activeInternalGenerationByThread.get(threadId) !== internalGeneration) {
|
|
73684
74344
|
return false;
|
|
73685
74345
|
}
|
|
74346
|
+
if (roomTeamComputer) {
|
|
74347
|
+
const attached = await attachTeamBox(
|
|
74348
|
+
roomTeamComputer,
|
|
74349
|
+
readyBot.id,
|
|
74350
|
+
resourceOwner,
|
|
74351
|
+
instance.adapter.capabilities.computerMcp === true || instance.driverKind === "boxAgent",
|
|
74352
|
+
instance.driverKind === "boxAgent"
|
|
74353
|
+
);
|
|
74354
|
+
if (isCancelled?.() || groupSpeakers.get(threadId) !== roomSpeaker || activeInternalGenerationByThread.get(threadId) !== internalGeneration) return false;
|
|
74355
|
+
integrations.computer = attached.integration;
|
|
74356
|
+
startScreenPoller(readyBot.id, threadId, { computer: attached.capture }, { screenIsTheWork: instance.driverKind === "boxAgent" });
|
|
74357
|
+
}
|
|
73686
74358
|
if (readyBot.computer === "vm") {
|
|
73687
74359
|
if (instance.adapter.capabilities.computerMcp !== true || instance.driverKind === "boxAgent") {
|
|
73688
74360
|
throw new Error("this model engine cannot use the Local VM");
|
|
@@ -73754,7 +74426,8 @@ ${cardContinuation}` : ""}`;
|
|
|
73754
74426
|
const roomSystem = buildSystemPrompt(system, store.bot(bot.id)?.soul ?? bot.soul ?? "", [
|
|
73755
74427
|
{ id: "files", label: "File locations", text: workspace ? workspaceLocationsPrompt(bot.id, cwd, readyBot.cwd) : "" },
|
|
73756
74428
|
{ id: "mcp", label: "MCP servers", text: customMcpPrompt(Object.keys(integrations.custom ?? {})) },
|
|
73757
|
-
{ id: "computer", label: "Computer", text: computerPrompt(roomVmTarget ? localVmMode(cfg) === "per-bot" ? "vm-private" : "vm-shared" : null) },
|
|
74429
|
+
{ id: "computer", label: "Computer", text: computerPrompt(roomTeamComputer ? instance.driverKind === "boxAgent" ? "box-agent" : "box" : roomVmTarget ? localVmMode(cfg) === "per-bot" ? "vm-private" : "vm-shared" : null) },
|
|
74430
|
+
{ id: "team-computer", label: "Team computer", text: teamComputerPrompt(roomTeamComputer) },
|
|
73758
74431
|
{ id: "plan", label: "Surface", text: roomPlan.note },
|
|
73759
74432
|
{ id: "browser", label: "Browser", text: integrations.browser ? BUILT_IN_BROWSER_SYSTEM_PROMPT : "" },
|
|
73760
74433
|
{ id: "recall", label: "Recall", text: integrations.agents ? SESSION_SEARCH_SYSTEM_PROMPT : "" },
|
|
@@ -73786,7 +74459,7 @@ ${memorySystemPrompt(bot.id, { managedWrites: Boolean(integrations.agents) }).tr
|
|
|
73786
74459
|
let replyText = "";
|
|
73787
74460
|
let providerTurnId;
|
|
73788
74461
|
let abandoned = false;
|
|
73789
|
-
const retirementOwner = `room-abandoned:${
|
|
74462
|
+
const retirementOwner = `room-abandoned:${randomUUID27()}`;
|
|
73790
74463
|
const abandonProviderTurn = () => {
|
|
73791
74464
|
if (abandoned) return;
|
|
73792
74465
|
abandoned = true;
|
|
@@ -74391,7 +75064,7 @@ function startGroupTurn(groupId, text8, replyTo, sendId, channelMode = "chat", q
|
|
|
74391
75064
|
goalCoordinator ? [] : responders.map((responder) => responder.id)
|
|
74392
75065
|
);
|
|
74393
75066
|
if (goalCoordinator) {
|
|
74394
|
-
const runId = options.goalRunId?.trim() || `goal-${Date.now().toString(36)}-${
|
|
75067
|
+
const runId = options.goalRunId?.trim() || `goal-${Date.now().toString(36)}-${randomUUID27()}`;
|
|
74395
75068
|
const startedAt = Date.now();
|
|
74396
75069
|
const detail = `${goalCoordinator.name} is coordinating this goal.`;
|
|
74397
75070
|
const card = store.appendMessage(threadId, {
|
|
@@ -74650,7 +75323,7 @@ Adds one line to the prompt index; the body is read only when used.${warnings}`,
|
|
|
74650
75323
|
};
|
|
74651
75324
|
}
|
|
74652
75325
|
function appendSkillRequestCard(args) {
|
|
74653
|
-
const requestId =
|
|
75326
|
+
const requestId = randomUUID27();
|
|
74654
75327
|
const copy = skillCardCopy(args.staged);
|
|
74655
75328
|
const payload = {
|
|
74656
75329
|
version: 1,
|
|
@@ -75235,7 +75908,7 @@ async function perBotLocalVmCountForModeChange() {
|
|
|
75235
75908
|
if (targets.length === 0) return 0;
|
|
75236
75909
|
const runtime = await containerRuntimeStatus();
|
|
75237
75910
|
if (!runtime.runtime || !runtime.daemonUp) {
|
|
75238
|
-
return targets.some((target) =>
|
|
75911
|
+
return targets.some((target) => existsSync31(target.workspaceDir)) ? null : 0;
|
|
75239
75912
|
}
|
|
75240
75913
|
return existingPerBotLocalVmCount(runtime.runtime);
|
|
75241
75914
|
}
|
|
@@ -75279,7 +75952,11 @@ function configStatus() {
|
|
|
75279
75952
|
features: {
|
|
75280
75953
|
skillAuthoring: skillAuthoringEnabled(cfg),
|
|
75281
75954
|
showToolCalls: showToolCallsEnabled(cfg),
|
|
75282
|
-
browser: builtInBrowserEnabled(cfg)
|
|
75955
|
+
browser: builtInBrowserEnabled(cfg),
|
|
75956
|
+
// Maintainer-only escape hatch, not a Settings toggle: the desktop
|
|
75957
|
+
// shell and the Settings UI read it so they offer nothing this server
|
|
75958
|
+
// would refuse.
|
|
75959
|
+
sharedComputers: sharedComputersEnabled(cfg)
|
|
75283
75960
|
},
|
|
75284
75961
|
// first-run progress — not a secret; the app decides whether to show
|
|
75285
75962
|
// the welcome tour from this, never from browser storage
|
|
@@ -75411,13 +76088,13 @@ function serveStatic(res, path) {
|
|
|
75411
76088
|
const safe = path === "/" ? "/index.html" : path.replace(/\.\./g, "");
|
|
75412
76089
|
const file2 = join61(STATIC_DIR, safe);
|
|
75413
76090
|
try {
|
|
75414
|
-
const data =
|
|
76091
|
+
const data = readFileSync43(file2);
|
|
75415
76092
|
res.writeHead(200, { "content-type": MIME[extname5(file2)] ?? "application/octet-stream" });
|
|
75416
76093
|
res.end(data);
|
|
75417
76094
|
return true;
|
|
75418
76095
|
} catch {
|
|
75419
76096
|
try {
|
|
75420
|
-
const data =
|
|
76097
|
+
const data = readFileSync43(join61(STATIC_DIR, "index.html"));
|
|
75421
76098
|
res.writeHead(200, { "content-type": "text/html" });
|
|
75422
76099
|
res.end(data);
|
|
75423
76100
|
return true;
|
|
@@ -75493,8 +76170,13 @@ var workspaceBackupRoutes = createWorkspaceBackupRoutes({
|
|
|
75493
76170
|
}).auth;
|
|
75494
76171
|
return Boolean(current2?.scopes.includes("admin") && current2.kind === original.kind && (current2.kind !== "session" || original.kind === "session" && current2.session.id === original.session.id));
|
|
75495
76172
|
},
|
|
75496
|
-
exclusive: (work, keepLocked) => workspaceMaintenance.run(
|
|
75497
|
-
|
|
76173
|
+
exclusive: (work, keepLocked) => workspaceMaintenance.run(async () => {
|
|
76174
|
+
if (keepLocked && teamComputers.list().some((computer) => computer.section !== null)) {
|
|
76175
|
+
throw Object.assign(new Error("Unassign team computers before restoring this workspace"), { status: 409 });
|
|
76176
|
+
}
|
|
76177
|
+
return work();
|
|
76178
|
+
}, {
|
|
76179
|
+
idle: () => !providerFleetReloading && !providerAuthSessions.active && !browserEngineInstall && !routines?.isTicking && !calendarCalls?.isTicking && !localVmImageBusy && !localVmProvisionBusy && !localVmModeChangeBusy && !localVmLifecycleBusy.size && !boxLifecycleBusyBots.size && !vpsPreviewRequests.size && !orphanBoxLifecycleBusyIds.size && !computerProviderConfigTransitions.size && !checkpointRestoreLeases.size && teamComputers.list().every((computer) => !teamComputerInUse(computer)) && store.bots.every((bot) => !botHasActiveTurn(bot.id) && !routines?.activeRunForBot(bot.id) && !botComputerControlSnapshot(bot.id).held) && store.groups.every((group) => !groupIsWorking(group)),
|
|
75498
76180
|
pause: () => {
|
|
75499
76181
|
routines?.stop();
|
|
75500
76182
|
calendarCalls?.stop();
|
|
@@ -75523,9 +76205,28 @@ var handleRequest = async (req, res) => {
|
|
|
75523
76205
|
let m2 = null;
|
|
75524
76206
|
let releaseWorkspaceRequest;
|
|
75525
76207
|
try {
|
|
76208
|
+
if (method === "GET" && path === "/api/health/hosted") {
|
|
76209
|
+
res.setHeader("cache-control", "no-store");
|
|
76210
|
+
const hosted = hostedWorkspaceConfiguration();
|
|
76211
|
+
if (!hosted?.portalMembership || !workspaceAccess || !entitled("admin")) {
|
|
76212
|
+
return json3(res, 503, { error: "Hosted workspace readiness is unavailable." });
|
|
76213
|
+
}
|
|
76214
|
+
res.setHeader(HOSTED_CONTRACT_HEADER, String(HOSTED_CONTRACT_VERSION));
|
|
76215
|
+
return json3(res, 200, { ok: true, service: "openmausbot", membershipAuthority: "portal", workspace: hosted.workspace, ...HOSTED_CONTRACT_METADATA });
|
|
76216
|
+
}
|
|
76217
|
+
if (HOSTED_WORKSPACE) {
|
|
76218
|
+
if (method === "POST" && ["/api/auth/pair", "/api/auth/pairing", "/api/auth/email/start", "/api/auth/email/verify"].includes(path)) {
|
|
76219
|
+
return json3(res, 403, { error: "Sign in through the workspace portal." });
|
|
76220
|
+
}
|
|
76221
|
+
if (workspaceAccess) {
|
|
76222
|
+
if (await workspaceAccess.handlePublic(req, res, url2)) return;
|
|
76223
|
+
} else if (path.startsWith("/api/auth/hosted/") || path === "/pair" || path === "/" && (!isLoopbackHost(req.headers.host) || isProxied(req))) {
|
|
76224
|
+
return json3(res, 503, { error: "Workspace sign-in is unavailable." });
|
|
76225
|
+
}
|
|
76226
|
+
}
|
|
75526
76227
|
if (method === "GET" && !path.startsWith("/api/") && !path.startsWith("/.well-known/") && serveStatic(res, path)) return;
|
|
75527
76228
|
if (method === "GET" && path === "/.well-known/openmausbot/environment") {
|
|
75528
|
-
return json3(res, 200, environmentDescriptor({ environmentId: ENVIRONMENT_ID, desktopManaged: DESKTOP_MANAGED, emailSignIn: emailSignIn.enabled() }));
|
|
76229
|
+
return json3(res, 200, environmentDescriptor({ environmentId: ENVIRONMENT_ID, desktopManaged: DESKTOP_MANAGED, emailSignIn: !HOSTED_WORKSPACE && emailSignIn.enabled(), sharedComputers: sharedComputersEnabled(cfg) }));
|
|
75529
76230
|
}
|
|
75530
76231
|
const domainCheck = /^\/\.well-known\/openmausbot\/domain-check\/([a-f0-9]{64})$/.exec(path);
|
|
75531
76232
|
if (method === "GET" && domainCheck) {
|
|
@@ -75561,7 +76262,7 @@ var handleRequest = async (req, res) => {
|
|
|
75561
76262
|
}
|
|
75562
76263
|
sessions.clearFailures(source);
|
|
75563
76264
|
const issued = sessions.issue({ label: label.trim() || labelFromUserAgent(req.headers["user-agent"]), scopes: verified.scopes, userId: verified.userId, email: verified.email });
|
|
75564
|
-
const environment = environmentDescriptor({ environmentId: ENVIRONMENT_ID, desktopManaged: DESKTOP_MANAGED, emailSignIn: true });
|
|
76265
|
+
const environment = environmentDescriptor({ environmentId: ENVIRONMENT_ID, desktopManaged: DESKTOP_MANAGED, emailSignIn: true, sharedComputers: sharedComputersEnabled(cfg) });
|
|
75565
76266
|
const secure = requestOrigin(req)?.startsWith("https://") === true;
|
|
75566
76267
|
res.setHeader("set-cookie", serializeSessionCookie(SESSION_COOKIE, issued.token, { secure, maxAgeSeconds: cookieMaxAgeSeconds(issued.session) }));
|
|
75567
76268
|
return json3(res, 200, { session: issued.session, environment });
|
|
@@ -75580,7 +76281,7 @@ var handleRequest = async (req, res) => {
|
|
|
75580
76281
|
console.warn(`pairing refused from ${requestSource(req)}: ${result.error}`);
|
|
75581
76282
|
return json3(res, result.status, { error: result.error });
|
|
75582
76283
|
}
|
|
75583
|
-
const environment = environmentDescriptor({ environmentId: ENVIRONMENT_ID, desktopManaged: DESKTOP_MANAGED, emailSignIn: emailSignIn.enabled() });
|
|
76284
|
+
const environment = environmentDescriptor({ environmentId: ENVIRONMENT_ID, desktopManaged: DESKTOP_MANAGED, emailSignIn: emailSignIn.enabled(), sharedComputers: sharedComputersEnabled(cfg) });
|
|
75584
76285
|
if (wantsCookie) {
|
|
75585
76286
|
const secure = requestOrigin(req)?.startsWith("https://") === true;
|
|
75586
76287
|
res.setHeader("set-cookie", serializeSessionCookie(SESSION_COOKIE, result.token, { secure, maxAgeSeconds: cookieMaxAgeSeconds(result.session) }));
|
|
@@ -75594,12 +76295,13 @@ var handleRequest = async (req, res) => {
|
|
|
75594
76295
|
streamPath: "/api/events",
|
|
75595
76296
|
url: url2,
|
|
75596
76297
|
loopbackMutationToken: desktopMutationToken,
|
|
75597
|
-
companionMutationToken
|
|
76298
|
+
companionMutationToken,
|
|
76299
|
+
features: { sharedComputers: sharedComputersEnabled(cfg) }
|
|
75598
76300
|
});
|
|
75599
76301
|
if (gate.auth?.kind === "session" && gate.auth.via === "cookie") {
|
|
75600
76302
|
const presented = parseCookies(req.headers.cookie).get(SESSION_COOKIE);
|
|
75601
76303
|
if (presented) {
|
|
75602
|
-
const secure = requestOrigin(req)?.startsWith("https://") === true;
|
|
76304
|
+
const secure = HOSTED_WORKSPACE || requestOrigin(req)?.startsWith("https://") === true;
|
|
75603
76305
|
res.setHeader("set-cookie", serializeSessionCookie(SESSION_COOKIE, presented, { secure, maxAgeSeconds: cookieMaxAgeSeconds(gate.auth.session) }));
|
|
75604
76306
|
}
|
|
75605
76307
|
}
|
|
@@ -75611,8 +76313,15 @@ var handleRequest = async (req, res) => {
|
|
|
75611
76313
|
}
|
|
75612
76314
|
if (!gate.auth) return json3(res, gate.status, { error: gate.error });
|
|
75613
76315
|
const auth = gate.auth;
|
|
76316
|
+
if (HOSTED_WORKSPACE && auth.kind === "session") {
|
|
76317
|
+
const failure5 = workspaceAccess ? await workspaceAccess.authorize(req, auth) : { status: 503, error: "Workspace sign-in is unavailable." };
|
|
76318
|
+
if (failure5) return json3(res, failure5.status, { error: failure5.error });
|
|
76319
|
+
}
|
|
76320
|
+
if (method === "POST" && path === "/api/workspace-backup/restore" && teamComputers.list().some((computer) => computer.section !== null)) {
|
|
76321
|
+
return json3(res, 409, { error: "Unassign team computers before restoring a workspace; restored team names must not gain access to existing desktops" });
|
|
76322
|
+
}
|
|
75614
76323
|
if (await workspaceBackupRoutes(req, res, path, auth)) return;
|
|
75615
|
-
if (path.startsWith("/api/") && path !== "/api/events" && path !== "/api/health" && !isWorkspaceBackupSessionControl(method, path)) {
|
|
76324
|
+
if (path.startsWith("/api/") && path !== "/api/events" && path !== "/api/health" && !path.startsWith("/api/shared-computers/") && !isWorkspaceBackupSessionControl(method, path)) {
|
|
75616
76325
|
releaseWorkspaceRequest = workspaceMaintenance.request();
|
|
75617
76326
|
}
|
|
75618
76327
|
if (method === "GET" && path === "/api/auth/session") {
|
|
@@ -75694,6 +76403,39 @@ var handleRequest = async (req, res) => {
|
|
|
75694
76403
|
const cancelled = sessions.cancelPairing(m2[1]);
|
|
75695
76404
|
return json3(res, cancelled ? 200 : 404, cancelled ? { ok: true } : { error: "no such pairing code" });
|
|
75696
76405
|
}
|
|
76406
|
+
if (method === "POST" && path === "/api/desktop/shared-computer-control" && sharedComputersEnabled(cfg)) {
|
|
76407
|
+
if (auth.kind !== "loopback") return json3(res, 403, { error: "Local desktop only" });
|
|
76408
|
+
const body = await readBody(req, 1024);
|
|
76409
|
+
if (!sharedComputersEnabled(cfg)) return json3(res, 404, { error: `no route: ${method} ${path}` });
|
|
76410
|
+
if (!external_exports.string().uuid().safeParse(body?.id).success || !["acquire", "release"].includes(body?.action)) return json3(res, 400, { error: "Invalid computer lease" });
|
|
76411
|
+
if (body.action === "release") sharedComputerControl.release(body.id);
|
|
76412
|
+
else sharedComputerControl.acquire(body.id);
|
|
76413
|
+
return json3(res, 200, { ok: true });
|
|
76414
|
+
}
|
|
76415
|
+
if (method === "POST" && path.startsWith("/api/shared-computers/") && sharedComputersEnabled(cfg)) {
|
|
76416
|
+
if (auth.kind !== "session") return json3(res, 403, { error: "Pair this desktop first" });
|
|
76417
|
+
if (!/^application\/json\b/i.test(String(req.headers["content-type"] ?? ""))) return json3(res, 415, { error: "JSON required" });
|
|
76418
|
+
const body = await readBody(req, 4e6);
|
|
76419
|
+
if (!sharedComputersEnabled(cfg)) return json3(res, 404, { error: `no route: ${method} ${path}` });
|
|
76420
|
+
if (!sessions.isLive(auth.session.id)) return json3(res, 401, { error: "Session ended" });
|
|
76421
|
+
const secret = String(req.headers["x-omb-computer-secret"] ?? "");
|
|
76422
|
+
if (path === "/api/shared-computers/connect") {
|
|
76423
|
+
const parsed = sharedComputerRegistration.safeParse(body);
|
|
76424
|
+
if (!parsed.success) return json3(res, 400, { error: "Invalid computer registration" });
|
|
76425
|
+
const registration = parsed.data;
|
|
76426
|
+
if (registration.environmentId !== ENVIRONMENT_ID) return json3(res, 409, { error: "Workspace identity changed. Pair again before sharing this computer." });
|
|
76427
|
+
sharedComputers.register(registration, auth.session.id, secret);
|
|
76428
|
+
return json3(res, 200, { ok: true });
|
|
76429
|
+
}
|
|
76430
|
+
const route = /^\/api\/shared-computers\/([\w-]+)\/(poll|lease|result|disconnect)$/.exec(path);
|
|
76431
|
+
if (!route) return json3(res, 404, { error: "not found" });
|
|
76432
|
+
const [, id, action] = route;
|
|
76433
|
+
if (action === "poll") return json3(res, 200, { job: await sharedComputers.poll(id, auth.session.id, secret) });
|
|
76434
|
+
if (action === "lease") return json3(res, 200, { active: sharedComputers.liveJob(id, auth.session.id, secret, String(body?.jobId)) });
|
|
76435
|
+
if (action === "result") sharedComputers.complete(id, auth.session.id, secret, String(body?.jobId), body?.result);
|
|
76436
|
+
if (action === "disconnect") sharedComputers.disconnect(id, auth.session.id, secret);
|
|
76437
|
+
return json3(res, 200, { ok: true });
|
|
76438
|
+
}
|
|
75697
76439
|
if (method === "GET" && path === "/api/auth/sessions") {
|
|
75698
76440
|
return json3(res, 200, { sessions: sessions.list(), current: auth.kind === "session" ? auth.session.id : null });
|
|
75699
76441
|
}
|
|
@@ -75708,7 +76450,7 @@ var handleRequest = async (req, res) => {
|
|
|
75708
76450
|
const actual = Array.isArray(req.headers["x-openmausbot-test-capability"]) ? "" : String(req.headers["x-openmausbot-test-capability"] ?? "");
|
|
75709
76451
|
const expectedBytes = Buffer.from(expected);
|
|
75710
76452
|
const actualBytes = Buffer.from(actual);
|
|
75711
|
-
if (!expected || actualBytes.length !== expectedBytes.length || !
|
|
76453
|
+
if (!expected || actualBytes.length !== expectedBytes.length || !timingSafeEqual5(actualBytes, expectedBytes)) return json3(res, 404, { error: "not found" });
|
|
75712
76454
|
const parsed = external_exports.object({
|
|
75713
76455
|
botId: external_exports.string().regex(/^[\w-]{1,128}$/),
|
|
75714
76456
|
threadId: external_exports.string().regex(/^[\w-]{1,128}$/),
|
|
@@ -75741,7 +76483,7 @@ var handleRequest = async (req, res) => {
|
|
|
75741
76483
|
if (internalCapability.kind !== requiredCapabilityKind) {
|
|
75742
76484
|
return json3(res, 403, { error: "this internal capability cannot access that service" });
|
|
75743
76485
|
}
|
|
75744
|
-
if (internalCapability.roomCoordination && method === "POST" && ["/api/internal/ask-bot", "/api/internal/delegate-bot"
|
|
76486
|
+
if (internalCapability.roomCoordination && method === "POST" && ["/api/internal/ask-bot", "/api/internal/delegate-bot"].includes(path)) {
|
|
75745
76487
|
return json3(res, 409, { error: "Use coordinate_bots for teamwork; it queues actual teammates and resumes you automatically." });
|
|
75746
76488
|
}
|
|
75747
76489
|
for (const key3 of ["self", "fromBotId", "botId"]) {
|
|
@@ -75819,6 +76561,13 @@ var handleRequest = async (req, res) => {
|
|
|
75819
76561
|
requireActiveInternalCapability();
|
|
75820
76562
|
return json3(res, 200, { result });
|
|
75821
76563
|
}
|
|
76564
|
+
if (method === "GET" && path === "/api/internal/shared-computers" && sharedComputersEnabled(cfg)) return json3(res, 200, { computers: sharedComputers.list() });
|
|
76565
|
+
if (method === "POST" && path === "/api/internal/shared-computers" && sharedComputersEnabled(cfg)) {
|
|
76566
|
+
const parsed = sharedComputerOperation.safeParse(await readInternalBody());
|
|
76567
|
+
if (!sharedComputersEnabled(cfg)) return json3(res, 404, { error: "unknown internal endpoint" });
|
|
76568
|
+
if (!parsed.success) return json3(res, 400, { error: "Invalid shared computer operation" });
|
|
76569
|
+
return json3(res, 200, { result: await sharedComputers.request(parsed.data, () => sharedComputersEnabled(cfg) && internalCapabilityIsActive(internalCapability)) });
|
|
76570
|
+
}
|
|
75822
76571
|
if (method === "GET" && path === "/api/internal/agents") {
|
|
75823
76572
|
const sender = internalSender;
|
|
75824
76573
|
const bots = reachablePeers(store.bots, sender).map((b2) => {
|
|
@@ -75848,6 +76597,7 @@ var handleRequest = async (req, res) => {
|
|
|
75848
76597
|
if (task2.activity === "waiting-on-you") return "waiting-on-you";
|
|
75849
76598
|
if (threadBusy(bot.id, task2.threadId)) return "running";
|
|
75850
76599
|
if (queuedThreadPosition(bot.id, task2.threadId) !== null) return "queued";
|
|
76600
|
+
if (roomHandoffs.activeDirect(task2.threadId)) return "queued";
|
|
75851
76601
|
if (task2.closedBy) return "closed";
|
|
75852
76602
|
return "idle";
|
|
75853
76603
|
};
|
|
@@ -75898,7 +76648,7 @@ var handleRequest = async (req, res) => {
|
|
|
75898
76648
|
return json3(res, 403, { error: "that thread is not yours to close \u2014 only the bot that opened it, or its own bot, can" });
|
|
75899
76649
|
}
|
|
75900
76650
|
if (threadId === fromThreadId) return json3(res, 400, { error: "you cannot close the thread you are speaking in \u2014 finish your turn instead" });
|
|
75901
|
-
if (threadBusy(owner2.id, threadId)) {
|
|
76651
|
+
if (threadBusy(owner2.id, threadId) || queuedThreadPosition(owner2.id, threadId) !== null || roomHandoffs.activeDirect(threadId)) {
|
|
75902
76652
|
return json3(res, 409, { error: `#${task2.title} is still running \u2014 wait for it to finish (list_threads), or the person can stop it from the app` });
|
|
75903
76653
|
}
|
|
75904
76654
|
if (task2.closedBy) {
|
|
@@ -76518,7 +77268,13 @@ var handleRequest = async (req, res) => {
|
|
|
76518
77268
|
const existing = roomHandoffs.children(internalCapability.roomHandoffId ?? internalCapability.generation).find((node3) => node3.key === parsed.data.requestKey + ":" + target.botId);
|
|
76519
77269
|
if (existing) target.threadId = existing.threadId;
|
|
76520
77270
|
else {
|
|
76521
|
-
const task2 = store.createTask(
|
|
77271
|
+
const task2 = store.createTask(
|
|
77272
|
+
target.botId,
|
|
77273
|
+
parsed.data.message,
|
|
77274
|
+
false,
|
|
77275
|
+
void 0,
|
|
77276
|
+
{ botId: internalSender.id, name: internalSender.name, at: Date.now() }
|
|
77277
|
+
);
|
|
76522
77278
|
if (!task2) throw new Error("The recipient no longer exists");
|
|
76523
77279
|
target.threadId = createdThread = task2.threadId;
|
|
76524
77280
|
}
|
|
@@ -76660,15 +77416,23 @@ var handleRequest = async (req, res) => {
|
|
|
76660
77416
|
const toBotId = typeof body.toBotId === "string" && body.toBotId.trim() ? body.toBotId.trim() : from.id;
|
|
76661
77417
|
const target = store.bot(toBotId);
|
|
76662
77418
|
if (!target) return json3(res, 404, { error: "no such bot" });
|
|
76663
|
-
|
|
77419
|
+
if (internalCapability.roomCoordination) {
|
|
77420
|
+
if (target.id !== from.id) {
|
|
77421
|
+
return json3(res, 409, { error: "Use coordinate_bots for teamwork; it queues actual teammates and resumes you automatically." });
|
|
77422
|
+
}
|
|
77423
|
+
if (!internalCapability.ownThreadCreation) {
|
|
77424
|
+
return json3(res, 409, { error: "Only a direct user request can open separate self-owned threads. Finish this assigned work here; use coordinate_bots for teammates." });
|
|
77425
|
+
}
|
|
77426
|
+
}
|
|
77427
|
+
const folder3 = typeof body.folder === "string" ? body.folder.trim() : "";
|
|
76664
77428
|
let projectId;
|
|
76665
|
-
if (
|
|
77429
|
+
if (folder3) {
|
|
76666
77430
|
const project = (target.projects ?? []).find(
|
|
76667
|
-
(candidate) => candidate.id ===
|
|
77431
|
+
(candidate) => candidate.id === folder3 || candidate.name.trim().toLowerCase() === folder3.toLowerCase()
|
|
76668
77432
|
);
|
|
76669
77433
|
if (!project) {
|
|
76670
77434
|
const names = (target.projects ?? []).map((candidate) => candidate.name).join(", ");
|
|
76671
|
-
return json3(res, 400, { error: `@${target.name} has no folder named "${
|
|
77435
|
+
return json3(res, 400, { error: `@${target.name} has no folder named "${folder3}"${names ? ` \u2014 the folders are: ${names}` : " \u2014 it has no folders; leave folder out"}` });
|
|
76672
77436
|
}
|
|
76673
77437
|
projectId = project.id;
|
|
76674
77438
|
}
|
|
@@ -76920,7 +77684,7 @@ ${message3}`;
|
|
|
76920
77684
|
description: `${reason ? `${target.description} ${reason}` : target.description} ${from.name} can use it but never read it back.`,
|
|
76921
77685
|
placeholder: target.placeholder,
|
|
76922
77686
|
helpUrl: target.helpUrl,
|
|
76923
|
-
requestKey:
|
|
77687
|
+
requestKey: randomUUID27()
|
|
76924
77688
|
}
|
|
76925
77689
|
});
|
|
76926
77690
|
return json3(res, 201, { messageId: message3.id, label: target.label });
|
|
@@ -76949,7 +77713,7 @@ ${message3}`;
|
|
|
76949
77713
|
const bot = store.bot(botId);
|
|
76950
77714
|
if (!bot) return json3(res, 404, { error: "no such bot" });
|
|
76951
77715
|
if (method === "GET") {
|
|
76952
|
-
const snapshot2 =
|
|
77716
|
+
const snapshot2 = botComputerControlSnapshot(botId, internalCapability.teamComputerId);
|
|
76953
77717
|
const computer = turnComputerResources.get(internalCapability.threadId);
|
|
76954
77718
|
if (!snapshot2.held && computer && computer.owner.generation === internalCapability.generation && !claimTurnResource(computer.owner, computer.resource)) {
|
|
76955
77719
|
return json3(res, 200, {
|
|
@@ -76962,7 +77726,8 @@ ${message3}`;
|
|
|
76962
77726
|
}
|
|
76963
77727
|
if (method === "POST") {
|
|
76964
77728
|
const body = await readInternalBody();
|
|
76965
|
-
const
|
|
77729
|
+
const controlKey = internalCapability.teamComputerId ? teamComputerOwner(internalCapability.teamComputerId) : botId;
|
|
77730
|
+
const { snapshot: snapshot2, requestId } = computerControl.requestHelpLease(controlKey, body.reason);
|
|
76966
77731
|
const roomTurn = activeGroupTurnForBot(bot.id);
|
|
76967
77732
|
const target = blockedTarget({ ...bot, threadId: internalCapability.threadId }, roomTurn && { ...roomTurn.group, threadId: roomTurn.threadId });
|
|
76968
77733
|
notify(
|
|
@@ -76974,7 +77739,7 @@ ${message3}`;
|
|
|
76974
77739
|
}
|
|
76975
77740
|
if (method === "DELETE") {
|
|
76976
77741
|
const body = await readInternalBody();
|
|
76977
|
-
const snapshot2 = computerControl.expireHelp(botId, body.requestId);
|
|
77742
|
+
const snapshot2 = computerControl.expireHelp(internalCapability.teamComputerId ? teamComputerOwner(internalCapability.teamComputerId) : botId, body.requestId);
|
|
76978
77743
|
return json3(res, 200, { held: snapshot2.held, helpOpen: snapshot2.helpReason !== null });
|
|
76979
77744
|
}
|
|
76980
77745
|
return json3(res, 405, { error: "method not allowed" });
|
|
@@ -77173,6 +77938,10 @@ ${message3}`;
|
|
|
77173
77938
|
}
|
|
77174
77939
|
const browser = await browserIntegration(bot.id, bot.browserProfile);
|
|
77175
77940
|
if (!browser) return json3(res, 503, { error: "Install the browser engine first." });
|
|
77941
|
+
if (HOSTED_WORKSPACE && auth.kind === "session") {
|
|
77942
|
+
const failure5 = workspaceAccess ? await workspaceAccess.authorize(req, auth) : { status: 503, error: "Workspace sign-in is unavailable." };
|
|
77943
|
+
if (failure5) return json3(res, failure5.status, { error: failure5.error });
|
|
77944
|
+
}
|
|
77176
77945
|
const owner2 = auth.kind === "session" ? auth.session.id : "local-owner";
|
|
77177
77946
|
const isCurrent = () => {
|
|
77178
77947
|
const current2 = store.bot(bot.id);
|
|
@@ -77270,7 +78039,7 @@ data: ${JSON.stringify({ kind: "ping" })}
|
|
|
77270
78039
|
groups: store.groups.map((g2) => ({ ...publicGroupState(g2), ...messagePage(g2.threadId, limit) })),
|
|
77271
78040
|
computerControl: Object.fromEntries(
|
|
77272
78041
|
store.bots.map((bot) => {
|
|
77273
|
-
const snapshot2 =
|
|
78042
|
+
const snapshot2 = botComputerControlSnapshot(bot.id);
|
|
77274
78043
|
return [bot.id, { held: snapshot2.held, helpReason: snapshot2.helpReason }];
|
|
77275
78044
|
})
|
|
77276
78045
|
)
|
|
@@ -77974,7 +78743,7 @@ data: ${JSON.stringify({ kind: "ping" })}
|
|
|
77974
78743
|
for (const threadId of threadIds) {
|
|
77975
78744
|
for (const dir of [EVENTS_DIR, NATIVE_DIR]) {
|
|
77976
78745
|
try {
|
|
77977
|
-
|
|
78746
|
+
unlinkSync13(join61(dir, `${threadId}.ndjson`));
|
|
77978
78747
|
} catch {
|
|
77979
78748
|
}
|
|
77980
78749
|
}
|
|
@@ -78128,6 +78897,7 @@ data: ${JSON.stringify({ kind: "ping" })}
|
|
|
78128
78897
|
if (path === "/api/sidebar-sections" && (method === "PATCH" || method === "DELETE")) {
|
|
78129
78898
|
const section2 = url2.searchParams.get("section")?.trim();
|
|
78130
78899
|
if (!section2) return json3(res, 400, { error: "Choose a named team" });
|
|
78900
|
+
if (teamComputers.forSection(section2)) return json3(res, 409, { error: "Unassign this team's computer before renaming or deleting the team" });
|
|
78131
78901
|
let nextName = null;
|
|
78132
78902
|
if (method === "PATCH") {
|
|
78133
78903
|
const parsed = external_exports.object({ name: external_exports.string().trim().min(1).max(60) }).strict().safeParse(await readBody(req));
|
|
@@ -78149,6 +78919,10 @@ data: ${JSON.stringify({ kind: "ping" })}
|
|
|
78149
78919
|
}
|
|
78150
78920
|
const botIds = [...new Set(parsed.data.botIds)];
|
|
78151
78921
|
if (!name2 && !botIds.length) return json3(res, 400, { error: "Team name is required" });
|
|
78922
|
+
for (const botId of botIds) {
|
|
78923
|
+
const bot = store.bot(botId);
|
|
78924
|
+
if (bot) assertTeamComputerChangeIdle(bot, { ...bot, section: name2 || void 0 });
|
|
78925
|
+
}
|
|
78152
78926
|
const result = store.setBotsSection(botIds, name2);
|
|
78153
78927
|
if (!result.ok) {
|
|
78154
78928
|
if (result.reason === "chief-conflict") {
|
|
@@ -78637,11 +79411,13 @@ data: ${JSON.stringify({ kind: "ping" })}
|
|
|
78637
79411
|
return json3(res, 409, { error: "Stop the bot and release browser control before changing its profile." });
|
|
78638
79412
|
}
|
|
78639
79413
|
if (profile.patch.soul !== void 0) {
|
|
79414
|
+
if (freshBrowserBot) assertTeamComputerChangeIdle(freshBrowserBot, { ...freshBrowserBot, ...patch });
|
|
78640
79415
|
const runtimePatch = { ...patch };
|
|
78641
79416
|
for (const field of Object.keys(profile.patch)) delete runtimePatch[field];
|
|
78642
79417
|
if (Object.keys(runtimePatch).length) store.patchBot(m2[1], runtimePatch);
|
|
78643
79418
|
bot = store.patchBotProfile(m2[1], profile.patch);
|
|
78644
79419
|
} else {
|
|
79420
|
+
if (freshBrowserBot) assertTeamComputerChangeIdle(freshBrowserBot, { ...freshBrowserBot, ...patch });
|
|
78645
79421
|
bot = store.patchBot(m2[1], patch);
|
|
78646
79422
|
}
|
|
78647
79423
|
if (!bot) return json3(res, 404, { error: "no such bot" });
|
|
@@ -79523,6 +80299,104 @@ data: ${JSON.stringify({ kind: "ping" })}
|
|
|
79523
80299
|
broadcast({ kind: "bot", bot: fresh });
|
|
79524
80300
|
return json3(res, 200, { bot: fresh });
|
|
79525
80301
|
}
|
|
80302
|
+
if (path === "/api/team-computers" && method === "GET") {
|
|
80303
|
+
res.setHeader("cache-control", "private, no-store");
|
|
80304
|
+
return json3(res, 200, await teamComputersPayload());
|
|
80305
|
+
}
|
|
80306
|
+
m2 = path.match(/^\/api\/team-computers(?:\/([\w-]+)(?:\/(provision|join|sleep|control))?)?$/);
|
|
80307
|
+
if (m2) {
|
|
80308
|
+
const computerId = m2[1];
|
|
80309
|
+
const action = m2[2];
|
|
80310
|
+
let found = computerId ? teamComputers.get(computerId) : void 0;
|
|
80311
|
+
if (computerId && !found) return json3(res, 404, { error: "No such team computer" });
|
|
80312
|
+
if (method === "GET" && action === "control" && found) {
|
|
80313
|
+
return json3(res, 200, computerControl.snapshot(teamComputerOwner(found.id)));
|
|
80314
|
+
}
|
|
80315
|
+
if (!String(req.headers["content-type"] ?? "").toLowerCase().startsWith("application/json")) return json3(res, 415, { error: "content-type must be application/json" });
|
|
80316
|
+
const body = await readBody(req);
|
|
80317
|
+
found = computerId ? teamComputers.get(computerId) : void 0;
|
|
80318
|
+
const assertCurrentOwner = () => {
|
|
80319
|
+
if (auth.kind === "session" && !sessions.isLive(auth.session.id)) throw Object.assign(new Error("Your session ended; sign in again"), { status: 401 });
|
|
80320
|
+
if (computerProviderConfigTransitions.has("box")) throw Object.assign(new Error(providerTransitionMessage("box")), { status: 409 });
|
|
80321
|
+
};
|
|
80322
|
+
assertCurrentOwner();
|
|
80323
|
+
if (action === "control" && method === "POST" && found) {
|
|
80324
|
+
const parsed = external_exports.object({ action: external_exports.enum(["take", "release"]), controlLeaseId: controlLeaseIdSchema.optional() }).strict().safeParse(body);
|
|
80325
|
+
if (!parsed.success) return json3(res, 400, { error: "Choose take or release with a valid optional controlLeaseId" });
|
|
80326
|
+
const key3 = teamComputerOwner(found.id);
|
|
80327
|
+
if (parsed.data.action === "take" && boxLifecycleBusyBots.has(key3)) return json3(res, 409, { error: "Wait for this computer's action to finish before taking control" });
|
|
80328
|
+
if (parsed.data.action === "take") assertTeamControlCanBeTaken(found.id);
|
|
80329
|
+
if (parsed.data.controlLeaseId) {
|
|
80330
|
+
const result = parsed.data.action === "take" ? computerControl.acquireLease(key3, parsed.data.controlLeaseId) : computerControl.releaseLease(key3, parsed.data.controlLeaseId);
|
|
80331
|
+
return json3(res, 200, { ...result.snapshot, ...result });
|
|
80332
|
+
}
|
|
80333
|
+
return json3(res, 200, parsed.data.action === "take" ? computerControl.take(key3) : computerControl.release(key3));
|
|
80334
|
+
}
|
|
80335
|
+
if (method === "PATCH" && found && !action) {
|
|
80336
|
+
const parsed = teamComputerAssignment.safeParse(body);
|
|
80337
|
+
if (!parsed.success) return json3(res, 400, { error: "Confirm shared desktop access with section and acknowledgeSharedAccess: true" });
|
|
80338
|
+
const section2 = parsed.data.section;
|
|
80339
|
+
const checkAssignment = () => {
|
|
80340
|
+
assertCurrentOwner();
|
|
80341
|
+
if (section2 !== null && section2 !== "" && !store.sections.includes(section2)) throw Object.assign(new Error("Create the team before assigning a computer"), { status: 404 });
|
|
80342
|
+
if (section2 !== null && store.bots.some((bot) => sectionKey3(bot.section) === section2 && (botHasActiveTurn(bot.id) || routines?.activeRunForBot(bot.id) || botComputerControlSnapshot(bot.id).held || boxLifecycleBusyBots.has(bot.id)))) throw Object.assign(new Error("Stop the target team's work and release computer control before assigning this computer"), { status: 409 });
|
|
80343
|
+
};
|
|
80344
|
+
checkAssignment();
|
|
80345
|
+
const release = claimTeamComputerLifecycle(found);
|
|
80346
|
+
try {
|
|
80347
|
+
if (section2 !== null && !await findBox(cfg, teamComputerOwner(found.id))) return json3(res, 409, { error: "Create or retry this computer before assigning it to a team" });
|
|
80348
|
+
checkAssignment();
|
|
80349
|
+
if (teamComputerInUse(found)) return json3(res, 409, { error: "This team computer became busy; stop its work before assigning it" });
|
|
80350
|
+
teamComputers.assign(found.id, section2);
|
|
80351
|
+
return json3(res, 200, { ok: true });
|
|
80352
|
+
} finally {
|
|
80353
|
+
release();
|
|
80354
|
+
}
|
|
80355
|
+
}
|
|
80356
|
+
if (method === "POST" && !computerId) {
|
|
80357
|
+
const parsed = teamComputerCreate.safeParse(body);
|
|
80358
|
+
if (!parsed.success) return json3(res, 400, { error: "Provide requestId (UUID), a name, and acknowledgeCost: true to create a paid Box" });
|
|
80359
|
+
if (!boxConfigured(cfg)) return json3(res, 409, { error: "Configure Box in Settings before creating a cloud computer" });
|
|
80360
|
+
const computer = teamComputers.create(parsed.data.name, parsed.data.requestId);
|
|
80361
|
+
const release = claimTeamComputerLifecycle(computer);
|
|
80362
|
+
try {
|
|
80363
|
+
await provisionBox(cfg, teamComputerOwner(computer.id), computer.name);
|
|
80364
|
+
teamComputers.setProblem(computer.id);
|
|
80365
|
+
return json3(res, 201, { id: computer.id });
|
|
80366
|
+
} catch (error51) {
|
|
80367
|
+
teamComputers.setProblem(computer.id, redactSecretsInText(error51 instanceof Error ? error51.message : String(error51)));
|
|
80368
|
+
throw error51;
|
|
80369
|
+
} finally {
|
|
80370
|
+
release();
|
|
80371
|
+
}
|
|
80372
|
+
}
|
|
80373
|
+
if (method === "POST" && found && action && action !== "control") {
|
|
80374
|
+
if (action === "provision" && body?.acknowledgeCost !== true) return json3(res, 400, { error: "Confirm Box creation or wake costs with acknowledgeCost: true" });
|
|
80375
|
+
if (action === "join") {
|
|
80376
|
+
const key3 = teamComputerOwner(found.id);
|
|
80377
|
+
if (boxLifecycleBusyBots.has(key3)) return json3(res, 409, { error: "Wait for this computer's action to finish" });
|
|
80378
|
+
if (!computerControl.snapshot(key3).held) return json3(res, 409, { error: "Take control before opening this shared desktop" });
|
|
80379
|
+
const release2 = claimBotComputerLifecycle(key3);
|
|
80380
|
+
try {
|
|
80381
|
+
return json3(res, 200, await joinReadyBox(cfg, key3));
|
|
80382
|
+
} finally {
|
|
80383
|
+
release2();
|
|
80384
|
+
}
|
|
80385
|
+
}
|
|
80386
|
+
const release = claimTeamComputerLifecycle(found);
|
|
80387
|
+
try {
|
|
80388
|
+
const result = action === "provision" ? await provisionBox(cfg, teamComputerOwner(found.id), found.name) : await sleepBox(cfg, teamComputerOwner(found.id));
|
|
80389
|
+
teamComputers.setProblem(found.id);
|
|
80390
|
+
return json3(res, 200, result);
|
|
80391
|
+
} catch (error51) {
|
|
80392
|
+
teamComputers.setProblem(found.id, redactSecretsInText(error51 instanceof Error ? error51.message : String(error51)));
|
|
80393
|
+
throw error51;
|
|
80394
|
+
} finally {
|
|
80395
|
+
release();
|
|
80396
|
+
}
|
|
80397
|
+
}
|
|
80398
|
+
return json3(res, 405, { error: "method not allowed" });
|
|
80399
|
+
}
|
|
79526
80400
|
if (method === "GET" && path === "/api/computers/boxes") {
|
|
79527
80401
|
res.setHeader("cache-control", "private, no-store");
|
|
79528
80402
|
return json3(res, 200, await listManagedBoxes(cfg, managedBoxOwners()));
|
|
@@ -79804,8 +80678,10 @@ data: ${JSON.stringify({ kind: "ping" })}
|
|
|
79804
80678
|
}
|
|
79805
80679
|
const kind = provider;
|
|
79806
80680
|
const saved = kind === "anthropic" ? cfg.anthropic : kind === "openaiCompat" ? cfg.openaiCompat : cfg.xai;
|
|
79807
|
-
|
|
79808
|
-
|
|
80681
|
+
if (body?.key !== void 0 && typeof body.key !== "string") {
|
|
80682
|
+
return json3(res, 400, { error: "key must be a string" });
|
|
80683
|
+
}
|
|
80684
|
+
const key3 = typeof body?.key === "string" ? body.key.trim() : saved?.key?.trim() || "";
|
|
79809
80685
|
if (!key3) return json3(res, 400, { error: "No key to test. Paste one or save one first." });
|
|
79810
80686
|
if (key3.length > 512) return json3(res, 400, { error: "That does not look like an API key." });
|
|
79811
80687
|
const url3 = typeof body?.url === "string" && body.url.trim() ? body.url.trim() : saved?.url;
|
|
@@ -80416,6 +81292,11 @@ data: ${JSON.stringify({ kind: "ping" })}
|
|
|
80416
81292
|
throw error51;
|
|
80417
81293
|
}
|
|
80418
81294
|
let browserReferenceCleanupError = null;
|
|
81295
|
+
if (patch.signIn !== void 0) sessions.revalidateEmailSessions();
|
|
81296
|
+
if (!sharedComputersEnabled(cfg)) {
|
|
81297
|
+
sharedComputers.close();
|
|
81298
|
+
sharedComputerControl.close();
|
|
81299
|
+
}
|
|
80419
81300
|
if (disablingBuiltInBrowser) browserLive.closeAll();
|
|
80420
81301
|
for (const request2 of browserCleanupRequests) {
|
|
80421
81302
|
if (request2.kind === "profile") browserLive.closeForSession(browserSessionId("", request2.partitionId));
|
|
@@ -80670,29 +81551,36 @@ data: ${JSON.stringify({ kind: "ping" })}
|
|
|
80670
81551
|
if (m2 && method === "GET") {
|
|
80671
81552
|
const bot = store.bot(m2[1]);
|
|
80672
81553
|
if (!bot) return json3(res, 404, { error: "no such bot" });
|
|
81554
|
+
const teamComputer = inheritedTeamComputer(bot);
|
|
81555
|
+
if (teamComputer) return json3(res, 200, { backend: "box", teamComputer: { id: teamComputer.id, name: teamComputer.name }, ...await boxStatus(cfg, teamComputerOwner(teamComputer.id)) });
|
|
80673
81556
|
return bot.cloudBackend === "vps" ? json3(res, 200, { backend: "vps", ...await vpsComputerStatus(cfg, bot.id) }) : json3(res, 200, { backend: "box", ...await boxStatus(cfg, bot.id) });
|
|
80674
81557
|
}
|
|
80675
81558
|
m2 = path.match(/^\/api\/bots\/([\w-]+)\/computer\/control$/);
|
|
80676
81559
|
if (m2) {
|
|
80677
81560
|
const bot = store.bot(m2[1]);
|
|
80678
81561
|
if (!bot) return json3(res, 404, { error: "no such bot" });
|
|
80679
|
-
if (method === "GET") return json3(res, 200,
|
|
81562
|
+
if (method === "GET") return json3(res, 200, botComputerControlSnapshot(bot.id));
|
|
80680
81563
|
if (method === "POST") {
|
|
80681
81564
|
if (!String(req.headers["content-type"] ?? "").toLowerCase().startsWith("application/json")) {
|
|
80682
81565
|
return json3(res, 415, { error: "content-type must be application/json" });
|
|
80683
81566
|
}
|
|
80684
81567
|
const body = await readBody(req);
|
|
80685
81568
|
const action = String(body.action ?? "");
|
|
81569
|
+
const currentBot = store.bot(bot.id);
|
|
81570
|
+
if (!currentBot) return json3(res, 404, { error: "no such bot" });
|
|
81571
|
+
const controlKey = botComputerControlKey(currentBot);
|
|
81572
|
+
const teamComputer = inheritedTeamComputer(currentBot);
|
|
81573
|
+
if (action === "take" && teamComputer) assertTeamControlCanBeTaken(teamComputer.id);
|
|
80686
81574
|
const leaseResult = body.controlLeaseId === void 0 ? null : controlLeaseIdSchema.safeParse(body.controlLeaseId);
|
|
80687
81575
|
if (leaseResult && !leaseResult.success) {
|
|
80688
81576
|
return json3(res, 400, { error: "controlLeaseId is invalid" });
|
|
80689
81577
|
}
|
|
80690
81578
|
const controlLeaseId = leaseResult?.data;
|
|
80691
|
-
if (action === "take" && boxLifecycleBusyBots.has(bot.id)) {
|
|
81579
|
+
if (action === "take" && (boxLifecycleBusyBots.has(bot.id) || boxLifecycleBusyBots.has(controlKey))) {
|
|
80692
81580
|
return json3(res, 409, { error: "this bot's cloud computer is being changed \u2014 wait before taking control" });
|
|
80693
81581
|
}
|
|
80694
81582
|
if (action === "take" && controlLeaseId) {
|
|
80695
|
-
const result = computerControl.acquireLease(
|
|
81583
|
+
const result = computerControl.acquireLease(controlKey, controlLeaseId);
|
|
80696
81584
|
return json3(res, 200, {
|
|
80697
81585
|
...result.snapshot,
|
|
80698
81586
|
owned: result.owned,
|
|
@@ -80700,12 +81588,12 @@ data: ${JSON.stringify({ kind: "ping" })}
|
|
|
80700
81588
|
});
|
|
80701
81589
|
}
|
|
80702
81590
|
if (action === "release" && controlLeaseId) {
|
|
80703
|
-
const result = computerControl.releaseLease(
|
|
81591
|
+
const result = computerControl.releaseLease(controlKey, controlLeaseId);
|
|
80704
81592
|
return json3(res, 200, { ...result.snapshot, released: result.released });
|
|
80705
81593
|
}
|
|
80706
|
-
if (action === "take") return json3(res, 200, computerControl.take(
|
|
80707
|
-
if (action === "release") return json3(res, 200, computerControl.release(
|
|
80708
|
-
if (action === "dismiss-help") return json3(res, 200, computerControl.dismissHelp(
|
|
81594
|
+
if (action === "take") return json3(res, 200, computerControl.take(controlKey));
|
|
81595
|
+
if (action === "release") return json3(res, 200, computerControl.release(controlKey));
|
|
81596
|
+
if (action === "dismiss-help") return json3(res, 200, computerControl.dismissHelp(controlKey));
|
|
80709
81597
|
return json3(res, 400, { error: "action must be take, release, or dismiss-help" });
|
|
80710
81598
|
}
|
|
80711
81599
|
return json3(res, 405, { error: "method not allowed" });
|
|
@@ -80734,6 +81622,22 @@ data: ${JSON.stringify({ kind: "ping" })}
|
|
|
80734
81622
|
if (boxLifecycleBusyBots.has(botId)) {
|
|
80735
81623
|
return json3(res, 409, { error: "this bot's cloud computer is being changed \u2014 wait for it to finish" });
|
|
80736
81624
|
}
|
|
81625
|
+
const teamComputer = inheritedTeamComputer(bot);
|
|
81626
|
+
if (teamComputer) {
|
|
81627
|
+
const key3 = teamComputerOwner(teamComputer.id);
|
|
81628
|
+
if (boxLifecycleBusyBots.has(key3)) return json3(res, 409, { error: "This team computer is being changed; wait for it to finish" });
|
|
81629
|
+
if (m2[2] === "provision" || m2[2] === "remove") return json3(res, 409, { error: "Manage this shared computer from the Team map" });
|
|
81630
|
+
if (m2[2] === "exec") return json3(res, 409, { error: "Use the bot's scoped computer tools for this shared desktop" });
|
|
81631
|
+
if (m2[2] === "join" && !computerControl.snapshot(key3).held) return json3(res, 409, { error: "Take control before opening this shared desktop" });
|
|
81632
|
+
const release = m2[2] === "sleep" ? claimTeamComputerLifecycle(teamComputer) : claimBotComputerLifecycle(key3);
|
|
81633
|
+
try {
|
|
81634
|
+
if (m2[2] === "join") return json3(res, 200, await joinReadyBox(cfg, key3));
|
|
81635
|
+
if (m2[2] === "screenshot") return json3(res, 200, await screenshotBox(cfg, key3));
|
|
81636
|
+
return json3(res, 200, await sleepBox(cfg, key3));
|
|
81637
|
+
} finally {
|
|
81638
|
+
release();
|
|
81639
|
+
}
|
|
81640
|
+
}
|
|
80737
81641
|
if (bot.cloudBackend === "vps") {
|
|
80738
81642
|
if (m2[2] === "screenshot") {
|
|
80739
81643
|
let preview = vpsPreviewRequests.get(botId);
|
|
@@ -80818,6 +81722,11 @@ data: ${JSON.stringify({ kind: "ping" })}
|
|
|
80818
81722
|
var server = createServer3(handleRequest);
|
|
80819
81723
|
calendarCalls.start();
|
|
80820
81724
|
console.log(describeEdition(await loadEnterpriseLayer()));
|
|
81725
|
+
workspaceAccess = createWorkspaceAccess({ sessions, cookieName: SESSION_COOKIE, closeSessionStreams });
|
|
81726
|
+
var workspaceAccessTimer = workspaceAccess ? setInterval(() => {
|
|
81727
|
+
void workspaceAccess.revalidate().catch((error51) => console.warn("workspace access revalidation failed", error51));
|
|
81728
|
+
}, 1e4) : null;
|
|
81729
|
+
workspaceAccessTimer?.unref();
|
|
80821
81730
|
console.log(describeBrand(loadBrand()));
|
|
80822
81731
|
try {
|
|
80823
81732
|
const reclaimedPartials = cleanupStaleAttachmentPartials();
|
|
@@ -80886,7 +81795,10 @@ var gracefulShutdown = createGracefulShutdown({
|
|
|
80886
81795
|
cleanup: [
|
|
80887
81796
|
() => {
|
|
80888
81797
|
followupsReady = false;
|
|
81798
|
+
if (workspaceAccessTimer) clearInterval(workspaceAccessTimer);
|
|
80889
81799
|
revokeAllInternalCapabilities();
|
|
81800
|
+
sharedComputers.close();
|
|
81801
|
+
sharedComputerControl.close();
|
|
80890
81802
|
browserLive.closeAll();
|
|
80891
81803
|
for (const idle of localVmIdles.values()) idle.cancel();
|
|
80892
81804
|
closeAllVpsDesktopTunnels();
|
|
@@ -80910,6 +81822,12 @@ var gracefulShutdown = createGracefulShutdown({
|
|
|
80910
81822
|
// the shutdown deadline), immediately before the process exits, so no new
|
|
80911
81823
|
// server can overlap with a still-mutating old one.
|
|
80912
81824
|
exit: (code) => {
|
|
81825
|
+
try {
|
|
81826
|
+
sessions.close();
|
|
81827
|
+
} catch {
|
|
81828
|
+
console.error("Session persistence failed during shutdown; account sign-in will be required again.");
|
|
81829
|
+
code = 1;
|
|
81830
|
+
}
|
|
80913
81831
|
closeMessageDb();
|
|
80914
81832
|
releaseDataDirLeaseAtExit();
|
|
80915
81833
|
process.exit(code);
|