openmausbot 0.1.75 → 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-BTlyRwLl.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/companion/src/routes.js +6 -2
- package/dist-server/container-mcp.js +5 -1
- package/dist-server/drivers/agents-proxy.js +184 -11
- package/dist-server/enterprise/server/index.js +387 -29
- package/dist-server/index.js +3786 -1135
- package/dist-server/mcp-gate.js +28 -18
- package/dist-server/openmausbot.js +927 -642
- package/dist-server/pair-cli.js +927 -642
- package/dist-server/server/agent-tool-policy.js +2 -0
- package/dist-server/server/atomic.js +37 -1
- package/dist-server/server/bot-overview.js +5 -1
- package/dist-server/server/browser-live.js +3 -0
- package/dist-server/server/chief-of-staff.js +17 -12
- package/dist-server/server/config.js +16 -0
- package/dist-server/server/delegations.js +177 -63
- package/dist-server/server/drivers/acp/core.js +3 -1
- package/dist-server/server/drivers/acp/hermes.js +21 -3
- package/dist-server/server/drivers/agents-proxy.js +169 -10
- package/dist-server/server/drivers/boxagent.js +1 -1
- package/dist-server/server/drivers/claude.js +34 -16
- package/dist-server/server/drivers/codex.js +3 -1
- package/dist-server/server/drivers/openai-chat.js +90 -67
- package/dist-server/server/drivers/openai-compat.js +9 -1
- package/dist-server/server/drivers/pi.js +22 -3
- package/dist-server/server/enterprise.js +38 -0
- package/dist-server/server/env-path.js +18 -17
- 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 +1777 -406
- package/dist-server/server/message-db.js +21 -0
- package/dist-server/server/message-file.js +4 -0
- package/dist-server/server/peer-roster.js +48 -9
- package/dist-server/server/procs.js +19 -3
- package/dist-server/server/provider-key-check.js +18 -1
- package/dist-server/server/request-auth.js +14 -5
- package/dist-server/server/room-handoffs.js +237 -0
- package/dist-server/server/section-context.js +64 -14
- 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 +267 -15
- package/dist-server/server/surface.js +16 -5
- package/dist-server/server/team-backup.js +17 -1
- package/dist-server/server/team-computers.js +108 -0
- package/dist-server/server/team-setup-requests.js +252 -0
- package/dist-server/server/tool-summary.js +53 -1
- package/dist-server/server/turn-dispatch-guard.js +31 -3
- package/dist-server/server/workspace-backup.js +1 -0
- package/dist-server/shared/approval-mode.js +11 -1
- package/dist-server/shared/team-backup.js +1 -0
- package/dist-server/shared/team-computer.js +1 -0
- package/dist-server/shared/team-setup.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-CeMbvsgx.js +0 -305
- package/dist/assets/index-DH7Zax6L.css +0 -1
package/dist-server/pair-cli.js
CHANGED
|
@@ -1113,6 +1113,21 @@ var require_main = __commonJS({
|
|
|
1113
1113
|
// server/atomic.ts
|
|
1114
1114
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
1115
1115
|
import { closeSync, fsyncSync, openSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
|
|
1116
|
+
function sleepSync(ms) {
|
|
1117
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
1118
|
+
}
|
|
1119
|
+
function renameWithRetry(tmp, path3, rename2 = renameSync) {
|
|
1120
|
+
for (let attempt = 0; ; attempt += 1) {
|
|
1121
|
+
try {
|
|
1122
|
+
rename2(tmp, path3);
|
|
1123
|
+
return;
|
|
1124
|
+
} catch (e) {
|
|
1125
|
+
const code = e.code;
|
|
1126
|
+
if (!code || !RETRYABLE_RENAME_CODES.has(code) || attempt >= RENAME_RETRY_DELAYS_MS.length) throw e;
|
|
1127
|
+
sleepSync(RENAME_RETRY_DELAYS_MS[attempt]);
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1116
1131
|
function writeFileAtomic(path3, data, options = {}) {
|
|
1117
1132
|
const tmp = `${path3}.${process.pid}.${randomUUID3()}.tmp`;
|
|
1118
1133
|
let fd = null;
|
|
@@ -1122,7 +1137,7 @@ function writeFileAtomic(path3, data, options = {}) {
|
|
|
1122
1137
|
fsyncSync(fd);
|
|
1123
1138
|
closeSync(fd);
|
|
1124
1139
|
fd = null;
|
|
1125
|
-
|
|
1140
|
+
renameWithRetry(tmp, path3);
|
|
1126
1141
|
} catch (e) {
|
|
1127
1142
|
if (fd !== null) {
|
|
1128
1143
|
try {
|
|
@@ -1137,9 +1152,12 @@ function writeFileAtomic(path3, data, options = {}) {
|
|
|
1137
1152
|
throw e;
|
|
1138
1153
|
}
|
|
1139
1154
|
}
|
|
1155
|
+
var RENAME_RETRY_DELAYS_MS, RETRYABLE_RENAME_CODES;
|
|
1140
1156
|
var init_atomic = __esm({
|
|
1141
1157
|
"server/atomic.ts"() {
|
|
1142
1158
|
"use strict";
|
|
1159
|
+
RENAME_RETRY_DELAYS_MS = [5, 10, 20, 40, 80];
|
|
1160
|
+
RETRYABLE_RENAME_CODES = /* @__PURE__ */ new Set(["EPERM", "EACCES", "EBUSY"]);
|
|
1143
1161
|
}
|
|
1144
1162
|
});
|
|
1145
1163
|
|
|
@@ -17601,9 +17619,9 @@ var init_schema = __esm({
|
|
|
17601
17619
|
});
|
|
17602
17620
|
|
|
17603
17621
|
// server/config.ts
|
|
17604
|
-
import { readFileSync as
|
|
17622
|
+
import { readFileSync as readFileSync3, mkdirSync as mkdirSync6, existsSync as existsSync5, renameSync as renameSync3 } from "node:fs";
|
|
17605
17623
|
import { homedir as homedir4 } from "node:os";
|
|
17606
|
-
import { join as
|
|
17624
|
+
import { join as join6 } from "node:path";
|
|
17607
17625
|
function isValidSshAlias(value) {
|
|
17608
17626
|
return typeof value === "string" && SSH_ALIAS.test(value);
|
|
17609
17627
|
}
|
|
@@ -17736,7 +17754,7 @@ function parseStoredConfig(value) {
|
|
|
17736
17754
|
function loadConfig() {
|
|
17737
17755
|
let cfg = {};
|
|
17738
17756
|
try {
|
|
17739
|
-
cfg = parseStoredConfig(parseJson(
|
|
17757
|
+
cfg = parseStoredConfig(parseJson(readFileSync3(join6(DATA_DIR, "config.json"), "utf8")));
|
|
17740
17758
|
} catch {
|
|
17741
17759
|
}
|
|
17742
17760
|
cfg.xai = { ...cfg.xai };
|
|
@@ -17772,10 +17790,10 @@ function stripWorkspaceCredentialEnv(env) {
|
|
|
17772
17790
|
for (const key of WORKSPACE_CREDENTIAL_ENV) delete env[key];
|
|
17773
17791
|
}
|
|
17774
17792
|
function saveConfig(patch, options = {}) {
|
|
17775
|
-
const p =
|
|
17793
|
+
const p = join6(DATA_DIR, "config.json");
|
|
17776
17794
|
let disk = {};
|
|
17777
17795
|
try {
|
|
17778
|
-
const parsed = jsonObjectSchema.safeParse(parseJson(
|
|
17796
|
+
const parsed = jsonObjectSchema.safeParse(parseJson(readFileSync3(p, "utf8")));
|
|
17779
17797
|
if (parsed.success) disk = parsed.data;
|
|
17780
17798
|
} catch {
|
|
17781
17799
|
}
|
|
@@ -17977,7 +17995,11 @@ var init_config = __esm({
|
|
|
17977
17995
|
showToolCalls: external_exports.boolean().optional(),
|
|
17978
17996
|
/** Experimental built-in browser. Off until explicitly enabled; each bot
|
|
17979
17997
|
* also has its own switch. */
|
|
17980
|
-
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()
|
|
17981
18003
|
});
|
|
17982
18004
|
onboardingConfigSchema = external_exports.object({
|
|
17983
18005
|
/** ISO timestamp of finishing (or skipping to the end of) the welcome flow. */
|
|
@@ -18094,10 +18116,10 @@ var init_config = __esm({
|
|
|
18094
18116
|
});
|
|
18095
18117
|
appConfigPatchSchema = appConfigSchema.omit({ instances: true, mcpServers: true, cliStartup: true, customDomain: true });
|
|
18096
18118
|
jsonObjectSchema = external_exports.record(external_exports.string(), external_exports.json());
|
|
18097
|
-
DATA_DIR = process.env.OMB_DATA_DIR ??
|
|
18098
|
-
LEGACY_DATA_DIR =
|
|
18099
|
-
EVENTS_DIR =
|
|
18100
|
-
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");
|
|
18101
18123
|
WORKSPACE_CREDENTIAL_ENV = [
|
|
18102
18124
|
"XAI_API_KEY",
|
|
18103
18125
|
"OMB_ANTHROPIC_API_KEY",
|
|
@@ -18135,11 +18157,11 @@ var init_config = __esm({
|
|
|
18135
18157
|
});
|
|
18136
18158
|
|
|
18137
18159
|
// electron/data-dir-lease.mjs
|
|
18138
|
-
import { execFileSync } from "node:child_process";
|
|
18160
|
+
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
18139
18161
|
import { createHash as createHash2, randomUUID as randomUUID5 } from "node:crypto";
|
|
18140
|
-
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";
|
|
18141
18163
|
import { hostname as hostname4, uptime } from "node:os";
|
|
18142
|
-
import { join as
|
|
18164
|
+
import { join as join7 } from "node:path";
|
|
18143
18165
|
function leaseError(message2, cause) {
|
|
18144
18166
|
return Object.assign(new DataDirLeaseError(message2), { cause });
|
|
18145
18167
|
}
|
|
@@ -18149,10 +18171,10 @@ function isPid(value) {
|
|
|
18149
18171
|
function readBootSession() {
|
|
18150
18172
|
try {
|
|
18151
18173
|
if (process.platform === "linux") {
|
|
18152
|
-
return normalizeBootId(
|
|
18174
|
+
return normalizeBootId(readFileSync4("/proc/sys/kernel/random/boot_id", "utf8"));
|
|
18153
18175
|
}
|
|
18154
18176
|
if (process.platform === "darwin") {
|
|
18155
|
-
return normalizeBootId(
|
|
18177
|
+
return normalizeBootId(execFileSync2("/usr/sbin/sysctl", ["-n", "kern.bootsessionuuid"], {
|
|
18156
18178
|
encoding: "utf8",
|
|
18157
18179
|
timeout: 5e3,
|
|
18158
18180
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -18196,7 +18218,7 @@ function isReaperOwner(value, targetToken) {
|
|
|
18196
18218
|
function parseRecord(path3, invalidMessage, validate) {
|
|
18197
18219
|
let raw;
|
|
18198
18220
|
try {
|
|
18199
|
-
raw =
|
|
18221
|
+
raw = readFileSync4(path3, "utf8");
|
|
18200
18222
|
} catch (error51) {
|
|
18201
18223
|
if (error51?.code === "ENOENT") return null;
|
|
18202
18224
|
throw leaseError("OpenMausBot cannot read the data-directory lease; refusing to start to protect its state.", error51);
|
|
@@ -18244,7 +18266,7 @@ function unlinkExact(path3, message2) {
|
|
|
18244
18266
|
function publishRecord(path3, record3, prepareMessage, acquireMessage) {
|
|
18245
18267
|
const candidatePath = `${path3}.candidate-${record3.pid}-${record3.token}`;
|
|
18246
18268
|
try {
|
|
18247
|
-
|
|
18269
|
+
writeFileSync4(candidatePath, `${JSON.stringify(record3)}
|
|
18248
18270
|
`, { flag: "wx", mode: 384, flush: true });
|
|
18249
18271
|
} catch (error51) {
|
|
18250
18272
|
throw leaseError(prepareMessage, error51);
|
|
@@ -18331,10 +18353,10 @@ function prepareDataDir(dataDir, legacyDataDir) {
|
|
|
18331
18353
|
} catch (error51) {
|
|
18332
18354
|
throw leaseError("OpenMausBot cannot create its data directory.", error51);
|
|
18333
18355
|
}
|
|
18334
|
-
return
|
|
18356
|
+
return join7(dataDir, LEASE_NAME);
|
|
18335
18357
|
}
|
|
18336
18358
|
function assertNoLiveDelegatedChild(dataDir) {
|
|
18337
|
-
const childLeasePath =
|
|
18359
|
+
const childLeasePath = join7(dataDir, DELEGATED_CHILD_DIR, LEASE_NAME);
|
|
18338
18360
|
const child = readOwner(childLeasePath);
|
|
18339
18361
|
if (!child) return;
|
|
18340
18362
|
if (child.host !== hostname4()) {
|
|
@@ -18365,7 +18387,7 @@ function acquireDataDirLeaseInternal(dataDir, options = {}) {
|
|
|
18365
18387
|
};
|
|
18366
18388
|
const candidatePath = `${leasePath}.candidate-${owner.pid}-${owner.token}`;
|
|
18367
18389
|
try {
|
|
18368
|
-
|
|
18390
|
+
writeFileSync4(candidatePath, `${JSON.stringify(owner)}
|
|
18369
18391
|
`, { flag: "wx", mode: 384, flush: true });
|
|
18370
18392
|
} catch (error51) {
|
|
18371
18393
|
throw leaseError("OpenMausBot cannot prepare its data-directory lease.", error51);
|
|
@@ -18457,13 +18479,13 @@ var init_data_dir_lease2 = __esm({
|
|
|
18457
18479
|
|
|
18458
18480
|
// server/env-path.ts
|
|
18459
18481
|
import { execFile as execFile2 } from "node:child_process";
|
|
18460
|
-
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";
|
|
18461
18483
|
import { homedir as homedir5 } from "node:os";
|
|
18462
|
-
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";
|
|
18463
18485
|
function nvmBinDirs() {
|
|
18464
18486
|
try {
|
|
18465
|
-
const base2 =
|
|
18466
|
-
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"));
|
|
18467
18489
|
} catch {
|
|
18468
18490
|
return [];
|
|
18469
18491
|
}
|
|
@@ -18471,49 +18493,49 @@ function nvmBinDirs() {
|
|
|
18471
18493
|
function knownDirs() {
|
|
18472
18494
|
const home = homedir5();
|
|
18473
18495
|
return [
|
|
18474
|
-
|
|
18496
|
+
join8(home, ".local", "bin"),
|
|
18475
18497
|
// claude installer default
|
|
18476
|
-
|
|
18498
|
+
join8(home, ".npm-global", "bin"),
|
|
18477
18499
|
// npm prefix ~/.npm-global (claude, opencode)
|
|
18478
|
-
|
|
18500
|
+
join8(home, ".kimi-code", "bin"),
|
|
18479
18501
|
// kimi-code installer
|
|
18480
|
-
|
|
18502
|
+
join8(home, ".grok", "bin"),
|
|
18481
18503
|
// x.ai installer
|
|
18482
|
-
|
|
18504
|
+
join8(home, ".opencode", "bin"),
|
|
18483
18505
|
// opencode installer
|
|
18484
|
-
|
|
18506
|
+
join8(home, ".claude", "local"),
|
|
18485
18507
|
// claude "local install"
|
|
18486
18508
|
"/opt/homebrew/bin",
|
|
18487
18509
|
// brew, Apple silicon
|
|
18488
18510
|
"/usr/local/bin",
|
|
18489
18511
|
// brew Intel / classic installs
|
|
18490
|
-
|
|
18491
|
-
|
|
18492
|
-
|
|
18493
|
-
|
|
18494
|
-
|
|
18512
|
+
join8(home, ".volta", "bin"),
|
|
18513
|
+
join8(home, ".bun", "bin"),
|
|
18514
|
+
join8(home, ".asdf", "shims"),
|
|
18515
|
+
join8(home, ".deno", "bin"),
|
|
18516
|
+
join8(home, "bin"),
|
|
18495
18517
|
...nvmBinDirs()
|
|
18496
18518
|
];
|
|
18497
18519
|
}
|
|
18498
18520
|
function windowsKnownDirs() {
|
|
18499
18521
|
const home = homedir5();
|
|
18500
|
-
const appData = process.env.APPDATA ??
|
|
18501
|
-
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");
|
|
18502
18524
|
return [
|
|
18503
|
-
|
|
18525
|
+
join8(appData, "npm"),
|
|
18504
18526
|
// npm -g shims: claude, codex
|
|
18505
|
-
|
|
18527
|
+
join8(home, ".grok", "bin"),
|
|
18506
18528
|
// x.ai installer
|
|
18507
|
-
|
|
18529
|
+
join8(localAppData, "agy", "bin"),
|
|
18508
18530
|
// Antigravity installer
|
|
18509
|
-
|
|
18531
|
+
join8(home, ".local", "bin"),
|
|
18510
18532
|
// claude native installer
|
|
18511
|
-
|
|
18512
|
-
|
|
18533
|
+
join8(home, ".claude", "local"),
|
|
18534
|
+
join8(home, "bin"),
|
|
18513
18535
|
// Factory droid installer (%USERPROFILE%\bin)
|
|
18514
|
-
|
|
18515
|
-
|
|
18516
|
-
|
|
18536
|
+
join8(home, ".bun", "bin"),
|
|
18537
|
+
join8(home, ".deno", "bin"),
|
|
18538
|
+
join8(home, "go", "bin")
|
|
18517
18539
|
];
|
|
18518
18540
|
}
|
|
18519
18541
|
function registerPathDir(dir) {
|
|
@@ -18573,7 +18595,7 @@ function findCliCandidates(name) {
|
|
|
18573
18595
|
for (const dir of augmentedPath().split(delimiter3)) {
|
|
18574
18596
|
if (!dir) continue;
|
|
18575
18597
|
for (const ext of exts) {
|
|
18576
|
-
const p =
|
|
18598
|
+
const p = join8(dir, name + ext);
|
|
18577
18599
|
if (existsSync7(p)) {
|
|
18578
18600
|
out.push(p);
|
|
18579
18601
|
break;
|
|
@@ -18609,49 +18631,50 @@ function isFile(p) {
|
|
|
18609
18631
|
return false;
|
|
18610
18632
|
}
|
|
18611
18633
|
}
|
|
18612
|
-
function whichWin(cli) {
|
|
18613
|
-
const exts = (process.env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean);
|
|
18634
|
+
function whichWin(cli, env) {
|
|
18635
|
+
const exts = ((env ?? process.env).PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean);
|
|
18614
18636
|
const probe = (base2) => {
|
|
18615
18637
|
const order = extname(base2) ? [base2, ...exts.map((e) => base2 + e)] : [...exts.map((e) => base2 + e), base2];
|
|
18616
18638
|
return order.find(isFile) ?? null;
|
|
18617
18639
|
};
|
|
18618
18640
|
if (/[\\/]/.test(cli) || /^[a-zA-Z]:/.test(cli)) return probe(cli);
|
|
18619
|
-
|
|
18641
|
+
const path3 = env ? env.PATH ?? env.Path ?? "" : augmentedPath();
|
|
18642
|
+
for (const dir of path3.split(delimiter3)) {
|
|
18620
18643
|
if (!dir) continue;
|
|
18621
|
-
const hit = probe(
|
|
18644
|
+
const hit = probe(join8(dir, cli));
|
|
18622
18645
|
if (hit) return hit;
|
|
18623
18646
|
}
|
|
18624
18647
|
return null;
|
|
18625
18648
|
}
|
|
18626
|
-
function nodeExe(near) {
|
|
18627
|
-
const local =
|
|
18649
|
+
function nodeExe(near, env) {
|
|
18650
|
+
const local = join8(near, "node.exe");
|
|
18628
18651
|
if (isFile(local)) return local;
|
|
18629
|
-
const onPath2 = whichWin("node.exe");
|
|
18652
|
+
const onPath2 = whichWin("node.exe", env);
|
|
18630
18653
|
if (onPath2 && extname(onPath2).toLowerCase() === ".exe") return onPath2;
|
|
18631
18654
|
return process.versions.electron ? null : process.execPath;
|
|
18632
18655
|
}
|
|
18633
|
-
function parseCmdShim(shim) {
|
|
18656
|
+
function parseCmdShim(shim, env) {
|
|
18634
18657
|
let text4;
|
|
18635
18658
|
try {
|
|
18636
|
-
text4 =
|
|
18659
|
+
text4 = readFileSync5(shim, "utf8");
|
|
18637
18660
|
} catch {
|
|
18638
18661
|
return null;
|
|
18639
18662
|
}
|
|
18640
18663
|
const dir = dirname4(shim);
|
|
18641
|
-
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");
|
|
18642
18665
|
const script = targets.find((p) => /\.[cm]?js$/i.test(p));
|
|
18643
18666
|
if (script) {
|
|
18644
|
-
const node = nodeExe(dir);
|
|
18667
|
+
const node = nodeExe(dir, env);
|
|
18645
18668
|
if (node) return { command: node, args: [script] };
|
|
18646
18669
|
}
|
|
18647
18670
|
const exe = targets.find((p) => extname(p).toLowerCase() === ".exe");
|
|
18648
18671
|
return exe ? { command: exe, args: [] } : null;
|
|
18649
18672
|
}
|
|
18650
|
-
function parseNodeShebang(file2) {
|
|
18673
|
+
function parseNodeShebang(file2, env) {
|
|
18651
18674
|
let head = "";
|
|
18652
18675
|
let fd = null;
|
|
18653
18676
|
try {
|
|
18654
|
-
fd =
|
|
18677
|
+
fd = openSync3(file2, "r");
|
|
18655
18678
|
const buf = Buffer.alloc(128);
|
|
18656
18679
|
const n3 = readSync(fd, buf, 0, buf.length, 0);
|
|
18657
18680
|
head = buf.subarray(0, n3).toString("utf8").split("\n", 1)[0];
|
|
@@ -18660,38 +18683,38 @@ function parseNodeShebang(file2) {
|
|
|
18660
18683
|
} finally {
|
|
18661
18684
|
if (fd !== null) {
|
|
18662
18685
|
try {
|
|
18663
|
-
|
|
18686
|
+
closeSync3(fd);
|
|
18664
18687
|
} catch {
|
|
18665
18688
|
}
|
|
18666
18689
|
}
|
|
18667
18690
|
}
|
|
18668
18691
|
if (!/^#!.*\bnode(\.exe)?\b/.test(head)) return null;
|
|
18669
|
-
const node = nodeExe(dirname4(file2));
|
|
18692
|
+
const node = nodeExe(dirname4(file2), env);
|
|
18670
18693
|
return node ? { command: node, args: [file2] } : null;
|
|
18671
18694
|
}
|
|
18672
|
-
function resolveWord(cli, args) {
|
|
18695
|
+
function resolveWord(cli, args, env) {
|
|
18673
18696
|
if (process.platform !== "win32") return { command: cli, args };
|
|
18674
|
-
const file2 = whichWin(cli);
|
|
18697
|
+
const file2 = whichWin(cli, env);
|
|
18675
18698
|
if (!file2) return { command: cli, args };
|
|
18676
18699
|
const ext = extname(file2).toLowerCase();
|
|
18677
18700
|
if (ext === ".cmd" || ext === ".bat") {
|
|
18678
|
-
const direct = parseCmdShim(file2);
|
|
18701
|
+
const direct = parseCmdShim(file2, env);
|
|
18679
18702
|
return direct ? { command: direct.command, args: [...direct.args, ...args] } : { command: file2, args };
|
|
18680
18703
|
}
|
|
18681
18704
|
if (ext === ".exe" || ext === ".com") return { command: file2, args };
|
|
18682
|
-
const viaNode = parseNodeShebang(file2);
|
|
18705
|
+
const viaNode = parseNodeShebang(file2, env);
|
|
18683
18706
|
return viaNode ? { command: viaNode.command, args: [...viaNode.args, ...args] } : { command: file2, args };
|
|
18684
18707
|
}
|
|
18685
|
-
function resolveCliSpawn(cli, args) {
|
|
18708
|
+
function resolveCliSpawn(cli, args, env) {
|
|
18686
18709
|
const trimmed = cli.trim();
|
|
18687
|
-
if (trimmed.includes(" ") && existsSync7(trimmed)) return resolveWord(trimmed, args);
|
|
18710
|
+
if (trimmed.includes(" ") && existsSync7(trimmed)) return resolveWord(trimmed, args, env);
|
|
18688
18711
|
const split = splitCliString(cli);
|
|
18689
18712
|
if (split.length > 1 || split[0] !== trimmed) {
|
|
18690
18713
|
const [head, ...fixed] = split;
|
|
18691
18714
|
if (!head) return { command: cli, args };
|
|
18692
|
-
return resolveWord(head, [...fixed, ...args]);
|
|
18715
|
+
return resolveWord(head, [...fixed, ...args], env);
|
|
18693
18716
|
}
|
|
18694
|
-
return resolveWord(cli, args);
|
|
18717
|
+
return resolveWord(cli, args, env);
|
|
18695
18718
|
}
|
|
18696
18719
|
var cached2, probed, loginShellPath, registeredDirs;
|
|
18697
18720
|
var init_env_path = __esm({
|
|
@@ -18709,9 +18732,9 @@ import {
|
|
|
18709
18732
|
spawn as spawn5,
|
|
18710
18733
|
execFile as execFile3
|
|
18711
18734
|
} from "node:child_process";
|
|
18712
|
-
import { join as
|
|
18713
|
-
function resolveCli(cli, args = []) {
|
|
18714
|
-
return resolveCliSpawn(cli, args);
|
|
18735
|
+
import { join as join9 } from "node:path";
|
|
18736
|
+
function resolveCli(cli, args = [], env) {
|
|
18737
|
+
return resolveCliSpawn(cli, args, env);
|
|
18715
18738
|
}
|
|
18716
18739
|
function estimatedWindowsCommandLineChars(resolved) {
|
|
18717
18740
|
return [resolved.command, ...resolved.args].reduce(
|
|
@@ -18825,9 +18848,13 @@ async function stopCliTree(child, pid, timeoutMs) {
|
|
|
18825
18848
|
if (exited()) return true;
|
|
18826
18849
|
return new Promise((resolve13) => {
|
|
18827
18850
|
let timer;
|
|
18851
|
+
let finished = false;
|
|
18828
18852
|
const done = (stopped) => {
|
|
18853
|
+
if (finished) return;
|
|
18854
|
+
finished = true;
|
|
18829
18855
|
clearTimeout(timer);
|
|
18830
18856
|
child.off("close", closed);
|
|
18857
|
+
child.off("exit", closed);
|
|
18831
18858
|
resolve13(stopped);
|
|
18832
18859
|
};
|
|
18833
18860
|
const closed = () => done(true);
|
|
@@ -18835,7 +18862,12 @@ async function stopCliTree(child, pid, timeoutMs) {
|
|
|
18835
18862
|
timer = setTimeout(() => done(false), timeoutMs);
|
|
18836
18863
|
timer.unref?.();
|
|
18837
18864
|
execFile3("taskkill", ["/PID", String(pid), "/T", "/F"], { windowsHide: true }, (err) => {
|
|
18838
|
-
if (
|
|
18865
|
+
if (finished) return;
|
|
18866
|
+
if (!err) {
|
|
18867
|
+
if (exited()) done(true);
|
|
18868
|
+
else child.once("exit", closed);
|
|
18869
|
+
return;
|
|
18870
|
+
}
|
|
18839
18871
|
try {
|
|
18840
18872
|
child.kill();
|
|
18841
18873
|
} catch {
|
|
@@ -18844,7 +18876,7 @@ async function stopCliTree(child, pid, timeoutMs) {
|
|
|
18844
18876
|
});
|
|
18845
18877
|
}
|
|
18846
18878
|
function brokerSocketPath(dataDir, tag) {
|
|
18847
|
-
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`);
|
|
18848
18880
|
}
|
|
18849
18881
|
var WINDOWS_SAFE_COMMAND_LINE_CHARS, cliGroups, stopping;
|
|
18850
18882
|
var init_procs = __esm({
|
|
@@ -18859,13 +18891,13 @@ var init_procs = __esm({
|
|
|
18859
18891
|
|
|
18860
18892
|
// server/engine-install.ts
|
|
18861
18893
|
import { existsSync as existsSync8, mkdirSync as mkdirSync8 } from "node:fs";
|
|
18862
|
-
import { join as
|
|
18894
|
+
import { join as join10 } from "node:path";
|
|
18863
18895
|
import { stripVTControlCharacters as stripVTControlCharacters3 } from "node:util";
|
|
18864
18896
|
function enginesPrefix(baseDir = DATA_DIR) {
|
|
18865
|
-
return
|
|
18897
|
+
return join10(baseDir, "tools", "npm");
|
|
18866
18898
|
}
|
|
18867
18899
|
function enginesBinDir(baseDir = DATA_DIR, platform = process.platform) {
|
|
18868
|
-
return platform === "win32" ? enginesPrefix(baseDir) :
|
|
18900
|
+
return platform === "win32" ? enginesPrefix(baseDir) : join10(enginesPrefix(baseDir), "bin");
|
|
18869
18901
|
}
|
|
18870
18902
|
function npmPackageOf(install) {
|
|
18871
18903
|
const line = install?.command?.linux ?? install?.command?.darwin ?? install?.command?.win32;
|
|
@@ -18915,9 +18947,9 @@ async function installOnce(pkg, options) {
|
|
|
18915
18947
|
}
|
|
18916
18948
|
}
|
|
18917
18949
|
function tail(output) {
|
|
18918
|
-
const
|
|
18919
|
-
if (!
|
|
18920
|
-
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);
|
|
18921
18953
|
return `
|
|
18922
18954
|
${lines.join("\n").slice(-600)}`;
|
|
18923
18955
|
}
|
|
@@ -19211,9 +19243,9 @@ var init_registry = __esm({
|
|
|
19211
19243
|
});
|
|
19212
19244
|
|
|
19213
19245
|
// server/drivers/local-inject.ts
|
|
19214
|
-
import { readFileSync as
|
|
19246
|
+
import { readFileSync as readFileSync6 } from "node:fs";
|
|
19215
19247
|
import { homedir as homedir6 } from "node:os";
|
|
19216
|
-
import { join as
|
|
19248
|
+
import { join as join11 } from "node:path";
|
|
19217
19249
|
function contextWindowsFromPs(extra) {
|
|
19218
19250
|
const out = /* @__PURE__ */ new Map();
|
|
19219
19251
|
const rec = extra && typeof extra === "object" ? extra : null;
|
|
@@ -19298,7 +19330,7 @@ function firstUnslothToken(row) {
|
|
|
19298
19330
|
function readUnslothKey(env) {
|
|
19299
19331
|
const home = env.HOME || env.USERPROFILE || homedir6();
|
|
19300
19332
|
try {
|
|
19301
|
-
const raw = JSON.parse(
|
|
19333
|
+
const raw = JSON.parse(readFileSync6(join11(home, ".unsloth", "studio", "auth", "agent_api_key.json"), "utf8"));
|
|
19302
19334
|
if (raw.servers && typeof raw.servers === "object") {
|
|
19303
19335
|
const servers = raw.servers;
|
|
19304
19336
|
for (const url2 of ["http://127.0.0.1:8888", "http://localhost:8888"]) {
|
|
@@ -19348,7 +19380,7 @@ async function timedJson(url2, env, host, fetchImpl) {
|
|
|
19348
19380
|
function loadedIdsFromPayloads(_host, catalog, extra) {
|
|
19349
19381
|
const loaded = /* @__PURE__ */ new Set();
|
|
19350
19382
|
const catalogIds = new Set(idsFromModelsPayload(catalog));
|
|
19351
|
-
const
|
|
19383
|
+
const add = (id) => {
|
|
19352
19384
|
const base2 = id.split(":")[0];
|
|
19353
19385
|
if (catalogIds.size && !catalogIds.has(id) && !catalogIds.has(base2)) return;
|
|
19354
19386
|
if (!MODEL_ID.test(id)) return;
|
|
@@ -19361,10 +19393,10 @@ function loadedIdsFromPayloads(_host, catalog, extra) {
|
|
|
19361
19393
|
const hasLoadedFlags = running2.some(
|
|
19362
19394
|
(row) => row && typeof row === "object" && ("loaded" in row || "state" in row)
|
|
19363
19395
|
);
|
|
19364
|
-
if (!hasLoadedFlags && typeof rec.default_model === "string")
|
|
19396
|
+
if (!hasLoadedFlags && typeof rec.default_model === "string") add(rec.default_model);
|
|
19365
19397
|
for (const row of running2) {
|
|
19366
19398
|
if (typeof row === "string") {
|
|
19367
|
-
if (!hasLoadedFlags)
|
|
19399
|
+
if (!hasLoadedFlags) add(row);
|
|
19368
19400
|
continue;
|
|
19369
19401
|
}
|
|
19370
19402
|
if (!row || typeof row !== "object") continue;
|
|
@@ -19374,20 +19406,20 @@ function loadedIdsFromPayloads(_host, catalog, extra) {
|
|
|
19374
19406
|
const state = typeof item.state === "string" ? item.state.toLowerCase() : "";
|
|
19375
19407
|
if (item.loaded === false || state === "not-loaded" || state === "unloaded") continue;
|
|
19376
19408
|
if (item.loaded === true || state === "loaded" || state === "idle" || !hasLoadedFlags) {
|
|
19377
|
-
|
|
19409
|
+
add(id);
|
|
19378
19410
|
}
|
|
19379
19411
|
}
|
|
19380
19412
|
}
|
|
19381
19413
|
if (!loaded.size && catalog && typeof catalog === "object") {
|
|
19382
19414
|
const rec = catalog;
|
|
19383
|
-
if (typeof rec.default_model === "string")
|
|
19415
|
+
if (typeof rec.default_model === "string") add(rec.default_model);
|
|
19384
19416
|
const records = Array.isArray(rec.data) ? rec.data : [];
|
|
19385
19417
|
for (const row of records) {
|
|
19386
19418
|
if (!row || typeof row !== "object") continue;
|
|
19387
19419
|
const item = row;
|
|
19388
19420
|
if (typeof item.id !== "string") continue;
|
|
19389
19421
|
const state = typeof item.state === "string" ? item.state.toLowerCase() : "";
|
|
19390
|
-
if (item.loaded === true || state === "loaded")
|
|
19422
|
+
if (item.loaded === true || state === "loaded") add(item.id);
|
|
19391
19423
|
}
|
|
19392
19424
|
}
|
|
19393
19425
|
return loaded;
|
|
@@ -19500,11 +19532,11 @@ var init_local_inject = __esm({
|
|
|
19500
19532
|
|
|
19501
19533
|
// server/proxy-paths.ts
|
|
19502
19534
|
import { existsSync as existsSync9 } from "node:fs";
|
|
19503
|
-
import { dirname as dirname5, join as
|
|
19535
|
+
import { dirname as dirname5, join as join12 } from "node:path";
|
|
19504
19536
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
19505
19537
|
function resolveProxy(relative2) {
|
|
19506
|
-
const source =
|
|
19507
|
-
return existsSync9(source) ? source :
|
|
19538
|
+
const source = join12(SERVER_ROOT, `${relative2}.ts`);
|
|
19539
|
+
return existsSync9(source) ? source : join12(SERVER_ROOT, `${relative2}.js`);
|
|
19508
19540
|
}
|
|
19509
19541
|
var SERVER_ROOT, SPAWNED_PROXIES;
|
|
19510
19542
|
var init_proxy_paths = __esm({
|
|
@@ -19533,7 +19565,7 @@ var init_proxy_paths = __esm({
|
|
|
19533
19565
|
|
|
19534
19566
|
// server/container-computer.ts
|
|
19535
19567
|
import { execFile as execFile4 } from "node:child_process";
|
|
19536
|
-
import { join as
|
|
19568
|
+
import { join as join13, resolve as resolve3 } from "node:path";
|
|
19537
19569
|
import { promisify } from "node:util";
|
|
19538
19570
|
function computerProxyEnv(computer) {
|
|
19539
19571
|
return {
|
|
@@ -19557,7 +19589,7 @@ var init_container_computer = __esm({
|
|
|
19557
19589
|
IMAGE_REPOSITORY = "localhost/openmausbot/cua-local-vm";
|
|
19558
19590
|
IMAGE_LAYER_VERSION = "5";
|
|
19559
19591
|
IMAGE = `${IMAGE_REPOSITORY}:driver-${CUA_DRIVER_VERSION}-v${IMAGE_LAYER_VERSION}`;
|
|
19560
|
-
VM_WORKSPACE_DIR =
|
|
19592
|
+
VM_WORKSPACE_DIR = join13(DATA_DIR, "vm-home");
|
|
19561
19593
|
MEMORY_BYTES = 4 * 1024 * 1024 * 1024;
|
|
19562
19594
|
SHM_BYTES = 512 * 1024 * 1024;
|
|
19563
19595
|
containerMcpPath = SPAWNED_PROXIES.containerMcp;
|
|
@@ -19661,11 +19693,11 @@ var init_redact = __esm({
|
|
|
19661
19693
|
|
|
19662
19694
|
// server/drivers/native.ts
|
|
19663
19695
|
import { appendFileSync as appendFileSync3 } from "node:fs";
|
|
19664
|
-
import { join as
|
|
19696
|
+
import { join as join14 } from "node:path";
|
|
19665
19697
|
function appendNative(threadId, entry) {
|
|
19666
19698
|
try {
|
|
19667
19699
|
appendFileSync3(
|
|
19668
|
-
|
|
19700
|
+
join14(NATIVE_DIR, `${threadId}.ndjson`),
|
|
19669
19701
|
JSON.stringify({ at: (/* @__PURE__ */ new Date()).toISOString(), ...entry, msg: redactSecrets(entry.msg) }) + "\n",
|
|
19670
19702
|
{ mode: 384 }
|
|
19671
19703
|
);
|
|
@@ -19681,6 +19713,41 @@ var init_native = __esm({
|
|
|
19681
19713
|
});
|
|
19682
19714
|
|
|
19683
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
|
+
}
|
|
19684
19751
|
function fieldsOf(input) {
|
|
19685
19752
|
if (input === null || typeof input !== "object" || Array.isArray(input)) return void 0;
|
|
19686
19753
|
return input;
|
|
@@ -19810,12 +19877,12 @@ function createAcpDriver(support11) {
|
|
|
19810
19877
|
support11.transformEnv?.(env, activeConfig, instanceId);
|
|
19811
19878
|
return env;
|
|
19812
19879
|
};
|
|
19813
|
-
let
|
|
19880
|
+
let models2 = support11.models;
|
|
19814
19881
|
const refreshModels = async () => {
|
|
19815
19882
|
if (!support11.resolveModels) return;
|
|
19816
19883
|
try {
|
|
19817
19884
|
const resolved = await support11.resolveModels(childEnv(), config2, instanceId);
|
|
19818
|
-
if (resolved.options.length)
|
|
19885
|
+
if (resolved.options.length) models2 = resolved;
|
|
19819
19886
|
} catch {
|
|
19820
19887
|
}
|
|
19821
19888
|
};
|
|
@@ -20186,7 +20253,8 @@ function createAcpDriver(support11) {
|
|
|
20186
20253
|
itemType: "tool",
|
|
20187
20254
|
itemId: u4.toolCallId,
|
|
20188
20255
|
title: String(u4.rawInput?.command ?? u4.title ?? "tool").slice(0, 80),
|
|
20189
|
-
summary: commandSummary(u4.rawInput)
|
|
20256
|
+
summary: commandSummary(u4.rawInput),
|
|
20257
|
+
input: toolDetailPreview(u4.rawInput)
|
|
20190
20258
|
});
|
|
20191
20259
|
break;
|
|
20192
20260
|
}
|
|
@@ -20197,7 +20265,8 @@ function createAcpDriver(support11) {
|
|
|
20197
20265
|
type: "item.completed",
|
|
20198
20266
|
itemType: "tool",
|
|
20199
20267
|
itemId: u4.toolCallId,
|
|
20200
|
-
ok: u4.status !== "failed"
|
|
20268
|
+
ok: u4.status !== "failed",
|
|
20269
|
+
output: toolDetailPreview(u4.rawOutput ?? u4.content)
|
|
20201
20270
|
});
|
|
20202
20271
|
}
|
|
20203
20272
|
break;
|
|
@@ -20438,7 +20507,7 @@ ${turn.text}` : turn.text;
|
|
|
20438
20507
|
displayName: input.displayName,
|
|
20439
20508
|
enabled: input.enabled,
|
|
20440
20509
|
get models() {
|
|
20441
|
-
return
|
|
20510
|
+
return models2;
|
|
20442
20511
|
},
|
|
20443
20512
|
refreshModels: support11.resolveModels ? refreshModels : void 0,
|
|
20444
20513
|
snapshot,
|
|
@@ -20515,7 +20584,7 @@ import { createHash as createHash3, randomUUID as randomUUID6 } from "node:crypt
|
|
|
20515
20584
|
import { mkdir as mkdir2, mkdtemp, readFile as readFile2, rm, stat as stat2 } from "node:fs/promises";
|
|
20516
20585
|
import { request as httpRequest } from "node:http";
|
|
20517
20586
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
20518
|
-
import { delimiter as delimiter4, join as
|
|
20587
|
+
import { delimiter as delimiter4, join as join15, resolve as resolve5 } from "node:path";
|
|
20519
20588
|
function quoteBrowserArgument(value) {
|
|
20520
20589
|
return `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
20521
20590
|
}
|
|
@@ -20550,11 +20619,11 @@ function authorizationUrlFromLine(line) {
|
|
|
20550
20619
|
}
|
|
20551
20620
|
}
|
|
20552
20621
|
function antigravityProfileDirectory(instanceId, baseDir = DATA_DIR) {
|
|
20553
|
-
return
|
|
20622
|
+
return join15(baseDir, "providers", "antigravity", createHash3("sha256").update(instanceId).digest("hex"));
|
|
20554
20623
|
}
|
|
20555
20624
|
async function prepareAntigravityProfile(input) {
|
|
20556
20625
|
const directory = resolve5(input.profileDirectory ?? antigravityProfileDirectory(input.instanceId, input.baseDir));
|
|
20557
|
-
const acpDirectory =
|
|
20626
|
+
const acpDirectory = join15(directory, "antigravity-acp");
|
|
20558
20627
|
await mkdir2(acpDirectory, { recursive: true, mode: 448 });
|
|
20559
20628
|
if (process.platform !== "win32") {
|
|
20560
20629
|
const { chmod: chmod2 } = await import("node:fs/promises");
|
|
@@ -20562,7 +20631,7 @@ async function prepareAntigravityProfile(input) {
|
|
|
20562
20631
|
await chmod2(acpDirectory, 448);
|
|
20563
20632
|
}
|
|
20564
20633
|
writeFileAtomic(
|
|
20565
|
-
|
|
20634
|
+
join15(acpDirectory, "settings.json"),
|
|
20566
20635
|
`${JSON.stringify({ auth: { type: "oauth-personal" } })}
|
|
20567
20636
|
`,
|
|
20568
20637
|
{ mode: 384 }
|
|
@@ -20585,7 +20654,7 @@ async function prepareAntigravityProfile(input) {
|
|
|
20585
20654
|
PYTHONUNBUFFERED: "1",
|
|
20586
20655
|
ELECTRON_RUN_AS_NODE: "1"
|
|
20587
20656
|
});
|
|
20588
|
-
return { directory, tokenPath:
|
|
20657
|
+
return { directory, tokenPath: join15(acpDirectory, "acp_token.json"), environment };
|
|
20589
20658
|
}
|
|
20590
20659
|
async function antigravityProfileAuthenticated(profile) {
|
|
20591
20660
|
try {
|
|
@@ -20714,7 +20783,7 @@ function isValidAntigravityInitializeResult(initialized, expectedVersion) {
|
|
|
20714
20783
|
return true;
|
|
20715
20784
|
}
|
|
20716
20785
|
async function validateAntigravityRuntime(runtime, expectedVersion) {
|
|
20717
|
-
const profileDirectory = await mkdtemp(
|
|
20786
|
+
const profileDirectory = await mkdtemp(join15(tmpdir2(), "openmaus-antigravity-verify-"));
|
|
20718
20787
|
let client;
|
|
20719
20788
|
let failed2 = false;
|
|
20720
20789
|
let failure;
|
|
@@ -21104,7 +21173,7 @@ import {
|
|
|
21104
21173
|
writeFile as writeFile2
|
|
21105
21174
|
} from "node:fs/promises";
|
|
21106
21175
|
import { constants } from "node:fs";
|
|
21107
|
-
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";
|
|
21108
21177
|
import { Transform } from "node:stream";
|
|
21109
21178
|
import { pipeline } from "node:stream/promises";
|
|
21110
21179
|
import { createInflateRaw } from "node:zlib";
|
|
@@ -21125,7 +21194,7 @@ async function retryOnWindowsFileLock(step, options = {}) {
|
|
|
21125
21194
|
}
|
|
21126
21195
|
}
|
|
21127
21196
|
function runtimeRoot(baseDir = DATA_DIR) {
|
|
21128
|
-
return
|
|
21197
|
+
return join16(baseDir, "tools", "antigravity-acp", `${process.platform}-${process.arch}`);
|
|
21129
21198
|
}
|
|
21130
21199
|
function executableNames(platform = process.platform) {
|
|
21131
21200
|
return platform === "win32" ? { executable: "agy_acp_server.exe", harness: "localharness_external.exe" } : { executable: "agy_acp_server.par", harness: "localharness_external" };
|
|
@@ -21142,10 +21211,10 @@ async function executableFile(path3, bytes) {
|
|
|
21142
21211
|
}
|
|
21143
21212
|
async function completedRelease(directory, asset) {
|
|
21144
21213
|
try {
|
|
21145
|
-
const record3 = JSON.parse(await readFile3(
|
|
21214
|
+
const record3 = JSON.parse(await readFile3(join16(directory, INSTALL_RECORD), "utf8"));
|
|
21146
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;
|
|
21147
|
-
const executablePath =
|
|
21148
|
-
const harnessPath =
|
|
21216
|
+
const executablePath = join16(directory, asset.executable.name);
|
|
21217
|
+
const harnessPath = join16(directory, asset.harness.name);
|
|
21149
21218
|
if (!await executableFile(executablePath, asset.executable.bytes)) return null;
|
|
21150
21219
|
if (!await executableFile(harnessPath, asset.harness.bytes)) return null;
|
|
21151
21220
|
return { executablePath, harnessPath, source: "managed", version: asset.version };
|
|
@@ -21161,7 +21230,7 @@ function pathCandidates(binary2, env) {
|
|
|
21161
21230
|
async function externalRuntime(candidate, source) {
|
|
21162
21231
|
const names = executableNames();
|
|
21163
21232
|
const executablePath = resolve6(candidate);
|
|
21164
|
-
const harnessPath =
|
|
21233
|
+
const harnessPath = join16(dirname7(executablePath), names.harness);
|
|
21165
21234
|
if (!await executableFile(executablePath) || !await executableFile(harnessPath)) return null;
|
|
21166
21235
|
return { executablePath, harnessPath, source, version: null };
|
|
21167
21236
|
}
|
|
@@ -21181,7 +21250,7 @@ async function resolveAntigravityRuntime(binaryPath, env = process.env, baseDir
|
|
|
21181
21250
|
}
|
|
21182
21251
|
const asset = resolveAntigravityReleaseAsset();
|
|
21183
21252
|
if (asset) {
|
|
21184
|
-
const managed = await completedRelease(
|
|
21253
|
+
const managed = await completedRelease(join16(runtimeRoot(baseDir), "versions", asset.sha256), asset);
|
|
21185
21254
|
if (managed) return managed;
|
|
21186
21255
|
}
|
|
21187
21256
|
const names = executableNames();
|
|
@@ -21303,7 +21372,7 @@ async function extractPinnedArchive(archivePath, outputDirectory, asset) {
|
|
|
21303
21372
|
start: dataOffset,
|
|
21304
21373
|
end: dataOffset + entry.compressedBytes - 1
|
|
21305
21374
|
});
|
|
21306
|
-
const destination = createWriteStream(
|
|
21375
|
+
const destination = createWriteStream(join16(outputDirectory, entry.name), { flags: "wx", mode: 448 });
|
|
21307
21376
|
if (entry.compression === 8) await pipeline(source, createInflateRaw(), counter, destination);
|
|
21308
21377
|
else await pipeline(source, counter, destination);
|
|
21309
21378
|
if (bytes !== expectedBytes) throw new Error("An extracted file was truncated.");
|
|
@@ -21314,8 +21383,8 @@ async function installOnce2(options) {
|
|
|
21314
21383
|
const asset = options.asset === void 0 ? resolveAntigravityReleaseAsset() : options.asset;
|
|
21315
21384
|
if (!asset) throw new Error(`Google does not publish an Antigravity runtime for ${process.platform}-${process.arch}.`);
|
|
21316
21385
|
const root = runtimeRoot(options.baseDir);
|
|
21317
|
-
const versions =
|
|
21318
|
-
const destination =
|
|
21386
|
+
const versions = join16(root, "versions");
|
|
21387
|
+
const destination = join16(versions, asset.sha256);
|
|
21319
21388
|
await mkdir3(versions, { recursive: true, mode: 448 });
|
|
21320
21389
|
const existing = await completedRelease(destination, asset);
|
|
21321
21390
|
if (existing) {
|
|
@@ -21331,11 +21400,11 @@ async function installOnce2(options) {
|
|
|
21331
21400
|
} catch (error51) {
|
|
21332
21401
|
if (error51 instanceof Error && error51.message.includes("Antigravity needs")) throw error51;
|
|
21333
21402
|
}
|
|
21334
|
-
const staging = await mkdtemp2(
|
|
21403
|
+
const staging = await mkdtemp2(join16(versions, `${STAGING_PREFIX}${randomUUID7()}-`));
|
|
21335
21404
|
let failed2 = false;
|
|
21336
21405
|
try {
|
|
21337
|
-
const archivePath =
|
|
21338
|
-
const runtimeDirectory =
|
|
21406
|
+
const archivePath = join16(staging, "download.zip");
|
|
21407
|
+
const runtimeDirectory = join16(staging, "runtime");
|
|
21339
21408
|
await mkdir3(runtimeDirectory, { mode: 448 });
|
|
21340
21409
|
const controller = new AbortController();
|
|
21341
21410
|
const timeout = setTimeout(() => controller.abort(), DOWNLOAD_TIMEOUT_MS2);
|
|
@@ -21381,12 +21450,12 @@ async function installOnce2(options) {
|
|
|
21381
21450
|
}
|
|
21382
21451
|
await extractPinnedArchive(archivePath, runtimeDirectory, asset);
|
|
21383
21452
|
if (process.platform !== "win32") {
|
|
21384
|
-
await chmod(
|
|
21385
|
-
await chmod(
|
|
21453
|
+
await chmod(join16(runtimeDirectory, asset.executable.name), 493);
|
|
21454
|
+
await chmod(join16(runtimeDirectory, asset.harness.name), 493);
|
|
21386
21455
|
}
|
|
21387
21456
|
const candidate = {
|
|
21388
|
-
executablePath:
|
|
21389
|
-
harnessPath:
|
|
21457
|
+
executablePath: join16(runtimeDirectory, asset.executable.name),
|
|
21458
|
+
harnessPath: join16(runtimeDirectory, asset.harness.name),
|
|
21390
21459
|
source: "managed",
|
|
21391
21460
|
version: asset.version
|
|
21392
21461
|
};
|
|
@@ -21397,7 +21466,7 @@ async function installOnce2(options) {
|
|
|
21397
21466
|
executable: asset.executable,
|
|
21398
21467
|
harness: asset.harness
|
|
21399
21468
|
};
|
|
21400
|
-
await writeFile2(
|
|
21469
|
+
await writeFile2(join16(runtimeDirectory, INSTALL_RECORD), `${JSON.stringify(record3)}
|
|
21401
21470
|
`, { flag: "wx", mode: 384 });
|
|
21402
21471
|
try {
|
|
21403
21472
|
await retryOnWindowsFileLock(() => rename(runtimeDirectory, destination));
|
|
@@ -21678,7 +21747,7 @@ var init_boxagent = __esm({
|
|
|
21678
21747
|
const model = turn.model || MODELS.default;
|
|
21679
21748
|
const prompt = [
|
|
21680
21749
|
turn.system,
|
|
21681
|
-
"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.",
|
|
21682
21751
|
"",
|
|
21683
21752
|
turn.text
|
|
21684
21753
|
].filter((s) => s !== void 0).join("\n");
|
|
@@ -21859,14 +21928,14 @@ var init_resume_recovery = __esm({
|
|
|
21859
21928
|
import { randomUUID as randomUUID8 } from "node:crypto";
|
|
21860
21929
|
import { realpathSync } from "node:fs";
|
|
21861
21930
|
import { homedir as homedir8 } from "node:os";
|
|
21862
|
-
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";
|
|
21863
21932
|
import { stripVTControlCharacters as stripVTControlCharacters4 } from "node:util";
|
|
21864
21933
|
function canonicalPath(path3) {
|
|
21865
21934
|
try {
|
|
21866
21935
|
return realpathSync(path3);
|
|
21867
21936
|
} catch {
|
|
21868
21937
|
const parent = dirname8(path3);
|
|
21869
|
-
return parent === path3 ? path3 :
|
|
21938
|
+
return parent === path3 ? path3 : join17(canonicalPath(parent), basename4(path3));
|
|
21870
21939
|
}
|
|
21871
21940
|
}
|
|
21872
21941
|
function claudeSignInLink(value) {
|
|
@@ -21923,7 +21992,7 @@ var init_claude_login_auth = __esm({
|
|
|
21923
21992
|
if (!isAbsolute3(home) || env.CLAUDE_CONFIG_DIR && !isAbsolute3(env.CLAUDE_CONFIG_DIR)) {
|
|
21924
21993
|
throw new Error("Use an absolute HOME and CLAUDE_CONFIG_DIR path for this server's Claude provider before signing in.");
|
|
21925
21994
|
}
|
|
21926
|
-
const homeKey = canonicalPath(resolve7(env.CLAUDE_CONFIG_DIR ||
|
|
21995
|
+
const homeKey = canonicalPath(resolve7(env.CLAUDE_CONFIG_DIR || join17(home, ".claude")));
|
|
21927
21996
|
if (authenticatingHomes.has(homeKey)) {
|
|
21928
21997
|
throw new Error("A Claude sign-in is already running for this server account. Finish or cancel it before starting another.");
|
|
21929
21998
|
}
|
|
@@ -22019,7 +22088,7 @@ var init_claude_login_auth = __esm({
|
|
|
22019
22088
|
if (!isAbsolute3(home) || env.CLAUDE_CONFIG_DIR && !isAbsolute3(env.CLAUDE_CONFIG_DIR)) {
|
|
22020
22089
|
throw new Error("Use an absolute HOME and CLAUDE_CONFIG_DIR path for this server's Claude provider before signing out.");
|
|
22021
22090
|
}
|
|
22022
|
-
const homeKey = canonicalPath(resolve7(env.CLAUDE_CONFIG_DIR ||
|
|
22091
|
+
const homeKey = canonicalPath(resolve7(env.CLAUDE_CONFIG_DIR || join17(home, ".claude")));
|
|
22023
22092
|
if (authenticatingHomes.has(homeKey)) {
|
|
22024
22093
|
throw new Error("A Claude sign-in is running for this server account. Finish or cancel it before signing out.");
|
|
22025
22094
|
}
|
|
@@ -22235,7 +22304,7 @@ var init_mcp_trim = __esm({
|
|
|
22235
22304
|
});
|
|
22236
22305
|
|
|
22237
22306
|
// server/mcp-gate-config.ts
|
|
22238
|
-
import { join as
|
|
22307
|
+
import { join as join18 } from "node:path";
|
|
22239
22308
|
function resultBudget(env = process.env) {
|
|
22240
22309
|
const raw = env.OMB_MCP_RESULT_BUDGET;
|
|
22241
22310
|
if (raw === void 0 || raw === "") return DEFAULT_RESULT_BUDGET;
|
|
@@ -22243,7 +22312,7 @@ function resultBudget(env = process.env) {
|
|
|
22243
22312
|
return Number.isFinite(parsed) && parsed >= 0 ? Math.floor(parsed) : DEFAULT_RESULT_BUDGET;
|
|
22244
22313
|
}
|
|
22245
22314
|
function spillDir(threadId) {
|
|
22246
|
-
return
|
|
22315
|
+
return join18(DATA_DIR, "tool-results", threadId.replace(/[^A-Za-z0-9_-]/g, "-").slice(0, 80) || "thread");
|
|
22247
22316
|
}
|
|
22248
22317
|
function gateServer(input) {
|
|
22249
22318
|
const { name, server, budget } = input;
|
|
@@ -22445,10 +22514,10 @@ var init_ask_question = __esm({
|
|
|
22445
22514
|
|
|
22446
22515
|
// server/drivers/claude.ts
|
|
22447
22516
|
import { createHash as createHash5, randomBytes as randomBytes2 } from "node:crypto";
|
|
22448
|
-
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";
|
|
22449
22518
|
import { createServer as createNetServer } from "node:net";
|
|
22450
22519
|
import { homedir as homedir9, tmpdir as tmpdir3 } from "node:os";
|
|
22451
|
-
import { join as
|
|
22520
|
+
import { join as join19, dirname as dirname9, isAbsolute as isAbsolute4, normalize } from "node:path";
|
|
22452
22521
|
function claudeAuthStatus(cli, env, run3 = execCli) {
|
|
22453
22522
|
return new Promise((resolve13) => {
|
|
22454
22523
|
run3(cli, ["auth", "status", "--json"], { timeout: 8e3, maxBuffer: 65536, env }, (_error, stdout2) => {
|
|
@@ -22476,8 +22545,8 @@ function claudeAuthStatus(cli, env, run3 = execCli) {
|
|
|
22476
22545
|
}
|
|
22477
22546
|
function resolveClaudeConfigDir(configDir, env = process.env) {
|
|
22478
22547
|
const home = env.HOME || env.USERPROFILE || homedir9();
|
|
22479
|
-
const configured = configDir?.trim() || env.CLAUDE_CONFIG_DIR?.trim() ||
|
|
22480
|
-
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;
|
|
22481
22550
|
if (!isAbsolute4(expanded) || /[\p{Cc}\p{Cf}]/u.test(expanded)) {
|
|
22482
22551
|
throw new Error("claude: configDir must be an absolute path or start with ~/");
|
|
22483
22552
|
}
|
|
@@ -22512,7 +22581,7 @@ function inheritsUserConfig(env) {
|
|
|
22512
22581
|
function readClaudeAuthSettings(env, instanceEnvironment = {}) {
|
|
22513
22582
|
if (CLAUDE_ACCOUNT_ENV_KEYS.some((key) => instanceEnvironment[key])) return {};
|
|
22514
22583
|
try {
|
|
22515
|
-
const settings2 = JSON.parse(
|
|
22584
|
+
const settings2 = JSON.parse(readFileSync7(join19(resolveClaudeConfigDir(void 0, env), "settings.json"), "utf8"));
|
|
22516
22585
|
const authEnv = {};
|
|
22517
22586
|
for (const key of CLAUDE_ACCOUNT_ENV_KEYS) {
|
|
22518
22587
|
if (!key.endsWith("_FILE_DESCRIPTOR") && typeof settings2?.env?.[key] === "string") {
|
|
@@ -22530,7 +22599,7 @@ function readClaudeAuthSettings(env, instanceEnvironment = {}) {
|
|
|
22530
22599
|
function projectMcpServers(cwd) {
|
|
22531
22600
|
let parsed;
|
|
22532
22601
|
try {
|
|
22533
|
-
parsed = JSON.parse(
|
|
22602
|
+
parsed = JSON.parse(readFileSync7(join19(cwd, ".mcp.json"), "utf8"));
|
|
22534
22603
|
} catch {
|
|
22535
22604
|
return {};
|
|
22536
22605
|
}
|
|
@@ -22598,7 +22667,7 @@ function extrasFromUnknown(value) {
|
|
|
22598
22667
|
function readClaudeModelCatalog(env = process.env) {
|
|
22599
22668
|
let settings2 = {};
|
|
22600
22669
|
try {
|
|
22601
|
-
settings2 = JSON.parse(
|
|
22670
|
+
settings2 = JSON.parse(readFileSync7(join19(resolveClaudeConfigDir(void 0, env), "settings.json"), "utf8"));
|
|
22602
22671
|
} catch {
|
|
22603
22672
|
return STATIC_CLAUDE_MODELS;
|
|
22604
22673
|
}
|
|
@@ -22639,16 +22708,17 @@ function askSummary(ask) {
|
|
|
22639
22708
|
if (questions) return askQuestionSummary(questions).slice(0, 300);
|
|
22640
22709
|
return askInputSummary(ask.input) ?? ask.tool ?? "tool";
|
|
22641
22710
|
}
|
|
22642
|
-
function permissionSocketPath(threadId) {
|
|
22711
|
+
function permissionSocketPath(threadId, botId) {
|
|
22712
|
+
const key = botId ? `${botId}\0${threadId}` : threadId;
|
|
22643
22713
|
const prefix = threadId.replace(/[^\w-]/g, "").slice(0, 4);
|
|
22644
|
-
const digest2 = createHash5("sha256").update(
|
|
22714
|
+
const digest2 = createHash5("sha256").update(key).digest("hex").slice(0, 4);
|
|
22645
22715
|
return brokerSocketPath(DATA_DIR, `${prefix}${digest2}`);
|
|
22646
22716
|
}
|
|
22647
|
-
function brokerSocketCandidates(threadId) {
|
|
22648
|
-
const base2 = permissionSocketPath(threadId);
|
|
22717
|
+
function brokerSocketCandidates(threadId, botId) {
|
|
22718
|
+
const base2 = permissionSocketPath(threadId, botId);
|
|
22649
22719
|
if (process.platform !== "win32") {
|
|
22650
|
-
const scope = createHash5("sha256").update(`${DATA_DIR}\0${process.pid}\0${threadId}`).digest("hex").slice(0, 16);
|
|
22651
|
-
return [base2,
|
|
22720
|
+
const scope = createHash5("sha256").update(`${DATA_DIR}\0${process.pid}\0${botId ?? ""}\0${threadId}`).digest("hex").slice(0, 16);
|
|
22721
|
+
return [base2, join19(tmpdir3(), `omb-perm-${scope}.sock`)];
|
|
22652
22722
|
}
|
|
22653
22723
|
return [
|
|
22654
22724
|
base2,
|
|
@@ -22866,7 +22936,7 @@ function claudeUserMessage(text4, images) {
|
|
|
22866
22936
|
source: {
|
|
22867
22937
|
type: "base64",
|
|
22868
22938
|
media_type: image.mime,
|
|
22869
|
-
data:
|
|
22939
|
+
data: readFileSync7(image.path).toString("base64")
|
|
22870
22940
|
}
|
|
22871
22941
|
}));
|
|
22872
22942
|
if (text4) content.push({ type: "text", text: text4 });
|
|
@@ -22970,11 +23040,11 @@ var init_claude = __esm({
|
|
|
22970
23040
|
const { instanceId, config: config2 } = input;
|
|
22971
23041
|
const environment = (model) => claudeEnvironment(model, { ...process.env, ...input.environment }, config2.configDir, input.environment);
|
|
22972
23042
|
const catalogEnv = environment();
|
|
22973
|
-
let
|
|
23043
|
+
let models2 = STATIC_CLAUDE_MODELS;
|
|
22974
23044
|
const refreshModels = async () => {
|
|
22975
23045
|
try {
|
|
22976
23046
|
const resolved = await mergeLocalInject(readClaudeModelCatalog(catalogEnv), catalogEnv);
|
|
22977
|
-
if (resolved.options.length)
|
|
23047
|
+
if (resolved.options.length) models2 = resolved;
|
|
22978
23048
|
} catch {
|
|
22979
23049
|
}
|
|
22980
23050
|
};
|
|
@@ -23045,8 +23115,8 @@ var init_claude = __esm({
|
|
|
23045
23115
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
23046
23116
|
});
|
|
23047
23117
|
const retryState = /* @__PURE__ */ new Map();
|
|
23048
|
-
const sendTurn = async (turn) => {
|
|
23049
|
-
const { threadId } = turn;
|
|
23118
|
+
const sendTurn = async (turn, logicalTurnId) => {
|
|
23119
|
+
const { threadId, botId } = turn;
|
|
23050
23120
|
if (active.has(threadId)) throw new Error("a turn is already running on this thread");
|
|
23051
23121
|
const permissionMode = turn.approvalMode === void 0 ? config2.permissionMode : turn.approvalMode === "full" ? "bypassPermissions" : turn.approvalMode === "auto" ? "auto" : turn.approvalMode === "edits" ? "acceptEdits" : "default";
|
|
23052
23122
|
const controlsHost = turn.integrations?.localComputer?.scope === "local-computer";
|
|
@@ -23054,7 +23124,7 @@ var init_claude = __esm({
|
|
|
23054
23124
|
throw new Error("local computer control requires the interactive approval broker");
|
|
23055
23125
|
}
|
|
23056
23126
|
const promptMsg = claudeUserMessage(turn.text, turn.images);
|
|
23057
|
-
const turnId = newId();
|
|
23127
|
+
const turnId = logicalTurnId ?? newId();
|
|
23058
23128
|
const retryAbort = new AbortController();
|
|
23059
23129
|
const retry = retryState.get(threadId) ?? { attempt: 0, cancelled: false };
|
|
23060
23130
|
retry.cancelled = false;
|
|
@@ -23159,7 +23229,7 @@ var init_claude = __esm({
|
|
|
23159
23229
|
if (gated) mcpServers[name] = gated;
|
|
23160
23230
|
}
|
|
23161
23231
|
let broker;
|
|
23162
|
-
const socketPath = permissionSocketPath(threadId);
|
|
23232
|
+
const socketPath = permissionSocketPath(threadId, botId);
|
|
23163
23233
|
if (permissionMode !== "bypassPermissions") {
|
|
23164
23234
|
args.push("--permission-prompt-tool", "mcp__ogb__approve");
|
|
23165
23235
|
}
|
|
@@ -23167,13 +23237,13 @@ var init_claude = __esm({
|
|
|
23167
23237
|
allowed.push("mcp__ogb");
|
|
23168
23238
|
let mcpConfigPath = null;
|
|
23169
23239
|
if (Object.keys(mcpServers).length) {
|
|
23170
|
-
mcpConfigPath =
|
|
23240
|
+
mcpConfigPath = join19(mkdtempSync2(join19(tmpdir3(), "omb-mcp-")), "mcp.json");
|
|
23171
23241
|
args.push("--mcp-config", mcpConfigPath);
|
|
23172
23242
|
args.push("--allowedTools", allowed.join(","));
|
|
23173
23243
|
}
|
|
23174
23244
|
const env = environment(turnModel);
|
|
23175
23245
|
const authSettings = isolated && !injected.injected ? readClaudeAuthSettings(env, input.environment) : {};
|
|
23176
|
-
const authSettingsPath = mcpConfigPath && Object.keys(authSettings).length ?
|
|
23246
|
+
const authSettingsPath = mcpConfigPath && Object.keys(authSettings).length ? join19(dirname9(mcpConfigPath), "auth-settings.json") : null;
|
|
23177
23247
|
if (authSettingsPath) args.push("--settings", authSettingsPath);
|
|
23178
23248
|
env.CLAUDE_CODE_DISABLE_BACKGROUND_TASKS = "1";
|
|
23179
23249
|
const cwd = turn.cwd ?? homedir9();
|
|
@@ -23198,9 +23268,11 @@ var init_claude = __esm({
|
|
|
23198
23268
|
const live = sessions.get(threadId);
|
|
23199
23269
|
if (live && !live.turn && !live.closing && live.child.exitCode === null && live.argsKey === argsKey && (!sessionId || sessionId === live.sessionId)) {
|
|
23200
23270
|
if (live.idleTimer) clearTimeout(live.idleTimer);
|
|
23201
|
-
live.turn = { turnId, settled: false, sawStreamDelta: false };
|
|
23271
|
+
live.turn = { turnId, input: turn, retryAbort, settled: false, sawStreamDelta: false };
|
|
23202
23272
|
active.set(threadId, { stop: () => {
|
|
23203
23273
|
closeSession(threadId, "interrupted");
|
|
23274
|
+
retry.cancelled = true;
|
|
23275
|
+
retryAbort.abort();
|
|
23204
23276
|
stopSession(live);
|
|
23205
23277
|
}, turnId, broker: live.broker });
|
|
23206
23278
|
emit({ ...base2(threadId, turnId), type: "turn.started" });
|
|
@@ -23248,14 +23320,14 @@ var init_claude = __esm({
|
|
|
23248
23320
|
};
|
|
23249
23321
|
try {
|
|
23250
23322
|
if (turn.system) {
|
|
23251
|
-
systemPromptPath =
|
|
23252
|
-
|
|
23323
|
+
systemPromptPath = join19(mkdtempSync2(join19(tmpdir3(), "omb-system-")), "prompt.txt");
|
|
23324
|
+
writeFileSync5(systemPromptPath, turn.system, { mode: 384 });
|
|
23253
23325
|
args.push("--append-system-prompt-file", systemPromptPath);
|
|
23254
23326
|
}
|
|
23255
23327
|
if (socketPath) {
|
|
23256
23328
|
const askTools = /* @__PURE__ */ new Map();
|
|
23257
23329
|
broker = await createPermissionBroker({
|
|
23258
|
-
socketPaths: brokerSocketCandidates(threadId),
|
|
23330
|
+
socketPaths: brokerSocketCandidates(threadId, botId),
|
|
23259
23331
|
isActive: () => Boolean(sessions.get(threadId)?.turn),
|
|
23260
23332
|
onAsk: (ask) => {
|
|
23261
23333
|
const eventTurnId = sessions.get(threadId)?.turn?.turnId ?? turnId;
|
|
@@ -23299,10 +23371,10 @@ var init_claude = __esm({
|
|
|
23299
23371
|
}
|
|
23300
23372
|
}
|
|
23301
23373
|
if (mcpConfigPath) {
|
|
23302
|
-
|
|
23374
|
+
writeFileSync5(mcpConfigPath, JSON.stringify({ mcpServers }), { mode: 384 });
|
|
23303
23375
|
}
|
|
23304
23376
|
if (authSettingsPath) {
|
|
23305
|
-
|
|
23377
|
+
writeFileSync5(authSettingsPath, JSON.stringify(authSettings), { mode: 384 });
|
|
23306
23378
|
}
|
|
23307
23379
|
if (sessionId) args.push("--resume", sessionId);
|
|
23308
23380
|
else args.push("--session-id", newSessionId);
|
|
@@ -23331,7 +23403,7 @@ var init_claude = __esm({
|
|
|
23331
23403
|
sessionId: sessionId ?? newSessionId,
|
|
23332
23404
|
sawInit: false,
|
|
23333
23405
|
nativePermissionMode: null,
|
|
23334
|
-
turn: { turnId, settled: false, sawStreamDelta: false },
|
|
23406
|
+
turn: { turnId, input: turn, retryAbort, settled: false, sawStreamDelta: false },
|
|
23335
23407
|
idleTimer: null,
|
|
23336
23408
|
closing: false,
|
|
23337
23409
|
stderr: ""
|
|
@@ -23415,7 +23487,8 @@ var init_claude = __esm({
|
|
|
23415
23487
|
itemType: "tool",
|
|
23416
23488
|
itemId: b.id,
|
|
23417
23489
|
title: b.name,
|
|
23418
|
-
summary: commandSummary(b.input)
|
|
23490
|
+
summary: commandSummary(b.input),
|
|
23491
|
+
input: toolDetailPreview(b.input)
|
|
23419
23492
|
});
|
|
23420
23493
|
}
|
|
23421
23494
|
}
|
|
@@ -23433,7 +23506,7 @@ var init_claude = __esm({
|
|
|
23433
23506
|
case "user":
|
|
23434
23507
|
for (const b of Array.isArray(o.message?.content) ? o.message.content : []) {
|
|
23435
23508
|
if (b.type === "tool_result") {
|
|
23436
|
-
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) });
|
|
23437
23510
|
}
|
|
23438
23511
|
}
|
|
23439
23512
|
break;
|
|
@@ -23483,9 +23556,11 @@ var init_claude = __esm({
|
|
|
23483
23556
|
if (closeFinalized) return;
|
|
23484
23557
|
closeFinalized = true;
|
|
23485
23558
|
if (session.turn && !session.turn.settled) {
|
|
23559
|
+
const { turnId: turnId2, input: turn2, retryAbort: retryAbort2 } = session.turn;
|
|
23560
|
+
const retry2 = retryState.get(threadId) ?? { attempt: 0, cancelled: false };
|
|
23486
23561
|
const message2 = `claude exited ${code} before result${session.stderr ? `: ${session.stderr.trim().slice(-300)}` : ""}`;
|
|
23487
23562
|
const verdict = classifyError({ exitCode: code, stderr: message2 });
|
|
23488
|
-
if (!
|
|
23563
|
+
if (!retry2.cancelled && code !== 0 && verdict.transient && !session.turn.sawStreamDelta && retry2.attempt < RETRY_MAX_ATTEMPTS - 1) {
|
|
23489
23564
|
const failedBroker = session.broker;
|
|
23490
23565
|
session.broker = void 0;
|
|
23491
23566
|
failedBroker?.pause();
|
|
@@ -23503,23 +23578,23 @@ var init_claude = __esm({
|
|
|
23503
23578
|
}
|
|
23504
23579
|
sessions.delete(threadId);
|
|
23505
23580
|
session.turn = null;
|
|
23506
|
-
|
|
23507
|
-
const delayMs = computeBackoff(
|
|
23581
|
+
retry2.attempt++;
|
|
23582
|
+
const delayMs = computeBackoff(retry2.attempt - 1);
|
|
23508
23583
|
emit({
|
|
23509
|
-
...base2(threadId,
|
|
23584
|
+
...base2(threadId, turnId2),
|
|
23510
23585
|
type: "turn.retrying",
|
|
23511
|
-
attempt:
|
|
23586
|
+
attempt: retry2.attempt,
|
|
23512
23587
|
delayMs,
|
|
23513
23588
|
reason: verdict.reason
|
|
23514
23589
|
});
|
|
23515
23590
|
void (async () => {
|
|
23516
|
-
const wait2 = interruptibleDelay(delayMs * retryScale,
|
|
23591
|
+
const wait2 = interruptibleDelay(delayMs * retryScale, retryAbort2.signal);
|
|
23517
23592
|
await wait2.promise;
|
|
23518
|
-
if (
|
|
23593
|
+
if (retry2.cancelled) {
|
|
23519
23594
|
active.delete(threadId);
|
|
23520
23595
|
retryState.delete(threadId);
|
|
23521
23596
|
emit({
|
|
23522
|
-
...base2(threadId,
|
|
23597
|
+
...base2(threadId, turnId2),
|
|
23523
23598
|
type: "turn.completed",
|
|
23524
23599
|
ok: false,
|
|
23525
23600
|
stopReason: "interrupted",
|
|
@@ -23530,16 +23605,16 @@ var init_claude = __esm({
|
|
|
23530
23605
|
active.delete(threadId);
|
|
23531
23606
|
try {
|
|
23532
23607
|
const cursor3 = session.sessionId ?? sessionId ?? void 0;
|
|
23533
|
-
await sendTurn({ ...
|
|
23608
|
+
await sendTurn({ ...turn2, resumeCursor: cursor3 }, turnId2);
|
|
23534
23609
|
} catch (e) {
|
|
23535
23610
|
retryState.delete(threadId);
|
|
23536
23611
|
emit({
|
|
23537
|
-
...base2(threadId,
|
|
23612
|
+
...base2(threadId, turnId2),
|
|
23538
23613
|
type: "runtime.error",
|
|
23539
23614
|
message: e instanceof Error ? e.message : String(e)
|
|
23540
23615
|
});
|
|
23541
23616
|
emit({
|
|
23542
|
-
...base2(threadId,
|
|
23617
|
+
...base2(threadId, turnId2),
|
|
23543
23618
|
type: "turn.completed",
|
|
23544
23619
|
ok: false,
|
|
23545
23620
|
stopReason: "exit_before_result",
|
|
@@ -23555,10 +23630,10 @@ var init_claude = __esm({
|
|
|
23555
23630
|
promptSubmitted: session.sawInit,
|
|
23556
23631
|
producedOutput: session.turn.sawStreamDelta
|
|
23557
23632
|
});
|
|
23558
|
-
if (mayReplay(resumeFailure) && !
|
|
23633
|
+
if (mayReplay(resumeFailure) && !retry2.cancelled) {
|
|
23559
23634
|
const recovery = recoveryPromptFor({
|
|
23560
|
-
recoveryText:
|
|
23561
|
-
currentText:
|
|
23635
|
+
recoveryText: turn2.recoveryText,
|
|
23636
|
+
currentText: turn2.text,
|
|
23562
23637
|
failure: resumeFailure
|
|
23563
23638
|
});
|
|
23564
23639
|
session.broker?.close();
|
|
@@ -23578,23 +23653,23 @@ var init_claude = __esm({
|
|
|
23578
23653
|
session.turn = null;
|
|
23579
23654
|
active.delete(threadId);
|
|
23580
23655
|
emit({
|
|
23581
|
-
...base2(threadId,
|
|
23656
|
+
...base2(threadId, turnId2),
|
|
23582
23657
|
type: "turn.retrying",
|
|
23583
|
-
attempt:
|
|
23658
|
+
attempt: retry2.attempt + 1,
|
|
23584
23659
|
delayMs: 0,
|
|
23585
23660
|
reason: "resume_rejected"
|
|
23586
23661
|
});
|
|
23587
23662
|
void (async () => {
|
|
23588
23663
|
try {
|
|
23589
|
-
await sendTurn({ ...
|
|
23664
|
+
await sendTurn({ ...turn2, resumeCursor: void 0, recoveryText: void 0, text: recovery.text }, turnId2);
|
|
23590
23665
|
} catch (e) {
|
|
23591
23666
|
retryState.delete(threadId);
|
|
23592
23667
|
emit({
|
|
23593
|
-
...base2(threadId,
|
|
23668
|
+
...base2(threadId, turnId2),
|
|
23594
23669
|
type: "runtime.error",
|
|
23595
23670
|
message: e instanceof Error ? e.message : String(e)
|
|
23596
23671
|
});
|
|
23597
|
-
emit({ ...base2(threadId,
|
|
23672
|
+
emit({ ...base2(threadId, turnId2), type: "turn.completed", ok: false, stopReason: "exit_before_result", cost: null });
|
|
23598
23673
|
}
|
|
23599
23674
|
})();
|
|
23600
23675
|
return;
|
|
@@ -23718,7 +23793,7 @@ var init_claude = __esm({
|
|
|
23718
23793
|
displayName: input.displayName,
|
|
23719
23794
|
enabled: input.enabled,
|
|
23720
23795
|
get models() {
|
|
23721
|
-
return
|
|
23796
|
+
return models2;
|
|
23722
23797
|
},
|
|
23723
23798
|
refreshModels,
|
|
23724
23799
|
snapshot,
|
|
@@ -23783,9 +23858,9 @@ var init_claude = __esm({
|
|
|
23783
23858
|
});
|
|
23784
23859
|
|
|
23785
23860
|
// server/drivers/codex-catalog.ts
|
|
23786
|
-
import { readdirSync as readdirSync2, readFileSync as
|
|
23861
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync8 } from "node:fs";
|
|
23787
23862
|
import { homedir as homedir10 } from "node:os";
|
|
23788
|
-
import { basename as basename5, join as
|
|
23863
|
+
import { basename as basename5, join as join20 } from "node:path";
|
|
23789
23864
|
function encodeCodexSelection(provider, model) {
|
|
23790
23865
|
return `${provider}${SEP}${model}`;
|
|
23791
23866
|
}
|
|
@@ -23807,7 +23882,7 @@ function readCodexAppServerModelCatalog(cli, env, timeoutMs = 8e3) {
|
|
|
23807
23882
|
let settled = false;
|
|
23808
23883
|
let buffer = "";
|
|
23809
23884
|
let nextId = 1;
|
|
23810
|
-
const
|
|
23885
|
+
const models2 = [];
|
|
23811
23886
|
const cursors = /* @__PURE__ */ new Set();
|
|
23812
23887
|
const pending = /* @__PURE__ */ new Map();
|
|
23813
23888
|
const finish = (catalog) => {
|
|
@@ -23865,7 +23940,7 @@ function readCodexAppServerModelCatalog(cli, env, timeoutMs = 8e3) {
|
|
|
23865
23940
|
continue;
|
|
23866
23941
|
}
|
|
23867
23942
|
const page = message2.result;
|
|
23868
|
-
if (Array.isArray(page?.data))
|
|
23943
|
+
if (Array.isArray(page?.data)) models2.push(...page.data);
|
|
23869
23944
|
const cursor3 = typeof page?.nextCursor === "string" && page.nextCursor ? page.nextCursor : null;
|
|
23870
23945
|
if (cursor3 && !cursors.has(cursor3)) {
|
|
23871
23946
|
cursors.add(cursor3);
|
|
@@ -23875,7 +23950,7 @@ function readCodexAppServerModelCatalog(cli, env, timeoutMs = 8e3) {
|
|
|
23875
23950
|
const options = [];
|
|
23876
23951
|
const seen = /* @__PURE__ */ new Set();
|
|
23877
23952
|
let defaultModel = null;
|
|
23878
|
-
for (const row of
|
|
23953
|
+
for (const row of models2) {
|
|
23879
23954
|
if (row.hidden === true || typeof row.id !== "string" || !MODEL_ID2.test(row.id) || seen.has(row.id)) continue;
|
|
23880
23955
|
seen.add(row.id);
|
|
23881
23956
|
options.push({
|
|
@@ -23901,7 +23976,7 @@ function readCodexAppServerModelCatalog(cli, env, timeoutMs = 8e3) {
|
|
|
23901
23976
|
}
|
|
23902
23977
|
function codexHome(env) {
|
|
23903
23978
|
if (env.CODEX_HOME) return env.CODEX_HOME;
|
|
23904
|
-
return
|
|
23979
|
+
return join20(env.HOME || env.USERPROFILE || homedir10(), ".codex");
|
|
23905
23980
|
}
|
|
23906
23981
|
function unquote(raw) {
|
|
23907
23982
|
let value = raw.trim();
|
|
@@ -23964,7 +24039,7 @@ function parseCodexToml(text4) {
|
|
|
23964
24039
|
}
|
|
23965
24040
|
function readText(path3) {
|
|
23966
24041
|
try {
|
|
23967
|
-
return
|
|
24042
|
+
return readFileSync8(path3, "utf8");
|
|
23968
24043
|
} catch {
|
|
23969
24044
|
return null;
|
|
23970
24045
|
}
|
|
@@ -23987,11 +24062,11 @@ function niceLabel(model, provider, known, named) {
|
|
|
23987
24062
|
}
|
|
23988
24063
|
function collectCatalogNames(home) {
|
|
23989
24064
|
const named = /* @__PURE__ */ new Map();
|
|
23990
|
-
for (const file2 of listDir(
|
|
24065
|
+
for (const file2 of listDir(join20(home, "model-catalogs"))) {
|
|
23991
24066
|
if (!file2.endsWith(".json")) continue;
|
|
23992
24067
|
const provider = basename5(file2, ".json").replace(/-models$/, "");
|
|
23993
24068
|
if (!PROVIDER_ID.test(provider)) continue;
|
|
23994
|
-
const raw = readText(
|
|
24069
|
+
const raw = readText(join20(home, "model-catalogs", file2));
|
|
23995
24070
|
if (!raw) continue;
|
|
23996
24071
|
let parsed;
|
|
23997
24072
|
try {
|
|
@@ -24043,7 +24118,7 @@ async function probeProviderModels(provider, env, fetchImpl) {
|
|
|
24043
24118
|
async function readCodexModelCatalog(env = process.env, fetchImpl = fetch, cli) {
|
|
24044
24119
|
const official = (cli ? await readCodexAppServerModelCatalog(cli, env) : null) ?? STATIC_CODEX_MODELS;
|
|
24045
24120
|
const home = codexHome(env);
|
|
24046
|
-
const mainText = readText(
|
|
24121
|
+
const mainText = readText(join20(home, "config.toml"));
|
|
24047
24122
|
if (!mainText) return mergeLocalInject(official, env, fetchImpl);
|
|
24048
24123
|
const main2 = parseCodexToml(mainText);
|
|
24049
24124
|
const known = new Map(main2.providers.map((provider) => [provider.id, provider]));
|
|
@@ -24058,7 +24133,7 @@ async function readCodexModelCatalog(env = process.env, fetchImpl = fetch, cli)
|
|
|
24058
24133
|
remember(main2.modelProvider, main2.model);
|
|
24059
24134
|
for (const file2 of listDir(home)) {
|
|
24060
24135
|
if (!file2.endsWith(".config.toml")) continue;
|
|
24061
|
-
const profile = parseCodexToml(readText(
|
|
24136
|
+
const profile = parseCodexToml(readText(join20(home, file2)) ?? "");
|
|
24062
24137
|
for (const provider of profile.providers) {
|
|
24063
24138
|
if (!known.has(provider.id)) known.set(provider.id, provider);
|
|
24064
24139
|
}
|
|
@@ -24124,8 +24199,8 @@ var init_codex_catalog = __esm({
|
|
|
24124
24199
|
|
|
24125
24200
|
// server/drivers/codex-instructions.ts
|
|
24126
24201
|
import { createHash as createHash6 } from "node:crypto";
|
|
24127
|
-
import { mkdirSync as mkdirSync9, readFileSync as
|
|
24128
|
-
import { join as
|
|
24202
|
+
import { mkdirSync as mkdirSync9, readFileSync as readFileSync9 } from "node:fs";
|
|
24203
|
+
import { join as join21 } from "node:path";
|
|
24129
24204
|
function codexDeveloperInstructions(config2, botInstructions) {
|
|
24130
24205
|
if (!config2 || typeof config2 !== "object" || Array.isArray(config2)) {
|
|
24131
24206
|
throw new Error("Codex returned no effective configuration; cannot safely update bot instructions.");
|
|
@@ -24139,12 +24214,12 @@ function codexDeveloperInstructions(config2, botInstructions) {
|
|
|
24139
24214
|
${configured}` : botInstructions;
|
|
24140
24215
|
}
|
|
24141
24216
|
async function syncCodexInstructions(key, nativeThreadId, instructions, resumed, request) {
|
|
24142
|
-
const directory =
|
|
24143
|
-
const path3 =
|
|
24217
|
+
const directory = join21(DATA_DIR, "codex-instructions");
|
|
24218
|
+
const path3 = join21(directory, `${digest(JSON.stringify([key, nativeThreadId]))}.sha256`);
|
|
24144
24219
|
const fingerprint = digest(instructions);
|
|
24145
24220
|
let previous;
|
|
24146
24221
|
try {
|
|
24147
|
-
previous =
|
|
24222
|
+
previous = readFileSync9(path3, "utf8");
|
|
24148
24223
|
} catch (error51) {
|
|
24149
24224
|
if (error51.code !== "ENOENT") throw error51;
|
|
24150
24225
|
}
|
|
@@ -24187,19 +24262,19 @@ var init_codex_instructions = __esm({
|
|
|
24187
24262
|
import { randomUUID as randomUUID9 } from "node:crypto";
|
|
24188
24263
|
import { realpathSync as realpathSync2 } from "node:fs";
|
|
24189
24264
|
import { homedir as homedir11 } from "node:os";
|
|
24190
|
-
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";
|
|
24191
24266
|
import { stripVTControlCharacters as stripVTControlCharacters5 } from "node:util";
|
|
24192
24267
|
function canonicalPath2(path3) {
|
|
24193
24268
|
try {
|
|
24194
24269
|
return realpathSync2(path3);
|
|
24195
24270
|
} catch {
|
|
24196
24271
|
const parent = dirname10(path3);
|
|
24197
|
-
return parent === path3 ? path3 :
|
|
24272
|
+
return parent === path3 ? path3 : join22(canonicalPath2(parent), basename6(path3));
|
|
24198
24273
|
}
|
|
24199
24274
|
}
|
|
24200
24275
|
function codexDevicePrompt(output) {
|
|
24201
|
-
const
|
|
24202
|
-
const lines =
|
|
24276
|
+
const clean = stripVTControlCharacters5(output);
|
|
24277
|
+
const lines = clean.split(/\r?\n/).slice(0, -1).map((line) => line.trim());
|
|
24203
24278
|
if (!lines.some((line) => /^https:\/\/auth\.openai\.com\/codex\/device\/?$/.test(line))) return null;
|
|
24204
24279
|
const userCode = lines.find((line) => /^[A-Z0-9]{4,8}-[A-Z0-9]{4,8}$/.test(line));
|
|
24205
24280
|
return userCode ? { authorizationUrl: DEVICE_URL, userCode } : null;
|
|
@@ -24245,7 +24320,7 @@ var init_codex_device_auth = __esm({
|
|
|
24245
24320
|
if (!isAbsolute5(home) || env.CODEX_HOME && !isAbsolute5(env.CODEX_HOME)) {
|
|
24246
24321
|
throw new Error("Use an absolute HOME and CODEX_HOME path for this server's Codex provider before signing in.");
|
|
24247
24322
|
}
|
|
24248
|
-
const homeKey = canonicalPath2(resolve8(env.CODEX_HOME ||
|
|
24323
|
+
const homeKey = canonicalPath2(resolve8(env.CODEX_HOME || join22(home, ".codex")));
|
|
24249
24324
|
if (authenticatingHomes2.has(homeKey)) {
|
|
24250
24325
|
throw new Error("A ChatGPT sign-in is already running for this server account. Finish or cancel it before starting another.");
|
|
24251
24326
|
}
|
|
@@ -24324,7 +24399,7 @@ var init_codex_device_auth = __esm({
|
|
|
24324
24399
|
if (!isAbsolute5(home) || env.CODEX_HOME && !isAbsolute5(env.CODEX_HOME)) {
|
|
24325
24400
|
throw new Error("Use an absolute HOME and CODEX_HOME path for this server's Codex provider before signing out.");
|
|
24326
24401
|
}
|
|
24327
|
-
const homeKey = canonicalPath2(resolve8(env.CODEX_HOME ||
|
|
24402
|
+
const homeKey = canonicalPath2(resolve8(env.CODEX_HOME || join22(home, ".codex")));
|
|
24328
24403
|
if (authenticatingHomes2.has(homeKey)) {
|
|
24329
24404
|
throw new Error("A ChatGPT sign-in is running for this server account. Finish or cancel it before signing out.");
|
|
24330
24405
|
}
|
|
@@ -24483,11 +24558,11 @@ var init_codex_device_auth = __esm({
|
|
|
24483
24558
|
|
|
24484
24559
|
// server/drivers/codex-identity.ts
|
|
24485
24560
|
import { homedir as homedir12 } from "node:os";
|
|
24486
|
-
import { isAbsolute as isAbsolute6, join as
|
|
24561
|
+
import { isAbsolute as isAbsolute6, join as join23, resolve as resolve9 } from "node:path";
|
|
24487
24562
|
function codexHome2(env) {
|
|
24488
24563
|
if (env.CODEX_HOME) return isAbsolute6(env.CODEX_HOME) ? resolve9(env.CODEX_HOME) : null;
|
|
24489
24564
|
const home = env.HOME || env.USERPROFILE || homedir12();
|
|
24490
|
-
return isAbsolute6(home) ?
|
|
24565
|
+
return isAbsolute6(home) ? join23(home, ".codex") : null;
|
|
24491
24566
|
}
|
|
24492
24567
|
async function codexAccountEmail(cli, env, timeoutMs = 3e3) {
|
|
24493
24568
|
const cwd = env.HOME || env.USERPROFILE || homedir12();
|
|
@@ -24617,8 +24692,8 @@ function codexUpdateCommand(cli, platform = process.platform) {
|
|
|
24617
24692
|
const command = (tokens.length > 0 ? tokens : [trimmed]).map(quote).join(" ");
|
|
24618
24693
|
return platform === "win32" ? `& ${command} update` : `${command} update`;
|
|
24619
24694
|
}
|
|
24620
|
-
function codexAstraUpdate(version2,
|
|
24621
|
-
if (
|
|
24695
|
+
function codexAstraUpdate(version2, models2, cli) {
|
|
24696
|
+
if (models2.options.some((model) => model.id === ASTRA_MODEL_ID) || !codexPredatesAstra(version2)) {
|
|
24622
24697
|
return void 0;
|
|
24623
24698
|
}
|
|
24624
24699
|
return {
|
|
@@ -24912,11 +24987,11 @@ var init_codex = __esm({
|
|
|
24912
24987
|
return env;
|
|
24913
24988
|
};
|
|
24914
24989
|
const catalogEnv = childEnv();
|
|
24915
|
-
let
|
|
24990
|
+
let models2 = STATIC_CODEX_MODELS;
|
|
24916
24991
|
const refreshModels = async () => {
|
|
24917
24992
|
try {
|
|
24918
24993
|
const resolved = await readCodexModelCatalog(catalogEnv, fetch, config2.cli);
|
|
24919
|
-
if (resolved.options.length)
|
|
24994
|
+
if (resolved.options.length) models2 = resolved;
|
|
24920
24995
|
} catch {
|
|
24921
24996
|
}
|
|
24922
24997
|
};
|
|
@@ -25211,7 +25286,8 @@ var init_codex = __esm({
|
|
|
25211
25286
|
itemType: "tool",
|
|
25212
25287
|
itemId: item.id,
|
|
25213
25288
|
title,
|
|
25214
|
-
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)
|
|
25215
25291
|
});
|
|
25216
25292
|
}
|
|
25217
25293
|
break;
|
|
@@ -25244,7 +25320,8 @@ var init_codex = __esm({
|
|
|
25244
25320
|
type: "item.completed",
|
|
25245
25321
|
itemType: "tool",
|
|
25246
25322
|
itemId: item.id,
|
|
25247
|
-
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)
|
|
25248
25325
|
});
|
|
25249
25326
|
} else if (item.type === "reasoning") {
|
|
25250
25327
|
emit({ ...base2(threadId, turnId), type: "item.updated", itemType: "reasoning", tokens: null });
|
|
@@ -25535,7 +25612,7 @@ ${stderr ?? ""}`))
|
|
|
25535
25612
|
version: version2,
|
|
25536
25613
|
authenticated,
|
|
25537
25614
|
...email3 ? { account: { email: email3 } } : {},
|
|
25538
|
-
update: codexAstraUpdate(version2,
|
|
25615
|
+
update: codexAstraUpdate(version2, models2, config2.cli),
|
|
25539
25616
|
billing: "subscription"
|
|
25540
25617
|
};
|
|
25541
25618
|
};
|
|
@@ -25545,7 +25622,7 @@ ${stderr ?? ""}`))
|
|
|
25545
25622
|
displayName: input.displayName,
|
|
25546
25623
|
enabled: input.enabled,
|
|
25547
25624
|
get models() {
|
|
25548
|
-
return
|
|
25625
|
+
return models2;
|
|
25549
25626
|
},
|
|
25550
25627
|
refreshModels,
|
|
25551
25628
|
startAuthentication: () => authentication.start(),
|
|
@@ -25615,72 +25692,89 @@ function createOpenAIChatRuntime(options) {
|
|
|
25615
25692
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
25616
25693
|
});
|
|
25617
25694
|
const complete = async (messages, model, stream, signal, onDelta) => {
|
|
25618
|
-
const
|
|
25619
|
-
|
|
25620
|
-
|
|
25621
|
-
|
|
25622
|
-
|
|
25623
|
-
|
|
25624
|
-
|
|
25625
|
-
|
|
25626
|
-
|
|
25627
|
-
throw new Error(`${options.httpErrorLabel} HTTP ${response.status}${body ? `: ${body.slice(0, 200)}` : ""}`);
|
|
25628
|
-
}
|
|
25629
|
-
if (!stream) {
|
|
25630
|
-
const json2 = await response.json();
|
|
25631
|
-
const message2 = json2.choices?.[0]?.message;
|
|
25632
|
-
return {
|
|
25633
|
-
text: typeof message2?.content === "string" ? message2.content : "",
|
|
25634
|
-
reasoning: options.reasoning && typeof message2?.reasoning_content === "string" ? message2.reasoning_content : "",
|
|
25635
|
-
usage: usageFrom(json2.usage)
|
|
25636
|
-
};
|
|
25637
|
-
}
|
|
25638
|
-
if (!response.body) {
|
|
25639
|
-
throw new Error(options.noBodyError ?? `${options.httpErrorLabel} returned no response body`);
|
|
25640
|
-
}
|
|
25641
|
-
let text4 = "";
|
|
25642
|
-
let reasoning = "";
|
|
25643
|
-
let usage = null;
|
|
25644
|
-
const reader = response.body.getReader();
|
|
25645
|
-
const decoder = new TextDecoder();
|
|
25646
|
-
let buffer = "";
|
|
25695
|
+
const timeoutController = new AbortController();
|
|
25696
|
+
let idleTimer = null;
|
|
25697
|
+
const resetIdleTimer = () => {
|
|
25698
|
+
if (idleTimer) clearTimeout(idleTimer);
|
|
25699
|
+
idleTimer = setTimeout(() => {
|
|
25700
|
+
timeoutController.abort(new DOMException("Streaming idle timeout elapsed", "AbortError"));
|
|
25701
|
+
}, options.timeoutMs);
|
|
25702
|
+
};
|
|
25703
|
+
resetIdleTimer();
|
|
25647
25704
|
try {
|
|
25648
|
-
|
|
25649
|
-
|
|
25650
|
-
|
|
25651
|
-
|
|
25652
|
-
|
|
25653
|
-
|
|
25654
|
-
|
|
25655
|
-
|
|
25656
|
-
|
|
25657
|
-
|
|
25658
|
-
|
|
25659
|
-
|
|
25660
|
-
|
|
25661
|
-
|
|
25662
|
-
|
|
25663
|
-
|
|
25664
|
-
|
|
25665
|
-
|
|
25666
|
-
|
|
25667
|
-
|
|
25668
|
-
|
|
25669
|
-
|
|
25670
|
-
|
|
25671
|
-
|
|
25672
|
-
|
|
25673
|
-
|
|
25674
|
-
|
|
25705
|
+
const activeSignal = signal ? AbortSignal.any([signal, timeoutController.signal]) : timeoutController.signal;
|
|
25706
|
+
const response = await fetch(`${options.apiUrl}/chat/completions`, {
|
|
25707
|
+
method: "POST",
|
|
25708
|
+
headers: { authorization: `Bearer ${options.apiKey}`, "content-type": "application/json" },
|
|
25709
|
+
body: JSON.stringify(options.requestBody(model, messages, stream)),
|
|
25710
|
+
signal: activeSignal
|
|
25711
|
+
});
|
|
25712
|
+
if (!response.ok) {
|
|
25713
|
+
const body = await response.text().catch(() => "");
|
|
25714
|
+
throw new Error(`${options.httpErrorLabel} HTTP ${response.status}${body ? `: ${body.slice(0, 200)}` : ""}`);
|
|
25715
|
+
}
|
|
25716
|
+
if (!stream) {
|
|
25717
|
+
const json2 = await response.json();
|
|
25718
|
+
const message2 = json2.choices?.[0]?.message;
|
|
25719
|
+
return {
|
|
25720
|
+
text: typeof message2?.content === "string" ? message2.content : "",
|
|
25721
|
+
reasoning: options.reasoning && typeof message2?.reasoning_content === "string" ? message2.reasoning_content : "",
|
|
25722
|
+
usage: usageFrom(json2.usage)
|
|
25723
|
+
};
|
|
25724
|
+
}
|
|
25725
|
+
if (!response.body) {
|
|
25726
|
+
throw new Error(options.noBodyError ?? `${options.httpErrorLabel} returned no response body`);
|
|
25727
|
+
}
|
|
25728
|
+
let text4 = "";
|
|
25729
|
+
let reasoning = "";
|
|
25730
|
+
let usage = null;
|
|
25731
|
+
const reader = response.body.getReader();
|
|
25732
|
+
const decoder = new TextDecoder();
|
|
25733
|
+
let buffer = "";
|
|
25734
|
+
try {
|
|
25735
|
+
readLoop: for (; ; ) {
|
|
25736
|
+
const { done, value } = await reader.read();
|
|
25737
|
+
if (done) {
|
|
25738
|
+
if (buffer.trim() === "data: [DONE]") break;
|
|
25739
|
+
throw new DOMException("Stream ended before completion", "AbortError");
|
|
25740
|
+
}
|
|
25741
|
+
resetIdleTimer();
|
|
25742
|
+
buffer += decoder.decode(value, { stream: true });
|
|
25743
|
+
let newline;
|
|
25744
|
+
while ((newline = buffer.indexOf("\n")) !== -1) {
|
|
25745
|
+
const line = buffer.slice(0, newline).trim();
|
|
25746
|
+
buffer = buffer.slice(newline + 1);
|
|
25747
|
+
if (!line.startsWith("data:")) continue;
|
|
25748
|
+
const data = line.slice(5).trim();
|
|
25749
|
+
if (data === "[DONE]") break readLoop;
|
|
25750
|
+
let chunk;
|
|
25751
|
+
try {
|
|
25752
|
+
chunk = JSON.parse(data);
|
|
25753
|
+
} catch {
|
|
25754
|
+
continue;
|
|
25755
|
+
}
|
|
25756
|
+
const delta = chunk.choices?.[0]?.delta;
|
|
25757
|
+
const reasoningDelta = options.reasoning && typeof delta?.reasoning_content === "string" ? delta.reasoning_content : "";
|
|
25758
|
+
const contentDelta = typeof delta?.content === "string" ? delta.content : "";
|
|
25759
|
+
if (reasoningDelta) {
|
|
25760
|
+
reasoning += reasoningDelta;
|
|
25761
|
+
onDelta?.(reasoningDelta, "reasoning_text");
|
|
25762
|
+
}
|
|
25763
|
+
if (contentDelta) {
|
|
25764
|
+
text4 += contentDelta;
|
|
25765
|
+
onDelta?.(contentDelta, "assistant_text");
|
|
25766
|
+
}
|
|
25767
|
+
if (chunk.usage) usage = usageFrom(chunk.usage);
|
|
25675
25768
|
}
|
|
25676
|
-
if (chunk.usage) usage = usageFrom(chunk.usage);
|
|
25677
25769
|
}
|
|
25770
|
+
} finally {
|
|
25771
|
+
await reader.cancel().catch(() => {
|
|
25772
|
+
});
|
|
25678
25773
|
}
|
|
25774
|
+
return { text: text4, reasoning, usage };
|
|
25679
25775
|
} finally {
|
|
25680
|
-
|
|
25681
|
-
});
|
|
25776
|
+
if (idleTimer) clearTimeout(idleTimer);
|
|
25682
25777
|
}
|
|
25683
|
-
return { text: text4, reasoning, usage };
|
|
25684
25778
|
};
|
|
25685
25779
|
const messagesFor = (turn) => [
|
|
25686
25780
|
...turn.system ? [{ role: "system", content: turn.system }] : [],
|
|
@@ -25875,12 +25969,12 @@ var init_grok = __esm({
|
|
|
25875
25969
|
});
|
|
25876
25970
|
|
|
25877
25971
|
// server/drivers/acp/grok.ts
|
|
25878
|
-
import { existsSync as existsSync11, readFileSync as
|
|
25972
|
+
import { existsSync as existsSync11, readFileSync as readFileSync10, writeFileSync as writeFileSync6 } from "node:fs";
|
|
25879
25973
|
import { homedir as homedir14 } from "node:os";
|
|
25880
|
-
import { join as
|
|
25974
|
+
import { join as join24 } from "node:path";
|
|
25881
25975
|
function grokHome(env) {
|
|
25882
25976
|
if (env.GROK_HOME) return env.GROK_HOME;
|
|
25883
|
-
return
|
|
25977
|
+
return join24(env.HOME || env.USERPROFILE || homedir14(), ".grok");
|
|
25884
25978
|
}
|
|
25885
25979
|
function unquote2(raw) {
|
|
25886
25980
|
const value = raw.trim();
|
|
@@ -25890,10 +25984,10 @@ function unquote2(raw) {
|
|
|
25890
25984
|
return value;
|
|
25891
25985
|
}
|
|
25892
25986
|
function readGrokModelCatalog(env = process.env) {
|
|
25893
|
-
const path3 =
|
|
25987
|
+
const path3 = join24(grokHome(env), "config.toml");
|
|
25894
25988
|
let text4 = "";
|
|
25895
25989
|
try {
|
|
25896
|
-
text4 =
|
|
25990
|
+
text4 = readFileSync10(path3, "utf8");
|
|
25897
25991
|
} catch {
|
|
25898
25992
|
return STATIC_GROK_MODELS;
|
|
25899
25993
|
}
|
|
@@ -25963,10 +26057,10 @@ function ensureGrokInjectSlug(modelId, env = process.env) {
|
|
|
25963
26057
|
if (!inject) return modelId;
|
|
25964
26058
|
const host = localHost(inject.host);
|
|
25965
26059
|
if (!host) return modelId;
|
|
25966
|
-
const path3 =
|
|
26060
|
+
const path3 = join24(grokHome(env), "config.toml");
|
|
25967
26061
|
let text4 = "";
|
|
25968
26062
|
try {
|
|
25969
|
-
text4 =
|
|
26063
|
+
text4 = readFileSync10(path3, "utf8");
|
|
25970
26064
|
} catch {
|
|
25971
26065
|
text4 = "";
|
|
25972
26066
|
}
|
|
@@ -26017,7 +26111,7 @@ function ensureGrokInjectSlug(modelId, env = process.env) {
|
|
|
26017
26111
|
const next = text4 && !text4.endsWith("\n") ? `${text4}
|
|
26018
26112
|
|
|
26019
26113
|
${block2}` : `${text4}${text4 ? "\n" : ""}${block2}`;
|
|
26020
|
-
|
|
26114
|
+
writeFileSync6(path3, next);
|
|
26021
26115
|
return slug;
|
|
26022
26116
|
}
|
|
26023
26117
|
function grokAcceptsUnadvertisedImages(init) {
|
|
@@ -26109,7 +26203,7 @@ var init_grok2 = __esm({
|
|
|
26109
26203
|
// an unauthenticated CLI is a user action, not something to paper over.
|
|
26110
26204
|
pickAuthMethod: (methods) => methods.some((m) => m.id === "cached_token") ? "cached_token" : null,
|
|
26111
26205
|
authFailure: "fail",
|
|
26112
|
-
isAuthenticated: () => existsSync11(
|
|
26206
|
+
isAuthenticated: () => existsSync11(join24(homedir14(), ".grok", "auth.json")),
|
|
26113
26207
|
// `--append-system-prompt`/`--rules` are accepted by the CLI but do NOT
|
|
26114
26208
|
// reach the agent-stdio system prompt (verified against 1.0.0), so the
|
|
26115
26209
|
// persona is prepended codex-style.
|
|
@@ -26122,12 +26216,12 @@ ${turn.text}` : turn.text
|
|
|
26122
26216
|
});
|
|
26123
26217
|
|
|
26124
26218
|
// server/drivers/acp/gemini.ts
|
|
26125
|
-
import { existsSync as existsSync12, readFileSync as
|
|
26219
|
+
import { existsSync as existsSync12, readFileSync as readFileSync11 } from "node:fs";
|
|
26126
26220
|
import { homedir as homedir15 } from "node:os";
|
|
26127
|
-
import { join as
|
|
26221
|
+
import { join as join25 } from "node:path";
|
|
26128
26222
|
function liveOauthCredential() {
|
|
26129
26223
|
try {
|
|
26130
|
-
const creds = JSON.parse(
|
|
26224
|
+
const creds = JSON.parse(readFileSync11(join25(homedir15(), ".gemini", "oauth_creds.json"), "utf8"));
|
|
26131
26225
|
if (typeof creds.access_token !== "string" || creds.access_token.trim().length === 0) return false;
|
|
26132
26226
|
const hasRefresh = typeof creds.refresh_token === "string" && creds.refresh_token.trim().length > 0;
|
|
26133
26227
|
if (creds.expiry_date !== void 0) {
|
|
@@ -26140,7 +26234,7 @@ function liveOauthCredential() {
|
|
|
26140
26234
|
}
|
|
26141
26235
|
}
|
|
26142
26236
|
function geminiIsAuthenticated(env) {
|
|
26143
|
-
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();
|
|
26144
26238
|
}
|
|
26145
26239
|
var AUTH_PREFERENCE, nonBlank, support3, GeminiAgentDriver;
|
|
26146
26240
|
var init_gemini = __esm({
|
|
@@ -26182,17 +26276,17 @@ ${turn.text}` : turn.text
|
|
|
26182
26276
|
});
|
|
26183
26277
|
|
|
26184
26278
|
// server/drivers/acp/kimi.ts
|
|
26185
|
-
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";
|
|
26186
26280
|
import { homedir as homedir16 } from "node:os";
|
|
26187
|
-
import { join as
|
|
26281
|
+
import { join as join26 } from "node:path";
|
|
26188
26282
|
function isLocalInjectAlias(slug) {
|
|
26189
26283
|
return LOCAL_PROVIDER_PREFIXES.some((prefix) => slug.startsWith(prefix));
|
|
26190
26284
|
}
|
|
26191
26285
|
function kimiDataRoot(env) {
|
|
26192
|
-
return env.KIMI_CODE_HOME ||
|
|
26286
|
+
return env.KIMI_CODE_HOME || join26(env.HOME || env.USERPROFILE || homedir16(), ".kimi-code");
|
|
26193
26287
|
}
|
|
26194
26288
|
function credentialsPath(env) {
|
|
26195
|
-
return
|
|
26289
|
+
return join26(kimiDataRoot(env), "credentials", "kimi-code.json");
|
|
26196
26290
|
}
|
|
26197
26291
|
function quoteToml2(value) {
|
|
26198
26292
|
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
@@ -26480,10 +26574,10 @@ function ensureKimiInjectAlias(modelId, env = process.env) {
|
|
|
26480
26574
|
const alias = `${inject.host}/${inject.model.replace(/\//g, "-")}`;
|
|
26481
26575
|
const dataRoot = kimiDataRoot(env);
|
|
26482
26576
|
mkdirSync10(dataRoot, { recursive: true });
|
|
26483
|
-
const path3 =
|
|
26577
|
+
const path3 = join26(dataRoot, "config.toml");
|
|
26484
26578
|
let text4 = "";
|
|
26485
26579
|
try {
|
|
26486
|
-
text4 =
|
|
26580
|
+
text4 = readFileSync12(path3, "utf8");
|
|
26487
26581
|
} catch {
|
|
26488
26582
|
text4 = "";
|
|
26489
26583
|
}
|
|
@@ -26521,9 +26615,9 @@ function ensureKimiInjectAlias(modelId, env = process.env) {
|
|
|
26521
26615
|
|
|
26522
26616
|
` : text4 ? `${text4}
|
|
26523
26617
|
` : "";
|
|
26524
|
-
|
|
26618
|
+
writeFileSync7(path3, `${prefix}${blocks.join("\n")}`);
|
|
26525
26619
|
} else if (text4 !== original) {
|
|
26526
|
-
|
|
26620
|
+
writeFileSync7(path3, text4);
|
|
26527
26621
|
}
|
|
26528
26622
|
return alias;
|
|
26529
26623
|
}
|
|
@@ -26545,7 +26639,7 @@ function readKimiModelCatalog(env) {
|
|
|
26545
26639
|
const dataRoot = kimiDataRoot(env);
|
|
26546
26640
|
let text4 = "";
|
|
26547
26641
|
try {
|
|
26548
|
-
text4 =
|
|
26642
|
+
text4 = readFileSync12(join26(dataRoot, "config.toml"), "utf8");
|
|
26549
26643
|
} catch {
|
|
26550
26644
|
return STATIC_KIMI_MODELS;
|
|
26551
26645
|
}
|
|
@@ -26664,12 +26758,12 @@ ${turn.text}` : turn.text
|
|
|
26664
26758
|
});
|
|
26665
26759
|
|
|
26666
26760
|
// server/drivers/acp/droid.ts
|
|
26667
|
-
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";
|
|
26668
26762
|
import { homedir as homedir17 } from "node:os";
|
|
26669
|
-
import { join as
|
|
26763
|
+
import { join as join27 } from "node:path";
|
|
26670
26764
|
function authFilePaths(env) {
|
|
26671
26765
|
const home = env.FACTORY_HOME_OVERRIDE || env.HOME || homedir17();
|
|
26672
|
-
return AUTH_FILES.map((name) =>
|
|
26766
|
+
return AUTH_FILES.map((name) => join27(home, ".factory", name));
|
|
26673
26767
|
}
|
|
26674
26768
|
function droidInjectId(host, model) {
|
|
26675
26769
|
const safe = `${host}-${model}`.replace(/[^a-zA-Z0-9._+-]+/g, "-").replace(/-+/g, "-");
|
|
@@ -26684,12 +26778,12 @@ function ensureDroidInjectModel(modelId, env = process.env) {
|
|
|
26684
26778
|
const host = localHost(inject.host);
|
|
26685
26779
|
if (!host) return modelId;
|
|
26686
26780
|
const id = droidInjectId(inject.host, inject.model);
|
|
26687
|
-
const dir =
|
|
26781
|
+
const dir = join27(factoryHome(env), ".factory");
|
|
26688
26782
|
mkdirSync11(dir, { recursive: true });
|
|
26689
|
-
const path3 =
|
|
26783
|
+
const path3 = join27(dir, "settings.json");
|
|
26690
26784
|
let settings2 = {};
|
|
26691
26785
|
try {
|
|
26692
|
-
settings2 = JSON.parse(
|
|
26786
|
+
settings2 = JSON.parse(readFileSync13(path3, "utf8"));
|
|
26693
26787
|
} catch (error51) {
|
|
26694
26788
|
if (existsSync14(path3)) throw error51;
|
|
26695
26789
|
}
|
|
@@ -26701,7 +26795,7 @@ function ensureDroidInjectModel(modelId, env = process.env) {
|
|
|
26701
26795
|
if (!match.id) {
|
|
26702
26796
|
match.id = id;
|
|
26703
26797
|
settings2.customModels = custom2;
|
|
26704
|
-
|
|
26798
|
+
writeFileSync8(path3, `${JSON.stringify(settings2, null, 2)}
|
|
26705
26799
|
`);
|
|
26706
26800
|
}
|
|
26707
26801
|
return match.id;
|
|
@@ -26715,7 +26809,7 @@ function ensureDroidInjectModel(modelId, env = process.env) {
|
|
|
26715
26809
|
provider: "generic-chat-completion-api"
|
|
26716
26810
|
});
|
|
26717
26811
|
settings2.customModels = custom2;
|
|
26718
|
-
|
|
26812
|
+
writeFileSync8(path3, `${JSON.stringify(settings2, null, 2)}
|
|
26719
26813
|
`);
|
|
26720
26814
|
return id;
|
|
26721
26815
|
}
|
|
@@ -26726,7 +26820,7 @@ function applyDroidLocalAuthEnv(env, modelId) {
|
|
|
26726
26820
|
env.FACTORY_API_KEY = "openmausbot-local";
|
|
26727
26821
|
}
|
|
26728
26822
|
function readSettings(env) {
|
|
26729
|
-
return JSON.parse(
|
|
26823
|
+
return JSON.parse(readFileSync13(join27(factoryHome(env), ".factory", "settings.json"), "utf8"));
|
|
26730
26824
|
}
|
|
26731
26825
|
async function resolveModels(env) {
|
|
26732
26826
|
let catalog = MODELS3;
|
|
@@ -27116,9 +27210,9 @@ ${turn.text}` : turn.text
|
|
|
27116
27210
|
});
|
|
27117
27211
|
|
|
27118
27212
|
// server/drivers/acp/opencode-go.ts
|
|
27119
|
-
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";
|
|
27120
27214
|
import { homedir as homedir18 } from "node:os";
|
|
27121
|
-
import { join as
|
|
27215
|
+
import { join as join28 } from "node:path";
|
|
27122
27216
|
function labelForModel(id) {
|
|
27123
27217
|
return id.split(/[-_.]+/g).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
27124
27218
|
}
|
|
@@ -27219,7 +27313,7 @@ async function discoverOpenCodeModels(environment, cli = "opencode") {
|
|
|
27219
27313
|
}
|
|
27220
27314
|
function opencodeConfigDir(env) {
|
|
27221
27315
|
const home = env.HOME || env.USERPROFILE || homedir18();
|
|
27222
|
-
return
|
|
27316
|
+
return join28(env.XDG_CONFIG_HOME || join28(home, ".config"), "opencode");
|
|
27223
27317
|
}
|
|
27224
27318
|
function ensureOpenCodeInjectModel(modelId, env = process.env) {
|
|
27225
27319
|
const inject = decodeInjectId(modelId);
|
|
@@ -27229,11 +27323,11 @@ function ensureOpenCodeInjectModel(modelId, env = process.env) {
|
|
|
27229
27323
|
const native = `${inject.host}/${inject.model}`;
|
|
27230
27324
|
const dir = opencodeConfigDir(env);
|
|
27231
27325
|
mkdirSync12(dir, { recursive: true });
|
|
27232
|
-
const path3 =
|
|
27326
|
+
const path3 = join28(dir, "opencode.json");
|
|
27233
27327
|
let config2 = { $schema: "https://opencode.ai/config.json" };
|
|
27234
27328
|
if (existsSync15(path3)) {
|
|
27235
27329
|
try {
|
|
27236
|
-
config2 = JSON.parse(
|
|
27330
|
+
config2 = JSON.parse(readFileSync14(path3, "utf8"));
|
|
27237
27331
|
} catch {
|
|
27238
27332
|
}
|
|
27239
27333
|
}
|
|
@@ -27248,29 +27342,29 @@ function ensureOpenCodeInjectModel(modelId, env = process.env) {
|
|
|
27248
27342
|
const options = existing.options && typeof existing.options === "object" && !Array.isArray(existing.options) ? { ...existing.options } : {};
|
|
27249
27343
|
options.baseURL = host.baseUrl;
|
|
27250
27344
|
if (!options.apiKey) options.apiKey = hostApiKey(host, env);
|
|
27251
|
-
const
|
|
27252
|
-
if (!
|
|
27253
|
-
|
|
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})` };
|
|
27254
27348
|
}
|
|
27255
27349
|
providers[inject.host] = {
|
|
27256
27350
|
...existing,
|
|
27257
27351
|
npm: existing.npm || "@ai-sdk/openai-compatible",
|
|
27258
27352
|
name: existing.name || host.label,
|
|
27259
27353
|
options,
|
|
27260
|
-
models
|
|
27354
|
+
models: models2
|
|
27261
27355
|
};
|
|
27262
27356
|
config2.provider = providers;
|
|
27263
|
-
|
|
27357
|
+
writeFileSync9(path3, `${JSON.stringify(config2, null, 2)}
|
|
27264
27358
|
`);
|
|
27265
27359
|
return native;
|
|
27266
27360
|
}
|
|
27267
27361
|
function storedAuthPaths(env) {
|
|
27268
27362
|
const home = env.HOME || env.USERPROFILE || homedir18();
|
|
27269
27363
|
const roots = [
|
|
27270
|
-
env.XDG_DATA_HOME ||
|
|
27271
|
-
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") : ""
|
|
27272
27366
|
].filter(Boolean);
|
|
27273
|
-
return [...new Set(roots)].map((root) =>
|
|
27367
|
+
return [...new Set(roots)].map((root) => join28(root, "opencode", "auth.json"));
|
|
27274
27368
|
}
|
|
27275
27369
|
function usableAuthEntry(parsed) {
|
|
27276
27370
|
return Object.values(parsed).some((auth) => {
|
|
@@ -27284,7 +27378,7 @@ function hasStoredOpenCodeAuth(env) {
|
|
|
27284
27378
|
if (env.OPENCODE_AUTH_CONTENT) candidates.push(env.OPENCODE_AUTH_CONTENT);
|
|
27285
27379
|
for (const path3 of storedAuthPaths(env)) {
|
|
27286
27380
|
try {
|
|
27287
|
-
candidates.push(
|
|
27381
|
+
candidates.push(readFileSync14(path3, "utf8"));
|
|
27288
27382
|
} catch {
|
|
27289
27383
|
}
|
|
27290
27384
|
}
|
|
@@ -27400,13 +27494,13 @@ ${turn.text}` : turn.text
|
|
|
27400
27494
|
});
|
|
27401
27495
|
|
|
27402
27496
|
// server/drivers/acp/qwen.ts
|
|
27403
|
-
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";
|
|
27404
27498
|
import { createHash as createHash7 } from "node:crypto";
|
|
27405
27499
|
import { homedir as homedir19 } from "node:os";
|
|
27406
|
-
import { join as
|
|
27500
|
+
import { join as join29 } from "node:path";
|
|
27407
27501
|
function qwenHome(env) {
|
|
27408
27502
|
const home = process.platform === "win32" ? env.USERPROFILE || env.HOME || homedir19() : env.HOME || env.USERPROFILE || homedir19();
|
|
27409
|
-
return
|
|
27503
|
+
return join29(home, ".qwen");
|
|
27410
27504
|
}
|
|
27411
27505
|
function isRecord2(value) {
|
|
27412
27506
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
@@ -27418,7 +27512,7 @@ function qwenModelLabel(id, name) {
|
|
|
27418
27512
|
function readQwenRoutes(env) {
|
|
27419
27513
|
let settings2;
|
|
27420
27514
|
try {
|
|
27421
|
-
settings2 = JSON.parse(
|
|
27515
|
+
settings2 = JSON.parse(readFileSync15(join29(qwenHome(env), "settings.json"), "utf8"));
|
|
27422
27516
|
} catch {
|
|
27423
27517
|
return [];
|
|
27424
27518
|
}
|
|
@@ -27502,11 +27596,11 @@ function ensureQwenInjectModel(modelId, env = process.env) {
|
|
|
27502
27596
|
if (!host) return modelId;
|
|
27503
27597
|
const dir = qwenHome(env);
|
|
27504
27598
|
mkdirSync13(dir, { recursive: true });
|
|
27505
|
-
const path3 =
|
|
27599
|
+
const path3 = join29(dir, "settings.json");
|
|
27506
27600
|
let settings2 = {};
|
|
27507
27601
|
if (existsSync16(path3)) {
|
|
27508
27602
|
try {
|
|
27509
|
-
settings2 = JSON.parse(
|
|
27603
|
+
settings2 = JSON.parse(readFileSync15(path3, "utf8"));
|
|
27510
27604
|
} catch {
|
|
27511
27605
|
}
|
|
27512
27606
|
}
|
|
@@ -27530,7 +27624,7 @@ function ensureQwenInjectModel(modelId, env = process.env) {
|
|
|
27530
27624
|
providers.openai = openai;
|
|
27531
27625
|
settings2.modelProviders = providers;
|
|
27532
27626
|
}
|
|
27533
|
-
|
|
27627
|
+
writeFileSync10(path3, `${JSON.stringify(settings2, null, 2)}
|
|
27534
27628
|
`, { mode: 384 });
|
|
27535
27629
|
try {
|
|
27536
27630
|
chmodSync5(path3, 384);
|
|
@@ -29594,7 +29688,7 @@ var init_YAMLMap = __esm({
|
|
|
29594
29688
|
static from(schema4, obj, ctx) {
|
|
29595
29689
|
const { keepUndefined, replacer } = ctx;
|
|
29596
29690
|
const map3 = new this(schema4);
|
|
29597
|
-
const
|
|
29691
|
+
const add = (key, value) => {
|
|
29598
29692
|
if (typeof replacer === "function")
|
|
29599
29693
|
value = replacer.call(obj, key, value);
|
|
29600
29694
|
else if (Array.isArray(replacer) && !replacer.includes(key))
|
|
@@ -29604,10 +29698,10 @@ var init_YAMLMap = __esm({
|
|
|
29604
29698
|
};
|
|
29605
29699
|
if (obj instanceof Map) {
|
|
29606
29700
|
for (const [key, value] of obj)
|
|
29607
|
-
|
|
29701
|
+
add(key, value);
|
|
29608
29702
|
} else if (obj && typeof obj === "object") {
|
|
29609
29703
|
for (const key of Object.keys(obj))
|
|
29610
|
-
|
|
29704
|
+
add(key, obj[key]);
|
|
29611
29705
|
}
|
|
29612
29706
|
if (typeof schema4.sortMapEntries === "function") {
|
|
29613
29707
|
map3.items.sort(schema4.sortMapEntries);
|
|
@@ -34501,9 +34595,9 @@ var init_browser = __esm({
|
|
|
34501
34595
|
|
|
34502
34596
|
// server/drivers/acp/hermes.ts
|
|
34503
34597
|
import { spawn as spawn6 } from "node:child_process";
|
|
34504
|
-
import { mkdirSync as mkdirSync14, readFileSync as
|
|
34598
|
+
import { mkdirSync as mkdirSync14, readFileSync as readFileSync16, writeFileSync as writeFileSync11 } from "node:fs";
|
|
34505
34599
|
import { homedir as homedir20 } from "node:os";
|
|
34506
|
-
import { join as
|
|
34600
|
+
import { join as join30 } from "node:path";
|
|
34507
34601
|
function bindHermesScreenshotCompat(env, modelId) {
|
|
34508
34602
|
delete env[HERMES_OPENMAUS_SCREENSHOT_COMPAT];
|
|
34509
34603
|
delete env[HERMES_OPENMAUS_SCREENSHOT_COMPAT_MODEL];
|
|
@@ -34513,7 +34607,7 @@ function bindHermesScreenshotCompat(env, modelId) {
|
|
|
34513
34607
|
env[HERMES_OPENMAUS_SCREENSHOT_COMPAT_MODEL] = inject.model;
|
|
34514
34608
|
}
|
|
34515
34609
|
function hermesHome(env) {
|
|
34516
|
-
return env.HERMES_HOME ||
|
|
34610
|
+
return env.HERMES_HOME || join30(env.HOME || env.USERPROFILE || homedir20(), ".hermes");
|
|
34517
34611
|
}
|
|
34518
34612
|
function quoteYaml(value) {
|
|
34519
34613
|
if (/^[\w./:+-]+$/.test(value)) return value;
|
|
@@ -34555,15 +34649,15 @@ function ensureHermesInjectProvider(modelId, env = process.env) {
|
|
|
34555
34649
|
if (!host) return modelId;
|
|
34556
34650
|
const dir = hermesHome(env);
|
|
34557
34651
|
mkdirSync14(dir, { recursive: true });
|
|
34558
|
-
const path3 =
|
|
34652
|
+
const path3 = join30(dir, "config.yaml");
|
|
34559
34653
|
let text4 = "";
|
|
34560
34654
|
try {
|
|
34561
|
-
text4 =
|
|
34655
|
+
text4 = readFileSync16(path3, "utf8");
|
|
34562
34656
|
} catch {
|
|
34563
34657
|
text4 = "";
|
|
34564
34658
|
}
|
|
34565
34659
|
const next = upsertHermesProvider(text4, inject.host, host.baseUrl, hostApiKey(host, env));
|
|
34566
|
-
if (next !== text4)
|
|
34660
|
+
if (next !== text4) writeFileSync11(path3, next);
|
|
34567
34661
|
return hermesAcpModelId(modelId) ?? modelId;
|
|
34568
34662
|
}
|
|
34569
34663
|
function hermesAcpModelId(modelId) {
|
|
@@ -34626,13 +34720,13 @@ function hermesConfiguredModel(env = process.env) {
|
|
|
34626
34720
|
const dir = hermesHome(env);
|
|
34627
34721
|
let secrets = "";
|
|
34628
34722
|
try {
|
|
34629
|
-
secrets =
|
|
34723
|
+
secrets = readFileSync16(join30(dir, ".env"), "utf8");
|
|
34630
34724
|
} catch {
|
|
34631
34725
|
}
|
|
34632
34726
|
const hasHostedProviderKey = HERMES_HOSTED_PROVIDER_KEYS.some((name) => nonEmptyDotenvValue(secrets, name));
|
|
34633
34727
|
let configuredDefault = null;
|
|
34634
34728
|
try {
|
|
34635
|
-
configuredDefault = hermesConfigDefault(
|
|
34729
|
+
configuredDefault = hermesConfigDefault(readFileSync16(join30(dir, "config.yaml"), "utf8"));
|
|
34636
34730
|
} catch {
|
|
34637
34731
|
}
|
|
34638
34732
|
const configuredProvider = configuredDefault?.provider.toLowerCase() ?? "";
|
|
@@ -34647,11 +34741,18 @@ function hermesConfiguredModel(env = process.env) {
|
|
|
34647
34741
|
custom: true
|
|
34648
34742
|
};
|
|
34649
34743
|
}
|
|
34744
|
+
function hermesAcpModelsTimeoutMs(env) {
|
|
34745
|
+
const raw = env[HERMES_ACP_MODELS_TIMEOUT_ENV];
|
|
34746
|
+
if (raw === void 0) return HERMES_ACP_MODELS_DEFAULT_TIMEOUT_MS;
|
|
34747
|
+
const parsed = Number(raw);
|
|
34748
|
+
return Number.isInteger(parsed) && parsed > 0 && parsed <= 2147483647 ? parsed : HERMES_ACP_MODELS_DEFAULT_TIMEOUT_MS;
|
|
34749
|
+
}
|
|
34650
34750
|
async function fetchHermesAcpModels(cli, env) {
|
|
34651
34751
|
return await new Promise((resolve13) => {
|
|
34652
34752
|
let child;
|
|
34653
34753
|
try {
|
|
34654
|
-
|
|
34754
|
+
const resolved = resolveCli(cli, ["acp"], env);
|
|
34755
|
+
child = spawn6(resolved.command, resolved.args, { stdio: ["pipe", "pipe", "ignore"], env, windowsHide: true });
|
|
34655
34756
|
} catch {
|
|
34656
34757
|
return resolve13([]);
|
|
34657
34758
|
}
|
|
@@ -34676,7 +34777,7 @@ async function fetchHermesAcpModels(cli, env) {
|
|
|
34676
34777
|
}
|
|
34677
34778
|
resolve13(out);
|
|
34678
34779
|
};
|
|
34679
|
-
timer = setTimeout(() => done([]),
|
|
34780
|
+
timer = setTimeout(() => done([]), hermesAcpModelsTimeoutMs(env));
|
|
34680
34781
|
child.once("error", () => done([]));
|
|
34681
34782
|
child.once("close", () => {
|
|
34682
34783
|
if (hardKillTimer) clearTimeout(hardKillTimer);
|
|
@@ -34756,11 +34857,12 @@ async function applySetting2(request, method, params, what) {
|
|
|
34756
34857
|
throw new Error(`Hermes rejected ${what} via ${method}: ${e.message}`);
|
|
34757
34858
|
}
|
|
34758
34859
|
}
|
|
34759
|
-
var EMPTY2, HERMES_OPENMAUS_SCREENSHOT_COMPAT, HERMES_OPENMAUS_SCREENSHOT_COMPAT_MODEL, HERMES_CONFIG_MODEL_ID, HERMES_HOSTED_PROVIDER_KEYS, HERMES_LOCAL_CONFIG_PROVIDERS, support10, HermesAgentDriver;
|
|
34860
|
+
var EMPTY2, HERMES_OPENMAUS_SCREENSHOT_COMPAT, HERMES_OPENMAUS_SCREENSHOT_COMPAT_MODEL, HERMES_CONFIG_MODEL_ID, HERMES_HOSTED_PROVIDER_KEYS, HERMES_LOCAL_CONFIG_PROVIDERS, HERMES_ACP_MODELS_TIMEOUT_ENV, HERMES_ACP_MODELS_DEFAULT_TIMEOUT_MS, support10, HermesAgentDriver;
|
|
34760
34861
|
var init_hermes = __esm({
|
|
34761
34862
|
"server/drivers/acp/hermes.ts"() {
|
|
34762
34863
|
"use strict";
|
|
34763
34864
|
init_browser();
|
|
34865
|
+
init_procs();
|
|
34764
34866
|
init_local_inject();
|
|
34765
34867
|
init_core3();
|
|
34766
34868
|
EMPTY2 = { default: "", options: [] };
|
|
@@ -34774,6 +34876,8 @@ var init_hermes = __esm({
|
|
|
34774
34876
|
"Z_AI_API_KEY"
|
|
34775
34877
|
];
|
|
34776
34878
|
HERMES_LOCAL_CONFIG_PROVIDERS = /* @__PURE__ */ new Set(["custom", "lmstudio", "ollama", "vllm", "llamacpp"]);
|
|
34879
|
+
HERMES_ACP_MODELS_TIMEOUT_ENV = "HERMES_ACP_MODELS_TIMEOUT_MS";
|
|
34880
|
+
HERMES_ACP_MODELS_DEFAULT_TIMEOUT_MS = 15e3;
|
|
34777
34881
|
support10 = {
|
|
34778
34882
|
driverKind: "hermesAgent",
|
|
34779
34883
|
displayName: "Hermes",
|
|
@@ -34846,12 +34950,19 @@ function decodeConfig5(raw) {
|
|
|
34846
34950
|
provider: typeof config2.provider === "string" ? config2.provider || void 0 : process.env.OPENAI_COMPAT_PROVIDER || void 0
|
|
34847
34951
|
};
|
|
34848
34952
|
}
|
|
34849
|
-
var DRIVER_KIND5, DEFAULT_MODELS, OpenAICompatDriver;
|
|
34953
|
+
var DRIVER_KIND5, DEFAULT_IDLE_TIMEOUT_MS, idleTimeoutMs, DEFAULT_MODELS, OpenAICompatDriver;
|
|
34850
34954
|
var init_openai_compat = __esm({
|
|
34851
34955
|
"server/drivers/openai-compat.ts"() {
|
|
34852
34956
|
"use strict";
|
|
34853
34957
|
init_openai_chat();
|
|
34854
34958
|
DRIVER_KIND5 = "openai-compat";
|
|
34959
|
+
DEFAULT_IDLE_TIMEOUT_MS = 18e4;
|
|
34960
|
+
idleTimeoutMs = () => {
|
|
34961
|
+
const raw = process.env.OPENMAUS_OPENAI_COMPAT_IDLE_TIMEOUT_MS;
|
|
34962
|
+
if (!raw) return DEFAULT_IDLE_TIMEOUT_MS;
|
|
34963
|
+
const value = Number(raw);
|
|
34964
|
+
return Number.isSafeInteger(value) && value >= 1e3 && value <= 2147483647 ? value : DEFAULT_IDLE_TIMEOUT_MS;
|
|
34965
|
+
};
|
|
34855
34966
|
DEFAULT_MODELS = {
|
|
34856
34967
|
default: "meta-llama/llama-3.3-70b-instruct",
|
|
34857
34968
|
options: [
|
|
@@ -34932,7 +35043,7 @@ var init_openai_compat = __esm({
|
|
|
34932
35043
|
httpErrorLabel: "upstream",
|
|
34933
35044
|
missingKeyError: `no API key \u2014 set ${config2.apiKeyEnv} or add it to the instance config`,
|
|
34934
35045
|
unavailableReason: `no API key \u2014 set ${config2.apiKeyEnv} or add it to the instance config`,
|
|
34935
|
-
timeoutMs:
|
|
35046
|
+
timeoutMs: idleTimeoutMs(),
|
|
34936
35047
|
reasoning: true,
|
|
34937
35048
|
billing: "metered",
|
|
34938
35049
|
includeUsageInCompleted: true,
|
|
@@ -34952,13 +35063,13 @@ var init_openai_compat = __esm({
|
|
|
34952
35063
|
});
|
|
34953
35064
|
|
|
34954
35065
|
// server/drivers/pi.ts
|
|
34955
|
-
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";
|
|
34956
35067
|
import { homedir as homedir21, tmpdir as tmpdir4 } from "node:os";
|
|
34957
|
-
import { join as
|
|
35068
|
+
import { join as join31 } from "node:path";
|
|
34958
35069
|
function readPiPromptImages(turn) {
|
|
34959
35070
|
return (turn.images ?? []).map((image) => ({
|
|
34960
35071
|
type: "image",
|
|
34961
|
-
data:
|
|
35072
|
+
data: readFileSync17(image.path).toString("base64"),
|
|
34962
35073
|
mimeType: image.mime
|
|
34963
35074
|
}));
|
|
34964
35075
|
}
|
|
@@ -35061,7 +35172,7 @@ async function applyPiLocalCatalog(catalog, env = process.env, fetchImpl = fetch
|
|
|
35061
35172
|
return preferPiInjectRows(await mergeLocalInject(catalog, env, fetchImpl));
|
|
35062
35173
|
}
|
|
35063
35174
|
function piAgentDir(env) {
|
|
35064
|
-
return
|
|
35175
|
+
return join31(env.HOME || env.USERPROFILE || homedir21(), ".pi", "agent");
|
|
35065
35176
|
}
|
|
35066
35177
|
function ensurePiInjectModel(modelId, env = process.env) {
|
|
35067
35178
|
const split = splitPiModel(modelId);
|
|
@@ -35072,11 +35183,11 @@ function ensurePiInjectModel(modelId, env = process.env) {
|
|
|
35072
35183
|
if (!host) return split;
|
|
35073
35184
|
const dir = piAgentDir(env);
|
|
35074
35185
|
mkdirSync15(dir, { recursive: true });
|
|
35075
|
-
const path3 =
|
|
35186
|
+
const path3 = join31(dir, "models.json");
|
|
35076
35187
|
let root = { providers: {} };
|
|
35077
35188
|
if (existsSync17(path3)) {
|
|
35078
35189
|
try {
|
|
35079
|
-
const parsed = JSON.parse(
|
|
35190
|
+
const parsed = JSON.parse(readFileSync17(path3, "utf8"));
|
|
35080
35191
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
35081
35192
|
root = parsed;
|
|
35082
35193
|
} else {
|
|
@@ -35101,11 +35212,11 @@ function ensurePiInjectModel(modelId, env = process.env) {
|
|
|
35101
35212
|
if (!existing.compat) {
|
|
35102
35213
|
existing.compat = { supportsDeveloperRole: false, supportsReasoningEffort: true };
|
|
35103
35214
|
}
|
|
35104
|
-
const
|
|
35215
|
+
const models2 = Array.isArray(existing.models) ? existing.models.filter(
|
|
35105
35216
|
(row) => Boolean(row) && typeof row === "object" && !Array.isArray(row)
|
|
35106
35217
|
) : [];
|
|
35107
|
-
if (!
|
|
35108
|
-
|
|
35218
|
+
if (!models2.some((row) => row.id === inject.model)) {
|
|
35219
|
+
models2.push({
|
|
35109
35220
|
id: inject.model,
|
|
35110
35221
|
name: inject.model,
|
|
35111
35222
|
reasoning: true,
|
|
@@ -35114,10 +35225,10 @@ function ensurePiInjectModel(modelId, env = process.env) {
|
|
|
35114
35225
|
maxTokens: 16384
|
|
35115
35226
|
});
|
|
35116
35227
|
}
|
|
35117
|
-
existing.models =
|
|
35228
|
+
existing.models = models2;
|
|
35118
35229
|
providers[inject.host] = existing;
|
|
35119
35230
|
root.providers = providers;
|
|
35120
|
-
|
|
35231
|
+
writeFileSync12(path3, `${JSON.stringify(root, null, 2)}
|
|
35121
35232
|
`, { mode: 384 });
|
|
35122
35233
|
try {
|
|
35123
35234
|
chmodSync6(path3, 384);
|
|
@@ -35128,7 +35239,7 @@ function ensurePiInjectModel(modelId, env = process.env) {
|
|
|
35128
35239
|
function readPiDefaultModel(env) {
|
|
35129
35240
|
const home = env.HOME || env.USERPROFILE || homedir21();
|
|
35130
35241
|
try {
|
|
35131
|
-
const s = JSON.parse(
|
|
35242
|
+
const s = JSON.parse(readFileSync17(`${home}/.pi/agent/settings.json`, "utf8"));
|
|
35132
35243
|
return s.defaultProvider && s.defaultModel ? `${s.defaultProvider}/${s.defaultModel}` : "";
|
|
35133
35244
|
} catch {
|
|
35134
35245
|
return "";
|
|
@@ -35176,6 +35287,16 @@ async function fetchPiModels(cli, env) {
|
|
|
35176
35287
|
}
|
|
35177
35288
|
});
|
|
35178
35289
|
}
|
|
35290
|
+
async function updatePiModelCatalog(cli, env) {
|
|
35291
|
+
return new Promise((resolve13) => {
|
|
35292
|
+
execCli(
|
|
35293
|
+
cli,
|
|
35294
|
+
PI_MODEL_UPDATE_ARGS,
|
|
35295
|
+
{ env, timeout: 6e4, maxBuffer: 1024 * 1024 },
|
|
35296
|
+
(error51) => resolve13(!error51)
|
|
35297
|
+
);
|
|
35298
|
+
});
|
|
35299
|
+
}
|
|
35179
35300
|
function decodeConfig6(raw) {
|
|
35180
35301
|
if (raw === null || raw === void 0) return { cli: "pi", fullAuto: false };
|
|
35181
35302
|
if (typeof raw !== "object") throw new Error("pi config must be an object");
|
|
@@ -35193,7 +35314,7 @@ function piEnvironment(source) {
|
|
|
35193
35314
|
for (const key of PROVIDER_CREDENTIAL_ENV) delete env[key];
|
|
35194
35315
|
return env;
|
|
35195
35316
|
}
|
|
35196
|
-
var DRIVER_KIND6, PI_ARGS, NODE_ENV_FLAG2, EMPTY3, PiDriver;
|
|
35317
|
+
var DRIVER_KIND6, PI_ARGS, PI_MODEL_UPDATE_ARGS, NODE_ENV_FLAG2, EMPTY3, PiDriver;
|
|
35197
35318
|
var init_pi = __esm({
|
|
35198
35319
|
"server/drivers/pi.ts"() {
|
|
35199
35320
|
"use strict";
|
|
@@ -35202,11 +35323,13 @@ var init_pi = __esm({
|
|
|
35202
35323
|
init_env_path();
|
|
35203
35324
|
init_procs();
|
|
35204
35325
|
init_proxy_paths();
|
|
35326
|
+
init_tool_summary();
|
|
35205
35327
|
init_contracts();
|
|
35206
35328
|
init_local_inject();
|
|
35207
35329
|
init_native();
|
|
35208
35330
|
DRIVER_KIND6 = "piAgent";
|
|
35209
35331
|
PI_ARGS = ["--mode", "rpc", "--no-session"];
|
|
35332
|
+
PI_MODEL_UPDATE_ARGS = ["update", "--models", "--no-approve"];
|
|
35210
35333
|
NODE_ENV_FLAG2 = { ELECTRON_RUN_AS_NODE: "1" };
|
|
35211
35334
|
EMPTY3 = { default: "", options: [] };
|
|
35212
35335
|
PiDriver = {
|
|
@@ -35227,9 +35350,9 @@ var init_pi = __esm({
|
|
|
35227
35350
|
async create(input) {
|
|
35228
35351
|
const { instanceId, config: config2 } = input;
|
|
35229
35352
|
const catalogEnv = piEnvironment({ ...process.env, ...input.environment });
|
|
35230
|
-
let
|
|
35231
|
-
const
|
|
35232
|
-
let base3 =
|
|
35353
|
+
let models2 = EMPTY3;
|
|
35354
|
+
const readModels = async () => {
|
|
35355
|
+
let base3 = models2;
|
|
35233
35356
|
try {
|
|
35234
35357
|
const resolved = await fetchPiModels(config2.cli, catalogEnv);
|
|
35235
35358
|
if (resolved.options.length) base3 = resolved;
|
|
@@ -35237,12 +35360,16 @@ var init_pi = __esm({
|
|
|
35237
35360
|
}
|
|
35238
35361
|
try {
|
|
35239
35362
|
const next = await applyPiLocalCatalog(base3, catalogEnv);
|
|
35240
|
-
if (next.options.length)
|
|
35363
|
+
if (next.options.length) models2 = next;
|
|
35241
35364
|
} catch {
|
|
35242
|
-
if (base3.options.length)
|
|
35365
|
+
if (base3.options.length) models2 = base3;
|
|
35243
35366
|
}
|
|
35244
35367
|
};
|
|
35245
|
-
|
|
35368
|
+
const refreshModels = async () => {
|
|
35369
|
+
await updatePiModelCatalog(config2.cli, catalogEnv);
|
|
35370
|
+
await readModels();
|
|
35371
|
+
};
|
|
35372
|
+
await readModels();
|
|
35246
35373
|
const listeners = /* @__PURE__ */ new Set();
|
|
35247
35374
|
const active = /* @__PURE__ */ new Map();
|
|
35248
35375
|
const emit = (event) => {
|
|
@@ -35274,9 +35401,9 @@ var init_pi = __esm({
|
|
|
35274
35401
|
const mcpServers = buildMcpServers(turn);
|
|
35275
35402
|
let mcpTempDir = null;
|
|
35276
35403
|
if (mcpServers) {
|
|
35277
|
-
mcpTempDir = mkdtempSync3(
|
|
35404
|
+
mcpTempDir = mkdtempSync3(join31(tmpdir4(), "omb-pi-mcp-"));
|
|
35278
35405
|
try {
|
|
35279
|
-
|
|
35406
|
+
writeFileSync12(join31(mcpTempDir, "mcp.json"), JSON.stringify({ mcpServers }), { mode: 384 });
|
|
35280
35407
|
} catch (err) {
|
|
35281
35408
|
try {
|
|
35282
35409
|
rmSync5(mcpTempDir, { recursive: true, force: true });
|
|
@@ -35294,7 +35421,7 @@ var init_pi = __esm({
|
|
|
35294
35421
|
env: piEnvironment({
|
|
35295
35422
|
...process.env,
|
|
35296
35423
|
...input.environment,
|
|
35297
|
-
...mcpServers && mcpTempDir ? { OMB_MCP_CONFIG:
|
|
35424
|
+
...mcpServers && mcpTempDir ? { OMB_MCP_CONFIG: join31(mcpTempDir, "mcp.json") } : {}
|
|
35298
35425
|
})
|
|
35299
35426
|
});
|
|
35300
35427
|
} catch (err) {
|
|
@@ -35406,7 +35533,9 @@ var init_pi = __esm({
|
|
|
35406
35533
|
type: "item.started",
|
|
35407
35534
|
itemType: "tool",
|
|
35408
35535
|
itemId: evt.toolCallId,
|
|
35409
|
-
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)
|
|
35410
35539
|
});
|
|
35411
35540
|
return;
|
|
35412
35541
|
}
|
|
@@ -35416,7 +35545,8 @@ var init_pi = __esm({
|
|
|
35416
35545
|
type: "item.completed",
|
|
35417
35546
|
itemType: "tool",
|
|
35418
35547
|
itemId: evt.toolCallId,
|
|
35419
|
-
ok: !evt.isError
|
|
35548
|
+
ok: !evt.isError,
|
|
35549
|
+
output: toolDetailPreview(evt.result)
|
|
35420
35550
|
});
|
|
35421
35551
|
return;
|
|
35422
35552
|
}
|
|
@@ -35565,7 +35695,7 @@ ${turn.text}` : turn.text;
|
|
|
35565
35695
|
displayName: input.displayName,
|
|
35566
35696
|
enabled: input.enabled,
|
|
35567
35697
|
get models() {
|
|
35568
|
-
return
|
|
35698
|
+
return models2;
|
|
35569
35699
|
},
|
|
35570
35700
|
refreshModels,
|
|
35571
35701
|
snapshot,
|
|
@@ -35631,16 +35761,16 @@ ${turn.text}` : turn.text;
|
|
|
35631
35761
|
});
|
|
35632
35762
|
|
|
35633
35763
|
// server/drivers/minimax.ts
|
|
35634
|
-
import { readFileSync as
|
|
35764
|
+
import { readFileSync as readFileSync18 } from "node:fs";
|
|
35635
35765
|
import { homedir as homedir22 } from "node:os";
|
|
35636
|
-
import { join as
|
|
35766
|
+
import { join as join32 } from "node:path";
|
|
35637
35767
|
function normalizedApiUrl(value) {
|
|
35638
35768
|
const root = value.trim().replace(/\/+$/, "");
|
|
35639
35769
|
return root.endsWith("/v1") ? root : `${root}/v1`;
|
|
35640
35770
|
}
|
|
35641
35771
|
function loadLocalMiniMaxConfig(home = homedir22()) {
|
|
35642
35772
|
try {
|
|
35643
|
-
const raw = localConfigSchema.parse(JSON.parse(
|
|
35773
|
+
const raw = localConfigSchema.parse(JSON.parse(readFileSync18(join32(home, ".mmx", "config.json"), "utf8")));
|
|
35644
35774
|
const configuredUrl = raw.base_url?.trim() ? raw.base_url : raw.region === "cn" ? CN_URL : DEFAULT_URL2;
|
|
35645
35775
|
return {
|
|
35646
35776
|
apiKey: raw.api_key?.trim() ?? "",
|
|
@@ -35703,13 +35833,13 @@ var init_minimax = __esm({
|
|
|
35703
35833
|
const local = loadLocalMiniMaxConfig();
|
|
35704
35834
|
const apiKey = input.environment[API_KEY_ENV]?.trim() || process.env[API_KEY_ENV]?.trim() || local.apiKey;
|
|
35705
35835
|
const apiUrl = input.config.url === DEFAULT_URL2 && local.url !== DEFAULT_URL2 ? local.url : input.config.url;
|
|
35706
|
-
const
|
|
35836
|
+
const models2 = local.defaultModel && MODELS4.options.some((model) => model.id === local.defaultModel) ? { ...MODELS4, default: local.defaultModel } : MODELS4;
|
|
35707
35837
|
return createOpenAIChatRuntime({
|
|
35708
35838
|
input,
|
|
35709
35839
|
driverKind: DRIVER_KIND7,
|
|
35710
35840
|
apiKey,
|
|
35711
35841
|
apiUrl,
|
|
35712
|
-
models: () =>
|
|
35842
|
+
models: () => models2,
|
|
35713
35843
|
requestBody: (model, messages, stream) => ({
|
|
35714
35844
|
model,
|
|
35715
35845
|
messages,
|
|
@@ -35854,16 +35984,16 @@ async function setupJson(url2, key, body) {
|
|
|
35854
35984
|
}
|
|
35855
35985
|
async function fetchSetupModels(url2, key) {
|
|
35856
35986
|
const json2 = record2(await setupJson(url2, key));
|
|
35857
|
-
const
|
|
35987
|
+
const models2 = /* @__PURE__ */ new Map();
|
|
35858
35988
|
for (const entry of Array.isArray(json2?.data) ? json2.data : []) {
|
|
35859
35989
|
const item = record2(entry);
|
|
35860
35990
|
const id = typeof item?.id === "string" ? item.id.trim() : "";
|
|
35861
|
-
if (!id || /[\u0000-\u001f\u007f]/u.test(id) ||
|
|
35991
|
+
if (!id || /[\u0000-\u001f\u007f]/u.test(id) || models2.has(id)) continue;
|
|
35862
35992
|
const name = typeof item?.name === "string" ? item.name.replace(/[\u0000-\u001f\u007f]/gu, "").trim() : "";
|
|
35863
|
-
|
|
35993
|
+
models2.set(id, { id, label: name || id });
|
|
35864
35994
|
}
|
|
35865
|
-
if (!
|
|
35866
|
-
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()];
|
|
35867
35997
|
}
|
|
35868
35998
|
async function verifySetupCompletion(url2, key, model, provider) {
|
|
35869
35999
|
const id = model.trim();
|
|
@@ -35907,8 +36037,8 @@ __export(cli_setup_exports, {
|
|
|
35907
36037
|
saveCliStartup: () => saveCliStartup
|
|
35908
36038
|
});
|
|
35909
36039
|
import { spawn as spawn7 } from "node:child_process";
|
|
35910
|
-
import { readFileSync as
|
|
35911
|
-
import { join as
|
|
36040
|
+
import { readFileSync as readFileSync19 } from "node:fs";
|
|
36041
|
+
import { join as join33, resolve as resolve10 } from "node:path";
|
|
35912
36042
|
import { randomUUID as randomUUID10 } from "node:crypto";
|
|
35913
36043
|
async function inspect(id, entry) {
|
|
35914
36044
|
const registry2 = new ProviderRegistry(BUILT_IN_DRIVERS);
|
|
@@ -35945,7 +36075,7 @@ function assertDataDir(dataDir) {
|
|
|
35945
36075
|
}
|
|
35946
36076
|
function checkStoredConfig(dataDir) {
|
|
35947
36077
|
try {
|
|
35948
|
-
parseStoredConfig(JSON.parse(
|
|
36078
|
+
parseStoredConfig(JSON.parse(readFileSync19(join33(dataDir, "config.json"), "utf8")));
|
|
35949
36079
|
} catch (error51) {
|
|
35950
36080
|
if (error51.code === "ENOENT") return;
|
|
35951
36081
|
throw new Error("Existing config.json could not be read or validated. Setup has not changed it; repair it before continuing.");
|
|
@@ -35977,14 +36107,14 @@ function saveCliStartup(dataDir, settings2) {
|
|
|
35977
36107
|
function rawObject(value) {
|
|
35978
36108
|
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
35979
36109
|
}
|
|
35980
|
-
async function chooseModel(io,
|
|
35981
|
-
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.");
|
|
35982
36112
|
let query = "";
|
|
35983
36113
|
for (; ; ) {
|
|
35984
|
-
const matches =
|
|
35985
|
-
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);
|
|
35986
36116
|
const visible = (preferred ? [preferred, ...matches.filter((m) => m !== preferred)] : matches).slice(0, 20);
|
|
35987
|
-
const defaultIndex = visible.findIndex((m) => m.id ===
|
|
36117
|
+
const defaultIndex = visible.findIndex((m) => m.id === models2.default);
|
|
35988
36118
|
const selected = await io.choose("Choose your model", [
|
|
35989
36119
|
...visible.map((m) => m.label.trim() || m.id),
|
|
35990
36120
|
"Search models\u2026"
|
|
@@ -36058,11 +36188,11 @@ async function runSetup(options, io = defaultSetupIo(), deps = dependencies) {
|
|
|
36058
36188
|
id = prior?.[0] ?? native.id;
|
|
36059
36189
|
if (!prior && runtime[id] && runtime[id].driver !== native.driver) id = `${native.id}-${randomUUID10().slice(0, 8)}`;
|
|
36060
36190
|
entry = { ...cfg.instances?.[id] ?? { driver: native.driver }, enabled: true };
|
|
36061
|
-
const
|
|
36191
|
+
const models2 = await connectNative(native, id, entry, io, deps);
|
|
36062
36192
|
const saved = cfg.defaultModelSelection;
|
|
36063
36193
|
model = await chooseModel(io, {
|
|
36064
|
-
...
|
|
36065
|
-
default: saved?.instanceId === id ? saved.model :
|
|
36194
|
+
...models2,
|
|
36195
|
+
default: saved?.instanceId === id ? saved.model : models2.default
|
|
36066
36196
|
});
|
|
36067
36197
|
} else {
|
|
36068
36198
|
io.log("Connect an API key");
|
|
@@ -36092,21 +36222,21 @@ async function runSetup(options, io = defaultSetupIo(), deps = dependencies) {
|
|
|
36092
36222
|
}
|
|
36093
36223
|
if (!key) throw new Error("An API key is required. Nothing was saved.");
|
|
36094
36224
|
io.log("Checking the model catalog\u2026");
|
|
36095
|
-
let
|
|
36225
|
+
let models2;
|
|
36096
36226
|
try {
|
|
36097
|
-
|
|
36227
|
+
models2 = await deps.models(url2, key);
|
|
36098
36228
|
} catch (error51) {
|
|
36099
36229
|
if (error51 instanceof SetupCancelled) throw error51;
|
|
36100
36230
|
io.log(error51 instanceof Error ? error51.message : "Could not load this model catalog.");
|
|
36101
36231
|
if (!await io.confirm("Enter an exact chat model ID and check it directly instead?", false)) throw error51;
|
|
36102
36232
|
const manual = (await io.ask("Chat model ID: ")).trim();
|
|
36103
36233
|
if (!manual) throw new Error("A model ID is required. Nothing was saved.");
|
|
36104
|
-
|
|
36234
|
+
models2 = [{ id: manual, label: manual }];
|
|
36105
36235
|
}
|
|
36106
36236
|
const previousModel = prior ? rawObject(prior[1].config).model : void 0;
|
|
36107
36237
|
const preferredModel = cfg.defaultModelSelection?.instanceId === prior?.[0] ? cfg.defaultModelSelection?.model : previousModel;
|
|
36108
36238
|
io.log("Choose a chat model; image, audio and embedding-only models cannot reply here.");
|
|
36109
|
-
model = await chooseModel(io, { default: typeof preferredModel === "string" ? preferredModel : "", options:
|
|
36239
|
+
model = await chooseModel(io, { default: typeof preferredModel === "string" ? preferredModel : "", options: models2 });
|
|
36110
36240
|
if (!await io.confirm("Send one short test message? Your API provider may charge for this request.", true)) throw new SetupCancelled();
|
|
36111
36241
|
if (routingProvider) await deps.verify(url2, key, model, routingProvider);
|
|
36112
36242
|
else await deps.verify(url2, key, model);
|
|
@@ -36223,7 +36353,7 @@ var init_browser_engine_release = __esm({
|
|
|
36223
36353
|
});
|
|
36224
36354
|
|
|
36225
36355
|
// server/browser-bundle-release.ts
|
|
36226
|
-
import { join as
|
|
36356
|
+
import { join as join34 } from "node:path";
|
|
36227
36357
|
function browserBundleSpec(target) {
|
|
36228
36358
|
if (!Object.hasOwn(CHROME_ASSETS, target)) throw new Error(`Unsupported desktop browser target: ${target}`);
|
|
36229
36359
|
const pinned = CHROME_ASSETS[target];
|
|
@@ -36259,10 +36389,10 @@ function browserBundlePaths(bundleDirectory, target) {
|
|
|
36259
36389
|
const spec = browserBundleSpec(target);
|
|
36260
36390
|
return {
|
|
36261
36391
|
directory: bundleDirectory,
|
|
36262
|
-
manifest:
|
|
36263
|
-
engine:
|
|
36264
|
-
chrome:
|
|
36265
|
-
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")
|
|
36266
36396
|
};
|
|
36267
36397
|
}
|
|
36268
36398
|
var CHROME_VERSION, CHROME_ASSETS;
|
|
@@ -36302,7 +36432,7 @@ var init_bot_profile = __esm({
|
|
|
36302
36432
|
});
|
|
36303
36433
|
|
|
36304
36434
|
// server/bot-folder.ts
|
|
36305
|
-
import { join as
|
|
36435
|
+
import { join as join35 } from "node:path";
|
|
36306
36436
|
var BOTS_DIR;
|
|
36307
36437
|
var init_bot_folder = __esm({
|
|
36308
36438
|
"server/bot-folder.ts"() {
|
|
@@ -36310,7 +36440,7 @@ var init_bot_folder = __esm({
|
|
|
36310
36440
|
init_atomic();
|
|
36311
36441
|
init_config();
|
|
36312
36442
|
init_bot_profile();
|
|
36313
|
-
BOTS_DIR =
|
|
36443
|
+
BOTS_DIR = join35(DATA_DIR, "bots");
|
|
36314
36444
|
}
|
|
36315
36445
|
});
|
|
36316
36446
|
|
|
@@ -36347,9 +36477,9 @@ __export(browser_engine_exports, {
|
|
|
36347
36477
|
});
|
|
36348
36478
|
import { spawn as spawn8 } from "node:child_process";
|
|
36349
36479
|
import { createHash as createHash8, randomBytes as randomBytes3, randomUUID as randomUUID11 } from "node:crypto";
|
|
36350
|
-
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";
|
|
36351
36481
|
import { homedir as homedir23, tmpdir as tmpdir5 } from "node:os";
|
|
36352
|
-
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";
|
|
36353
36483
|
function browserRestoreKey(session) {
|
|
36354
36484
|
if (!/^[A-Za-z0-9_-]{1,96}$/.test(session)) throw new Error("Invalid browser session.");
|
|
36355
36485
|
return `omb-${createHash8("sha256").update(session).digest("hex")}`;
|
|
@@ -36358,20 +36488,20 @@ function browserSessionsDirectory(env) {
|
|
|
36358
36488
|
const namespace = env.AGENT_BROWSER_NAMESPACE;
|
|
36359
36489
|
if (namespace && !/^[a-z0-9]+(?:[-_][a-z0-9]+)*$/.test(namespace)) throw new Error("Invalid browser namespace.");
|
|
36360
36490
|
const home = (process.platform === "win32" ? env.USERPROFILE : env.HOME) || homedir23();
|
|
36361
|
-
const stateRoot = namespace ?
|
|
36362
|
-
return
|
|
36491
|
+
const stateRoot = namespace ? join36(home, ".agent-browser", "namespaces", namespace, "state") : join36(home, ".agent-browser");
|
|
36492
|
+
return join36(stateRoot, "sessions");
|
|
36363
36493
|
}
|
|
36364
36494
|
function managedBrowserConfigPath(env) {
|
|
36365
|
-
return
|
|
36495
|
+
return join36(browserSessionsDirectory(env), "..", "omb-managed-config.json");
|
|
36366
36496
|
}
|
|
36367
36497
|
function ensureManagedBrowserConfig(env) {
|
|
36368
36498
|
const path3 = managedBrowserConfigPath(env);
|
|
36369
36499
|
mkdirSync16(dirname11(path3), { recursive: true, mode: 448 });
|
|
36370
36500
|
try {
|
|
36371
|
-
|
|
36501
|
+
writeFileSync13(path3, "{}\n", { flag: "wx", mode: 384 });
|
|
36372
36502
|
} catch (error51) {
|
|
36373
36503
|
if (error51.code !== "EEXIST") throw error51;
|
|
36374
|
-
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.");
|
|
36375
36505
|
}
|
|
36376
36506
|
return path3;
|
|
36377
36507
|
}
|
|
@@ -36449,11 +36579,11 @@ async function prepareBrowserSessionState(binaryPath, session, options = {}) {
|
|
|
36449
36579
|
ensureManagedBrowserConfig(env);
|
|
36450
36580
|
if (options.persistent === false) return;
|
|
36451
36581
|
const directory = browserSessionsDirectory(env);
|
|
36452
|
-
const key =
|
|
36582
|
+
const key = join36(directory, session);
|
|
36453
36583
|
let pending = preparedBrowserSessions.get(key);
|
|
36454
36584
|
if (!pending) {
|
|
36455
36585
|
pending = (async () => {
|
|
36456
|
-
const targetBase =
|
|
36586
|
+
const targetBase = join36(directory, `${restoreKey}-${session}`);
|
|
36457
36587
|
const hasTarget = () => existsSync18(`${targetBase}.json.enc`) || existsSync18(`${targetBase}.json`);
|
|
36458
36588
|
if (hasTarget()) return;
|
|
36459
36589
|
check2();
|
|
@@ -36463,7 +36593,7 @@ async function prepareBrowserSessionState(binaryPath, session, options = {}) {
|
|
|
36463
36593
|
check2();
|
|
36464
36594
|
if (hasTarget()) return;
|
|
36465
36595
|
const candidates = [".json.enc", ".json"].flatMap((suffix) => {
|
|
36466
|
-
const path3 =
|
|
36596
|
+
const path3 = join36(directory, `${session}-${session}${suffix}`);
|
|
36467
36597
|
try {
|
|
36468
36598
|
const stat4 = statSync3(path3);
|
|
36469
36599
|
if (!stat4.isFile()) throw new Error("Saved browser state is not a file.");
|
|
@@ -36498,7 +36628,7 @@ function isMusl(platform = process.platform, exists = existsSync18) {
|
|
|
36498
36628
|
}
|
|
36499
36629
|
function pinnedBinaryPath(dataDir = DATA_DIR, platform = process.platform, arch = process.arch) {
|
|
36500
36630
|
const version2 = agentBrowserReleaseVersion(resolveAgentBrowserReleaseAsset(platform, arch));
|
|
36501
|
-
return
|
|
36631
|
+
return join36(dataDir, ENGINE_DIR, version2, executableName(platform));
|
|
36502
36632
|
}
|
|
36503
36633
|
function onPath(env, platform, exists) {
|
|
36504
36634
|
const pathValue = platform === "win32" ? Object.entries(env).findLast(([key]) => key.toUpperCase() === "PATH")?.[1] : env.PATH;
|
|
@@ -36514,7 +36644,7 @@ function packagedBrowser(options) {
|
|
|
36514
36644
|
const resources = (options.env ?? process.env).OMB_RESOURCES_PATH;
|
|
36515
36645
|
if (!resources) return null;
|
|
36516
36646
|
try {
|
|
36517
|
-
return browserBundlePaths(
|
|
36647
|
+
return browserBundlePaths(join36(resolve11(resources), "browser-engine"), `${options.platform ?? process.platform}-${options.arch ?? process.arch}`);
|
|
36518
36648
|
} catch {
|
|
36519
36649
|
return null;
|
|
36520
36650
|
}
|
|
@@ -36539,7 +36669,7 @@ async function installAgentBrowserBinary(options = {}) {
|
|
|
36539
36669
|
const asset = options.asset ?? resolveAgentBrowserReleaseAsset(platform, options.arch ?? process.arch, options.musl ?? isMusl(platform));
|
|
36540
36670
|
if (!asset) throw new Error(`agent-browser publishes no build for ${platform}-${options.arch ?? process.arch}.`);
|
|
36541
36671
|
const destination = pinnedBinaryPath(options.dataDir, platform, options.arch);
|
|
36542
|
-
const directory =
|
|
36672
|
+
const directory = join36(destination, "..");
|
|
36543
36673
|
mkdirSync16(directory, { recursive: true, mode: 448 });
|
|
36544
36674
|
const url2 = agentBrowserReleaseUrl(asset);
|
|
36545
36675
|
options.log?.(`downloading agent-browser ${agentBrowserReleaseVersion(asset)} (${Math.round(asset.bytes / 1024 / 1024)} MB, digest pinned)`);
|
|
@@ -36558,7 +36688,7 @@ async function installAgentBrowserBinary(options = {}) {
|
|
|
36558
36688
|
const digest2 = createHash8("sha256").update(body).digest("hex");
|
|
36559
36689
|
if (digest2 !== asset.sha256) throw new Error("the agent-browser download failed its SHA-256 check; nothing was installed");
|
|
36560
36690
|
const staging = `${destination}.${randomBytes3(6).toString("hex")}.part`;
|
|
36561
|
-
|
|
36691
|
+
writeFileSync13(staging, body, { mode: 493 });
|
|
36562
36692
|
if (platform !== "win32") chmodSync7(staging, 493);
|
|
36563
36693
|
renameSync5(staging, destination);
|
|
36564
36694
|
return destination;
|
|
@@ -36591,9 +36721,9 @@ function ensureChrome(binaryPath, options = {}) {
|
|
|
36591
36721
|
});
|
|
36592
36722
|
}
|
|
36593
36723
|
function browserEngineEncryptionKey(dataDir = DATA_DIR) {
|
|
36594
|
-
const file2 =
|
|
36724
|
+
const file2 = join36(dataDir, KEY_FILE);
|
|
36595
36725
|
try {
|
|
36596
|
-
const existing =
|
|
36726
|
+
const existing = readFileSync20(file2, "utf8").trim();
|
|
36597
36727
|
if (/^[0-9a-f]{64}$/u.test(existing)) return existing;
|
|
36598
36728
|
} catch {
|
|
36599
36729
|
}
|
|
@@ -36651,7 +36781,7 @@ function agentBrowserIntegration(input) {
|
|
|
36651
36781
|
return { command: input.binaryPath, args: ["mcp", "--tools", "core", "--no-webmcp"], env };
|
|
36652
36782
|
}
|
|
36653
36783
|
function agentBrowserFrame(input) {
|
|
36654
|
-
const file2 =
|
|
36784
|
+
const file2 = join36(tmpdir5(), `openmausbot-browser-${randomUUID11()}.png`);
|
|
36655
36785
|
return new Promise((settle, fail) => {
|
|
36656
36786
|
const child = spawn8(input.binaryPath, ["screenshot", file2], {
|
|
36657
36787
|
env: browserRuntimeEnv(input.env),
|
|
@@ -36673,7 +36803,7 @@ function agentBrowserFrame(input) {
|
|
|
36673
36803
|
fail(error51);
|
|
36674
36804
|
return;
|
|
36675
36805
|
}
|
|
36676
|
-
settle({ png:
|
|
36806
|
+
settle({ png: readFileSync20(file2).toString("base64"), format: "png" });
|
|
36677
36807
|
} catch {
|
|
36678
36808
|
fail(new Error("the browser reported a picture it did not write"));
|
|
36679
36809
|
} finally {
|
|
@@ -36721,8 +36851,8 @@ async function clearBrowserSessionState(binaryPath, session, options = {}) {
|
|
|
36721
36851
|
}
|
|
36722
36852
|
};
|
|
36723
36853
|
try {
|
|
36724
|
-
for (const base2 of bases) for (const suffix of [".json", ".json.enc", ".json.previous", ".json.enc.previous"]) remove(
|
|
36725
|
-
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");
|
|
36726
36856
|
let pendingFiles = [];
|
|
36727
36857
|
try {
|
|
36728
36858
|
pendingFiles = readdirSync3(pendingDirectory);
|
|
@@ -36730,8 +36860,8 @@ async function clearBrowserSessionState(binaryPath, session, options = {}) {
|
|
|
36730
36860
|
if (error51.code !== "ENOENT") throw error51;
|
|
36731
36861
|
}
|
|
36732
36862
|
const candidates = bases.map((base2) => new RegExp(`^${base2}-candidate-[0-9]+\\.json(?:\\.enc)?$`));
|
|
36733
|
-
for (const name of pendingFiles) if (candidates.some((candidate) => candidate.test(name))) remove(
|
|
36734
|
-
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));
|
|
36735
36865
|
return true;
|
|
36736
36866
|
} catch {
|
|
36737
36867
|
return false;
|
|
@@ -36759,9 +36889,9 @@ var init_browser_engine = __esm({
|
|
|
36759
36889
|
// server/cli.ts
|
|
36760
36890
|
var import_qrcode_terminal = __toESM(require_main(), 1);
|
|
36761
36891
|
import { spawn as spawn9 } from "node:child_process";
|
|
36762
|
-
import { closeSync as
|
|
36892
|
+
import { closeSync as closeSync4, existsSync as existsSync19, mkdirSync as mkdirSync17, openSync as openSync4, readFileSync as readFileSync21 } from "node:fs";
|
|
36763
36893
|
import { homedir as homedir24 } from "node:os";
|
|
36764
|
-
import { dirname as dirname12, join as
|
|
36894
|
+
import { dirname as dirname12, join as join37, resolve as resolve12 } from "node:path";
|
|
36765
36895
|
import { createInterface as createInterface2 } from "node:readline/promises";
|
|
36766
36896
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
36767
36897
|
|
|
@@ -38533,8 +38663,9 @@ function runServiceCommand(input, io) {
|
|
|
38533
38663
|
}
|
|
38534
38664
|
|
|
38535
38665
|
// server/fleet-cli.ts
|
|
38536
|
-
|
|
38537
|
-
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";
|
|
38538
38669
|
import { posix as posix3 } from "node:path";
|
|
38539
38670
|
|
|
38540
38671
|
// server/fleet.ts
|
|
@@ -38626,8 +38757,9 @@ function templateUnit(spec) {
|
|
|
38626
38757
|
"# Written by `openmausbot fleet init`. One unit for every workspace: %i is the slug.",
|
|
38627
38758
|
"[Unit]",
|
|
38628
38759
|
"Description=OpenMausBot workspace %i",
|
|
38629
|
-
"After=network-online.target",
|
|
38760
|
+
"After=network-online.target openmausbot-fence.service",
|
|
38630
38761
|
"Wants=network-online.target",
|
|
38762
|
+
"Requires=openmausbot-fence.service",
|
|
38631
38763
|
"",
|
|
38632
38764
|
"[Service]",
|
|
38633
38765
|
"Type=simple",
|
|
@@ -38659,7 +38791,6 @@ function fenceUnit(layout = fleetLayout()) {
|
|
|
38659
38791
|
"# Written by `openmausbot fleet init`: keeps each workspace's loopback ports to its own user.",
|
|
38660
38792
|
"[Unit]",
|
|
38661
38793
|
"Description=OpenMausBot per-workspace loopback fence",
|
|
38662
|
-
"Before=openmausbot@.service",
|
|
38663
38794
|
"",
|
|
38664
38795
|
"[Service]",
|
|
38665
38796
|
"Type=oneshot",
|
|
@@ -38674,14 +38805,16 @@ function fenceUnit(layout = fleetLayout()) {
|
|
|
38674
38805
|
function fenceRules(workspaces) {
|
|
38675
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;"];
|
|
38676
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;
|
|
38677
38809
|
lines.push(` oif lo tcp dport { ${workspace.port}, ${workspace.webhookPort} } meta skuid != { ${fleetUser(workspace.slug)}, caddy, root } reject`);
|
|
38678
38810
|
}
|
|
38679
38811
|
lines.push(" }", "}", "");
|
|
38680
38812
|
return lines.join("\n");
|
|
38681
38813
|
}
|
|
38682
38814
|
function caddySite(workspace) {
|
|
38683
|
-
if (workspace.status
|
|
38684
|
-
|
|
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");
|
|
38685
38818
|
}
|
|
38686
38819
|
return [
|
|
38687
38820
|
`${workspace.host} {`,
|
|
@@ -38709,11 +38842,23 @@ function instanceEnv(input) {
|
|
|
38709
38842
|
`OMB_PUBLIC_URL=https://${input.workspace.host}`
|
|
38710
38843
|
];
|
|
38711
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}`);
|
|
38712
38846
|
return lines.join("\n") + "\n";
|
|
38713
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
|
+
}
|
|
38714
38858
|
function initialConfig(seed) {
|
|
38715
38859
|
const config2 = { signIn: { admins: seed.admins, members: seed.members } };
|
|
38716
|
-
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]}` };
|
|
38717
38862
|
if (seed.monthlyCapUsd !== void 0) config2.budgets = { monthlyUsd: seed.monthlyCapUsd };
|
|
38718
38863
|
return `${JSON.stringify(config2, null, 2)}
|
|
38719
38864
|
`;
|
|
@@ -38757,6 +38902,18 @@ function initPlan(input) {
|
|
|
38757
38902
|
}
|
|
38758
38903
|
function createPlan(input) {
|
|
38759
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
|
+
}
|
|
38760
38917
|
if (input.registry.workspaces[input.slug]) throw new Error(`workspace "${input.slug}" already exists`);
|
|
38761
38918
|
if (!input.seed.admins.length) throw new Error("a workspace needs at least one admin email (--admin)");
|
|
38762
38919
|
for (const entry of [...input.seed.admins, ...input.seed.members]) assertEmailOrDomain(entry);
|
|
@@ -38772,15 +38929,36 @@ function createPlan(input) {
|
|
|
38772
38929
|
createdAt: (input.now ?? /* @__PURE__ */ new Date()).toISOString()
|
|
38773
38930
|
};
|
|
38774
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
|
+
});
|
|
38775
38936
|
const user = fleetUser(workspace.slug);
|
|
38776
38937
|
const home = workspaceHome(layout, workspace.slug);
|
|
38777
38938
|
const dataDir = workspaceDataDir(layout, workspace.slug);
|
|
38778
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 },
|
|
38779
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 },
|
|
38780
38947
|
{ kind: "mkdir", path: dataDir, mode: 448, owner: user },
|
|
38781
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
|
+
] : [],
|
|
38782
38960
|
...input.seed.brandJson ? [{ kind: "write", path: join3(dataDir, "brand.json"), content: input.seed.brandJson, mode: 384, owner: user }] : [],
|
|
38783
|
-
{ 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 },
|
|
38784
38962
|
...input.memoryMax ? [
|
|
38785
38963
|
{ kind: "mkdir", path: join3(layout.unitFile.replace(/openmausbot@\.service$/, ""), `openmausbot@${workspace.slug}.service.d`), mode: 493 },
|
|
38786
38964
|
{ kind: "write", path: join3(layout.unitFile.replace(/openmausbot@\.service$/, ""), `openmausbot@${workspace.slug}.service.d`, "limits.conf"), content: `[Service]
|
|
@@ -38800,10 +38978,14 @@ MemoryMax=${input.memoryMax}
|
|
|
38800
38978
|
];
|
|
38801
38979
|
return { steps, registry: registry2, workspace };
|
|
38802
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
|
+
}
|
|
38803
38984
|
function withStatus(registry2, slug, status) {
|
|
38804
38985
|
assertSlug(slug);
|
|
38805
38986
|
const current = registry2.workspaces[slug];
|
|
38806
38987
|
if (!current) throw new Error(`no workspace "${slug}"`);
|
|
38988
|
+
assertManagedWorkspace(current);
|
|
38807
38989
|
const workspace = { ...current, status };
|
|
38808
38990
|
return { registry: { ...registry2, workspaces: { ...registry2.workspaces, [slug]: workspace } }, workspace };
|
|
38809
38991
|
}
|
|
@@ -38841,8 +39023,9 @@ function deletePlan(input) {
|
|
|
38841
39023
|
const layout = input.layout ?? fleetLayout();
|
|
38842
39024
|
const workspace = input.registry.workspaces[input.slug];
|
|
38843
39025
|
if (!workspace) throw new Error(`no workspace "${input.slug}"`);
|
|
39026
|
+
assertManagedWorkspace(workspace);
|
|
38844
39027
|
const { [input.slug]: _gone, ...rest } = input.registry.workspaces;
|
|
38845
|
-
const registry2 = { ...input.registry, workspaces: rest };
|
|
39028
|
+
const registry2 = { ...input.registry, workspaces: input.keepData ? { ...rest, [input.slug]: { ...workspace, status: "retained" } } : rest };
|
|
38846
39029
|
const unitsDir = layout.unitFile.replace(/openmausbot@\.service$/, "");
|
|
38847
39030
|
return {
|
|
38848
39031
|
registry: registry2,
|
|
@@ -38853,8 +39036,10 @@ function deletePlan(input) {
|
|
|
38853
39036
|
{ kind: "remove", path: join3(layout.instancesDir, `${workspace.slug}.env`) },
|
|
38854
39037
|
{ kind: "remove", path: join3(unitsDir, `openmausbot@${workspace.slug}.service.d`, "limits.conf") },
|
|
38855
39038
|
{ kind: "write", path: layout.fenceFile, content: fenceRules(Object.values(registry2.workspaces)), mode: 384 },
|
|
38856
|
-
{ kind: "run", argv: ["nft", "-f", layout.fenceFile], why: "drop its fence rule" },
|
|
38857
|
-
|
|
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" }],
|
|
38858
39043
|
{ kind: "write", path: layout.registryFile, content: `${JSON.stringify(registry2, null, 2)}
|
|
38859
39044
|
`, mode: 384 }
|
|
38860
39045
|
]
|
|
@@ -38881,10 +39066,12 @@ function applySignIn(configText, action, email3, chatOnly) {
|
|
|
38881
39066
|
const members = without(list(current.members));
|
|
38882
39067
|
if (action === "remove") {
|
|
38883
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");
|
|
38884
39070
|
return { config: `${JSON.stringify({ ...raw, signIn: { admins, members } }, null, 2)}
|
|
38885
|
-
`, summary: `${entry}
|
|
39071
|
+
`, summary: `${entry} was removed from the allow-list; existing email sessions are rechecked against the remaining entries` };
|
|
38886
39072
|
}
|
|
38887
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");
|
|
38888
39075
|
return { config: `${JSON.stringify({ ...raw, signIn: next }, null, 2)}
|
|
38889
39076
|
`, summary: `${entry} can sign in with an emailed code (${chatOnly ? "chat and approvals" : "full access"})` };
|
|
38890
39077
|
}
|
|
@@ -38896,11 +39083,14 @@ function describeSteps(steps) {
|
|
|
38896
39083
|
for (const step of steps) {
|
|
38897
39084
|
switch (step.kind) {
|
|
38898
39085
|
case "mkdir":
|
|
38899
|
-
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)}`);
|
|
38900
39087
|
break;
|
|
38901
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
|
+
}
|
|
38902
39093
|
lines.push(`cat > ${shellQuote(step.path)} <<'OMB_EOF'`, step.content.replace(/\n$/, ""), "OMB_EOF", `chmod ${step.mode.toString(8)} ${shellQuote(step.path)}`);
|
|
38903
|
-
if (step.owner) lines.push(`chown ${step.owner}:${step.owner} ${shellQuote(step.path)}`);
|
|
38904
39094
|
break;
|
|
38905
39095
|
case "append-once":
|
|
38906
39096
|
lines.push(`grep -qxF ${shellQuote(step.line)} ${shellQuote(step.path)} || printf '\\n%s\\n' ${shellQuote(step.line)} >> ${shellQuote(step.path)}`);
|
|
@@ -38923,6 +39113,113 @@ function describeSteps(steps) {
|
|
|
38923
39113
|
}
|
|
38924
39114
|
|
|
38925
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
|
+
}
|
|
38926
39223
|
function defaultFleetDeps() {
|
|
38927
39224
|
return {
|
|
38928
39225
|
isRoot: () => typeof process.getuid === "function" && process.getuid() === 0,
|
|
@@ -38942,9 +39239,37 @@ function defaultFleetDeps() {
|
|
|
38942
39239
|
resolve13({ code: null, output: error51 instanceof Error ? error51.message : String(error51) });
|
|
38943
39240
|
}
|
|
38944
39241
|
}),
|
|
38945
|
-
|
|
38946
|
-
|
|
38947
|
-
|
|
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
|
+
},
|
|
38948
39273
|
appendOnce: (path3, line) => {
|
|
38949
39274
|
const current = existsSync2(path3) ? readFileSync(path3, "utf8") : "";
|
|
38950
39275
|
if (!current.split("\n").some((existing) => existing.trim() === line)) appendFileSync(path3, `${current.endsWith("\n") || !current ? "" : "\n"}
|
|
@@ -38971,18 +39296,10 @@ async function executePlan(steps, deps, io) {
|
|
|
38971
39296
|
for (const step of steps) {
|
|
38972
39297
|
switch (step.kind) {
|
|
38973
39298
|
case "mkdir":
|
|
38974
|
-
deps.mkdir(step.path, step.mode);
|
|
38975
|
-
if (step.owner) {
|
|
38976
|
-
const chown = await deps.run(["chown", `${step.owner}:${step.owner}`, step.path]);
|
|
38977
|
-
if (chown.code !== 0) return failed(io, `chown ${step.path}`, chown.output);
|
|
38978
|
-
}
|
|
39299
|
+
deps.mkdir(step.path, step.mode, step.owner);
|
|
38979
39300
|
break;
|
|
38980
39301
|
case "write":
|
|
38981
|
-
deps.writeText(step.path, step.content, step.mode);
|
|
38982
|
-
if (step.owner) {
|
|
38983
|
-
const chown = await deps.run(["chown", `${step.owner}:${step.owner}`, step.path]);
|
|
38984
|
-
if (chown.code !== 0) return failed(io, `chown ${step.path}`, chown.output);
|
|
38985
|
-
}
|
|
39302
|
+
deps.writeText(step.path, step.content, step.mode, step.owner);
|
|
38986
39303
|
break;
|
|
38987
39304
|
case "append-once":
|
|
38988
39305
|
deps.appendOnce(step.path, step.line);
|
|
@@ -39025,19 +39342,28 @@ function planFleetAction(input, deps) {
|
|
|
39025
39342
|
if (warning) throw new Error(warning);
|
|
39026
39343
|
const existing = deps.readText(layout.registryFile);
|
|
39027
39344
|
if (existing !== null && !input.yes) throw new Error(`${layout.registryFile} exists; pass --yes to rewrite the template unit and fence (workspaces are kept)`);
|
|
39028
|
-
const
|
|
39029
|
-
|
|
39030
|
-
|
|
39031
|
-
plan.registry.workspaces
|
|
39032
|
-
plan.
|
|
39033
|
-
|
|
39034
|
-
` }
|
|
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
|
+
});
|
|
39035
39356
|
}
|
|
39036
39357
|
return plan.steps;
|
|
39037
39358
|
}
|
|
39038
39359
|
case "create": {
|
|
39039
39360
|
if (!input.slug) throw new Error("fleet create needs a workspace name");
|
|
39361
|
+
assertSlug(input.slug);
|
|
39040
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
|
+
}
|
|
39041
39367
|
const brandJson = input.brandJson ?? (input.brandFile ? deps.readText(input.brandFile) : void 0);
|
|
39042
39368
|
if (input.brandFile && brandJson === null) throw new Error(`${input.brandFile} not found`);
|
|
39043
39369
|
if (brandJson) JSON.parse(brandJson);
|
|
@@ -39046,19 +39372,60 @@ function planFleetAction(input, deps) {
|
|
|
39046
39372
|
return createPlan({
|
|
39047
39373
|
registry: registry2,
|
|
39048
39374
|
slug: input.slug,
|
|
39049
|
-
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 },
|
|
39050
39376
|
...input.licenseKey ? { licenseKey: input.licenseKey } : {},
|
|
39051
39377
|
...input.memory ? { memoryMax: input.memory } : {},
|
|
39052
39378
|
layout
|
|
39053
39379
|
}).steps;
|
|
39054
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
|
+
}
|
|
39055
39421
|
case "users": {
|
|
39056
39422
|
if (!input.slug || !input.userAction || !input.email) throw new Error("fleet users needs: NAME add|remove EMAIL [--chat-only]");
|
|
39057
39423
|
assertSlug(input.slug);
|
|
39058
39424
|
const registry2 = loadRegistry(layout, deps);
|
|
39059
39425
|
if (!registry2.workspaces[input.slug]) throw new Error(`no workspace "${input.slug}"`);
|
|
39426
|
+
assertManagedWorkspace(registry2.workspaces[input.slug]);
|
|
39060
39427
|
const file2 = posix3.join(workspaceDataDir(layout, input.slug), "config.json");
|
|
39061
|
-
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));
|
|
39062
39429
|
return [
|
|
39063
39430
|
{ kind: "write", path: file2, content: next.config, mode: 384, owner: fleetUser(input.slug) },
|
|
39064
39431
|
{ kind: "note", text: next.summary }
|
|
@@ -39072,7 +39439,7 @@ function planFleetAction(input, deps) {
|
|
|
39072
39439
|
}
|
|
39073
39440
|
case "delete": {
|
|
39074
39441
|
if (!input.slug) throw new Error("fleet delete needs a workspace name");
|
|
39075
|
-
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"}`);
|
|
39076
39443
|
const registry2 = loadRegistry(layout, deps);
|
|
39077
39444
|
return deletePlan({ registry: registry2, slug: input.slug, keepData: input.keepData, layout }).steps;
|
|
39078
39445
|
}
|
|
@@ -39082,8 +39449,18 @@ function planFleetAction(input, deps) {
|
|
|
39082
39449
|
return [];
|
|
39083
39450
|
}
|
|
39084
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
|
+
}
|
|
39085
39462
|
function printPlan(steps, io, reason) {
|
|
39086
|
-
io.log(`${reason};
|
|
39463
|
+
io.log(`${reason}; inspect this plan, then rerun the fleet command as root without --dry-run (with --yes where required):`);
|
|
39087
39464
|
io.log("");
|
|
39088
39465
|
for (const line of describeSteps(steps)) io.log(line);
|
|
39089
39466
|
return 0;
|
|
@@ -39100,13 +39477,21 @@ async function runFleetCommand(input, io, deps = defaultFleetDeps()) {
|
|
|
39100
39477
|
return 0;
|
|
39101
39478
|
}
|
|
39102
39479
|
for (const workspace of rows) {
|
|
39103
|
-
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;
|
|
39104
39481
|
io.log(`${workspace.slug.padEnd(24)} https://${workspace.host.padEnd(40)} :${String(workspace.port).padEnd(6)} ${live}`);
|
|
39105
39482
|
}
|
|
39106
39483
|
return 0;
|
|
39107
39484
|
}
|
|
39108
39485
|
const steps = planFleetAction(input, deps);
|
|
39109
|
-
|
|
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
|
+
}
|
|
39110
39495
|
} catch (error51) {
|
|
39111
39496
|
io.error(`fleet: ${error51 instanceof Error ? error51.message : String(error51)}`);
|
|
39112
39497
|
return 2;
|
|
@@ -39117,147 +39502,6 @@ async function runFleetCommand(input, io, deps = defaultFleetDeps()) {
|
|
|
39117
39502
|
import { appendFileSync as appendFileSync2, chmodSync as chmodSync2, existsSync as existsSync3, mkdirSync as mkdirSync4, rmSync as rmSync3 } from "node:fs";
|
|
39118
39503
|
import { createServer } from "node:http";
|
|
39119
39504
|
import { dirname as dirname2 } from "node:path";
|
|
39120
|
-
|
|
39121
|
-
// server/usage-ledger.ts
|
|
39122
|
-
import { readFileSync as readFileSync2 } from "node:fs";
|
|
39123
|
-
import { join as join4 } from "node:path";
|
|
39124
|
-
|
|
39125
|
-
// server/prices.ts
|
|
39126
|
-
function priceFor(turn, prices) {
|
|
39127
|
-
return prices[`${turn.driverKind}/${turn.model}`] ?? prices[turn.model] ?? prices.default ?? null;
|
|
39128
|
-
}
|
|
39129
|
-
var clean = (value) => typeof value === "number" && Number.isFinite(value) ? Math.max(0, value) : 0;
|
|
39130
|
-
function billableFor(turn, prices) {
|
|
39131
|
-
const price = priceFor(turn, prices);
|
|
39132
|
-
if (!price) return null;
|
|
39133
|
-
const input = clean(turn.input);
|
|
39134
|
-
const cached3 = Math.min(clean(turn.cachedInput), input);
|
|
39135
|
-
const fresh = input - cached3;
|
|
39136
|
-
const cachedRate = price.cachedInputPerMillion ?? price.inputPerMillion;
|
|
39137
|
-
return (fresh * price.inputPerMillion + cached3 * cachedRate + clean(turn.output) * price.outputPerMillion) / 1e6;
|
|
39138
|
-
}
|
|
39139
|
-
|
|
39140
|
-
// server/usage-ledger.ts
|
|
39141
|
-
var DIR = "usage";
|
|
39142
|
-
var DAY_MS = 24 * 60 * 6e4;
|
|
39143
|
-
var clean2 = (value) => typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.trunc(value)) : 0;
|
|
39144
|
-
var finiteOrNull = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
|
|
39145
|
-
function monthKey(at) {
|
|
39146
|
-
return `${at.getUTCFullYear()}-${String(at.getUTCMonth() + 1).padStart(2, "0")}`;
|
|
39147
|
-
}
|
|
39148
|
-
var isTrigger = (value) => typeof value === "object" && value !== null && typeof value.kind === "string";
|
|
39149
|
-
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);
|
|
39150
|
-
function monthsBetween(from, to) {
|
|
39151
|
-
const keys = [];
|
|
39152
|
-
const cursor3 = new Date(Date.UTC(from.getUTCFullYear(), from.getUTCMonth(), 1));
|
|
39153
|
-
while (cursor3.getTime() <= to.getTime()) {
|
|
39154
|
-
keys.push(monthKey(cursor3));
|
|
39155
|
-
cursor3.setUTCMonth(cursor3.getUTCMonth() + 1);
|
|
39156
|
-
}
|
|
39157
|
-
return keys;
|
|
39158
|
-
}
|
|
39159
|
-
function readUsage(dataDir, range) {
|
|
39160
|
-
const rows = [];
|
|
39161
|
-
const from = range.from.getTime();
|
|
39162
|
-
const to = range.to.getTime();
|
|
39163
|
-
for (const key of monthsBetween(range.from, range.to)) {
|
|
39164
|
-
let text4;
|
|
39165
|
-
try {
|
|
39166
|
-
text4 = readFileSync2(join4(dataDir, DIR, `${key}.jsonl`), "utf8");
|
|
39167
|
-
} catch {
|
|
39168
|
-
continue;
|
|
39169
|
-
}
|
|
39170
|
-
for (const line of text4.split("\n")) {
|
|
39171
|
-
if (!line) continue;
|
|
39172
|
-
try {
|
|
39173
|
-
const value = JSON.parse(line);
|
|
39174
|
-
if (!isUsageRow(value)) continue;
|
|
39175
|
-
const at = Date.parse(value.at);
|
|
39176
|
-
if (at >= from && at <= to) rows.push(value);
|
|
39177
|
-
} catch {
|
|
39178
|
-
}
|
|
39179
|
-
}
|
|
39180
|
-
}
|
|
39181
|
-
return rows;
|
|
39182
|
-
}
|
|
39183
|
-
function triggerKey(trigger) {
|
|
39184
|
-
switch (trigger.kind) {
|
|
39185
|
-
case "user":
|
|
39186
|
-
return `user:${(trigger.email ?? trigger.label ?? "").toLowerCase() || "unknown"}`;
|
|
39187
|
-
case "owner":
|
|
39188
|
-
return "owner";
|
|
39189
|
-
case "routine":
|
|
39190
|
-
return `routine:${trigger.routineId ?? trigger.label ?? "unknown"}`;
|
|
39191
|
-
default:
|
|
39192
|
-
return "bot";
|
|
39193
|
-
}
|
|
39194
|
-
}
|
|
39195
|
-
function triggerLabel(trigger) {
|
|
39196
|
-
switch (trigger.kind) {
|
|
39197
|
-
case "user":
|
|
39198
|
-
return trigger.email ?? trigger.label ?? "Signed-in user";
|
|
39199
|
-
case "owner":
|
|
39200
|
-
return "This computer";
|
|
39201
|
-
case "routine":
|
|
39202
|
-
return `Routine: ${trigger.label ?? trigger.routineId ?? "unknown"}`;
|
|
39203
|
-
default:
|
|
39204
|
-
return "Bot to bot";
|
|
39205
|
-
}
|
|
39206
|
-
}
|
|
39207
|
-
function groupOf(row, groupBy) {
|
|
39208
|
-
switch (groupBy) {
|
|
39209
|
-
case "bot":
|
|
39210
|
-
return { key: `bot:${row.botId}`, label: row.botName || row.botId };
|
|
39211
|
-
case "model":
|
|
39212
|
-
return { key: `model:${row.driverKind}/${row.model}`, label: row.model };
|
|
39213
|
-
case "user":
|
|
39214
|
-
return { key: triggerKey(row.trigger), label: triggerLabel(row.trigger) };
|
|
39215
|
-
case "day":
|
|
39216
|
-
return { key: `day:${row.at.slice(0, 10)}`, label: row.at.slice(0, 10) };
|
|
39217
|
-
default:
|
|
39218
|
-
return { key: `engine:${row.driverKind}`, label: row.driverKind };
|
|
39219
|
-
}
|
|
39220
|
-
}
|
|
39221
|
-
function emptyGroup(key, label) {
|
|
39222
|
-
return { key, label, turns: 0, input: 0, output: 0, cachedInput: 0, costUsd: null, unpriced: 0, billableUsd: null };
|
|
39223
|
-
}
|
|
39224
|
-
function add(group, row, prices) {
|
|
39225
|
-
group.turns += 1;
|
|
39226
|
-
group.input += clean2(row.input);
|
|
39227
|
-
group.output += clean2(row.output);
|
|
39228
|
-
group.cachedInput += clean2(row.cachedInput);
|
|
39229
|
-
const cost = finiteOrNull(row.costUsd);
|
|
39230
|
-
if (cost === null) group.unpriced += 1;
|
|
39231
|
-
else group.costUsd = (group.costUsd ?? 0) + cost;
|
|
39232
|
-
const billable = prices ? billableFor(row, prices) : null;
|
|
39233
|
-
if (billable !== null) group.billableUsd = (group.billableUsd ?? 0) + billable;
|
|
39234
|
-
}
|
|
39235
|
-
function summarizeUsage(rows, groupBy, prices = null) {
|
|
39236
|
-
const groups = /* @__PURE__ */ new Map();
|
|
39237
|
-
const total = emptyGroup("total", "total");
|
|
39238
|
-
for (const row of rows) {
|
|
39239
|
-
const { key, label } = groupOf(row, groupBy);
|
|
39240
|
-
let group = groups.get(key);
|
|
39241
|
-
if (!group) {
|
|
39242
|
-
group = emptyGroup(key, label);
|
|
39243
|
-
groups.set(key, group);
|
|
39244
|
-
}
|
|
39245
|
-
add(group, row, prices);
|
|
39246
|
-
add(total, row, prices);
|
|
39247
|
-
}
|
|
39248
|
-
const ordered = [...groups.values()];
|
|
39249
|
-
if (groupBy === "day") ordered.sort((a2, b) => a2.key.localeCompare(b.key));
|
|
39250
|
-
else {
|
|
39251
|
-
ordered.sort((a2, b) => {
|
|
39252
|
-
const costA = a2.costUsd ?? Number.NEGATIVE_INFINITY;
|
|
39253
|
-
const costB = b.costUsd ?? Number.NEGATIVE_INFINITY;
|
|
39254
|
-
return costB - costA || b.input + b.output - (a2.input + a2.output);
|
|
39255
|
-
});
|
|
39256
|
-
}
|
|
39257
|
-
return { groups: ordered, total };
|
|
39258
|
-
}
|
|
39259
|
-
|
|
39260
|
-
// server/fleet-agent.ts
|
|
39261
39505
|
var MAX_BODY = 256 * 1024;
|
|
39262
39506
|
function readBody(req) {
|
|
39263
39507
|
return new Promise((resolve13, reject) => {
|
|
@@ -39281,10 +39525,20 @@ function readBody(req) {
|
|
|
39281
39525
|
var strings = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string").map((item) => item.trim()).filter(Boolean) : [];
|
|
39282
39526
|
var text = (value) => typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
39283
39527
|
var number = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
39284
|
-
|
|
39285
|
-
|
|
39286
|
-
|
|
39287
|
-
|
|
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
|
+
}
|
|
39288
39542
|
}
|
|
39289
39543
|
function createFleetAgent(options) {
|
|
39290
39544
|
const deps = options.deps ?? defaultFleetDeps();
|
|
@@ -39311,7 +39565,7 @@ function createFleetAgent(options) {
|
|
|
39311
39565
|
} catch {
|
|
39312
39566
|
}
|
|
39313
39567
|
};
|
|
39314
|
-
const
|
|
39568
|
+
const runAction = async (input) => {
|
|
39315
39569
|
const log = [];
|
|
39316
39570
|
const errors = [];
|
|
39317
39571
|
const io = { log: (line) => log.push(line), error: (line) => errors.push(line) };
|
|
@@ -39322,10 +39576,29 @@ function createFleetAgent(options) {
|
|
|
39322
39576
|
audit({ action: input.action, slug: input.slug, ok: false, error: error51 instanceof Error ? error51.message : String(error51) });
|
|
39323
39577
|
return { status: 400, body: { error: error51 instanceof Error ? error51.message : String(error51) } };
|
|
39324
39578
|
}
|
|
39325
|
-
|
|
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
|
+
}
|
|
39326
39592
|
audit({ action: input.action, slug: input.slug, ok: code === 0, ...input.email ? { email: input.email } : {} });
|
|
39327
39593
|
return code === 0 ? { status: 200, body: { ok: true, log } } : { status: 500, body: { error: errors[0] ?? "the step failed", log: [...log, ...errors] } };
|
|
39328
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
|
+
};
|
|
39329
39602
|
const handle = async (req, res) => {
|
|
39330
39603
|
const url2 = new URL(req.url ?? "/", "http://fleet");
|
|
39331
39604
|
const method = req.method ?? "GET";
|
|
@@ -39337,10 +39610,11 @@ function createFleetAgent(options) {
|
|
|
39337
39610
|
if (method === "GET" && url2.pathname === "/health") return send(200, { ok: true });
|
|
39338
39611
|
if (method === "GET" && url2.pathname === "/workspaces") {
|
|
39339
39612
|
const registry2 = loadRegistry(layout, deps);
|
|
39613
|
+
const statusOnly = url2.searchParams.get("statusOnly") === "true";
|
|
39340
39614
|
const workspaces = [];
|
|
39341
39615
|
for (const workspace of Object.values(registry2.workspaces).sort((a2, b) => a2.slug.localeCompare(b.slug))) {
|
|
39342
|
-
const live = (await deps.run(["systemctl", "is-active", `openmausbot@${workspace.slug}.service`])).output.trim() || "unknown";
|
|
39343
|
-
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) });
|
|
39344
39618
|
}
|
|
39345
39619
|
return send(200, { domain: registry2.domain, operator: registry2.operator ?? null, workspaces });
|
|
39346
39620
|
}
|
|
@@ -39355,7 +39629,13 @@ function createFleetAgent(options) {
|
|
|
39355
39629
|
cap: number(body.cap),
|
|
39356
39630
|
memory: text(body.memory),
|
|
39357
39631
|
brandJson: text(body.brandJson),
|
|
39358
|
-
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
|
|
39359
39639
|
});
|
|
39360
39640
|
return send(result.status, result.body);
|
|
39361
39641
|
}
|
|
@@ -39363,11 +39643,16 @@ function createFleetAgent(options) {
|
|
|
39363
39643
|
const result = await act({ ...base2(), action: "upgrade" });
|
|
39364
39644
|
return send(result.status, result.body);
|
|
39365
39645
|
}
|
|
39366
|
-
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);
|
|
39367
39647
|
if (match) {
|
|
39368
39648
|
const slug = match[1];
|
|
39369
39649
|
assertSlug(slug);
|
|
39370
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
|
+
}
|
|
39371
39656
|
if (method === "POST" && sub === "users") {
|
|
39372
39657
|
const body = await readBody(req);
|
|
39373
39658
|
const action = body.action === "remove" ? "remove" : body.action === "add" ? "add" : void 0;
|
|
@@ -39418,11 +39703,11 @@ import { execFile } from "node:child_process";
|
|
|
39418
39703
|
|
|
39419
39704
|
// companion/src/listener.ts
|
|
39420
39705
|
import { homedir as homedir3, networkInterfaces } from "node:os";
|
|
39421
|
-
import { delimiter as delimiter2, join as
|
|
39706
|
+
import { delimiter as delimiter2, join as join4 } from "node:path";
|
|
39422
39707
|
function tailscaleCandidates(home = homedir3()) {
|
|
39423
39708
|
return [
|
|
39424
39709
|
"/Applications/Tailscale.app/Contents/MacOS/Tailscale",
|
|
39425
|
-
|
|
39710
|
+
join4(home, "Applications", "Tailscale.app", "Contents", "MacOS", "Tailscale"),
|
|
39426
39711
|
"/opt/homebrew/bin/tailscale",
|
|
39427
39712
|
"/usr/local/bin/tailscale",
|
|
39428
39713
|
"/usr/bin/tailscale",
|
|
@@ -39640,9 +39925,9 @@ function phonePairingInstructions(phone, input) {
|
|
|
39640
39925
|
// server/tunnel.ts
|
|
39641
39926
|
import { spawn as spawn4 } from "node:child_process";
|
|
39642
39927
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
39643
|
-
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";
|
|
39644
39929
|
import { hostname } from "node:os";
|
|
39645
|
-
import { dirname as dirname3, join as
|
|
39930
|
+
import { dirname as dirname3, join as join5, resolve as resolve2 } from "node:path";
|
|
39646
39931
|
import { fileURLToPath } from "node:url";
|
|
39647
39932
|
|
|
39648
39933
|
// electron/secure-credential-state.mjs
|
|
@@ -39725,13 +40010,13 @@ function plainDocument(value) {
|
|
|
39725
40010
|
return document;
|
|
39726
40011
|
}
|
|
39727
40012
|
function openTunnelCredentials(dataDir) {
|
|
39728
|
-
const file2 =
|
|
40013
|
+
const file2 = join5(dataDir, TUNNEL_CREDENTIALS_FILE);
|
|
39729
40014
|
let initial = {};
|
|
39730
40015
|
let status = "empty";
|
|
39731
40016
|
if (existsSync4(file2)) {
|
|
39732
40017
|
try {
|
|
39733
40018
|
if (process.platform !== "win32" && (statSync(file2).mode & 63) !== 0) chmodSync3(file2, 384);
|
|
39734
|
-
const parsed = plainDocument(JSON.parse(
|
|
40019
|
+
const parsed = plainDocument(JSON.parse(readFileSync2(file2, "utf8")));
|
|
39735
40020
|
if (!parsed) throw new Error("not a JSON object");
|
|
39736
40021
|
initial = parsed;
|
|
39737
40022
|
status = "ok";
|
|
@@ -39807,11 +40092,11 @@ function cloudflaredPath(dataDir, env = process.env) {
|
|
|
39807
40092
|
return resolveCloudflaredBinary({ isPackaged: false, appPath: dataDir, environment: env });
|
|
39808
40093
|
}
|
|
39809
40094
|
function prepareEntry(here = HERE) {
|
|
39810
|
-
const bundled =
|
|
40095
|
+
const bundled = join5(here, "prepare-cloudflared.js");
|
|
39811
40096
|
return existsSync4(bundled) ? bundled : resolve2(here, "..", "scripts", "prepare-cloudflared.mjs");
|
|
39812
40097
|
}
|
|
39813
40098
|
function guardianEntry(here = HERE) {
|
|
39814
|
-
const bundled =
|
|
40099
|
+
const bundled = join5(here, "tunnel-guardian.js");
|
|
39815
40100
|
if (existsSync4(bundled)) return bundled;
|
|
39816
40101
|
const source = resolve2(here, "..", "electron", "managed-companion-guardian-main.mjs");
|
|
39817
40102
|
return existsSync4(source) ? source : null;
|
|
@@ -39846,7 +40131,7 @@ function startTunnel(options) {
|
|
|
39846
40131
|
const tunnel = createManagedCompanionTunnel({
|
|
39847
40132
|
binaryPath: options.binaryPath,
|
|
39848
40133
|
guardianEntry: options.guardian,
|
|
39849
|
-
runtimeRoot:
|
|
40134
|
+
runtimeRoot: join5(options.dataDir, TUNNEL_RUNTIME_DIR),
|
|
39850
40135
|
originPort,
|
|
39851
40136
|
environment: env,
|
|
39852
40137
|
fetchImpl: options.fetchImpl,
|
|
@@ -39892,7 +40177,7 @@ function parseArgs(argv, env = process.env) {
|
|
|
39892
40177
|
const options = {
|
|
39893
40178
|
command: command === "--help" || command === "-h" ? "help" : command,
|
|
39894
40179
|
port: Number(env.OMB_PORT || 8799),
|
|
39895
|
-
dataDir: env.OMB_DATA_DIR ||
|
|
40180
|
+
dataDir: env.OMB_DATA_DIR || join37(homedir24(), ".openmausbot"),
|
|
39896
40181
|
tailscale: false,
|
|
39897
40182
|
tunnel: false,
|
|
39898
40183
|
client: false,
|
|
@@ -40059,7 +40344,7 @@ function defaultIo() {
|
|
|
40059
40344
|
}
|
|
40060
40345
|
function serverVersion(here = HERE2) {
|
|
40061
40346
|
try {
|
|
40062
|
-
const parsed = JSON.parse(
|
|
40347
|
+
const parsed = JSON.parse(readFileSync21(resolve12(here, "..", "package.json"), "utf8"));
|
|
40063
40348
|
const version2 = typeof parsed === "object" && parsed !== null ? Reflect.get(parsed, "version") : void 0;
|
|
40064
40349
|
return typeof version2 === "string" && version2 ? version2 : "0.0.0";
|
|
40065
40350
|
} catch {
|
|
@@ -40084,7 +40369,7 @@ async function isWorkspaceRunning(options) {
|
|
|
40084
40369
|
try {
|
|
40085
40370
|
const { status, body } = await api(options.port, "/api/health");
|
|
40086
40371
|
if (status !== 200 || body?.app !== "openmausbot") return false;
|
|
40087
|
-
const expected =
|
|
40372
|
+
const expected = readFileSync21(join37(options.dataDir, "environment-id"), "utf8").trim();
|
|
40088
40373
|
const descriptor = await api(options.port, "/.well-known/openmausbot/environment");
|
|
40089
40374
|
return /^[0-9a-f-]{36}$/i.test(expected) && descriptor.status === 200 && descriptor.body?.environmentId === expected;
|
|
40090
40375
|
} catch {
|
|
@@ -40284,11 +40569,11 @@ async function runStatus(options, io = defaultIo()) {
|
|
|
40284
40569
|
return code;
|
|
40285
40570
|
}
|
|
40286
40571
|
async function runAccess(options, io = defaultIo()) {
|
|
40287
|
-
const file2 =
|
|
40572
|
+
const file2 = join37(options.dataDir, "config.json");
|
|
40288
40573
|
let raw = {};
|
|
40289
40574
|
if (existsSync19(file2)) {
|
|
40290
40575
|
try {
|
|
40291
|
-
const parsed = JSON.parse(
|
|
40576
|
+
const parsed = JSON.parse(readFileSync21(file2, "utf8"));
|
|
40292
40577
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error("not an object");
|
|
40293
40578
|
raw = Object.fromEntries(Object.entries(parsed));
|
|
40294
40579
|
} catch (error51) {
|
|
@@ -40436,16 +40721,16 @@ async function runBrowser(options, io = defaultIo()) {
|
|
|
40436
40721
|
return 0;
|
|
40437
40722
|
}
|
|
40438
40723
|
function serverEntry(here = HERE2) {
|
|
40439
|
-
const bundled =
|
|
40724
|
+
const bundled = join37(here, "index.js");
|
|
40440
40725
|
const root = resolve12(here, "..");
|
|
40441
40726
|
if (existsSync19(bundled)) {
|
|
40442
|
-
const staticDir2 = [
|
|
40443
|
-
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;
|
|
40444
40729
|
return { command: process.execPath, args: [bundled], staticDir: staticDir2, skillsDir };
|
|
40445
40730
|
}
|
|
40446
|
-
const source =
|
|
40447
|
-
const staticDir = existsSync19(
|
|
40448
|
-
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 };
|
|
40449
40734
|
}
|
|
40450
40735
|
async function planTunnel(options, log) {
|
|
40451
40736
|
let access2 = null;
|
|
@@ -40545,10 +40830,10 @@ async function runServe(options, log = console.log) {
|
|
|
40545
40830
|
let child;
|
|
40546
40831
|
try {
|
|
40547
40832
|
if (options.guided) {
|
|
40548
|
-
const logsDir =
|
|
40833
|
+
const logsDir = join37(options.dataDir, "logs");
|
|
40549
40834
|
mkdirSync17(logsDir, { recursive: true, mode: 448 });
|
|
40550
|
-
logPath =
|
|
40551
|
-
logFd =
|
|
40835
|
+
logPath = join37(logsDir, `server-${Date.now()}-${process.pid}.log`);
|
|
40836
|
+
logFd = openSync4(logPath, "wx", 384);
|
|
40552
40837
|
log("\nStarting your workspace\u2026");
|
|
40553
40838
|
}
|
|
40554
40839
|
if (tailscale) {
|
|
@@ -40572,7 +40857,7 @@ async function runServe(options, log = console.log) {
|
|
|
40572
40857
|
}
|
|
40573
40858
|
throw error51;
|
|
40574
40859
|
} finally {
|
|
40575
|
-
if (logFd !== void 0)
|
|
40860
|
+
if (logFd !== void 0) closeSync4(logFd);
|
|
40576
40861
|
process.removeListener("SIGINT", cancelStartup);
|
|
40577
40862
|
process.removeListener("SIGTERM", cancelStartup);
|
|
40578
40863
|
}
|
|
@@ -40739,7 +41024,7 @@ async function runOnboardingCommand(options, io = defaultIo(), startServer = run
|
|
|
40739
41024
|
}
|
|
40740
41025
|
if (options.command === "setup") {
|
|
40741
41026
|
io.log("\nAll set. Start with: openmausbot (or npx openmausbot without a global install).");
|
|
40742
|
-
if (options.dataDir !==
|
|
41027
|
+
if (options.dataDir !== join37(homedir24(), ".openmausbot") || options.port !== 8799) {
|
|
40743
41028
|
io.log(`Use the same --data-dir (${options.dataDir}) and --port (${options.port}) options when starting.`);
|
|
40744
41029
|
}
|
|
40745
41030
|
return 0;
|