openmausbot 0.1.61 → 0.1.62
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 +71 -8
- package/dist/assets/index-BSI5e_fY.js +264 -0
- package/dist/assets/{index-CAZ41wL2.js → index-DTeYtYoC.js} +1 -1
- package/dist/index.html +1 -1
- package/dist-server/container-mcp.js +16 -1
- package/dist-server/drivers/agents-proxy.js +8 -4
- package/dist-server/index.js +463 -134
- package/dist-server/openmausbot.js +33585 -14506
- package/dist-server/pair-cli.js +33585 -14506
- package/dist-server/server/browser-bundle-release.js +56 -0
- package/dist-server/server/browser-engine-release.js +13 -5
- package/dist-server/server/browser-engine.js +114 -15
- package/dist-server/server/cli-api-setup.js +144 -0
- package/dist-server/server/cli-phone-setup.js +130 -0
- package/dist-server/server/cli-prompts.js +185 -0
- package/dist-server/server/cli-setup.js +303 -0
- package/dist-server/server/cli.js +375 -70
- package/dist-server/server/config.js +25 -1
- package/dist-server/server/default-model-selection.js +21 -0
- package/dist-server/server/drivers/agents-proxy.js +13 -4
- package/dist-server/server/drivers/claude.js +26 -1
- package/dist-server/server/drivers/openai-compat.js +4 -2
- package/dist-server/server/index.js +117 -80
- package/dist-server/server/recall-disclosure.js +45 -0
- package/dist-server/server/request-auth.js +6 -0
- package/dist-server/server/screen-frame-gate.js +36 -1
- package/dist-server/server/screen-frame-source.js +57 -0
- package/dist-server/server/sessions.js +52 -4
- package/dist-server/vps-container-mcp.js +16 -1
- package/package.json +1 -1
- package/dist/assets/index-Br81ZQCY.js +0 -264
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { captureOutsideHumanControl } from "./private-screen-capture.js";
|
|
2
|
+
/** One serialized preview source. A cached or in-flight desktop frame must
|
|
3
|
+
* never become the browser's settled picture after the bot switches surfaces. */
|
|
4
|
+
export function createScreenFrameSource(input) {
|
|
5
|
+
let current = null;
|
|
6
|
+
let lastAt = 0;
|
|
7
|
+
let attemptedSurface;
|
|
8
|
+
let last = null;
|
|
9
|
+
let lastSurface;
|
|
10
|
+
const selectedSurface = () => input.captures[source.surface]
|
|
11
|
+
? source.surface
|
|
12
|
+
: input.captures.computer ? "computer" : "browser";
|
|
13
|
+
const source = {
|
|
14
|
+
surface: (input.captures.computer ? "computer" : "browser"),
|
|
15
|
+
get last() {
|
|
16
|
+
return lastSurface === selectedSurface() ? last : null;
|
|
17
|
+
},
|
|
18
|
+
async capture(fresh = false) {
|
|
19
|
+
// Wait for the previous source before selecting the next one. A final
|
|
20
|
+
// capture bypasses the preview throttle, even if a poke just finished.
|
|
21
|
+
while (current)
|
|
22
|
+
await current;
|
|
23
|
+
if (input.control().held)
|
|
24
|
+
return;
|
|
25
|
+
const surface = selectedSurface();
|
|
26
|
+
if (!fresh && attemptedSurface === surface && Date.now() - lastAt < input.minGapMs)
|
|
27
|
+
return;
|
|
28
|
+
attemptedSurface = surface;
|
|
29
|
+
current = (async () => {
|
|
30
|
+
try {
|
|
31
|
+
const capture = input.captures[surface];
|
|
32
|
+
if (!capture)
|
|
33
|
+
return;
|
|
34
|
+
const frame = await captureOutsideHumanControl(input.control, capture);
|
|
35
|
+
if (!frame || surface !== selectedSurface())
|
|
36
|
+
return;
|
|
37
|
+
last = frame;
|
|
38
|
+
lastSurface = surface;
|
|
39
|
+
input.onFrame(frame);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// A sleeping or mid-command surface is retried by the next tick.
|
|
43
|
+
}
|
|
44
|
+
finally {
|
|
45
|
+
lastAt = Date.now();
|
|
46
|
+
}
|
|
47
|
+
})();
|
|
48
|
+
try {
|
|
49
|
+
await current;
|
|
50
|
+
}
|
|
51
|
+
finally {
|
|
52
|
+
current = null;
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
return source;
|
|
57
|
+
}
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
//
|
|
5
5
|
// A pairing code is 12 characters from a 32-symbol alphabet with no 0/O/1/I
|
|
6
6
|
// (60 bits), single use, five minutes. Exchanging it yields an opaque
|
|
7
|
-
// session token (`omb_sess_…`, 256 bits) that lives 30 days
|
|
8
|
-
// is stored. A stream ticket is a 5-minute single-use credential for the SSE
|
|
7
|
+
// session token (`omb_sess_…`, 256 bits) that lives 30 days, renewed on use
|
|
8
|
+
// up to 180 days from pairing (`renew`); only its sha256 is stored. A stream ticket is a 5-minute single-use credential for the SSE
|
|
9
9
|
// endpoint, because EventSource cannot set headers. Failed exchanges are
|
|
10
10
|
// counted per source: five in a minute lock that source out for ten.
|
|
11
11
|
import { createHash, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
@@ -16,7 +16,31 @@ export const SCOPES = ["admin", "client"];
|
|
|
16
16
|
export const PAIRING_CODE_ALPHABET = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
|
|
17
17
|
export const PAIRING_CODE_LENGTH = 12;
|
|
18
18
|
export const PAIRING_CODE_TTL_MS = 5 * 60_000;
|
|
19
|
-
|
|
19
|
+
/** A whole number of days from an environment variable, as milliseconds.
|
|
20
|
+
* Anything that is not a whole number of days between 1 and 3650 falls back
|
|
21
|
+
* to the default rather than being clamped, so a typo is not silently
|
|
22
|
+
* turned into a policy. */
|
|
23
|
+
export function daysMs(value, fallbackDays) {
|
|
24
|
+
const days = Number(value);
|
|
25
|
+
const ok = Number.isInteger(days) && days >= 1 && days <= 3650;
|
|
26
|
+
return (ok ? days : fallbackDays) * 24 * 60 * 60_000;
|
|
27
|
+
}
|
|
28
|
+
/** How long a session lives after its last renewal. A session used with half
|
|
29
|
+
* the term or less left is renewed for the full term again (see `renew`), so
|
|
30
|
+
* a device in regular use keeps working; one that goes quiet lapses.
|
|
31
|
+
* OMB_SESSION_TTL_DAYS overrides the 30-day default. */
|
|
32
|
+
export const SESSION_TTL_MS = daysMs(process.env.OMB_SESSION_TTL_DAYS, 30);
|
|
33
|
+
/** The most a session may live from the day it was paired, however often it
|
|
34
|
+
* is used. Renewal never pushes a session past this, so a stolen cookie has a
|
|
35
|
+
* bounded life and every device re-pairs occasionally. OMB_SESSION_MAX_DAYS
|
|
36
|
+
* overrides the 180-day default. */
|
|
37
|
+
export const SESSION_MAX_AGE_MS = daysMs(process.env.OMB_SESSION_MAX_DAYS, 180);
|
|
38
|
+
/** Renewal is due once half the term or less is left. */
|
|
39
|
+
export const SESSION_RENEW_WHEN_LEFT_MS = SESSION_TTL_MS / 2;
|
|
40
|
+
/** Max-Age for a cookie that should die with its session: whole seconds, at least one. */
|
|
41
|
+
export function cookieMaxAgeSeconds(session, now = Date.now()) {
|
|
42
|
+
return Math.max(1, Math.floor((session.expiresAt - now) / 1000));
|
|
43
|
+
}
|
|
20
44
|
export const STREAM_TICKET_TTL_MS = 5 * 60_000;
|
|
21
45
|
/** Per-source slow-down only. A 60-bit code cannot be guessed online in
|
|
22
46
|
* five minutes whatever the rate, so the lock exists to make noise visible,
|
|
@@ -248,7 +272,9 @@ export class SessionRegistry {
|
|
|
248
272
|
scopes: [...pairing.scopes],
|
|
249
273
|
createdAt: now,
|
|
250
274
|
lastSeenAt: now,
|
|
251
|
-
|
|
275
|
+
// The absolute cap applies from the first term, so a TTL configured
|
|
276
|
+
// longer than the cap does not hand out a session the cap forbids.
|
|
277
|
+
expiresAt: now + Math.min(SESSION_TTL_MS, SESSION_MAX_AGE_MS),
|
|
252
278
|
};
|
|
253
279
|
this.sessions.push(record);
|
|
254
280
|
this.lastSeenWrites.set(record.id, now); // the exchange itself was the first sighting
|
|
@@ -275,6 +301,28 @@ export class SessionRegistry {
|
|
|
275
301
|
}
|
|
276
302
|
return record;
|
|
277
303
|
}
|
|
304
|
+
/** Sliding expiry. Called by the request gate once a request has passed
|
|
305
|
+
* the origin and scope checks (so a rejected request never extends
|
|
306
|
+
* anything): a still-valid session with half its term or less left is
|
|
307
|
+
* renewed for the full term, capped at SESSION_MAX_AGE_MS from pairing.
|
|
308
|
+
* Nothing expired is revived. Writes at most once per half-term, so it
|
|
309
|
+
* adds nothing to the last-seen traffic. Returns whether it renewed. */
|
|
310
|
+
renew(sessionId) {
|
|
311
|
+
const record = this.sessions.find((s) => s.id === sessionId);
|
|
312
|
+
const now = this.now();
|
|
313
|
+
if (!record || record.expiresAt <= now)
|
|
314
|
+
return false;
|
|
315
|
+
if (record.expiresAt - now > SESSION_RENEW_WHEN_LEFT_MS)
|
|
316
|
+
return false;
|
|
317
|
+
const next = Math.min(now + SESSION_TTL_MS, record.createdAt + SESSION_MAX_AGE_MS);
|
|
318
|
+
if (next <= record.expiresAt)
|
|
319
|
+
return false; // already at the absolute cap
|
|
320
|
+
record.expiresAt = next;
|
|
321
|
+
record.lastSeenAt = now;
|
|
322
|
+
this.lastSeenWrites.set(record.id, now);
|
|
323
|
+
this.persist();
|
|
324
|
+
return true;
|
|
325
|
+
}
|
|
278
326
|
/** Still valid right now (prunes expiry first). */
|
|
279
327
|
isLive(sessionId) {
|
|
280
328
|
this.prune();
|
|
@@ -14914,6 +14914,9 @@ function date4(params) {
|
|
|
14914
14914
|
// node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
|
|
14915
14915
|
config(en_default());
|
|
14916
14916
|
|
|
14917
|
+
// server/contracts.ts
|
|
14918
|
+
var EFFORT_LEVELS = ["none", "low", "medium", "high", "xhigh", "max"];
|
|
14919
|
+
|
|
14917
14920
|
// server/mcp-registry.ts
|
|
14918
14921
|
var MAX_ARGS = 64;
|
|
14919
14922
|
var storedEntrySchema = external_exports.object({
|
|
@@ -15077,7 +15080,19 @@ var instanceConfigSchema = external_exports.object({
|
|
|
15077
15080
|
config: external_exports.json().optional()
|
|
15078
15081
|
});
|
|
15079
15082
|
var instanceConfigMapSchema = external_exports.record(external_exports.string(), instanceConfigSchema);
|
|
15083
|
+
var defaultModelSelectionSchema = external_exports.object({
|
|
15084
|
+
instanceId: external_exports.string().trim().min(1),
|
|
15085
|
+
model: external_exports.string().trim().min(1),
|
|
15086
|
+
effort: external_exports.enum(EFFORT_LEVELS).optional()
|
|
15087
|
+
});
|
|
15080
15088
|
var appConfigSchema = external_exports.object({
|
|
15089
|
+
defaultModelSelection: defaultModelSelectionSchema.optional(),
|
|
15090
|
+
/** CLI-only launch preferences. Never enable remote access implicitly. */
|
|
15091
|
+
cliStartup: external_exports.object({
|
|
15092
|
+
access: external_exports.enum(["local", "tunnel", "tailscale", "public-url"]),
|
|
15093
|
+
publicUrl: external_exports.string().url().optional(),
|
|
15094
|
+
phone: external_exports.enum(["ios", "android"]).optional()
|
|
15095
|
+
}).optional(),
|
|
15081
15096
|
xai: external_exports.object({ key: optionalText, url: optionalText }).optional(),
|
|
15082
15097
|
/** `model` seeds the default selection; `provider` pins an OpenRouter
|
|
15083
15098
|
* upstream (e.g. "fireworks"). Both are non-secret and optional. */
|
|
@@ -15114,7 +15129,7 @@ var appConfigSchema = external_exports.object({
|
|
|
15114
15129
|
var storedAppConfigSchema = appConfigSchema.extend({
|
|
15115
15130
|
browserProfiles: storedBrowserProfilesSchema.optional()
|
|
15116
15131
|
});
|
|
15117
|
-
var appConfigPatchSchema = appConfigSchema.omit({ instances: true, mcpServers: true });
|
|
15132
|
+
var appConfigPatchSchema = appConfigSchema.omit({ instances: true, mcpServers: true, cliStartup: true });
|
|
15118
15133
|
var jsonObjectSchema = external_exports.record(external_exports.string(), external_exports.json());
|
|
15119
15134
|
var DATA_DIR = process.env.OMB_DATA_DIR ?? join2(homedir2(), ".openmausbot");
|
|
15120
15135
|
var LEGACY_DATA_DIR = join2(homedir2(), ".opengrokbot");
|