openmausbot 0.1.90 → 0.1.91
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/{abnfDiagram-O67JEVCF-DUyXd0kL.js → abnfDiagram-O67JEVCF-yP-w8pRq.js} +1 -1
- package/dist/assets/{arc-q73qM-At.js → arc-DsivfZEh.js} +1 -1
- package/dist/assets/{architectureDiagram-NJMV4G6O-D33O3Wur.js → architectureDiagram-NJMV4G6O-CILw7lzT.js} +1 -1
- package/dist/assets/{blockDiagram-BEXU5L5S-DLsJeoXC.js → blockDiagram-BEXU5L5S-DEQUV3HF.js} +1 -1
- package/dist/assets/{c4Diagram-YGBWAQC7-DnaljZnu.js → c4Diagram-YGBWAQC7-lSXCZoKA.js} +1 -1
- package/dist/assets/channel-DrfGMu7X.js +1 -0
- package/dist/assets/{chunk-2Q5K7J3B-Da0h2i79.js → chunk-2Q5K7J3B-QCD5bgWP.js} +1 -1
- package/dist/assets/{chunk-3FUC2YCW-CfzHi54f.js → chunk-3FUC2YCW-C5-iZJuL.js} +2 -2
- package/dist/assets/{chunk-5VM5RSS4-LjE4d9Jf.js → chunk-5VM5RSS4-cxsawMpY.js} +1 -1
- package/dist/assets/{chunk-GTNCS2PH-DY-UGmvc.js → chunk-GTNCS2PH-DV8tV8H7.js} +1 -1
- package/dist/assets/{chunk-GWA4HPMP-BqeGhyhP.js → chunk-GWA4HPMP-CJFZyfbW.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-BYa9EBAg.js → chunk-JWPE2WC7-BigbMXco.js} +1 -1
- package/dist/assets/{chunk-NETBCI7D-vTrDtWyb.js → chunk-NETBCI7D-oVxTfpDy.js} +1 -1
- package/dist/assets/{chunk-WEXAMYUT-ic5tXadM.js → chunk-WEXAMYUT-CH4BiroW.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-DvRCY-c6.js → chunk-XXDRQBXY-KEDa6gax.js} +1 -1
- package/dist/assets/{classDiagram-v2-NBCMYWYE-DVbmgz_L.js → classDiagram-v2-NBCMYWYE-BiPjfqVv.js} +1 -1
- package/dist/assets/{cose-bilkent-JH36ORCC-vdUs2n57.js → cose-bilkent-JH36ORCC-Caxew3OY.js} +1 -1
- package/dist/assets/{cynefin-OW5HDTMX-BvLGlV3U.js → cynefin-OW5HDTMX-B_Pui369.js} +1 -1
- package/dist/assets/{cynefinDiagram-VND7K2PF-C4GcJQDt.js → cynefinDiagram-VND7K2PF-X64ydIhP.js} +1 -1
- package/dist/assets/{dagre-6A5THRUB-BMSaHpt_.js → dagre-6A5THRUB-TqYCI1E1.js} +1 -1
- package/dist/assets/{diagram-22UHCM2B-D5b9UBhC.js → diagram-22UHCM2B-TeOt3rAQ.js} +1 -1
- package/dist/assets/{diagram-3UASUU5V-CIKLlB7Y.js → diagram-3UASUU5V-D70BLhDA.js} +1 -1
- package/dist/assets/{diagram-ATOU4E4O-BzXb-7k7.js → diagram-ATOU4E4O-BbQDLwUC.js} +1 -1
- package/dist/assets/{diagram-CDSNMT55-HsTvJ7uT.js → diagram-CDSNMT55-ChNJZ1u-.js} +1 -1
- package/dist/assets/{diagram-MLGK6HIB-Db9ySNbQ.js → diagram-MLGK6HIB-DMBl2APa.js} +1 -1
- package/dist/assets/{diagram-MPIPVDR6-D6BKbpqY.js → diagram-MPIPVDR6-CEqDchOp.js} +1 -1
- package/dist/assets/{ebnfDiagram-ZINNZB2B-Bq067L0t.js → ebnfDiagram-ZINNZB2B-BVDKRp_-.js} +1 -1
- package/dist/assets/{elk-276RUBZZ-DhaD7fNJ.js → elk-276RUBZZ-CRkofO9B.js} +1 -1
- package/dist/assets/{erDiagram-OPXOYQCR-fwIhfz36.js → erDiagram-OPXOYQCR-DruOMh5e.js} +1 -1
- package/dist/assets/{flowDiagram-KWPJA3E3-DygIyu0V.js → flowDiagram-KWPJA3E3-8GMQeB7t.js} +1 -1
- package/dist/assets/{ganttDiagram-FUAMR5RP-DPYfSO96.js → ganttDiagram-FUAMR5RP-ORE9jcsM.js} +1 -1
- package/dist/assets/{gitGraphDiagram-X574FWY7-DhkHeCle.js → gitGraphDiagram-X574FWY7-BmsedsWh.js} +1 -1
- package/dist/assets/index-BlnCEcU6.js +588 -0
- package/dist/assets/{index-G1PHKGyh.js → index-DKNEM8Q6.js} +1 -1
- package/dist/assets/index-DVxjOvID.css +1 -0
- package/dist/assets/{infoDiagram-VRGFBTTK-DstBLxME.js → infoDiagram-VRGFBTTK-E_p7AooJ.js} +1 -1
- package/dist/assets/{ishikawaDiagram-OU5B5YK6-CrIsGgch.js → ishikawaDiagram-OU5B5YK6-Y-B2mKda.js} +1 -1
- package/dist/assets/{journeyDiagram-ZHPQQLJL-CYypGDbB.js → journeyDiagram-ZHPQQLJL-eA8RELcK.js} +1 -1
- package/dist/assets/{kanban-definition-PNTS6WVX-DGjPt8E_.js → kanban-definition-PNTS6WVX-DqIL2e8e.js} +1 -1
- package/dist/assets/{linear-Dwc3Hrru.js → linear-yC_29R6e.js} +1 -1
- package/dist/assets/{mermaid.core-Dc4OePBL.js → mermaid.core-VhEMkSND.js} +5 -5
- package/dist/assets/{mindmap-definition-NLK3R4M7-DV-vepj-.js → mindmap-definition-NLK3R4M7-oJ7kyQZy.js} +1 -1
- package/dist/assets/{pegDiagram-GJSIUBJH-lx5Kj-n5.js → pegDiagram-GJSIUBJH-BmfYcY0O.js} +1 -1
- package/dist/assets/{pieDiagram-5QR66LMP-Bq7Vmm15.js → pieDiagram-5QR66LMP-DKvgP7w9.js} +1 -1
- package/dist/assets/{quadrantDiagram-O4NWA36T-DeqeqOBj.js → quadrantDiagram-O4NWA36T-BEKL3W0L.js} +1 -1
- package/dist/assets/{railroadDiagram-XR7U4H2S-CXwRQzSC.js → railroadDiagram-XR7U4H2S-BEsiqR5s.js} +1 -1
- package/dist/assets/{requirementDiagram-PLB6GJNP-8tAeoB88.js → requirementDiagram-PLB6GJNP-DWBcWvAP.js} +1 -1
- package/dist/assets/{sankeyDiagram-IPEJSGJF-BG5zjGWU.js → sankeyDiagram-IPEJSGJF-rpZ2_O4s.js} +1 -1
- package/dist/assets/{sequenceDiagram-PO4LG4MO-DKF3iLcw.js → sequenceDiagram-PO4LG4MO-B99HQdJR.js} +1 -1
- package/dist/assets/{sizeCapture-INFHLROL-B-y1qUgT.js → sizeCapture-INFHLROL-BjN0qT5T.js} +1 -1
- package/dist/assets/{stateDiagram-v2-GCMORJYK-MkzPgxAc.js → stateDiagram-v2-GCMORJYK-BbsVgGsC.js} +1 -1
- package/dist/assets/{swimlanes-2SLR337P-DnbaOEwH.js → swimlanes-2SLR337P-DG3gKkeR.js} +1 -1
- package/dist/assets/swimlanesDiagram-TC7HE7FX-BCZhxl7e.js +8 -0
- package/dist/assets/{timeline-definition-EJHVYXUP-Bb4xG_Bt.js → timeline-definition-EJHVYXUP-BTHOdtF_.js} +1 -1
- package/dist/assets/{usecaseDiagram-POWQR4AR-JgkR9pFf.js → usecaseDiagram-POWQR4AR-acLSLFwc.js} +1 -1
- package/dist/assets/{vennDiagram-UO4OBE2U-CIPIrAYe.js → vennDiagram-UO4OBE2U-C5JzNX9G.js} +1 -1
- package/dist/assets/{wardleyDiagram-VNRHLVJA-BjCLuUvJ.js → wardleyDiagram-VNRHLVJA-DL1-PrR_.js} +1 -1
- package/dist/assets/{xychartDiagram-PMCCYNJV-DKgiaYxT.js → xychartDiagram-PMCCYNJV-Bvr1477p.js} +1 -1
- package/dist/index.html +2 -2
- package/dist-server/cloud-home-start.js +18 -3
- package/dist-server/container-mcp.js +84 -32
- package/dist-server/drivers/agents-proxy.js +11 -0
- package/dist-server/index.js +25183 -23580
- package/dist-server/openmausbot.js +313 -159
- package/dist-server/pair-cli.js +313 -159
- package/dist-server/server/boat.js +66 -21
- package/dist-server/server/bot-overview.js +7 -4
- package/dist-server/server/cloud-home-start.js +23 -5
- package/dist-server/server/cloud-home.js +25 -2
- package/dist-server/server/cloud-lending.js +103 -0
- package/dist-server/server/cloud-move-http.js +268 -0
- package/dist-server/server/cloud-move.js +418 -0
- package/dist-server/server/config.js +42 -4
- package/dist-server/server/decider/index.js +238 -0
- package/dist-server/server/decider/jev.js +183 -0
- package/dist-server/server/decider/log.js +62 -0
- package/dist-server/server/decider/relay.js +52 -0
- package/dist-server/server/decider/room-routing.js +88 -0
- package/dist-server/server/decider/types.js +5 -0
- package/dist-server/server/drivers/agents-catalog.js +16 -0
- package/dist-server/server/drivers/boatagent.js +20 -12
- package/dist-server/server/drivers/claude.js +1 -0
- package/dist-server/server/group-goal-run.js +6 -4
- package/dist-server/server/harness-capabilities.js +2 -0
- package/dist-server/server/included-services.js +70 -0
- package/dist-server/server/index.js +438 -102
- package/dist-server/server/model-context-window.js +4 -4
- package/dist-server/server/package-export.js +17 -10
- package/dist-server/server/package-import.js +6 -3
- package/dist-server/server/package-parts.js +3 -1
- package/dist-server/server/request-auth.js +2 -0
- package/dist-server/server/routes/decider.js +19 -0
- package/dist-server/server/shared-computers.js +116 -30
- package/dist-server/server/store.js +17 -2
- package/dist-server/server/surface.js +13 -0
- package/dist-server/server/system-prompt.js +11 -0
- package/dist-server/server/team-backup.js +6 -2
- package/dist-server/server/team-manifest.js +11 -3
- package/dist-server/server/tts/elevenlabs.js +11 -6
- package/dist-server/server/tts/index.js +17 -10
- package/dist-server/server/workspace-backup-policy.js +22 -3
- package/dist-server/server/workspace-backup.js +25 -8
- package/dist-server/shared/cloud-home.js +4 -0
- package/dist-server/shared/package-format.js +30 -4
- package/dist-server/shared/team-backup.js +20 -3
- package/dist-server/vps-container-mcp.js +84 -32
- package/dist-server/workspace-backup.worker.js +113 -31
- package/package.json +1 -1
- package/dist/assets/channel-BCuyHjFV.js +0 -1
- package/dist/assets/index-CRvo-DS2.css +0 -1
- package/dist/assets/index-CWYGlA63.js +0 -588
- package/dist/assets/swimlanesDiagram-TC7HE7FX-BZQ07DGE.js +0 -8
|
@@ -12,13 +12,29 @@ export function portableWorkspaceConfig(value) {
|
|
|
12
12
|
const config = parseStoredConfig(value);
|
|
13
13
|
return Object.fromEntries(PORTABLE_CONFIG_KEYS.filter((key) => config[key] !== undefined).map((key) => [key, config[key]]));
|
|
14
14
|
}
|
|
15
|
+
// Switches that belong to the machine even inside a portable section.
|
|
16
|
+
// Computer sharing (lending a computer to this server) opens routes and a
|
|
17
|
+
// desktop connector here, so a restored workspace never turns it on or off.
|
|
18
|
+
const DESTINATION_FEATURES = ["sharedComputers"];
|
|
15
19
|
export function restoredWorkspaceConfig(portable, destination) {
|
|
16
20
|
if (!destination || typeof destination !== "object" || Array.isArray(destination))
|
|
17
21
|
throw new Error("Invalid destination workspace configuration; connections were not changed.");
|
|
18
22
|
const merged = { ...destination };
|
|
19
23
|
for (const key of PORTABLE_CONFIG_KEYS)
|
|
20
24
|
delete merged[key];
|
|
21
|
-
|
|
25
|
+
const restored = { ...merged, ...portableWorkspaceConfig(portable) };
|
|
26
|
+
const own = destination.features ?? {};
|
|
27
|
+
const features = { ...restored.features };
|
|
28
|
+
for (const key of DESTINATION_FEATURES) {
|
|
29
|
+
delete features[key];
|
|
30
|
+
if (own[key] !== undefined)
|
|
31
|
+
features[key] = own[key];
|
|
32
|
+
}
|
|
33
|
+
if (Object.keys(features).length)
|
|
34
|
+
restored.features = features;
|
|
35
|
+
else
|
|
36
|
+
delete restored.features;
|
|
37
|
+
return restored;
|
|
22
38
|
}
|
|
23
39
|
/** Exact app-owned authentication paths, not a scan of user document text. */
|
|
24
40
|
export function excludedWorkspaceAuthPath(path) {
|
|
@@ -41,7 +57,10 @@ export function redownloadedOrgLibraryPath(path) {
|
|
|
41
57
|
/** Per-turn engine hook bearers (`hook-tokens/<digest>.token`, written by the
|
|
42
58
|
* Claude driver). They are dead once their turn settles, so they are never
|
|
43
59
|
* exported. Unlike the saved auth paths above they are not refused on import:
|
|
44
|
-
* v0.1.85 did export them, and such a backup must still restore, without them.
|
|
60
|
+
* v0.1.85 did export them, and such a backup must still restore, without them.
|
|
61
|
+
* The same holds for the session registry's open marker: it describes the
|
|
62
|
+
* process that wrote it, so it is never exported or installed, and a restore
|
|
63
|
+
* leaves the destination's own marker where it is (workspace-backup.ts). */
|
|
45
64
|
export function ephemeralWorkspaceTokenPath(path) {
|
|
46
|
-
return /^hook-tokens(?:\/|$)/.test(path);
|
|
65
|
+
return /^(?:hook-tokens(?:\/|$)|sessions\.json\.open$)/.test(path);
|
|
47
66
|
}
|
|
@@ -26,6 +26,9 @@ const EXCLUDED = new Set([
|
|
|
26
26
|
".openmausbot-server-child", "environment-id", "sessions.json", "tunnel-account.json",
|
|
27
27
|
"team-computers.json",
|
|
28
28
|
"openmausbot-server.lease", "box-create-requests.lock", "messages.db-wal", "messages.db-shm",
|
|
29
|
+
// This machine's decision-model log (server/decider/log.ts): local
|
|
30
|
+
// measurement of what the classifier picked, not workspace data.
|
|
31
|
+
"decider-log",
|
|
29
32
|
]);
|
|
30
33
|
const EXCLUSION_NOTES = [
|
|
31
34
|
"Device pairing, server identity, live leases and runtime files (existing destination identities are preserved).",
|
|
@@ -49,6 +52,12 @@ function preserved(name) {
|
|
|
49
52
|
const folded = name.toLowerCase();
|
|
50
53
|
return excluded(folded) && folded !== "messages.db-wal" && folded !== "messages.db-shm";
|
|
51
54
|
}
|
|
55
|
+
/** Left where it is by a restore. The session registry's open marker is this
|
|
56
|
+
* machine's own (never exported or installed): a crash before the restore
|
|
57
|
+
* still costs account sign-ins, as it would have without one. */
|
|
58
|
+
function keptInPlace(name) {
|
|
59
|
+
return preserved(name) || name.toLowerCase() === "sessions.json.open";
|
|
60
|
+
}
|
|
52
61
|
function folder(path) {
|
|
53
62
|
mkdirSync(path, { recursive: true, mode: 0o700 });
|
|
54
63
|
const stat = lstatSync(path);
|
|
@@ -544,22 +553,24 @@ async function decryptArchive(inputPath, plaintext, password) {
|
|
|
544
553
|
}
|
|
545
554
|
async function inspectTar(path) {
|
|
546
555
|
const fd = openSync(path, "r");
|
|
547
|
-
const signature = Buffer.alloc(
|
|
556
|
+
const signature = Buffer.alloc(4);
|
|
548
557
|
try {
|
|
549
|
-
readSync(fd, signature, 0,
|
|
558
|
+
readSync(fd, signature, 0, 4, 0);
|
|
550
559
|
}
|
|
551
560
|
finally {
|
|
552
561
|
closeSync(fd);
|
|
553
562
|
}
|
|
554
|
-
// This format is uncompressed tar. Reject auto-detected gzip
|
|
555
|
-
// parser can inflate an authenticated but malicious
|
|
556
|
-
|
|
563
|
+
// This format is uncompressed tar. Reject auto-detected gzip and zstd
|
|
564
|
+
// before tar's parser can inflate an authenticated but malicious
|
|
565
|
+
// decompression bomb; tar is also told never to decompress (below).
|
|
566
|
+
if ((signature[0] === 0x1f && signature[1] === 0x8b) || signature.equals(Buffer.from([0x28, 0xb5, 0x2f, 0xfd]))) {
|
|
557
567
|
throw new Error("Compressed payloads are not supported in this workspace backup version.");
|
|
568
|
+
}
|
|
558
569
|
const entries = new Map();
|
|
559
570
|
const names = new Set();
|
|
560
571
|
let bytes = 0;
|
|
561
572
|
let problem = "";
|
|
562
|
-
await tar.t({ file: path, strict: true, onReadEntry(entry) {
|
|
573
|
+
await tar.t({ file: path, strict: true, brotli: false, zstd: false, onReadEntry(entry) {
|
|
563
574
|
const name = entry.type === "Directory" ? entry.path.replace(/\/$/, "") : entry.path;
|
|
564
575
|
const expectedRoot = name === "manifest.json" || name === "data" || name.startsWith("data/");
|
|
565
576
|
// ReadEntry normalizes backslashes on Windows; inspect the raw header too
|
|
@@ -651,7 +662,7 @@ export async function stageWorkspaceBackup(dataDir, archivePath, options) {
|
|
|
651
662
|
const expected = await inspectTar(plaintext);
|
|
652
663
|
const staged = join(job.directory, "staged");
|
|
653
664
|
folder(staged);
|
|
654
|
-
await tar.x({ file: plaintext, cwd: staged, strict: true, preservePaths: false, umask: 0o077, noChmod: true });
|
|
665
|
+
await tar.x({ file: plaintext, cwd: staged, strict: true, preservePaths: false, umask: 0o077, noChmod: true, brotli: false, zstd: false });
|
|
655
666
|
const manifest = validateStaged(staged, expected);
|
|
656
667
|
const versions = [manifest.summary.appVersion, options.currentAppVersion ?? ""].map((version) => /^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/.exec(version)?.slice(1).map(Number));
|
|
657
668
|
if (versions[0] && versions[1]) {
|
|
@@ -1037,7 +1048,7 @@ export function applyPendingWorkspaceRestore(dataDir) {
|
|
|
1037
1048
|
folder(join(safetyCopyPath, "data"));
|
|
1038
1049
|
const journal = {
|
|
1039
1050
|
id: pending.id, phase: "applying",
|
|
1040
|
-
existing: readdirSync(dataDir).filter((name) => !
|
|
1051
|
+
existing: readdirSync(dataDir).filter((name) => !keptInPlace(name)).sort(),
|
|
1041
1052
|
incoming: readdirSync(prepared).sort(),
|
|
1042
1053
|
};
|
|
1043
1054
|
// Top-level source and destination names are checked before recording any
|
|
@@ -1087,3 +1098,9 @@ export function removeWorkspaceBackupJob(dataDir, id) {
|
|
|
1087
1098
|
throw new Error("Unsafe workspace backup cleanup target.");
|
|
1088
1099
|
rmSync(path, { recursive: true, force: true });
|
|
1089
1100
|
}
|
|
1101
|
+
/** Whether a snapshot leaves out this workspace-relative path, by the same
|
|
1102
|
+
* rules as the export walk (cloud-move.ts sizes a move with it). Symbolic
|
|
1103
|
+
* links are the caller's to skip. */
|
|
1104
|
+
export function omittedFromWorkspaceBackup(path) {
|
|
1105
|
+
return excluded(path.split("/")[0]) || excludedWorkspaceAuthPath(path) || ephemeralWorkspaceTokenPath(path) || redownloadedOrgLibraryPath(path);
|
|
1106
|
+
}
|
|
@@ -161,7 +161,24 @@ const requirementsSchema = z.object({
|
|
|
161
161
|
capabilities: z.array(requiredText(80)).max(20),
|
|
162
162
|
platforms: z.array(requiredText(80)).max(10).optional(),
|
|
163
163
|
});
|
|
164
|
-
const
|
|
164
|
+
const ROOM_RESPONDER_KINDS = new Set(["agent", "everyone", "mentions", "auto"]);
|
|
165
|
+
/** A responder kind from a newer release degrades to the room's first member
|
|
166
|
+
* as lead, which is what a room without a responder does, instead of
|
|
167
|
+
* refusing the whole package. Malformed values still fail below. */
|
|
168
|
+
function knownRoomResponder(value) {
|
|
169
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
170
|
+
return value;
|
|
171
|
+
const room = value;
|
|
172
|
+
const responder = room.defaultResponder;
|
|
173
|
+
if (!responder || typeof responder !== "object" || Array.isArray(responder))
|
|
174
|
+
return value;
|
|
175
|
+
const kind = responder.kind;
|
|
176
|
+
if (typeof kind !== "string" || ROOM_RESPONDER_KINDS.has(kind))
|
|
177
|
+
return value;
|
|
178
|
+
const first = Array.isArray(room.members) && typeof room.members[0] === "string" ? room.members[0] : undefined;
|
|
179
|
+
return { ...room, defaultResponder: first ? { kind: "agent", agent: first } : { kind: "mentions" } };
|
|
180
|
+
}
|
|
181
|
+
const roomSchema = z.preprocess(knownRoomResponder, z.object({
|
|
165
182
|
key,
|
|
166
183
|
name: requiredText(100),
|
|
167
184
|
members: z.array(key).min(1).max(200),
|
|
@@ -170,8 +187,11 @@ const roomSchema = z.object({
|
|
|
170
187
|
z.object({ kind: z.literal("agent"), agent: key }),
|
|
171
188
|
z.object({ kind: z.literal("everyone") }),
|
|
172
189
|
z.object({ kind: z.literal("mentions") }),
|
|
190
|
+
// Read, not written: exports spell an Auto room as its lead (see
|
|
191
|
+
// server/package-export.ts) so every older app can still open them.
|
|
192
|
+
z.object({ kind: z.literal("auto"), agent: key.optional() }),
|
|
173
193
|
]),
|
|
174
|
-
});
|
|
194
|
+
}));
|
|
175
195
|
const exampleSchema = z.object({
|
|
176
196
|
title: requiredText(120),
|
|
177
197
|
input: requiredText(4_000),
|
|
@@ -580,7 +600,8 @@ function checkV1References(pkg) {
|
|
|
580
600
|
if (!agents.has(member))
|
|
581
601
|
throw invalid(`Room ${room.key} references unknown agent: ${member}`);
|
|
582
602
|
}
|
|
583
|
-
if (room.defaultResponder.kind === "agent" &&
|
|
603
|
+
if ((room.defaultResponder.kind === "agent" || room.defaultResponder.kind === "auto") && room.defaultResponder.agent !== undefined &&
|
|
604
|
+
!members.has(room.defaultResponder.agent)) {
|
|
584
605
|
throw invalid(`Room ${room.key} has an unknown default responder`);
|
|
585
606
|
}
|
|
586
607
|
}
|
|
@@ -637,7 +658,8 @@ function checkV2References(pkg) {
|
|
|
637
658
|
if (!agents.has(member))
|
|
638
659
|
throw invalid(`Room ${room.key} references unknown agent: ${member}`);
|
|
639
660
|
}
|
|
640
|
-
if (room.defaultResponder.kind === "agent" &&
|
|
661
|
+
if ((room.defaultResponder.kind === "agent" || room.defaultResponder.kind === "auto") && room.defaultResponder.agent !== undefined &&
|
|
662
|
+
!members.has(room.defaultResponder.agent)) {
|
|
641
663
|
throw invalid(`Room ${room.key} has a default responder who is not a member: ${room.defaultResponder.agent}`);
|
|
642
664
|
}
|
|
643
665
|
roomMembers.set(room.key, members);
|
|
@@ -821,8 +843,12 @@ export function downgradeToV1(document) {
|
|
|
821
843
|
return [{ ...kept, enabledAfterInstall: false }];
|
|
822
844
|
});
|
|
823
845
|
const { team, publisher: _publisher, notes: _notes, presets: _presets, connections: _connections, skills: _skills, agents: _agents, routines: _routines, ...rest } = pkg;
|
|
846
|
+
// Older apps know no Auto room: spell one as its lead, as exports do.
|
|
847
|
+
const rooms = rest.rooms?.map((room) => room.defaultResponder.kind !== "auto" ? room
|
|
848
|
+
: { ...room, defaultResponder: { kind: "agent", agent: room.defaultResponder.agent ?? room.members[0] } });
|
|
824
849
|
const v1 = {
|
|
825
850
|
...rest,
|
|
851
|
+
...(rooms ? { rooms } : {}),
|
|
826
852
|
agents,
|
|
827
853
|
...(team.leader ? { chiefOfStaff: team.leader } : {}),
|
|
828
854
|
...(routines.length ? { routines } : {}),
|
|
@@ -63,6 +63,20 @@ const memory = z.object({
|
|
|
63
63
|
* against. Shareable exports never carry grants at all, and an import
|
|
64
64
|
* always lands bots grant-less whatever the file says. */
|
|
65
65
|
const connectorTools = z.record(z.string().regex(CONNECTOR_SLUG_PATTERN), z.object({ tools: z.union([z.literal("*"), z.array(z.string().regex(CONNECTOR_TOOL_NAME_PATTERN)).min(1).max(500)]) }));
|
|
66
|
+
/** A responder kind from a newer release restores as the room's first
|
|
67
|
+
* member as lead (what a room without one does) instead of refusing the
|
|
68
|
+
* whole backup. Malformed values still fail the schema. */
|
|
69
|
+
function knownRoomResponder(value) {
|
|
70
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
71
|
+
return value;
|
|
72
|
+
const group = value;
|
|
73
|
+
const kind = group.defaultResponder && typeof group.defaultResponder === "object"
|
|
74
|
+
? group.defaultResponder.kind : undefined;
|
|
75
|
+
if (typeof kind !== "string" || ["member", "everyone", "mentions", "auto"].includes(kind))
|
|
76
|
+
return value;
|
|
77
|
+
const first = Array.isArray(group.memberIds) && typeof group.memberIds[0] === "string" ? group.memberIds[0] : undefined;
|
|
78
|
+
return { ...group, defaultResponder: first ? { kind: "member", botId: first } : { kind: "mentions" } };
|
|
79
|
+
}
|
|
66
80
|
const backupSchema = z.object({
|
|
67
81
|
format: z.literal("openmaus.backup"),
|
|
68
82
|
version: z.literal(1),
|
|
@@ -86,7 +100,7 @@ const backupSchema = z.object({
|
|
|
86
100
|
connectorTools: connectorTools.optional(),
|
|
87
101
|
memory: memory.optional(),
|
|
88
102
|
})).min(1).max(200),
|
|
89
|
-
groups: z.array(z.object({
|
|
103
|
+
groups: z.array(z.preprocess(knownRoomResponder, z.object({
|
|
90
104
|
...owner,
|
|
91
105
|
memberIds: z.array(key).max(200),
|
|
92
106
|
dm: z.boolean(),
|
|
@@ -95,8 +109,9 @@ const backupSchema = z.object({
|
|
|
95
109
|
z.object({ kind: z.literal("member"), botId: key }),
|
|
96
110
|
z.object({ kind: z.literal("everyone") }),
|
|
97
111
|
z.object({ kind: z.literal("mentions") }),
|
|
112
|
+
z.object({ kind: z.literal("auto"), fallbackBotId: key.optional() }),
|
|
98
113
|
]),
|
|
99
|
-
})).max(2_000),
|
|
114
|
+
}))).max(2_000),
|
|
100
115
|
routines: z.array(z.object({
|
|
101
116
|
name, prompt: z.string().trim().min(1).max(200_000),
|
|
102
117
|
target: z.enum(["bot", "room-goal"]), botId: key, groupId: key.optional(),
|
|
@@ -136,7 +151,9 @@ export function parseTeamBackup(input) {
|
|
|
136
151
|
if (group.memberIds.some((id) => !bots.has(id)))
|
|
137
152
|
throw new Error("Invalid backup: unknown room member");
|
|
138
153
|
unique(group.memberIds, "room member");
|
|
139
|
-
|
|
154
|
+
const lead = group.defaultResponder.kind === "member" ? group.defaultResponder.botId
|
|
155
|
+
: group.defaultResponder.kind === "auto" ? group.defaultResponder.fallbackBotId : undefined;
|
|
156
|
+
if (lead !== undefined && !group.memberIds.includes(lead)) {
|
|
140
157
|
throw new Error("Invalid backup: room responder is not a member");
|
|
141
158
|
}
|
|
142
159
|
if (group.dm && (group.memberIds.length !== 2 || group.tasks.length !== 1))
|
|
@@ -503,7 +503,7 @@ import { promisify } from "node:util";
|
|
|
503
503
|
|
|
504
504
|
// server/config.ts
|
|
505
505
|
import { homedir as homedir2 } from "node:os";
|
|
506
|
-
import { join as
|
|
506
|
+
import { join as join3 } from "node:path";
|
|
507
507
|
|
|
508
508
|
// node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
|
|
509
509
|
var external_exports = {};
|
|
@@ -16023,6 +16023,72 @@ var remoteMutationSchema = remoteEntrySchema.extend({
|
|
|
16023
16023
|
var CLOUD_SEAT_IDLE_STOP_MS = 5 * 6e4;
|
|
16024
16024
|
var CLOUD_SEAT_SLEEP_RETRY_MAX_MS = 10 * 6e4;
|
|
16025
16025
|
|
|
16026
|
+
// server/proxy-paths.ts
|
|
16027
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
16028
|
+
import { dirname as dirname2, join as join2 } from "node:path";
|
|
16029
|
+
import { fileURLToPath } from "node:url";
|
|
16030
|
+
var SERVER_ROOT = dirname2(fileURLToPath(import.meta.url));
|
|
16031
|
+
function resolveProxy(relative) {
|
|
16032
|
+
const source = join2(SERVER_ROOT, `${relative}.ts`);
|
|
16033
|
+
return existsSync2(source) ? source : join2(SERVER_ROOT, `${relative}.js`);
|
|
16034
|
+
}
|
|
16035
|
+
var SPAWNED_PROXIES = {
|
|
16036
|
+
browser: resolveProxy("browser-proxy"),
|
|
16037
|
+
localComputer: resolveProxy("local-computer-proxy"),
|
|
16038
|
+
permission: resolveProxy("permission-proxy"),
|
|
16039
|
+
containerMcp: resolveProxy("container-mcp"),
|
|
16040
|
+
vpsContainerMcp: resolveProxy("vps-container-mcp"),
|
|
16041
|
+
agents: resolveProxy("drivers/agents-proxy"),
|
|
16042
|
+
dweb: resolveProxy("drivers/dweb-proxy"),
|
|
16043
|
+
connectors: resolveProxy("connector-proxy"),
|
|
16044
|
+
mcpGate: resolveProxy("mcp-gate"),
|
|
16045
|
+
phone: resolveProxy("drivers/phone-proxy"),
|
|
16046
|
+
hook: resolveProxy("hooks/omb-hook"),
|
|
16047
|
+
// Loaded by the external `pi` process via `-e`, not by this server — but
|
|
16048
|
+
// resolved through the same single source of truth so the packaged layout
|
|
16049
|
+
// check can assert it ships.
|
|
16050
|
+
piMcpExtension: resolveProxy("drivers/pi-mcp-extension")
|
|
16051
|
+
};
|
|
16052
|
+
|
|
16053
|
+
// server/enterprise.ts
|
|
16054
|
+
var layerSchema = external_exports.object({
|
|
16055
|
+
customer: external_exports.string().min(1),
|
|
16056
|
+
features: external_exports.array(external_exports.string().min(1)),
|
|
16057
|
+
expiresAt: external_exports.string().nullable()
|
|
16058
|
+
});
|
|
16059
|
+
var DAY_MS = 24 * 60 * 6e4;
|
|
16060
|
+
|
|
16061
|
+
// server/sessions.ts
|
|
16062
|
+
var PAIRING_CODE_TTL_MS = 5 * 6e4;
|
|
16063
|
+
function daysMs(value, fallbackDays) {
|
|
16064
|
+
const days = Number(value);
|
|
16065
|
+
const ok = Number.isInteger(days) && days >= 1 && days <= 3650;
|
|
16066
|
+
return (ok ? days : fallbackDays) * 24 * 60 * 6e4;
|
|
16067
|
+
}
|
|
16068
|
+
var SESSION_TTL_MS = daysMs(process.env.OMB_SESSION_TTL_DAYS, 30);
|
|
16069
|
+
var SESSION_MAX_AGE_MS = daysMs(process.env.OMB_SESSION_MAX_DAYS, 180);
|
|
16070
|
+
var SESSION_RENEW_WHEN_LEFT_MS = SESSION_TTL_MS / 2;
|
|
16071
|
+
var STREAM_TICKET_TTL_MS = 5 * 6e4;
|
|
16072
|
+
var scopeSchema = external_exports.enum(["admin", "client"]);
|
|
16073
|
+
var sessionSchema = external_exports.object({
|
|
16074
|
+
id: external_exports.string().min(1),
|
|
16075
|
+
tokenHash: external_exports.string().length(64),
|
|
16076
|
+
label: external_exports.string().max(80),
|
|
16077
|
+
scopes: external_exports.array(scopeSchema).min(1),
|
|
16078
|
+
createdAt: external_exports.number(),
|
|
16079
|
+
lastSeenAt: external_exports.number(),
|
|
16080
|
+
expiresAt: external_exports.number(),
|
|
16081
|
+
/** Set when the session came from an account sign-in rather than a code. */
|
|
16082
|
+
userId: external_exports.string().max(256).optional(),
|
|
16083
|
+
email: external_exports.string().max(320).optional(),
|
|
16084
|
+
/** Set only by the internal verified-portal issuance path, never by email or pairing input. */
|
|
16085
|
+
membershipAuthority: external_exports.literal("portal").optional()
|
|
16086
|
+
});
|
|
16087
|
+
var fileSchema = external_exports.object({ version: external_exports.literal(1), sessions: external_exports.array(sessionSchema) });
|
|
16088
|
+
|
|
16089
|
+
// server/cloud-home.ts
|
|
16090
|
+
var CLOUD_PAIRING_NONCE_MS = 10 * 6e4;
|
|
16091
|
+
|
|
16026
16092
|
// server/config.ts
|
|
16027
16093
|
var optionalText = external_exports.string().optional();
|
|
16028
16094
|
var SSH_ALIAS = /^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$/;
|
|
@@ -16354,6 +16420,19 @@ var appConfigSchema = external_exports.object({
|
|
|
16354
16420
|
baseUrl: external_exports.string().trim().max(2048).refine((value) => !value || /^https?:\/\//i.test(value), "the Chatterbox server address must start with http:// or https://").optional(),
|
|
16355
16421
|
model: optionalText
|
|
16356
16422
|
}).optional(),
|
|
16423
|
+
/** The decision model (server/decider): a fast classifier that picks
|
|
16424
|
+
* things for bots, starting with who answers a room message. `key` is
|
|
16425
|
+
* write-only like every credential; `baseUrl` points at a Jev-compatible
|
|
16426
|
+
* server instead of TypeSafe's (an operator setting with no UI); `jobs`
|
|
16427
|
+
* switches each decision on or off. Not `decisions`: that section is the
|
|
16428
|
+
* authorization log's retention. */
|
|
16429
|
+
decider: external_exports.object({
|
|
16430
|
+
enabled: external_exports.boolean().optional(),
|
|
16431
|
+
provider: external_exports.enum(["jev", "off"]).optional(),
|
|
16432
|
+
key: optionalText,
|
|
16433
|
+
baseUrl: external_exports.string().trim().max(2048).refine((value) => !value || /^https?:\/\//i.test(value), "the decision model address must start with http:// or https://").optional(),
|
|
16434
|
+
jobs: external_exports.object({ roomRouting: external_exports.boolean().optional() }).optional()
|
|
16435
|
+
}).optional(),
|
|
16357
16436
|
/** Avatar provider credentials stay separate; choosing a router never reuses a cloud key. */
|
|
16358
16437
|
imageGen: external_exports.object({
|
|
16359
16438
|
provider: external_exports.enum(["openai", "xai", "custom"]).optional(),
|
|
@@ -16423,37 +16502,10 @@ var storedAppConfigSchema = appConfigSchema.extend({
|
|
|
16423
16502
|
var appConfigPatchSchema = appConfigSchema.omit({ instances: true, mcpServers: true, cliStartup: true, customDomain: true }).extend({ threads: threadsPatchSchema.optional(), newBots: newBotsPatchSchema.optional() });
|
|
16424
16503
|
var jsonObjectSchema = external_exports.record(external_exports.string(), external_exports.json());
|
|
16425
16504
|
var DEFAULT_CAPTURE_QUIET_MS = 2 * 6e4;
|
|
16426
|
-
var DATA_DIR = process.env.OMB_DATA_DIR ??
|
|
16427
|
-
var LEGACY_DATA_DIR =
|
|
16428
|
-
var EVENTS_DIR =
|
|
16429
|
-
var NATIVE_DIR =
|
|
16430
|
-
|
|
16431
|
-
// server/proxy-paths.ts
|
|
16432
|
-
import { existsSync as existsSync2 } from "node:fs";
|
|
16433
|
-
import { dirname as dirname2, join as join3 } from "node:path";
|
|
16434
|
-
import { fileURLToPath } from "node:url";
|
|
16435
|
-
var SERVER_ROOT = dirname2(fileURLToPath(import.meta.url));
|
|
16436
|
-
function resolveProxy(relative) {
|
|
16437
|
-
const source = join3(SERVER_ROOT, `${relative}.ts`);
|
|
16438
|
-
return existsSync2(source) ? source : join3(SERVER_ROOT, `${relative}.js`);
|
|
16439
|
-
}
|
|
16440
|
-
var SPAWNED_PROXIES = {
|
|
16441
|
-
browser: resolveProxy("browser-proxy"),
|
|
16442
|
-
localComputer: resolveProxy("local-computer-proxy"),
|
|
16443
|
-
permission: resolveProxy("permission-proxy"),
|
|
16444
|
-
containerMcp: resolveProxy("container-mcp"),
|
|
16445
|
-
vpsContainerMcp: resolveProxy("vps-container-mcp"),
|
|
16446
|
-
agents: resolveProxy("drivers/agents-proxy"),
|
|
16447
|
-
dweb: resolveProxy("drivers/dweb-proxy"),
|
|
16448
|
-
connectors: resolveProxy("connector-proxy"),
|
|
16449
|
-
mcpGate: resolveProxy("mcp-gate"),
|
|
16450
|
-
phone: resolveProxy("drivers/phone-proxy"),
|
|
16451
|
-
hook: resolveProxy("hooks/omb-hook"),
|
|
16452
|
-
// Loaded by the external `pi` process via `-e`, not by this server — but
|
|
16453
|
-
// resolved through the same single source of truth so the packaged layout
|
|
16454
|
-
// check can assert it ships.
|
|
16455
|
-
piMcpExtension: resolveProxy("drivers/pi-mcp-extension")
|
|
16456
|
-
};
|
|
16505
|
+
var DATA_DIR = process.env.OMB_DATA_DIR ?? join3(homedir2(), ".openmausbot");
|
|
16506
|
+
var LEGACY_DATA_DIR = join3(homedir2(), ".opengrokbot");
|
|
16507
|
+
var EVENTS_DIR = join3(DATA_DIR, "events");
|
|
16508
|
+
var NATIVE_DIR = join3(DATA_DIR, "native");
|
|
16457
16509
|
|
|
16458
16510
|
// server/container-computer.ts
|
|
16459
16511
|
var run = promisify(execFile2);
|