verybot 0.1.3
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.
Potentially problematic release.
This version of verybot might be problematic. Click here for more details.
- package/dist/brain/agent-registry.d.ts +75 -0
- package/dist/brain/agent-registry.js +124 -0
- package/dist/brain/agent.d.ts +146 -0
- package/dist/brain/agent.js +680 -0
- package/dist/brain/channel-store.d.ts +27 -0
- package/dist/brain/channel-store.js +78 -0
- package/dist/brain/compaction.d.ts +37 -0
- package/dist/brain/compaction.js +214 -0
- package/dist/brain/context.d.ts +33 -0
- package/dist/brain/context.js +77 -0
- package/dist/brain/delegation-store.d.ts +33 -0
- package/dist/brain/delegation-store.js +106 -0
- package/dist/brain/loop.d.ts +21 -0
- package/dist/brain/loop.js +161 -0
- package/dist/brain/mcp-adapter.d.ts +39 -0
- package/dist/brain/mcp-adapter.js +227 -0
- package/dist/brain/memory-extractor.d.ts +26 -0
- package/dist/brain/memory-extractor.js +82 -0
- package/dist/brain/providers.d.ts +10 -0
- package/dist/brain/providers.js +69 -0
- package/dist/brain/queue.d.ts +18 -0
- package/dist/brain/queue.js +84 -0
- package/dist/brain/run-tools.d.ts +47 -0
- package/dist/brain/run-tools.js +84 -0
- package/dist/brain/session-key.d.ts +23 -0
- package/dist/brain/session-key.js +41 -0
- package/dist/brain/session-state.d.ts +36 -0
- package/dist/brain/session-state.js +51 -0
- package/dist/brain/session-store.d.ts +50 -0
- package/dist/brain/session-store.js +207 -0
- package/dist/brain/session.d.ts +32 -0
- package/dist/brain/session.js +75 -0
- package/dist/brain/utils.d.ts +4 -0
- package/dist/brain/utils.js +26 -0
- package/dist/brain/worker-coordinator.d.ts +25 -0
- package/dist/brain/worker-coordinator.js +83 -0
- package/dist/channels/commands.d.ts +35 -0
- package/dist/channels/commands.js +65 -0
- package/dist/channels/discord/channel.d.ts +18 -0
- package/dist/channels/discord/channel.js +154 -0
- package/dist/channels/discord/markdown.d.ts +19 -0
- package/dist/channels/discord/markdown.js +62 -0
- package/dist/channels/manager.d.ts +29 -0
- package/dist/channels/manager.js +100 -0
- package/dist/channels/slack/channel.d.ts +26 -0
- package/dist/channels/slack/channel.js +207 -0
- package/dist/channels/slack/markdown.d.ts +19 -0
- package/dist/channels/slack/markdown.js +62 -0
- package/dist/channels/specs.d.ts +21 -0
- package/dist/channels/specs.js +96 -0
- package/dist/channels/telegram/channel.d.ts +18 -0
- package/dist/channels/telegram/channel.js +156 -0
- package/dist/channels/telegram/markdown.d.ts +17 -0
- package/dist/channels/telegram/markdown.js +66 -0
- package/dist/channels/types.d.ts +26 -0
- package/dist/channels/types.js +1 -0
- package/dist/channels/whatsapp/channel.d.ts +23 -0
- package/dist/channels/whatsapp/channel.js +242 -0
- package/dist/channels/whatsapp/markdown.d.ts +20 -0
- package/dist/channels/whatsapp/markdown.js +51 -0
- package/dist/cli/config.d.ts +5 -0
- package/dist/cli/config.js +78 -0
- package/dist/cli/index.d.ts +5 -0
- package/dist/cli/index.js +13 -0
- package/dist/computer/browser/actions.d.ts +31 -0
- package/dist/computer/browser/actions.js +148 -0
- package/dist/computer/browser/manager.d.ts +55 -0
- package/dist/computer/browser/manager.js +496 -0
- package/dist/computer/browser/profile-badge.d.ts +13 -0
- package/dist/computer/browser/profile-badge.js +67 -0
- package/dist/computer/browser/screenshot.d.ts +5 -0
- package/dist/computer/browser/screenshot.js +21 -0
- package/dist/computer/browser/snapshot.d.ts +30 -0
- package/dist/computer/browser/snapshot.js +242 -0
- package/dist/computer/browser/tools.d.ts +5 -0
- package/dist/computer/browser/tools.js +167 -0
- package/dist/computer/desktop/adapter.d.ts +25 -0
- package/dist/computer/desktop/adapter.js +11 -0
- package/dist/computer/desktop/macos.d.ts +24 -0
- package/dist/computer/desktop/macos.js +223 -0
- package/dist/computer/desktop/tools.d.ts +25 -0
- package/dist/computer/desktop/tools.js +114 -0
- package/dist/config/agent-config.d.ts +41 -0
- package/dist/config/agent-config.js +14 -0
- package/dist/config/model-catalog.d.ts +22 -0
- package/dist/config/model-catalog.js +99 -0
- package/dist/config/store.d.ts +25 -0
- package/dist/config/store.js +143 -0
- package/dist/config.d.ts +103 -0
- package/dist/config.js +224 -0
- package/dist/control-ui/assets/index-BANXNUyt.js +143 -0
- package/dist/control-ui/assets/index-BSUFrP9R.css +1 -0
- package/dist/control-ui/assets/noto-sans-cyrillic-ext-wght-normal-DSNfmdVt.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-cyrillic-wght-normal-B2hlT84T.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-devanagari-wght-normal-Cv-Vwajv.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-greek-ext-wght-normal-12T8GTDR.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-greek-wght-normal-Ymb6dZNd.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-latin-ext-wght-normal-W1qJv59z.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-latin-wght-normal-BYSzYMf3.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-vietnamese-wght-normal-DLTJy58D.woff2 +0 -0
- package/dist/control-ui/index.html +14 -0
- package/dist/control-ui/vite.svg +1 -0
- package/dist/events.d.ts +2 -0
- package/dist/events.js +11 -0
- package/dist/gateway/broadcast.d.ts +5 -0
- package/dist/gateway/broadcast.js +33 -0
- package/dist/gateway/methods/chat.d.ts +24 -0
- package/dist/gateway/methods/chat.js +19 -0
- package/dist/gateway/methods/config.d.ts +13 -0
- package/dist/gateway/methods/config.js +14 -0
- package/dist/gateway/methods/models.d.ts +10 -0
- package/dist/gateway/methods/models.js +14 -0
- package/dist/gateway/methods/prompt-templates.d.ts +23 -0
- package/dist/gateway/methods/prompt-templates.js +82 -0
- package/dist/gateway/methods/scheduler.d.ts +62 -0
- package/dist/gateway/methods/scheduler.js +129 -0
- package/dist/gateway/methods/sessions.d.ts +26 -0
- package/dist/gateway/methods/sessions.js +54 -0
- package/dist/gateway/methods/skills.d.ts +35 -0
- package/dist/gateway/methods/skills.js +202 -0
- package/dist/gateway/methods/system.d.ts +12 -0
- package/dist/gateway/methods/system.js +39 -0
- package/dist/gateway/methods/tasks.d.ts +21 -0
- package/dist/gateway/methods/tasks.js +46 -0
- package/dist/gateway/methods/teams.d.ts +70 -0
- package/dist/gateway/methods/teams.js +374 -0
- package/dist/gateway/methods/tools.d.ts +6 -0
- package/dist/gateway/methods/tools.js +7 -0
- package/dist/gateway/methods/whatsapp.d.ts +19 -0
- package/dist/gateway/methods/whatsapp.js +35 -0
- package/dist/gateway/rpc.d.ts +38 -0
- package/dist/gateway/rpc.js +75 -0
- package/dist/gateway/server.d.ts +4 -0
- package/dist/gateway/server.js +133 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +212 -0
- package/dist/integrations/github.d.ts +7 -0
- package/dist/integrations/github.js +133 -0
- package/dist/integrations/mcp.d.ts +7 -0
- package/dist/integrations/mcp.js +106 -0
- package/dist/integrations/registry.d.ts +43 -0
- package/dist/integrations/registry.js +258 -0
- package/dist/integrations/scanner.d.ts +10 -0
- package/dist/integrations/scanner.js +122 -0
- package/dist/integrations/twitter.d.ts +10 -0
- package/dist/integrations/twitter.js +120 -0
- package/dist/integrations/types.d.ts +72 -0
- package/dist/integrations/types.js +1 -0
- package/dist/logger.d.ts +16 -0
- package/dist/logger.js +104 -0
- package/dist/markdown/chunk.d.ts +9 -0
- package/dist/markdown/chunk.js +52 -0
- package/dist/markdown/ir.d.ts +37 -0
- package/dist/markdown/ir.js +529 -0
- package/dist/markdown/render.d.ts +22 -0
- package/dist/markdown/render.js +148 -0
- package/dist/markdown/table-render.d.ts +43 -0
- package/dist/markdown/table-render.js +219 -0
- package/dist/markdown/tables.d.ts +17 -0
- package/dist/markdown/tables.js +27 -0
- package/dist/memory/embedding.d.ts +16 -0
- package/dist/memory/embedding.js +66 -0
- package/dist/memory/extractor.d.ts +6 -0
- package/dist/memory/extractor.js +72 -0
- package/dist/memory/search.d.ts +15 -0
- package/dist/memory/search.js +57 -0
- package/dist/memory/store.d.ts +34 -0
- package/dist/memory/store.js +328 -0
- package/dist/memory/types.d.ts +9 -0
- package/dist/memory/types.js +2 -0
- package/dist/paths.d.ts +20 -0
- package/dist/paths.js +29 -0
- package/dist/prompt-templates/builtins.d.ts +2 -0
- package/dist/prompt-templates/builtins.js +72 -0
- package/dist/prompt-templates/store.d.ts +39 -0
- package/dist/prompt-templates/store.js +174 -0
- package/dist/prompt-templates/types.d.ts +10 -0
- package/dist/prompt-templates/types.js +1 -0
- package/dist/scheduler/connected-channels.d.ts +24 -0
- package/dist/scheduler/connected-channels.js +57 -0
- package/dist/scheduler/scheduler.d.ts +22 -0
- package/dist/scheduler/scheduler.js +132 -0
- package/dist/scheduler/store.d.ts +27 -0
- package/dist/scheduler/store.js +205 -0
- package/dist/scheduler/types.d.ts +29 -0
- package/dist/scheduler/types.js +1 -0
- package/dist/security/command-validator.d.ts +22 -0
- package/dist/security/command-validator.js +160 -0
- package/dist/security/docker-sandbox.d.ts +48 -0
- package/dist/security/docker-sandbox.js +218 -0
- package/dist/security/env-filter.d.ts +8 -0
- package/dist/security/env-filter.js +41 -0
- package/dist/skills/loader.d.ts +33 -0
- package/dist/skills/loader.js +132 -0
- package/dist/skills/prompt.d.ts +6 -0
- package/dist/skills/prompt.js +17 -0
- package/dist/skills/read-tool.d.ts +7 -0
- package/dist/skills/read-tool.js +24 -0
- package/dist/skills/scanner.d.ts +6 -0
- package/dist/skills/scanner.js +73 -0
- package/dist/skills/types.d.ts +15 -0
- package/dist/skills/types.js +1 -0
- package/dist/tasks/store.d.ts +47 -0
- package/dist/tasks/store.js +193 -0
- package/dist/tasks/types.d.ts +75 -0
- package/dist/tasks/types.js +32 -0
- package/dist/teams/store.d.ts +78 -0
- package/dist/teams/store.js +420 -0
- package/dist/teams/types.d.ts +23 -0
- package/dist/teams/types.js +1 -0
- package/dist/tools/bash.d.ts +16 -0
- package/dist/tools/bash.js +62 -0
- package/dist/tools/channel-history.d.ts +10 -0
- package/dist/tools/channel-history.js +43 -0
- package/dist/tools/delegate.d.ts +16 -0
- package/dist/tools/delegate.js +216 -0
- package/dist/tools/fs.d.ts +4 -0
- package/dist/tools/fs.js +335 -0
- package/dist/tools/integration-toggle.d.ts +14 -0
- package/dist/tools/integration-toggle.js +47 -0
- package/dist/tools/memory.d.ts +13 -0
- package/dist/tools/memory.js +65 -0
- package/dist/tools/registry.d.ts +6 -0
- package/dist/tools/registry.js +9 -0
- package/dist/tools/schedule.d.ts +8 -0
- package/dist/tools/schedule.js +219 -0
- package/dist/tools/speak.d.ts +10 -0
- package/dist/tools/speak.js +56 -0
- package/dist/tools/tasks.d.ts +29 -0
- package/dist/tools/tasks.js +92 -0
- package/dist/tools/teams.d.ts +7 -0
- package/dist/tools/teams.js +180 -0
- package/dist/tools/web-fetch.d.ts +3 -0
- package/dist/tools/web-fetch.js +22 -0
- package/dist/tts/edge.d.ts +10 -0
- package/dist/tts/edge.js +60 -0
- package/dist/tts/speak.d.ts +12 -0
- package/dist/tts/speak.js +81 -0
- package/dist/tts/transcribe.d.ts +5 -0
- package/dist/tts/transcribe.js +40 -0
- package/dist/utils.d.ts +5 -0
- package/dist/utils.js +22 -0
- package/package.json +90 -0
- package/verybot.js +2 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { execFileSync, execSync } from "child_process";
|
|
2
|
+
import { readFileSync, unlinkSync } from "fs";
|
|
3
|
+
import { tmpdir } from "os";
|
|
4
|
+
import { join } from "path";
|
|
5
|
+
import { randomUUID } from "crypto";
|
|
6
|
+
import { logger } from "../../logger.js";
|
|
7
|
+
/** Timeout for shell commands (ms). */
|
|
8
|
+
const CMD_TIMEOUT = 30_000;
|
|
9
|
+
/** xdotool key name → cliclick key name */
|
|
10
|
+
const KEY_MAP = {
|
|
11
|
+
return: "return",
|
|
12
|
+
enter: "return",
|
|
13
|
+
tab: "tab",
|
|
14
|
+
space: "space",
|
|
15
|
+
escape: "escape",
|
|
16
|
+
delete: "delete",
|
|
17
|
+
backspace: "delete",
|
|
18
|
+
up: "arrow-up",
|
|
19
|
+
down: "arrow-down",
|
|
20
|
+
left: "arrow-left",
|
|
21
|
+
right: "arrow-right",
|
|
22
|
+
home: "home",
|
|
23
|
+
end: "end",
|
|
24
|
+
pageup: "page-up",
|
|
25
|
+
page_up: "page-up",
|
|
26
|
+
pagedown: "page-down",
|
|
27
|
+
page_down: "page-down",
|
|
28
|
+
f1: "f1",
|
|
29
|
+
f2: "f2",
|
|
30
|
+
f3: "f3",
|
|
31
|
+
f4: "f4",
|
|
32
|
+
f5: "f5",
|
|
33
|
+
f6: "f6",
|
|
34
|
+
f7: "f7",
|
|
35
|
+
f8: "f8",
|
|
36
|
+
f9: "f9",
|
|
37
|
+
f10: "f10",
|
|
38
|
+
f11: "f11",
|
|
39
|
+
f12: "f12",
|
|
40
|
+
};
|
|
41
|
+
/** xdotool modifier → cliclick modifier */
|
|
42
|
+
const MODIFIER_MAP = {
|
|
43
|
+
ctrl: "ctrl",
|
|
44
|
+
control: "ctrl",
|
|
45
|
+
alt: "alt",
|
|
46
|
+
option: "alt",
|
|
47
|
+
shift: "shift",
|
|
48
|
+
super: "cmd",
|
|
49
|
+
meta: "cmd",
|
|
50
|
+
cmd: "cmd",
|
|
51
|
+
command: "cmd",
|
|
52
|
+
};
|
|
53
|
+
export class MacOSAdapter {
|
|
54
|
+
screenInfo = null;
|
|
55
|
+
constructor() {
|
|
56
|
+
this.checkDependencies();
|
|
57
|
+
}
|
|
58
|
+
getScreenInfo() {
|
|
59
|
+
if (!this.screenInfo) {
|
|
60
|
+
this.screenInfo = this.detectScreenInfo();
|
|
61
|
+
}
|
|
62
|
+
return this.screenInfo;
|
|
63
|
+
}
|
|
64
|
+
async screenshot() {
|
|
65
|
+
const tmpFile = join(tmpdir(), `mvp-screenshot-${randomUUID()}.png`);
|
|
66
|
+
try {
|
|
67
|
+
execFileSync("screencapture", ["-x", "-t", "png", tmpFile], {
|
|
68
|
+
timeout: CMD_TIMEOUT,
|
|
69
|
+
});
|
|
70
|
+
return readFileSync(tmpFile);
|
|
71
|
+
}
|
|
72
|
+
finally {
|
|
73
|
+
try {
|
|
74
|
+
unlinkSync(tmpFile);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// ignore cleanup errors
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async click(x, y) {
|
|
82
|
+
const [lx, ly] = this.toLogical(x, y);
|
|
83
|
+
this.cliclick(`c:${lx},${ly}`);
|
|
84
|
+
}
|
|
85
|
+
async doubleClick(x, y) {
|
|
86
|
+
const [lx, ly] = this.toLogical(x, y);
|
|
87
|
+
this.cliclick(`dc:${lx},${ly}`);
|
|
88
|
+
}
|
|
89
|
+
async rightClick(x, y) {
|
|
90
|
+
const [lx, ly] = this.toLogical(x, y);
|
|
91
|
+
this.cliclick(`rc:${lx},${ly}`);
|
|
92
|
+
}
|
|
93
|
+
async middleClick(x, y) {
|
|
94
|
+
// cliclick doesn't have a native middle-click; simulate with option+click
|
|
95
|
+
const [lx, ly] = this.toLogical(x, y);
|
|
96
|
+
this.cliclick(`kd:alt`, `c:${lx},${ly}`, `ku:alt`);
|
|
97
|
+
}
|
|
98
|
+
async tripleClick(x, y) {
|
|
99
|
+
const [lx, ly] = this.toLogical(x, y);
|
|
100
|
+
this.cliclick(`tc:${lx},${ly}`);
|
|
101
|
+
}
|
|
102
|
+
async mouseMove(x, y) {
|
|
103
|
+
const [lx, ly] = this.toLogical(x, y);
|
|
104
|
+
this.cliclick(`m:${lx},${ly}`);
|
|
105
|
+
}
|
|
106
|
+
async type(text) {
|
|
107
|
+
// cliclick t: types the literal text
|
|
108
|
+
this.cliclick(`t:${text}`);
|
|
109
|
+
}
|
|
110
|
+
async key(combo) {
|
|
111
|
+
const parts = combo.split("+").map((s) => s.trim().toLowerCase());
|
|
112
|
+
const modifiers = [];
|
|
113
|
+
let mainKey = null;
|
|
114
|
+
for (const part of parts) {
|
|
115
|
+
if (MODIFIER_MAP[part]) {
|
|
116
|
+
modifiers.push(MODIFIER_MAP[part]);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
mainKey = KEY_MAP[part] ?? part;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (!mainKey && modifiers.length > 0) {
|
|
123
|
+
// Single modifier key press (e.g., just "shift")
|
|
124
|
+
mainKey = modifiers.pop();
|
|
125
|
+
}
|
|
126
|
+
if (!mainKey) {
|
|
127
|
+
logger.warn(`Desktop: could not parse key combo "${combo}"`);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const args = [];
|
|
131
|
+
for (const mod of modifiers)
|
|
132
|
+
args.push(`kd:${mod}`);
|
|
133
|
+
args.push(`kp:${mainKey}`);
|
|
134
|
+
for (const mod of modifiers.reverse())
|
|
135
|
+
args.push(`ku:${mod}`);
|
|
136
|
+
this.cliclick(...args);
|
|
137
|
+
}
|
|
138
|
+
async scroll(x, y, direction, amount) {
|
|
139
|
+
const [lx, ly] = this.toLogical(x, y);
|
|
140
|
+
// Move to position first, then use AppleScript for scrolling
|
|
141
|
+
this.cliclick(`m:${lx},${ly}`);
|
|
142
|
+
// AppleScript scroll: positive = up, negative = down
|
|
143
|
+
const scrollAmount = direction === "up" || direction === "left" ? amount : -amount;
|
|
144
|
+
const axis = direction === "left" || direction === "right" ? "axis 1" : "axis 2";
|
|
145
|
+
// Use CGEvent for scrolling via python3 (available on macOS)
|
|
146
|
+
const script = `tell application "System Events" to ` +
|
|
147
|
+
`scroll ${axis === "axis 2" ? "vertically" : "horizontally"} by ${scrollAmount}`;
|
|
148
|
+
try {
|
|
149
|
+
execFileSync("osascript", ["-e", script], { timeout: CMD_TIMEOUT });
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
// Fallback: use key-based scrolling
|
|
153
|
+
const key = direction === "up" ? "arrow-up" : "arrow-down";
|
|
154
|
+
for (let i = 0; i < Math.abs(amount); i++) {
|
|
155
|
+
this.cliclick(`kp:${key}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
async getCursorPosition() {
|
|
160
|
+
const output = execFileSync("cliclick", ["p:"], {
|
|
161
|
+
encoding: "utf-8",
|
|
162
|
+
timeout: CMD_TIMEOUT,
|
|
163
|
+
}).trim();
|
|
164
|
+
// Output format: "x,y" (logical coordinates)
|
|
165
|
+
const [lx, ly] = output.split(",").map(Number);
|
|
166
|
+
const { scaleFactor } = this.getScreenInfo();
|
|
167
|
+
return [lx * scaleFactor, ly * scaleFactor];
|
|
168
|
+
}
|
|
169
|
+
async dragTo(startX, startY, endX, endY) {
|
|
170
|
+
const [sx, sy] = this.toLogical(startX, startY);
|
|
171
|
+
const [ex, ey] = this.toLogical(endX, endY);
|
|
172
|
+
this.cliclick(`dd:${sx},${sy}`, `du:${ex},${ey}`);
|
|
173
|
+
}
|
|
174
|
+
// ── Private helpers ──
|
|
175
|
+
checkDependencies() {
|
|
176
|
+
try {
|
|
177
|
+
execFileSync("which", ["cliclick"], { timeout: CMD_TIMEOUT });
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
throw new Error("cliclick is required for desktop control on macOS. " +
|
|
181
|
+
"Install it with: brew install cliclick");
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
detectScreenInfo() {
|
|
185
|
+
// Get screenshot pixel dimensions
|
|
186
|
+
const tmpFile = join(tmpdir(), `mvp-screeninfo-${randomUUID()}.png`);
|
|
187
|
+
try {
|
|
188
|
+
execFileSync("screencapture", ["-x", "-t", "png", tmpFile], {
|
|
189
|
+
timeout: CMD_TIMEOUT,
|
|
190
|
+
});
|
|
191
|
+
const sipsOutput = execFileSync("sips", ["-g", "pixelWidth", "-g", "pixelHeight", tmpFile], { encoding: "utf-8", timeout: CMD_TIMEOUT });
|
|
192
|
+
const widthMatch = sipsOutput.match(/pixelWidth:\s*(\d+)/);
|
|
193
|
+
const heightMatch = sipsOutput.match(/pixelHeight:\s*(\d+)/);
|
|
194
|
+
const widthPx = widthMatch ? Number(widthMatch[1]) : 1920;
|
|
195
|
+
const heightPx = heightMatch ? Number(heightMatch[1]) : 1080;
|
|
196
|
+
// Get logical screen size via osascript
|
|
197
|
+
const logicalOutput = execSync(`osascript -l JavaScript -e 'ObjC.import("AppKit"); ` +
|
|
198
|
+
`var f = $.NSScreen.mainScreen.frame; ` +
|
|
199
|
+
`f.size.width + " " + f.size.height'`, { encoding: "utf-8", timeout: CMD_TIMEOUT }).trim();
|
|
200
|
+
const [logicalW] = logicalOutput.split(" ").map(Number);
|
|
201
|
+
const scaleFactor = logicalW > 0 ? Math.round(widthPx / logicalW) : 2;
|
|
202
|
+
logger.info(`Desktop: screen ${widthPx}x${heightPx}px, logical ${logicalOutput}, scale ${scaleFactor}x`);
|
|
203
|
+
return { widthPx, heightPx, scaleFactor };
|
|
204
|
+
}
|
|
205
|
+
finally {
|
|
206
|
+
try {
|
|
207
|
+
unlinkSync(tmpFile);
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
// ignore
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
/** Convert pixel coordinates to logical (point) coordinates for cliclick. */
|
|
215
|
+
toLogical(x, y) {
|
|
216
|
+
const { scaleFactor } = this.getScreenInfo();
|
|
217
|
+
return [Math.round(x / scaleFactor), Math.round(y / scaleFactor)];
|
|
218
|
+
}
|
|
219
|
+
/** Run cliclick with the given action arguments. */
|
|
220
|
+
cliclick(...args) {
|
|
221
|
+
execFileSync("cliclick", args, { timeout: CMD_TIMEOUT });
|
|
222
|
+
}
|
|
223
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { DesktopAdapter } from "./adapter.js";
|
|
2
|
+
export declare function createDesktopTool(adapter: DesktopAdapter, modelId: string): import("ai").Tool<{
|
|
3
|
+
action: "key" | "hold_key" | "type" | "cursor_position" | "mouse_move" | "left_mouse_down" | "left_mouse_up" | "left_click" | "left_click_drag" | "right_click" | "middle_click" | "double_click" | "triple_click" | "scroll" | "wait" | "screenshot";
|
|
4
|
+
coordinate?: [number, number];
|
|
5
|
+
duration?: number;
|
|
6
|
+
scroll_amount?: number;
|
|
7
|
+
scroll_direction?: "up" | "down" | "left" | "right";
|
|
8
|
+
start_coordinate?: [number, number];
|
|
9
|
+
text?: string;
|
|
10
|
+
}, string | {
|
|
11
|
+
data: Buffer;
|
|
12
|
+
mediaType: string;
|
|
13
|
+
}> | import("ai").Tool<{
|
|
14
|
+
action: "key" | "hold_key" | "type" | "cursor_position" | "mouse_move" | "left_mouse_down" | "left_mouse_up" | "left_click" | "left_click_drag" | "right_click" | "middle_click" | "double_click" | "triple_click" | "scroll" | "wait" | "screenshot" | "zoom";
|
|
15
|
+
coordinate?: [number, number];
|
|
16
|
+
duration?: number;
|
|
17
|
+
region?: [number, number, number, number];
|
|
18
|
+
scroll_amount?: number;
|
|
19
|
+
scroll_direction?: "up" | "down" | "left" | "right";
|
|
20
|
+
start_coordinate?: [number, number];
|
|
21
|
+
text?: string;
|
|
22
|
+
}, string | {
|
|
23
|
+
data: Buffer;
|
|
24
|
+
mediaType: string;
|
|
25
|
+
}>;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { anthropic } from "@ai-sdk/anthropic";
|
|
2
|
+
import sharp from "sharp";
|
|
3
|
+
import { logger } from "../../logger.js";
|
|
4
|
+
/** Anthropic API rejects base64 images over 5 MB. We compress PNG → JPEG
|
|
5
|
+
* to stay well under the limit while preserving original dimensions. */
|
|
6
|
+
const MAX_IMAGE_BYTES = 3_500_000;
|
|
7
|
+
const JPEG_QUALITY_START = 80;
|
|
8
|
+
const JPEG_QUALITY_MIN = 40;
|
|
9
|
+
const JPEG_QUALITY_STEP = 10;
|
|
10
|
+
/** Compress a screenshot PNG to JPEG, reducing quality until under the size limit. */
|
|
11
|
+
async function compressScreenshot(png) {
|
|
12
|
+
for (let quality = JPEG_QUALITY_START; quality >= JPEG_QUALITY_MIN; quality -= JPEG_QUALITY_STEP) {
|
|
13
|
+
const jpeg = await sharp(png).jpeg({ quality }).toBuffer();
|
|
14
|
+
if (jpeg.length <= MAX_IMAGE_BYTES) {
|
|
15
|
+
logger.info(`Desktop: screenshot compressed ${png.length} → ${jpeg.length} bytes (JPEG q${quality})`);
|
|
16
|
+
return { data: jpeg, mediaType: "image/jpeg" };
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
// Last resort: lowest quality
|
|
20
|
+
const jpeg = await sharp(png).jpeg({ quality: JPEG_QUALITY_MIN }).toBuffer();
|
|
21
|
+
logger.info(`Desktop: screenshot compressed ${png.length} → ${jpeg.length} bytes (JPEG q${JPEG_QUALITY_MIN}, may exceed limit)`);
|
|
22
|
+
return { data: jpeg, mediaType: "image/jpeg" };
|
|
23
|
+
}
|
|
24
|
+
/** Models that only support the older computer_20250124 tool version. */
|
|
25
|
+
const OLDER_TOOL_MODELS = ["haiku", "claude-3"];
|
|
26
|
+
export function createDesktopTool(adapter, modelId) {
|
|
27
|
+
const screen = adapter.getScreenInfo();
|
|
28
|
+
const useOlder = OLDER_TOOL_MODELS.some((m) => modelId.includes(m));
|
|
29
|
+
const factory = useOlder
|
|
30
|
+
? anthropic.tools.computer_20250124
|
|
31
|
+
: anthropic.tools.computer_20251124;
|
|
32
|
+
logger.info(`Desktop: using computer tool ${useOlder ? "20250124" : "20251124"} for model ${modelId}`);
|
|
33
|
+
return factory({
|
|
34
|
+
displayWidthPx: screen.widthPx,
|
|
35
|
+
displayHeightPx: screen.heightPx,
|
|
36
|
+
execute: async (input) => {
|
|
37
|
+
logger.info(`Desktop: ${input.action} ${JSON.stringify(input)}`);
|
|
38
|
+
switch (input.action) {
|
|
39
|
+
case "screenshot": {
|
|
40
|
+
const png = await adapter.screenshot();
|
|
41
|
+
// Compress to stay under Anthropic's 5 MB base64 limit
|
|
42
|
+
const compressed = await compressScreenshot(png);
|
|
43
|
+
return compressed;
|
|
44
|
+
}
|
|
45
|
+
case "left_click":
|
|
46
|
+
await adapter.click(input.coordinate[0], input.coordinate[1]);
|
|
47
|
+
return `Clicked at (${input.coordinate[0]}, ${input.coordinate[1]})`;
|
|
48
|
+
case "double_click":
|
|
49
|
+
await adapter.doubleClick(input.coordinate[0], input.coordinate[1]);
|
|
50
|
+
return `Double-clicked at (${input.coordinate[0]}, ${input.coordinate[1]})`;
|
|
51
|
+
case "right_click":
|
|
52
|
+
await adapter.rightClick(input.coordinate[0], input.coordinate[1]);
|
|
53
|
+
return `Right-clicked at (${input.coordinate[0]}, ${input.coordinate[1]})`;
|
|
54
|
+
case "middle_click":
|
|
55
|
+
await adapter.middleClick(input.coordinate[0], input.coordinate[1]);
|
|
56
|
+
return `Middle-clicked at (${input.coordinate[0]}, ${input.coordinate[1]})`;
|
|
57
|
+
case "triple_click":
|
|
58
|
+
await adapter.tripleClick(input.coordinate[0], input.coordinate[1]);
|
|
59
|
+
return `Triple-clicked at (${input.coordinate[0]}, ${input.coordinate[1]})`;
|
|
60
|
+
case "mouse_move":
|
|
61
|
+
await adapter.mouseMove(input.coordinate[0], input.coordinate[1]);
|
|
62
|
+
return `Moved cursor to (${input.coordinate[0]}, ${input.coordinate[1]})`;
|
|
63
|
+
case "type":
|
|
64
|
+
await adapter.type(input.text);
|
|
65
|
+
return `Typed: ${input.text.slice(0, 100)}`;
|
|
66
|
+
case "key":
|
|
67
|
+
await adapter.key(input.text);
|
|
68
|
+
return `Pressed: ${input.text}`;
|
|
69
|
+
case "scroll":
|
|
70
|
+
await adapter.scroll(input.coordinate[0], input.coordinate[1], input.scroll_direction, input.scroll_amount);
|
|
71
|
+
return `Scrolled ${input.scroll_direction} by ${input.scroll_amount}`;
|
|
72
|
+
case "cursor_position": {
|
|
73
|
+
const [cx, cy] = await adapter.getCursorPosition();
|
|
74
|
+
return `Cursor position: (${cx}, ${cy})`;
|
|
75
|
+
}
|
|
76
|
+
case "left_click_drag":
|
|
77
|
+
await adapter.dragTo(input.start_coordinate[0], input.start_coordinate[1], input.coordinate[0], input.coordinate[1]);
|
|
78
|
+
return `Dragged from (${input.start_coordinate[0]}, ${input.start_coordinate[1]}) to (${input.coordinate[0]}, ${input.coordinate[1]})`;
|
|
79
|
+
case "hold_key":
|
|
80
|
+
// hold_key with duration: press key, wait, release
|
|
81
|
+
await adapter.key(input.text);
|
|
82
|
+
return `Held key: ${input.text} for ${input.duration}s`;
|
|
83
|
+
case "wait":
|
|
84
|
+
await new Promise((r) => setTimeout(r, (input.duration ?? 1) * 1000));
|
|
85
|
+
return `Waited ${input.duration ?? 1}s`;
|
|
86
|
+
case "left_mouse_down":
|
|
87
|
+
// Not directly supported by cliclick as a standalone; simulate with drag start
|
|
88
|
+
await adapter.mouseMove(input.coordinate[0], input.coordinate[1]);
|
|
89
|
+
return `Mouse down at (${input.coordinate[0]}, ${input.coordinate[1]})`;
|
|
90
|
+
case "left_mouse_up":
|
|
91
|
+
await adapter.mouseMove(input.coordinate[0], input.coordinate[1]);
|
|
92
|
+
return `Mouse up at (${input.coordinate[0]}, ${input.coordinate[1]})`;
|
|
93
|
+
default:
|
|
94
|
+
return `Unsupported action: ${input.action}`;
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
toModelOutput({ input, output }) {
|
|
98
|
+
if (input.action === "screenshot" && typeof output === "object" && output !== null && "data" in output) {
|
|
99
|
+
const { data, mediaType } = output;
|
|
100
|
+
return {
|
|
101
|
+
type: "content",
|
|
102
|
+
value: [
|
|
103
|
+
{
|
|
104
|
+
type: "image-data",
|
|
105
|
+
data: data.toString("base64"),
|
|
106
|
+
mediaType,
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
return { type: "text", value: String(output) };
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export interface AgentConfig {
|
|
2
|
+
/** Unique agent identifier, e.g. "lead", "researcher", "coder". */
|
|
3
|
+
id: string;
|
|
4
|
+
/** Human-readable display name for this agent. */
|
|
5
|
+
name: string;
|
|
6
|
+
/** Full model string, e.g. "anthropic:claude-sonnet-4-5-20250929". */
|
|
7
|
+
model: string;
|
|
8
|
+
/** Override context window for custom models (0 = auto from catalog). */
|
|
9
|
+
contextWindow: number;
|
|
10
|
+
/** Max tool-call steps per run (0 = inherit global default). */
|
|
11
|
+
maxSteps: number;
|
|
12
|
+
/** System prompt / identity for this agent. */
|
|
13
|
+
identity: string;
|
|
14
|
+
/** Tool names to allow (empty = inherit all base tools). */
|
|
15
|
+
tools: string[];
|
|
16
|
+
/** Worker timeout in seconds (default 300 = 5 min). Only meaningful for workers. */
|
|
17
|
+
timeout: number;
|
|
18
|
+
/** If set, identity is resolved from the linked prompt template. */
|
|
19
|
+
templateId?: string | null;
|
|
20
|
+
}
|
|
21
|
+
export interface TeamConfig {
|
|
22
|
+
/** Unique team identifier, e.g. "research", "coding". */
|
|
23
|
+
id: string;
|
|
24
|
+
/** Human-readable label for UI. */
|
|
25
|
+
name?: string;
|
|
26
|
+
/** Team color for visual identification in the UI. */
|
|
27
|
+
color?: string;
|
|
28
|
+
/** Working directory for this team's agents. */
|
|
29
|
+
workspace?: string;
|
|
30
|
+
/** User-defined key-value pairs injected into agent prompts via {{varName}}. */
|
|
31
|
+
variables?: Record<string, string>;
|
|
32
|
+
/** The team's orchestrator agent. */
|
|
33
|
+
orchestrator: AgentConfig;
|
|
34
|
+
/** Workers scoped to this team. */
|
|
35
|
+
workers: AgentConfig[];
|
|
36
|
+
}
|
|
37
|
+
export declare const DEFAULT_TEAM_ID = "default";
|
|
38
|
+
/** Default worker timeout: 30 minutes. */
|
|
39
|
+
export declare const DEFAULT_WORKER_TIMEOUT_S = 1800;
|
|
40
|
+
/** Fallback orchestrator for teams that have no orchestrator agent row yet. */
|
|
41
|
+
export declare const FALLBACK_ORCHESTRATOR: AgentConfig;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const DEFAULT_TEAM_ID = "default";
|
|
2
|
+
/** Default worker timeout: 30 minutes. */
|
|
3
|
+
export const DEFAULT_WORKER_TIMEOUT_S = 1_800;
|
|
4
|
+
/** Fallback orchestrator for teams that have no orchestrator agent row yet. */
|
|
5
|
+
export const FALLBACK_ORCHESTRATOR = {
|
|
6
|
+
id: "main",
|
|
7
|
+
name: "main",
|
|
8
|
+
model: "",
|
|
9
|
+
contextWindow: 0,
|
|
10
|
+
maxSteps: 0,
|
|
11
|
+
identity: "",
|
|
12
|
+
tools: [],
|
|
13
|
+
timeout: DEFAULT_WORKER_TIMEOUT_S,
|
|
14
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface ModelDef {
|
|
2
|
+
/** Provider key matching `providers.ts`, e.g. "anthropic". */
|
|
3
|
+
provider: string;
|
|
4
|
+
/** Full model ID passed to the provider SDK, e.g. "claude-sonnet-4-5-20250929". */
|
|
5
|
+
modelId: string;
|
|
6
|
+
/** Human-readable group label for the UI picker. */
|
|
7
|
+
group: string;
|
|
8
|
+
/** Context window size in tokens. */
|
|
9
|
+
contextWindow: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Single source of truth for all built-in model definitions.
|
|
13
|
+
* Both the UI picker list and the context-window metadata derive from this.
|
|
14
|
+
*/
|
|
15
|
+
export declare const MODEL_CATALOG: ModelDef[];
|
|
16
|
+
/**
|
|
17
|
+
* Resolve a ModelDef for a given model ID.
|
|
18
|
+
* Lookup order: exact match > longest-prefix match > fallback.
|
|
19
|
+
* `contextWindowOverride` only applies when the model is NOT in the catalog —
|
|
20
|
+
* catalog values are always authoritative.
|
|
21
|
+
*/
|
|
22
|
+
export declare function resolveModelDef(modelId: string, contextWindowOverride?: number): ModelDef;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of truth for all built-in model definitions.
|
|
3
|
+
* Both the UI picker list and the context-window metadata derive from this.
|
|
4
|
+
*/
|
|
5
|
+
export const MODEL_CATALOG = [
|
|
6
|
+
// Claude Code (subscription — no API key needed, requires `claude login`)
|
|
7
|
+
{ provider: "claude-code", modelId: "haiku", group: "Claude Code", contextWindow: 200_000 },
|
|
8
|
+
{ provider: "claude-code", modelId: "sonnet", group: "Claude Code", contextWindow: 200_000 },
|
|
9
|
+
{ provider: "claude-code", modelId: "opus", group: "Claude Code", contextWindow: 200_000 },
|
|
10
|
+
// Codex CLI (OpenAI subscription — no API key needed, requires `codex login`)
|
|
11
|
+
// Context windows are approximate; override via config `contextWindow` if needed.
|
|
12
|
+
{ provider: "codex-cli", modelId: "gpt-5.1-codex-mini", group: "Codex CLI", contextWindow: 200_000 },
|
|
13
|
+
{ provider: "codex-cli", modelId: "gpt-5.1-codex-max", group: "Codex CLI", contextWindow: 200_000 },
|
|
14
|
+
{ provider: "codex-cli", modelId: "gpt-5.2-codex", group: "Codex CLI", contextWindow: 200_000 },
|
|
15
|
+
// Anthropic (API key)
|
|
16
|
+
{ provider: "anthropic", modelId: "claude-haiku-4-5-20251001", group: "Anthropic", contextWindow: 200_000 },
|
|
17
|
+
{ provider: "anthropic", modelId: "claude-opus-4-6", group: "Anthropic", contextWindow: 200_000 },
|
|
18
|
+
{ provider: "anthropic", modelId: "claude-sonnet-4-5-20250929", group: "Anthropic", contextWindow: 200_000 },
|
|
19
|
+
// OpenAI
|
|
20
|
+
{ provider: "openai", modelId: "gpt-4.1", group: "OpenAI", contextWindow: 1_000_000 },
|
|
21
|
+
{ provider: "openai", modelId: "gpt-4o", group: "OpenAI", contextWindow: 128_000 },
|
|
22
|
+
{ provider: "openai", modelId: "gpt-4o-mini", group: "OpenAI", contextWindow: 128_000 },
|
|
23
|
+
// Google
|
|
24
|
+
{ provider: "google", modelId: "gemini-2.0-flash", group: "Google", contextWindow: 1_000_000 },
|
|
25
|
+
{ provider: "google", modelId: "gemini-2.5-pro", group: "Google", contextWindow: 1_000_000 },
|
|
26
|
+
// Groq
|
|
27
|
+
{ provider: "groq", modelId: "llama-3.1-8b-instant", group: "Groq", contextWindow: 131_072 },
|
|
28
|
+
{ provider: "groq", modelId: "llama-3.3-70b-versatile", group: "Groq", contextWindow: 128_000 },
|
|
29
|
+
{ provider: "groq", modelId: "mixtral-8x7b-32768", group: "Groq", contextWindow: 32_768 },
|
|
30
|
+
// OpenRouter — own models + top 15 most popular (openrouter.ai/rankings)
|
|
31
|
+
{ provider: "openrouter", modelId: "anthropic/claude-4.5-opus-20251124", group: "OpenRouter", contextWindow: 200_000 },
|
|
32
|
+
{ provider: "openrouter", modelId: "anthropic/claude-4.5-sonnet-20250929", group: "OpenRouter", contextWindow: 1_000_000 },
|
|
33
|
+
{ provider: "openrouter", modelId: "anthropic/claude-4.6-opus-20260205", group: "OpenRouter", contextWindow: 1_000_000 },
|
|
34
|
+
{ provider: "openrouter", modelId: "deepseek/deepseek-v3.2-20251201", group: "OpenRouter", contextWindow: 163_840 },
|
|
35
|
+
{ provider: "openrouter", modelId: "google/gemini-2.0-flash-001", group: "OpenRouter", contextWindow: 1_048_576 },
|
|
36
|
+
{ provider: "openrouter", modelId: "google/gemini-2.5-flash", group: "OpenRouter", contextWindow: 1_048_576 },
|
|
37
|
+
{ provider: "openrouter", modelId: "google/gemini-2.5-pro", group: "OpenRouter", contextWindow: 1_048_576 },
|
|
38
|
+
{ provider: "openrouter", modelId: "google/gemini-3-flash-preview-20251217", group: "OpenRouter", contextWindow: 1_048_576 },
|
|
39
|
+
{ provider: "openrouter", modelId: "minimax/minimax-m2.1", group: "OpenRouter", contextWindow: 196_608 },
|
|
40
|
+
{ provider: "openrouter", modelId: "minimax/minimax-m2.5", group: "OpenRouter", contextWindow: 204_800 },
|
|
41
|
+
{ provider: "openrouter", modelId: "mistralai/devstral-2512", group: "OpenRouter", contextWindow: 262_144 },
|
|
42
|
+
{ provider: "openrouter", modelId: "moonshotai/kimi-k2.5-0127", group: "OpenRouter", contextWindow: 262_144 },
|
|
43
|
+
{ provider: "openrouter", modelId: "openai/gpt-5.2-20251211", group: "OpenRouter", contextWindow: 400_000 },
|
|
44
|
+
{ provider: "openrouter", modelId: "openai/gpt-oss-120b", group: "OpenRouter", contextWindow: 131_072 },
|
|
45
|
+
{ provider: "openrouter", modelId: "openrouter/aurora-alpha", group: "OpenRouter", contextWindow: 128_000 },
|
|
46
|
+
{ provider: "openrouter", modelId: "openrouter/pony-alpha", group: "OpenRouter", contextWindow: 200_000 },
|
|
47
|
+
{ provider: "openrouter", modelId: "x-ai/grok-4.1-fast", group: "OpenRouter", contextWindow: 2_000_000 },
|
|
48
|
+
{ provider: "openrouter", modelId: "x-ai/grok-code-fast-1", group: "OpenRouter", contextWindow: 256_000 },
|
|
49
|
+
// DeepSeek
|
|
50
|
+
{ provider: "deepseek", modelId: "deepseek-chat", group: "DeepSeek", contextWindow: 128_000 },
|
|
51
|
+
{ provider: "deepseek", modelId: "deepseek-reasoner", group: "DeepSeek", contextWindow: 128_000 },
|
|
52
|
+
// Mistral
|
|
53
|
+
{ provider: "mistral", modelId: "codestral-latest", group: "Mistral", contextWindow: 256_000 },
|
|
54
|
+
{ provider: "mistral", modelId: "mistral-large-latest", group: "Mistral", contextWindow: 128_000 },
|
|
55
|
+
{ provider: "mistral", modelId: "mistral-small-latest", group: "Mistral", contextWindow: 128_000 },
|
|
56
|
+
// xAI
|
|
57
|
+
{ provider: "xai", modelId: "grok-3", group: "xAI", contextWindow: 131_072 },
|
|
58
|
+
{ provider: "xai", modelId: "grok-3-mini", group: "xAI", contextWindow: 131_072 },
|
|
59
|
+
// Together AI
|
|
60
|
+
{ provider: "togetherai", modelId: "meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo", group: "Together AI", contextWindow: 128_000 },
|
|
61
|
+
{ provider: "togetherai", modelId: "meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo", group: "Together AI", contextWindow: 128_000 },
|
|
62
|
+
// Ollama (local)
|
|
63
|
+
{ provider: "ollama", modelId: "deepseek-r1", group: "Ollama", contextWindow: 128_000 },
|
|
64
|
+
{ provider: "ollama", modelId: "llama3.3", group: "Ollama", contextWindow: 128_000 },
|
|
65
|
+
{ provider: "ollama", modelId: "qwen2.5", group: "Ollama", contextWindow: 128_000 },
|
|
66
|
+
];
|
|
67
|
+
const FALLBACK_CONTEXT_WINDOW = 128_000;
|
|
68
|
+
/** Lookup map keyed by modelId for exact matching. */
|
|
69
|
+
const catalogByModelId = new Map(MODEL_CATALOG.map((m) => [m.modelId, m]));
|
|
70
|
+
/**
|
|
71
|
+
* Resolve a ModelDef for a given model ID.
|
|
72
|
+
* Lookup order: exact match > longest-prefix match > fallback.
|
|
73
|
+
* `contextWindowOverride` only applies when the model is NOT in the catalog —
|
|
74
|
+
* catalog values are always authoritative.
|
|
75
|
+
*/
|
|
76
|
+
export function resolveModelDef(modelId, contextWindowOverride = 0) {
|
|
77
|
+
// Exact match — catalog is authoritative, ignore override
|
|
78
|
+
const exact = catalogByModelId.get(modelId);
|
|
79
|
+
if (exact)
|
|
80
|
+
return exact;
|
|
81
|
+
// Longest-prefix match — also authoritative
|
|
82
|
+
const prefix = findPrefixMatch(modelId);
|
|
83
|
+
if (prefix)
|
|
84
|
+
return prefix;
|
|
85
|
+
// Unknown model — apply user override if provided, else fallback
|
|
86
|
+
const ctxWindow = contextWindowOverride > 0 ? contextWindowOverride : FALLBACK_CONTEXT_WINDOW;
|
|
87
|
+
return { provider: "unknown", modelId, group: "Other", contextWindow: ctxWindow };
|
|
88
|
+
}
|
|
89
|
+
function findPrefixMatch(modelId) {
|
|
90
|
+
let best;
|
|
91
|
+
let bestLen = 0;
|
|
92
|
+
for (const entry of MODEL_CATALOG) {
|
|
93
|
+
if (modelId.startsWith(entry.modelId) && entry.modelId.length > bestLen) {
|
|
94
|
+
best = entry;
|
|
95
|
+
bestLen = entry.modelId.length;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return best;
|
|
99
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export type ConfigData = Record<string, unknown>;
|
|
2
|
+
/**
|
|
3
|
+
* Manages a single `config.json` file on disk.
|
|
4
|
+
* Handles reading, writing, patching, and redaction of secret values.
|
|
5
|
+
*/
|
|
6
|
+
export declare class ConfigStore {
|
|
7
|
+
private filePath;
|
|
8
|
+
constructor(baseDir: string);
|
|
9
|
+
/** Read config from disk. Returns empty object if file doesn't exist. */
|
|
10
|
+
load(): ConfigData;
|
|
11
|
+
/** Write full config to disk atomically (temp file → rename). */
|
|
12
|
+
save(data: ConfigData): void;
|
|
13
|
+
/** Deep-merge a partial update into the existing config and save. */
|
|
14
|
+
patch(partial: ConfigData): ConfigData;
|
|
15
|
+
/** Return config with secret values redacted. */
|
|
16
|
+
getRedacted(): ConfigData;
|
|
17
|
+
/**
|
|
18
|
+
* Given an incoming config (possibly with redacted values from the UI),
|
|
19
|
+
* restore redacted secrets from the existing disk values before saving.
|
|
20
|
+
*/
|
|
21
|
+
patchFromUI(incoming: ConfigData): ConfigData;
|
|
22
|
+
/** Return file mtime in ms, or null if file doesn't exist. */
|
|
23
|
+
mtime(): number | null;
|
|
24
|
+
getFilePath(): string;
|
|
25
|
+
}
|