openmausbot 0.1.68 → 0.1.70
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-B2F__IRS.css +1 -0
- package/dist/assets/index-CLj3t5Ba.js +280 -0
- package/dist/assets/{index-HqooKFTS.js → index-D1RGhLUK.js} +1 -1
- package/dist/index.html +2 -2
- package/dist-server/container-mcp.js +40 -2
- package/dist-server/drivers/agents-proxy.js +188 -5
- package/dist-server/index.js +2431 -984
- package/dist-server/local-computer-proxy.js +2 -0
- package/dist-server/openmausbot.js +923 -334
- package/dist-server/pair-cli.js +923 -334
- package/dist-server/prepare-cloudflared.js +20 -3
- package/dist-server/server/avatar-image.js +86 -34
- package/dist-server/server/bot-package.js +60 -14
- package/dist-server/server/config.js +23 -2
- package/dist-server/server/delegations.js +132 -74
- package/dist-server/server/drivers/acp/qwen.js +142 -5
- package/dist-server/server/drivers/agents-proxy.js +224 -4
- package/dist-server/server/drivers/claude-login-auth.js +102 -9
- package/dist-server/server/drivers/claude.js +11 -5
- package/dist-server/server/drivers/codex-device-auth.js +102 -3
- package/dist-server/server/drivers/codex-identity.js +115 -0
- package/dist-server/server/drivers/codex.js +6 -0
- package/dist-server/server/engine-install.js +137 -0
- package/dist-server/server/env-path.js +12 -0
- package/dist-server/server/harness/registry.js +33 -6
- package/dist-server/server/index.js +365 -26
- package/dist-server/server/package-export.js +7 -0
- package/dist-server/server/peer-provenance.js +6 -2
- package/dist-server/server/provider-auth-sessions.js +24 -0
- package/dist-server/server/routine-requests.js +270 -25
- package/dist-server/server/routines.js +297 -69
- package/dist-server/server/steer-queue.js +48 -1
- package/dist-server/server/store.js +22 -4
- package/dist-server/server/system-prompt.js +1 -0
- package/dist-server/server/team-backup.js +18 -4
- package/dist-server/shared/image-generation.js +28 -0
- package/dist-server/shared/team-backup.js +1 -0
- package/dist-server/vps-container-mcp.js +40 -2
- package/package.json +1 -1
- package/dist/assets/index-CbaejjQA.js +0 -278
- package/dist/assets/index-aJ5EWZOQ.css +0 -1
|
@@ -26,6 +26,7 @@ export function isProjectEmoji(value) {
|
|
|
26
26
|
const TASK_PATCH_FIELDS = [
|
|
27
27
|
"title", "projectId", "modelSelection", "approvalMode", "autoApprove", "alwaysAllow",
|
|
28
28
|
"unread", "rewound", "pinnedMessageId", "resumeCursors", "lastInstanceId", "cwd",
|
|
29
|
+
"routineRunId",
|
|
29
30
|
];
|
|
30
31
|
/** Everything the BOT authored is scrubbed of content-shaped secrets before
|
|
31
32
|
* it is stored: its reply text, a tool title (an ACP engine's title can be
|
|
@@ -1444,7 +1445,7 @@ export class Store {
|
|
|
1444
1445
|
}
|
|
1445
1446
|
/** A fresh context on the same bot: new thread, new session, same
|
|
1446
1447
|
* persona/tools/computer. Becomes the active task. */
|
|
1447
|
-
createTask(botId, title, activate = true, projectId) {
|
|
1448
|
+
createTask(botId, title, activate = true, projectId, openedBy) {
|
|
1448
1449
|
const bot = this.bot(botId);
|
|
1449
1450
|
if (!bot)
|
|
1450
1451
|
return null;
|
|
@@ -1455,6 +1456,7 @@ export class Store {
|
|
|
1455
1456
|
title: title?.trim().slice(0, 80) || UNTITLED_THREAD,
|
|
1456
1457
|
createdAt: Date.now(),
|
|
1457
1458
|
...(projectId ? { projectId } : {}),
|
|
1459
|
+
...(openedBy ? { openedBy: structuredClone(openedBy) } : {}),
|
|
1458
1460
|
resumeCursors: {},
|
|
1459
1461
|
modelSelection: structuredClone(bot.modelSelection),
|
|
1460
1462
|
approvalMode: approvalModeFor(bot),
|
|
@@ -1472,6 +1474,20 @@ export class Store {
|
|
|
1472
1474
|
this.emit({ type: "bot", botId });
|
|
1473
1475
|
return task;
|
|
1474
1476
|
}
|
|
1477
|
+
/** Attach (or complete) the opener record after the thread exists — the
|
|
1478
|
+
* handoff id is only known once the thread it targets has an id, so a
|
|
1479
|
+
* peer-opened thread is created first and stamped second. Never reachable
|
|
1480
|
+
* from the HTTP task PATCH: openedBy is not a TASK_PATCH_FIELD. */
|
|
1481
|
+
setTaskOpenedBy(botId, threadId, openedBy) {
|
|
1482
|
+
const bot = this.bot(botId);
|
|
1483
|
+
const task = this.taskByThread(botId, threadId);
|
|
1484
|
+
if (!bot || !task)
|
|
1485
|
+
return null;
|
|
1486
|
+
task.openedBy = structuredClone(openedBy);
|
|
1487
|
+
this.saveBots();
|
|
1488
|
+
this.emit({ type: "bot", botId });
|
|
1489
|
+
return task;
|
|
1490
|
+
}
|
|
1475
1491
|
switchTask(botId, threadId) {
|
|
1476
1492
|
const bot = this.bot(botId);
|
|
1477
1493
|
const task = bot?.tasks?.find((t) => t.threadId === threadId);
|
|
@@ -1503,10 +1519,12 @@ export class Store {
|
|
|
1503
1519
|
if (!bot.tasks.some((t) => t.threadId === threadId))
|
|
1504
1520
|
return null;
|
|
1505
1521
|
bot.tasks = bot.tasks.filter((t) => t.threadId !== threadId);
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1522
|
+
const visible = bot.tasks.find((task) => !task.routineRunId)
|
|
1523
|
+
?? this.createTask(botId, undefined, bot.threadId === threadId);
|
|
1524
|
+
if (bot.threadId === threadId || this.taskByThread(botId, bot.threadId)?.routineRunId) {
|
|
1525
|
+
this.mirrorActiveTask(bot, visible);
|
|
1509
1526
|
}
|
|
1527
|
+
this.deleteThreadRecord(threadId);
|
|
1510
1528
|
bot.unread = bot.tasks.some((task) => task.unread);
|
|
1511
1529
|
this.refreshBotActivity(bot);
|
|
1512
1530
|
this.saveBots();
|
|
@@ -36,6 +36,7 @@ export function computerPrompt(kind) {
|
|
|
36
36
|
}
|
|
37
37
|
export const COMPOSIO_PROMPT = " The user's connected apps (Gmail, Calendar, Slack, Notion, and the rest) are reachable through the composio tools — find the right one with COMPOSIO_SEARCH_TOOLS, read its arguments with COMPOSIO_GET_TOOL_SCHEMAS, then run it with COMPOSIO_MULTI_EXECUTE_TOOL. Reach for them before telling the user you have no access to a service.";
|
|
38
38
|
export const CREDENTIAL_PROMPT = " If a supported API key is missing, use request_credential to create a secure credential request. A freshly QR-paired mobile app or the desktop app can show the secure entry card. Never claim it opened unless the request succeeded, and never ask the user to paste credentials into chat.";
|
|
39
|
+
export const THREADS_PROMPT = " A thread is one conversation with its own history and its own run; a bot can have several running at once, and the person sees them as rows under that bot. Use start_thread to open one on yourself for separate work, or on a teammate to hand them a job that should run on its own. Use list_threads to see how the ones you opened are going. When you mention a thread to the person, write its title as #Title so it links. Do not use a ticket comment, a note, or a room post as a stand-in for a thread.";
|
|
39
40
|
export const ROUTINE_PROMPT = " If the user explicitly asks to list or review, schedule, run, or change routines, use list_routines and propose_routine or propose_routine_action. A proposal is not applied until the user confirms its in-app card, so never claim the action completed before that confirmation.";
|
|
40
41
|
export const ROUTINE_EXECUTION_PROMPT = " Execute this routine now: use available peer tools for required handoffs rather than merely announcing that you will wait; after an accepted delegation, end this turn for automatic resumption, and report a concrete blocker if no handoff is possible.";
|
|
41
42
|
export const LEARN_PROMPT = " If the user sends /learn or asks you to save a reusable procedure from this work, use skills_list and skill_manage. Create new skills; update an existing learned skill only when the user explicitly asks to revise that exact name. Include source provenance and wait for the review card decision.";
|
|
@@ -29,6 +29,11 @@ export function createTeamBackup(store, routines, name) {
|
|
|
29
29
|
const tasks = record.tasks?.length ? record.tasks : [{ threadId: record.threadId, title: "Conversation", createdAt: record.createdAt }];
|
|
30
30
|
return tasks.map((task) => ({
|
|
31
31
|
key: task.threadId, title: task.title, createdAt: task.createdAt,
|
|
32
|
+
// Who opened the thread travels; the handoff id does not — the
|
|
33
|
+
// delegation ledger is process-local and never part of a backup.
|
|
34
|
+
openedBy: "openedBy" in task && task.openedBy
|
|
35
|
+
? { botId: task.openedBy.botId, name: task.openedBy.name, at: task.openedBy.at }
|
|
36
|
+
: undefined,
|
|
32
37
|
activeLeafId: store.activeLeaf(task.threadId),
|
|
33
38
|
messages: store.messagesFor(task.threadId).map((message) => ({
|
|
34
39
|
id: message.id, role: message.role, text: messageText(message), at: message.at,
|
|
@@ -131,10 +136,19 @@ export function importTeamBackup(store, routines, input, selection) {
|
|
|
131
136
|
}
|
|
132
137
|
for (const source of backup.bots) {
|
|
133
138
|
const bot = store.bot(botIds.get(source.key));
|
|
134
|
-
const tasks = source.tasks.map((task, i) =>
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
139
|
+
const tasks = source.tasks.map((task, i) => {
|
|
140
|
+
const record = {
|
|
141
|
+
threadId: i === 0 ? bot.threadId : newId(), title: task.title, createdAt: task.createdAt, resumeCursors: {},
|
|
142
|
+
modelSelection: structuredClone(selection), activity: "idle", busy: false, unread: false,
|
|
143
|
+
};
|
|
144
|
+
// Same rule as a message's `from`: the opener is remapped to its
|
|
145
|
+
// imported twin, and an opener outside this backup leaves no record
|
|
146
|
+
// rather than a bot id that resolves to a stranger.
|
|
147
|
+
const opener = task.openedBy && botIds.get(task.openedBy.botId);
|
|
148
|
+
if (task.openedBy && opener)
|
|
149
|
+
record.openedBy = { botId: opener, name: task.openedBy.name, at: task.openedBy.at };
|
|
150
|
+
return record;
|
|
151
|
+
});
|
|
138
152
|
// Own the task IDs before writing their transcripts, so rollback also
|
|
139
153
|
// removes partially imported history if persistence fails midway.
|
|
140
154
|
store.patchBot(bot.id, { tasks, threadId: tasks[source.tasks.findIndex((task) => task.key === source.activeTask)].threadId });
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** A deliberately configured Images API endpoint, not a provider-returned URL.
|
|
2
|
+
* Local HTTP is useful for self-hosted routers; internet endpoints require TLS. */
|
|
3
|
+
export function normalizeImageGenerationUrl(value) {
|
|
4
|
+
const message = "Use an HTTPS API base URL, or HTTP for a local router, without passwords, query parameters or fragments";
|
|
5
|
+
let url;
|
|
6
|
+
try {
|
|
7
|
+
url = new URL(value.trim());
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
throw new Error(message);
|
|
11
|
+
}
|
|
12
|
+
if (value.length > 2048 || url.username || url.password || url.search || url.hash)
|
|
13
|
+
throw new Error(message);
|
|
14
|
+
const host = url.hostname.toLowerCase();
|
|
15
|
+
const ipv4 = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/.exec(host);
|
|
16
|
+
const local = host === "localhost" || host.endsWith(".localhost") || host.endsWith(".local")
|
|
17
|
+
|| host === "host.docker.internal" || host === "host.containers.internal"
|
|
18
|
+
|| host === "[::1]" || /^\[f[cd][0-9a-f]{2}:/.test(host)
|
|
19
|
+
|| (ipv4 && (Number(ipv4[1]) === 127 || Number(ipv4[1]) === 10
|
|
20
|
+
|| (Number(ipv4[1]) === 192 && Number(ipv4[2]) === 168)
|
|
21
|
+
|| (Number(ipv4[1]) === 172 && Number(ipv4[2]) >= 16 && Number(ipv4[2]) <= 31)));
|
|
22
|
+
if (url.protocol !== "https:" && !(url.protocol === "http:" && local))
|
|
23
|
+
throw new Error(message);
|
|
24
|
+
if (ipv4 && (Number(ipv4[1]) === 0 || (Number(ipv4[1]) === 169 && Number(ipv4[2]) === 254)))
|
|
25
|
+
throw new Error(message);
|
|
26
|
+
url.pathname = url.pathname.replace(/\/+$/, "").replace(/\/images\/generations$/, "");
|
|
27
|
+
return url.toString().replace(/\/+$/, "");
|
|
28
|
+
}
|
|
@@ -141,9 +141,11 @@ function windowsKnownDirs() {
|
|
|
141
141
|
var cached = null;
|
|
142
142
|
var probed = false;
|
|
143
143
|
var loginShellPath = null;
|
|
144
|
+
var registeredDirs = [];
|
|
144
145
|
function augmentedPath() {
|
|
145
146
|
if (cached === null) {
|
|
146
147
|
cached = mergePaths([
|
|
148
|
+
...registeredDirs.filter((d) => existsSync(d)),
|
|
147
149
|
...process.env.OMB_EXTRA_PATH ? process.env.OMB_EXTRA_PATH.split(delimiter) : [],
|
|
148
150
|
...process.env.PATH ? process.env.PATH.split(delimiter) : [],
|
|
149
151
|
// Keep the last successful login-shell result while a rescan starts a
|
|
@@ -14933,6 +14935,25 @@ function date4(params) {
|
|
|
14933
14935
|
// node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
|
|
14934
14936
|
config(en_default());
|
|
14935
14937
|
|
|
14938
|
+
// shared/image-generation.ts
|
|
14939
|
+
function normalizeImageGenerationUrl(value) {
|
|
14940
|
+
const message = "Use an HTTPS API base URL, or HTTP for a local router, without passwords, query parameters or fragments";
|
|
14941
|
+
let url2;
|
|
14942
|
+
try {
|
|
14943
|
+
url2 = new URL(value.trim());
|
|
14944
|
+
} catch {
|
|
14945
|
+
throw new Error(message);
|
|
14946
|
+
}
|
|
14947
|
+
if (value.length > 2048 || url2.username || url2.password || url2.search || url2.hash) throw new Error(message);
|
|
14948
|
+
const host = url2.hostname.toLowerCase();
|
|
14949
|
+
const ipv43 = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/.exec(host);
|
|
14950
|
+
const local = host === "localhost" || host.endsWith(".localhost") || host.endsWith(".local") || host === "host.docker.internal" || host === "host.containers.internal" || host === "[::1]" || /^\[f[cd][0-9a-f]{2}:/.test(host) || ipv43 && (Number(ipv43[1]) === 127 || Number(ipv43[1]) === 10 || Number(ipv43[1]) === 192 && Number(ipv43[2]) === 168 || Number(ipv43[1]) === 172 && Number(ipv43[2]) >= 16 && Number(ipv43[2]) <= 31);
|
|
14951
|
+
if (url2.protocol !== "https:" && !(url2.protocol === "http:" && local)) throw new Error(message);
|
|
14952
|
+
if (ipv43 && (Number(ipv43[1]) === 0 || Number(ipv43[1]) === 169 && Number(ipv43[2]) === 254)) throw new Error(message);
|
|
14953
|
+
url2.pathname = url2.pathname.replace(/\/+$/, "").replace(/\/images\/generations$/, "");
|
|
14954
|
+
return url2.toString().replace(/\/+$/, "");
|
|
14955
|
+
}
|
|
14956
|
+
|
|
14936
14957
|
// server/contracts.ts
|
|
14937
14958
|
var EFFORT_LEVELS = ["none", "low", "medium", "high", "xhigh", "max"];
|
|
14938
14959
|
|
|
@@ -15133,8 +15154,25 @@ var appConfigSchema = external_exports.object({
|
|
|
15133
15154
|
* engine: "elevenlabs" (default; needs a key) or "system" (the Mac's
|
|
15134
15155
|
* built-in voices, no key). */
|
|
15135
15156
|
tts: external_exports.object({ key: optionalText, voice: optionalText, provider: external_exports.enum(["elevenlabs", "system"]).optional() }).optional(),
|
|
15136
|
-
/**
|
|
15137
|
-
imageGen: external_exports.object({
|
|
15157
|
+
/** Avatar provider credentials stay separate; choosing a router never reuses a cloud key. */
|
|
15158
|
+
imageGen: external_exports.object({
|
|
15159
|
+
provider: external_exports.enum(["openai", "xai", "custom"]).optional(),
|
|
15160
|
+
key: optionalText,
|
|
15161
|
+
customApiKey: optionalText,
|
|
15162
|
+
customUrl: external_exports.string().trim().max(2048).transform((value, ctx) => {
|
|
15163
|
+
if (!value) return "";
|
|
15164
|
+
try {
|
|
15165
|
+
return normalizeImageGenerationUrl(value);
|
|
15166
|
+
} catch (error51) {
|
|
15167
|
+
ctx.addIssue({ code: "custom", message: error51.message });
|
|
15168
|
+
return external_exports.NEVER;
|
|
15169
|
+
}
|
|
15170
|
+
}).optional(),
|
|
15171
|
+
customModel: external_exports.string().trim().max(200).refine(
|
|
15172
|
+
(value) => !["\r", "\n", "\0"].some((character) => value.includes(character)),
|
|
15173
|
+
"Use a model ID without control characters"
|
|
15174
|
+
).optional()
|
|
15175
|
+
}).optional(),
|
|
15138
15176
|
/** Non-secret profile details shown in the sidebar. */
|
|
15139
15177
|
profile: external_exports.object({ name: optionalText, email: optionalText }).optional(),
|
|
15140
15178
|
/** UI language override (BCP-47, lowercase). Empty/absent = follow the
|