openmausbot 0.1.66 → 0.1.68
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/index-CbaejjQA.js +278 -0
- package/dist/assets/{index-kW5L1QeO.js → index-HqooKFTS.js} +1 -1
- package/dist/assets/index-aJ5EWZOQ.css +1 -0
- package/dist/index.html +2 -2
- package/dist-server/computer-proxy.js +14 -3
- package/dist-server/container-mcp.js +44 -19
- package/dist-server/drivers/agents-proxy.js +59 -3
- package/dist-server/enterprise/server/index.js +14587 -0
- package/dist-server/index.js +2675 -1055
- package/dist-server/local-computer-proxy.js +433 -0
- package/dist-server/local-computer.js +48 -5
- package/dist-server/mcp-server.js +38 -17
- package/dist-server/openmausbot.js +1560 -745
- package/dist-server/pair-cli.js +1560 -745
- package/dist-server/proxy-paths.js +1 -0
- package/dist-server/server/account-signin.js +103 -0
- package/dist-server/server/brand.js +7 -0
- package/dist-server/server/caddy.js +203 -0
- package/dist-server/server/cli.js +185 -22
- package/dist-server/server/computer-proxy.js +11 -2
- package/dist-server/server/config.js +22 -1
- package/dist-server/server/control-client.js +7 -1
- package/dist-server/server/delegations.js +56 -11
- package/dist-server/server/drivers/agents-proxy.js +75 -5
- package/dist-server/server/drivers/claude-login-auth.js +334 -0
- package/dist-server/server/drivers/claude.js +8 -0
- package/dist-server/server/environment.js +1 -0
- package/dist-server/server/harness/registry.js +7 -1
- package/dist-server/server/index.js +965 -363
- package/dist-server/server/local-computer-proxy.js +28 -0
- package/dist-server/server/local-computer.js +17 -0
- package/dist-server/server/mcp-bridge.js +27 -11
- package/dist-server/server/peer-approval.js +19 -6
- package/dist-server/server/proxy-paths.js +1 -0
- package/dist-server/server/routines.js +36 -9
- package/dist-server/server/service-cli.js +63 -0
- package/dist-server/server/service-unit.js +129 -0
- package/dist-server/server/sessions.js +42 -1
- package/dist-server/server/steer-queue.js +12 -9
- package/dist-server/server/store.js +260 -34
- package/dist-server/server/system-prompt.js +1 -0
- package/dist-server/server/team-backup.js +1 -0
- package/dist-server/server/tunnel.js +27 -1
- package/dist-server/server/turn-dispatch-guard.js +6 -0
- package/dist-server/server/turn-resources.js +50 -0
- package/dist-server/server/workspace.js +53 -3
- package/dist-server/vps-container-mcp.js +44 -19
- package/enterprise/LICENSE +32 -0
- package/enterprise/README.md +89 -0
- package/enterprise/server/index.js +14587 -0
- package/package.json +2 -1
- package/dist/assets/index-BftlaHAF.css +0 -1
- package/dist/assets/index-DuR3H-Hf.js +0 -278
|
@@ -25,7 +25,12 @@ function createControlClient(options) {
|
|
|
25
25
|
const res = await fetchImpl(url2, { headers, signal: AbortSignal.timeout(2e3) });
|
|
26
26
|
if (!res.ok) return UNAVAILABLE;
|
|
27
27
|
const body = await res.json().catch(() => null);
|
|
28
|
-
|
|
28
|
+
if (typeof body?.held !== "boolean" || typeof body?.helpOpen !== "boolean") return UNAVAILABLE;
|
|
29
|
+
return {
|
|
30
|
+
held: body.held,
|
|
31
|
+
helpOpen: body.helpOpen,
|
|
32
|
+
...typeof body.blockedReason === "string" && body.blockedReason.trim() ? { blockedReason: body.blockedReason } : {}
|
|
33
|
+
};
|
|
29
34
|
} catch {
|
|
30
35
|
return UNAVAILABLE;
|
|
31
36
|
}
|
|
@@ -276,7 +281,7 @@ function createGateInterceptor(options) {
|
|
|
276
281
|
options.forward(line);
|
|
277
282
|
return;
|
|
278
283
|
}
|
|
279
|
-
const held = await options.isHeld().catch(() =>
|
|
284
|
+
const held = await options.isHeld().catch(() => true);
|
|
280
285
|
if (!held) {
|
|
281
286
|
options.forward(line);
|
|
282
287
|
return;
|
|
@@ -285,10 +290,11 @@ function createGateInterceptor(options) {
|
|
|
285
290
|
JSON.stringify({
|
|
286
291
|
jsonrpc: "2.0",
|
|
287
292
|
id: frame.id ?? null,
|
|
288
|
-
result: { content: [{ type: "text", text: refusalText }], isError: true }
|
|
293
|
+
result: { content: [{ type: "text", text: options.getRefusalReason?.() ?? refusalText }], isError: true }
|
|
289
294
|
})
|
|
290
295
|
);
|
|
291
296
|
});
|
|
297
|
+
return queue;
|
|
292
298
|
};
|
|
293
299
|
}
|
|
294
300
|
function createMcpBridgeInterceptor(options) {
|
|
@@ -296,8 +302,11 @@ function createMcpBridgeInterceptor(options) {
|
|
|
296
302
|
isHeld: options.gate.isHeld,
|
|
297
303
|
forward: options.forward,
|
|
298
304
|
refuse: options.answer,
|
|
299
|
-
refusalText: options.gate.refusalText
|
|
300
|
-
|
|
305
|
+
refusalText: options.gate.refusalText,
|
|
306
|
+
getRefusalReason: options.gate.getRefusalReason
|
|
307
|
+
}) : (line) => {
|
|
308
|
+
options.forward(line);
|
|
309
|
+
};
|
|
301
310
|
return (line) => {
|
|
302
311
|
let frame = null;
|
|
303
312
|
try {
|
|
@@ -310,37 +319,47 @@ function createMcpBridgeInterceptor(options) {
|
|
|
310
319
|
}
|
|
311
320
|
return;
|
|
312
321
|
}
|
|
313
|
-
afterPing(line);
|
|
322
|
+
return afterPing(line);
|
|
314
323
|
};
|
|
315
324
|
}
|
|
316
325
|
function runMcpBridge(options) {
|
|
317
326
|
const child = spawn(options.command, options.args, {
|
|
318
327
|
shell: false,
|
|
319
|
-
env: { ...process.env, PATH: augmentedPath() },
|
|
328
|
+
env: options.env ?? { ...process.env, PATH: augmentedPath() },
|
|
320
329
|
stdio: ["pipe", "pipe", "pipe"]
|
|
321
330
|
});
|
|
322
331
|
child.stdin.on("error", () => {
|
|
323
332
|
});
|
|
324
333
|
child.stderr.pipe(process.stderr);
|
|
325
334
|
const client = options.gate ? createControlClient({ url: options.gate.url, token: options.gate.token }) : null;
|
|
335
|
+
let refusalReason;
|
|
326
336
|
const answer = (line) => process.stdout.write(line + "\n");
|
|
327
337
|
const forward = (line) => child.stdin.write(line + "\n");
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
338
|
+
const intercept = createMcpBridgeInterceptor({
|
|
339
|
+
answer,
|
|
340
|
+
forward,
|
|
341
|
+
...options.gate ? {
|
|
342
|
+
gate: {
|
|
343
|
+
isHeld: async () => {
|
|
344
|
+
refusalReason = void 0;
|
|
345
|
+
const state = await client.state(true);
|
|
346
|
+
refusalReason = state.blockedReason;
|
|
347
|
+
return state.held;
|
|
348
|
+
},
|
|
349
|
+
getRefusalReason: () => refusalReason
|
|
350
|
+
}
|
|
351
|
+
} : {}
|
|
352
|
+
});
|
|
353
|
+
let pendingInput = Promise.resolve();
|
|
354
|
+
const inbound = createLineSplitter((line) => {
|
|
355
|
+
const completion = intercept(line);
|
|
356
|
+
if (completion) pendingInput = completion;
|
|
357
|
+
});
|
|
339
358
|
const onStdin = (chunk) => inbound.push(chunk);
|
|
340
359
|
process.stdin.on("data", onStdin);
|
|
341
360
|
process.stdin.on("end", () => {
|
|
342
361
|
inbound.flush();
|
|
343
|
-
child.stdin.end();
|
|
362
|
+
void pendingInput.finally(() => child.stdin.end());
|
|
344
363
|
});
|
|
345
364
|
const outbound = createLineSplitter((line) => process.stdout.write(line + "\n"));
|
|
346
365
|
child.stdout.on("data", (chunk) => outbound.push(chunk));
|
|
@@ -14936,6 +14955,7 @@ var LEGACY_BROWSER_PROFILE_ID = /^[A-Za-z0-9_-]{1,40}$/;
|
|
|
14936
14955
|
var BROWSER_PROFILE_ID = /^[a-z0-9_-]{1,40}$/;
|
|
14937
14956
|
var MIN_ROOM_TURN_TIMEOUT_MINUTES = 1;
|
|
14938
14957
|
var MAX_ROOM_TURN_TIMEOUT_MINUTES = 1440;
|
|
14958
|
+
var MAX_CONCURRENT_BOT_THREADS = 10;
|
|
14939
14959
|
var MIN_LOCAL_VM_MAX_INSTANCES = 1;
|
|
14940
14960
|
var MAX_LOCAL_VM_MAX_INSTANCES = 4;
|
|
14941
14961
|
function isValidSshAlias(value) {
|
|
@@ -15088,6 +15108,9 @@ var defaultModelSelectionSchema = external_exports.object({
|
|
|
15088
15108
|
var appConfigSchema = external_exports.object({
|
|
15089
15109
|
/** Verified by the dedicated domain endpoint, never a generic config patch. */
|
|
15090
15110
|
customDomain: external_exports.string().optional(),
|
|
15111
|
+
/** Who may sign in with an emailed code (server/account-signin.ts):
|
|
15112
|
+
* addresses or `@domain` entries; admins get every scope, members chat only. */
|
|
15113
|
+
signIn: external_exports.object({ admins: external_exports.array(external_exports.string().max(320)).max(500).optional(), members: external_exports.array(external_exports.string().max(320)).max(5e3).optional() }).optional(),
|
|
15091
15114
|
defaultModelSelection: defaultModelSelectionSchema.optional(),
|
|
15092
15115
|
/** CLI-only launch preferences. Never enable remote access implicitly. */
|
|
15093
15116
|
cliStartup: external_exports.object({
|
|
@@ -15118,6 +15141,7 @@ var appConfigSchema = external_exports.object({
|
|
|
15118
15141
|
* system language. Unknown tags degrade to English in the renderer. */
|
|
15119
15142
|
language: optionalText,
|
|
15120
15143
|
rooms: roomConfigSchema.optional(),
|
|
15144
|
+
threads: external_exports.object({ maxConcurrentPerBot: external_exports.number().int().min(1).max(MAX_CONCURRENT_BOT_THREADS) }).strict().optional(),
|
|
15121
15145
|
localVm: localVmConfigSchema.optional(),
|
|
15122
15146
|
features: featureConfigSchema.optional(),
|
|
15123
15147
|
browserProfiles: browserProfilesSchema.optional(),
|
|
@@ -15150,6 +15174,7 @@ function resolveProxy(relative) {
|
|
|
15150
15174
|
var SPAWNED_PROXIES = {
|
|
15151
15175
|
browser: resolveProxy("browser-proxy"),
|
|
15152
15176
|
computer: resolveProxy("computer-proxy"),
|
|
15177
|
+
localComputer: resolveProxy("local-computer-proxy"),
|
|
15153
15178
|
permission: resolveProxy("permission-proxy"),
|
|
15154
15179
|
containerMcp: resolveProxy("container-mcp"),
|
|
15155
15180
|
vpsContainerMcp: resolveProxy("vps-container-mcp"),
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
OpenMausBot Enterprise License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Milind Soni. All rights reserved.
|
|
4
|
+
|
|
5
|
+
This directory ("enterprise") and everything in it is NOT covered by the
|
|
6
|
+
Apache License 2.0 that applies to the rest of this repository.
|
|
7
|
+
|
|
8
|
+
1. Source-available. You may read, build, and evaluate this code, and you may
|
|
9
|
+
modify it for your own evaluation, so that you know exactly what runs.
|
|
10
|
+
Development, test, and evaluation use is free of charge.
|
|
11
|
+
|
|
12
|
+
2. Use requires a license key. Running this code with enterprise features
|
|
13
|
+
enabled in production requires a valid, unexpired license key issued by
|
|
14
|
+
the copyright holder for your organization, under a separate agreement
|
|
15
|
+
that sets the licensed features, seats, and term.
|
|
16
|
+
|
|
17
|
+
3. No redistribution. You may not redistribute, sublicense, sell, host for
|
|
18
|
+
third parties, or offer as a service this directory or any modified
|
|
19
|
+
version of it, and you may not remove, disable, or circumvent the license
|
|
20
|
+
check. Hosting for third parties, reselling, and white-labelling the
|
|
21
|
+
product for third parties are permitted only under a written partner
|
|
22
|
+
agreement with the copyright holder.
|
|
23
|
+
|
|
24
|
+
4. Everything else is open source. Delete this directory and the remainder
|
|
25
|
+
of the repository builds and runs as the open-source edition under
|
|
26
|
+
Apache 2.0, with no obligation under this license.
|
|
27
|
+
|
|
28
|
+
5. No warranty. This code is provided "as is", without warranty of any kind.
|
|
29
|
+
To the extent permitted by law, the copyright holder is not liable for
|
|
30
|
+
any damages arising from its use.
|
|
31
|
+
|
|
32
|
+
Questions and license keys: soni.mil2001@gmail.com
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# OpenMausBot Enterprise
|
|
2
|
+
|
|
3
|
+
Source-available features for hosted and white-labelled deployments. This
|
|
4
|
+
folder has its own [LICENSE](./LICENSE); everything outside it is Apache 2.0.
|
|
5
|
+
|
|
6
|
+
**Delete this folder and you have the open-source edition.** Core only
|
|
7
|
+
reaches the layer through one hook point, `server/enterprise.ts`, which
|
|
8
|
+
loads `enterprise/server/index.ts` if it exists and otherwise reports the
|
|
9
|
+
open-source edition. No core file imports anything from here.
|
|
10
|
+
|
|
11
|
+
## How a deployment turns enterprise
|
|
12
|
+
|
|
13
|
+
Set `OMB_LICENSE_KEY` on the server. The key is `omb1.<claims>.<signature>`:
|
|
14
|
+
the claims are visible JSON (who it is for, which entitlements, when it
|
|
15
|
+
expires), signed with an Ed25519 key whose public half is baked into
|
|
16
|
+
`server/license.ts`. Verification is offline, and one build serves every
|
|
17
|
+
customer, because the key decides the feature set rather than the code.
|
|
18
|
+
|
|
19
|
+
`GET /api/edition` reports the outcome:
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{ "edition": "enterprise", "customer": "Acme", "features": ["sso", "whitelabel"], "expiresAt": "2027-09-02" }
|
|
23
|
+
{ "edition": "oss", "features": [], "notice": "OMB_LICENSE_KEY expired on 2027-09-02; renew it to keep enterprise features" }
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
A missing, altered, or expired key never stops the server: it runs the
|
|
27
|
+
open-source edition and the notice says what to fix.
|
|
28
|
+
|
|
29
|
+
## Entitlement ids
|
|
30
|
+
|
|
31
|
+
| id | grants |
|
|
32
|
+
|---|---|
|
|
33
|
+
| `whitelabel` | product name, tagline, accent colour, logo, favicon and support link from `brand.json` (below) |
|
|
34
|
+
| `sso` | identity-header trust behind an OIDC proxy |
|
|
35
|
+
| `admin` | the admin panel routes |
|
|
36
|
+
| `budgets` | per-bot and per-section spend limits |
|
|
37
|
+
|
|
38
|
+
Core gates a feature with `entitled("id")` from `server/enterprise.ts`.
|
|
39
|
+
Unknown ids are carried in the key but grant nothing, so keys can be issued
|
|
40
|
+
ahead of a feature landing.
|
|
41
|
+
|
|
42
|
+
## Issuing keys
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
node enterprise/scripts/issue-license.mjs keygen # once; prints the public key to add to license.ts
|
|
46
|
+
node enterprise/scripts/issue-license.mjs issue --customer "Acme" --features whitelabel,sso --expires 2027-09-02
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The signing key lives outside the repo (default
|
|
50
|
+
`~/.config/openmausbot-enterprise/signing-key.json`). Rotate by generating a
|
|
51
|
+
new pair and appending its public key: keys signed by older pairs keep
|
|
52
|
+
working until they expire.
|
|
53
|
+
|
|
54
|
+
## What goes where
|
|
55
|
+
|
|
56
|
+
- Could any open-source user want it? It goes in core, as a public PR.
|
|
57
|
+
- Org-, admin- or tier-flavoured, or something the next enterprise lead
|
|
58
|
+
would be shown? It lives here, behind an entitlement.
|
|
59
|
+
- Customer-specific brand, skills, packages, connectors? The customer's own
|
|
60
|
+
repo: data and config, never a fork.
|
|
61
|
+
|
|
62
|
+
## White-label (`whitelabel`)
|
|
63
|
+
|
|
64
|
+
Put a `brand.json` in the server's data dir (`OMB_DATA_DIR`, the `/data`
|
|
65
|
+
volume in Docker) or point `OMB_BRAND_FILE` at one:
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"name": "Reliable Platform",
|
|
70
|
+
"tagline": "Back office, on autopilot",
|
|
71
|
+
"accent": "#1D4ED8",
|
|
72
|
+
"logo": "data:image/svg+xml;base64,…",
|
|
73
|
+
"favicon": "data:image/png;base64,…",
|
|
74
|
+
"supportUrl": "https://help.example.com"
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Only `name` is required. `logo` is an inline `data:image/…` URI or an
|
|
79
|
+
`https://` URL; `accent` is a 6-digit hex colour, and the text colour on it
|
|
80
|
+
is derived for contrast. The server reads the file on every `GET /api/brand`,
|
|
81
|
+
so edits show on the next reload; the app fetches it before the first paint,
|
|
82
|
+
so the window never flashes the default name. An unlicensed server, or a
|
|
83
|
+
file with a mistake, keeps the default brand and says why in `/api/brand`
|
|
84
|
+
and the startup log.
|
|
85
|
+
|
|
86
|
+
What `brand.json` cannot change, because it is baked at packaging time: the
|
|
87
|
+
desktop app's bundle and menu-bar name, installer names, the macOS
|
|
88
|
+
permission prompts, the iOS app's name, and the helper apps' paths. A fully
|
|
89
|
+
rebranded desktop build is a per-customer packaging job, not config.
|