openmausbot 0.1.86 → 0.1.88
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/README.md +4 -3
- package/dist/assets/{abnfDiagram-O67JEVCF-DpUZNnFD.js → abnfDiagram-O67JEVCF-qd9VD3GV.js} +1 -1
- package/dist/assets/{arc-DW7mrI4p.js → arc-3RwkUjRe.js} +1 -1
- package/dist/assets/{architectureDiagram-NJMV4G6O-CyeZ5WLZ.js → architectureDiagram-NJMV4G6O-CFKSJS7a.js} +1 -1
- package/dist/assets/{blockDiagram-BEXU5L5S-CBVaNjO7.js → blockDiagram-BEXU5L5S-CG1rEfWD.js} +1 -1
- package/dist/assets/{c4Diagram-YGBWAQC7-sf8d41uT.js → c4Diagram-YGBWAQC7-CBW9N664.js} +1 -1
- package/dist/assets/channel-CnRnO3tF.js +1 -0
- package/dist/assets/{chunk-2Q5K7J3B-BOGvy7Y_.js → chunk-2Q5K7J3B-2mRfTksB.js} +1 -1
- package/dist/assets/{chunk-3FUC2YCW-BiF9zyiz.js → chunk-3FUC2YCW-Cv1dpErj.js} +2 -2
- package/dist/assets/{chunk-5VM5RSS4-EFuZ1SBZ.js → chunk-5VM5RSS4-B7IACJp-.js} +1 -1
- package/dist/assets/{chunk-GTNCS2PH-pJLNSAT7.js → chunk-GTNCS2PH-An0hZtYa.js} +1 -1
- package/dist/assets/{chunk-GWA4HPMP-Cb-3_3IT.js → chunk-GWA4HPMP-BcJB2nEm.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-DAf5aogn.js → chunk-JWPE2WC7-CV_W7TN7.js} +1 -1
- package/dist/assets/{chunk-NETBCI7D-DR3kiwZz.js → chunk-NETBCI7D-Bcs_kwnA.js} +1 -1
- package/dist/assets/{chunk-WEXAMYUT-BmU8PeyM.js → chunk-WEXAMYUT-lrx_DxFP.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-Buh8g8OT.js → chunk-XXDRQBXY-DabHlLGM.js} +1 -1
- package/dist/assets/{classDiagram-v2-NBCMYWYE-BurfmMWM.js → classDiagram-v2-NBCMYWYE-BnHj6P7d.js} +1 -1
- package/dist/assets/{cose-bilkent-JH36ORCC-KkGZUEkU.js → cose-bilkent-JH36ORCC-CfLp6UDi.js} +1 -1
- package/dist/assets/{cynefin-OW5HDTMX-C3PpV96d.js → cynefin-OW5HDTMX-Dm4dbVA1.js} +1 -1
- package/dist/assets/{cynefinDiagram-VND7K2PF-Dt8JwJaI.js → cynefinDiagram-VND7K2PF-B9mH-PID.js} +1 -1
- package/dist/assets/{dagre-6A5THRUB-DkjN8266.js → dagre-6A5THRUB-DDc_NVQJ.js} +1 -1
- package/dist/assets/{diagram-22UHCM2B-DUAJS0JV.js → diagram-22UHCM2B-lwr8Ip8-.js} +1 -1
- package/dist/assets/{diagram-3UASUU5V-Dw4KhF1a.js → diagram-3UASUU5V-BLLe6dmI.js} +1 -1
- package/dist/assets/{diagram-ATOU4E4O-Dd1gmQqf.js → diagram-ATOU4E4O-6eUIq4lz.js} +1 -1
- package/dist/assets/{diagram-CDSNMT55-CjwGs0Yw.js → diagram-CDSNMT55-O-pRN7VQ.js} +1 -1
- package/dist/assets/{diagram-MLGK6HIB-BL-69Zrq.js → diagram-MLGK6HIB-BietvGAI.js} +1 -1
- package/dist/assets/{diagram-MPIPVDR6-5Zi2N7wo.js → diagram-MPIPVDR6-Bd2Cl62X.js} +1 -1
- package/dist/assets/{ebnfDiagram-ZINNZB2B-D9NekWUX.js → ebnfDiagram-ZINNZB2B-B6iDy69W.js} +1 -1
- package/dist/assets/{elk-276RUBZZ-CkaSQjxw.js → elk-276RUBZZ-DQVHfKe0.js} +1 -1
- package/dist/assets/{erDiagram-OPXOYQCR-CdAVBV8-.js → erDiagram-OPXOYQCR-BWdVp2tQ.js} +1 -1
- package/dist/assets/{flowDiagram-KWPJA3E3-Bm_lWWH7.js → flowDiagram-KWPJA3E3-WLE6rKB4.js} +1 -1
- package/dist/assets/{ganttDiagram-FUAMR5RP-FZ8ejYWJ.js → ganttDiagram-FUAMR5RP-BbjS-4gn.js} +1 -1
- package/dist/assets/{gitGraphDiagram-X574FWY7-Dlv9rizZ.js → gitGraphDiagram-X574FWY7-Dpy2QSGq.js} +1 -1
- package/dist/assets/index-BIMdIbaw.css +1 -0
- package/dist/assets/index-CHnODCr2.js +583 -0
- package/dist/assets/{index-h9FFNwTu.js → index-CIaHn5NS.js} +1 -1
- package/dist/assets/{infoDiagram-VRGFBTTK-BdQ_gW5B.js → infoDiagram-VRGFBTTK-D_jKWlKe.js} +1 -1
- package/dist/assets/{ishikawaDiagram-OU5B5YK6-EwssEp2X.js → ishikawaDiagram-OU5B5YK6-DLJohSuK.js} +1 -1
- package/dist/assets/{journeyDiagram-ZHPQQLJL-BX1G1vOd.js → journeyDiagram-ZHPQQLJL-DPOFj0qN.js} +1 -1
- package/dist/assets/{kanban-definition-PNTS6WVX-BB4kAsyC.js → kanban-definition-PNTS6WVX-BI7v-xpK.js} +1 -1
- package/dist/assets/{linear-3su2TG93.js → linear-C2pWPSxO.js} +1 -1
- package/dist/assets/{mermaid.core-CDD1E0s9.js → mermaid.core-CR-j5WQU.js} +5 -5
- package/dist/assets/{mindmap-definition-NLK3R4M7-Ba50MsNK.js → mindmap-definition-NLK3R4M7-lz9GvXRh.js} +1 -1
- package/dist/assets/{pegDiagram-GJSIUBJH-DZLkkZlM.js → pegDiagram-GJSIUBJH-Dl4_ia61.js} +1 -1
- package/dist/assets/{pieDiagram-5QR66LMP-DaTz77Uq.js → pieDiagram-5QR66LMP-DWuT3n88.js} +1 -1
- package/dist/assets/{quadrantDiagram-O4NWA36T-CpaiidpR.js → quadrantDiagram-O4NWA36T-DXWe9Jx7.js} +1 -1
- package/dist/assets/{railroadDiagram-XR7U4H2S-mN4JrMeI.js → railroadDiagram-XR7U4H2S-COY1IBVY.js} +1 -1
- package/dist/assets/{requirementDiagram-PLB6GJNP-BSNDRpg7.js → requirementDiagram-PLB6GJNP-DnyVuhsQ.js} +1 -1
- package/dist/assets/{sankeyDiagram-IPEJSGJF-FngzGNug.js → sankeyDiagram-IPEJSGJF-BSc4NdGK.js} +1 -1
- package/dist/assets/{sequenceDiagram-PO4LG4MO-CM4pCC3L.js → sequenceDiagram-PO4LG4MO-kqEU2EGq.js} +1 -1
- package/dist/assets/{sizeCapture-INFHLROL-CRICHXkw.js → sizeCapture-INFHLROL-BvB4bkWD.js} +1 -1
- package/dist/assets/{stateDiagram-v2-GCMORJYK-oyTWpxH2.js → stateDiagram-v2-GCMORJYK-BIrqNv7y.js} +1 -1
- package/dist/assets/{swimlanes-2SLR337P-D9h9mPBq.js → swimlanes-2SLR337P-CFnalQUE.js} +1 -1
- package/dist/assets/swimlanesDiagram-TC7HE7FX-C4a8qNGj.js +8 -0
- package/dist/assets/{timeline-definition-EJHVYXUP-BJUlN9bW.js → timeline-definition-EJHVYXUP-CsSWJnSQ.js} +1 -1
- package/dist/assets/{usecaseDiagram-POWQR4AR-3uBNkbAb.js → usecaseDiagram-POWQR4AR-DkAmJ1EE.js} +1 -1
- package/dist/assets/{vennDiagram-UO4OBE2U-D2odalzT.js → vennDiagram-UO4OBE2U-C3vd9drL.js} +1 -1
- package/dist/assets/{wardleyDiagram-VNRHLVJA-DHgFaB9S.js → wardleyDiagram-VNRHLVJA-j26CM-pi.js} +1 -1
- package/dist/assets/{xychartDiagram-PMCCYNJV-w5GYM2h0.js → xychartDiagram-PMCCYNJV-BYu1Q_at.js} +1 -1
- package/dist/index.html +2 -2
- package/dist-server/browser-proxy.js +2 -1
- package/dist-server/companion/src/routes.js +8 -1
- package/dist-server/connector-proxy.js +105 -1
- package/dist-server/container-mcp.js +1032 -165
- package/dist-server/drivers/agents-proxy.js +338 -29
- package/dist-server/drivers/phone-proxy.js +33 -2
- package/dist-server/index.js +40590 -31811
- package/dist-server/local-computer-proxy.js +5 -3
- package/dist-server/local-computer.js +53 -17
- package/dist-server/mcp-gate.js +2 -0
- package/dist-server/mcp-server.js +5 -3
- package/dist-server/message-search.worker.js +52 -0
- package/dist-server/openmausbot.js +4457 -1399
- package/dist-server/pair-cli.js +4457 -1399
- package/dist-server/permission-proxy.js +29 -9
- package/dist-server/server/admission.js +81 -0
- package/dist-server/server/aside-queue.js +294 -0
- package/dist-server/server/attachments.js +98 -1
- package/dist-server/server/auto-approve.js +4 -3
- package/dist-server/server/auto-vm-claims.js +1 -0
- package/dist-server/server/bot-attachment.js +107 -0
- package/dist-server/server/bot-overview.js +19 -0
- package/dist-server/server/bot-package.js +18 -292
- package/dist-server/server/bot-profile.js +5 -17
- package/dist-server/server/box-create-idempotency.js +3 -3
- package/dist-server/server/box.js +57 -55
- package/dist-server/server/browser-proxy.js +3 -2
- package/dist-server/server/browser-runtime.js +37 -1
- package/dist-server/server/cli-api-setup.js +1 -0
- package/dist-server/server/cli.js +2 -2
- package/dist-server/server/command-allowlist.js +154 -0
- package/dist-server/server/composio.js +297 -1
- package/dist-server/server/computer-wait.js +22 -9
- package/dist-server/server/config.js +84 -12
- package/dist-server/server/connector-advertisement.js +165 -0
- package/dist-server/server/connector-proxy.js +14 -2
- package/dist-server/server/connector-verdict.js +168 -0
- package/dist-server/server/container-computer.js +61 -1
- package/dist-server/server/contracts.js +22 -0
- package/dist-server/server/default-model-selection.js +9 -0
- package/dist-server/server/delegations.js +85 -34
- package/dist-server/server/drivers/acp/core.js +80 -20
- package/dist-server/server/drivers/acp/cursor.js +3 -7
- package/dist-server/server/drivers/acp/droid.js +4 -7
- package/dist-server/server/drivers/acp/gemini.js +4 -4
- package/dist-server/server/drivers/acp/grok.js +4 -9
- package/dist-server/server/drivers/acp/hermes.js +3 -5
- package/dist-server/server/drivers/acp/opencode-go.js +2 -5
- package/dist-server/server/drivers/acp/qwen.js +9 -15
- package/dist-server/server/drivers/agents-call.js +179 -13
- package/dist-server/server/drivers/agents-catalog.js +134 -10
- package/dist-server/server/drivers/boxagent.js +160 -36
- package/dist-server/server/drivers/builtIn.js +2 -0
- package/dist-server/server/drivers/chat-box-tools.js +90 -0
- package/dist-server/server/drivers/chat-images.js +54 -0
- package/dist-server/server/drivers/chat-mcp-tools.js +38 -8
- package/dist-server/server/drivers/chat-tool-approval.js +70 -18
- package/dist-server/server/drivers/claude.js +38 -19
- package/dist-server/server/drivers/codex-catalog.js +14 -6
- package/dist-server/server/drivers/codex-identity.js +2 -1
- package/dist-server/server/drivers/codex-instructions.js +48 -6
- package/dist-server/server/drivers/codex-release.js +48 -0
- package/dist-server/server/drivers/codex-tool-surface.js +20 -0
- package/dist-server/server/drivers/codex.js +102 -55
- package/dist-server/server/drivers/mistral.js +115 -0
- package/dist-server/server/drivers/openai-chat.js +180 -33
- package/dist-server/server/drivers/openai-compat.js +1 -0
- package/dist-server/server/drivers/permission-command.js +35 -0
- package/dist-server/server/drivers/phone-proxy.js +47 -2
- package/dist-server/server/drivers/pi.js +306 -28
- package/dist-server/server/drivers/prompt-split.js +110 -0
- package/dist-server/server/drivers/session-idle.js +29 -0
- package/dist-server/server/env-path.js +15 -0
- package/dist-server/server/fleet.js +1 -1
- package/dist-server/server/group-thread-usage.js +103 -0
- package/dist-server/server/guarded-requests.js +2 -2
- package/dist-server/server/harness/registry.js +1 -0
- package/dist-server/server/harness-capabilities.js +46 -0
- package/dist-server/server/hosted-models.js +1 -1
- package/dist-server/server/index.js +2396 -505
- package/dist-server/server/local-computer.js +72 -23
- package/dist-server/server/managed-desktop.js +1 -1
- package/dist-server/server/mcp-bridge.js +6 -6
- package/dist-server/server/message-db.js +22 -42
- package/dist-server/server/message-file.js +9 -1
- package/dist-server/server/message-search-query.js +43 -0
- package/dist-server/server/message-search-worker.js +80 -0
- package/dist-server/server/message-search.worker.js +21 -0
- package/dist-server/server/model-requests.js +248 -0
- package/dist-server/server/new-bot-defaults.js +115 -0
- package/dist-server/server/options-card.js +21 -0
- package/dist-server/server/org-library.js +1065 -0
- package/dist-server/server/package-export.js +627 -39
- package/dist-server/server/package-import.js +458 -0
- package/dist-server/server/package-parts.js +87 -0
- package/dist-server/server/peer-approval.js +16 -7
- package/dist-server/server/peer-delivery.js +60 -0
- package/dist-server/server/peer-roster.js +67 -0
- package/dist-server/server/presets.js +492 -0
- package/dist-server/server/profile-requests.js +66 -39
- package/dist-server/server/profile-revision.js +9 -1
- package/dist-server/server/profile-versions.js +6 -3
- package/dist-server/server/provider-key-check.js +3 -2
- package/dist-server/server/published-teams.js +80 -0
- package/dist-server/server/room-handoffs.js +19 -3
- package/dist-server/server/routes/bot-presets.js +25 -0
- package/dist-server/server/routine-requests.js +35 -10
- package/dist-server/server/routines.js +41 -1
- package/dist-server/server/section-context.js +10 -0
- package/dist-server/server/skill-fetch.js +72 -14
- package/dist-server/server/skills.js +35 -61
- package/dist-server/server/steer-queue.js +3 -2
- package/dist-server/server/store.js +291 -17
- package/dist-server/server/surface.js +10 -4
- package/dist-server/server/system-prompt.js +38 -6
- package/dist-server/server/team-backup.js +6 -2
- package/dist-server/server/team-computers.js +10 -0
- package/dist-server/server/team-library.js +6 -2
- package/dist-server/server/team-manifest.js +4 -3
- package/dist-server/server/team-setup-requests.js +139 -27
- package/dist-server/server/thread-events.js +20 -2
- package/dist-server/server/tightening-requests.js +405 -0
- package/dist-server/server/tts/chatterbox.js +27 -5
- package/dist-server/server/turn-context.js +2 -0
- package/dist-server/server/turn-watchdog.js +12 -3
- package/dist-server/server/usage-ledger.js +19 -2
- package/dist-server/server/vps-computer.js +139 -51
- package/dist-server/server/vps-container-mcp.js +7 -0
- package/dist-server/server/vps-ssh.js +36 -7
- package/dist-server/server/workspace-backup-policy.js +12 -0
- package/dist-server/server/workspace-backup.js +42 -5
- package/dist-server/server/workspace-backup.worker.js +10 -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 +207 -12
- package/dist-server/shared/bot-profile.js +15 -0
- package/dist-server/shared/command-allowlist.js +1 -0
- package/dist-server/shared/credential-request.js +6 -2
- package/dist-server/shared/line-diff.js +23 -0
- package/dist-server/shared/model-request.js +1 -0
- package/dist-server/shared/options-card.js +51 -0
- package/dist-server/shared/package-format.js +967 -0
- package/dist-server/shared/profile-request.js +5 -4
- package/dist-server/shared/skill-md.js +64 -0
- package/dist-server/shared/team-backup.js +7 -0
- package/dist-server/shared/tightening-request.js +87 -0
- package/dist-server/shared/wire.js +4 -0
- package/dist-server/src/lib/composer-attachments.js +25 -3
- package/dist-server/vps-container-mcp.js +1132 -171
- package/dist-server/workspace-backup.worker.js +19474 -0
- package/enterprise/README.md +1 -1
- package/package.json +1 -1
- package/dist/assets/channel-BXFkgus9.js +0 -1
- package/dist/assets/index-BGgqlWPJ.css +0 -1
- package/dist/assets/index-BtHo8ZKl.js +0 -577
- package/dist/assets/swimlanesDiagram-TC7HE7FX-BWrvB2ak.js +0 -8
|
@@ -70,19 +70,21 @@ function sameDriverFileIdentity(expected, actual) {
|
|
|
70
70
|
}
|
|
71
71
|
function decodeLegacyDescriptor(value, platform) {
|
|
72
72
|
const supportedPlatform = legacyPlatform(platform);
|
|
73
|
-
if (!supportedPlatform || !value ||
|
|
73
|
+
if (!supportedPlatform || !value ||
|
|
74
|
+
!(value.mode === "embedded" || (supportedPlatform === "darwin" && value.mode === "standalone")) ||
|
|
75
|
+
(Object.hasOwn(value, "status") && value.status !== "ready") ||
|
|
76
|
+
typeof value.socketPath !== "string" || !value.socketPath ||
|
|
77
|
+
typeof value.mcpCommand !== "string" || !value.mcpCommand.trim()) {
|
|
74
78
|
return null;
|
|
75
79
|
}
|
|
76
|
-
if (
|
|
80
|
+
if (!Array.isArray(value.mcpArgs) || value.mcpArgs[0] !== "mcp")
|
|
77
81
|
return null;
|
|
78
|
-
if (value.mcpEnv !==
|
|
79
|
-
(!value.mcpEnv || typeof value.mcpEnv !== "object" || Array.isArray(value.mcpEnv))) {
|
|
82
|
+
if (!value.mcpEnv || typeof value.mcpEnv !== "object" || Array.isArray(value.mcpEnv))
|
|
80
83
|
return null;
|
|
81
|
-
|
|
82
|
-
const args = value.mcpArgs ?? ["mcp"];
|
|
84
|
+
const args = value.mcpArgs;
|
|
83
85
|
if (!args.every((arg) => typeof arg === "string"))
|
|
84
86
|
return null;
|
|
85
|
-
const env = value.mcpEnv
|
|
87
|
+
const env = value.mcpEnv;
|
|
86
88
|
if (!Object.values(env).every((entry) => typeof entry === "string"))
|
|
87
89
|
return null;
|
|
88
90
|
return {
|
|
@@ -288,31 +290,78 @@ function validateLegacyDescriptorRuntime(descriptorFile, platform, { uid = proce
|
|
|
288
290
|
return false;
|
|
289
291
|
}
|
|
290
292
|
}
|
|
293
|
+
/** An older fallback cannot override a present but unavailable, malformed, or
|
|
294
|
+
* unreadable descriptor at a more specific location. */
|
|
295
|
+
function firstPresentCuaDescriptor(candidates) {
|
|
296
|
+
for (const file of new Set(candidates)) {
|
|
297
|
+
try {
|
|
298
|
+
lstatSync(file);
|
|
299
|
+
return file;
|
|
300
|
+
}
|
|
301
|
+
catch (error) {
|
|
302
|
+
if (error.code !== "ENOENT")
|
|
303
|
+
return file;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
291
308
|
export function readCuaConnection({ platform = process.platform, userData = process.env.OMB_USER_DATA, home = homedir(), validateLinuxRuntime = validateLinuxDescriptorRuntime, validateLegacyRuntime = validateLegacyDescriptorRuntime, } = {}) {
|
|
292
309
|
const candidates = userData ? [join(userData, "cua-connection.json")] : [];
|
|
293
|
-
if (platform === "darwin") {
|
|
294
|
-
// Legacy/dev fallback
|
|
310
|
+
if (platform === "darwin" && !userData) {
|
|
311
|
+
// Legacy/dev fallback only when Electron did not provide its exact path.
|
|
295
312
|
for (const directory of ["OpenMausBot", "openmausbot", "OpenGrokBot", "opengrokbot"]) {
|
|
296
313
|
candidates.push(join(home, "Library", "Application Support", directory, "cua-connection.json"));
|
|
297
314
|
}
|
|
298
315
|
}
|
|
299
|
-
|
|
300
|
-
|
|
316
|
+
const file = firstPresentCuaDescriptor(candidates);
|
|
317
|
+
if (!file)
|
|
318
|
+
return null;
|
|
319
|
+
try {
|
|
320
|
+
const raw = JSON.parse(readFileSync(file, "utf8"));
|
|
321
|
+
if (platform === "linux") {
|
|
322
|
+
const decoded = decodeLinuxDescriptor(raw);
|
|
323
|
+
if (decoded && validateLinuxRuntime(file, raw))
|
|
324
|
+
return decoded;
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
const decoded = decodeLegacyDescriptor(raw, platform);
|
|
328
|
+
if (decoded && validateLegacyRuntime(file, platform))
|
|
329
|
+
return decoded;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
catch {
|
|
333
|
+
// Missing, invalid, tampered, or stale descriptors are unavailable.
|
|
334
|
+
}
|
|
335
|
+
return null;
|
|
336
|
+
}
|
|
337
|
+
/** An unavailable descriptor is diagnostic only. It never becomes a connection
|
|
338
|
+
* and only a private, well-formed macOS/Windows descriptor may supply text. */
|
|
339
|
+
export function readCuaUnavailableReason({ platform = process.platform, userData = process.env.OMB_USER_DATA, home = homedir(), } = {}) {
|
|
340
|
+
if (!legacyPlatform(platform))
|
|
341
|
+
return null;
|
|
342
|
+
const candidates = userData ? [join(userData, "cua-connection.json")] : [];
|
|
343
|
+
if (platform === "darwin" && !userData) {
|
|
344
|
+
for (const directory of ["OpenMausBot", "openmausbot", "OpenGrokBot", "opengrokbot"]) {
|
|
345
|
+
candidates.push(join(home, "Library", "Application Support", directory, "cua-connection.json"));
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
const file = firstPresentCuaDescriptor(candidates);
|
|
349
|
+
if (!file)
|
|
350
|
+
return null;
|
|
351
|
+
try {
|
|
352
|
+
if (validateLegacyDescriptorRuntime(file, platform)) {
|
|
301
353
|
const raw = JSON.parse(readFileSync(file, "utf8"));
|
|
302
|
-
if (
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
const decoded = decodeLegacyDescriptor(raw, platform);
|
|
309
|
-
if (decoded && validateLegacyRuntime(file, platform))
|
|
310
|
-
return decoded;
|
|
354
|
+
if (raw && typeof raw === "object" && !Array.isArray(raw) &&
|
|
355
|
+
Object.keys(raw).length === 2 &&
|
|
356
|
+
raw.mode === "unavailable") {
|
|
357
|
+
const reason = raw.reason;
|
|
358
|
+
if (typeof reason === "string" && reason.trim() && reason.length <= 2_000)
|
|
359
|
+
return reason.trim();
|
|
311
360
|
}
|
|
312
361
|
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
362
|
+
}
|
|
363
|
+
catch {
|
|
364
|
+
// No usable diagnostic; the caller still refuses computer control.
|
|
316
365
|
}
|
|
317
366
|
return null;
|
|
318
367
|
}
|
|
@@ -20,7 +20,7 @@ const identitySchema = z.object({ portalOrigin: z.string().max(2048), organizati
|
|
|
20
20
|
const PROVIDER_IDS = ["anthropic", "openai", "openrouter"];
|
|
21
21
|
const signature = (value) => createHash("sha256").update(JSON.stringify(value)).digest("hex");
|
|
22
22
|
const expiredMessage = "Company access has ended. Reconnect your organization or explicitly choose a personal model; personal billing will not be used automatically.";
|
|
23
|
-
export const LICENSE_EXPIRED_MESSAGE = "Your
|
|
23
|
+
export const LICENSE_EXPIRED_MESSAGE = "Your organization's OpenMaus Admin license has expired. Contact your admin.";
|
|
24
24
|
/** Validate only messages received over Electron's private utility-parent port.
|
|
25
25
|
* Never expose this input to renderer HTTP or merge it into saved AppConfig.
|
|
26
26
|
*/
|
|
@@ -36,14 +36,14 @@ import { createToolListNormalizer } from "./mcp-tool-schema.js";
|
|
|
36
36
|
// stdin/stdout/stderr resets the window.
|
|
37
37
|
export const BRIDGE_INACTIVITY_MS = 45_000;
|
|
38
38
|
const PROBE_TIMEOUT_MS = 10_000;
|
|
39
|
-
/** Run the liveness command; alive means "exited 0 within the timeout".
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
export function runLivenessProbe(probe, timeoutMs = PROBE_TIMEOUT_MS) {
|
|
39
|
+
/** Run the liveness command; alive means "exited 0 within the timeout". A
|
|
40
|
+
* separate bounded command checks the transport without waiting for an
|
|
41
|
+
* answer on the possibly wedged MCP stream. */
|
|
42
|
+
export function runLivenessProbe(probe, timeoutMs = PROBE_TIMEOUT_MS, env) {
|
|
43
43
|
return new Promise((resolve) => {
|
|
44
44
|
const child = spawn(probe.command, probe.args, {
|
|
45
45
|
shell: false,
|
|
46
|
-
env: { ...process.env, PATH: augmentedPath() },
|
|
46
|
+
env: env ?? { ...process.env, PATH: augmentedPath() },
|
|
47
47
|
stdio: ["ignore", "ignore", "ignore"],
|
|
48
48
|
});
|
|
49
49
|
const timer = setTimeout(() => {
|
|
@@ -265,7 +265,7 @@ export function runMcpBridge(options) {
|
|
|
265
265
|
const liveness = options.liveness;
|
|
266
266
|
watchdog = createInactivityWatchdog({
|
|
267
267
|
inactivityMs: BRIDGE_INACTIVITY_MS,
|
|
268
|
-
probe: () => runLivenessProbe(liveness),
|
|
268
|
+
probe: () => runLivenessProbe(liveness, PROBE_TIMEOUT_MS, options.env),
|
|
269
269
|
onDead: () => {
|
|
270
270
|
process.stderr.write(`${options.label} transport went silent and stopped answering liveness probes; ending the bridge\n`);
|
|
271
271
|
process.exitCode = 1;
|
|
@@ -15,9 +15,13 @@ import { join } from "node:path";
|
|
|
15
15
|
import { DatabaseSync } from "node:sqlite";
|
|
16
16
|
import { DATA_DIR } from "./config.js";
|
|
17
17
|
import { peerProvenanceAuthor } from "./peer-provenance.js";
|
|
18
|
+
import { MessageSearchWorker } from "./message-search-worker.js";
|
|
19
|
+
import { searchMessagesInDatabase } from "./message-search-query.js";
|
|
18
20
|
const DB_FILE = () => join(DATA_DIR, "messages.db");
|
|
19
21
|
let handle = null;
|
|
20
22
|
let handlePath = null;
|
|
23
|
+
let searchWorker = null;
|
|
24
|
+
let closingSearch = Promise.resolve();
|
|
21
25
|
function open() {
|
|
22
26
|
mkdirSync(DATA_DIR, { recursive: true, mode: 0o700 });
|
|
23
27
|
const file = DB_FILE();
|
|
@@ -444,49 +448,16 @@ export function threadsReferencing(fragment) {
|
|
|
444
448
|
return rows.map((row) => row.thread_id);
|
|
445
449
|
}
|
|
446
450
|
/** Case-insensitive substring search over text messages, newest first.
|
|
447
|
-
*
|
|
448
|
-
* scan is milliseconds, and it needs no FTS extension to exist. */
|
|
451
|
+
* Keep literal substring semantics; HTTP callers run the scan off-thread. */
|
|
449
452
|
export function searchMessages(query, limit = 40, threadId) {
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
//
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
const scope = threadId ? "thread_id = ? AND " : "";
|
|
459
|
-
const statement = db().prepare("SELECT thread_id, id, at, role, kind, text, json_extract(json, '$.tool.name') AS tool_name, json_extract(json, '$.from.name') AS from_name FROM messages " +
|
|
460
|
-
`WHERE ${scope}((kind = 'text' AND text IS NOT NULL AND lower(text) LIKE ? ESCAPE '\\') ` +
|
|
461
|
-
" OR (kind = 'activity' AND tool_name IS NOT NULL AND lower(tool_name) LIKE ? ESCAPE '\\')) " +
|
|
462
|
-
"ORDER BY at DESC LIMIT ?");
|
|
463
|
-
const rows = (threadId
|
|
464
|
-
? statement.all(threadId, pattern, pattern, limit)
|
|
465
|
-
: statement.all(pattern, pattern, limit));
|
|
466
|
-
return rows.map((row) => {
|
|
467
|
-
const haystack = row.kind === "activity" ? (row.tool_name ?? "") : (row.text ?? "");
|
|
468
|
-
const hitAt = Math.max(0, haystack.toLowerCase().indexOf(needle));
|
|
469
|
-
const start = Math.max(0, hitAt - 60);
|
|
470
|
-
const end = Math.min(haystack.length, hitAt + needle.length + 90);
|
|
471
|
-
const head = start > 0 ? "…" : "";
|
|
472
|
-
const body = haystack.slice(start, end).replace(/\s+/g, " ").trim();
|
|
473
|
-
const snippet = head + body + (end < haystack.length ? "…" : "");
|
|
474
|
-
// whitespace folding can shift the offset; find the match again inside
|
|
475
|
-
const folded = needle.replace(/\s+/g, " ");
|
|
476
|
-
const matchStart = snippet.toLowerCase().indexOf(folded);
|
|
477
|
-
return {
|
|
478
|
-
threadId: row.thread_id,
|
|
479
|
-
messageId: row.id,
|
|
480
|
-
at: row.at,
|
|
481
|
-
role: row.role,
|
|
482
|
-
kind: row.kind,
|
|
483
|
-
snippet,
|
|
484
|
-
matchStart: matchStart < 0 ? head.length : matchStart,
|
|
485
|
-
// A defensive fallback must not mark arbitrary snippet text as the hit.
|
|
486
|
-
matchLength: matchStart < 0 ? 0 : folded.length,
|
|
487
|
-
...(row.from_name ? { from: row.from_name } : {}),
|
|
488
|
-
};
|
|
489
|
-
});
|
|
453
|
+
return searchMessagesInDatabase(db(), query, limit, threadId);
|
|
454
|
+
}
|
|
455
|
+
export function searchMessagesAsync(query, limit = 40, threadId) {
|
|
456
|
+
if (!query.trim())
|
|
457
|
+
return Promise.resolve([]);
|
|
458
|
+
db(); // The owner initializes/migrates the schema before any read-only scan.
|
|
459
|
+
searchWorker ??= new MessageSearchWorker(DB_FILE());
|
|
460
|
+
return searchWorker.search(query, limit, threadId);
|
|
490
461
|
}
|
|
491
462
|
/** Who wrote a user-role line that reads as the user's. The structural
|
|
492
463
|
* field wins; rows stored before it existed still open with the note. */
|
|
@@ -675,8 +646,17 @@ export function recallMemory(query, botId, limit = 12) {
|
|
|
675
646
|
.all(match, botId, limit);
|
|
676
647
|
return rows.map((row) => ({ file: row.path, at: row.mtime_ms, snippet: row.snippet.replace(/\s+/g, " ").trim() }));
|
|
677
648
|
}
|
|
649
|
+
/** Await before maintenance replaces the database or shutdown releases its lease. */
|
|
650
|
+
export async function closeMessageSearch() {
|
|
651
|
+
const worker = searchWorker;
|
|
652
|
+
searchWorker = null;
|
|
653
|
+
if (worker)
|
|
654
|
+
closingSearch = Promise.all([closingSearch, worker.close()]).then(() => { });
|
|
655
|
+
await closingSearch;
|
|
656
|
+
}
|
|
678
657
|
/** Test/shutdown hook — closes the handle so a wiped DATA_DIR starts clean. */
|
|
679
658
|
export function closeMessageDb() {
|
|
659
|
+
void closeMessageSearch();
|
|
680
660
|
try {
|
|
681
661
|
handle?.close();
|
|
682
662
|
}
|
|
@@ -313,7 +313,7 @@ function containedBy(root, candidate) {
|
|
|
313
313
|
const suffix = relative(root, candidate);
|
|
314
314
|
return suffix === "" || (suffix !== ".." && !suffix.startsWith(`..${sep}`) && !isAbsolute(suffix));
|
|
315
315
|
}
|
|
316
|
-
function mimeFor(path) {
|
|
316
|
+
export function mimeFor(path) {
|
|
317
317
|
switch (extname(path).toLowerCase()) {
|
|
318
318
|
case ".md": return "text/markdown; charset=utf-8";
|
|
319
319
|
case ".txt": return "text/plain; charset=utf-8";
|
|
@@ -331,6 +331,14 @@ function mimeFor(path) {
|
|
|
331
331
|
case ".m4v": return "video/x-m4v";
|
|
332
332
|
case ".webm": return "video/webm";
|
|
333
333
|
case ".mov": return "video/quicktime";
|
|
334
|
+
case ".mp3": return "audio/mpeg";
|
|
335
|
+
case ".m4a": return "audio/mp4";
|
|
336
|
+
case ".aac": return "audio/aac";
|
|
337
|
+
case ".wav": return "audio/wav";
|
|
338
|
+
case ".ogg":
|
|
339
|
+
case ".oga": return "audio/ogg";
|
|
340
|
+
case ".opus": return "audio/opus";
|
|
341
|
+
case ".flac": return "audio/flac";
|
|
334
342
|
case ".doc": return "application/msword";
|
|
335
343
|
case ".docx": return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
|
|
336
344
|
case ".xls": return "application/vnd.ms-excel";
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/** Shared query and snippet logic; the worker never calls the write-side open(). */
|
|
2
|
+
export function searchMessagesInDatabase(database, query, limit = 40, threadId) {
|
|
3
|
+
const needle = query.trim().toLowerCase();
|
|
4
|
+
if (!needle)
|
|
5
|
+
return [];
|
|
6
|
+
// escape LIKE wildcards so a literal % or _ in the query stays literal
|
|
7
|
+
const pattern = `%${needle.replace(/([\\%_])/g, "\\$1")}%`;
|
|
8
|
+
// text messages by their text; activity chips by the tool name — "which
|
|
9
|
+
// bot ran that migration" is a tool-name question. The chip's name lives
|
|
10
|
+
// in the row's json; a JSON1 extract keeps this one query.
|
|
11
|
+
const scope = threadId ? "thread_id = ? AND " : "";
|
|
12
|
+
const statement = database.prepare("SELECT thread_id, id, at, role, kind, text, json_extract(json, '$.tool.name') AS tool_name, json_extract(json, '$.from.name') AS from_name FROM messages " +
|
|
13
|
+
`WHERE ${scope}((kind = 'text' AND text IS NOT NULL AND lower(text) LIKE ? ESCAPE '\\') ` +
|
|
14
|
+
" OR (kind = 'activity' AND tool_name IS NOT NULL AND lower(tool_name) LIKE ? ESCAPE '\\')) " +
|
|
15
|
+
"ORDER BY at DESC LIMIT ?");
|
|
16
|
+
const rows = (threadId
|
|
17
|
+
? statement.all(threadId, pattern, pattern, limit)
|
|
18
|
+
: statement.all(pattern, pattern, limit));
|
|
19
|
+
return rows.map((row) => {
|
|
20
|
+
const haystack = row.kind === "activity" ? (row.tool_name ?? "") : (row.text ?? "");
|
|
21
|
+
const hitAt = Math.max(0, haystack.toLowerCase().indexOf(needle));
|
|
22
|
+
const start = Math.max(0, hitAt - 60);
|
|
23
|
+
const end = Math.min(haystack.length, hitAt + needle.length + 90);
|
|
24
|
+
const head = start > 0 ? "…" : "";
|
|
25
|
+
const body = haystack.slice(start, end).replace(/\s+/g, " ").trim();
|
|
26
|
+
const snippet = head + body + (end < haystack.length ? "…" : "");
|
|
27
|
+
// whitespace folding can shift the offset; find the match again inside
|
|
28
|
+
const folded = needle.replace(/\s+/g, " ");
|
|
29
|
+
const matchStart = snippet.toLowerCase().indexOf(folded);
|
|
30
|
+
return {
|
|
31
|
+
threadId: row.thread_id,
|
|
32
|
+
messageId: row.id,
|
|
33
|
+
at: row.at,
|
|
34
|
+
role: row.role,
|
|
35
|
+
kind: row.kind,
|
|
36
|
+
snippet,
|
|
37
|
+
matchStart: matchStart < 0 ? head.length : matchStart,
|
|
38
|
+
// A defensive fallback must not mark arbitrary snippet text as the hit.
|
|
39
|
+
matchLength: matchStart < 0 ? 0 : folded.length,
|
|
40
|
+
...(row.from_name ? { from: row.from_name } : {}),
|
|
41
|
+
};
|
|
42
|
+
});
|
|
43
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { Worker } from "node:worker_threads";
|
|
3
|
+
export class SearchUnavailable extends Error {
|
|
4
|
+
status = 503;
|
|
5
|
+
}
|
|
6
|
+
/** One read-only worker, not a worker per keystroke. Bound outstanding scans
|
|
7
|
+
* so a burst of searches cannot build an unbounded queue behind the database. */
|
|
8
|
+
export class MessageSearchWorker {
|
|
9
|
+
worker;
|
|
10
|
+
stopping = Promise.resolve();
|
|
11
|
+
sequence = 0;
|
|
12
|
+
pending = new Map();
|
|
13
|
+
file;
|
|
14
|
+
timeoutMs;
|
|
15
|
+
constructor(file, timeoutMs = 30_000) {
|
|
16
|
+
this.file = file;
|
|
17
|
+
this.timeoutMs = timeoutMs;
|
|
18
|
+
}
|
|
19
|
+
search(query, limit, threadId) {
|
|
20
|
+
if (this.pending.size >= 8)
|
|
21
|
+
return Promise.reject(new SearchUnavailable("Search is busy. Try again shortly."));
|
|
22
|
+
if (!this.worker) {
|
|
23
|
+
const source = new URL("./message-search.worker.ts", import.meta.url);
|
|
24
|
+
const worker = new Worker(existsSync(source) ? source : new URL("./message-search.worker.js", import.meta.url), {
|
|
25
|
+
workerData: { file: this.file },
|
|
26
|
+
// Do not inherit test loaders or inspector ports into the worker.
|
|
27
|
+
execArgv: [],
|
|
28
|
+
});
|
|
29
|
+
this.worker = worker;
|
|
30
|
+
worker.on("message", (reply) => {
|
|
31
|
+
if (this.worker !== worker)
|
|
32
|
+
return;
|
|
33
|
+
const request = this.pending.get(reply.id);
|
|
34
|
+
if (!request)
|
|
35
|
+
return;
|
|
36
|
+
clearTimeout(request.timer);
|
|
37
|
+
this.pending.delete(reply.id);
|
|
38
|
+
if (reply.failed)
|
|
39
|
+
request.reject(new SearchUnavailable("Search could not finish. Try again shortly."));
|
|
40
|
+
else
|
|
41
|
+
request.resolve(reply.hits ?? []);
|
|
42
|
+
if (!this.pending.size)
|
|
43
|
+
worker.unref();
|
|
44
|
+
});
|
|
45
|
+
worker.on("error", () => {
|
|
46
|
+
if (this.worker === worker)
|
|
47
|
+
void this.close();
|
|
48
|
+
});
|
|
49
|
+
worker.on("exit", () => {
|
|
50
|
+
if (this.worker === worker)
|
|
51
|
+
void this.close();
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
const worker = this.worker;
|
|
55
|
+
worker.ref();
|
|
56
|
+
const id = ++this.sequence;
|
|
57
|
+
return new Promise((resolve, reject) => {
|
|
58
|
+
const timer = setTimeout(() => { void this.close(); }, this.timeoutMs);
|
|
59
|
+
this.pending.set(id, { resolve, reject, timer });
|
|
60
|
+
try {
|
|
61
|
+
worker.postMessage({ id, query, limit, threadId });
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
void this.close();
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
async close() {
|
|
69
|
+
const worker = this.worker;
|
|
70
|
+
this.worker = undefined;
|
|
71
|
+
for (const request of this.pending.values()) {
|
|
72
|
+
clearTimeout(request.timer);
|
|
73
|
+
request.reject(new SearchUnavailable("Search was interrupted. Try again shortly."));
|
|
74
|
+
}
|
|
75
|
+
this.pending.clear();
|
|
76
|
+
if (worker)
|
|
77
|
+
this.stopping = Promise.all([this.stopping, worker.terminate()]).then(() => { });
|
|
78
|
+
await this.stopping;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { DatabaseSync } from "node:sqlite";
|
|
2
|
+
import { parentPort, workerData } from "node:worker_threads";
|
|
3
|
+
import { searchMessagesInDatabase } from "./message-search-query.js";
|
|
4
|
+
// Each request reads the latest committed WAL state. Closing after the scan
|
|
5
|
+
// avoids keeping an old database inode open across a workspace restore.
|
|
6
|
+
parentPort.on("message", (request) => {
|
|
7
|
+
let db;
|
|
8
|
+
try {
|
|
9
|
+
db = new DatabaseSync(workerData.file, { readOnly: true });
|
|
10
|
+
const hits = searchMessagesInDatabase(db, request.query, request.limit, request.threadId);
|
|
11
|
+
parentPort.postMessage({ id: request.id, hits });
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
// Database errors can contain private paths or query text. The server
|
|
15
|
+
// returns a generic retryable error, never a synchronous fallback scan.
|
|
16
|
+
parentPort.postMessage({ id: request.id, failed: true });
|
|
17
|
+
}
|
|
18
|
+
finally {
|
|
19
|
+
db?.close();
|
|
20
|
+
}
|
|
21
|
+
});
|