openmausbot 0.1.92 → 0.1.94
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/{DesktopViewer-Do6lBa4C.js → DesktopViewer-bxM7NV3N.js} +1 -1
- package/dist/assets/{abnfDiagram-O67JEVCF-Cd3X7Hxc.js → abnfDiagram-O67JEVCF-BhMdJhRJ.js} +1 -1
- package/dist/assets/{arc-DIYc7Ymx.js → arc-B5itKDcx.js} +1 -1
- package/dist/assets/{architectureDiagram-NJMV4G6O-DBmRGVyP.js → architectureDiagram-NJMV4G6O-C5d7UY6U.js} +1 -1
- package/dist/assets/{blockDiagram-BEXU5L5S-CPFePSEE.js → blockDiagram-BEXU5L5S-BrZIhMmu.js} +1 -1
- package/dist/assets/{c4Diagram-YGBWAQC7-DD_DO1qy.js → c4Diagram-YGBWAQC7-7I0ArfbD.js} +1 -1
- package/dist/assets/channel-CdJX6i8k.js +1 -0
- package/dist/assets/{chunk-2Q5K7J3B-BNEeNSjX.js → chunk-2Q5K7J3B-BnEsyN36.js} +1 -1
- package/dist/assets/{chunk-3FUC2YCW-BbinLaqH.js → chunk-3FUC2YCW-BXATQ52s.js} +2 -2
- package/dist/assets/{chunk-5VM5RSS4-BBgYrgDR.js → chunk-5VM5RSS4-B0BpTkEx.js} +1 -1
- package/dist/assets/{chunk-GTNCS2PH-ezXj-kA-.js → chunk-GTNCS2PH-BtLTz6FR.js} +1 -1
- package/dist/assets/{chunk-GWA4HPMP-9FxZ3diV.js → chunk-GWA4HPMP-D6gcZOYt.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-CO-40JpI.js → chunk-JWPE2WC7-DCLrosvf.js} +1 -1
- package/dist/assets/{chunk-NETBCI7D-BEhjslYE.js → chunk-NETBCI7D-B9JsknV9.js} +1 -1
- package/dist/assets/{chunk-WEXAMYUT-CNeq4n7_.js → chunk-WEXAMYUT-CsoTxw7z.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-OsCY7KSi.js → chunk-XXDRQBXY-0F_frB0c.js} +1 -1
- package/dist/assets/{classDiagram-v2-NBCMYWYE-CloRUEtk.js → classDiagram-v2-NBCMYWYE-DkrpnflX.js} +1 -1
- package/dist/assets/{cose-bilkent-JH36ORCC-D1BGZhp4.js → cose-bilkent-JH36ORCC-BNDwYBt9.js} +1 -1
- package/dist/assets/{cynefin-OW5HDTMX-CUf4T2CN.js → cynefin-OW5HDTMX-JUkNr1Mk.js} +1 -1
- package/dist/assets/{cynefinDiagram-VND7K2PF-DPEZrFGA.js → cynefinDiagram-VND7K2PF-DKeh9N4M.js} +1 -1
- package/dist/assets/{dagre-6A5THRUB-bDe6GIv5.js → dagre-6A5THRUB-ve-6T_wX.js} +1 -1
- package/dist/assets/{diagram-22UHCM2B-CdcUQz55.js → diagram-22UHCM2B-BuJ2d8rT.js} +1 -1
- package/dist/assets/{diagram-3UASUU5V-BZpqGg7c.js → diagram-3UASUU5V-BgOs_Ni-.js} +1 -1
- package/dist/assets/{diagram-ATOU4E4O-DvGM6ugp.js → diagram-ATOU4E4O-DMK6VmcP.js} +1 -1
- package/dist/assets/{diagram-CDSNMT55-DSTmMm0s.js → diagram-CDSNMT55-DksASlcY.js} +1 -1
- package/dist/assets/{diagram-MLGK6HIB-15cucnMn.js → diagram-MLGK6HIB-Bgn8I5k6.js} +1 -1
- package/dist/assets/{diagram-MPIPVDR6-tMbuaE6n.js → diagram-MPIPVDR6-zK0jqO8N.js} +1 -1
- package/dist/assets/{ebnfDiagram-ZINNZB2B-BYxYzjHI.js → ebnfDiagram-ZINNZB2B-vKrbzTwr.js} +1 -1
- package/dist/assets/{elk-276RUBZZ-v6bj852W.js → elk-276RUBZZ-Bzv2mPNf.js} +1 -1
- package/dist/assets/{erDiagram-OPXOYQCR-5lBBlnd2.js → erDiagram-OPXOYQCR-CvOyYuxu.js} +1 -1
- package/dist/assets/{flowDiagram-KWPJA3E3-CZyl1zIj.js → flowDiagram-KWPJA3E3-CtbnDbc0.js} +1 -1
- package/dist/assets/{ganttDiagram-FUAMR5RP-BiAI-ltZ.js → ganttDiagram-FUAMR5RP-c4dMRYyG.js} +1 -1
- package/dist/assets/{gitGraphDiagram-X574FWY7-BAZISkj3.js → gitGraphDiagram-X574FWY7-B34uaQho.js} +1 -1
- package/dist/assets/index-CPYrGsv1.js +610 -0
- package/dist/assets/{index-DGBaEXrM.js → index-CPiqc_DN.js} +1 -1
- package/dist/assets/index-CzT42GR8.css +1 -0
- package/dist/assets/{infoDiagram-VRGFBTTK-B6ooOy-Z.js → infoDiagram-VRGFBTTK-o64ma4CI.js} +1 -1
- package/dist/assets/{ishikawaDiagram-OU5B5YK6-BZ9ksi-B.js → ishikawaDiagram-OU5B5YK6-DV4C9BAm.js} +1 -1
- package/dist/assets/{journeyDiagram-ZHPQQLJL-DWqlzIEe.js → journeyDiagram-ZHPQQLJL-Bf0AHk1H.js} +1 -1
- package/dist/assets/{kanban-definition-PNTS6WVX-JX6piDwL.js → kanban-definition-PNTS6WVX-B1UMSKmE.js} +1 -1
- package/dist/assets/{linear-CmOWzX0P.js → linear-BpKhuyLm.js} +1 -1
- package/dist/assets/{mermaid.core-CF1zwoQ-.js → mermaid.core-B_BKNCw5.js} +5 -5
- package/dist/assets/{mindmap-definition-NLK3R4M7-CGh2K4fW.js → mindmap-definition-NLK3R4M7-DeARruT1.js} +1 -1
- package/dist/assets/{pegDiagram-GJSIUBJH-CDwYN0s7.js → pegDiagram-GJSIUBJH-BOQEk_1D.js} +1 -1
- package/dist/assets/{pieDiagram-5QR66LMP-1rfjJocB.js → pieDiagram-5QR66LMP-EIfoiTZo.js} +1 -1
- package/dist/assets/{quadrantDiagram-O4NWA36T-tKPvPiok.js → quadrantDiagram-O4NWA36T-GgZwT-fi.js} +1 -1
- package/dist/assets/{railroadDiagram-XR7U4H2S-BHLm5vXR.js → railroadDiagram-XR7U4H2S-D9rDQomj.js} +1 -1
- package/dist/assets/{requirementDiagram-PLB6GJNP-CB45STp7.js → requirementDiagram-PLB6GJNP-DAh1MIeo.js} +1 -1
- package/dist/assets/{sankeyDiagram-IPEJSGJF-55QA_huC.js → sankeyDiagram-IPEJSGJF-CPr6Uz64.js} +1 -1
- package/dist/assets/{sequenceDiagram-PO4LG4MO-BckFmQHn.js → sequenceDiagram-PO4LG4MO-BU6VIdcm.js} +1 -1
- package/dist/assets/{sizeCapture-INFHLROL-BFCOJF11.js → sizeCapture-INFHLROL-qF2HO41r.js} +1 -1
- package/dist/assets/{stateDiagram-v2-GCMORJYK-CZvmiVVN.js → stateDiagram-v2-GCMORJYK-DlJ8G4MD.js} +1 -1
- package/dist/assets/{swimlanes-2SLR337P-BpDUCEKy.js → swimlanes-2SLR337P-DzYE8I54.js} +1 -1
- package/dist/assets/swimlanesDiagram-TC7HE7FX-D-U4GtLQ.js +8 -0
- package/dist/assets/{timeline-definition-EJHVYXUP-BpxHq6IK.js → timeline-definition-EJHVYXUP-B7wzEQ0A.js} +1 -1
- package/dist/assets/{usecaseDiagram-POWQR4AR-Cj66vm02.js → usecaseDiagram-POWQR4AR-BvGgrxIP.js} +1 -1
- package/dist/assets/{vennDiagram-UO4OBE2U-C9nRkWE_.js → vennDiagram-UO4OBE2U-CEECdgQg.js} +1 -1
- package/dist/assets/{wardleyDiagram-VNRHLVJA-D-me_nWj.js → wardleyDiagram-VNRHLVJA-XQHA0P_P.js} +1 -1
- package/dist/assets/{xychartDiagram-PMCCYNJV-Dc_fL4mk.js → xychartDiagram-PMCCYNJV-B5mLcZI2.js} +1 -1
- package/dist/index.html +2 -2
- package/dist-server/cloud-home-start.js +1 -0
- package/dist-server/companion/src/routes.js +45 -1
- package/dist-server/container-mcp.js +99 -77
- package/dist-server/drivers/agents-proxy.js +40 -1
- package/dist-server/drivers/pi-mcp-extension.ts +245 -299
- package/dist-server/enterprise/server/index.js +1 -0
- package/dist-server/index.js +18361 -13794
- package/dist-server/local-computer-proxy.js +5 -1
- package/dist-server/local-computer.js +1 -0
- package/dist-server/mcp-gate.js +152 -20
- package/dist-server/mcp-remote-proxy.js +437 -0
- package/dist-server/openmausbot.js +8872 -7620
- package/dist-server/pair-cli.js +8872 -7620
- package/dist-server/permission-proxy.js +65 -1
- package/dist-server/proxy-paths.js +1 -0
- package/dist-server/server/auto-approve.js +33 -11
- package/dist-server/server/boat.js +3 -3
- package/dist-server/server/browser-live.js +14 -5
- package/dist-server/server/browser-runtime.js +6 -0
- package/dist-server/server/channel-queue.js +4 -1
- package/dist-server/server/cli.js +32 -12
- package/dist-server/server/cloud-move-http.js +9 -3
- package/dist-server/server/cloud-move.js +18 -0
- package/dist-server/server/cloud-owner.js +1 -1
- package/dist-server/server/computer-control.js +6 -0
- package/dist-server/server/computer-wait.js +33 -13
- package/dist-server/server/config.js +57 -10
- package/dist-server/server/container-computer.js +43 -32
- package/dist-server/server/decision-log.js +9 -6
- package/dist-server/server/delegations.js +47 -22
- package/dist-server/server/digest.js +12 -2
- package/dist-server/server/drivers/acp/core.js +229 -20
- package/dist-server/server/drivers/acp/cursor.js +13 -1
- package/dist-server/server/drivers/acp/grok.js +208 -3
- package/dist-server/server/drivers/acp/quiet-status.js +257 -0
- package/dist-server/server/drivers/acp/qwen.js +18 -0
- package/dist-server/server/drivers/agents-call.js +28 -0
- package/dist-server/server/drivers/agents-catalog.js +10 -1
- package/dist-server/server/drivers/boatagent.js +2 -0
- package/dist-server/server/drivers/builtIn.js +2 -0
- package/dist-server/server/drivers/cerebras.js +108 -0
- package/dist-server/server/drivers/chat-mcp-tools.js +45 -15
- package/dist-server/server/drivers/claude.js +35 -9
- package/dist-server/server/drivers/codex.js +84 -10
- package/dist-server/server/drivers/openai-chat.js +37 -5
- package/dist-server/server/drivers/pi.js +48 -4
- package/dist-server/server/drivers/prompt-split.js +2 -0
- package/dist-server/server/env-path.js +6 -2
- package/dist-server/server/environment.js +3 -0
- package/dist-server/server/guarded-requests.js +2 -1
- package/dist-server/server/index.js +1353 -311
- package/dist-server/server/live-call-controller.js +868 -0
- package/dist-server/server/live-call.js +216 -0
- package/dist-server/server/local-vm-stop-reason.js +26 -0
- package/dist-server/server/mcp-gate-config.js +43 -12
- package/dist-server/server/mcp-gate.js +127 -25
- package/dist-server/server/mcp-http.js +46 -8
- package/dist-server/server/mcp-remote-proxy.js +108 -0
- package/dist-server/server/new-bot-defaults.js +3 -0
- package/dist-server/server/peer-provenance.js +4 -2
- package/dist-server/server/permission-proxy.js +15 -1
- package/dist-server/server/plan-usage.js +797 -0
- package/dist-server/server/provider-key-check.js +2 -1
- package/dist-server/server/proxy-paths.js +1 -0
- package/dist-server/server/redact.js +2 -0
- package/dist-server/server/request-auth.js +3 -2
- package/dist-server/server/routes/desktop-viewer.js +50 -6
- package/dist-server/server/routes/live.js +98 -0
- package/dist-server/server/routines.js +6 -3
- package/dist-server/server/skill-library.js +207 -0
- package/dist-server/server/skills-library-migration.js +235 -0
- package/dist-server/server/skills.js +66 -3
- package/dist-server/server/steer-queue.js +13 -1
- package/dist-server/server/store.js +86 -4
- package/dist-server/server/surface.js +9 -3
- package/dist-server/server/team-backup.js +2 -1
- package/dist-server/server/thread-events.js +1 -1
- package/dist-server/server/tunnel.js +20 -1
- package/dist-server/server/turn-log.js +10 -0
- package/dist-server/server/turn-resources.js +124 -0
- package/dist-server/server/vps-computer.js +1 -1
- package/dist-server/server/workspace-backup.js +22 -0
- package/dist-server/server/workspace.js +1 -1
- package/dist-server/shared/approval-mode.js +1 -1
- package/dist-server/shared/ask-question.js +13 -0
- package/dist-server/shared/call-consent.js +28 -0
- package/dist-server/shared/live-approval.js +124 -0
- package/dist-server/shared/live-call.js +176 -0
- package/dist-server/shared/redact.js +1 -0
- package/dist-server/shared/skill-md.js +10 -0
- package/dist-server/shared/team-backup.js +3 -0
- package/dist-server/shared/tool-name.js +22 -0
- package/dist-server/shared/tool-scope-support.js +25 -0
- package/dist-server/shared/tool-scope.js +136 -0
- package/dist-server/src/lib/thread-refs.js +406 -0
- package/dist-server/vps-container-mcp.js +99 -77
- package/dist-server/workspace-backup.worker.js +103 -100
- package/enterprise/server/index.js +1 -0
- package/package.json +1 -1
- package/dist/assets/channel-Dtk5Mtic.js +0 -1
- package/dist/assets/index-Cdmjrl28.css +0 -1
- package/dist/assets/index-DR7YPMkW.js +0 -597
- package/dist/assets/swimlanesDiagram-TC7HE7FX-M0GYW9Y3.js +0 -8
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
// "#QA PR 245" in prose becomes a link to that thread. Bots are asked to
|
|
2
|
+
// write a thread's title as #Title when they mention one, and the person
|
|
3
|
+
// may type the same. Resolution mirrors @mention resolution for bots:
|
|
4
|
+
// word-start, longest known title first, case-insensitive — titles hold
|
|
5
|
+
// spaces, so the longest known title following the "#" wins. Nothing else
|
|
6
|
+
// is a link: "#123" issue numbers, "# Heading" markers (the parser never
|
|
7
|
+
// hands those here) and unknown titles all stay plain text.
|
|
8
|
+
const WORD_CHAR = /[\p{L}\p{N}_]/u;
|
|
9
|
+
/** a "#" glued to one of these is not a word start: C#Title, ', ##x */
|
|
10
|
+
const NOT_WORD_START = /[\p{L}\p{N}_#&]/u;
|
|
11
|
+
/** Spacing a pasted thread token needs on each side: a lead space after
|
|
12
|
+
* any char the resolver refuses as a word start — word chars, but also #
|
|
13
|
+
* and &, so a paste after them cannot glue into ##Title — and a trail
|
|
14
|
+
* space before a plain word char. */
|
|
15
|
+
export function threadTokenSpacing(text, start, end) {
|
|
16
|
+
return {
|
|
17
|
+
lead: start > 0 && NOT_WORD_START.test(text[start - 1] ?? "") ? " " : "",
|
|
18
|
+
trail: end < text.length && WORD_CHAR.test(text[end] ?? "") ? " " : "",
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/** Only a title that is a real name can be linked: "123" would turn every
|
|
22
|
+
* issue number into a thread link. */
|
|
23
|
+
function linkable(title) {
|
|
24
|
+
return title.length > 0 && !/^\d+$/.test(title);
|
|
25
|
+
}
|
|
26
|
+
/** Known titles, longest first, each with every thread that carries it in
|
|
27
|
+
* the caller's (recency) order. */
|
|
28
|
+
function groupTitles(threads) {
|
|
29
|
+
const groups = new Map();
|
|
30
|
+
const seen = new Set();
|
|
31
|
+
for (const thread of threads) {
|
|
32
|
+
const title = thread.title.trim();
|
|
33
|
+
const key = `${thread.botId}/${thread.threadId}`;
|
|
34
|
+
if (!linkable(title) || seen.has(key))
|
|
35
|
+
continue;
|
|
36
|
+
seen.add(key);
|
|
37
|
+
const lower = title.toLowerCase();
|
|
38
|
+
const group = groups.get(lower);
|
|
39
|
+
if (group)
|
|
40
|
+
group.candidates.push({ ...thread, title });
|
|
41
|
+
else
|
|
42
|
+
groups.set(lower, { lower, candidates: [{ ...thread, title }] });
|
|
43
|
+
}
|
|
44
|
+
return [...groups.values()].sort((a, b) => b.lower.length - a.lower.length);
|
|
45
|
+
}
|
|
46
|
+
/** Which of several same-titled threads a mention means: the current
|
|
47
|
+
* bot's, else the most recently active, else the first — flagged so the
|
|
48
|
+
* link can say which bot it landed on. */
|
|
49
|
+
function pick(candidates, currentBotId) {
|
|
50
|
+
const resolved = (thread, ambiguous) => ({
|
|
51
|
+
botId: thread.botId, botName: thread.botName, threadId: thread.threadId, title: thread.title, ambiguous,
|
|
52
|
+
});
|
|
53
|
+
if (candidates.length === 1)
|
|
54
|
+
return resolved(candidates[0], false);
|
|
55
|
+
const own = candidates.filter((thread) => thread.botId === currentBotId);
|
|
56
|
+
if (own.length === 1)
|
|
57
|
+
return resolved(own[0], false);
|
|
58
|
+
const pool = own.length > 1 ? own : candidates;
|
|
59
|
+
const stamps = pool.map((thread) => thread.activeAt);
|
|
60
|
+
if (stamps.every((stamp) => typeof stamp === "number")) {
|
|
61
|
+
const newest = Math.max(...stamps);
|
|
62
|
+
const latest = pool.filter((thread) => thread.activeAt === newest);
|
|
63
|
+
if (latest.length === 1)
|
|
64
|
+
return resolved(latest[0], false);
|
|
65
|
+
}
|
|
66
|
+
return resolved(pool[0], true);
|
|
67
|
+
}
|
|
68
|
+
/** Split `text` into plain runs and linked "#Title" runs. The linked run
|
|
69
|
+
* keeps the person's own spelling ("#qa pr 245" stays as typed). */
|
|
70
|
+
export function resolveThreadRefs(text, threads, currentBotId) {
|
|
71
|
+
const groups = groupTitles(threads);
|
|
72
|
+
if (!groups.length || !text.includes("#"))
|
|
73
|
+
return [{ text }];
|
|
74
|
+
const spans = [];
|
|
75
|
+
let last = 0;
|
|
76
|
+
let at = text.indexOf("#");
|
|
77
|
+
while (at !== -1) {
|
|
78
|
+
const before = at > 0 ? text[at - 1] : "";
|
|
79
|
+
const start = at + 1;
|
|
80
|
+
let matched = null;
|
|
81
|
+
if (!before || !NOT_WORD_START.test(before)) {
|
|
82
|
+
for (const group of groups) {
|
|
83
|
+
const end = start + group.lower.length;
|
|
84
|
+
if (text.slice(start, end).toLowerCase() !== group.lower)
|
|
85
|
+
continue;
|
|
86
|
+
const after = text[end] ?? "";
|
|
87
|
+
if (after && WORD_CHAR.test(after))
|
|
88
|
+
continue;
|
|
89
|
+
matched = { end, ref: pick(group.candidates, currentBotId) };
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (matched) {
|
|
94
|
+
if (at > last)
|
|
95
|
+
spans.push({ text: text.slice(last, at) });
|
|
96
|
+
spans.push({ text: text.slice(at, matched.end), ref: matched.ref });
|
|
97
|
+
last = matched.end;
|
|
98
|
+
at = text.indexOf("#", matched.end);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
at = text.indexOf("#", at + 1);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (last < text.length)
|
|
105
|
+
spans.push({ text: text.slice(last) });
|
|
106
|
+
return spans.length ? spans : [{ text }];
|
|
107
|
+
}
|
|
108
|
+
const THREAD_URL_PREFIX = "openmausbot://thread/";
|
|
109
|
+
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
110
|
+
const ID_LIMIT = 256;
|
|
111
|
+
const plainId = (value) => {
|
|
112
|
+
// oxlint-disable-next-line no-control-regex -- a thread id never carries control characters
|
|
113
|
+
if (!value || value.length > ID_LIMIT || /[\u0000-\u001f]/.test(value))
|
|
114
|
+
return null;
|
|
115
|
+
return value;
|
|
116
|
+
};
|
|
117
|
+
/** The canonical link for a thread a person can copy. */
|
|
118
|
+
export function threadRefUrl(ref) {
|
|
119
|
+
return THREAD_URL_PREFIX + encodeURIComponent(ref.threadId) + "?bot=" + encodeURIComponent(ref.botId);
|
|
120
|
+
}
|
|
121
|
+
/** Parse a canonical thread link; null for anything else, including
|
|
122
|
+
* near-misses with extra path, extra query or a foreign scheme. */
|
|
123
|
+
export function parseThreadRefUrl(value) {
|
|
124
|
+
const raw = value.trim();
|
|
125
|
+
if (!raw.toLowerCase().startsWith(THREAD_URL_PREFIX))
|
|
126
|
+
return null;
|
|
127
|
+
let url;
|
|
128
|
+
try {
|
|
129
|
+
url = new URL(raw);
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
if (url.username || url.password || url.port || url.hash)
|
|
135
|
+
return null;
|
|
136
|
+
if (url.hostname.toLowerCase() !== "thread")
|
|
137
|
+
return null;
|
|
138
|
+
let threadId;
|
|
139
|
+
try {
|
|
140
|
+
threadId = decodeURIComponent(url.pathname.slice(1));
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
if (!threadId || threadId.includes("/") || url.pathname.slice(1) !== encodeURIComponent(threadId))
|
|
146
|
+
return null;
|
|
147
|
+
const keys = [...url.searchParams.keys()];
|
|
148
|
+
if (keys.some((key) => key !== "bot"))
|
|
149
|
+
return null;
|
|
150
|
+
const bot = url.searchParams.get("bot");
|
|
151
|
+
if (keys.length > 1 || (bot !== null && !bot))
|
|
152
|
+
return null;
|
|
153
|
+
const thread = plainId(threadId);
|
|
154
|
+
const owner = bot === null ? undefined : plainId(bot);
|
|
155
|
+
if (!thread || owner === null)
|
|
156
|
+
return null;
|
|
157
|
+
return owner ? { threadId: thread, botId: owner } : { threadId: thread };
|
|
158
|
+
}
|
|
159
|
+
/** Whether a value is shaped like a thread link at all — even one that
|
|
160
|
+
* fails strict parsing. Markdown must never hand such a URL to the shell
|
|
161
|
+
* as an external link, live or dead. */
|
|
162
|
+
export function looksLikeThreadRefUrl(value) {
|
|
163
|
+
return value.trim().toLowerCase().startsWith(THREAD_URL_PREFIX);
|
|
164
|
+
}
|
|
165
|
+
/** A raw thread id on its own: the UUID form the server mints. */
|
|
166
|
+
export function isThreadUuid(value) {
|
|
167
|
+
return UUID.test(value.trim());
|
|
168
|
+
}
|
|
169
|
+
/** Resolve an address (or bare UUID) against the visible threads. The
|
|
170
|
+
* link's own bot wins when it names one; otherwise the same preference a
|
|
171
|
+
* #Title mention uses: the open bot's thread, then the newest, then a
|
|
172
|
+
* deterministic first — flagged ambiguous so the chip can name the bot. */
|
|
173
|
+
export function resolveThreadRefAddress(threads, address, currentBotId) {
|
|
174
|
+
const candidates = threads.filter((thread) => thread.threadId === address.threadId);
|
|
175
|
+
if (!candidates.length)
|
|
176
|
+
return null;
|
|
177
|
+
const pinned = address.botId ? candidates.filter((thread) => thread.botId === address.botId) : [];
|
|
178
|
+
if (pinned.length === 1)
|
|
179
|
+
return { ...pinned[0], title: pinned[0].title.trim(), ambiguous: false };
|
|
180
|
+
if (pinned.length > 1)
|
|
181
|
+
return pick(pinned, currentBotId);
|
|
182
|
+
// a pinned owner missing from the visible threads is a dead reference:
|
|
183
|
+
// never fall through to another bot's same-id thread
|
|
184
|
+
if (address.botId)
|
|
185
|
+
return null;
|
|
186
|
+
return pick(candidates, currentBotId);
|
|
187
|
+
}
|
|
188
|
+
// ── sent-message serialization ─────────────────────────────────────────
|
|
189
|
+
// A resolvable #Title becomes a markdown link whose URL carries the thread
|
|
190
|
+
// id, so bots and API consumers keep the machine-readable reference. The
|
|
191
|
+
// label keeps the title readable; escapes keep brackets in titles parseable.
|
|
192
|
+
const escapeThreadLabel = (title) => title.replace(/[[\]\\]/g, "\\$&");
|
|
193
|
+
const unescapeThreadLabel = (label) => label.replace(/\\(.)/g, "$1");
|
|
194
|
+
/** The markdown shape a thread reference is sent as. */
|
|
195
|
+
export function threadRefMarkdown(ref) {
|
|
196
|
+
return "[" + escapeThreadLabel(ref.title) + "](" + threadRefUrl(ref) + ")";
|
|
197
|
+
}
|
|
198
|
+
const MARKDOWN_LINK_LABEL = /\[((?:\\.|[^\\\]])*)\]\(/y;
|
|
199
|
+
const INLINE_LINK_SPACE = " \t\n\r";
|
|
200
|
+
/** The CommonMark inline-link tail after "[label](": optional whitespace,
|
|
201
|
+
* an angle-bracket destination (spaces allowed) or a bare destination with
|
|
202
|
+
* balanced parentheses, optional whitespace, an optional quoted title, and
|
|
203
|
+
* the closing ")". Returns the tail length plus the bare destination for
|
|
204
|
+
* resolution, or null when no complete link closes here. */
|
|
205
|
+
function inlineLinkTail(rest, from) {
|
|
206
|
+
let at = from;
|
|
207
|
+
while (at < rest.length && INLINE_LINK_SPACE.includes(rest.charAt(at)))
|
|
208
|
+
at += 1;
|
|
209
|
+
const destStart = at;
|
|
210
|
+
let destination = "";
|
|
211
|
+
if (rest.charAt(at) === "<") {
|
|
212
|
+
const close = rest.indexOf(">", at + 1);
|
|
213
|
+
if (close === -1)
|
|
214
|
+
return null;
|
|
215
|
+
destination = rest.slice(at + 1, close);
|
|
216
|
+
at = close + 1;
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
let depth = 0;
|
|
220
|
+
while (at < rest.length) {
|
|
221
|
+
const ch = rest.charAt(at);
|
|
222
|
+
if (ch === "(")
|
|
223
|
+
depth += 1;
|
|
224
|
+
else if (ch === ")") {
|
|
225
|
+
if (depth === 0)
|
|
226
|
+
break;
|
|
227
|
+
depth -= 1;
|
|
228
|
+
}
|
|
229
|
+
else if (INLINE_LINK_SPACE.includes(ch))
|
|
230
|
+
break;
|
|
231
|
+
at += 1;
|
|
232
|
+
}
|
|
233
|
+
if (depth !== 0)
|
|
234
|
+
return null;
|
|
235
|
+
destination = rest.slice(destStart, at);
|
|
236
|
+
}
|
|
237
|
+
while (at < rest.length && INLINE_LINK_SPACE.includes(rest.charAt(at)))
|
|
238
|
+
at += 1;
|
|
239
|
+
const titleQuote = rest.charAt(at);
|
|
240
|
+
if (titleQuote === '"' || titleQuote === "'") {
|
|
241
|
+
const close = rest.indexOf(titleQuote, at + 1);
|
|
242
|
+
if (close === -1)
|
|
243
|
+
return null;
|
|
244
|
+
at = close + 1;
|
|
245
|
+
}
|
|
246
|
+
else if (titleQuote === "(") {
|
|
247
|
+
let depth = 1;
|
|
248
|
+
at += 1;
|
|
249
|
+
while (at < rest.length && depth > 0) {
|
|
250
|
+
const ch = rest.charAt(at);
|
|
251
|
+
if (ch === "(")
|
|
252
|
+
depth += 1;
|
|
253
|
+
else if (ch === ")")
|
|
254
|
+
depth -= 1;
|
|
255
|
+
at += 1;
|
|
256
|
+
}
|
|
257
|
+
if (depth !== 0)
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
while (at < rest.length && INLINE_LINK_SPACE.includes(rest.charAt(at)))
|
|
261
|
+
at += 1;
|
|
262
|
+
if (rest.charAt(at) !== ")")
|
|
263
|
+
return null;
|
|
264
|
+
return { tail: at + 1 - from, destination };
|
|
265
|
+
}
|
|
266
|
+
/** Split text into plain runs and the spans resolution must never touch.
|
|
267
|
+
* Every markdown link is kept verbatim — a #Title inside a label would
|
|
268
|
+
* nest links — and so is code: inline backticks and fenced blocks quote
|
|
269
|
+
* their contents, so only plain runs carry #Title mentions. */
|
|
270
|
+
function splitThreadLinkMarkdown(text) {
|
|
271
|
+
if (!text.includes("](") && !text.includes("`"))
|
|
272
|
+
return [{ raw: text }];
|
|
273
|
+
const spans = [];
|
|
274
|
+
let plain = "";
|
|
275
|
+
const flush = () => {
|
|
276
|
+
if (plain)
|
|
277
|
+
spans.push({ raw: plain });
|
|
278
|
+
plain = "";
|
|
279
|
+
};
|
|
280
|
+
let at = 0;
|
|
281
|
+
while (at < text.length) {
|
|
282
|
+
const rest = text.slice(at);
|
|
283
|
+
const guarded = (end) => {
|
|
284
|
+
flush();
|
|
285
|
+
spans.push({ raw: text.slice(at, end), code: true });
|
|
286
|
+
at = end;
|
|
287
|
+
};
|
|
288
|
+
if (rest.startsWith("```")) {
|
|
289
|
+
const close = rest.indexOf("```", 3);
|
|
290
|
+
guarded(close === -1 ? text.length : at + close + 3);
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
const ticks = /^`+/.exec(rest)?.[0];
|
|
294
|
+
if (ticks) {
|
|
295
|
+
const close = rest.indexOf(ticks, ticks.length);
|
|
296
|
+
guarded(close === -1 ? text.length : at + close + ticks.length);
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
MARKDOWN_LINK_LABEL.lastIndex = 0;
|
|
300
|
+
const open = MARKDOWN_LINK_LABEL.exec(rest);
|
|
301
|
+
const tail = open && inlineLinkTail(rest, open[0].length);
|
|
302
|
+
if (open && tail) {
|
|
303
|
+
flush();
|
|
304
|
+
const raw = rest.slice(0, open[0].length + tail.tail);
|
|
305
|
+
spans.push({ raw, link: { label: open[1], address: parseThreadRefUrl(tail.destination) } });
|
|
306
|
+
at += raw.length;
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
plain += text[at];
|
|
310
|
+
at += 1;
|
|
311
|
+
}
|
|
312
|
+
flush();
|
|
313
|
+
return spans.length ? spans : [{ raw: text }];
|
|
314
|
+
}
|
|
315
|
+
/** The text a send carries: resolvable #Title runs become canonical links;
|
|
316
|
+
* existing links and unknown titles pass through untouched. */
|
|
317
|
+
export function serializeThreadRefs(text, threads, currentBotId) {
|
|
318
|
+
if (!groupTitles(threads).length)
|
|
319
|
+
return text;
|
|
320
|
+
return splitThreadLinkMarkdown(text)
|
|
321
|
+
.map((span) => (span.link || span.code) ? span.raw : resolveThreadRefs(span.raw, threads, currentBotId)
|
|
322
|
+
.map((run) => run.ref ? threadRefMarkdown(run.ref) : run.text)
|
|
323
|
+
.join(""))
|
|
324
|
+
.join("");
|
|
325
|
+
}
|
|
326
|
+
export function splitThreadRefsForDisplay(text, threads, currentBotId) {
|
|
327
|
+
const spans = [];
|
|
328
|
+
for (const span of splitThreadLinkMarkdown(text)) {
|
|
329
|
+
if (span.link) {
|
|
330
|
+
const ref = span.link.address ? resolveThreadRefAddress(threads, span.link.address, currentBotId) : null;
|
|
331
|
+
spans.push(ref ? { text: unescapeThreadLabel(span.link.label), ref } : { text: span.raw });
|
|
332
|
+
}
|
|
333
|
+
else if (span.code) {
|
|
334
|
+
spans.push({ text: span.raw });
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
spans.push(...resolveThreadRefs(span.raw, threads, currentBotId));
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
return spans.length ? spans : [{ text }];
|
|
341
|
+
}
|
|
342
|
+
/** The whole paste is one thread reference — a canonical link, the markdown
|
|
343
|
+
* link shape messages carry, or a raw UUID — and it resolves to a visible
|
|
344
|
+
* thread: the #Title token the composer holds, plus what it resolved to.
|
|
345
|
+
* null when the paste is not a reference or names no known thread, so an
|
|
346
|
+
* unknown id stays plain text. */
|
|
347
|
+
export function threadTokenFromPaste(pasted, threads, currentBotId) {
|
|
348
|
+
const trimmed = pasted.trim();
|
|
349
|
+
if (!trimmed)
|
|
350
|
+
return null;
|
|
351
|
+
const wrapped = /^\[((?:\\.|[^\\\]])*)\]\((openmausbot:\/\/thread\/[^()\s]*)\)$/.exec(trimmed);
|
|
352
|
+
const address = wrapped
|
|
353
|
+
? parseThreadRefUrl(wrapped[2])
|
|
354
|
+
: parseThreadRefUrl(trimmed) ?? (isThreadUuid(trimmed) ? { threadId: trimmed } : null);
|
|
355
|
+
if (!address)
|
|
356
|
+
return null;
|
|
357
|
+
const ref = resolveThreadRefAddress(threads, address, currentBotId);
|
|
358
|
+
if (!ref)
|
|
359
|
+
return null;
|
|
360
|
+
// '#Title' is the token of choice, but a numeric or '#' prefixed title
|
|
361
|
+
// can never re-resolve through that spelling — those pastes hold the
|
|
362
|
+
// canonical markdown link instead, which sends and displays the same.
|
|
363
|
+
const title = ref.title.trim();
|
|
364
|
+
const token = linkable(title) && !title.startsWith("#") ? `#${title}` : threadRefMarkdown(ref);
|
|
365
|
+
return { token, ref };
|
|
366
|
+
}
|
|
367
|
+
const OPAQUE = new Set(["link", "linkReference", "image", "imageReference", "definition", "html", "mention"]);
|
|
368
|
+
export const THREAD_REF_NODE = "threadRef";
|
|
369
|
+
function linkNode(text, ref) {
|
|
370
|
+
return {
|
|
371
|
+
type: THREAD_REF_NODE,
|
|
372
|
+
data: {
|
|
373
|
+
hName: "span",
|
|
374
|
+
hProperties: {
|
|
375
|
+
"data-thread-bot": ref.botId,
|
|
376
|
+
"data-thread-bot-name": ref.botName,
|
|
377
|
+
"data-thread-id": ref.threadId,
|
|
378
|
+
"data-thread-title": ref.title,
|
|
379
|
+
"data-thread-ambiguous": ref.ambiguous ? "true" : "false",
|
|
380
|
+
},
|
|
381
|
+
},
|
|
382
|
+
children: [{ type: "text", value: text }],
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
/** A unified attacher: `remarkPlugins={[remarkThreadRefs(threads, botId)]}`. */
|
|
386
|
+
export function remarkThreadRefs(threads, currentBotId) {
|
|
387
|
+
return () => (tree) => {
|
|
388
|
+
if (!threads.length)
|
|
389
|
+
return;
|
|
390
|
+
const visit = (node) => {
|
|
391
|
+
if (!node.children || OPAQUE.has(node.type))
|
|
392
|
+
return;
|
|
393
|
+
node.children = node.children.flatMap((child) => {
|
|
394
|
+
if (child.type !== "text" || typeof child.value !== "string") {
|
|
395
|
+
visit(child);
|
|
396
|
+
return [child];
|
|
397
|
+
}
|
|
398
|
+
const spans = resolveThreadRefs(child.value, threads, currentBotId);
|
|
399
|
+
if (!spans.some((span) => span.ref))
|
|
400
|
+
return [child];
|
|
401
|
+
return spans.map((span) => (span.ref ? linkNode(span.text, span.ref) : { type: "text", value: span.text }));
|
|
402
|
+
});
|
|
403
|
+
};
|
|
404
|
+
visit(tree);
|
|
405
|
+
};
|
|
406
|
+
}
|
|
@@ -136,6 +136,8 @@ function windowsKnownDirs() {
|
|
|
136
136
|
// x.ai installer
|
|
137
137
|
join(localAppData, "agy", "bin"),
|
|
138
138
|
// Antigravity installer
|
|
139
|
+
// Cursor installer: cursor-agent.* and its `agent` copies (MOCA-272)
|
|
140
|
+
join(localAppData, "cursor-agent"),
|
|
139
141
|
join(home, ".local", "bin"),
|
|
140
142
|
// claude native installer
|
|
141
143
|
join(home, ".claude", "local"),
|
|
@@ -143,7 +145,9 @@ function windowsKnownDirs() {
|
|
|
143
145
|
// Factory droid installer (%USERPROFILE%\bin)
|
|
144
146
|
join(home, ".bun", "bin"),
|
|
145
147
|
join(home, ".deno", "bin"),
|
|
146
|
-
join(home, "go", "bin")
|
|
148
|
+
join(home, "go", "bin"),
|
|
149
|
+
join(process.env.ProgramFiles ?? "C:\\Program Files", "Docker", "Docker", "resources", "bin")
|
|
150
|
+
// Docker Desktop installer
|
|
147
151
|
];
|
|
148
152
|
}
|
|
149
153
|
var cached = null;
|
|
@@ -624,7 +628,7 @@ import { promisify } from "node:util";
|
|
|
624
628
|
|
|
625
629
|
// server/config.ts
|
|
626
630
|
import { homedir as homedir2 } from "node:os";
|
|
627
|
-
import { join as
|
|
631
|
+
import { join as join2 } from "node:path";
|
|
628
632
|
|
|
629
633
|
// node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
|
|
630
634
|
var external_exports = {};
|
|
@@ -15926,6 +15930,48 @@ function normalizeCronSchedule(value, after = Date.now()) {
|
|
|
15926
15930
|
return schedule;
|
|
15927
15931
|
}
|
|
15928
15932
|
|
|
15933
|
+
// shared/tool-scope.ts
|
|
15934
|
+
var SERVER_NAME = /^[a-z][a-z0-9_-]{0,31}$/;
|
|
15935
|
+
var CONTROL_CHARACTER = /[\x00-\x1f\x7f]/;
|
|
15936
|
+
var MAX_SELECTORS = 256;
|
|
15937
|
+
var MAX_SELECTOR_LENGTH = 1024;
|
|
15938
|
+
function validName(name) {
|
|
15939
|
+
return typeof name === "string" && name.trim().length > 0 && name.length <= MAX_SELECTOR_LENGTH && !CONTROL_CHARACTER.test(name) && !name.includes("*");
|
|
15940
|
+
}
|
|
15941
|
+
function selectorIdentity(selector) {
|
|
15942
|
+
if (selector.length > MAX_SELECTOR_LENGTH || CONTROL_CHARACTER.test(selector)) return;
|
|
15943
|
+
if (selector.startsWith("native:")) {
|
|
15944
|
+
const name = selector.slice(7);
|
|
15945
|
+
if (name === "*" || validName(name)) return { kind: "native", name };
|
|
15946
|
+
} else if (selector.startsWith("mcp:")) {
|
|
15947
|
+
const separator = selector.indexOf(":", 4);
|
|
15948
|
+
if (separator < 0) return;
|
|
15949
|
+
const server = selector.slice(4, separator);
|
|
15950
|
+
const name = selector.slice(separator + 1);
|
|
15951
|
+
if (SERVER_NAME.test(server) && (name === "*" || validName(name))) {
|
|
15952
|
+
return { kind: "mcp", server, name };
|
|
15953
|
+
}
|
|
15954
|
+
}
|
|
15955
|
+
}
|
|
15956
|
+
function parseToolScope(value) {
|
|
15957
|
+
if (value === void 0) return { ok: true, scope: void 0 };
|
|
15958
|
+
const invalid = { ok: false, error: "Tool selection must contain only valid allow and deny lists." };
|
|
15959
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return invalid;
|
|
15960
|
+
const prototype = Object.getPrototypeOf(value);
|
|
15961
|
+
if (prototype !== Object.prototype && prototype !== null) return invalid;
|
|
15962
|
+
const scope = {};
|
|
15963
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
15964
|
+
if (key !== "allow" && key !== "deny") return invalid;
|
|
15965
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
15966
|
+
if (!descriptor || !("value" in descriptor)) return invalid;
|
|
15967
|
+
const list = descriptor.value;
|
|
15968
|
+
if (!Array.isArray(list) || list.length > MAX_SELECTORS) return invalid;
|
|
15969
|
+
if (!Array.from(list).every((entry) => typeof entry === "string" && selectorIdentity(entry))) return invalid;
|
|
15970
|
+
scope[key] = [...new Set(list)];
|
|
15971
|
+
}
|
|
15972
|
+
return { ok: true, scope };
|
|
15973
|
+
}
|
|
15974
|
+
|
|
15929
15975
|
// server/new-bot-defaults.ts
|
|
15930
15976
|
var strings = (max, length) => external_exports.array(external_exports.string().max(length)).max(max);
|
|
15931
15977
|
var clock = external_exports.string().regex(/^(?:[01]\d|2[0-3]):[0-5]\d$/);
|
|
@@ -15960,6 +16006,10 @@ var botDefaultsProfileSchema = profilePatchSchema.extend({
|
|
|
15960
16006
|
browser: external_exports.boolean().optional(),
|
|
15961
16007
|
browserProfile: external_exports.string().max(100).nullable().optional(),
|
|
15962
16008
|
mcpServers: strings(100, 100).nullable().optional(),
|
|
16009
|
+
toolScope: external_exports.custom((value) => value !== void 0 && parseToolScope(value).ok, "Invalid tool selection").transform((value) => {
|
|
16010
|
+
const parsed = parseToolScope(value);
|
|
16011
|
+
return parsed.ok ? parsed.scope : value;
|
|
16012
|
+
}).nullable().optional(),
|
|
15963
16013
|
parkDirectMessages: external_exports.boolean().optional()
|
|
15964
16014
|
}).strict();
|
|
15965
16015
|
var scheduleSchema = external_exports.discriminatedUnion("type", [
|
|
@@ -16156,76 +16206,6 @@ var remoteMutationSchema = remoteEntrySchema.extend({
|
|
|
16156
16206
|
var CLOUD_SEAT_IDLE_STOP_MS = 5 * 6e4;
|
|
16157
16207
|
var CLOUD_SEAT_SLEEP_RETRY_MAX_MS = 10 * 6e4;
|
|
16158
16208
|
|
|
16159
|
-
// server/proxy-paths.ts
|
|
16160
|
-
import { existsSync as existsSync2 } from "node:fs";
|
|
16161
|
-
import { dirname as dirname2, join as join2 } from "node:path";
|
|
16162
|
-
import { fileURLToPath } from "node:url";
|
|
16163
|
-
var SERVER_ROOT = dirname2(fileURLToPath(import.meta.url));
|
|
16164
|
-
function resolveProxy(relative) {
|
|
16165
|
-
const source = join2(SERVER_ROOT, `${relative}.ts`);
|
|
16166
|
-
return existsSync2(source) ? source : join2(SERVER_ROOT, `${relative}.js`);
|
|
16167
|
-
}
|
|
16168
|
-
var SPAWNED_PROXIES = {
|
|
16169
|
-
browser: resolveProxy("browser-proxy"),
|
|
16170
|
-
localComputer: resolveProxy("local-computer-proxy"),
|
|
16171
|
-
permission: resolveProxy("permission-proxy"),
|
|
16172
|
-
containerMcp: resolveProxy("container-mcp"),
|
|
16173
|
-
vpsContainerMcp: resolveProxy("vps-container-mcp"),
|
|
16174
|
-
agents: resolveProxy("drivers/agents-proxy"),
|
|
16175
|
-
dweb: resolveProxy("drivers/dweb-proxy"),
|
|
16176
|
-
connectors: resolveProxy("connector-proxy"),
|
|
16177
|
-
mcpGate: resolveProxy("mcp-gate"),
|
|
16178
|
-
phone: resolveProxy("drivers/phone-proxy"),
|
|
16179
|
-
hook: resolveProxy("hooks/omb-hook"),
|
|
16180
|
-
// Loaded by the external `pi` process via `-e`, not by this server — but
|
|
16181
|
-
// resolved through the same single source of truth so the packaged layout
|
|
16182
|
-
// check can assert it ships.
|
|
16183
|
-
piMcpExtension: resolveProxy("drivers/pi-mcp-extension")
|
|
16184
|
-
};
|
|
16185
|
-
|
|
16186
|
-
// server/enterprise.ts
|
|
16187
|
-
var layerSchema = external_exports.object({
|
|
16188
|
-
customer: external_exports.string().min(1),
|
|
16189
|
-
features: external_exports.array(external_exports.string().min(1)),
|
|
16190
|
-
expiresAt: external_exports.string().nullable()
|
|
16191
|
-
});
|
|
16192
|
-
var DAY_MS = 24 * 60 * 6e4;
|
|
16193
|
-
|
|
16194
|
-
// server/sessions.ts
|
|
16195
|
-
var PAIRING_CODE_TTL_MS = 5 * 6e4;
|
|
16196
|
-
function daysMs(value, fallbackDays) {
|
|
16197
|
-
const days = Number(value);
|
|
16198
|
-
const ok = Number.isInteger(days) && days >= 1 && days <= 3650;
|
|
16199
|
-
return (ok ? days : fallbackDays) * 24 * 60 * 6e4;
|
|
16200
|
-
}
|
|
16201
|
-
var SESSION_TTL_MS = daysMs(process.env.OMB_SESSION_TTL_DAYS, 30);
|
|
16202
|
-
var SESSION_MAX_AGE_MS = daysMs(process.env.OMB_SESSION_MAX_DAYS, 180);
|
|
16203
|
-
var SESSION_RENEW_WHEN_LEFT_MS = SESSION_TTL_MS / 2;
|
|
16204
|
-
var STREAM_TICKET_TTL_MS = 5 * 6e4;
|
|
16205
|
-
var scopeSchema = external_exports.enum(["admin", "client"]);
|
|
16206
|
-
var sessionSchema = external_exports.object({
|
|
16207
|
-
id: external_exports.string().min(1),
|
|
16208
|
-
tokenHash: external_exports.string().length(64),
|
|
16209
|
-
label: external_exports.string().max(80),
|
|
16210
|
-
scopes: external_exports.array(scopeSchema).min(1),
|
|
16211
|
-
createdAt: external_exports.number(),
|
|
16212
|
-
lastSeenAt: external_exports.number(),
|
|
16213
|
-
expiresAt: external_exports.number(),
|
|
16214
|
-
/** Set when the session came from an account sign-in rather than a code. */
|
|
16215
|
-
userId: external_exports.string().max(256).optional(),
|
|
16216
|
-
email: external_exports.string().max(320).optional(),
|
|
16217
|
-
/** Set only by the internal verified-portal issuance path, never by email or pairing input. */
|
|
16218
|
-
membershipAuthority: external_exports.literal("portal").optional(),
|
|
16219
|
-
/** A browser sign-in (PairingCode `browser`): accepted only as its browser's cookie, never as a bearer token. */
|
|
16220
|
-
cookieOnly: external_exports.literal(true).optional(),
|
|
16221
|
-
/** Whose OMB Cloud a browser sign-in signed in to (its PairingCode `owner`). */
|
|
16222
|
-
owner: external_exports.string().max(254).optional()
|
|
16223
|
-
});
|
|
16224
|
-
var fileSchema = external_exports.object({ version: external_exports.literal(1), sessions: external_exports.array(sessionSchema) });
|
|
16225
|
-
|
|
16226
|
-
// server/cloud-home.ts
|
|
16227
|
-
var CLOUD_PAIRING_NONCE_MS = 10 * 6e4;
|
|
16228
|
-
|
|
16229
16209
|
// server/config.ts
|
|
16230
16210
|
var optionalText = external_exports.string().optional();
|
|
16231
16211
|
var SSH_ALIAS = /^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$/;
|
|
@@ -16433,7 +16413,11 @@ var featureConfigSchema = external_exports.object({
|
|
|
16433
16413
|
* may offer a per-second-billed cloud seat, but only through an explicit
|
|
16434
16414
|
* consent card or a configured allowlist — never a silent Auto default.
|
|
16435
16415
|
* Off until baked; see cloudOverflowEnabled for how to enable it by hand. */
|
|
16436
|
-
cloudOverflow: external_exports.boolean().optional()
|
|
16416
|
+
cloudOverflow: external_exports.boolean().optional(),
|
|
16417
|
+
/** Opt-in shared skills library: one store at the data dir that bots
|
|
16418
|
+
* read by assignment instead of per-workspace copies. Off until
|
|
16419
|
+
* explicitly enabled — see skillsLibraryEnabled. */
|
|
16420
|
+
skillsLibrary: external_exports.boolean().optional()
|
|
16437
16421
|
});
|
|
16438
16422
|
var onboardingConfigSchema = external_exports.object({
|
|
16439
16423
|
/** ISO timestamp of finishing (or skipping to the end of) the welcome flow. */
|
|
@@ -16515,6 +16499,7 @@ var appConfigSchema = external_exports.object({
|
|
|
16515
16499
|
phone: external_exports.enum(["ios", "android"]).optional()
|
|
16516
16500
|
}).optional(),
|
|
16517
16501
|
mistral: external_exports.object({ key: optionalText }).optional(),
|
|
16502
|
+
cerebras: external_exports.object({ key: optionalText }).optional(),
|
|
16518
16503
|
xai: external_exports.object({ key: optionalText, url: optionalText }).optional(),
|
|
16519
16504
|
/** Anthropic API key for Claude Code billed per token, handed only to
|
|
16520
16505
|
* Claude instances; `url` only for a proxy or a test double. Never a
|
|
@@ -16585,6 +16570,15 @@ var appConfigSchema = external_exports.object({
|
|
|
16585
16570
|
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(),
|
|
16586
16571
|
jobs: external_exports.object({ roomRouting: external_exports.boolean().optional() }).optional()
|
|
16587
16572
|
}).optional(),
|
|
16573
|
+
/** Live calls: an OpenAI project key for GPT-Live, kept apart from every
|
|
16574
|
+
* other OpenAI credential so a Live call never bills an image or engine key
|
|
16575
|
+
* the user did not hand to it. `voice` is a GPT-Live built-in voice name. */
|
|
16576
|
+
live: external_exports.object({
|
|
16577
|
+
key: optionalText,
|
|
16578
|
+
voice: external_exports.string().trim().max(40).regex(/^[a-z]*$/, "a Live voice is a lowercase built-in voice name").optional(),
|
|
16579
|
+
readTypedReplies: external_exports.boolean().optional(),
|
|
16580
|
+
idleMinutes: external_exports.number().int().min(1).max(60).optional()
|
|
16581
|
+
}).optional(),
|
|
16588
16582
|
/** Avatar provider credentials stay separate; choosing a router never reuses a cloud key. */
|
|
16589
16583
|
imageGen: external_exports.object({
|
|
16590
16584
|
provider: external_exports.enum(["openai", "xai", "custom"]).optional(),
|
|
@@ -16654,10 +16648,10 @@ var storedAppConfigSchema = appConfigSchema.extend({
|
|
|
16654
16648
|
var appConfigPatchSchema = appConfigSchema.omit({ instances: true, mcpServers: true, cliStartup: true, customDomain: true }).extend({ threads: threadsPatchSchema.optional(), newBots: newBotsPatchSchema.optional() });
|
|
16655
16649
|
var jsonObjectSchema = external_exports.record(external_exports.string(), external_exports.json());
|
|
16656
16650
|
var DEFAULT_CAPTURE_QUIET_MS = 2 * 6e4;
|
|
16657
|
-
var DATA_DIR = process.env.OMB_DATA_DIR ??
|
|
16658
|
-
var LEGACY_DATA_DIR =
|
|
16659
|
-
var EVENTS_DIR =
|
|
16660
|
-
var NATIVE_DIR =
|
|
16651
|
+
var DATA_DIR = process.env.OMB_DATA_DIR ?? join2(homedir2(), ".openmausbot");
|
|
16652
|
+
var LEGACY_DATA_DIR = join2(homedir2(), ".opengrokbot");
|
|
16653
|
+
var EVENTS_DIR = join2(DATA_DIR, "events");
|
|
16654
|
+
var NATIVE_DIR = join2(DATA_DIR, "native");
|
|
16661
16655
|
var OPENAI_API_INSTANCE = "openai";
|
|
16662
16656
|
var OPENROUTER_API_INSTANCE = "openrouter";
|
|
16663
16657
|
var XAI_API_INSTANCE = "xaiApi";
|
|
@@ -16687,6 +16681,34 @@ var API_KEY_FLEET = {
|
|
|
16687
16681
|
}
|
|
16688
16682
|
};
|
|
16689
16683
|
|
|
16684
|
+
// server/proxy-paths.ts
|
|
16685
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
16686
|
+
import { dirname as dirname2, join as join3 } from "node:path";
|
|
16687
|
+
import { fileURLToPath } from "node:url";
|
|
16688
|
+
var SERVER_ROOT = dirname2(fileURLToPath(import.meta.url));
|
|
16689
|
+
function resolveProxy(relative) {
|
|
16690
|
+
const source = join3(SERVER_ROOT, `${relative}.ts`);
|
|
16691
|
+
return existsSync2(source) ? source : join3(SERVER_ROOT, `${relative}.js`);
|
|
16692
|
+
}
|
|
16693
|
+
var SPAWNED_PROXIES = {
|
|
16694
|
+
browser: resolveProxy("browser-proxy"),
|
|
16695
|
+
localComputer: resolveProxy("local-computer-proxy"),
|
|
16696
|
+
permission: resolveProxy("permission-proxy"),
|
|
16697
|
+
containerMcp: resolveProxy("container-mcp"),
|
|
16698
|
+
vpsContainerMcp: resolveProxy("vps-container-mcp"),
|
|
16699
|
+
agents: resolveProxy("drivers/agents-proxy"),
|
|
16700
|
+
dweb: resolveProxy("drivers/dweb-proxy"),
|
|
16701
|
+
connectors: resolveProxy("connector-proxy"),
|
|
16702
|
+
mcpGate: resolveProxy("mcp-gate"),
|
|
16703
|
+
mcpRemote: resolveProxy("mcp-remote-proxy"),
|
|
16704
|
+
phone: resolveProxy("drivers/phone-proxy"),
|
|
16705
|
+
hook: resolveProxy("hooks/omb-hook"),
|
|
16706
|
+
// Loaded by the external `pi` process via `-e`, not by this server — but
|
|
16707
|
+
// resolved through the same single source of truth so the packaged layout
|
|
16708
|
+
// check can assert it ships.
|
|
16709
|
+
piMcpExtension: resolveProxy("drivers/pi-mcp-extension")
|
|
16710
|
+
};
|
|
16711
|
+
|
|
16690
16712
|
// server/container-computer.ts
|
|
16691
16713
|
var run = promisify(execFile2);
|
|
16692
16714
|
var CUA_DRIVER_VERSION = "0.20.0";
|