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
|
@@ -17619,9 +17619,9 @@ var init_schema = __esm({
|
|
|
17619
17619
|
});
|
|
17620
17620
|
|
|
17621
17621
|
// server/config.ts
|
|
17622
|
-
import { readFileSync as
|
|
17622
|
+
import { readFileSync as readFileSync3, mkdirSync as mkdirSync6, existsSync as existsSync5, renameSync as renameSync3 } from "node:fs";
|
|
17623
17623
|
import { homedir as homedir4 } from "node:os";
|
|
17624
|
-
import { join as
|
|
17624
|
+
import { join as join6 } from "node:path";
|
|
17625
17625
|
function isValidSshAlias(value) {
|
|
17626
17626
|
return typeof value === "string" && SSH_ALIAS.test(value);
|
|
17627
17627
|
}
|
|
@@ -17754,7 +17754,7 @@ function parseStoredConfig(value) {
|
|
|
17754
17754
|
function loadConfig() {
|
|
17755
17755
|
let cfg = {};
|
|
17756
17756
|
try {
|
|
17757
|
-
cfg = parseStoredConfig(parseJson(
|
|
17757
|
+
cfg = parseStoredConfig(parseJson(readFileSync3(join6(DATA_DIR, "config.json"), "utf8")));
|
|
17758
17758
|
} catch {
|
|
17759
17759
|
}
|
|
17760
17760
|
cfg.xai = { ...cfg.xai };
|
|
@@ -17790,10 +17790,10 @@ function stripWorkspaceCredentialEnv(env) {
|
|
|
17790
17790
|
for (const key of WORKSPACE_CREDENTIAL_ENV) delete env[key];
|
|
17791
17791
|
}
|
|
17792
17792
|
function saveConfig(patch, options = {}) {
|
|
17793
|
-
const p =
|
|
17793
|
+
const p = join6(DATA_DIR, "config.json");
|
|
17794
17794
|
let disk = {};
|
|
17795
17795
|
try {
|
|
17796
|
-
const parsed = jsonObjectSchema.safeParse(parseJson(
|
|
17796
|
+
const parsed = jsonObjectSchema.safeParse(parseJson(readFileSync3(p, "utf8")));
|
|
17797
17797
|
if (parsed.success) disk = parsed.data;
|
|
17798
17798
|
} catch {
|
|
17799
17799
|
}
|
|
@@ -17995,7 +17995,11 @@ var init_config = __esm({
|
|
|
17995
17995
|
showToolCalls: external_exports.boolean().optional(),
|
|
17996
17996
|
/** Experimental built-in browser. Off until explicitly enabled; each bot
|
|
17997
17997
|
* also has its own switch. */
|
|
17998
|
-
browser: external_exports.boolean().optional()
|
|
17998
|
+
browser: external_exports.boolean().optional(),
|
|
17999
|
+
/** Opt-in computer sharing (a desktop lending folders, a terminal or
|
|
18000
|
+
* computer control to a workspace). Off until explicitly enabled; there is
|
|
18001
|
+
* no Settings toggle — see sharedComputersEnabled. */
|
|
18002
|
+
sharedComputers: external_exports.boolean().optional()
|
|
17999
18003
|
});
|
|
18000
18004
|
onboardingConfigSchema = external_exports.object({
|
|
18001
18005
|
/** ISO timestamp of finishing (or skipping to the end of) the welcome flow. */
|
|
@@ -18112,10 +18116,10 @@ var init_config = __esm({
|
|
|
18112
18116
|
});
|
|
18113
18117
|
appConfigPatchSchema = appConfigSchema.omit({ instances: true, mcpServers: true, cliStartup: true, customDomain: true });
|
|
18114
18118
|
jsonObjectSchema = external_exports.record(external_exports.string(), external_exports.json());
|
|
18115
|
-
DATA_DIR = process.env.OMB_DATA_DIR ??
|
|
18116
|
-
LEGACY_DATA_DIR =
|
|
18117
|
-
EVENTS_DIR =
|
|
18118
|
-
NATIVE_DIR =
|
|
18119
|
+
DATA_DIR = process.env.OMB_DATA_DIR ?? join6(homedir4(), ".openmausbot");
|
|
18120
|
+
LEGACY_DATA_DIR = join6(homedir4(), ".opengrokbot");
|
|
18121
|
+
EVENTS_DIR = join6(DATA_DIR, "events");
|
|
18122
|
+
NATIVE_DIR = join6(DATA_DIR, "native");
|
|
18119
18123
|
WORKSPACE_CREDENTIAL_ENV = [
|
|
18120
18124
|
"XAI_API_KEY",
|
|
18121
18125
|
"OMB_ANTHROPIC_API_KEY",
|
|
@@ -18153,11 +18157,11 @@ var init_config = __esm({
|
|
|
18153
18157
|
});
|
|
18154
18158
|
|
|
18155
18159
|
// electron/data-dir-lease.mjs
|
|
18156
|
-
import { execFileSync } from "node:child_process";
|
|
18160
|
+
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
18157
18161
|
import { createHash as createHash2, randomUUID as randomUUID5 } from "node:crypto";
|
|
18158
|
-
import { existsSync as existsSync6, linkSync, mkdirSync as mkdirSync7, readFileSync as
|
|
18162
|
+
import { existsSync as existsSync6, linkSync, mkdirSync as mkdirSync7, readFileSync as readFileSync4, renameSync as renameSync4, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "node:fs";
|
|
18159
18163
|
import { hostname as hostname4, uptime } from "node:os";
|
|
18160
|
-
import { join as
|
|
18164
|
+
import { join as join7 } from "node:path";
|
|
18161
18165
|
function leaseError(message2, cause) {
|
|
18162
18166
|
return Object.assign(new DataDirLeaseError(message2), { cause });
|
|
18163
18167
|
}
|
|
@@ -18167,10 +18171,10 @@ function isPid(value) {
|
|
|
18167
18171
|
function readBootSession() {
|
|
18168
18172
|
try {
|
|
18169
18173
|
if (process.platform === "linux") {
|
|
18170
|
-
return normalizeBootId(
|
|
18174
|
+
return normalizeBootId(readFileSync4("/proc/sys/kernel/random/boot_id", "utf8"));
|
|
18171
18175
|
}
|
|
18172
18176
|
if (process.platform === "darwin") {
|
|
18173
|
-
return normalizeBootId(
|
|
18177
|
+
return normalizeBootId(execFileSync2("/usr/sbin/sysctl", ["-n", "kern.bootsessionuuid"], {
|
|
18174
18178
|
encoding: "utf8",
|
|
18175
18179
|
timeout: 5e3,
|
|
18176
18180
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -18214,7 +18218,7 @@ function isReaperOwner(value, targetToken) {
|
|
|
18214
18218
|
function parseRecord(path3, invalidMessage, validate) {
|
|
18215
18219
|
let raw;
|
|
18216
18220
|
try {
|
|
18217
|
-
raw =
|
|
18221
|
+
raw = readFileSync4(path3, "utf8");
|
|
18218
18222
|
} catch (error51) {
|
|
18219
18223
|
if (error51?.code === "ENOENT") return null;
|
|
18220
18224
|
throw leaseError("OpenMausBot cannot read the data-directory lease; refusing to start to protect its state.", error51);
|
|
@@ -18262,7 +18266,7 @@ function unlinkExact(path3, message2) {
|
|
|
18262
18266
|
function publishRecord(path3, record3, prepareMessage, acquireMessage) {
|
|
18263
18267
|
const candidatePath = `${path3}.candidate-${record3.pid}-${record3.token}`;
|
|
18264
18268
|
try {
|
|
18265
|
-
|
|
18269
|
+
writeFileSync4(candidatePath, `${JSON.stringify(record3)}
|
|
18266
18270
|
`, { flag: "wx", mode: 384, flush: true });
|
|
18267
18271
|
} catch (error51) {
|
|
18268
18272
|
throw leaseError(prepareMessage, error51);
|
|
@@ -18349,10 +18353,10 @@ function prepareDataDir(dataDir, legacyDataDir) {
|
|
|
18349
18353
|
} catch (error51) {
|
|
18350
18354
|
throw leaseError("OpenMausBot cannot create its data directory.", error51);
|
|
18351
18355
|
}
|
|
18352
|
-
return
|
|
18356
|
+
return join7(dataDir, LEASE_NAME);
|
|
18353
18357
|
}
|
|
18354
18358
|
function assertNoLiveDelegatedChild(dataDir) {
|
|
18355
|
-
const childLeasePath =
|
|
18359
|
+
const childLeasePath = join7(dataDir, DELEGATED_CHILD_DIR, LEASE_NAME);
|
|
18356
18360
|
const child = readOwner(childLeasePath);
|
|
18357
18361
|
if (!child) return;
|
|
18358
18362
|
if (child.host !== hostname4()) {
|
|
@@ -18383,7 +18387,7 @@ function acquireDataDirLeaseInternal(dataDir, options = {}) {
|
|
|
18383
18387
|
};
|
|
18384
18388
|
const candidatePath = `${leasePath}.candidate-${owner.pid}-${owner.token}`;
|
|
18385
18389
|
try {
|
|
18386
|
-
|
|
18390
|
+
writeFileSync4(candidatePath, `${JSON.stringify(owner)}
|
|
18387
18391
|
`, { flag: "wx", mode: 384, flush: true });
|
|
18388
18392
|
} catch (error51) {
|
|
18389
18393
|
throw leaseError("OpenMausBot cannot prepare its data-directory lease.", error51);
|
|
@@ -18475,13 +18479,13 @@ var init_data_dir_lease2 = __esm({
|
|
|
18475
18479
|
|
|
18476
18480
|
// server/env-path.ts
|
|
18477
18481
|
import { execFile as execFile2 } from "node:child_process";
|
|
18478
|
-
import { closeSync as
|
|
18482
|
+
import { closeSync as closeSync3, existsSync as existsSync7, openSync as openSync3, readFileSync as readFileSync5, readSync, statSync as statSync2, readdirSync } from "node:fs";
|
|
18479
18483
|
import { homedir as homedir5 } from "node:os";
|
|
18480
|
-
import { basename as basename2, delimiter as delimiter3, dirname as dirname4, extname, join as
|
|
18484
|
+
import { basename as basename2, delimiter as delimiter3, dirname as dirname4, extname, join as join8 } from "node:path";
|
|
18481
18485
|
function nvmBinDirs() {
|
|
18482
18486
|
try {
|
|
18483
|
-
const base2 =
|
|
18484
|
-
return readdirSync(base2).filter((v) => v.startsWith("v")).sort((a2, b) => b.localeCompare(a2, void 0, { numeric: true })).map((v) =>
|
|
18487
|
+
const base2 = join8(homedir5(), ".nvm", "versions", "node");
|
|
18488
|
+
return readdirSync(base2).filter((v) => v.startsWith("v")).sort((a2, b) => b.localeCompare(a2, void 0, { numeric: true })).map((v) => join8(base2, v, "bin"));
|
|
18485
18489
|
} catch {
|
|
18486
18490
|
return [];
|
|
18487
18491
|
}
|
|
@@ -18489,49 +18493,49 @@ function nvmBinDirs() {
|
|
|
18489
18493
|
function knownDirs() {
|
|
18490
18494
|
const home = homedir5();
|
|
18491
18495
|
return [
|
|
18492
|
-
|
|
18496
|
+
join8(home, ".local", "bin"),
|
|
18493
18497
|
// claude installer default
|
|
18494
|
-
|
|
18498
|
+
join8(home, ".npm-global", "bin"),
|
|
18495
18499
|
// npm prefix ~/.npm-global (claude, opencode)
|
|
18496
|
-
|
|
18500
|
+
join8(home, ".kimi-code", "bin"),
|
|
18497
18501
|
// kimi-code installer
|
|
18498
|
-
|
|
18502
|
+
join8(home, ".grok", "bin"),
|
|
18499
18503
|
// x.ai installer
|
|
18500
|
-
|
|
18504
|
+
join8(home, ".opencode", "bin"),
|
|
18501
18505
|
// opencode installer
|
|
18502
|
-
|
|
18506
|
+
join8(home, ".claude", "local"),
|
|
18503
18507
|
// claude "local install"
|
|
18504
18508
|
"/opt/homebrew/bin",
|
|
18505
18509
|
// brew, Apple silicon
|
|
18506
18510
|
"/usr/local/bin",
|
|
18507
18511
|
// brew Intel / classic installs
|
|
18508
|
-
|
|
18509
|
-
|
|
18510
|
-
|
|
18511
|
-
|
|
18512
|
-
|
|
18512
|
+
join8(home, ".volta", "bin"),
|
|
18513
|
+
join8(home, ".bun", "bin"),
|
|
18514
|
+
join8(home, ".asdf", "shims"),
|
|
18515
|
+
join8(home, ".deno", "bin"),
|
|
18516
|
+
join8(home, "bin"),
|
|
18513
18517
|
...nvmBinDirs()
|
|
18514
18518
|
];
|
|
18515
18519
|
}
|
|
18516
18520
|
function windowsKnownDirs() {
|
|
18517
18521
|
const home = homedir5();
|
|
18518
|
-
const appData = process.env.APPDATA ??
|
|
18519
|
-
const localAppData = process.env.LOCALAPPDATA ??
|
|
18522
|
+
const appData = process.env.APPDATA ?? join8(home, "AppData", "Roaming");
|
|
18523
|
+
const localAppData = process.env.LOCALAPPDATA ?? join8(home, "AppData", "Local");
|
|
18520
18524
|
return [
|
|
18521
|
-
|
|
18525
|
+
join8(appData, "npm"),
|
|
18522
18526
|
// npm -g shims: claude, codex
|
|
18523
|
-
|
|
18527
|
+
join8(home, ".grok", "bin"),
|
|
18524
18528
|
// x.ai installer
|
|
18525
|
-
|
|
18529
|
+
join8(localAppData, "agy", "bin"),
|
|
18526
18530
|
// Antigravity installer
|
|
18527
|
-
|
|
18531
|
+
join8(home, ".local", "bin"),
|
|
18528
18532
|
// claude native installer
|
|
18529
|
-
|
|
18530
|
-
|
|
18533
|
+
join8(home, ".claude", "local"),
|
|
18534
|
+
join8(home, "bin"),
|
|
18531
18535
|
// Factory droid installer (%USERPROFILE%\bin)
|
|
18532
|
-
|
|
18533
|
-
|
|
18534
|
-
|
|
18536
|
+
join8(home, ".bun", "bin"),
|
|
18537
|
+
join8(home, ".deno", "bin"),
|
|
18538
|
+
join8(home, "go", "bin")
|
|
18535
18539
|
];
|
|
18536
18540
|
}
|
|
18537
18541
|
function registerPathDir(dir) {
|
|
@@ -18591,7 +18595,7 @@ function findCliCandidates(name) {
|
|
|
18591
18595
|
for (const dir of augmentedPath().split(delimiter3)) {
|
|
18592
18596
|
if (!dir) continue;
|
|
18593
18597
|
for (const ext of exts) {
|
|
18594
|
-
const p =
|
|
18598
|
+
const p = join8(dir, name + ext);
|
|
18595
18599
|
if (existsSync7(p)) {
|
|
18596
18600
|
out.push(p);
|
|
18597
18601
|
break;
|
|
@@ -18637,13 +18641,13 @@ function whichWin(cli, env) {
|
|
|
18637
18641
|
const path3 = env ? env.PATH ?? env.Path ?? "" : augmentedPath();
|
|
18638
18642
|
for (const dir of path3.split(delimiter3)) {
|
|
18639
18643
|
if (!dir) continue;
|
|
18640
|
-
const hit = probe(
|
|
18644
|
+
const hit = probe(join8(dir, cli));
|
|
18641
18645
|
if (hit) return hit;
|
|
18642
18646
|
}
|
|
18643
18647
|
return null;
|
|
18644
18648
|
}
|
|
18645
18649
|
function nodeExe(near, env) {
|
|
18646
|
-
const local =
|
|
18650
|
+
const local = join8(near, "node.exe");
|
|
18647
18651
|
if (isFile(local)) return local;
|
|
18648
18652
|
const onPath2 = whichWin("node.exe", env);
|
|
18649
18653
|
if (onPath2 && extname(onPath2).toLowerCase() === ".exe") return onPath2;
|
|
@@ -18652,12 +18656,12 @@ function nodeExe(near, env) {
|
|
|
18652
18656
|
function parseCmdShim(shim, env) {
|
|
18653
18657
|
let text4;
|
|
18654
18658
|
try {
|
|
18655
|
-
text4 =
|
|
18659
|
+
text4 = readFileSync5(shim, "utf8");
|
|
18656
18660
|
} catch {
|
|
18657
18661
|
return null;
|
|
18658
18662
|
}
|
|
18659
18663
|
const dir = dirname4(shim);
|
|
18660
|
-
const targets = [...text4.matchAll(/"%~?dp0%?\\?([^"]+)"/g)].map((m) =>
|
|
18664
|
+
const targets = [...text4.matchAll(/"%~?dp0%?\\?([^"]+)"/g)].map((m) => join8(dir, m[1])).filter((p) => isFile(p) && basename2(p).toLowerCase() !== "node.exe");
|
|
18661
18665
|
const script = targets.find((p) => /\.[cm]?js$/i.test(p));
|
|
18662
18666
|
if (script) {
|
|
18663
18667
|
const node = nodeExe(dir, env);
|
|
@@ -18670,7 +18674,7 @@ function parseNodeShebang(file2, env) {
|
|
|
18670
18674
|
let head = "";
|
|
18671
18675
|
let fd = null;
|
|
18672
18676
|
try {
|
|
18673
|
-
fd =
|
|
18677
|
+
fd = openSync3(file2, "r");
|
|
18674
18678
|
const buf = Buffer.alloc(128);
|
|
18675
18679
|
const n3 = readSync(fd, buf, 0, buf.length, 0);
|
|
18676
18680
|
head = buf.subarray(0, n3).toString("utf8").split("\n", 1)[0];
|
|
@@ -18679,7 +18683,7 @@ function parseNodeShebang(file2, env) {
|
|
|
18679
18683
|
} finally {
|
|
18680
18684
|
if (fd !== null) {
|
|
18681
18685
|
try {
|
|
18682
|
-
|
|
18686
|
+
closeSync3(fd);
|
|
18683
18687
|
} catch {
|
|
18684
18688
|
}
|
|
18685
18689
|
}
|
|
@@ -18728,7 +18732,7 @@ import {
|
|
|
18728
18732
|
spawn as spawn5,
|
|
18729
18733
|
execFile as execFile3
|
|
18730
18734
|
} from "node:child_process";
|
|
18731
|
-
import { join as
|
|
18735
|
+
import { join as join9 } from "node:path";
|
|
18732
18736
|
function resolveCli(cli, args = [], env) {
|
|
18733
18737
|
return resolveCliSpawn(cli, args, env);
|
|
18734
18738
|
}
|
|
@@ -18872,7 +18876,7 @@ async function stopCliTree(child, pid, timeoutMs) {
|
|
|
18872
18876
|
});
|
|
18873
18877
|
}
|
|
18874
18878
|
function brokerSocketPath(dataDir, tag) {
|
|
18875
|
-
return process.platform === "win32" ? `\\\\.\\pipe\\openmausbot-perm-${process.pid}-${tag}` :
|
|
18879
|
+
return process.platform === "win32" ? `\\\\.\\pipe\\openmausbot-perm-${process.pid}-${tag}` : join9(dataDir, `perm-${tag}.sock`);
|
|
18876
18880
|
}
|
|
18877
18881
|
var WINDOWS_SAFE_COMMAND_LINE_CHARS, cliGroups, stopping;
|
|
18878
18882
|
var init_procs = __esm({
|
|
@@ -18887,13 +18891,13 @@ var init_procs = __esm({
|
|
|
18887
18891
|
|
|
18888
18892
|
// server/engine-install.ts
|
|
18889
18893
|
import { existsSync as existsSync8, mkdirSync as mkdirSync8 } from "node:fs";
|
|
18890
|
-
import { join as
|
|
18894
|
+
import { join as join10 } from "node:path";
|
|
18891
18895
|
import { stripVTControlCharacters as stripVTControlCharacters3 } from "node:util";
|
|
18892
18896
|
function enginesPrefix(baseDir = DATA_DIR) {
|
|
18893
|
-
return
|
|
18897
|
+
return join10(baseDir, "tools", "npm");
|
|
18894
18898
|
}
|
|
18895
18899
|
function enginesBinDir(baseDir = DATA_DIR, platform = process.platform) {
|
|
18896
|
-
return platform === "win32" ? enginesPrefix(baseDir) :
|
|
18900
|
+
return platform === "win32" ? enginesPrefix(baseDir) : join10(enginesPrefix(baseDir), "bin");
|
|
18897
18901
|
}
|
|
18898
18902
|
function npmPackageOf(install) {
|
|
18899
18903
|
const line = install?.command?.linux ?? install?.command?.darwin ?? install?.command?.win32;
|
|
@@ -18943,9 +18947,9 @@ async function installOnce(pkg, options) {
|
|
|
18943
18947
|
}
|
|
18944
18948
|
}
|
|
18945
18949
|
function tail(output) {
|
|
18946
|
-
const
|
|
18947
|
-
if (!
|
|
18948
|
-
const lines =
|
|
18950
|
+
const clean = stripVTControlCharacters3(output).trim();
|
|
18951
|
+
if (!clean) return "";
|
|
18952
|
+
const lines = clean.split(/\r?\n/).filter((line) => line.trim()).slice(-6);
|
|
18949
18953
|
return `
|
|
18950
18954
|
${lines.join("\n").slice(-600)}`;
|
|
18951
18955
|
}
|
|
@@ -19239,9 +19243,9 @@ var init_registry = __esm({
|
|
|
19239
19243
|
});
|
|
19240
19244
|
|
|
19241
19245
|
// server/drivers/local-inject.ts
|
|
19242
|
-
import { readFileSync as
|
|
19246
|
+
import { readFileSync as readFileSync6 } from "node:fs";
|
|
19243
19247
|
import { homedir as homedir6 } from "node:os";
|
|
19244
|
-
import { join as
|
|
19248
|
+
import { join as join11 } from "node:path";
|
|
19245
19249
|
function contextWindowsFromPs(extra) {
|
|
19246
19250
|
const out = /* @__PURE__ */ new Map();
|
|
19247
19251
|
const rec = extra && typeof extra === "object" ? extra : null;
|
|
@@ -19326,7 +19330,7 @@ function firstUnslothToken(row) {
|
|
|
19326
19330
|
function readUnslothKey(env) {
|
|
19327
19331
|
const home = env.HOME || env.USERPROFILE || homedir6();
|
|
19328
19332
|
try {
|
|
19329
|
-
const raw = JSON.parse(
|
|
19333
|
+
const raw = JSON.parse(readFileSync6(join11(home, ".unsloth", "studio", "auth", "agent_api_key.json"), "utf8"));
|
|
19330
19334
|
if (raw.servers && typeof raw.servers === "object") {
|
|
19331
19335
|
const servers = raw.servers;
|
|
19332
19336
|
for (const url2 of ["http://127.0.0.1:8888", "http://localhost:8888"]) {
|
|
@@ -19376,7 +19380,7 @@ async function timedJson(url2, env, host, fetchImpl) {
|
|
|
19376
19380
|
function loadedIdsFromPayloads(_host, catalog, extra) {
|
|
19377
19381
|
const loaded = /* @__PURE__ */ new Set();
|
|
19378
19382
|
const catalogIds = new Set(idsFromModelsPayload(catalog));
|
|
19379
|
-
const
|
|
19383
|
+
const add = (id) => {
|
|
19380
19384
|
const base2 = id.split(":")[0];
|
|
19381
19385
|
if (catalogIds.size && !catalogIds.has(id) && !catalogIds.has(base2)) return;
|
|
19382
19386
|
if (!MODEL_ID.test(id)) return;
|
|
@@ -19389,10 +19393,10 @@ function loadedIdsFromPayloads(_host, catalog, extra) {
|
|
|
19389
19393
|
const hasLoadedFlags = running2.some(
|
|
19390
19394
|
(row) => row && typeof row === "object" && ("loaded" in row || "state" in row)
|
|
19391
19395
|
);
|
|
19392
|
-
if (!hasLoadedFlags && typeof rec.default_model === "string")
|
|
19396
|
+
if (!hasLoadedFlags && typeof rec.default_model === "string") add(rec.default_model);
|
|
19393
19397
|
for (const row of running2) {
|
|
19394
19398
|
if (typeof row === "string") {
|
|
19395
|
-
if (!hasLoadedFlags)
|
|
19399
|
+
if (!hasLoadedFlags) add(row);
|
|
19396
19400
|
continue;
|
|
19397
19401
|
}
|
|
19398
19402
|
if (!row || typeof row !== "object") continue;
|
|
@@ -19402,20 +19406,20 @@ function loadedIdsFromPayloads(_host, catalog, extra) {
|
|
|
19402
19406
|
const state = typeof item.state === "string" ? item.state.toLowerCase() : "";
|
|
19403
19407
|
if (item.loaded === false || state === "not-loaded" || state === "unloaded") continue;
|
|
19404
19408
|
if (item.loaded === true || state === "loaded" || state === "idle" || !hasLoadedFlags) {
|
|
19405
|
-
|
|
19409
|
+
add(id);
|
|
19406
19410
|
}
|
|
19407
19411
|
}
|
|
19408
19412
|
}
|
|
19409
19413
|
if (!loaded.size && catalog && typeof catalog === "object") {
|
|
19410
19414
|
const rec = catalog;
|
|
19411
|
-
if (typeof rec.default_model === "string")
|
|
19415
|
+
if (typeof rec.default_model === "string") add(rec.default_model);
|
|
19412
19416
|
const records = Array.isArray(rec.data) ? rec.data : [];
|
|
19413
19417
|
for (const row of records) {
|
|
19414
19418
|
if (!row || typeof row !== "object") continue;
|
|
19415
19419
|
const item = row;
|
|
19416
19420
|
if (typeof item.id !== "string") continue;
|
|
19417
19421
|
const state = typeof item.state === "string" ? item.state.toLowerCase() : "";
|
|
19418
|
-
if (item.loaded === true || state === "loaded")
|
|
19422
|
+
if (item.loaded === true || state === "loaded") add(item.id);
|
|
19419
19423
|
}
|
|
19420
19424
|
}
|
|
19421
19425
|
return loaded;
|
|
@@ -19528,11 +19532,11 @@ var init_local_inject = __esm({
|
|
|
19528
19532
|
|
|
19529
19533
|
// server/proxy-paths.ts
|
|
19530
19534
|
import { existsSync as existsSync9 } from "node:fs";
|
|
19531
|
-
import { dirname as dirname5, join as
|
|
19535
|
+
import { dirname as dirname5, join as join12 } from "node:path";
|
|
19532
19536
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
19533
19537
|
function resolveProxy(relative2) {
|
|
19534
|
-
const source =
|
|
19535
|
-
return existsSync9(source) ? source :
|
|
19538
|
+
const source = join12(SERVER_ROOT, `${relative2}.ts`);
|
|
19539
|
+
return existsSync9(source) ? source : join12(SERVER_ROOT, `${relative2}.js`);
|
|
19536
19540
|
}
|
|
19537
19541
|
var SERVER_ROOT, SPAWNED_PROXIES;
|
|
19538
19542
|
var init_proxy_paths = __esm({
|
|
@@ -19561,7 +19565,7 @@ var init_proxy_paths = __esm({
|
|
|
19561
19565
|
|
|
19562
19566
|
// server/container-computer.ts
|
|
19563
19567
|
import { execFile as execFile4 } from "node:child_process";
|
|
19564
|
-
import { join as
|
|
19568
|
+
import { join as join13, resolve as resolve3 } from "node:path";
|
|
19565
19569
|
import { promisify } from "node:util";
|
|
19566
19570
|
function computerProxyEnv(computer) {
|
|
19567
19571
|
return {
|
|
@@ -19585,7 +19589,7 @@ var init_container_computer = __esm({
|
|
|
19585
19589
|
IMAGE_REPOSITORY = "localhost/openmausbot/cua-local-vm";
|
|
19586
19590
|
IMAGE_LAYER_VERSION = "5";
|
|
19587
19591
|
IMAGE = `${IMAGE_REPOSITORY}:driver-${CUA_DRIVER_VERSION}-v${IMAGE_LAYER_VERSION}`;
|
|
19588
|
-
VM_WORKSPACE_DIR =
|
|
19592
|
+
VM_WORKSPACE_DIR = join13(DATA_DIR, "vm-home");
|
|
19589
19593
|
MEMORY_BYTES = 4 * 1024 * 1024 * 1024;
|
|
19590
19594
|
SHM_BYTES = 512 * 1024 * 1024;
|
|
19591
19595
|
containerMcpPath = SPAWNED_PROXIES.containerMcp;
|
|
@@ -19689,11 +19693,11 @@ var init_redact = __esm({
|
|
|
19689
19693
|
|
|
19690
19694
|
// server/drivers/native.ts
|
|
19691
19695
|
import { appendFileSync as appendFileSync3 } from "node:fs";
|
|
19692
|
-
import { join as
|
|
19696
|
+
import { join as join14 } from "node:path";
|
|
19693
19697
|
function appendNative(threadId, entry) {
|
|
19694
19698
|
try {
|
|
19695
19699
|
appendFileSync3(
|
|
19696
|
-
|
|
19700
|
+
join14(NATIVE_DIR, `${threadId}.ndjson`),
|
|
19697
19701
|
JSON.stringify({ at: (/* @__PURE__ */ new Date()).toISOString(), ...entry, msg: redactSecrets(entry.msg) }) + "\n",
|
|
19698
19702
|
{ mode: 384 }
|
|
19699
19703
|
);
|
|
@@ -19709,6 +19713,41 @@ var init_native = __esm({
|
|
|
19709
19713
|
});
|
|
19710
19714
|
|
|
19711
19715
|
// server/tool-summary.ts
|
|
19716
|
+
function toolDetailPreview(value) {
|
|
19717
|
+
if (value === void 0) return void 0;
|
|
19718
|
+
let budget = 200;
|
|
19719
|
+
let textBudget = 256e3;
|
|
19720
|
+
const bounded = (item, depth = 0) => {
|
|
19721
|
+
if (--budget < 0 || depth > 6) return "[additional data omitted]";
|
|
19722
|
+
if (typeof item === "string") {
|
|
19723
|
+
if (item.length > textBudget) return "[large content omitted]";
|
|
19724
|
+
if (/^data:[^,\s]+;base64,/i.test(item)) return "[binary content omitted]";
|
|
19725
|
+
if (/^\s*[[{]/.test(item)) {
|
|
19726
|
+
try {
|
|
19727
|
+
return bounded(JSON.parse(item), depth + 1);
|
|
19728
|
+
} catch {
|
|
19729
|
+
}
|
|
19730
|
+
}
|
|
19731
|
+
textBudget -= item.length;
|
|
19732
|
+
return item;
|
|
19733
|
+
}
|
|
19734
|
+
if (typeof item === "bigint") return String(item);
|
|
19735
|
+
if (item === null || typeof item !== "object") return item;
|
|
19736
|
+
if (Array.isArray(item)) return item.slice(0, 40).map((child) => bounded(child, depth + 1)).concat(item.length > 40 ? ["[additional items omitted]"] : []);
|
|
19737
|
+
const entries = Object.entries(item);
|
|
19738
|
+
const result = /* @__PURE__ */ Object.create(null);
|
|
19739
|
+
for (const [key, child] of entries.slice(0, 40)) {
|
|
19740
|
+
result[key] = /token|secret|password|passwd|cookie|authorization|api.?key|(^|[_.-])keys?$/i.test(key) ? "[redacted]" : /^(data|base64|blob)$/i.test(key) && typeof child === "string" ? "[binary content omitted]" : bounded(child, depth + 1);
|
|
19741
|
+
}
|
|
19742
|
+
if (entries.length > 40) result["\u2026"] = "[additional fields omitted]";
|
|
19743
|
+
return result;
|
|
19744
|
+
};
|
|
19745
|
+
const safe = redactSecrets(bounded(value));
|
|
19746
|
+
const text4 = typeof safe === "string" ? safe : JSON.stringify(safe, null, 2);
|
|
19747
|
+
if (!text4?.trim() || text4 === "{}" || text4 === "[]") return void 0;
|
|
19748
|
+
return text4.length > 6e3 ? `${text4.slice(0, 6e3)}
|
|
19749
|
+
[\u2026 preview shortened]` : text4;
|
|
19750
|
+
}
|
|
19712
19751
|
function fieldsOf(input) {
|
|
19713
19752
|
if (input === null || typeof input !== "object" || Array.isArray(input)) return void 0;
|
|
19714
19753
|
return input;
|
|
@@ -19838,12 +19877,12 @@ function createAcpDriver(support11) {
|
|
|
19838
19877
|
support11.transformEnv?.(env, activeConfig, instanceId);
|
|
19839
19878
|
return env;
|
|
19840
19879
|
};
|
|
19841
|
-
let
|
|
19880
|
+
let models2 = support11.models;
|
|
19842
19881
|
const refreshModels = async () => {
|
|
19843
19882
|
if (!support11.resolveModels) return;
|
|
19844
19883
|
try {
|
|
19845
19884
|
const resolved = await support11.resolveModels(childEnv(), config2, instanceId);
|
|
19846
|
-
if (resolved.options.length)
|
|
19885
|
+
if (resolved.options.length) models2 = resolved;
|
|
19847
19886
|
} catch {
|
|
19848
19887
|
}
|
|
19849
19888
|
};
|
|
@@ -20214,7 +20253,8 @@ function createAcpDriver(support11) {
|
|
|
20214
20253
|
itemType: "tool",
|
|
20215
20254
|
itemId: u4.toolCallId,
|
|
20216
20255
|
title: String(u4.rawInput?.command ?? u4.title ?? "tool").slice(0, 80),
|
|
20217
|
-
summary: commandSummary(u4.rawInput)
|
|
20256
|
+
summary: commandSummary(u4.rawInput),
|
|
20257
|
+
input: toolDetailPreview(u4.rawInput)
|
|
20218
20258
|
});
|
|
20219
20259
|
break;
|
|
20220
20260
|
}
|
|
@@ -20225,7 +20265,8 @@ function createAcpDriver(support11) {
|
|
|
20225
20265
|
type: "item.completed",
|
|
20226
20266
|
itemType: "tool",
|
|
20227
20267
|
itemId: u4.toolCallId,
|
|
20228
|
-
ok: u4.status !== "failed"
|
|
20268
|
+
ok: u4.status !== "failed",
|
|
20269
|
+
output: toolDetailPreview(u4.rawOutput ?? u4.content)
|
|
20229
20270
|
});
|
|
20230
20271
|
}
|
|
20231
20272
|
break;
|
|
@@ -20466,7 +20507,7 @@ ${turn.text}` : turn.text;
|
|
|
20466
20507
|
displayName: input.displayName,
|
|
20467
20508
|
enabled: input.enabled,
|
|
20468
20509
|
get models() {
|
|
20469
|
-
return
|
|
20510
|
+
return models2;
|
|
20470
20511
|
},
|
|
20471
20512
|
refreshModels: support11.resolveModels ? refreshModels : void 0,
|
|
20472
20513
|
snapshot,
|
|
@@ -20543,7 +20584,7 @@ import { createHash as createHash3, randomUUID as randomUUID6 } from "node:crypt
|
|
|
20543
20584
|
import { mkdir as mkdir2, mkdtemp, readFile as readFile2, rm, stat as stat2 } from "node:fs/promises";
|
|
20544
20585
|
import { request as httpRequest } from "node:http";
|
|
20545
20586
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
20546
|
-
import { delimiter as delimiter4, join as
|
|
20587
|
+
import { delimiter as delimiter4, join as join15, resolve as resolve5 } from "node:path";
|
|
20547
20588
|
function quoteBrowserArgument(value) {
|
|
20548
20589
|
return `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
20549
20590
|
}
|
|
@@ -20578,11 +20619,11 @@ function authorizationUrlFromLine(line) {
|
|
|
20578
20619
|
}
|
|
20579
20620
|
}
|
|
20580
20621
|
function antigravityProfileDirectory(instanceId, baseDir = DATA_DIR) {
|
|
20581
|
-
return
|
|
20622
|
+
return join15(baseDir, "providers", "antigravity", createHash3("sha256").update(instanceId).digest("hex"));
|
|
20582
20623
|
}
|
|
20583
20624
|
async function prepareAntigravityProfile(input) {
|
|
20584
20625
|
const directory = resolve5(input.profileDirectory ?? antigravityProfileDirectory(input.instanceId, input.baseDir));
|
|
20585
|
-
const acpDirectory =
|
|
20626
|
+
const acpDirectory = join15(directory, "antigravity-acp");
|
|
20586
20627
|
await mkdir2(acpDirectory, { recursive: true, mode: 448 });
|
|
20587
20628
|
if (process.platform !== "win32") {
|
|
20588
20629
|
const { chmod: chmod2 } = await import("node:fs/promises");
|
|
@@ -20590,7 +20631,7 @@ async function prepareAntigravityProfile(input) {
|
|
|
20590
20631
|
await chmod2(acpDirectory, 448);
|
|
20591
20632
|
}
|
|
20592
20633
|
writeFileAtomic(
|
|
20593
|
-
|
|
20634
|
+
join15(acpDirectory, "settings.json"),
|
|
20594
20635
|
`${JSON.stringify({ auth: { type: "oauth-personal" } })}
|
|
20595
20636
|
`,
|
|
20596
20637
|
{ mode: 384 }
|
|
@@ -20613,7 +20654,7 @@ async function prepareAntigravityProfile(input) {
|
|
|
20613
20654
|
PYTHONUNBUFFERED: "1",
|
|
20614
20655
|
ELECTRON_RUN_AS_NODE: "1"
|
|
20615
20656
|
});
|
|
20616
|
-
return { directory, tokenPath:
|
|
20657
|
+
return { directory, tokenPath: join15(acpDirectory, "acp_token.json"), environment };
|
|
20617
20658
|
}
|
|
20618
20659
|
async function antigravityProfileAuthenticated(profile) {
|
|
20619
20660
|
try {
|
|
@@ -20742,7 +20783,7 @@ function isValidAntigravityInitializeResult(initialized, expectedVersion) {
|
|
|
20742
20783
|
return true;
|
|
20743
20784
|
}
|
|
20744
20785
|
async function validateAntigravityRuntime(runtime, expectedVersion) {
|
|
20745
|
-
const profileDirectory = await mkdtemp(
|
|
20786
|
+
const profileDirectory = await mkdtemp(join15(tmpdir2(), "openmaus-antigravity-verify-"));
|
|
20746
20787
|
let client;
|
|
20747
20788
|
let failed2 = false;
|
|
20748
20789
|
let failure;
|
|
@@ -21132,7 +21173,7 @@ import {
|
|
|
21132
21173
|
writeFile as writeFile2
|
|
21133
21174
|
} from "node:fs/promises";
|
|
21134
21175
|
import { constants } from "node:fs";
|
|
21135
|
-
import { delimiter as delimiter5, dirname as dirname7, isAbsolute as isAbsolute2, join as
|
|
21176
|
+
import { delimiter as delimiter5, dirname as dirname7, isAbsolute as isAbsolute2, join as join16, resolve as resolve6 } from "node:path";
|
|
21136
21177
|
import { Transform } from "node:stream";
|
|
21137
21178
|
import { pipeline } from "node:stream/promises";
|
|
21138
21179
|
import { createInflateRaw } from "node:zlib";
|
|
@@ -21153,7 +21194,7 @@ async function retryOnWindowsFileLock(step, options = {}) {
|
|
|
21153
21194
|
}
|
|
21154
21195
|
}
|
|
21155
21196
|
function runtimeRoot(baseDir = DATA_DIR) {
|
|
21156
|
-
return
|
|
21197
|
+
return join16(baseDir, "tools", "antigravity-acp", `${process.platform}-${process.arch}`);
|
|
21157
21198
|
}
|
|
21158
21199
|
function executableNames(platform = process.platform) {
|
|
21159
21200
|
return platform === "win32" ? { executable: "agy_acp_server.exe", harness: "localharness_external.exe" } : { executable: "agy_acp_server.par", harness: "localharness_external" };
|
|
@@ -21170,10 +21211,10 @@ async function executableFile(path3, bytes) {
|
|
|
21170
21211
|
}
|
|
21171
21212
|
async function completedRelease(directory, asset) {
|
|
21172
21213
|
try {
|
|
21173
|
-
const record3 = JSON.parse(await readFile3(
|
|
21214
|
+
const record3 = JSON.parse(await readFile3(join16(directory, INSTALL_RECORD), "utf8"));
|
|
21174
21215
|
if (record3.sha256 !== asset.sha256 || record3.version !== asset.version || record3.executable.name !== asset.executable.name || record3.executable.bytes !== asset.executable.bytes || record3.harness.name !== asset.harness.name || record3.harness.bytes !== asset.harness.bytes) return null;
|
|
21175
|
-
const executablePath =
|
|
21176
|
-
const harnessPath =
|
|
21216
|
+
const executablePath = join16(directory, asset.executable.name);
|
|
21217
|
+
const harnessPath = join16(directory, asset.harness.name);
|
|
21177
21218
|
if (!await executableFile(executablePath, asset.executable.bytes)) return null;
|
|
21178
21219
|
if (!await executableFile(harnessPath, asset.harness.bytes)) return null;
|
|
21179
21220
|
return { executablePath, harnessPath, source: "managed", version: asset.version };
|
|
@@ -21189,7 +21230,7 @@ function pathCandidates(binary2, env) {
|
|
|
21189
21230
|
async function externalRuntime(candidate, source) {
|
|
21190
21231
|
const names = executableNames();
|
|
21191
21232
|
const executablePath = resolve6(candidate);
|
|
21192
|
-
const harnessPath =
|
|
21233
|
+
const harnessPath = join16(dirname7(executablePath), names.harness);
|
|
21193
21234
|
if (!await executableFile(executablePath) || !await executableFile(harnessPath)) return null;
|
|
21194
21235
|
return { executablePath, harnessPath, source, version: null };
|
|
21195
21236
|
}
|
|
@@ -21209,7 +21250,7 @@ async function resolveAntigravityRuntime(binaryPath, env = process.env, baseDir
|
|
|
21209
21250
|
}
|
|
21210
21251
|
const asset = resolveAntigravityReleaseAsset();
|
|
21211
21252
|
if (asset) {
|
|
21212
|
-
const managed = await completedRelease(
|
|
21253
|
+
const managed = await completedRelease(join16(runtimeRoot(baseDir), "versions", asset.sha256), asset);
|
|
21213
21254
|
if (managed) return managed;
|
|
21214
21255
|
}
|
|
21215
21256
|
const names = executableNames();
|
|
@@ -21331,7 +21372,7 @@ async function extractPinnedArchive(archivePath, outputDirectory, asset) {
|
|
|
21331
21372
|
start: dataOffset,
|
|
21332
21373
|
end: dataOffset + entry.compressedBytes - 1
|
|
21333
21374
|
});
|
|
21334
|
-
const destination = createWriteStream(
|
|
21375
|
+
const destination = createWriteStream(join16(outputDirectory, entry.name), { flags: "wx", mode: 448 });
|
|
21335
21376
|
if (entry.compression === 8) await pipeline(source, createInflateRaw(), counter, destination);
|
|
21336
21377
|
else await pipeline(source, counter, destination);
|
|
21337
21378
|
if (bytes !== expectedBytes) throw new Error("An extracted file was truncated.");
|
|
@@ -21342,8 +21383,8 @@ async function installOnce2(options) {
|
|
|
21342
21383
|
const asset = options.asset === void 0 ? resolveAntigravityReleaseAsset() : options.asset;
|
|
21343
21384
|
if (!asset) throw new Error(`Google does not publish an Antigravity runtime for ${process.platform}-${process.arch}.`);
|
|
21344
21385
|
const root = runtimeRoot(options.baseDir);
|
|
21345
|
-
const versions =
|
|
21346
|
-
const destination =
|
|
21386
|
+
const versions = join16(root, "versions");
|
|
21387
|
+
const destination = join16(versions, asset.sha256);
|
|
21347
21388
|
await mkdir3(versions, { recursive: true, mode: 448 });
|
|
21348
21389
|
const existing = await completedRelease(destination, asset);
|
|
21349
21390
|
if (existing) {
|
|
@@ -21359,11 +21400,11 @@ async function installOnce2(options) {
|
|
|
21359
21400
|
} catch (error51) {
|
|
21360
21401
|
if (error51 instanceof Error && error51.message.includes("Antigravity needs")) throw error51;
|
|
21361
21402
|
}
|
|
21362
|
-
const staging = await mkdtemp2(
|
|
21403
|
+
const staging = await mkdtemp2(join16(versions, `${STAGING_PREFIX}${randomUUID7()}-`));
|
|
21363
21404
|
let failed2 = false;
|
|
21364
21405
|
try {
|
|
21365
|
-
const archivePath =
|
|
21366
|
-
const runtimeDirectory =
|
|
21406
|
+
const archivePath = join16(staging, "download.zip");
|
|
21407
|
+
const runtimeDirectory = join16(staging, "runtime");
|
|
21367
21408
|
await mkdir3(runtimeDirectory, { mode: 448 });
|
|
21368
21409
|
const controller = new AbortController();
|
|
21369
21410
|
const timeout = setTimeout(() => controller.abort(), DOWNLOAD_TIMEOUT_MS2);
|
|
@@ -21409,12 +21450,12 @@ async function installOnce2(options) {
|
|
|
21409
21450
|
}
|
|
21410
21451
|
await extractPinnedArchive(archivePath, runtimeDirectory, asset);
|
|
21411
21452
|
if (process.platform !== "win32") {
|
|
21412
|
-
await chmod(
|
|
21413
|
-
await chmod(
|
|
21453
|
+
await chmod(join16(runtimeDirectory, asset.executable.name), 493);
|
|
21454
|
+
await chmod(join16(runtimeDirectory, asset.harness.name), 493);
|
|
21414
21455
|
}
|
|
21415
21456
|
const candidate = {
|
|
21416
|
-
executablePath:
|
|
21417
|
-
harnessPath:
|
|
21457
|
+
executablePath: join16(runtimeDirectory, asset.executable.name),
|
|
21458
|
+
harnessPath: join16(runtimeDirectory, asset.harness.name),
|
|
21418
21459
|
source: "managed",
|
|
21419
21460
|
version: asset.version
|
|
21420
21461
|
};
|
|
@@ -21425,7 +21466,7 @@ async function installOnce2(options) {
|
|
|
21425
21466
|
executable: asset.executable,
|
|
21426
21467
|
harness: asset.harness
|
|
21427
21468
|
};
|
|
21428
|
-
await writeFile2(
|
|
21469
|
+
await writeFile2(join16(runtimeDirectory, INSTALL_RECORD), `${JSON.stringify(record3)}
|
|
21429
21470
|
`, { flag: "wx", mode: 384 });
|
|
21430
21471
|
try {
|
|
21431
21472
|
await retryOnWindowsFileLock(() => rename(runtimeDirectory, destination));
|
|
@@ -21706,7 +21747,7 @@ var init_boxagent = __esm({
|
|
|
21706
21747
|
const model = turn.model || MODELS.default;
|
|
21707
21748
|
const prompt = [
|
|
21708
21749
|
turn.system,
|
|
21709
|
-
"You are working on
|
|
21750
|
+
"You are working on the assigned cloud computer \u2014 use its desktop, Chrome, and shell within the access described above.",
|
|
21710
21751
|
"",
|
|
21711
21752
|
turn.text
|
|
21712
21753
|
].filter((s) => s !== void 0).join("\n");
|
|
@@ -21887,14 +21928,14 @@ var init_resume_recovery = __esm({
|
|
|
21887
21928
|
import { randomUUID as randomUUID8 } from "node:crypto";
|
|
21888
21929
|
import { realpathSync } from "node:fs";
|
|
21889
21930
|
import { homedir as homedir8 } from "node:os";
|
|
21890
|
-
import { basename as basename4, dirname as dirname8, isAbsolute as isAbsolute3, join as
|
|
21931
|
+
import { basename as basename4, dirname as dirname8, isAbsolute as isAbsolute3, join as join17, resolve as resolve7 } from "node:path";
|
|
21891
21932
|
import { stripVTControlCharacters as stripVTControlCharacters4 } from "node:util";
|
|
21892
21933
|
function canonicalPath(path3) {
|
|
21893
21934
|
try {
|
|
21894
21935
|
return realpathSync(path3);
|
|
21895
21936
|
} catch {
|
|
21896
21937
|
const parent = dirname8(path3);
|
|
21897
|
-
return parent === path3 ? path3 :
|
|
21938
|
+
return parent === path3 ? path3 : join17(canonicalPath(parent), basename4(path3));
|
|
21898
21939
|
}
|
|
21899
21940
|
}
|
|
21900
21941
|
function claudeSignInLink(value) {
|
|
@@ -21951,7 +21992,7 @@ var init_claude_login_auth = __esm({
|
|
|
21951
21992
|
if (!isAbsolute3(home) || env.CLAUDE_CONFIG_DIR && !isAbsolute3(env.CLAUDE_CONFIG_DIR)) {
|
|
21952
21993
|
throw new Error("Use an absolute HOME and CLAUDE_CONFIG_DIR path for this server's Claude provider before signing in.");
|
|
21953
21994
|
}
|
|
21954
|
-
const homeKey = canonicalPath(resolve7(env.CLAUDE_CONFIG_DIR ||
|
|
21995
|
+
const homeKey = canonicalPath(resolve7(env.CLAUDE_CONFIG_DIR || join17(home, ".claude")));
|
|
21955
21996
|
if (authenticatingHomes.has(homeKey)) {
|
|
21956
21997
|
throw new Error("A Claude sign-in is already running for this server account. Finish or cancel it before starting another.");
|
|
21957
21998
|
}
|
|
@@ -22047,7 +22088,7 @@ var init_claude_login_auth = __esm({
|
|
|
22047
22088
|
if (!isAbsolute3(home) || env.CLAUDE_CONFIG_DIR && !isAbsolute3(env.CLAUDE_CONFIG_DIR)) {
|
|
22048
22089
|
throw new Error("Use an absolute HOME and CLAUDE_CONFIG_DIR path for this server's Claude provider before signing out.");
|
|
22049
22090
|
}
|
|
22050
|
-
const homeKey = canonicalPath(resolve7(env.CLAUDE_CONFIG_DIR ||
|
|
22091
|
+
const homeKey = canonicalPath(resolve7(env.CLAUDE_CONFIG_DIR || join17(home, ".claude")));
|
|
22051
22092
|
if (authenticatingHomes.has(homeKey)) {
|
|
22052
22093
|
throw new Error("A Claude sign-in is running for this server account. Finish or cancel it before signing out.");
|
|
22053
22094
|
}
|
|
@@ -22263,7 +22304,7 @@ var init_mcp_trim = __esm({
|
|
|
22263
22304
|
});
|
|
22264
22305
|
|
|
22265
22306
|
// server/mcp-gate-config.ts
|
|
22266
|
-
import { join as
|
|
22307
|
+
import { join as join18 } from "node:path";
|
|
22267
22308
|
function resultBudget(env = process.env) {
|
|
22268
22309
|
const raw = env.OMB_MCP_RESULT_BUDGET;
|
|
22269
22310
|
if (raw === void 0 || raw === "") return DEFAULT_RESULT_BUDGET;
|
|
@@ -22271,7 +22312,7 @@ function resultBudget(env = process.env) {
|
|
|
22271
22312
|
return Number.isFinite(parsed) && parsed >= 0 ? Math.floor(parsed) : DEFAULT_RESULT_BUDGET;
|
|
22272
22313
|
}
|
|
22273
22314
|
function spillDir(threadId) {
|
|
22274
|
-
return
|
|
22315
|
+
return join18(DATA_DIR, "tool-results", threadId.replace(/[^A-Za-z0-9_-]/g, "-").slice(0, 80) || "thread");
|
|
22275
22316
|
}
|
|
22276
22317
|
function gateServer(input) {
|
|
22277
22318
|
const { name, server, budget } = input;
|
|
@@ -22473,10 +22514,10 @@ var init_ask_question = __esm({
|
|
|
22473
22514
|
|
|
22474
22515
|
// server/drivers/claude.ts
|
|
22475
22516
|
import { createHash as createHash5, randomBytes as randomBytes2 } from "node:crypto";
|
|
22476
|
-
import { chmodSync as chmodSync4, mkdtempSync as mkdtempSync2, readFileSync as
|
|
22517
|
+
import { chmodSync as chmodSync4, mkdtempSync as mkdtempSync2, readFileSync as readFileSync7, rmSync as rmSync4, unlinkSync as unlinkSync3, writeFileSync as writeFileSync5 } from "node:fs";
|
|
22477
22518
|
import { createServer as createNetServer } from "node:net";
|
|
22478
22519
|
import { homedir as homedir9, tmpdir as tmpdir3 } from "node:os";
|
|
22479
|
-
import { join as
|
|
22520
|
+
import { join as join19, dirname as dirname9, isAbsolute as isAbsolute4, normalize } from "node:path";
|
|
22480
22521
|
function claudeAuthStatus(cli, env, run3 = execCli) {
|
|
22481
22522
|
return new Promise((resolve13) => {
|
|
22482
22523
|
run3(cli, ["auth", "status", "--json"], { timeout: 8e3, maxBuffer: 65536, env }, (_error, stdout2) => {
|
|
@@ -22504,8 +22545,8 @@ function claudeAuthStatus(cli, env, run3 = execCli) {
|
|
|
22504
22545
|
}
|
|
22505
22546
|
function resolveClaudeConfigDir(configDir, env = process.env) {
|
|
22506
22547
|
const home = env.HOME || env.USERPROFILE || homedir9();
|
|
22507
|
-
const configured = configDir?.trim() || env.CLAUDE_CONFIG_DIR?.trim() ||
|
|
22508
|
-
const expanded = configured === "~" ? home : configured.startsWith("~/") ?
|
|
22548
|
+
const configured = configDir?.trim() || env.CLAUDE_CONFIG_DIR?.trim() || join19(home, ".claude");
|
|
22549
|
+
const expanded = configured === "~" ? home : configured.startsWith("~/") ? join19(home, configured.slice(2)) : configured;
|
|
22509
22550
|
if (!isAbsolute4(expanded) || /[\p{Cc}\p{Cf}]/u.test(expanded)) {
|
|
22510
22551
|
throw new Error("claude: configDir must be an absolute path or start with ~/");
|
|
22511
22552
|
}
|
|
@@ -22540,7 +22581,7 @@ function inheritsUserConfig(env) {
|
|
|
22540
22581
|
function readClaudeAuthSettings(env, instanceEnvironment = {}) {
|
|
22541
22582
|
if (CLAUDE_ACCOUNT_ENV_KEYS.some((key) => instanceEnvironment[key])) return {};
|
|
22542
22583
|
try {
|
|
22543
|
-
const settings2 = JSON.parse(
|
|
22584
|
+
const settings2 = JSON.parse(readFileSync7(join19(resolveClaudeConfigDir(void 0, env), "settings.json"), "utf8"));
|
|
22544
22585
|
const authEnv = {};
|
|
22545
22586
|
for (const key of CLAUDE_ACCOUNT_ENV_KEYS) {
|
|
22546
22587
|
if (!key.endsWith("_FILE_DESCRIPTOR") && typeof settings2?.env?.[key] === "string") {
|
|
@@ -22558,7 +22599,7 @@ function readClaudeAuthSettings(env, instanceEnvironment = {}) {
|
|
|
22558
22599
|
function projectMcpServers(cwd) {
|
|
22559
22600
|
let parsed;
|
|
22560
22601
|
try {
|
|
22561
|
-
parsed = JSON.parse(
|
|
22602
|
+
parsed = JSON.parse(readFileSync7(join19(cwd, ".mcp.json"), "utf8"));
|
|
22562
22603
|
} catch {
|
|
22563
22604
|
return {};
|
|
22564
22605
|
}
|
|
@@ -22626,7 +22667,7 @@ function extrasFromUnknown(value) {
|
|
|
22626
22667
|
function readClaudeModelCatalog(env = process.env) {
|
|
22627
22668
|
let settings2 = {};
|
|
22628
22669
|
try {
|
|
22629
|
-
settings2 = JSON.parse(
|
|
22670
|
+
settings2 = JSON.parse(readFileSync7(join19(resolveClaudeConfigDir(void 0, env), "settings.json"), "utf8"));
|
|
22630
22671
|
} catch {
|
|
22631
22672
|
return STATIC_CLAUDE_MODELS;
|
|
22632
22673
|
}
|
|
@@ -22677,7 +22718,7 @@ function brokerSocketCandidates(threadId, botId) {
|
|
|
22677
22718
|
const base2 = permissionSocketPath(threadId, botId);
|
|
22678
22719
|
if (process.platform !== "win32") {
|
|
22679
22720
|
const scope = createHash5("sha256").update(`${DATA_DIR}\0${process.pid}\0${botId ?? ""}\0${threadId}`).digest("hex").slice(0, 16);
|
|
22680
|
-
return [base2,
|
|
22721
|
+
return [base2, join19(tmpdir3(), `omb-perm-${scope}.sock`)];
|
|
22681
22722
|
}
|
|
22682
22723
|
return [
|
|
22683
22724
|
base2,
|
|
@@ -22895,7 +22936,7 @@ function claudeUserMessage(text4, images) {
|
|
|
22895
22936
|
source: {
|
|
22896
22937
|
type: "base64",
|
|
22897
22938
|
media_type: image.mime,
|
|
22898
|
-
data:
|
|
22939
|
+
data: readFileSync7(image.path).toString("base64")
|
|
22899
22940
|
}
|
|
22900
22941
|
}));
|
|
22901
22942
|
if (text4) content.push({ type: "text", text: text4 });
|
|
@@ -22999,11 +23040,11 @@ var init_claude = __esm({
|
|
|
22999
23040
|
const { instanceId, config: config2 } = input;
|
|
23000
23041
|
const environment = (model) => claudeEnvironment(model, { ...process.env, ...input.environment }, config2.configDir, input.environment);
|
|
23001
23042
|
const catalogEnv = environment();
|
|
23002
|
-
let
|
|
23043
|
+
let models2 = STATIC_CLAUDE_MODELS;
|
|
23003
23044
|
const refreshModels = async () => {
|
|
23004
23045
|
try {
|
|
23005
23046
|
const resolved = await mergeLocalInject(readClaudeModelCatalog(catalogEnv), catalogEnv);
|
|
23006
|
-
if (resolved.options.length)
|
|
23047
|
+
if (resolved.options.length) models2 = resolved;
|
|
23007
23048
|
} catch {
|
|
23008
23049
|
}
|
|
23009
23050
|
};
|
|
@@ -23196,13 +23237,13 @@ var init_claude = __esm({
|
|
|
23196
23237
|
allowed.push("mcp__ogb");
|
|
23197
23238
|
let mcpConfigPath = null;
|
|
23198
23239
|
if (Object.keys(mcpServers).length) {
|
|
23199
|
-
mcpConfigPath =
|
|
23240
|
+
mcpConfigPath = join19(mkdtempSync2(join19(tmpdir3(), "omb-mcp-")), "mcp.json");
|
|
23200
23241
|
args.push("--mcp-config", mcpConfigPath);
|
|
23201
23242
|
args.push("--allowedTools", allowed.join(","));
|
|
23202
23243
|
}
|
|
23203
23244
|
const env = environment(turnModel);
|
|
23204
23245
|
const authSettings = isolated && !injected.injected ? readClaudeAuthSettings(env, input.environment) : {};
|
|
23205
|
-
const authSettingsPath = mcpConfigPath && Object.keys(authSettings).length ?
|
|
23246
|
+
const authSettingsPath = mcpConfigPath && Object.keys(authSettings).length ? join19(dirname9(mcpConfigPath), "auth-settings.json") : null;
|
|
23206
23247
|
if (authSettingsPath) args.push("--settings", authSettingsPath);
|
|
23207
23248
|
env.CLAUDE_CODE_DISABLE_BACKGROUND_TASKS = "1";
|
|
23208
23249
|
const cwd = turn.cwd ?? homedir9();
|
|
@@ -23279,8 +23320,8 @@ var init_claude = __esm({
|
|
|
23279
23320
|
};
|
|
23280
23321
|
try {
|
|
23281
23322
|
if (turn.system) {
|
|
23282
|
-
systemPromptPath =
|
|
23283
|
-
|
|
23323
|
+
systemPromptPath = join19(mkdtempSync2(join19(tmpdir3(), "omb-system-")), "prompt.txt");
|
|
23324
|
+
writeFileSync5(systemPromptPath, turn.system, { mode: 384 });
|
|
23284
23325
|
args.push("--append-system-prompt-file", systemPromptPath);
|
|
23285
23326
|
}
|
|
23286
23327
|
if (socketPath) {
|
|
@@ -23330,10 +23371,10 @@ var init_claude = __esm({
|
|
|
23330
23371
|
}
|
|
23331
23372
|
}
|
|
23332
23373
|
if (mcpConfigPath) {
|
|
23333
|
-
|
|
23374
|
+
writeFileSync5(mcpConfigPath, JSON.stringify({ mcpServers }), { mode: 384 });
|
|
23334
23375
|
}
|
|
23335
23376
|
if (authSettingsPath) {
|
|
23336
|
-
|
|
23377
|
+
writeFileSync5(authSettingsPath, JSON.stringify(authSettings), { mode: 384 });
|
|
23337
23378
|
}
|
|
23338
23379
|
if (sessionId) args.push("--resume", sessionId);
|
|
23339
23380
|
else args.push("--session-id", newSessionId);
|
|
@@ -23446,7 +23487,8 @@ var init_claude = __esm({
|
|
|
23446
23487
|
itemType: "tool",
|
|
23447
23488
|
itemId: b.id,
|
|
23448
23489
|
title: b.name,
|
|
23449
|
-
summary: commandSummary(b.input)
|
|
23490
|
+
summary: commandSummary(b.input),
|
|
23491
|
+
input: toolDetailPreview(b.input)
|
|
23450
23492
|
});
|
|
23451
23493
|
}
|
|
23452
23494
|
}
|
|
@@ -23464,7 +23506,7 @@ var init_claude = __esm({
|
|
|
23464
23506
|
case "user":
|
|
23465
23507
|
for (const b of Array.isArray(o.message?.content) ? o.message.content : []) {
|
|
23466
23508
|
if (b.type === "tool_result") {
|
|
23467
|
-
emit({ ...base2(threadId, currentTurnId()), type: "item.completed", itemType: "tool", itemId: b.tool_use_id, ok: !b.is_error });
|
|
23509
|
+
emit({ ...base2(threadId, currentTurnId()), type: "item.completed", itemType: "tool", itemId: b.tool_use_id, ok: !b.is_error, output: toolDetailPreview(b.content) });
|
|
23468
23510
|
}
|
|
23469
23511
|
}
|
|
23470
23512
|
break;
|
|
@@ -23751,7 +23793,7 @@ var init_claude = __esm({
|
|
|
23751
23793
|
displayName: input.displayName,
|
|
23752
23794
|
enabled: input.enabled,
|
|
23753
23795
|
get models() {
|
|
23754
|
-
return
|
|
23796
|
+
return models2;
|
|
23755
23797
|
},
|
|
23756
23798
|
refreshModels,
|
|
23757
23799
|
snapshot,
|
|
@@ -23816,9 +23858,9 @@ var init_claude = __esm({
|
|
|
23816
23858
|
});
|
|
23817
23859
|
|
|
23818
23860
|
// server/drivers/codex-catalog.ts
|
|
23819
|
-
import { readdirSync as readdirSync2, readFileSync as
|
|
23861
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync8 } from "node:fs";
|
|
23820
23862
|
import { homedir as homedir10 } from "node:os";
|
|
23821
|
-
import { basename as basename5, join as
|
|
23863
|
+
import { basename as basename5, join as join20 } from "node:path";
|
|
23822
23864
|
function encodeCodexSelection(provider, model) {
|
|
23823
23865
|
return `${provider}${SEP}${model}`;
|
|
23824
23866
|
}
|
|
@@ -23840,7 +23882,7 @@ function readCodexAppServerModelCatalog(cli, env, timeoutMs = 8e3) {
|
|
|
23840
23882
|
let settled = false;
|
|
23841
23883
|
let buffer = "";
|
|
23842
23884
|
let nextId = 1;
|
|
23843
|
-
const
|
|
23885
|
+
const models2 = [];
|
|
23844
23886
|
const cursors = /* @__PURE__ */ new Set();
|
|
23845
23887
|
const pending = /* @__PURE__ */ new Map();
|
|
23846
23888
|
const finish = (catalog) => {
|
|
@@ -23898,7 +23940,7 @@ function readCodexAppServerModelCatalog(cli, env, timeoutMs = 8e3) {
|
|
|
23898
23940
|
continue;
|
|
23899
23941
|
}
|
|
23900
23942
|
const page = message2.result;
|
|
23901
|
-
if (Array.isArray(page?.data))
|
|
23943
|
+
if (Array.isArray(page?.data)) models2.push(...page.data);
|
|
23902
23944
|
const cursor3 = typeof page?.nextCursor === "string" && page.nextCursor ? page.nextCursor : null;
|
|
23903
23945
|
if (cursor3 && !cursors.has(cursor3)) {
|
|
23904
23946
|
cursors.add(cursor3);
|
|
@@ -23908,7 +23950,7 @@ function readCodexAppServerModelCatalog(cli, env, timeoutMs = 8e3) {
|
|
|
23908
23950
|
const options = [];
|
|
23909
23951
|
const seen = /* @__PURE__ */ new Set();
|
|
23910
23952
|
let defaultModel = null;
|
|
23911
|
-
for (const row of
|
|
23953
|
+
for (const row of models2) {
|
|
23912
23954
|
if (row.hidden === true || typeof row.id !== "string" || !MODEL_ID2.test(row.id) || seen.has(row.id)) continue;
|
|
23913
23955
|
seen.add(row.id);
|
|
23914
23956
|
options.push({
|
|
@@ -23934,7 +23976,7 @@ function readCodexAppServerModelCatalog(cli, env, timeoutMs = 8e3) {
|
|
|
23934
23976
|
}
|
|
23935
23977
|
function codexHome(env) {
|
|
23936
23978
|
if (env.CODEX_HOME) return env.CODEX_HOME;
|
|
23937
|
-
return
|
|
23979
|
+
return join20(env.HOME || env.USERPROFILE || homedir10(), ".codex");
|
|
23938
23980
|
}
|
|
23939
23981
|
function unquote(raw) {
|
|
23940
23982
|
let value = raw.trim();
|
|
@@ -23997,7 +24039,7 @@ function parseCodexToml(text4) {
|
|
|
23997
24039
|
}
|
|
23998
24040
|
function readText(path3) {
|
|
23999
24041
|
try {
|
|
24000
|
-
return
|
|
24042
|
+
return readFileSync8(path3, "utf8");
|
|
24001
24043
|
} catch {
|
|
24002
24044
|
return null;
|
|
24003
24045
|
}
|
|
@@ -24020,11 +24062,11 @@ function niceLabel(model, provider, known, named) {
|
|
|
24020
24062
|
}
|
|
24021
24063
|
function collectCatalogNames(home) {
|
|
24022
24064
|
const named = /* @__PURE__ */ new Map();
|
|
24023
|
-
for (const file2 of listDir(
|
|
24065
|
+
for (const file2 of listDir(join20(home, "model-catalogs"))) {
|
|
24024
24066
|
if (!file2.endsWith(".json")) continue;
|
|
24025
24067
|
const provider = basename5(file2, ".json").replace(/-models$/, "");
|
|
24026
24068
|
if (!PROVIDER_ID.test(provider)) continue;
|
|
24027
|
-
const raw = readText(
|
|
24069
|
+
const raw = readText(join20(home, "model-catalogs", file2));
|
|
24028
24070
|
if (!raw) continue;
|
|
24029
24071
|
let parsed;
|
|
24030
24072
|
try {
|
|
@@ -24076,7 +24118,7 @@ async function probeProviderModels(provider, env, fetchImpl) {
|
|
|
24076
24118
|
async function readCodexModelCatalog(env = process.env, fetchImpl = fetch, cli) {
|
|
24077
24119
|
const official = (cli ? await readCodexAppServerModelCatalog(cli, env) : null) ?? STATIC_CODEX_MODELS;
|
|
24078
24120
|
const home = codexHome(env);
|
|
24079
|
-
const mainText = readText(
|
|
24121
|
+
const mainText = readText(join20(home, "config.toml"));
|
|
24080
24122
|
if (!mainText) return mergeLocalInject(official, env, fetchImpl);
|
|
24081
24123
|
const main2 = parseCodexToml(mainText);
|
|
24082
24124
|
const known = new Map(main2.providers.map((provider) => [provider.id, provider]));
|
|
@@ -24091,7 +24133,7 @@ async function readCodexModelCatalog(env = process.env, fetchImpl = fetch, cli)
|
|
|
24091
24133
|
remember(main2.modelProvider, main2.model);
|
|
24092
24134
|
for (const file2 of listDir(home)) {
|
|
24093
24135
|
if (!file2.endsWith(".config.toml")) continue;
|
|
24094
|
-
const profile = parseCodexToml(readText(
|
|
24136
|
+
const profile = parseCodexToml(readText(join20(home, file2)) ?? "");
|
|
24095
24137
|
for (const provider of profile.providers) {
|
|
24096
24138
|
if (!known.has(provider.id)) known.set(provider.id, provider);
|
|
24097
24139
|
}
|
|
@@ -24157,8 +24199,8 @@ var init_codex_catalog = __esm({
|
|
|
24157
24199
|
|
|
24158
24200
|
// server/drivers/codex-instructions.ts
|
|
24159
24201
|
import { createHash as createHash6 } from "node:crypto";
|
|
24160
|
-
import { mkdirSync as mkdirSync9, readFileSync as
|
|
24161
|
-
import { join as
|
|
24202
|
+
import { mkdirSync as mkdirSync9, readFileSync as readFileSync9 } from "node:fs";
|
|
24203
|
+
import { join as join21 } from "node:path";
|
|
24162
24204
|
function codexDeveloperInstructions(config2, botInstructions) {
|
|
24163
24205
|
if (!config2 || typeof config2 !== "object" || Array.isArray(config2)) {
|
|
24164
24206
|
throw new Error("Codex returned no effective configuration; cannot safely update bot instructions.");
|
|
@@ -24172,12 +24214,12 @@ function codexDeveloperInstructions(config2, botInstructions) {
|
|
|
24172
24214
|
${configured}` : botInstructions;
|
|
24173
24215
|
}
|
|
24174
24216
|
async function syncCodexInstructions(key, nativeThreadId, instructions, resumed, request) {
|
|
24175
|
-
const directory =
|
|
24176
|
-
const path3 =
|
|
24217
|
+
const directory = join21(DATA_DIR, "codex-instructions");
|
|
24218
|
+
const path3 = join21(directory, `${digest(JSON.stringify([key, nativeThreadId]))}.sha256`);
|
|
24177
24219
|
const fingerprint = digest(instructions);
|
|
24178
24220
|
let previous;
|
|
24179
24221
|
try {
|
|
24180
|
-
previous =
|
|
24222
|
+
previous = readFileSync9(path3, "utf8");
|
|
24181
24223
|
} catch (error51) {
|
|
24182
24224
|
if (error51.code !== "ENOENT") throw error51;
|
|
24183
24225
|
}
|
|
@@ -24220,19 +24262,19 @@ var init_codex_instructions = __esm({
|
|
|
24220
24262
|
import { randomUUID as randomUUID9 } from "node:crypto";
|
|
24221
24263
|
import { realpathSync as realpathSync2 } from "node:fs";
|
|
24222
24264
|
import { homedir as homedir11 } from "node:os";
|
|
24223
|
-
import { basename as basename6, dirname as dirname10, isAbsolute as isAbsolute5, join as
|
|
24265
|
+
import { basename as basename6, dirname as dirname10, isAbsolute as isAbsolute5, join as join22, resolve as resolve8 } from "node:path";
|
|
24224
24266
|
import { stripVTControlCharacters as stripVTControlCharacters5 } from "node:util";
|
|
24225
24267
|
function canonicalPath2(path3) {
|
|
24226
24268
|
try {
|
|
24227
24269
|
return realpathSync2(path3);
|
|
24228
24270
|
} catch {
|
|
24229
24271
|
const parent = dirname10(path3);
|
|
24230
|
-
return parent === path3 ? path3 :
|
|
24272
|
+
return parent === path3 ? path3 : join22(canonicalPath2(parent), basename6(path3));
|
|
24231
24273
|
}
|
|
24232
24274
|
}
|
|
24233
24275
|
function codexDevicePrompt(output) {
|
|
24234
|
-
const
|
|
24235
|
-
const lines =
|
|
24276
|
+
const clean = stripVTControlCharacters5(output);
|
|
24277
|
+
const lines = clean.split(/\r?\n/).slice(0, -1).map((line) => line.trim());
|
|
24236
24278
|
if (!lines.some((line) => /^https:\/\/auth\.openai\.com\/codex\/device\/?$/.test(line))) return null;
|
|
24237
24279
|
const userCode = lines.find((line) => /^[A-Z0-9]{4,8}-[A-Z0-9]{4,8}$/.test(line));
|
|
24238
24280
|
return userCode ? { authorizationUrl: DEVICE_URL, userCode } : null;
|
|
@@ -24278,7 +24320,7 @@ var init_codex_device_auth = __esm({
|
|
|
24278
24320
|
if (!isAbsolute5(home) || env.CODEX_HOME && !isAbsolute5(env.CODEX_HOME)) {
|
|
24279
24321
|
throw new Error("Use an absolute HOME and CODEX_HOME path for this server's Codex provider before signing in.");
|
|
24280
24322
|
}
|
|
24281
|
-
const homeKey = canonicalPath2(resolve8(env.CODEX_HOME ||
|
|
24323
|
+
const homeKey = canonicalPath2(resolve8(env.CODEX_HOME || join22(home, ".codex")));
|
|
24282
24324
|
if (authenticatingHomes2.has(homeKey)) {
|
|
24283
24325
|
throw new Error("A ChatGPT sign-in is already running for this server account. Finish or cancel it before starting another.");
|
|
24284
24326
|
}
|
|
@@ -24357,7 +24399,7 @@ var init_codex_device_auth = __esm({
|
|
|
24357
24399
|
if (!isAbsolute5(home) || env.CODEX_HOME && !isAbsolute5(env.CODEX_HOME)) {
|
|
24358
24400
|
throw new Error("Use an absolute HOME and CODEX_HOME path for this server's Codex provider before signing out.");
|
|
24359
24401
|
}
|
|
24360
|
-
const homeKey = canonicalPath2(resolve8(env.CODEX_HOME ||
|
|
24402
|
+
const homeKey = canonicalPath2(resolve8(env.CODEX_HOME || join22(home, ".codex")));
|
|
24361
24403
|
if (authenticatingHomes2.has(homeKey)) {
|
|
24362
24404
|
throw new Error("A ChatGPT sign-in is running for this server account. Finish or cancel it before signing out.");
|
|
24363
24405
|
}
|
|
@@ -24516,11 +24558,11 @@ var init_codex_device_auth = __esm({
|
|
|
24516
24558
|
|
|
24517
24559
|
// server/drivers/codex-identity.ts
|
|
24518
24560
|
import { homedir as homedir12 } from "node:os";
|
|
24519
|
-
import { isAbsolute as isAbsolute6, join as
|
|
24561
|
+
import { isAbsolute as isAbsolute6, join as join23, resolve as resolve9 } from "node:path";
|
|
24520
24562
|
function codexHome2(env) {
|
|
24521
24563
|
if (env.CODEX_HOME) return isAbsolute6(env.CODEX_HOME) ? resolve9(env.CODEX_HOME) : null;
|
|
24522
24564
|
const home = env.HOME || env.USERPROFILE || homedir12();
|
|
24523
|
-
return isAbsolute6(home) ?
|
|
24565
|
+
return isAbsolute6(home) ? join23(home, ".codex") : null;
|
|
24524
24566
|
}
|
|
24525
24567
|
async function codexAccountEmail(cli, env, timeoutMs = 3e3) {
|
|
24526
24568
|
const cwd = env.HOME || env.USERPROFILE || homedir12();
|
|
@@ -24650,8 +24692,8 @@ function codexUpdateCommand(cli, platform = process.platform) {
|
|
|
24650
24692
|
const command = (tokens.length > 0 ? tokens : [trimmed]).map(quote).join(" ");
|
|
24651
24693
|
return platform === "win32" ? `& ${command} update` : `${command} update`;
|
|
24652
24694
|
}
|
|
24653
|
-
function codexAstraUpdate(version2,
|
|
24654
|
-
if (
|
|
24695
|
+
function codexAstraUpdate(version2, models2, cli) {
|
|
24696
|
+
if (models2.options.some((model) => model.id === ASTRA_MODEL_ID) || !codexPredatesAstra(version2)) {
|
|
24655
24697
|
return void 0;
|
|
24656
24698
|
}
|
|
24657
24699
|
return {
|
|
@@ -24945,11 +24987,11 @@ var init_codex = __esm({
|
|
|
24945
24987
|
return env;
|
|
24946
24988
|
};
|
|
24947
24989
|
const catalogEnv = childEnv();
|
|
24948
|
-
let
|
|
24990
|
+
let models2 = STATIC_CODEX_MODELS;
|
|
24949
24991
|
const refreshModels = async () => {
|
|
24950
24992
|
try {
|
|
24951
24993
|
const resolved = await readCodexModelCatalog(catalogEnv, fetch, config2.cli);
|
|
24952
|
-
if (resolved.options.length)
|
|
24994
|
+
if (resolved.options.length) models2 = resolved;
|
|
24953
24995
|
} catch {
|
|
24954
24996
|
}
|
|
24955
24997
|
};
|
|
@@ -25244,7 +25286,8 @@ var init_codex = __esm({
|
|
|
25244
25286
|
itemType: "tool",
|
|
25245
25287
|
itemId: item.id,
|
|
25246
25288
|
title,
|
|
25247
|
-
summary: item.type === "commandExecution" ? commandSummary({ command: item.command }) : void 0
|
|
25289
|
+
summary: item.type === "commandExecution" ? commandSummary({ command: item.command }) : void 0,
|
|
25290
|
+
input: toolDetailPreview(item.type === "commandExecution" ? { command: item.command, cwd: item.cwd } : item.type === "mcpToolCall" ? item.arguments : item.type === "fileChange" ? item.changes : item.query)
|
|
25248
25291
|
});
|
|
25249
25292
|
}
|
|
25250
25293
|
break;
|
|
@@ -25277,7 +25320,8 @@ var init_codex = __esm({
|
|
|
25277
25320
|
type: "item.completed",
|
|
25278
25321
|
itemType: "tool",
|
|
25279
25322
|
itemId: item.id,
|
|
25280
|
-
ok: item.status !== "failed" && item.status !== "declined"
|
|
25323
|
+
ok: item.status !== "failed" && item.status !== "declined",
|
|
25324
|
+
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)
|
|
25281
25325
|
});
|
|
25282
25326
|
} else if (item.type === "reasoning") {
|
|
25283
25327
|
emit({ ...base2(threadId, turnId), type: "item.updated", itemType: "reasoning", tokens: null });
|
|
@@ -25568,7 +25612,7 @@ ${stderr ?? ""}`))
|
|
|
25568
25612
|
version: version2,
|
|
25569
25613
|
authenticated,
|
|
25570
25614
|
...email3 ? { account: { email: email3 } } : {},
|
|
25571
|
-
update: codexAstraUpdate(version2,
|
|
25615
|
+
update: codexAstraUpdate(version2, models2, config2.cli),
|
|
25572
25616
|
billing: "subscription"
|
|
25573
25617
|
};
|
|
25574
25618
|
};
|
|
@@ -25578,7 +25622,7 @@ ${stderr ?? ""}`))
|
|
|
25578
25622
|
displayName: input.displayName,
|
|
25579
25623
|
enabled: input.enabled,
|
|
25580
25624
|
get models() {
|
|
25581
|
-
return
|
|
25625
|
+
return models2;
|
|
25582
25626
|
},
|
|
25583
25627
|
refreshModels,
|
|
25584
25628
|
startAuthentication: () => authentication.start(),
|
|
@@ -25925,12 +25969,12 @@ var init_grok = __esm({
|
|
|
25925
25969
|
});
|
|
25926
25970
|
|
|
25927
25971
|
// server/drivers/acp/grok.ts
|
|
25928
|
-
import { existsSync as existsSync11, readFileSync as
|
|
25972
|
+
import { existsSync as existsSync11, readFileSync as readFileSync10, writeFileSync as writeFileSync6 } from "node:fs";
|
|
25929
25973
|
import { homedir as homedir14 } from "node:os";
|
|
25930
|
-
import { join as
|
|
25974
|
+
import { join as join24 } from "node:path";
|
|
25931
25975
|
function grokHome(env) {
|
|
25932
25976
|
if (env.GROK_HOME) return env.GROK_HOME;
|
|
25933
|
-
return
|
|
25977
|
+
return join24(env.HOME || env.USERPROFILE || homedir14(), ".grok");
|
|
25934
25978
|
}
|
|
25935
25979
|
function unquote2(raw) {
|
|
25936
25980
|
const value = raw.trim();
|
|
@@ -25940,10 +25984,10 @@ function unquote2(raw) {
|
|
|
25940
25984
|
return value;
|
|
25941
25985
|
}
|
|
25942
25986
|
function readGrokModelCatalog(env = process.env) {
|
|
25943
|
-
const path3 =
|
|
25987
|
+
const path3 = join24(grokHome(env), "config.toml");
|
|
25944
25988
|
let text4 = "";
|
|
25945
25989
|
try {
|
|
25946
|
-
text4 =
|
|
25990
|
+
text4 = readFileSync10(path3, "utf8");
|
|
25947
25991
|
} catch {
|
|
25948
25992
|
return STATIC_GROK_MODELS;
|
|
25949
25993
|
}
|
|
@@ -26013,10 +26057,10 @@ function ensureGrokInjectSlug(modelId, env = process.env) {
|
|
|
26013
26057
|
if (!inject) return modelId;
|
|
26014
26058
|
const host = localHost(inject.host);
|
|
26015
26059
|
if (!host) return modelId;
|
|
26016
|
-
const path3 =
|
|
26060
|
+
const path3 = join24(grokHome(env), "config.toml");
|
|
26017
26061
|
let text4 = "";
|
|
26018
26062
|
try {
|
|
26019
|
-
text4 =
|
|
26063
|
+
text4 = readFileSync10(path3, "utf8");
|
|
26020
26064
|
} catch {
|
|
26021
26065
|
text4 = "";
|
|
26022
26066
|
}
|
|
@@ -26067,7 +26111,7 @@ function ensureGrokInjectSlug(modelId, env = process.env) {
|
|
|
26067
26111
|
const next = text4 && !text4.endsWith("\n") ? `${text4}
|
|
26068
26112
|
|
|
26069
26113
|
${block2}` : `${text4}${text4 ? "\n" : ""}${block2}`;
|
|
26070
|
-
|
|
26114
|
+
writeFileSync6(path3, next);
|
|
26071
26115
|
return slug;
|
|
26072
26116
|
}
|
|
26073
26117
|
function grokAcceptsUnadvertisedImages(init) {
|
|
@@ -26159,7 +26203,7 @@ var init_grok2 = __esm({
|
|
|
26159
26203
|
// an unauthenticated CLI is a user action, not something to paper over.
|
|
26160
26204
|
pickAuthMethod: (methods) => methods.some((m) => m.id === "cached_token") ? "cached_token" : null,
|
|
26161
26205
|
authFailure: "fail",
|
|
26162
|
-
isAuthenticated: () => existsSync11(
|
|
26206
|
+
isAuthenticated: () => existsSync11(join24(homedir14(), ".grok", "auth.json")),
|
|
26163
26207
|
// `--append-system-prompt`/`--rules` are accepted by the CLI but do NOT
|
|
26164
26208
|
// reach the agent-stdio system prompt (verified against 1.0.0), so the
|
|
26165
26209
|
// persona is prepended codex-style.
|
|
@@ -26172,12 +26216,12 @@ ${turn.text}` : turn.text
|
|
|
26172
26216
|
});
|
|
26173
26217
|
|
|
26174
26218
|
// server/drivers/acp/gemini.ts
|
|
26175
|
-
import { existsSync as existsSync12, readFileSync as
|
|
26219
|
+
import { existsSync as existsSync12, readFileSync as readFileSync11 } from "node:fs";
|
|
26176
26220
|
import { homedir as homedir15 } from "node:os";
|
|
26177
|
-
import { join as
|
|
26221
|
+
import { join as join25 } from "node:path";
|
|
26178
26222
|
function liveOauthCredential() {
|
|
26179
26223
|
try {
|
|
26180
|
-
const creds = JSON.parse(
|
|
26224
|
+
const creds = JSON.parse(readFileSync11(join25(homedir15(), ".gemini", "oauth_creds.json"), "utf8"));
|
|
26181
26225
|
if (typeof creds.access_token !== "string" || creds.access_token.trim().length === 0) return false;
|
|
26182
26226
|
const hasRefresh = typeof creds.refresh_token === "string" && creds.refresh_token.trim().length > 0;
|
|
26183
26227
|
if (creds.expiry_date !== void 0) {
|
|
@@ -26190,7 +26234,7 @@ function liveOauthCredential() {
|
|
|
26190
26234
|
}
|
|
26191
26235
|
}
|
|
26192
26236
|
function geminiIsAuthenticated(env) {
|
|
26193
|
-
return nonBlank(env.GEMINI_API_KEY) || nonBlank(env.GOOGLE_API_KEY) || existsSync12(
|
|
26237
|
+
return nonBlank(env.GEMINI_API_KEY) || nonBlank(env.GOOGLE_API_KEY) || existsSync12(join25(homedir15(), ".gemini", "oauth_creds.json")) && liveOauthCredential();
|
|
26194
26238
|
}
|
|
26195
26239
|
var AUTH_PREFERENCE, nonBlank, support3, GeminiAgentDriver;
|
|
26196
26240
|
var init_gemini = __esm({
|
|
@@ -26232,17 +26276,17 @@ ${turn.text}` : turn.text
|
|
|
26232
26276
|
});
|
|
26233
26277
|
|
|
26234
26278
|
// server/drivers/acp/kimi.ts
|
|
26235
|
-
import { existsSync as existsSync13, mkdirSync as mkdirSync10, readFileSync as
|
|
26279
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync10, readFileSync as readFileSync12, writeFileSync as writeFileSync7 } from "node:fs";
|
|
26236
26280
|
import { homedir as homedir16 } from "node:os";
|
|
26237
|
-
import { join as
|
|
26281
|
+
import { join as join26 } from "node:path";
|
|
26238
26282
|
function isLocalInjectAlias(slug) {
|
|
26239
26283
|
return LOCAL_PROVIDER_PREFIXES.some((prefix) => slug.startsWith(prefix));
|
|
26240
26284
|
}
|
|
26241
26285
|
function kimiDataRoot(env) {
|
|
26242
|
-
return env.KIMI_CODE_HOME ||
|
|
26286
|
+
return env.KIMI_CODE_HOME || join26(env.HOME || env.USERPROFILE || homedir16(), ".kimi-code");
|
|
26243
26287
|
}
|
|
26244
26288
|
function credentialsPath(env) {
|
|
26245
|
-
return
|
|
26289
|
+
return join26(kimiDataRoot(env), "credentials", "kimi-code.json");
|
|
26246
26290
|
}
|
|
26247
26291
|
function quoteToml2(value) {
|
|
26248
26292
|
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
@@ -26530,10 +26574,10 @@ function ensureKimiInjectAlias(modelId, env = process.env) {
|
|
|
26530
26574
|
const alias = `${inject.host}/${inject.model.replace(/\//g, "-")}`;
|
|
26531
26575
|
const dataRoot = kimiDataRoot(env);
|
|
26532
26576
|
mkdirSync10(dataRoot, { recursive: true });
|
|
26533
|
-
const path3 =
|
|
26577
|
+
const path3 = join26(dataRoot, "config.toml");
|
|
26534
26578
|
let text4 = "";
|
|
26535
26579
|
try {
|
|
26536
|
-
text4 =
|
|
26580
|
+
text4 = readFileSync12(path3, "utf8");
|
|
26537
26581
|
} catch {
|
|
26538
26582
|
text4 = "";
|
|
26539
26583
|
}
|
|
@@ -26571,9 +26615,9 @@ function ensureKimiInjectAlias(modelId, env = process.env) {
|
|
|
26571
26615
|
|
|
26572
26616
|
` : text4 ? `${text4}
|
|
26573
26617
|
` : "";
|
|
26574
|
-
|
|
26618
|
+
writeFileSync7(path3, `${prefix}${blocks.join("\n")}`);
|
|
26575
26619
|
} else if (text4 !== original) {
|
|
26576
|
-
|
|
26620
|
+
writeFileSync7(path3, text4);
|
|
26577
26621
|
}
|
|
26578
26622
|
return alias;
|
|
26579
26623
|
}
|
|
@@ -26595,7 +26639,7 @@ function readKimiModelCatalog(env) {
|
|
|
26595
26639
|
const dataRoot = kimiDataRoot(env);
|
|
26596
26640
|
let text4 = "";
|
|
26597
26641
|
try {
|
|
26598
|
-
text4 =
|
|
26642
|
+
text4 = readFileSync12(join26(dataRoot, "config.toml"), "utf8");
|
|
26599
26643
|
} catch {
|
|
26600
26644
|
return STATIC_KIMI_MODELS;
|
|
26601
26645
|
}
|
|
@@ -26714,12 +26758,12 @@ ${turn.text}` : turn.text
|
|
|
26714
26758
|
});
|
|
26715
26759
|
|
|
26716
26760
|
// server/drivers/acp/droid.ts
|
|
26717
|
-
import { existsSync as existsSync14, mkdirSync as mkdirSync11, readFileSync as
|
|
26761
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync11, readFileSync as readFileSync13, writeFileSync as writeFileSync8 } from "node:fs";
|
|
26718
26762
|
import { homedir as homedir17 } from "node:os";
|
|
26719
|
-
import { join as
|
|
26763
|
+
import { join as join27 } from "node:path";
|
|
26720
26764
|
function authFilePaths(env) {
|
|
26721
26765
|
const home = env.FACTORY_HOME_OVERRIDE || env.HOME || homedir17();
|
|
26722
|
-
return AUTH_FILES.map((name) =>
|
|
26766
|
+
return AUTH_FILES.map((name) => join27(home, ".factory", name));
|
|
26723
26767
|
}
|
|
26724
26768
|
function droidInjectId(host, model) {
|
|
26725
26769
|
const safe = `${host}-${model}`.replace(/[^a-zA-Z0-9._+-]+/g, "-").replace(/-+/g, "-");
|
|
@@ -26734,12 +26778,12 @@ function ensureDroidInjectModel(modelId, env = process.env) {
|
|
|
26734
26778
|
const host = localHost(inject.host);
|
|
26735
26779
|
if (!host) return modelId;
|
|
26736
26780
|
const id = droidInjectId(inject.host, inject.model);
|
|
26737
|
-
const dir =
|
|
26781
|
+
const dir = join27(factoryHome(env), ".factory");
|
|
26738
26782
|
mkdirSync11(dir, { recursive: true });
|
|
26739
|
-
const path3 =
|
|
26783
|
+
const path3 = join27(dir, "settings.json");
|
|
26740
26784
|
let settings2 = {};
|
|
26741
26785
|
try {
|
|
26742
|
-
settings2 = JSON.parse(
|
|
26786
|
+
settings2 = JSON.parse(readFileSync13(path3, "utf8"));
|
|
26743
26787
|
} catch (error51) {
|
|
26744
26788
|
if (existsSync14(path3)) throw error51;
|
|
26745
26789
|
}
|
|
@@ -26751,7 +26795,7 @@ function ensureDroidInjectModel(modelId, env = process.env) {
|
|
|
26751
26795
|
if (!match.id) {
|
|
26752
26796
|
match.id = id;
|
|
26753
26797
|
settings2.customModels = custom2;
|
|
26754
|
-
|
|
26798
|
+
writeFileSync8(path3, `${JSON.stringify(settings2, null, 2)}
|
|
26755
26799
|
`);
|
|
26756
26800
|
}
|
|
26757
26801
|
return match.id;
|
|
@@ -26765,7 +26809,7 @@ function ensureDroidInjectModel(modelId, env = process.env) {
|
|
|
26765
26809
|
provider: "generic-chat-completion-api"
|
|
26766
26810
|
});
|
|
26767
26811
|
settings2.customModels = custom2;
|
|
26768
|
-
|
|
26812
|
+
writeFileSync8(path3, `${JSON.stringify(settings2, null, 2)}
|
|
26769
26813
|
`);
|
|
26770
26814
|
return id;
|
|
26771
26815
|
}
|
|
@@ -26776,7 +26820,7 @@ function applyDroidLocalAuthEnv(env, modelId) {
|
|
|
26776
26820
|
env.FACTORY_API_KEY = "openmausbot-local";
|
|
26777
26821
|
}
|
|
26778
26822
|
function readSettings(env) {
|
|
26779
|
-
return JSON.parse(
|
|
26823
|
+
return JSON.parse(readFileSync13(join27(factoryHome(env), ".factory", "settings.json"), "utf8"));
|
|
26780
26824
|
}
|
|
26781
26825
|
async function resolveModels(env) {
|
|
26782
26826
|
let catalog = MODELS3;
|
|
@@ -27166,9 +27210,9 @@ ${turn.text}` : turn.text
|
|
|
27166
27210
|
});
|
|
27167
27211
|
|
|
27168
27212
|
// server/drivers/acp/opencode-go.ts
|
|
27169
|
-
import { existsSync as existsSync15, mkdirSync as mkdirSync12, readFileSync as
|
|
27213
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync12, readFileSync as readFileSync14, writeFileSync as writeFileSync9 } from "node:fs";
|
|
27170
27214
|
import { homedir as homedir18 } from "node:os";
|
|
27171
|
-
import { join as
|
|
27215
|
+
import { join as join28 } from "node:path";
|
|
27172
27216
|
function labelForModel(id) {
|
|
27173
27217
|
return id.split(/[-_.]+/g).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
27174
27218
|
}
|
|
@@ -27269,7 +27313,7 @@ async function discoverOpenCodeModels(environment, cli = "opencode") {
|
|
|
27269
27313
|
}
|
|
27270
27314
|
function opencodeConfigDir(env) {
|
|
27271
27315
|
const home = env.HOME || env.USERPROFILE || homedir18();
|
|
27272
|
-
return
|
|
27316
|
+
return join28(env.XDG_CONFIG_HOME || join28(home, ".config"), "opencode");
|
|
27273
27317
|
}
|
|
27274
27318
|
function ensureOpenCodeInjectModel(modelId, env = process.env) {
|
|
27275
27319
|
const inject = decodeInjectId(modelId);
|
|
@@ -27279,11 +27323,11 @@ function ensureOpenCodeInjectModel(modelId, env = process.env) {
|
|
|
27279
27323
|
const native = `${inject.host}/${inject.model}`;
|
|
27280
27324
|
const dir = opencodeConfigDir(env);
|
|
27281
27325
|
mkdirSync12(dir, { recursive: true });
|
|
27282
|
-
const path3 =
|
|
27326
|
+
const path3 = join28(dir, "opencode.json");
|
|
27283
27327
|
let config2 = { $schema: "https://opencode.ai/config.json" };
|
|
27284
27328
|
if (existsSync15(path3)) {
|
|
27285
27329
|
try {
|
|
27286
|
-
config2 = JSON.parse(
|
|
27330
|
+
config2 = JSON.parse(readFileSync14(path3, "utf8"));
|
|
27287
27331
|
} catch {
|
|
27288
27332
|
}
|
|
27289
27333
|
}
|
|
@@ -27298,29 +27342,29 @@ function ensureOpenCodeInjectModel(modelId, env = process.env) {
|
|
|
27298
27342
|
const options = existing.options && typeof existing.options === "object" && !Array.isArray(existing.options) ? { ...existing.options } : {};
|
|
27299
27343
|
options.baseURL = host.baseUrl;
|
|
27300
27344
|
if (!options.apiKey) options.apiKey = hostApiKey(host, env);
|
|
27301
|
-
const
|
|
27302
|
-
if (!
|
|
27303
|
-
|
|
27345
|
+
const models2 = existing.models && typeof existing.models === "object" && !Array.isArray(existing.models) ? { ...existing.models } : {};
|
|
27346
|
+
if (!models2[inject.model]) {
|
|
27347
|
+
models2[inject.model] = { name: `${inject.model} (${host.label})` };
|
|
27304
27348
|
}
|
|
27305
27349
|
providers[inject.host] = {
|
|
27306
27350
|
...existing,
|
|
27307
27351
|
npm: existing.npm || "@ai-sdk/openai-compatible",
|
|
27308
27352
|
name: existing.name || host.label,
|
|
27309
27353
|
options,
|
|
27310
|
-
models
|
|
27354
|
+
models: models2
|
|
27311
27355
|
};
|
|
27312
27356
|
config2.provider = providers;
|
|
27313
|
-
|
|
27357
|
+
writeFileSync9(path3, `${JSON.stringify(config2, null, 2)}
|
|
27314
27358
|
`);
|
|
27315
27359
|
return native;
|
|
27316
27360
|
}
|
|
27317
27361
|
function storedAuthPaths(env) {
|
|
27318
27362
|
const home = env.HOME || env.USERPROFILE || homedir18();
|
|
27319
27363
|
const roots = [
|
|
27320
|
-
env.XDG_DATA_HOME ||
|
|
27321
|
-
process.platform === "darwin" ?
|
|
27364
|
+
env.XDG_DATA_HOME || join28(home, ".local", "share"),
|
|
27365
|
+
process.platform === "darwin" ? join28(home, "Library", "Application Support") : process.platform === "win32" ? env.LOCALAPPDATA || join28(home, "AppData", "Local") : ""
|
|
27322
27366
|
].filter(Boolean);
|
|
27323
|
-
return [...new Set(roots)].map((root) =>
|
|
27367
|
+
return [...new Set(roots)].map((root) => join28(root, "opencode", "auth.json"));
|
|
27324
27368
|
}
|
|
27325
27369
|
function usableAuthEntry(parsed) {
|
|
27326
27370
|
return Object.values(parsed).some((auth) => {
|
|
@@ -27334,7 +27378,7 @@ function hasStoredOpenCodeAuth(env) {
|
|
|
27334
27378
|
if (env.OPENCODE_AUTH_CONTENT) candidates.push(env.OPENCODE_AUTH_CONTENT);
|
|
27335
27379
|
for (const path3 of storedAuthPaths(env)) {
|
|
27336
27380
|
try {
|
|
27337
|
-
candidates.push(
|
|
27381
|
+
candidates.push(readFileSync14(path3, "utf8"));
|
|
27338
27382
|
} catch {
|
|
27339
27383
|
}
|
|
27340
27384
|
}
|
|
@@ -27450,13 +27494,13 @@ ${turn.text}` : turn.text
|
|
|
27450
27494
|
});
|
|
27451
27495
|
|
|
27452
27496
|
// server/drivers/acp/qwen.ts
|
|
27453
|
-
import { chmodSync as chmodSync5, existsSync as existsSync16, mkdirSync as mkdirSync13, readFileSync as
|
|
27497
|
+
import { chmodSync as chmodSync5, existsSync as existsSync16, mkdirSync as mkdirSync13, readFileSync as readFileSync15, writeFileSync as writeFileSync10 } from "node:fs";
|
|
27454
27498
|
import { createHash as createHash7 } from "node:crypto";
|
|
27455
27499
|
import { homedir as homedir19 } from "node:os";
|
|
27456
|
-
import { join as
|
|
27500
|
+
import { join as join29 } from "node:path";
|
|
27457
27501
|
function qwenHome(env) {
|
|
27458
27502
|
const home = process.platform === "win32" ? env.USERPROFILE || env.HOME || homedir19() : env.HOME || env.USERPROFILE || homedir19();
|
|
27459
|
-
return
|
|
27503
|
+
return join29(home, ".qwen");
|
|
27460
27504
|
}
|
|
27461
27505
|
function isRecord2(value) {
|
|
27462
27506
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
@@ -27468,7 +27512,7 @@ function qwenModelLabel(id, name) {
|
|
|
27468
27512
|
function readQwenRoutes(env) {
|
|
27469
27513
|
let settings2;
|
|
27470
27514
|
try {
|
|
27471
|
-
settings2 = JSON.parse(
|
|
27515
|
+
settings2 = JSON.parse(readFileSync15(join29(qwenHome(env), "settings.json"), "utf8"));
|
|
27472
27516
|
} catch {
|
|
27473
27517
|
return [];
|
|
27474
27518
|
}
|
|
@@ -27552,11 +27596,11 @@ function ensureQwenInjectModel(modelId, env = process.env) {
|
|
|
27552
27596
|
if (!host) return modelId;
|
|
27553
27597
|
const dir = qwenHome(env);
|
|
27554
27598
|
mkdirSync13(dir, { recursive: true });
|
|
27555
|
-
const path3 =
|
|
27599
|
+
const path3 = join29(dir, "settings.json");
|
|
27556
27600
|
let settings2 = {};
|
|
27557
27601
|
if (existsSync16(path3)) {
|
|
27558
27602
|
try {
|
|
27559
|
-
settings2 = JSON.parse(
|
|
27603
|
+
settings2 = JSON.parse(readFileSync15(path3, "utf8"));
|
|
27560
27604
|
} catch {
|
|
27561
27605
|
}
|
|
27562
27606
|
}
|
|
@@ -27580,7 +27624,7 @@ function ensureQwenInjectModel(modelId, env = process.env) {
|
|
|
27580
27624
|
providers.openai = openai;
|
|
27581
27625
|
settings2.modelProviders = providers;
|
|
27582
27626
|
}
|
|
27583
|
-
|
|
27627
|
+
writeFileSync10(path3, `${JSON.stringify(settings2, null, 2)}
|
|
27584
27628
|
`, { mode: 384 });
|
|
27585
27629
|
try {
|
|
27586
27630
|
chmodSync5(path3, 384);
|
|
@@ -29644,7 +29688,7 @@ var init_YAMLMap = __esm({
|
|
|
29644
29688
|
static from(schema4, obj, ctx) {
|
|
29645
29689
|
const { keepUndefined, replacer } = ctx;
|
|
29646
29690
|
const map3 = new this(schema4);
|
|
29647
|
-
const
|
|
29691
|
+
const add = (key, value) => {
|
|
29648
29692
|
if (typeof replacer === "function")
|
|
29649
29693
|
value = replacer.call(obj, key, value);
|
|
29650
29694
|
else if (Array.isArray(replacer) && !replacer.includes(key))
|
|
@@ -29654,10 +29698,10 @@ var init_YAMLMap = __esm({
|
|
|
29654
29698
|
};
|
|
29655
29699
|
if (obj instanceof Map) {
|
|
29656
29700
|
for (const [key, value] of obj)
|
|
29657
|
-
|
|
29701
|
+
add(key, value);
|
|
29658
29702
|
} else if (obj && typeof obj === "object") {
|
|
29659
29703
|
for (const key of Object.keys(obj))
|
|
29660
|
-
|
|
29704
|
+
add(key, obj[key]);
|
|
29661
29705
|
}
|
|
29662
29706
|
if (typeof schema4.sortMapEntries === "function") {
|
|
29663
29707
|
map3.items.sort(schema4.sortMapEntries);
|
|
@@ -34551,9 +34595,9 @@ var init_browser = __esm({
|
|
|
34551
34595
|
|
|
34552
34596
|
// server/drivers/acp/hermes.ts
|
|
34553
34597
|
import { spawn as spawn6 } from "node:child_process";
|
|
34554
|
-
import { mkdirSync as mkdirSync14, readFileSync as
|
|
34598
|
+
import { mkdirSync as mkdirSync14, readFileSync as readFileSync16, writeFileSync as writeFileSync11 } from "node:fs";
|
|
34555
34599
|
import { homedir as homedir20 } from "node:os";
|
|
34556
|
-
import { join as
|
|
34600
|
+
import { join as join30 } from "node:path";
|
|
34557
34601
|
function bindHermesScreenshotCompat(env, modelId) {
|
|
34558
34602
|
delete env[HERMES_OPENMAUS_SCREENSHOT_COMPAT];
|
|
34559
34603
|
delete env[HERMES_OPENMAUS_SCREENSHOT_COMPAT_MODEL];
|
|
@@ -34563,7 +34607,7 @@ function bindHermesScreenshotCompat(env, modelId) {
|
|
|
34563
34607
|
env[HERMES_OPENMAUS_SCREENSHOT_COMPAT_MODEL] = inject.model;
|
|
34564
34608
|
}
|
|
34565
34609
|
function hermesHome(env) {
|
|
34566
|
-
return env.HERMES_HOME ||
|
|
34610
|
+
return env.HERMES_HOME || join30(env.HOME || env.USERPROFILE || homedir20(), ".hermes");
|
|
34567
34611
|
}
|
|
34568
34612
|
function quoteYaml(value) {
|
|
34569
34613
|
if (/^[\w./:+-]+$/.test(value)) return value;
|
|
@@ -34605,15 +34649,15 @@ function ensureHermesInjectProvider(modelId, env = process.env) {
|
|
|
34605
34649
|
if (!host) return modelId;
|
|
34606
34650
|
const dir = hermesHome(env);
|
|
34607
34651
|
mkdirSync14(dir, { recursive: true });
|
|
34608
|
-
const path3 =
|
|
34652
|
+
const path3 = join30(dir, "config.yaml");
|
|
34609
34653
|
let text4 = "";
|
|
34610
34654
|
try {
|
|
34611
|
-
text4 =
|
|
34655
|
+
text4 = readFileSync16(path3, "utf8");
|
|
34612
34656
|
} catch {
|
|
34613
34657
|
text4 = "";
|
|
34614
34658
|
}
|
|
34615
34659
|
const next = upsertHermesProvider(text4, inject.host, host.baseUrl, hostApiKey(host, env));
|
|
34616
|
-
if (next !== text4)
|
|
34660
|
+
if (next !== text4) writeFileSync11(path3, next);
|
|
34617
34661
|
return hermesAcpModelId(modelId) ?? modelId;
|
|
34618
34662
|
}
|
|
34619
34663
|
function hermesAcpModelId(modelId) {
|
|
@@ -34676,13 +34720,13 @@ function hermesConfiguredModel(env = process.env) {
|
|
|
34676
34720
|
const dir = hermesHome(env);
|
|
34677
34721
|
let secrets = "";
|
|
34678
34722
|
try {
|
|
34679
|
-
secrets =
|
|
34723
|
+
secrets = readFileSync16(join30(dir, ".env"), "utf8");
|
|
34680
34724
|
} catch {
|
|
34681
34725
|
}
|
|
34682
34726
|
const hasHostedProviderKey = HERMES_HOSTED_PROVIDER_KEYS.some((name) => nonEmptyDotenvValue(secrets, name));
|
|
34683
34727
|
let configuredDefault = null;
|
|
34684
34728
|
try {
|
|
34685
|
-
configuredDefault = hermesConfigDefault(
|
|
34729
|
+
configuredDefault = hermesConfigDefault(readFileSync16(join30(dir, "config.yaml"), "utf8"));
|
|
34686
34730
|
} catch {
|
|
34687
34731
|
}
|
|
34688
34732
|
const configuredProvider = configuredDefault?.provider.toLowerCase() ?? "";
|
|
@@ -35019,13 +35063,13 @@ var init_openai_compat = __esm({
|
|
|
35019
35063
|
});
|
|
35020
35064
|
|
|
35021
35065
|
// server/drivers/pi.ts
|
|
35022
|
-
import { chmodSync as chmodSync6, existsSync as existsSync17, mkdirSync as mkdirSync15, mkdtempSync as mkdtempSync3, readFileSync as
|
|
35066
|
+
import { chmodSync as chmodSync6, existsSync as existsSync17, mkdirSync as mkdirSync15, mkdtempSync as mkdtempSync3, readFileSync as readFileSync17, rmSync as rmSync5, writeFileSync as writeFileSync12 } from "node:fs";
|
|
35023
35067
|
import { homedir as homedir21, tmpdir as tmpdir4 } from "node:os";
|
|
35024
|
-
import { join as
|
|
35068
|
+
import { join as join31 } from "node:path";
|
|
35025
35069
|
function readPiPromptImages(turn) {
|
|
35026
35070
|
return (turn.images ?? []).map((image) => ({
|
|
35027
35071
|
type: "image",
|
|
35028
|
-
data:
|
|
35072
|
+
data: readFileSync17(image.path).toString("base64"),
|
|
35029
35073
|
mimeType: image.mime
|
|
35030
35074
|
}));
|
|
35031
35075
|
}
|
|
@@ -35128,7 +35172,7 @@ async function applyPiLocalCatalog(catalog, env = process.env, fetchImpl = fetch
|
|
|
35128
35172
|
return preferPiInjectRows(await mergeLocalInject(catalog, env, fetchImpl));
|
|
35129
35173
|
}
|
|
35130
35174
|
function piAgentDir(env) {
|
|
35131
|
-
return
|
|
35175
|
+
return join31(env.HOME || env.USERPROFILE || homedir21(), ".pi", "agent");
|
|
35132
35176
|
}
|
|
35133
35177
|
function ensurePiInjectModel(modelId, env = process.env) {
|
|
35134
35178
|
const split = splitPiModel(modelId);
|
|
@@ -35139,11 +35183,11 @@ function ensurePiInjectModel(modelId, env = process.env) {
|
|
|
35139
35183
|
if (!host) return split;
|
|
35140
35184
|
const dir = piAgentDir(env);
|
|
35141
35185
|
mkdirSync15(dir, { recursive: true });
|
|
35142
|
-
const path3 =
|
|
35186
|
+
const path3 = join31(dir, "models.json");
|
|
35143
35187
|
let root = { providers: {} };
|
|
35144
35188
|
if (existsSync17(path3)) {
|
|
35145
35189
|
try {
|
|
35146
|
-
const parsed = JSON.parse(
|
|
35190
|
+
const parsed = JSON.parse(readFileSync17(path3, "utf8"));
|
|
35147
35191
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
35148
35192
|
root = parsed;
|
|
35149
35193
|
} else {
|
|
@@ -35168,11 +35212,11 @@ function ensurePiInjectModel(modelId, env = process.env) {
|
|
|
35168
35212
|
if (!existing.compat) {
|
|
35169
35213
|
existing.compat = { supportsDeveloperRole: false, supportsReasoningEffort: true };
|
|
35170
35214
|
}
|
|
35171
|
-
const
|
|
35215
|
+
const models2 = Array.isArray(existing.models) ? existing.models.filter(
|
|
35172
35216
|
(row) => Boolean(row) && typeof row === "object" && !Array.isArray(row)
|
|
35173
35217
|
) : [];
|
|
35174
|
-
if (!
|
|
35175
|
-
|
|
35218
|
+
if (!models2.some((row) => row.id === inject.model)) {
|
|
35219
|
+
models2.push({
|
|
35176
35220
|
id: inject.model,
|
|
35177
35221
|
name: inject.model,
|
|
35178
35222
|
reasoning: true,
|
|
@@ -35181,10 +35225,10 @@ function ensurePiInjectModel(modelId, env = process.env) {
|
|
|
35181
35225
|
maxTokens: 16384
|
|
35182
35226
|
});
|
|
35183
35227
|
}
|
|
35184
|
-
existing.models =
|
|
35228
|
+
existing.models = models2;
|
|
35185
35229
|
providers[inject.host] = existing;
|
|
35186
35230
|
root.providers = providers;
|
|
35187
|
-
|
|
35231
|
+
writeFileSync12(path3, `${JSON.stringify(root, null, 2)}
|
|
35188
35232
|
`, { mode: 384 });
|
|
35189
35233
|
try {
|
|
35190
35234
|
chmodSync6(path3, 384);
|
|
@@ -35195,7 +35239,7 @@ function ensurePiInjectModel(modelId, env = process.env) {
|
|
|
35195
35239
|
function readPiDefaultModel(env) {
|
|
35196
35240
|
const home = env.HOME || env.USERPROFILE || homedir21();
|
|
35197
35241
|
try {
|
|
35198
|
-
const s = JSON.parse(
|
|
35242
|
+
const s = JSON.parse(readFileSync17(`${home}/.pi/agent/settings.json`, "utf8"));
|
|
35199
35243
|
return s.defaultProvider && s.defaultModel ? `${s.defaultProvider}/${s.defaultModel}` : "";
|
|
35200
35244
|
} catch {
|
|
35201
35245
|
return "";
|
|
@@ -35279,6 +35323,7 @@ var init_pi = __esm({
|
|
|
35279
35323
|
init_env_path();
|
|
35280
35324
|
init_procs();
|
|
35281
35325
|
init_proxy_paths();
|
|
35326
|
+
init_tool_summary();
|
|
35282
35327
|
init_contracts();
|
|
35283
35328
|
init_local_inject();
|
|
35284
35329
|
init_native();
|
|
@@ -35305,9 +35350,9 @@ var init_pi = __esm({
|
|
|
35305
35350
|
async create(input) {
|
|
35306
35351
|
const { instanceId, config: config2 } = input;
|
|
35307
35352
|
const catalogEnv = piEnvironment({ ...process.env, ...input.environment });
|
|
35308
|
-
let
|
|
35353
|
+
let models2 = EMPTY3;
|
|
35309
35354
|
const readModels = async () => {
|
|
35310
|
-
let base3 =
|
|
35355
|
+
let base3 = models2;
|
|
35311
35356
|
try {
|
|
35312
35357
|
const resolved = await fetchPiModels(config2.cli, catalogEnv);
|
|
35313
35358
|
if (resolved.options.length) base3 = resolved;
|
|
@@ -35315,9 +35360,9 @@ var init_pi = __esm({
|
|
|
35315
35360
|
}
|
|
35316
35361
|
try {
|
|
35317
35362
|
const next = await applyPiLocalCatalog(base3, catalogEnv);
|
|
35318
|
-
if (next.options.length)
|
|
35363
|
+
if (next.options.length) models2 = next;
|
|
35319
35364
|
} catch {
|
|
35320
|
-
if (base3.options.length)
|
|
35365
|
+
if (base3.options.length) models2 = base3;
|
|
35321
35366
|
}
|
|
35322
35367
|
};
|
|
35323
35368
|
const refreshModels = async () => {
|
|
@@ -35356,9 +35401,9 @@ var init_pi = __esm({
|
|
|
35356
35401
|
const mcpServers = buildMcpServers(turn);
|
|
35357
35402
|
let mcpTempDir = null;
|
|
35358
35403
|
if (mcpServers) {
|
|
35359
|
-
mcpTempDir = mkdtempSync3(
|
|
35404
|
+
mcpTempDir = mkdtempSync3(join31(tmpdir4(), "omb-pi-mcp-"));
|
|
35360
35405
|
try {
|
|
35361
|
-
|
|
35406
|
+
writeFileSync12(join31(mcpTempDir, "mcp.json"), JSON.stringify({ mcpServers }), { mode: 384 });
|
|
35362
35407
|
} catch (err) {
|
|
35363
35408
|
try {
|
|
35364
35409
|
rmSync5(mcpTempDir, { recursive: true, force: true });
|
|
@@ -35376,7 +35421,7 @@ var init_pi = __esm({
|
|
|
35376
35421
|
env: piEnvironment({
|
|
35377
35422
|
...process.env,
|
|
35378
35423
|
...input.environment,
|
|
35379
|
-
...mcpServers && mcpTempDir ? { OMB_MCP_CONFIG:
|
|
35424
|
+
...mcpServers && mcpTempDir ? { OMB_MCP_CONFIG: join31(mcpTempDir, "mcp.json") } : {}
|
|
35380
35425
|
})
|
|
35381
35426
|
});
|
|
35382
35427
|
} catch (err) {
|
|
@@ -35488,7 +35533,9 @@ var init_pi = __esm({
|
|
|
35488
35533
|
type: "item.started",
|
|
35489
35534
|
itemType: "tool",
|
|
35490
35535
|
itemId: evt.toolCallId,
|
|
35491
|
-
title: String(evt.toolName ?? "tool").slice(0, 80)
|
|
35536
|
+
title: String(evt.toolName ?? "tool").slice(0, 80),
|
|
35537
|
+
summary: commandSummary(evt.args),
|
|
35538
|
+
input: toolDetailPreview(evt.args)
|
|
35492
35539
|
});
|
|
35493
35540
|
return;
|
|
35494
35541
|
}
|
|
@@ -35498,7 +35545,8 @@ var init_pi = __esm({
|
|
|
35498
35545
|
type: "item.completed",
|
|
35499
35546
|
itemType: "tool",
|
|
35500
35547
|
itemId: evt.toolCallId,
|
|
35501
|
-
ok: !evt.isError
|
|
35548
|
+
ok: !evt.isError,
|
|
35549
|
+
output: toolDetailPreview(evt.result)
|
|
35502
35550
|
});
|
|
35503
35551
|
return;
|
|
35504
35552
|
}
|
|
@@ -35647,7 +35695,7 @@ ${turn.text}` : turn.text;
|
|
|
35647
35695
|
displayName: input.displayName,
|
|
35648
35696
|
enabled: input.enabled,
|
|
35649
35697
|
get models() {
|
|
35650
|
-
return
|
|
35698
|
+
return models2;
|
|
35651
35699
|
},
|
|
35652
35700
|
refreshModels,
|
|
35653
35701
|
snapshot,
|
|
@@ -35713,16 +35761,16 @@ ${turn.text}` : turn.text;
|
|
|
35713
35761
|
});
|
|
35714
35762
|
|
|
35715
35763
|
// server/drivers/minimax.ts
|
|
35716
|
-
import { readFileSync as
|
|
35764
|
+
import { readFileSync as readFileSync18 } from "node:fs";
|
|
35717
35765
|
import { homedir as homedir22 } from "node:os";
|
|
35718
|
-
import { join as
|
|
35766
|
+
import { join as join32 } from "node:path";
|
|
35719
35767
|
function normalizedApiUrl(value) {
|
|
35720
35768
|
const root = value.trim().replace(/\/+$/, "");
|
|
35721
35769
|
return root.endsWith("/v1") ? root : `${root}/v1`;
|
|
35722
35770
|
}
|
|
35723
35771
|
function loadLocalMiniMaxConfig(home = homedir22()) {
|
|
35724
35772
|
try {
|
|
35725
|
-
const raw = localConfigSchema.parse(JSON.parse(
|
|
35773
|
+
const raw = localConfigSchema.parse(JSON.parse(readFileSync18(join32(home, ".mmx", "config.json"), "utf8")));
|
|
35726
35774
|
const configuredUrl = raw.base_url?.trim() ? raw.base_url : raw.region === "cn" ? CN_URL : DEFAULT_URL2;
|
|
35727
35775
|
return {
|
|
35728
35776
|
apiKey: raw.api_key?.trim() ?? "",
|
|
@@ -35785,13 +35833,13 @@ var init_minimax = __esm({
|
|
|
35785
35833
|
const local = loadLocalMiniMaxConfig();
|
|
35786
35834
|
const apiKey = input.environment[API_KEY_ENV]?.trim() || process.env[API_KEY_ENV]?.trim() || local.apiKey;
|
|
35787
35835
|
const apiUrl = input.config.url === DEFAULT_URL2 && local.url !== DEFAULT_URL2 ? local.url : input.config.url;
|
|
35788
|
-
const
|
|
35836
|
+
const models2 = local.defaultModel && MODELS4.options.some((model) => model.id === local.defaultModel) ? { ...MODELS4, default: local.defaultModel } : MODELS4;
|
|
35789
35837
|
return createOpenAIChatRuntime({
|
|
35790
35838
|
input,
|
|
35791
35839
|
driverKind: DRIVER_KIND7,
|
|
35792
35840
|
apiKey,
|
|
35793
35841
|
apiUrl,
|
|
35794
|
-
models: () =>
|
|
35842
|
+
models: () => models2,
|
|
35795
35843
|
requestBody: (model, messages, stream) => ({
|
|
35796
35844
|
model,
|
|
35797
35845
|
messages,
|
|
@@ -35936,16 +35984,16 @@ async function setupJson(url2, key, body) {
|
|
|
35936
35984
|
}
|
|
35937
35985
|
async function fetchSetupModels(url2, key) {
|
|
35938
35986
|
const json2 = record2(await setupJson(url2, key));
|
|
35939
|
-
const
|
|
35987
|
+
const models2 = /* @__PURE__ */ new Map();
|
|
35940
35988
|
for (const entry of Array.isArray(json2?.data) ? json2.data : []) {
|
|
35941
35989
|
const item = record2(entry);
|
|
35942
35990
|
const id = typeof item?.id === "string" ? item.id.trim() : "";
|
|
35943
|
-
if (!id || /[\u0000-\u001f\u007f]/u.test(id) ||
|
|
35991
|
+
if (!id || /[\u0000-\u001f\u007f]/u.test(id) || models2.has(id)) continue;
|
|
35944
35992
|
const name = typeof item?.name === "string" ? item.name.replace(/[\u0000-\u001f\u007f]/gu, "").trim() : "";
|
|
35945
|
-
|
|
35993
|
+
models2.set(id, { id, label: name || id });
|
|
35946
35994
|
}
|
|
35947
|
-
if (!
|
|
35948
|
-
return [...
|
|
35995
|
+
if (!models2.size) throw new SetupApiError("The API returned no usable models. Check the API URL and model access, or enter a model ID from your provider manually.");
|
|
35996
|
+
return [...models2.values()];
|
|
35949
35997
|
}
|
|
35950
35998
|
async function verifySetupCompletion(url2, key, model, provider) {
|
|
35951
35999
|
const id = model.trim();
|
|
@@ -35989,8 +36037,8 @@ __export(cli_setup_exports, {
|
|
|
35989
36037
|
saveCliStartup: () => saveCliStartup
|
|
35990
36038
|
});
|
|
35991
36039
|
import { spawn as spawn7 } from "node:child_process";
|
|
35992
|
-
import { readFileSync as
|
|
35993
|
-
import { join as
|
|
36040
|
+
import { readFileSync as readFileSync19 } from "node:fs";
|
|
36041
|
+
import { join as join33, resolve as resolve10 } from "node:path";
|
|
35994
36042
|
import { randomUUID as randomUUID10 } from "node:crypto";
|
|
35995
36043
|
async function inspect(id, entry) {
|
|
35996
36044
|
const registry2 = new ProviderRegistry(BUILT_IN_DRIVERS);
|
|
@@ -36027,7 +36075,7 @@ function assertDataDir(dataDir) {
|
|
|
36027
36075
|
}
|
|
36028
36076
|
function checkStoredConfig(dataDir) {
|
|
36029
36077
|
try {
|
|
36030
|
-
parseStoredConfig(JSON.parse(
|
|
36078
|
+
parseStoredConfig(JSON.parse(readFileSync19(join33(dataDir, "config.json"), "utf8")));
|
|
36031
36079
|
} catch (error51) {
|
|
36032
36080
|
if (error51.code === "ENOENT") return;
|
|
36033
36081
|
throw new Error("Existing config.json could not be read or validated. Setup has not changed it; repair it before continuing.");
|
|
@@ -36059,14 +36107,14 @@ function saveCliStartup(dataDir, settings2) {
|
|
|
36059
36107
|
function rawObject(value) {
|
|
36060
36108
|
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
36061
36109
|
}
|
|
36062
|
-
async function chooseModel(io,
|
|
36063
|
-
if (!
|
|
36110
|
+
async function chooseModel(io, models2) {
|
|
36111
|
+
if (!models2.options.length) throw new Error("This connection returned no models. Check the provider and try again.");
|
|
36064
36112
|
let query = "";
|
|
36065
36113
|
for (; ; ) {
|
|
36066
|
-
const matches =
|
|
36067
|
-
const preferred = matches.find((m) => m.id ===
|
|
36114
|
+
const matches = models2.options.filter((m) => `${m.label} ${m.id}`.toLowerCase().includes(query.toLowerCase()));
|
|
36115
|
+
const preferred = matches.find((m) => m.id === models2.default);
|
|
36068
36116
|
const visible = (preferred ? [preferred, ...matches.filter((m) => m !== preferred)] : matches).slice(0, 20);
|
|
36069
|
-
const defaultIndex = visible.findIndex((m) => m.id ===
|
|
36117
|
+
const defaultIndex = visible.findIndex((m) => m.id === models2.default);
|
|
36070
36118
|
const selected = await io.choose("Choose your model", [
|
|
36071
36119
|
...visible.map((m) => m.label.trim() || m.id),
|
|
36072
36120
|
"Search models\u2026"
|
|
@@ -36140,11 +36188,11 @@ async function runSetup(options, io = defaultSetupIo(), deps = dependencies) {
|
|
|
36140
36188
|
id = prior?.[0] ?? native.id;
|
|
36141
36189
|
if (!prior && runtime[id] && runtime[id].driver !== native.driver) id = `${native.id}-${randomUUID10().slice(0, 8)}`;
|
|
36142
36190
|
entry = { ...cfg.instances?.[id] ?? { driver: native.driver }, enabled: true };
|
|
36143
|
-
const
|
|
36191
|
+
const models2 = await connectNative(native, id, entry, io, deps);
|
|
36144
36192
|
const saved = cfg.defaultModelSelection;
|
|
36145
36193
|
model = await chooseModel(io, {
|
|
36146
|
-
...
|
|
36147
|
-
default: saved?.instanceId === id ? saved.model :
|
|
36194
|
+
...models2,
|
|
36195
|
+
default: saved?.instanceId === id ? saved.model : models2.default
|
|
36148
36196
|
});
|
|
36149
36197
|
} else {
|
|
36150
36198
|
io.log("Connect an API key");
|
|
@@ -36174,21 +36222,21 @@ async function runSetup(options, io = defaultSetupIo(), deps = dependencies) {
|
|
|
36174
36222
|
}
|
|
36175
36223
|
if (!key) throw new Error("An API key is required. Nothing was saved.");
|
|
36176
36224
|
io.log("Checking the model catalog\u2026");
|
|
36177
|
-
let
|
|
36225
|
+
let models2;
|
|
36178
36226
|
try {
|
|
36179
|
-
|
|
36227
|
+
models2 = await deps.models(url2, key);
|
|
36180
36228
|
} catch (error51) {
|
|
36181
36229
|
if (error51 instanceof SetupCancelled) throw error51;
|
|
36182
36230
|
io.log(error51 instanceof Error ? error51.message : "Could not load this model catalog.");
|
|
36183
36231
|
if (!await io.confirm("Enter an exact chat model ID and check it directly instead?", false)) throw error51;
|
|
36184
36232
|
const manual = (await io.ask("Chat model ID: ")).trim();
|
|
36185
36233
|
if (!manual) throw new Error("A model ID is required. Nothing was saved.");
|
|
36186
|
-
|
|
36234
|
+
models2 = [{ id: manual, label: manual }];
|
|
36187
36235
|
}
|
|
36188
36236
|
const previousModel = prior ? rawObject(prior[1].config).model : void 0;
|
|
36189
36237
|
const preferredModel = cfg.defaultModelSelection?.instanceId === prior?.[0] ? cfg.defaultModelSelection?.model : previousModel;
|
|
36190
36238
|
io.log("Choose a chat model; image, audio and embedding-only models cannot reply here.");
|
|
36191
|
-
model = await chooseModel(io, { default: typeof preferredModel === "string" ? preferredModel : "", options:
|
|
36239
|
+
model = await chooseModel(io, { default: typeof preferredModel === "string" ? preferredModel : "", options: models2 });
|
|
36192
36240
|
if (!await io.confirm("Send one short test message? Your API provider may charge for this request.", true)) throw new SetupCancelled();
|
|
36193
36241
|
if (routingProvider) await deps.verify(url2, key, model, routingProvider);
|
|
36194
36242
|
else await deps.verify(url2, key, model);
|
|
@@ -36305,7 +36353,7 @@ var init_browser_engine_release = __esm({
|
|
|
36305
36353
|
});
|
|
36306
36354
|
|
|
36307
36355
|
// server/browser-bundle-release.ts
|
|
36308
|
-
import { join as
|
|
36356
|
+
import { join as join34 } from "node:path";
|
|
36309
36357
|
function browserBundleSpec(target) {
|
|
36310
36358
|
if (!Object.hasOwn(CHROME_ASSETS, target)) throw new Error(`Unsupported desktop browser target: ${target}`);
|
|
36311
36359
|
const pinned = CHROME_ASSETS[target];
|
|
@@ -36341,10 +36389,10 @@ function browserBundlePaths(bundleDirectory, target) {
|
|
|
36341
36389
|
const spec = browserBundleSpec(target);
|
|
36342
36390
|
return {
|
|
36343
36391
|
directory: bundleDirectory,
|
|
36344
|
-
manifest:
|
|
36345
|
-
engine:
|
|
36346
|
-
chrome:
|
|
36347
|
-
licenses:
|
|
36392
|
+
manifest: join34(bundleDirectory, "manifest.json"),
|
|
36393
|
+
engine: join34(bundleDirectory, spec.engine.executable),
|
|
36394
|
+
chrome: join34(bundleDirectory, spec.chrome.executable),
|
|
36395
|
+
licenses: join34(bundleDirectory, "licenses")
|
|
36348
36396
|
};
|
|
36349
36397
|
}
|
|
36350
36398
|
var CHROME_VERSION, CHROME_ASSETS;
|
|
@@ -36384,7 +36432,7 @@ var init_bot_profile = __esm({
|
|
|
36384
36432
|
});
|
|
36385
36433
|
|
|
36386
36434
|
// server/bot-folder.ts
|
|
36387
|
-
import { join as
|
|
36435
|
+
import { join as join35 } from "node:path";
|
|
36388
36436
|
var BOTS_DIR;
|
|
36389
36437
|
var init_bot_folder = __esm({
|
|
36390
36438
|
"server/bot-folder.ts"() {
|
|
@@ -36392,7 +36440,7 @@ var init_bot_folder = __esm({
|
|
|
36392
36440
|
init_atomic();
|
|
36393
36441
|
init_config();
|
|
36394
36442
|
init_bot_profile();
|
|
36395
|
-
BOTS_DIR =
|
|
36443
|
+
BOTS_DIR = join35(DATA_DIR, "bots");
|
|
36396
36444
|
}
|
|
36397
36445
|
});
|
|
36398
36446
|
|
|
@@ -36429,9 +36477,9 @@ __export(browser_engine_exports, {
|
|
|
36429
36477
|
});
|
|
36430
36478
|
import { spawn as spawn8 } from "node:child_process";
|
|
36431
36479
|
import { createHash as createHash8, randomBytes as randomBytes3, randomUUID as randomUUID11 } from "node:crypto";
|
|
36432
|
-
import { chmodSync as chmodSync7, constants as constants2, copyFileSync, existsSync as existsSync18, mkdirSync as mkdirSync16, readFileSync as
|
|
36480
|
+
import { chmodSync as chmodSync7, constants as constants2, copyFileSync, existsSync as existsSync18, mkdirSync as mkdirSync16, readFileSync as readFileSync20, readdirSync as readdirSync3, renameSync as renameSync5, rmSync as rmSync6, statSync as statSync3, unlinkSync as unlinkSync4, writeFileSync as writeFileSync13 } from "node:fs";
|
|
36433
36481
|
import { homedir as homedir23, tmpdir as tmpdir5 } from "node:os";
|
|
36434
|
-
import { delimiter as delimiter6, dirname as dirname11, join as
|
|
36482
|
+
import { delimiter as delimiter6, dirname as dirname11, join as join36, resolve as resolve11 } from "node:path";
|
|
36435
36483
|
function browserRestoreKey(session) {
|
|
36436
36484
|
if (!/^[A-Za-z0-9_-]{1,96}$/.test(session)) throw new Error("Invalid browser session.");
|
|
36437
36485
|
return `omb-${createHash8("sha256").update(session).digest("hex")}`;
|
|
@@ -36440,20 +36488,20 @@ function browserSessionsDirectory(env) {
|
|
|
36440
36488
|
const namespace = env.AGENT_BROWSER_NAMESPACE;
|
|
36441
36489
|
if (namespace && !/^[a-z0-9]+(?:[-_][a-z0-9]+)*$/.test(namespace)) throw new Error("Invalid browser namespace.");
|
|
36442
36490
|
const home = (process.platform === "win32" ? env.USERPROFILE : env.HOME) || homedir23();
|
|
36443
|
-
const stateRoot = namespace ?
|
|
36444
|
-
return
|
|
36491
|
+
const stateRoot = namespace ? join36(home, ".agent-browser", "namespaces", namespace, "state") : join36(home, ".agent-browser");
|
|
36492
|
+
return join36(stateRoot, "sessions");
|
|
36445
36493
|
}
|
|
36446
36494
|
function managedBrowserConfigPath(env) {
|
|
36447
|
-
return
|
|
36495
|
+
return join36(browserSessionsDirectory(env), "..", "omb-managed-config.json");
|
|
36448
36496
|
}
|
|
36449
36497
|
function ensureManagedBrowserConfig(env) {
|
|
36450
36498
|
const path3 = managedBrowserConfigPath(env);
|
|
36451
36499
|
mkdirSync16(dirname11(path3), { recursive: true, mode: 448 });
|
|
36452
36500
|
try {
|
|
36453
|
-
|
|
36501
|
+
writeFileSync13(path3, "{}\n", { flag: "wx", mode: 384 });
|
|
36454
36502
|
} catch (error51) {
|
|
36455
36503
|
if (error51.code !== "EEXIST") throw error51;
|
|
36456
|
-
if (
|
|
36504
|
+
if (readFileSync20(path3, "utf8").trim() !== "{}") throw new Error("The managed browser configuration was changed. Restore it to an empty JSON object before connecting.");
|
|
36457
36505
|
}
|
|
36458
36506
|
return path3;
|
|
36459
36507
|
}
|
|
@@ -36531,11 +36579,11 @@ async function prepareBrowserSessionState(binaryPath, session, options = {}) {
|
|
|
36531
36579
|
ensureManagedBrowserConfig(env);
|
|
36532
36580
|
if (options.persistent === false) return;
|
|
36533
36581
|
const directory = browserSessionsDirectory(env);
|
|
36534
|
-
const key =
|
|
36582
|
+
const key = join36(directory, session);
|
|
36535
36583
|
let pending = preparedBrowserSessions.get(key);
|
|
36536
36584
|
if (!pending) {
|
|
36537
36585
|
pending = (async () => {
|
|
36538
|
-
const targetBase =
|
|
36586
|
+
const targetBase = join36(directory, `${restoreKey}-${session}`);
|
|
36539
36587
|
const hasTarget = () => existsSync18(`${targetBase}.json.enc`) || existsSync18(`${targetBase}.json`);
|
|
36540
36588
|
if (hasTarget()) return;
|
|
36541
36589
|
check2();
|
|
@@ -36545,7 +36593,7 @@ async function prepareBrowserSessionState(binaryPath, session, options = {}) {
|
|
|
36545
36593
|
check2();
|
|
36546
36594
|
if (hasTarget()) return;
|
|
36547
36595
|
const candidates = [".json.enc", ".json"].flatMap((suffix) => {
|
|
36548
|
-
const path3 =
|
|
36596
|
+
const path3 = join36(directory, `${session}-${session}${suffix}`);
|
|
36549
36597
|
try {
|
|
36550
36598
|
const stat4 = statSync3(path3);
|
|
36551
36599
|
if (!stat4.isFile()) throw new Error("Saved browser state is not a file.");
|
|
@@ -36580,7 +36628,7 @@ function isMusl(platform = process.platform, exists = existsSync18) {
|
|
|
36580
36628
|
}
|
|
36581
36629
|
function pinnedBinaryPath(dataDir = DATA_DIR, platform = process.platform, arch = process.arch) {
|
|
36582
36630
|
const version2 = agentBrowserReleaseVersion(resolveAgentBrowserReleaseAsset(platform, arch));
|
|
36583
|
-
return
|
|
36631
|
+
return join36(dataDir, ENGINE_DIR, version2, executableName(platform));
|
|
36584
36632
|
}
|
|
36585
36633
|
function onPath(env, platform, exists) {
|
|
36586
36634
|
const pathValue = platform === "win32" ? Object.entries(env).findLast(([key]) => key.toUpperCase() === "PATH")?.[1] : env.PATH;
|
|
@@ -36596,7 +36644,7 @@ function packagedBrowser(options) {
|
|
|
36596
36644
|
const resources = (options.env ?? process.env).OMB_RESOURCES_PATH;
|
|
36597
36645
|
if (!resources) return null;
|
|
36598
36646
|
try {
|
|
36599
|
-
return browserBundlePaths(
|
|
36647
|
+
return browserBundlePaths(join36(resolve11(resources), "browser-engine"), `${options.platform ?? process.platform}-${options.arch ?? process.arch}`);
|
|
36600
36648
|
} catch {
|
|
36601
36649
|
return null;
|
|
36602
36650
|
}
|
|
@@ -36621,7 +36669,7 @@ async function installAgentBrowserBinary(options = {}) {
|
|
|
36621
36669
|
const asset = options.asset ?? resolveAgentBrowserReleaseAsset(platform, options.arch ?? process.arch, options.musl ?? isMusl(platform));
|
|
36622
36670
|
if (!asset) throw new Error(`agent-browser publishes no build for ${platform}-${options.arch ?? process.arch}.`);
|
|
36623
36671
|
const destination = pinnedBinaryPath(options.dataDir, platform, options.arch);
|
|
36624
|
-
const directory =
|
|
36672
|
+
const directory = join36(destination, "..");
|
|
36625
36673
|
mkdirSync16(directory, { recursive: true, mode: 448 });
|
|
36626
36674
|
const url2 = agentBrowserReleaseUrl(asset);
|
|
36627
36675
|
options.log?.(`downloading agent-browser ${agentBrowserReleaseVersion(asset)} (${Math.round(asset.bytes / 1024 / 1024)} MB, digest pinned)`);
|
|
@@ -36640,7 +36688,7 @@ async function installAgentBrowserBinary(options = {}) {
|
|
|
36640
36688
|
const digest2 = createHash8("sha256").update(body).digest("hex");
|
|
36641
36689
|
if (digest2 !== asset.sha256) throw new Error("the agent-browser download failed its SHA-256 check; nothing was installed");
|
|
36642
36690
|
const staging = `${destination}.${randomBytes3(6).toString("hex")}.part`;
|
|
36643
|
-
|
|
36691
|
+
writeFileSync13(staging, body, { mode: 493 });
|
|
36644
36692
|
if (platform !== "win32") chmodSync7(staging, 493);
|
|
36645
36693
|
renameSync5(staging, destination);
|
|
36646
36694
|
return destination;
|
|
@@ -36673,9 +36721,9 @@ function ensureChrome(binaryPath, options = {}) {
|
|
|
36673
36721
|
});
|
|
36674
36722
|
}
|
|
36675
36723
|
function browserEngineEncryptionKey(dataDir = DATA_DIR) {
|
|
36676
|
-
const file2 =
|
|
36724
|
+
const file2 = join36(dataDir, KEY_FILE);
|
|
36677
36725
|
try {
|
|
36678
|
-
const existing =
|
|
36726
|
+
const existing = readFileSync20(file2, "utf8").trim();
|
|
36679
36727
|
if (/^[0-9a-f]{64}$/u.test(existing)) return existing;
|
|
36680
36728
|
} catch {
|
|
36681
36729
|
}
|
|
@@ -36733,7 +36781,7 @@ function agentBrowserIntegration(input) {
|
|
|
36733
36781
|
return { command: input.binaryPath, args: ["mcp", "--tools", "core", "--no-webmcp"], env };
|
|
36734
36782
|
}
|
|
36735
36783
|
function agentBrowserFrame(input) {
|
|
36736
|
-
const file2 =
|
|
36784
|
+
const file2 = join36(tmpdir5(), `openmausbot-browser-${randomUUID11()}.png`);
|
|
36737
36785
|
return new Promise((settle, fail) => {
|
|
36738
36786
|
const child = spawn8(input.binaryPath, ["screenshot", file2], {
|
|
36739
36787
|
env: browserRuntimeEnv(input.env),
|
|
@@ -36755,7 +36803,7 @@ function agentBrowserFrame(input) {
|
|
|
36755
36803
|
fail(error51);
|
|
36756
36804
|
return;
|
|
36757
36805
|
}
|
|
36758
|
-
settle({ png:
|
|
36806
|
+
settle({ png: readFileSync20(file2).toString("base64"), format: "png" });
|
|
36759
36807
|
} catch {
|
|
36760
36808
|
fail(new Error("the browser reported a picture it did not write"));
|
|
36761
36809
|
} finally {
|
|
@@ -36803,8 +36851,8 @@ async function clearBrowserSessionState(binaryPath, session, options = {}) {
|
|
|
36803
36851
|
}
|
|
36804
36852
|
};
|
|
36805
36853
|
try {
|
|
36806
|
-
for (const base2 of bases) for (const suffix of [".json", ".json.enc", ".json.previous", ".json.enc.previous"]) remove(
|
|
36807
|
-
const pendingDirectory =
|
|
36854
|
+
for (const base2 of bases) for (const suffix of [".json", ".json.enc", ".json.previous", ".json.enc.previous"]) remove(join36(directory, base2 + suffix));
|
|
36855
|
+
const pendingDirectory = join36(directory, ".tmp");
|
|
36808
36856
|
let pendingFiles = [];
|
|
36809
36857
|
try {
|
|
36810
36858
|
pendingFiles = readdirSync3(pendingDirectory);
|
|
@@ -36812,8 +36860,8 @@ async function clearBrowserSessionState(binaryPath, session, options = {}) {
|
|
|
36812
36860
|
if (error51.code !== "ENOENT") throw error51;
|
|
36813
36861
|
}
|
|
36814
36862
|
const candidates = bases.map((base2) => new RegExp(`^${base2}-candidate-[0-9]+\\.json(?:\\.enc)?$`));
|
|
36815
|
-
for (const name of pendingFiles) if (candidates.some((candidate) => candidate.test(name))) remove(
|
|
36816
|
-
preparedBrowserSessions.delete(
|
|
36863
|
+
for (const name of pendingFiles) if (candidates.some((candidate) => candidate.test(name))) remove(join36(pendingDirectory, name));
|
|
36864
|
+
preparedBrowserSessions.delete(join36(directory, session));
|
|
36817
36865
|
return true;
|
|
36818
36866
|
} catch {
|
|
36819
36867
|
return false;
|
|
@@ -36841,9 +36889,9 @@ var init_browser_engine = __esm({
|
|
|
36841
36889
|
// server/cli.ts
|
|
36842
36890
|
var import_qrcode_terminal = __toESM(require_main(), 1);
|
|
36843
36891
|
import { spawn as spawn9 } from "node:child_process";
|
|
36844
|
-
import { closeSync as
|
|
36892
|
+
import { closeSync as closeSync4, existsSync as existsSync19, mkdirSync as mkdirSync17, openSync as openSync4, readFileSync as readFileSync21 } from "node:fs";
|
|
36845
36893
|
import { homedir as homedir24 } from "node:os";
|
|
36846
|
-
import { dirname as dirname12, join as
|
|
36894
|
+
import { dirname as dirname12, join as join37, resolve as resolve12 } from "node:path";
|
|
36847
36895
|
import { createInterface as createInterface2 } from "node:readline/promises";
|
|
36848
36896
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
36849
36897
|
|
|
@@ -38615,8 +38663,9 @@ function runServiceCommand(input, io) {
|
|
|
38615
38663
|
}
|
|
38616
38664
|
|
|
38617
38665
|
// server/fleet-cli.ts
|
|
38618
|
-
|
|
38619
|
-
import {
|
|
38666
|
+
init_atomic();
|
|
38667
|
+
import { execFileSync, spawn as spawn3 } from "node:child_process";
|
|
38668
|
+
import { appendFileSync, closeSync as closeSync2, existsSync as existsSync2, fsyncSync as fsyncSync2, lstatSync, mkdirSync as mkdirSync3, openSync as openSync2, readFileSync, rmSync as rmSync2 } from "node:fs";
|
|
38620
38669
|
import { posix as posix3 } from "node:path";
|
|
38621
38670
|
|
|
38622
38671
|
// server/fleet.ts
|
|
@@ -38708,8 +38757,9 @@ function templateUnit(spec) {
|
|
|
38708
38757
|
"# Written by `openmausbot fleet init`. One unit for every workspace: %i is the slug.",
|
|
38709
38758
|
"[Unit]",
|
|
38710
38759
|
"Description=OpenMausBot workspace %i",
|
|
38711
|
-
"After=network-online.target",
|
|
38760
|
+
"After=network-online.target openmausbot-fence.service",
|
|
38712
38761
|
"Wants=network-online.target",
|
|
38762
|
+
"Requires=openmausbot-fence.service",
|
|
38713
38763
|
"",
|
|
38714
38764
|
"[Service]",
|
|
38715
38765
|
"Type=simple",
|
|
@@ -38741,7 +38791,6 @@ function fenceUnit(layout = fleetLayout()) {
|
|
|
38741
38791
|
"# Written by `openmausbot fleet init`: keeps each workspace's loopback ports to its own user.",
|
|
38742
38792
|
"[Unit]",
|
|
38743
38793
|
"Description=OpenMausBot per-workspace loopback fence",
|
|
38744
|
-
"Before=openmausbot@.service",
|
|
38745
38794
|
"",
|
|
38746
38795
|
"[Service]",
|
|
38747
38796
|
"Type=oneshot",
|
|
@@ -38756,14 +38805,16 @@ function fenceUnit(layout = fleetLayout()) {
|
|
|
38756
38805
|
function fenceRules(workspaces) {
|
|
38757
38806
|
const lines = ["#!/usr/sbin/nft -f", "# Written by `openmausbot fleet`; regenerated on every create, suspend, resume and delete.", "add table inet openmausbot", "flush table inet openmausbot", "table inet openmausbot {", " chain output {", " type filter hook output priority 0; policy accept;"];
|
|
38758
38807
|
for (const workspace of [...workspaces].sort((a2, b) => a2.slug.localeCompare(b.slug))) {
|
|
38808
|
+
if (workspace.status !== "running" && workspace.status !== "suspended" && workspace.status !== "retained" && !workspace.accountCreated) continue;
|
|
38759
38809
|
lines.push(` oif lo tcp dport { ${workspace.port}, ${workspace.webhookPort} } meta skuid != { ${fleetUser(workspace.slug)}, caddy, root } reject`);
|
|
38760
38810
|
}
|
|
38761
38811
|
lines.push(" }", "}", "");
|
|
38762
38812
|
return lines.join("\n");
|
|
38763
38813
|
}
|
|
38764
38814
|
function caddySite(workspace) {
|
|
38765
|
-
if (workspace.status
|
|
38766
|
-
|
|
38815
|
+
if (workspace.status !== "running") {
|
|
38816
|
+
const message2 = workspace.status === "suspended" ? "This workspace is suspended." : "This workspace is not available.";
|
|
38817
|
+
return [`${workspace.host} {`, ` respond "${message2}" 503`, "}", ""].join("\n");
|
|
38767
38818
|
}
|
|
38768
38819
|
return [
|
|
38769
38820
|
`${workspace.host} {`,
|
|
@@ -38791,11 +38842,23 @@ function instanceEnv(input) {
|
|
|
38791
38842
|
`OMB_PUBLIC_URL=https://${input.workspace.host}`
|
|
38792
38843
|
];
|
|
38793
38844
|
if (input.licenseKey) lines.push(`OMB_LICENSE_KEY=${input.licenseKey}`);
|
|
38845
|
+
if (input.portalUrl) lines.push(`OMB_ADMIN_URL=${input.portalUrl}`, `OMB_ADMIN_WORKSPACE=${input.workspace.slug}`);
|
|
38794
38846
|
return lines.join("\n") + "\n";
|
|
38795
38847
|
}
|
|
38848
|
+
var MANAGED_OPENROUTER = "omb-managed-openrouter";
|
|
38849
|
+
function openRouterModelIds(value) {
|
|
38850
|
+
if (!Array.isArray(value) || value.length > 100 || value.some((id) => typeof id !== "string" || !/^[a-zA-Z0-9][a-zA-Z0-9._:/+-]{0,159}$/.test(id))) {
|
|
38851
|
+
throw new Error("OpenRouter models must be an array of at most 100 valid model IDs");
|
|
38852
|
+
}
|
|
38853
|
+
return [...new Set(value)];
|
|
38854
|
+
}
|
|
38855
|
+
function managedOpenRouterModels(models2) {
|
|
38856
|
+
return Object.fromEntries(openRouterModelIds(models2).map((id) => [id, { name: id }]));
|
|
38857
|
+
}
|
|
38796
38858
|
function initialConfig(seed) {
|
|
38797
38859
|
const config2 = { signIn: { admins: seed.admins, members: seed.members } };
|
|
38798
|
-
if (seed.anthropicKey) config2.anthropic = { key: seed.anthropicKey };
|
|
38860
|
+
if (seed.anthropicKey) config2.anthropic = { key: seed.anthropicKey, ...seed.anthropicUrl ? { url: seed.anthropicUrl } : {} };
|
|
38861
|
+
if (seed.openrouterDefault && seed.openrouterModels?.length) config2.defaultModelSelection = { instanceId: "opencodeGo", model: `${MANAGED_OPENROUTER}/${seed.openrouterModels[0]}` };
|
|
38799
38862
|
if (seed.monthlyCapUsd !== void 0) config2.budgets = { monthlyUsd: seed.monthlyCapUsd };
|
|
38800
38863
|
return `${JSON.stringify(config2, null, 2)}
|
|
38801
38864
|
`;
|
|
@@ -38839,6 +38902,18 @@ function initPlan(input) {
|
|
|
38839
38902
|
}
|
|
38840
38903
|
function createPlan(input) {
|
|
38841
38904
|
assertSlug(input.slug);
|
|
38905
|
+
if (input.seed.portalUrl) {
|
|
38906
|
+
const portal = new URL(input.seed.portalUrl);
|
|
38907
|
+
if (portal.protocol !== "https:" || portal.origin !== input.seed.portalUrl) throw new Error("portalUrl must be an HTTPS origin without a path or credentials");
|
|
38908
|
+
if (portal.hostname === `${input.slug}.${input.registry.domain}`) throw new Error("this workspace name is reserved for the admin portal");
|
|
38909
|
+
if (input.seed.anthropicUrl && input.seed.anthropicUrl !== `${portal.origin}/api/gateway/${input.slug}/anthropic`) throw new Error("managed provider URL must belong to this workspace's portal gateway");
|
|
38910
|
+
} else if (input.seed.anthropicUrl) throw new Error("a managed provider URL requires portalUrl");
|
|
38911
|
+
if (input.seed.openrouterKey !== void 0 || input.seed.openrouterUrl !== void 0 || input.seed.openrouterModels !== void 0 || input.seed.openrouterDefault) {
|
|
38912
|
+
if (!input.seed.portalUrl || input.seed.openrouterUrl !== `${input.seed.portalUrl}/api/gateway/${input.slug}/openrouter/v1`) throw new Error("managed OpenRouter URL must belong to this workspace's portal gateway");
|
|
38913
|
+
if (!input.seed.openrouterKey || !/^[!-~]+$/.test(input.seed.openrouterKey)) throw new Error("managed OpenRouter requires a non-empty workspace credential without whitespace");
|
|
38914
|
+
const models2 = openRouterModelIds(input.seed.openrouterModels ?? []);
|
|
38915
|
+
if (input.seed.openrouterDefault && !models2.length) throw new Error("a default OpenRouter model requires a model assignment");
|
|
38916
|
+
}
|
|
38842
38917
|
if (input.registry.workspaces[input.slug]) throw new Error(`workspace "${input.slug}" already exists`);
|
|
38843
38918
|
if (!input.seed.admins.length) throw new Error("a workspace needs at least one admin email (--admin)");
|
|
38844
38919
|
for (const entry of [...input.seed.admins, ...input.seed.members]) assertEmailOrDomain(entry);
|
|
@@ -38854,15 +38929,36 @@ function createPlan(input) {
|
|
|
38854
38929
|
createdAt: (input.now ?? /* @__PURE__ */ new Date()).toISOString()
|
|
38855
38930
|
};
|
|
38856
38931
|
const registry2 = { ...input.registry, nextPort: next, workspaces: { ...input.registry.workspaces, [workspace.slug]: workspace } };
|
|
38932
|
+
const reserved = (accountCreated) => ({
|
|
38933
|
+
...registry2,
|
|
38934
|
+
workspaces: { ...registry2.workspaces, [workspace.slug]: { ...workspace, status: "provisioning", accountCreated } }
|
|
38935
|
+
});
|
|
38857
38936
|
const user = fleetUser(workspace.slug);
|
|
38858
38937
|
const home = workspaceHome(layout, workspace.slug);
|
|
38859
38938
|
const dataDir = workspaceDataDir(layout, workspace.slug);
|
|
38860
38939
|
const steps = [
|
|
38940
|
+
// Reserve the identity and ports before any external resource is created.
|
|
38941
|
+
// A crash leaves an inspectable reservation, never a silently reusable home.
|
|
38942
|
+
{ kind: "write", path: layout.registryFile, content: `${JSON.stringify(reserved(false), null, 2)}
|
|
38943
|
+
`, mode: 384 },
|
|
38861
38944
|
{ kind: "run", argv: ["useradd", "--system", "--create-home", "--home-dir", home, "--shell", "/usr/sbin/nologin", "--user-group", user], why: `the workspace's own account` },
|
|
38945
|
+
{ kind: "write", path: layout.registryFile, content: `${JSON.stringify(reserved(true), null, 2)}
|
|
38946
|
+
`, mode: 384 },
|
|
38862
38947
|
{ kind: "mkdir", path: dataDir, mode: 448, owner: user },
|
|
38863
38948
|
{ kind: "write", path: join3(dataDir, "config.json"), content: initialConfig(input.seed), mode: 384, owner: user },
|
|
38949
|
+
...input.seed.openrouterKey ? [
|
|
38950
|
+
{ kind: "mkdir", path: join3(home, ".config"), mode: 448, owner: user },
|
|
38951
|
+
{ kind: "mkdir", path: join3(home, ".config", "opencode"), mode: 448, owner: user },
|
|
38952
|
+
{ kind: "write", path: join3(home, ".config", "opencode", "opencode.json"), mode: 384, owner: user, content: `${JSON.stringify({ provider: { [MANAGED_OPENROUTER]: {
|
|
38953
|
+
npm: "@ai-sdk/openai-compatible",
|
|
38954
|
+
name: "Managed OpenRouter",
|
|
38955
|
+
options: { baseURL: input.seed.openrouterUrl, apiKey: input.seed.openrouterKey },
|
|
38956
|
+
models: managedOpenRouterModels(input.seed.openrouterModels ?? [])
|
|
38957
|
+
} } }, null, 2)}
|
|
38958
|
+
` }
|
|
38959
|
+
] : [],
|
|
38864
38960
|
...input.seed.brandJson ? [{ kind: "write", path: join3(dataDir, "brand.json"), content: input.seed.brandJson, mode: 384, owner: user }] : [],
|
|
38865
|
-
{ kind: "write", path: join3(layout.instancesDir, `${workspace.slug}.env`), content: instanceEnv({ workspace, dataDir, licenseKey: input.licenseKey }), mode: 384 },
|
|
38961
|
+
{ kind: "write", path: join3(layout.instancesDir, `${workspace.slug}.env`), content: instanceEnv({ workspace, dataDir, licenseKey: input.licenseKey, portalUrl: input.seed.portalUrl }), mode: 384 },
|
|
38866
38962
|
...input.memoryMax ? [
|
|
38867
38963
|
{ kind: "mkdir", path: join3(layout.unitFile.replace(/openmausbot@\.service$/, ""), `openmausbot@${workspace.slug}.service.d`), mode: 493 },
|
|
38868
38964
|
{ kind: "write", path: join3(layout.unitFile.replace(/openmausbot@\.service$/, ""), `openmausbot@${workspace.slug}.service.d`, "limits.conf"), content: `[Service]
|
|
@@ -38882,10 +38978,14 @@ MemoryMax=${input.memoryMax}
|
|
|
38882
38978
|
];
|
|
38883
38979
|
return { steps, registry: registry2, workspace };
|
|
38884
38980
|
}
|
|
38981
|
+
function assertManagedWorkspace(workspace) {
|
|
38982
|
+
if (workspace.status !== "running" && workspace.status !== "suspended") throw new Error(`workspace "${workspace.slug}" is ${workspace.status} and requires operator recovery`);
|
|
38983
|
+
}
|
|
38885
38984
|
function withStatus(registry2, slug, status) {
|
|
38886
38985
|
assertSlug(slug);
|
|
38887
38986
|
const current = registry2.workspaces[slug];
|
|
38888
38987
|
if (!current) throw new Error(`no workspace "${slug}"`);
|
|
38988
|
+
assertManagedWorkspace(current);
|
|
38889
38989
|
const workspace = { ...current, status };
|
|
38890
38990
|
return { registry: { ...registry2, workspaces: { ...registry2.workspaces, [slug]: workspace } }, workspace };
|
|
38891
38991
|
}
|
|
@@ -38923,8 +39023,9 @@ function deletePlan(input) {
|
|
|
38923
39023
|
const layout = input.layout ?? fleetLayout();
|
|
38924
39024
|
const workspace = input.registry.workspaces[input.slug];
|
|
38925
39025
|
if (!workspace) throw new Error(`no workspace "${input.slug}"`);
|
|
39026
|
+
assertManagedWorkspace(workspace);
|
|
38926
39027
|
const { [input.slug]: _gone, ...rest } = input.registry.workspaces;
|
|
38927
|
-
const registry2 = { ...input.registry, workspaces: rest };
|
|
39028
|
+
const registry2 = { ...input.registry, workspaces: input.keepData ? { ...rest, [input.slug]: { ...workspace, status: "retained" } } : rest };
|
|
38928
39029
|
const unitsDir = layout.unitFile.replace(/openmausbot@\.service$/, "");
|
|
38929
39030
|
return {
|
|
38930
39031
|
registry: registry2,
|
|
@@ -38935,8 +39036,10 @@ function deletePlan(input) {
|
|
|
38935
39036
|
{ kind: "remove", path: join3(layout.instancesDir, `${workspace.slug}.env`) },
|
|
38936
39037
|
{ kind: "remove", path: join3(unitsDir, `openmausbot@${workspace.slug}.service.d`, "limits.conf") },
|
|
38937
39038
|
{ kind: "write", path: layout.fenceFile, content: fenceRules(Object.values(registry2.workspaces)), mode: 384 },
|
|
38938
|
-
{ kind: "run", argv: ["nft", "-f", layout.fenceFile], why: "drop its fence rule" },
|
|
38939
|
-
|
|
39039
|
+
{ kind: "run", argv: ["nft", "-f", layout.fenceFile], why: input.keepData ? "keep the retained account's fence" : "drop its fence rule" },
|
|
39040
|
+
// Retain the nologin account too: recycling its UID would give a new
|
|
39041
|
+
// workspace ownership of the preserved home, even under a different slug.
|
|
39042
|
+
...input.keepData ? [{ kind: "note", text: "keeping the disabled workspace's account, home and name reserved; operator recovery is required before reuse" }] : [{ kind: "run", argv: ["userdel", "--remove", fleetUser(workspace.slug)], why: "remove the account and everything it owned" }],
|
|
38940
39043
|
{ kind: "write", path: layout.registryFile, content: `${JSON.stringify(registry2, null, 2)}
|
|
38941
39044
|
`, mode: 384 }
|
|
38942
39045
|
]
|
|
@@ -38963,10 +39066,12 @@ function applySignIn(configText, action, email3, chatOnly) {
|
|
|
38963
39066
|
const members = without(list(current.members));
|
|
38964
39067
|
if (action === "remove") {
|
|
38965
39068
|
if (admins.length + members.length === list(current.admins).length + list(current.members).length) throw new Error(`${entry} is not on the list`);
|
|
39069
|
+
if (!admins.length) throw new Error("a workspace needs at least one admin; add another admin first");
|
|
38966
39070
|
return { config: `${JSON.stringify({ ...raw, signIn: { admins, members } }, null, 2)}
|
|
38967
|
-
`, summary: `${entry}
|
|
39071
|
+
`, summary: `${entry} was removed from the allow-list; existing email sessions are rechecked against the remaining entries` };
|
|
38968
39072
|
}
|
|
38969
39073
|
const next = chatOnly ? { admins, members: [...members, entry] } : { admins: [...admins, entry], members };
|
|
39074
|
+
if (!next.admins.length) throw new Error("a workspace needs at least one admin; add another admin first");
|
|
38970
39075
|
return { config: `${JSON.stringify({ ...raw, signIn: next }, null, 2)}
|
|
38971
39076
|
`, summary: `${entry} can sign in with an emailed code (${chatOnly ? "chat and approvals" : "full access"})` };
|
|
38972
39077
|
}
|
|
@@ -38978,11 +39083,14 @@ function describeSteps(steps) {
|
|
|
38978
39083
|
for (const step of steps) {
|
|
38979
39084
|
switch (step.kind) {
|
|
38980
39085
|
case "mkdir":
|
|
38981
|
-
lines.push(
|
|
39086
|
+
lines.push(step.owner ? `# Create ${shellQuote(step.path)} mode ${step.mode.toString(8)} as ${step.owner} (use fleet --yes; never root chown on tenant paths)` : `install -d -m ${step.mode.toString(8)} ${shellQuote(step.path)}`);
|
|
38982
39087
|
break;
|
|
38983
39088
|
case "write":
|
|
39089
|
+
if (step.owner) {
|
|
39090
|
+
lines.push(`# Write ${shellQuote(step.path)} mode ${step.mode.toString(8)} as ${step.owner} (use fleet --yes; tenant content omitted)`);
|
|
39091
|
+
break;
|
|
39092
|
+
}
|
|
38984
39093
|
lines.push(`cat > ${shellQuote(step.path)} <<'OMB_EOF'`, step.content.replace(/\n$/, ""), "OMB_EOF", `chmod ${step.mode.toString(8)} ${shellQuote(step.path)}`);
|
|
38985
|
-
if (step.owner) lines.push(`chown ${step.owner}:${step.owner} ${shellQuote(step.path)}`);
|
|
38986
39094
|
break;
|
|
38987
39095
|
case "append-once":
|
|
38988
39096
|
lines.push(`grep -qxF ${shellQuote(step.line)} ${shellQuote(step.path)} || printf '\\n%s\\n' ${shellQuote(step.line)} >> ${shellQuote(step.path)}`);
|
|
@@ -39005,6 +39113,113 @@ function describeSteps(steps) {
|
|
|
39005
39113
|
}
|
|
39006
39114
|
|
|
39007
39115
|
// server/fleet-cli.ts
|
|
39116
|
+
var TENANT_IO = String.raw`
|
|
39117
|
+
const fs = require("node:fs");
|
|
39118
|
+
const path = require("node:path");
|
|
39119
|
+
const { randomUUID } = require("node:crypto");
|
|
39120
|
+
const request = JSON.parse(fs.readFileSync(0, "utf8"));
|
|
39121
|
+
const { uid, gid, home, file, action, content, mode } = request;
|
|
39122
|
+
if (process.getuid() === 0) {
|
|
39123
|
+
process.setgroups([]);
|
|
39124
|
+
process.setgid(gid);
|
|
39125
|
+
process.setuid(uid);
|
|
39126
|
+
}
|
|
39127
|
+
if (process.getuid() !== uid || process.getgid() !== gid || uid === 0 || gid === 0) throw Error("could not drop fleet file privileges");
|
|
39128
|
+
const limit = 4 * 1024 * 1024;
|
|
39129
|
+
const check = (stat, directory) => {
|
|
39130
|
+
if (stat.uid !== uid || (directory ? !stat.isDirectory() : !stat.isFile() || stat.nlink !== 1)) throw Error("unsafe workspace file or directory");
|
|
39131
|
+
};
|
|
39132
|
+
const totals = { turns: 0, costUsd: null, billableUsd: null };
|
|
39133
|
+
let cursor = home;
|
|
39134
|
+
try {
|
|
39135
|
+
check(fs.lstatSync(cursor), true);
|
|
39136
|
+
for (const part of path.relative(home, path.dirname(file)).split(path.sep).filter(Boolean)) {
|
|
39137
|
+
cursor = path.join(cursor, part);
|
|
39138
|
+
check(fs.lstatSync(cursor), true);
|
|
39139
|
+
}
|
|
39140
|
+
} catch (error) {
|
|
39141
|
+
if (action !== "usage" || error.code !== "ENOENT") throw error;
|
|
39142
|
+
process.stdout.write(JSON.stringify(totals));
|
|
39143
|
+
process.exit(0);
|
|
39144
|
+
}
|
|
39145
|
+
let result = null;
|
|
39146
|
+
if (action === "mkdir") {
|
|
39147
|
+
try { fs.mkdirSync(file, { mode }); } catch (error) { if (error.code !== "EEXIST") throw error; }
|
|
39148
|
+
check(fs.lstatSync(file), true);
|
|
39149
|
+
} else if (action === "read" || action === "usage") {
|
|
39150
|
+
let fd;
|
|
39151
|
+
try { fd = fs.openSync(file, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW | fs.constants.O_NONBLOCK); }
|
|
39152
|
+
catch (error) { if (error.code !== "ENOENT") throw error; }
|
|
39153
|
+
if (fd !== undefined) {
|
|
39154
|
+
try {
|
|
39155
|
+
const stat = fs.fstatSync(fd);
|
|
39156
|
+
check(stat, false);
|
|
39157
|
+
if (stat.size > limit) throw Error("workspace file exceeds 4 MiB");
|
|
39158
|
+
const data = Buffer.alloc(limit + 1);
|
|
39159
|
+
let size = 0;
|
|
39160
|
+
while (size < data.length) { const read = fs.readSync(fd, data, size, data.length - size, null); if (!read) break; size += read; }
|
|
39161
|
+
if (size > limit) throw Error("workspace file exceeds 4 MiB");
|
|
39162
|
+
result = data.subarray(0, size).toString("utf8");
|
|
39163
|
+
} finally { fs.closeSync(fd); }
|
|
39164
|
+
}
|
|
39165
|
+
if (action === "usage") {
|
|
39166
|
+
const to = Date.parse(content);
|
|
39167
|
+
const date = new Date(to);
|
|
39168
|
+
const from = Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), 1);
|
|
39169
|
+
// Scan one bounded month's text without retaining rows or allocating groups.
|
|
39170
|
+
const text = result || "";
|
|
39171
|
+
for (let offset = 0; offset < text.length;) {
|
|
39172
|
+
const end = text.indexOf("\n", offset);
|
|
39173
|
+
const line = text.slice(offset, end < 0 ? text.length : end);
|
|
39174
|
+
offset = end < 0 ? text.length : end + 1;
|
|
39175
|
+
let row;
|
|
39176
|
+
try { row = JSON.parse(line); } catch { continue; }
|
|
39177
|
+
if (!row || typeof row !== "object" || typeof row.at !== "string" || typeof row.botId !== "string" || typeof row.model !== "string" || typeof row.input !== "number" || typeof row.output !== "number" || !row.trigger || typeof row.trigger !== "object" || typeof row.trigger.kind !== "string") continue;
|
|
39178
|
+
const at = Date.parse(row.at);
|
|
39179
|
+
if (!(at >= from && at <= to)) continue;
|
|
39180
|
+
totals.turns++;
|
|
39181
|
+
if (typeof row.costUsd === "number" && Number.isFinite(row.costUsd) && row.costUsd >= 0) {
|
|
39182
|
+
const sum = (totals.costUsd || 0) + row.costUsd;
|
|
39183
|
+
if (!Number.isFinite(sum)) throw Error("workspace usage total exceeds numeric range");
|
|
39184
|
+
totals.costUsd = sum;
|
|
39185
|
+
}
|
|
39186
|
+
}
|
|
39187
|
+
result = totals;
|
|
39188
|
+
}
|
|
39189
|
+
} else if (action === "write") {
|
|
39190
|
+
if (Buffer.byteLength(content) > limit) throw Error("workspace file exceeds 4 MiB");
|
|
39191
|
+
try { check(fs.lstatSync(file), false); } catch (error) { if (error.code !== "ENOENT") throw error; }
|
|
39192
|
+
const temp = file + ".fleet-" + randomUUID();
|
|
39193
|
+
let fd;
|
|
39194
|
+
try {
|
|
39195
|
+
fd = fs.openSync(temp, fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_NOFOLLOW, mode);
|
|
39196
|
+
fs.writeFileSync(fd, content);
|
|
39197
|
+
fs.fsyncSync(fd);
|
|
39198
|
+
fs.closeSync(fd); fd = undefined;
|
|
39199
|
+
fs.renameSync(temp, file);
|
|
39200
|
+
} finally {
|
|
39201
|
+
if (fd !== undefined) fs.closeSync(fd);
|
|
39202
|
+
try { fs.unlinkSync(temp); } catch (error) { if (error.code !== "ENOENT") throw error; }
|
|
39203
|
+
}
|
|
39204
|
+
} else throw Error("unknown fleet file operation");
|
|
39205
|
+
process.stdout.write(JSON.stringify(result));
|
|
39206
|
+
`;
|
|
39207
|
+
function tenantIo(owner, file2, action, content, mode) {
|
|
39208
|
+
assertUnixUser(owner);
|
|
39209
|
+
const options = { encoding: "utf8", timeout: 5e3, killSignal: "SIGKILL", cwd: "/", env: { PATH: "/usr/bin:/bin" }, stdio: ["pipe", "pipe", "pipe"] };
|
|
39210
|
+
const account = execFileSync("/usr/bin/getent", ["passwd", owner], { ...options, maxBuffer: 8192 }).trim().split(":");
|
|
39211
|
+
const uid = Number(account[2]);
|
|
39212
|
+
const gid = Number(account[3]);
|
|
39213
|
+
const home = account[5];
|
|
39214
|
+
if (account.length !== 7 || account[0] !== owner || !Number.isSafeInteger(uid) || uid <= 0 || !Number.isSafeInteger(gid) || gid <= 0 || !home?.startsWith("/") || posix3.normalize(home) !== home || !file2.startsWith(`${home}/`) || posix3.normalize(file2) !== file2) {
|
|
39215
|
+
throw new Error(`unsafe filesystem identity or path for ${owner}`);
|
|
39216
|
+
}
|
|
39217
|
+
try {
|
|
39218
|
+
return JSON.parse(execFileSync(process.execPath, ["--eval", TENANT_IO], { ...options, maxBuffer: action === "usage" ? 8192 : 32 * 1024 * 1024, input: JSON.stringify({ uid, gid, home, file: file2, action, content, mode }) }));
|
|
39219
|
+
} catch {
|
|
39220
|
+
throw new Error(`could not ${action} workspace file as ${owner}: check ownership, links, file size and permissions`);
|
|
39221
|
+
}
|
|
39222
|
+
}
|
|
39008
39223
|
function defaultFleetDeps() {
|
|
39009
39224
|
return {
|
|
39010
39225
|
isRoot: () => typeof process.getuid === "function" && process.getuid() === 0,
|
|
@@ -39024,9 +39239,37 @@ function defaultFleetDeps() {
|
|
|
39024
39239
|
resolve13({ code: null, output: error51 instanceof Error ? error51.message : String(error51) });
|
|
39025
39240
|
}
|
|
39026
39241
|
}),
|
|
39027
|
-
|
|
39028
|
-
|
|
39029
|
-
|
|
39242
|
+
pathExists: (path3) => {
|
|
39243
|
+
try {
|
|
39244
|
+
lstatSync(path3);
|
|
39245
|
+
return true;
|
|
39246
|
+
} catch (error51) {
|
|
39247
|
+
if (error51.code === "ENOENT") return false;
|
|
39248
|
+
throw error51;
|
|
39249
|
+
}
|
|
39250
|
+
},
|
|
39251
|
+
readText: (path3, owner) => owner ? tenantIo(owner, path3, "read") : existsSync2(path3) ? readFileSync(path3, "utf8") : null,
|
|
39252
|
+
usage: (dataDir, owner, now) => {
|
|
39253
|
+
const at = now.toISOString();
|
|
39254
|
+
return tenantIo(owner, posix3.join(dataDir, "usage", `${at.slice(0, 7)}.jsonl`), "usage", at);
|
|
39255
|
+
},
|
|
39256
|
+
writeText: (path3, content, mode, owner) => {
|
|
39257
|
+
if (owner) {
|
|
39258
|
+
tenantIo(owner, path3, "write", content, mode);
|
|
39259
|
+
return;
|
|
39260
|
+
}
|
|
39261
|
+
writeFileAtomic(path3, content, { mode });
|
|
39262
|
+
const directory = openSync2(posix3.dirname(path3), "r");
|
|
39263
|
+
try {
|
|
39264
|
+
fsyncSync2(directory);
|
|
39265
|
+
} finally {
|
|
39266
|
+
closeSync2(directory);
|
|
39267
|
+
}
|
|
39268
|
+
},
|
|
39269
|
+
mkdir: (path3, mode, owner) => {
|
|
39270
|
+
if (owner) tenantIo(owner, path3, "mkdir", void 0, mode);
|
|
39271
|
+
else mkdirSync3(path3, { recursive: true, mode });
|
|
39272
|
+
},
|
|
39030
39273
|
appendOnce: (path3, line) => {
|
|
39031
39274
|
const current = existsSync2(path3) ? readFileSync(path3, "utf8") : "";
|
|
39032
39275
|
if (!current.split("\n").some((existing) => existing.trim() === line)) appendFileSync(path3, `${current.endsWith("\n") || !current ? "" : "\n"}
|
|
@@ -39053,18 +39296,10 @@ async function executePlan(steps, deps, io) {
|
|
|
39053
39296
|
for (const step of steps) {
|
|
39054
39297
|
switch (step.kind) {
|
|
39055
39298
|
case "mkdir":
|
|
39056
|
-
deps.mkdir(step.path, step.mode);
|
|
39057
|
-
if (step.owner) {
|
|
39058
|
-
const chown = await deps.run(["chown", `${step.owner}:${step.owner}`, step.path]);
|
|
39059
|
-
if (chown.code !== 0) return failed(io, `chown ${step.path}`, chown.output);
|
|
39060
|
-
}
|
|
39299
|
+
deps.mkdir(step.path, step.mode, step.owner);
|
|
39061
39300
|
break;
|
|
39062
39301
|
case "write":
|
|
39063
|
-
deps.writeText(step.path, step.content, step.mode);
|
|
39064
|
-
if (step.owner) {
|
|
39065
|
-
const chown = await deps.run(["chown", `${step.owner}:${step.owner}`, step.path]);
|
|
39066
|
-
if (chown.code !== 0) return failed(io, `chown ${step.path}`, chown.output);
|
|
39067
|
-
}
|
|
39302
|
+
deps.writeText(step.path, step.content, step.mode, step.owner);
|
|
39068
39303
|
break;
|
|
39069
39304
|
case "append-once":
|
|
39070
39305
|
deps.appendOnce(step.path, step.line);
|
|
@@ -39107,19 +39342,28 @@ function planFleetAction(input, deps) {
|
|
|
39107
39342
|
if (warning) throw new Error(warning);
|
|
39108
39343
|
const existing = deps.readText(layout.registryFile);
|
|
39109
39344
|
if (existing !== null && !input.yes) throw new Error(`${layout.registryFile} exists; pass --yes to rewrite the template unit and fence (workspaces are kept)`);
|
|
39110
|
-
const
|
|
39111
|
-
|
|
39112
|
-
|
|
39113
|
-
plan.registry.workspaces
|
|
39114
|
-
plan.
|
|
39115
|
-
|
|
39116
|
-
` }
|
|
39345
|
+
const kept = existing !== null ? parseRegistry(existing) : void 0;
|
|
39346
|
+
const plan = initPlan({ domain: input.domain, node: input.node, script: input.script, operator: input.operator ?? kept?.operator, layout });
|
|
39347
|
+
if (kept) {
|
|
39348
|
+
const registry2 = { ...kept, ...plan.registry, workspaces: kept.workspaces, nextPort: kept.nextPort };
|
|
39349
|
+
return plan.steps.flatMap((step) => {
|
|
39350
|
+
if (step.kind === "write" && step.path === layout.registryFile) return [{ ...step, content: `${JSON.stringify(registry2, null, 2)}
|
|
39351
|
+
` }];
|
|
39352
|
+
if (step.kind === "write" && step.path === layout.fenceFile) return [{ ...step, content: fenceRules(Object.values(registry2.workspaces)) }];
|
|
39353
|
+
if (step.kind === "run" && step.argv.join(" ") === "systemctl enable --now openmausbot-fence.service") return [step, { kind: "run", argv: ["nft", "-f", layout.fenceFile], why: "reapply the preserved workspace fence" }];
|
|
39354
|
+
return [step];
|
|
39355
|
+
});
|
|
39117
39356
|
}
|
|
39118
39357
|
return plan.steps;
|
|
39119
39358
|
}
|
|
39120
39359
|
case "create": {
|
|
39121
39360
|
if (!input.slug) throw new Error("fleet create needs a workspace name");
|
|
39361
|
+
assertSlug(input.slug);
|
|
39122
39362
|
const registry2 = loadRegistry(layout, deps);
|
|
39363
|
+
if (registry2.workspaces[input.slug]) throw new Error(`workspace "${input.slug}" already exists`);
|
|
39364
|
+
for (const path3 of [workspaceHome(layout, input.slug), posix3.join(layout.instancesDir, `${input.slug}.env`), posix3.join(layout.caddyDir, `${input.slug}.caddy`), posix3.join(posix3.dirname(layout.unitFile), `openmausbot@${input.slug}.service.d`)]) {
|
|
39365
|
+
if (deps.pathExists(path3)) throw new Error(`workspace path already exists: ${path3}; recover or remove it explicitly before provisioning a fresh workspace`);
|
|
39366
|
+
}
|
|
39123
39367
|
const brandJson = input.brandJson ?? (input.brandFile ? deps.readText(input.brandFile) : void 0);
|
|
39124
39368
|
if (input.brandFile && brandJson === null) throw new Error(`${input.brandFile} not found`);
|
|
39125
39369
|
if (brandJson) JSON.parse(brandJson);
|
|
@@ -39128,19 +39372,60 @@ function planFleetAction(input, deps) {
|
|
|
39128
39372
|
return createPlan({
|
|
39129
39373
|
registry: registry2,
|
|
39130
39374
|
slug: input.slug,
|
|
39131
|
-
seed: { admins: input.admins, members: input.members, ...anthropicKey ? { anthropicKey } : {}, ...input.cap !== void 0 ? { monthlyCapUsd: input.cap } : {}, ...brandJson ? { brandJson } : {} },
|
|
39375
|
+
seed: { admins: input.admins, members: input.members, ...anthropicKey ? { anthropicKey } : {}, ...input.anthropicUrl ? { anthropicUrl: input.anthropicUrl } : {}, ...input.portalUrl ? { portalUrl: input.portalUrl } : {}, ...input.cap !== void 0 ? { monthlyCapUsd: input.cap } : {}, ...brandJson ? { brandJson } : {}, openrouterKey: input.openrouterKey, openrouterUrl: input.openrouterUrl, openrouterModels: input.openrouterModels, openrouterDefault: input.openrouterDefault },
|
|
39132
39376
|
...input.licenseKey ? { licenseKey: input.licenseKey } : {},
|
|
39133
39377
|
...input.memory ? { memoryMax: input.memory } : {},
|
|
39134
39378
|
layout
|
|
39135
39379
|
}).steps;
|
|
39136
39380
|
}
|
|
39381
|
+
case "providers": {
|
|
39382
|
+
if (!input.slug) throw new Error("fleet providers needs a workspace name");
|
|
39383
|
+
assertSlug(input.slug);
|
|
39384
|
+
const workspace = loadRegistry(layout, deps).workspaces[input.slug];
|
|
39385
|
+
if (!workspace) throw new Error(`no workspace "${input.slug}"`);
|
|
39386
|
+
assertManagedWorkspace(workspace);
|
|
39387
|
+
const models2 = managedOpenRouterModels(input.openrouterModels);
|
|
39388
|
+
const env = deps.readText(posix3.join(layout.instancesDir, `${input.slug}.env`)) ?? "";
|
|
39389
|
+
const portal = /^OMB_ADMIN_URL=(.+)$/m.exec(env)?.[1];
|
|
39390
|
+
if (!portal || !env.split("\n").includes(`OMB_ADMIN_WORKSPACE=${input.slug}`)) throw new Error("workspace has no trusted portal configuration; operator recovery is required");
|
|
39391
|
+
try {
|
|
39392
|
+
const url2 = new URL(portal);
|
|
39393
|
+
if (url2.protocol !== "https:" || url2.origin !== portal) throw new Error();
|
|
39394
|
+
} catch {
|
|
39395
|
+
throw new Error("workspace portal configuration is invalid; operator recovery is required");
|
|
39396
|
+
}
|
|
39397
|
+
const file2 = posix3.join(workspaceHome(layout, input.slug), ".config", "opencode", "opencode.json");
|
|
39398
|
+
const raw = deps.readText(file2, fleetUser(input.slug));
|
|
39399
|
+
const object2 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
39400
|
+
let config2;
|
|
39401
|
+
try {
|
|
39402
|
+
const parsed = JSON.parse(raw ?? "null");
|
|
39403
|
+
if (!object2(parsed)) throw new Error();
|
|
39404
|
+
config2 = parsed;
|
|
39405
|
+
} catch {
|
|
39406
|
+
throw new Error("managed OpenRouter configuration is missing or invalid; operator recovery is required");
|
|
39407
|
+
}
|
|
39408
|
+
const providers = config2.provider;
|
|
39409
|
+
const managed = object2(providers) ? providers[MANAGED_OPENROUTER] : void 0;
|
|
39410
|
+
const options = object2(managed) ? managed.options : void 0;
|
|
39411
|
+
if (!object2(managed) || managed.npm !== "@ai-sdk/openai-compatible" || !object2(options) || options.baseURL !== `${portal}/api/gateway/${input.slug}/openrouter/v1` || typeof options.apiKey !== "string" || !/^[!-~]+$/.test(options.apiKey)) {
|
|
39412
|
+
throw new Error("managed OpenRouter configuration does not match the workspace portal; operator recovery is required");
|
|
39413
|
+
}
|
|
39414
|
+
managed.models = models2;
|
|
39415
|
+
return [
|
|
39416
|
+
{ kind: "write", path: file2, content: `${JSON.stringify(config2, null, 2)}
|
|
39417
|
+
`, mode: 384, owner: fleetUser(input.slug) },
|
|
39418
|
+
{ kind: "note", text: "managed OpenRouter models updated; refresh the workspace model picker to load its catalog" }
|
|
39419
|
+
];
|
|
39420
|
+
}
|
|
39137
39421
|
case "users": {
|
|
39138
39422
|
if (!input.slug || !input.userAction || !input.email) throw new Error("fleet users needs: NAME add|remove EMAIL [--chat-only]");
|
|
39139
39423
|
assertSlug(input.slug);
|
|
39140
39424
|
const registry2 = loadRegistry(layout, deps);
|
|
39141
39425
|
if (!registry2.workspaces[input.slug]) throw new Error(`no workspace "${input.slug}"`);
|
|
39426
|
+
assertManagedWorkspace(registry2.workspaces[input.slug]);
|
|
39142
39427
|
const file2 = posix3.join(workspaceDataDir(layout, input.slug), "config.json");
|
|
39143
|
-
const next = applySignIn(deps.readText(file2) ?? "{}", input.userAction, input.email, Boolean(input.chatOnly));
|
|
39428
|
+
const next = applySignIn(deps.readText(file2, fleetUser(input.slug)) ?? "{}", input.userAction, input.email, Boolean(input.chatOnly));
|
|
39144
39429
|
return [
|
|
39145
39430
|
{ kind: "write", path: file2, content: next.config, mode: 384, owner: fleetUser(input.slug) },
|
|
39146
39431
|
{ kind: "note", text: next.summary }
|
|
@@ -39154,7 +39439,7 @@ function planFleetAction(input, deps) {
|
|
|
39154
39439
|
}
|
|
39155
39440
|
case "delete": {
|
|
39156
39441
|
if (!input.slug) throw new Error("fleet delete needs a workspace name");
|
|
39157
|
-
if (!input.yes) throw new Error(`fleet delete
|
|
39442
|
+
if (!input.yes) throw new Error(`fleet delete ${input.keepData ? "retires the workspace, keeping its data and reserved Unix account" : "removes the workspace's account and all of its data"}; pass --yes to confirm${input.keepData ? "" : ", or --keep-data to keep the home folder and account reserved"}`);
|
|
39158
39443
|
const registry2 = loadRegistry(layout, deps);
|
|
39159
39444
|
return deletePlan({ registry: registry2, slug: input.slug, keepData: input.keepData, layout }).steps;
|
|
39160
39445
|
}
|
|
@@ -39164,8 +39449,18 @@ function planFleetAction(input, deps) {
|
|
|
39164
39449
|
return [];
|
|
39165
39450
|
}
|
|
39166
39451
|
}
|
|
39452
|
+
function markProvisioningFailed(input, deps) {
|
|
39453
|
+
if (input.action !== "create" || !input.slug) return;
|
|
39454
|
+
const layout = fleetLayout(input.root ?? "/");
|
|
39455
|
+
const registry2 = loadRegistry(layout, deps);
|
|
39456
|
+
const workspace = registry2.workspaces[input.slug];
|
|
39457
|
+
if (workspace?.status !== "provisioning") return;
|
|
39458
|
+
registry2.workspaces[input.slug] = { ...workspace, status: "error" };
|
|
39459
|
+
deps.writeText(layout.registryFile, `${JSON.stringify(registry2, null, 2)}
|
|
39460
|
+
`, 384);
|
|
39461
|
+
}
|
|
39167
39462
|
function printPlan(steps, io, reason) {
|
|
39168
|
-
io.log(`${reason};
|
|
39463
|
+
io.log(`${reason}; inspect this plan, then rerun the fleet command as root without --dry-run (with --yes where required):`);
|
|
39169
39464
|
io.log("");
|
|
39170
39465
|
for (const line of describeSteps(steps)) io.log(line);
|
|
39171
39466
|
return 0;
|
|
@@ -39182,13 +39477,21 @@ async function runFleetCommand(input, io, deps = defaultFleetDeps()) {
|
|
|
39182
39477
|
return 0;
|
|
39183
39478
|
}
|
|
39184
39479
|
for (const workspace of rows) {
|
|
39185
|
-
const live = asRoot ? (await deps.run(["systemctl", "is-active", `openmausbot@${workspace.slug}.service`])).output.trim() : workspace.status;
|
|
39480
|
+
const live = asRoot && (workspace.status === "running" || workspace.status === "suspended") ? (await deps.run(["systemctl", "is-active", `openmausbot@${workspace.slug}.service`])).output.trim() : workspace.status;
|
|
39186
39481
|
io.log(`${workspace.slug.padEnd(24)} https://${workspace.host.padEnd(40)} :${String(workspace.port).padEnd(6)} ${live}`);
|
|
39187
39482
|
}
|
|
39188
39483
|
return 0;
|
|
39189
39484
|
}
|
|
39190
39485
|
const steps = planFleetAction(input, deps);
|
|
39191
|
-
|
|
39486
|
+
if (!asRoot) return printPlan(steps, io, input.dryRun ? "dry run" : "not running as root");
|
|
39487
|
+
try {
|
|
39488
|
+
const code = await executePlan(steps, deps, io);
|
|
39489
|
+
if (code !== 0) markProvisioningFailed(input, deps);
|
|
39490
|
+
return code;
|
|
39491
|
+
} catch (error51) {
|
|
39492
|
+
markProvisioningFailed(input, deps);
|
|
39493
|
+
throw error51;
|
|
39494
|
+
}
|
|
39192
39495
|
} catch (error51) {
|
|
39193
39496
|
io.error(`fleet: ${error51 instanceof Error ? error51.message : String(error51)}`);
|
|
39194
39497
|
return 2;
|
|
@@ -39199,147 +39502,6 @@ async function runFleetCommand(input, io, deps = defaultFleetDeps()) {
|
|
|
39199
39502
|
import { appendFileSync as appendFileSync2, chmodSync as chmodSync2, existsSync as existsSync3, mkdirSync as mkdirSync4, rmSync as rmSync3 } from "node:fs";
|
|
39200
39503
|
import { createServer } from "node:http";
|
|
39201
39504
|
import { dirname as dirname2 } from "node:path";
|
|
39202
|
-
|
|
39203
|
-
// server/usage-ledger.ts
|
|
39204
|
-
import { readFileSync as readFileSync2 } from "node:fs";
|
|
39205
|
-
import { join as join4 } from "node:path";
|
|
39206
|
-
|
|
39207
|
-
// server/prices.ts
|
|
39208
|
-
function priceFor(turn, prices) {
|
|
39209
|
-
return prices[`${turn.driverKind}/${turn.model}`] ?? prices[turn.model] ?? prices.default ?? null;
|
|
39210
|
-
}
|
|
39211
|
-
var clean = (value) => typeof value === "number" && Number.isFinite(value) ? Math.max(0, value) : 0;
|
|
39212
|
-
function billableFor(turn, prices) {
|
|
39213
|
-
const price = priceFor(turn, prices);
|
|
39214
|
-
if (!price) return null;
|
|
39215
|
-
const input = clean(turn.input);
|
|
39216
|
-
const cached3 = Math.min(clean(turn.cachedInput), input);
|
|
39217
|
-
const fresh = input - cached3;
|
|
39218
|
-
const cachedRate = price.cachedInputPerMillion ?? price.inputPerMillion;
|
|
39219
|
-
return (fresh * price.inputPerMillion + cached3 * cachedRate + clean(turn.output) * price.outputPerMillion) / 1e6;
|
|
39220
|
-
}
|
|
39221
|
-
|
|
39222
|
-
// server/usage-ledger.ts
|
|
39223
|
-
var DIR = "usage";
|
|
39224
|
-
var DAY_MS = 24 * 60 * 6e4;
|
|
39225
|
-
var clean2 = (value) => typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.trunc(value)) : 0;
|
|
39226
|
-
var finiteOrNull = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
|
|
39227
|
-
function monthKey(at) {
|
|
39228
|
-
return `${at.getUTCFullYear()}-${String(at.getUTCMonth() + 1).padStart(2, "0")}`;
|
|
39229
|
-
}
|
|
39230
|
-
var isTrigger = (value) => typeof value === "object" && value !== null && typeof value.kind === "string";
|
|
39231
|
-
var isUsageRow = (value) => typeof value === "object" && value !== null && typeof value.at === "string" && typeof value.botId === "string" && typeof value.model === "string" && typeof value.input === "number" && typeof value.output === "number" && isTrigger(value.trigger);
|
|
39232
|
-
function monthsBetween(from, to) {
|
|
39233
|
-
const keys = [];
|
|
39234
|
-
const cursor3 = new Date(Date.UTC(from.getUTCFullYear(), from.getUTCMonth(), 1));
|
|
39235
|
-
while (cursor3.getTime() <= to.getTime()) {
|
|
39236
|
-
keys.push(monthKey(cursor3));
|
|
39237
|
-
cursor3.setUTCMonth(cursor3.getUTCMonth() + 1);
|
|
39238
|
-
}
|
|
39239
|
-
return keys;
|
|
39240
|
-
}
|
|
39241
|
-
function readUsage(dataDir, range) {
|
|
39242
|
-
const rows = [];
|
|
39243
|
-
const from = range.from.getTime();
|
|
39244
|
-
const to = range.to.getTime();
|
|
39245
|
-
for (const key of monthsBetween(range.from, range.to)) {
|
|
39246
|
-
let text4;
|
|
39247
|
-
try {
|
|
39248
|
-
text4 = readFileSync2(join4(dataDir, DIR, `${key}.jsonl`), "utf8");
|
|
39249
|
-
} catch {
|
|
39250
|
-
continue;
|
|
39251
|
-
}
|
|
39252
|
-
for (const line of text4.split("\n")) {
|
|
39253
|
-
if (!line) continue;
|
|
39254
|
-
try {
|
|
39255
|
-
const value = JSON.parse(line);
|
|
39256
|
-
if (!isUsageRow(value)) continue;
|
|
39257
|
-
const at = Date.parse(value.at);
|
|
39258
|
-
if (at >= from && at <= to) rows.push(value);
|
|
39259
|
-
} catch {
|
|
39260
|
-
}
|
|
39261
|
-
}
|
|
39262
|
-
}
|
|
39263
|
-
return rows;
|
|
39264
|
-
}
|
|
39265
|
-
function triggerKey(trigger) {
|
|
39266
|
-
switch (trigger.kind) {
|
|
39267
|
-
case "user":
|
|
39268
|
-
return `user:${(trigger.email ?? trigger.label ?? "").toLowerCase() || "unknown"}`;
|
|
39269
|
-
case "owner":
|
|
39270
|
-
return "owner";
|
|
39271
|
-
case "routine":
|
|
39272
|
-
return `routine:${trigger.routineId ?? trigger.label ?? "unknown"}`;
|
|
39273
|
-
default:
|
|
39274
|
-
return "bot";
|
|
39275
|
-
}
|
|
39276
|
-
}
|
|
39277
|
-
function triggerLabel(trigger) {
|
|
39278
|
-
switch (trigger.kind) {
|
|
39279
|
-
case "user":
|
|
39280
|
-
return trigger.email ?? trigger.label ?? "Signed-in user";
|
|
39281
|
-
case "owner":
|
|
39282
|
-
return "This computer";
|
|
39283
|
-
case "routine":
|
|
39284
|
-
return `Routine: ${trigger.label ?? trigger.routineId ?? "unknown"}`;
|
|
39285
|
-
default:
|
|
39286
|
-
return "Bot to bot";
|
|
39287
|
-
}
|
|
39288
|
-
}
|
|
39289
|
-
function groupOf(row, groupBy) {
|
|
39290
|
-
switch (groupBy) {
|
|
39291
|
-
case "bot":
|
|
39292
|
-
return { key: `bot:${row.botId}`, label: row.botName || row.botId };
|
|
39293
|
-
case "model":
|
|
39294
|
-
return { key: `model:${row.driverKind}/${row.model}`, label: row.model };
|
|
39295
|
-
case "user":
|
|
39296
|
-
return { key: triggerKey(row.trigger), label: triggerLabel(row.trigger) };
|
|
39297
|
-
case "day":
|
|
39298
|
-
return { key: `day:${row.at.slice(0, 10)}`, label: row.at.slice(0, 10) };
|
|
39299
|
-
default:
|
|
39300
|
-
return { key: `engine:${row.driverKind}`, label: row.driverKind };
|
|
39301
|
-
}
|
|
39302
|
-
}
|
|
39303
|
-
function emptyGroup(key, label) {
|
|
39304
|
-
return { key, label, turns: 0, input: 0, output: 0, cachedInput: 0, costUsd: null, unpriced: 0, billableUsd: null };
|
|
39305
|
-
}
|
|
39306
|
-
function add(group, row, prices) {
|
|
39307
|
-
group.turns += 1;
|
|
39308
|
-
group.input += clean2(row.input);
|
|
39309
|
-
group.output += clean2(row.output);
|
|
39310
|
-
group.cachedInput += clean2(row.cachedInput);
|
|
39311
|
-
const cost = finiteOrNull(row.costUsd);
|
|
39312
|
-
if (cost === null) group.unpriced += 1;
|
|
39313
|
-
else group.costUsd = (group.costUsd ?? 0) + cost;
|
|
39314
|
-
const billable = prices ? billableFor(row, prices) : null;
|
|
39315
|
-
if (billable !== null) group.billableUsd = (group.billableUsd ?? 0) + billable;
|
|
39316
|
-
}
|
|
39317
|
-
function summarizeUsage(rows, groupBy, prices = null) {
|
|
39318
|
-
const groups = /* @__PURE__ */ new Map();
|
|
39319
|
-
const total = emptyGroup("total", "total");
|
|
39320
|
-
for (const row of rows) {
|
|
39321
|
-
const { key, label } = groupOf(row, groupBy);
|
|
39322
|
-
let group = groups.get(key);
|
|
39323
|
-
if (!group) {
|
|
39324
|
-
group = emptyGroup(key, label);
|
|
39325
|
-
groups.set(key, group);
|
|
39326
|
-
}
|
|
39327
|
-
add(group, row, prices);
|
|
39328
|
-
add(total, row, prices);
|
|
39329
|
-
}
|
|
39330
|
-
const ordered = [...groups.values()];
|
|
39331
|
-
if (groupBy === "day") ordered.sort((a2, b) => a2.key.localeCompare(b.key));
|
|
39332
|
-
else {
|
|
39333
|
-
ordered.sort((a2, b) => {
|
|
39334
|
-
const costA = a2.costUsd ?? Number.NEGATIVE_INFINITY;
|
|
39335
|
-
const costB = b.costUsd ?? Number.NEGATIVE_INFINITY;
|
|
39336
|
-
return costB - costA || b.input + b.output - (a2.input + a2.output);
|
|
39337
|
-
});
|
|
39338
|
-
}
|
|
39339
|
-
return { groups: ordered, total };
|
|
39340
|
-
}
|
|
39341
|
-
|
|
39342
|
-
// server/fleet-agent.ts
|
|
39343
39505
|
var MAX_BODY = 256 * 1024;
|
|
39344
39506
|
function readBody(req) {
|
|
39345
39507
|
return new Promise((resolve13, reject) => {
|
|
@@ -39363,10 +39525,20 @@ function readBody(req) {
|
|
|
39363
39525
|
var strings = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string").map((item) => item.trim()).filter(Boolean) : [];
|
|
39364
39526
|
var text = (value) => typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
39365
39527
|
var number = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
39366
|
-
|
|
39367
|
-
|
|
39368
|
-
|
|
39369
|
-
|
|
39528
|
+
var models = (value) => {
|
|
39529
|
+
try {
|
|
39530
|
+
return openRouterModelIds(value);
|
|
39531
|
+
} catch (error51) {
|
|
39532
|
+
throw Object.assign(error51, { status: 400 });
|
|
39533
|
+
}
|
|
39534
|
+
};
|
|
39535
|
+
function workspaceUsage(layout, slug, now, deps) {
|
|
39536
|
+
const month = now.toISOString().slice(0, 7);
|
|
39537
|
+
try {
|
|
39538
|
+
return { month, ...deps.usage(workspaceDataDir(layout, slug), fleetUser(slug), now) };
|
|
39539
|
+
} catch {
|
|
39540
|
+
return { month, turns: null, costUsd: null, billableUsd: null, unavailable: true };
|
|
39541
|
+
}
|
|
39370
39542
|
}
|
|
39371
39543
|
function createFleetAgent(options) {
|
|
39372
39544
|
const deps = options.deps ?? defaultFleetDeps();
|
|
@@ -39393,7 +39565,7 @@ function createFleetAgent(options) {
|
|
|
39393
39565
|
} catch {
|
|
39394
39566
|
}
|
|
39395
39567
|
};
|
|
39396
|
-
const
|
|
39568
|
+
const runAction = async (input) => {
|
|
39397
39569
|
const log = [];
|
|
39398
39570
|
const errors = [];
|
|
39399
39571
|
const io = { log: (line) => log.push(line), error: (line) => errors.push(line) };
|
|
@@ -39404,10 +39576,29 @@ function createFleetAgent(options) {
|
|
|
39404
39576
|
audit({ action: input.action, slug: input.slug, ok: false, error: error51 instanceof Error ? error51.message : String(error51) });
|
|
39405
39577
|
return { status: 400, body: { error: error51 instanceof Error ? error51.message : String(error51) } };
|
|
39406
39578
|
}
|
|
39407
|
-
|
|
39579
|
+
let code = 1;
|
|
39580
|
+
try {
|
|
39581
|
+
code = await executePlan(steps, deps, io);
|
|
39582
|
+
} catch (error51) {
|
|
39583
|
+
errors.push(error51 instanceof Error ? error51.message : String(error51));
|
|
39584
|
+
}
|
|
39585
|
+
if (code !== 0) {
|
|
39586
|
+
try {
|
|
39587
|
+
markProvisioningFailed(input, deps);
|
|
39588
|
+
} catch {
|
|
39589
|
+
errors.push("could not record the failed provisioning; inspect the reserved workspace before recovery");
|
|
39590
|
+
}
|
|
39591
|
+
}
|
|
39408
39592
|
audit({ action: input.action, slug: input.slug, ok: code === 0, ...input.email ? { email: input.email } : {} });
|
|
39409
39593
|
return code === 0 ? { status: 200, body: { ok: true, log } } : { status: 500, body: { error: errors[0] ?? "the step failed", log: [...log, ...errors] } };
|
|
39410
39594
|
};
|
|
39595
|
+
let mutationTail = Promise.resolve();
|
|
39596
|
+
const act = (input) => {
|
|
39597
|
+
const result = mutationTail.then(() => runAction(input));
|
|
39598
|
+
mutationTail = result.catch(() => {
|
|
39599
|
+
});
|
|
39600
|
+
return result;
|
|
39601
|
+
};
|
|
39411
39602
|
const handle = async (req, res) => {
|
|
39412
39603
|
const url2 = new URL(req.url ?? "/", "http://fleet");
|
|
39413
39604
|
const method = req.method ?? "GET";
|
|
@@ -39419,10 +39610,11 @@ function createFleetAgent(options) {
|
|
|
39419
39610
|
if (method === "GET" && url2.pathname === "/health") return send(200, { ok: true });
|
|
39420
39611
|
if (method === "GET" && url2.pathname === "/workspaces") {
|
|
39421
39612
|
const registry2 = loadRegistry(layout, deps);
|
|
39613
|
+
const statusOnly = url2.searchParams.get("statusOnly") === "true";
|
|
39422
39614
|
const workspaces = [];
|
|
39423
39615
|
for (const workspace of Object.values(registry2.workspaces).sort((a2, b) => a2.slug.localeCompare(b.slug))) {
|
|
39424
|
-
const live = (await deps.run(["systemctl", "is-active", `openmausbot@${workspace.slug}.service`])).output.trim() || "unknown";
|
|
39425
|
-
workspaces.push({ ...workspace, live, usage: workspaceUsage(layout, workspace.slug, now()) });
|
|
39616
|
+
const live = workspace.status === "running" || workspace.status === "suspended" ? (await deps.run(["systemctl", "is-active", `openmausbot@${workspace.slug}.service`])).output.trim() || "unknown" : workspace.status;
|
|
39617
|
+
workspaces.push(statusOnly ? { slug: workspace.slug, live } : { ...workspace, live, usage: workspaceUsage(layout, workspace.slug, now(), deps) });
|
|
39426
39618
|
}
|
|
39427
39619
|
return send(200, { domain: registry2.domain, operator: registry2.operator ?? null, workspaces });
|
|
39428
39620
|
}
|
|
@@ -39437,7 +39629,13 @@ function createFleetAgent(options) {
|
|
|
39437
39629
|
cap: number(body.cap),
|
|
39438
39630
|
memory: text(body.memory),
|
|
39439
39631
|
brandJson: text(body.brandJson),
|
|
39440
|
-
anthropicKey: text(body.anthropicKey)
|
|
39632
|
+
anthropicKey: text(body.anthropicKey),
|
|
39633
|
+
portalUrl: text(body.portalUrl),
|
|
39634
|
+
anthropicUrl: text(body.anthropicUrl),
|
|
39635
|
+
openrouterKey: text(body.openrouterKey),
|
|
39636
|
+
openrouterUrl: text(body.openrouterUrl),
|
|
39637
|
+
openrouterModels: body.openrouterModels === void 0 ? void 0 : models(body.openrouterModels),
|
|
39638
|
+
openrouterDefault: body.openrouterDefault === true
|
|
39441
39639
|
});
|
|
39442
39640
|
return send(result.status, result.body);
|
|
39443
39641
|
}
|
|
@@ -39445,11 +39643,16 @@ function createFleetAgent(options) {
|
|
|
39445
39643
|
const result = await act({ ...base2(), action: "upgrade" });
|
|
39446
39644
|
return send(result.status, result.body);
|
|
39447
39645
|
}
|
|
39448
|
-
const match = /^\/workspaces\/([a-z0-9-]+)(?:\/(users|suspend|resume))?$/.exec(url2.pathname);
|
|
39646
|
+
const match = /^\/workspaces\/([a-z0-9-]+)(?:\/(users|providers|suspend|resume))?$/.exec(url2.pathname);
|
|
39449
39647
|
if (match) {
|
|
39450
39648
|
const slug = match[1];
|
|
39451
39649
|
assertSlug(slug);
|
|
39452
39650
|
const sub = match[2];
|
|
39651
|
+
if (method === "POST" && sub === "providers") {
|
|
39652
|
+
const body = await readBody(req);
|
|
39653
|
+
const result = await act({ ...base2(), action: "providers", slug, openrouterModels: models(body.models) });
|
|
39654
|
+
return send(result.status, result.body);
|
|
39655
|
+
}
|
|
39453
39656
|
if (method === "POST" && sub === "users") {
|
|
39454
39657
|
const body = await readBody(req);
|
|
39455
39658
|
const action = body.action === "remove" ? "remove" : body.action === "add" ? "add" : void 0;
|
|
@@ -39500,11 +39703,11 @@ import { execFile } from "node:child_process";
|
|
|
39500
39703
|
|
|
39501
39704
|
// companion/src/listener.ts
|
|
39502
39705
|
import { homedir as homedir3, networkInterfaces } from "node:os";
|
|
39503
|
-
import { delimiter as delimiter2, join as
|
|
39706
|
+
import { delimiter as delimiter2, join as join4 } from "node:path";
|
|
39504
39707
|
function tailscaleCandidates(home = homedir3()) {
|
|
39505
39708
|
return [
|
|
39506
39709
|
"/Applications/Tailscale.app/Contents/MacOS/Tailscale",
|
|
39507
|
-
|
|
39710
|
+
join4(home, "Applications", "Tailscale.app", "Contents", "MacOS", "Tailscale"),
|
|
39508
39711
|
"/opt/homebrew/bin/tailscale",
|
|
39509
39712
|
"/usr/local/bin/tailscale",
|
|
39510
39713
|
"/usr/bin/tailscale",
|
|
@@ -39722,9 +39925,9 @@ function phonePairingInstructions(phone, input) {
|
|
|
39722
39925
|
// server/tunnel.ts
|
|
39723
39926
|
import { spawn as spawn4 } from "node:child_process";
|
|
39724
39927
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
39725
|
-
import { chmodSync as chmodSync3, existsSync as existsSync4, mkdirSync as mkdirSync5, readFileSync as
|
|
39928
|
+
import { chmodSync as chmodSync3, existsSync as existsSync4, mkdirSync as mkdirSync5, readFileSync as readFileSync2, statSync } from "node:fs";
|
|
39726
39929
|
import { hostname } from "node:os";
|
|
39727
|
-
import { dirname as dirname3, join as
|
|
39930
|
+
import { dirname as dirname3, join as join5, resolve as resolve2 } from "node:path";
|
|
39728
39931
|
import { fileURLToPath } from "node:url";
|
|
39729
39932
|
|
|
39730
39933
|
// electron/secure-credential-state.mjs
|
|
@@ -39807,13 +40010,13 @@ function plainDocument(value) {
|
|
|
39807
40010
|
return document;
|
|
39808
40011
|
}
|
|
39809
40012
|
function openTunnelCredentials(dataDir) {
|
|
39810
|
-
const file2 =
|
|
40013
|
+
const file2 = join5(dataDir, TUNNEL_CREDENTIALS_FILE);
|
|
39811
40014
|
let initial = {};
|
|
39812
40015
|
let status = "empty";
|
|
39813
40016
|
if (existsSync4(file2)) {
|
|
39814
40017
|
try {
|
|
39815
40018
|
if (process.platform !== "win32" && (statSync(file2).mode & 63) !== 0) chmodSync3(file2, 384);
|
|
39816
|
-
const parsed = plainDocument(JSON.parse(
|
|
40019
|
+
const parsed = plainDocument(JSON.parse(readFileSync2(file2, "utf8")));
|
|
39817
40020
|
if (!parsed) throw new Error("not a JSON object");
|
|
39818
40021
|
initial = parsed;
|
|
39819
40022
|
status = "ok";
|
|
@@ -39889,11 +40092,11 @@ function cloudflaredPath(dataDir, env = process.env) {
|
|
|
39889
40092
|
return resolveCloudflaredBinary({ isPackaged: false, appPath: dataDir, environment: env });
|
|
39890
40093
|
}
|
|
39891
40094
|
function prepareEntry(here = HERE) {
|
|
39892
|
-
const bundled =
|
|
40095
|
+
const bundled = join5(here, "prepare-cloudflared.js");
|
|
39893
40096
|
return existsSync4(bundled) ? bundled : resolve2(here, "..", "scripts", "prepare-cloudflared.mjs");
|
|
39894
40097
|
}
|
|
39895
40098
|
function guardianEntry(here = HERE) {
|
|
39896
|
-
const bundled =
|
|
40099
|
+
const bundled = join5(here, "tunnel-guardian.js");
|
|
39897
40100
|
if (existsSync4(bundled)) return bundled;
|
|
39898
40101
|
const source = resolve2(here, "..", "electron", "managed-companion-guardian-main.mjs");
|
|
39899
40102
|
return existsSync4(source) ? source : null;
|
|
@@ -39928,7 +40131,7 @@ function startTunnel(options) {
|
|
|
39928
40131
|
const tunnel = createManagedCompanionTunnel({
|
|
39929
40132
|
binaryPath: options.binaryPath,
|
|
39930
40133
|
guardianEntry: options.guardian,
|
|
39931
|
-
runtimeRoot:
|
|
40134
|
+
runtimeRoot: join5(options.dataDir, TUNNEL_RUNTIME_DIR),
|
|
39932
40135
|
originPort,
|
|
39933
40136
|
environment: env,
|
|
39934
40137
|
fetchImpl: options.fetchImpl,
|
|
@@ -39974,7 +40177,7 @@ function parseArgs(argv, env = process.env) {
|
|
|
39974
40177
|
const options = {
|
|
39975
40178
|
command: command === "--help" || command === "-h" ? "help" : command,
|
|
39976
40179
|
port: Number(env.OMB_PORT || 8799),
|
|
39977
|
-
dataDir: env.OMB_DATA_DIR ||
|
|
40180
|
+
dataDir: env.OMB_DATA_DIR || join37(homedir24(), ".openmausbot"),
|
|
39978
40181
|
tailscale: false,
|
|
39979
40182
|
tunnel: false,
|
|
39980
40183
|
client: false,
|
|
@@ -40141,7 +40344,7 @@ function defaultIo() {
|
|
|
40141
40344
|
}
|
|
40142
40345
|
function serverVersion(here = HERE2) {
|
|
40143
40346
|
try {
|
|
40144
|
-
const parsed = JSON.parse(
|
|
40347
|
+
const parsed = JSON.parse(readFileSync21(resolve12(here, "..", "package.json"), "utf8"));
|
|
40145
40348
|
const version2 = typeof parsed === "object" && parsed !== null ? Reflect.get(parsed, "version") : void 0;
|
|
40146
40349
|
return typeof version2 === "string" && version2 ? version2 : "0.0.0";
|
|
40147
40350
|
} catch {
|
|
@@ -40166,7 +40369,7 @@ async function isWorkspaceRunning(options) {
|
|
|
40166
40369
|
try {
|
|
40167
40370
|
const { status, body } = await api(options.port, "/api/health");
|
|
40168
40371
|
if (status !== 200 || body?.app !== "openmausbot") return false;
|
|
40169
|
-
const expected =
|
|
40372
|
+
const expected = readFileSync21(join37(options.dataDir, "environment-id"), "utf8").trim();
|
|
40170
40373
|
const descriptor = await api(options.port, "/.well-known/openmausbot/environment");
|
|
40171
40374
|
return /^[0-9a-f-]{36}$/i.test(expected) && descriptor.status === 200 && descriptor.body?.environmentId === expected;
|
|
40172
40375
|
} catch {
|
|
@@ -40366,11 +40569,11 @@ async function runStatus(options, io = defaultIo()) {
|
|
|
40366
40569
|
return code;
|
|
40367
40570
|
}
|
|
40368
40571
|
async function runAccess(options, io = defaultIo()) {
|
|
40369
|
-
const file2 =
|
|
40572
|
+
const file2 = join37(options.dataDir, "config.json");
|
|
40370
40573
|
let raw = {};
|
|
40371
40574
|
if (existsSync19(file2)) {
|
|
40372
40575
|
try {
|
|
40373
|
-
const parsed = JSON.parse(
|
|
40576
|
+
const parsed = JSON.parse(readFileSync21(file2, "utf8"));
|
|
40374
40577
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error("not an object");
|
|
40375
40578
|
raw = Object.fromEntries(Object.entries(parsed));
|
|
40376
40579
|
} catch (error51) {
|
|
@@ -40518,16 +40721,16 @@ async function runBrowser(options, io = defaultIo()) {
|
|
|
40518
40721
|
return 0;
|
|
40519
40722
|
}
|
|
40520
40723
|
function serverEntry(here = HERE2) {
|
|
40521
|
-
const bundled =
|
|
40724
|
+
const bundled = join37(here, "index.js");
|
|
40522
40725
|
const root = resolve12(here, "..");
|
|
40523
40726
|
if (existsSync19(bundled)) {
|
|
40524
|
-
const staticDir2 = [
|
|
40525
|
-
const skillsDir = existsSync19(
|
|
40727
|
+
const staticDir2 = [join37(root, "dist"), join37(here, "..", "ui")].find((d) => existsSync19(join37(d, "index.html"))) ?? null;
|
|
40728
|
+
const skillsDir = existsSync19(join37(root, "skills")) ? join37(root, "skills") : null;
|
|
40526
40729
|
return { command: process.execPath, args: [bundled], staticDir: staticDir2, skillsDir };
|
|
40527
40730
|
}
|
|
40528
|
-
const source =
|
|
40529
|
-
const staticDir = existsSync19(
|
|
40530
|
-
return { command: process.execPath, args: ["--experimental-strip-types", source], staticDir, skillsDir: existsSync19(
|
|
40731
|
+
const source = join37(here, "index.ts");
|
|
40732
|
+
const staticDir = existsSync19(join37(root, "dist", "index.html")) ? join37(root, "dist") : null;
|
|
40733
|
+
return { command: process.execPath, args: ["--experimental-strip-types", source], staticDir, skillsDir: existsSync19(join37(root, "skills")) ? join37(root, "skills") : null };
|
|
40531
40734
|
}
|
|
40532
40735
|
async function planTunnel(options, log) {
|
|
40533
40736
|
let access2 = null;
|
|
@@ -40627,10 +40830,10 @@ async function runServe(options, log = console.log) {
|
|
|
40627
40830
|
let child;
|
|
40628
40831
|
try {
|
|
40629
40832
|
if (options.guided) {
|
|
40630
|
-
const logsDir =
|
|
40833
|
+
const logsDir = join37(options.dataDir, "logs");
|
|
40631
40834
|
mkdirSync17(logsDir, { recursive: true, mode: 448 });
|
|
40632
|
-
logPath =
|
|
40633
|
-
logFd =
|
|
40835
|
+
logPath = join37(logsDir, `server-${Date.now()}-${process.pid}.log`);
|
|
40836
|
+
logFd = openSync4(logPath, "wx", 384);
|
|
40634
40837
|
log("\nStarting your workspace\u2026");
|
|
40635
40838
|
}
|
|
40636
40839
|
if (tailscale) {
|
|
@@ -40654,7 +40857,7 @@ async function runServe(options, log = console.log) {
|
|
|
40654
40857
|
}
|
|
40655
40858
|
throw error51;
|
|
40656
40859
|
} finally {
|
|
40657
|
-
if (logFd !== void 0)
|
|
40860
|
+
if (logFd !== void 0) closeSync4(logFd);
|
|
40658
40861
|
process.removeListener("SIGINT", cancelStartup);
|
|
40659
40862
|
process.removeListener("SIGTERM", cancelStartup);
|
|
40660
40863
|
}
|
|
@@ -40821,7 +41024,7 @@ async function runOnboardingCommand(options, io = defaultIo(), startServer = run
|
|
|
40821
41024
|
}
|
|
40822
41025
|
if (options.command === "setup") {
|
|
40823
41026
|
io.log("\nAll set. Start with: openmausbot (or npx openmausbot without a global install).");
|
|
40824
|
-
if (options.dataDir !==
|
|
41027
|
+
if (options.dataDir !== join37(homedir24(), ".openmausbot") || options.port !== 8799) {
|
|
40825
41028
|
io.log(`Use the same --data-dir (${options.dataDir}) and --port (${options.port}) options when starting.`);
|
|
40826
41029
|
}
|
|
40827
41030
|
return 0;
|