assistme 0.3.0 → 0.3.2
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/PLAN.md +14 -3
- package/dist/{chunk-UWE5WVQI.js → chunk-KX7ITO55.js} +20 -11
- package/dist/index.js +1791 -572
- package/dist/{job-runner-N4XAAWLJ.js → job-runner-P2L6MOOX.js} +1 -1
- package/package.json +5 -3
- package/src/agent/job-runner.ts +9 -13
- package/src/agent/mcp-servers.ts +6 -1020
- package/src/agent/memory.ts +2 -11
- package/src/agent/processor.ts +18 -108
- package/src/agent/scheduler.ts +2 -3
- package/src/agent/session.ts +20 -36
- package/src/agent/skills.ts +167 -61
- package/src/agent/system-prompt.ts +126 -0
- package/src/browser/chrome-launcher.ts +555 -0
- package/src/browser/controller.ts +1386 -0
- package/src/browser/types.ts +70 -0
- package/src/commands/credential.ts +190 -0
- package/src/commands/job.ts +14 -45
- package/src/commands/memory.ts +16 -29
- package/src/commands/schedule.ts +15 -37
- package/src/commands/start.ts +11 -43
- package/src/credentials/credential-store.test.ts +162 -0
- package/src/credentials/credential-store.ts +266 -0
- package/src/credentials/encryption.test.ts +98 -0
- package/src/credentials/encryption.ts +82 -0
- package/src/credentials/index.ts +15 -0
- package/src/credentials/local-store.ts +89 -0
- package/src/db/action.ts +19 -0
- package/src/db/api-client.ts +3 -32
- package/src/db/auth-store.ts +41 -0
- package/src/db/auth.ts +38 -0
- package/src/db/conversation.ts +39 -0
- package/src/db/event.ts +52 -0
- package/src/db/job-poll.ts +18 -0
- package/src/db/session.ts +60 -0
- package/src/db/supabase.ts +40 -383
- package/src/db/task.ts +69 -0
- package/src/db/types.ts +54 -0
- package/src/index.ts +2 -0
- package/src/mcp/agent-tools-server.ts +1047 -0
- package/src/mcp/browser-server.ts +258 -0
- package/src/tools/browser.ts +28 -1208
- package/src/tools/index.ts +32 -263
- package/src/tools/web.ts +0 -73
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createSdkMcpServer,
|
|
3
|
+
tool,
|
|
4
|
+
type McpSdkServerConfigWithInstance,
|
|
5
|
+
} from "@anthropic-ai/claude-agent-sdk";
|
|
6
|
+
import { z } from "zod/v4";
|
|
7
|
+
import { executeTool } from "../tools/index.js";
|
|
8
|
+
import { getLimiterForTool } from "../utils/rate-limiter.js";
|
|
9
|
+
|
|
10
|
+
// ── Helper ──────────────────────────────────────────────────────────
|
|
11
|
+
|
|
12
|
+
/** Wrap executeTool with rate limiting and text result. */
|
|
13
|
+
async function callTool(
|
|
14
|
+
name: string,
|
|
15
|
+
input: Record<string, unknown>
|
|
16
|
+
): Promise<{ content: Array<{ type: "text"; text: string }> }> {
|
|
17
|
+
const limiter = getLimiterForTool(name);
|
|
18
|
+
if (limiter) await limiter.acquire();
|
|
19
|
+
const result = await executeTool(name, input);
|
|
20
|
+
return { content: [{ type: "text", text: result }] };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// ── Browser MCP Server ──────────────────────────────────────────────
|
|
24
|
+
|
|
25
|
+
export const BROWSER_TOOL_NAMES = [
|
|
26
|
+
"browser_connect",
|
|
27
|
+
"browser_navigate",
|
|
28
|
+
"browser_read_page",
|
|
29
|
+
"browser_screenshot",
|
|
30
|
+
"browser_click",
|
|
31
|
+
"browser_type",
|
|
32
|
+
"browser_press_key",
|
|
33
|
+
"browser_scroll",
|
|
34
|
+
"browser_get_elements",
|
|
35
|
+
"browser_select",
|
|
36
|
+
"browser_snapshot",
|
|
37
|
+
"browser_act",
|
|
38
|
+
"browser_evaluate",
|
|
39
|
+
"browser_list_tabs",
|
|
40
|
+
"browser_switch_tab",
|
|
41
|
+
"browser_new_tab",
|
|
42
|
+
"browser_request_user_action",
|
|
43
|
+
] as const;
|
|
44
|
+
|
|
45
|
+
export function createBrowserMcpServer(): McpSdkServerConfigWithInstance {
|
|
46
|
+
return createSdkMcpServer({
|
|
47
|
+
name: "assistme-browser",
|
|
48
|
+
version: "1.0.0",
|
|
49
|
+
tools: [
|
|
50
|
+
tool(
|
|
51
|
+
"browser_connect",
|
|
52
|
+
"Connect to the user's real Chrome browser via CDP. Chrome will be auto-launched if not already running.",
|
|
53
|
+
{ tab_index: z.number().optional().describe("Tab index (default: 0)") },
|
|
54
|
+
async (args) => callTool("browser_connect", args)
|
|
55
|
+
),
|
|
56
|
+
tool(
|
|
57
|
+
"browser_navigate",
|
|
58
|
+
"Navigate the user's browser to a URL, using the user's real browser with all their cookies and logins.",
|
|
59
|
+
{ url: z.string().describe("URL to navigate to") },
|
|
60
|
+
async (args) => callTool("browser_navigate", args)
|
|
61
|
+
),
|
|
62
|
+
tool(
|
|
63
|
+
"browser_read_page",
|
|
64
|
+
"Read the text content of the currently open page. Returns page title, URL, and main text content.",
|
|
65
|
+
{},
|
|
66
|
+
async () => callTool("browser_read_page", {})
|
|
67
|
+
),
|
|
68
|
+
tool(
|
|
69
|
+
"browser_screenshot",
|
|
70
|
+
"Take a screenshot of the current browser page. Returns a base64-encoded PNG image.",
|
|
71
|
+
{},
|
|
72
|
+
async () => {
|
|
73
|
+
const limiter = getLimiterForTool("browser_screenshot");
|
|
74
|
+
if (limiter) await limiter.acquire();
|
|
75
|
+
const base64 = await executeTool("browser_screenshot", {});
|
|
76
|
+
if (base64.length > 100) {
|
|
77
|
+
return {
|
|
78
|
+
content: [
|
|
79
|
+
{
|
|
80
|
+
type: "image" as const,
|
|
81
|
+
data: base64,
|
|
82
|
+
mimeType: "image/png",
|
|
83
|
+
} as unknown as { type: "text"; text: string },
|
|
84
|
+
],
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
return { content: [{ type: "text", text: base64 }] };
|
|
88
|
+
}
|
|
89
|
+
),
|
|
90
|
+
tool(
|
|
91
|
+
"browser_click",
|
|
92
|
+
"Click on an element in the user's browser using a CSS selector.",
|
|
93
|
+
{ selector: z.string().describe("CSS selector of the element to click") },
|
|
94
|
+
async (args) => callTool("browser_click", args)
|
|
95
|
+
),
|
|
96
|
+
tool(
|
|
97
|
+
"browser_type",
|
|
98
|
+
"Type text into an input field in the user's browser.",
|
|
99
|
+
{
|
|
100
|
+
selector: z.string().describe("CSS selector of the input element"),
|
|
101
|
+
text: z.string().describe("Text to type"),
|
|
102
|
+
},
|
|
103
|
+
async (args) => callTool("browser_type", args)
|
|
104
|
+
),
|
|
105
|
+
tool(
|
|
106
|
+
"browser_press_key",
|
|
107
|
+
"Press a keyboard key in the browser. Supports: Enter, Tab, Escape, Backspace, ArrowDown, ArrowUp.",
|
|
108
|
+
{ key: z.string().describe("Key to press") },
|
|
109
|
+
async (args) => callTool("browser_press_key", args)
|
|
110
|
+
),
|
|
111
|
+
tool(
|
|
112
|
+
"browser_scroll",
|
|
113
|
+
"Scroll the page up or down.",
|
|
114
|
+
{ direction: z.string().describe("'down' or 'up'") },
|
|
115
|
+
async (args) => callTool("browser_scroll", args)
|
|
116
|
+
),
|
|
117
|
+
tool(
|
|
118
|
+
"browser_get_elements",
|
|
119
|
+
"Find all interactive elements (links, buttons, inputs) on the current page.",
|
|
120
|
+
{},
|
|
121
|
+
async () => callTool("browser_get_elements", {})
|
|
122
|
+
),
|
|
123
|
+
tool(
|
|
124
|
+
"browser_select",
|
|
125
|
+
"Select an option from a dropdown menu. Handles both native <select> elements and custom dropdowns (Material Design, React, Angular). Use this instead of manually clicking dropdown items.",
|
|
126
|
+
{
|
|
127
|
+
selector: z
|
|
128
|
+
.string()
|
|
129
|
+
.describe(
|
|
130
|
+
"CSS selector of the dropdown, or its label/placeholder text (e.g. 'Month', 'Gender', '#country')"
|
|
131
|
+
),
|
|
132
|
+
option: z
|
|
133
|
+
.string()
|
|
134
|
+
.describe("Visible text of the option to select (e.g. 'March', 'Male')"),
|
|
135
|
+
},
|
|
136
|
+
async (args) => callTool("browser_select", args)
|
|
137
|
+
),
|
|
138
|
+
tool(
|
|
139
|
+
"browser_snapshot",
|
|
140
|
+
"Take a screenshot and discover all interactive elements with numbered refs. " +
|
|
141
|
+
"Returns a screenshot + a compact ref table. PREFERRED way to understand a page. " +
|
|
142
|
+
"Set annotate=true to overlay red ref badges (useful for simple pages). " +
|
|
143
|
+
"Use the ref numbers with browser_act to interact with elements.",
|
|
144
|
+
{
|
|
145
|
+
annotate: z
|
|
146
|
+
.boolean()
|
|
147
|
+
.optional()
|
|
148
|
+
.describe(
|
|
149
|
+
"Overlay ref badges on the screenshot. Default false. Use true for simple pages where visual context helps."
|
|
150
|
+
),
|
|
151
|
+
},
|
|
152
|
+
async (args) => {
|
|
153
|
+
const limiter = getLimiterForTool("browser_snapshot");
|
|
154
|
+
if (limiter) await limiter.acquire();
|
|
155
|
+
const result = await executeTool("browser_snapshot", args);
|
|
156
|
+
|
|
157
|
+
// Parse the composite result: ref table text + image data
|
|
158
|
+
const parts = result.split("\n__SNAPSHOT_IMAGE__:");
|
|
159
|
+
const refTable = parts[0];
|
|
160
|
+
const imageData = parts[1] || "";
|
|
161
|
+
|
|
162
|
+
const content: Array<{ type: "text"; text: string }> = [];
|
|
163
|
+
if (imageData.length > 100) {
|
|
164
|
+
content.push({
|
|
165
|
+
type: "image" as const,
|
|
166
|
+
data: imageData,
|
|
167
|
+
mimeType: "image/png",
|
|
168
|
+
} as unknown as { type: "text"; text: string });
|
|
169
|
+
}
|
|
170
|
+
content.push({ type: "text", text: refTable });
|
|
171
|
+
|
|
172
|
+
return { content };
|
|
173
|
+
}
|
|
174
|
+
),
|
|
175
|
+
tool(
|
|
176
|
+
"browser_act",
|
|
177
|
+
"Execute actions using ref numbers from browser_snapshot. Supports: click, type, select, press, scroll, wait. " +
|
|
178
|
+
"Batch multiple actions in one call to reduce round-trips. Set screenshot=true to see the result.",
|
|
179
|
+
{
|
|
180
|
+
actions: z
|
|
181
|
+
.array(
|
|
182
|
+
z.object({
|
|
183
|
+
action: z
|
|
184
|
+
.enum(["click", "type", "select", "press", "scroll", "wait"])
|
|
185
|
+
.describe("Action type"),
|
|
186
|
+
ref: z.number().optional().describe("Ref number from browser_snapshot"),
|
|
187
|
+
text: z.string().optional().describe("Text to type (for 'type' action)"),
|
|
188
|
+
option: z.string().optional().describe("Option to select (for 'select' action)"),
|
|
189
|
+
key: z.string().optional().describe("Key to press (for 'press' action)"),
|
|
190
|
+
direction: z.string().optional().describe("'up' or 'down' (for 'scroll')"),
|
|
191
|
+
ms: z.number().optional().describe("Wait duration in ms (for 'wait', max 5000)"),
|
|
192
|
+
})
|
|
193
|
+
)
|
|
194
|
+
.describe("Actions to execute sequentially"),
|
|
195
|
+
screenshot: z
|
|
196
|
+
.boolean()
|
|
197
|
+
.optional()
|
|
198
|
+
.describe("Take screenshot after actions (default: false)"),
|
|
199
|
+
},
|
|
200
|
+
async (args) => {
|
|
201
|
+
const limiter = getLimiterForTool("browser_act");
|
|
202
|
+
if (limiter) await limiter.acquire();
|
|
203
|
+
const result = await executeTool("browser_act", {
|
|
204
|
+
actions: args.actions,
|
|
205
|
+
screenshot: args.screenshot,
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// Parse composite result: action results + optional screenshot
|
|
209
|
+
const parts = result.split("\n__ACT_SCREENSHOT__:");
|
|
210
|
+
const actionText = parts[0];
|
|
211
|
+
const screenshotData = parts[1] || "";
|
|
212
|
+
|
|
213
|
+
const content: Array<{ type: "text"; text: string }> = [];
|
|
214
|
+
content.push({ type: "text", text: actionText });
|
|
215
|
+
if (screenshotData.length > 100) {
|
|
216
|
+
content.push({
|
|
217
|
+
type: "image" as const,
|
|
218
|
+
data: screenshotData,
|
|
219
|
+
mimeType: "image/png",
|
|
220
|
+
} as unknown as { type: "text"; text: string });
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return { content };
|
|
224
|
+
}
|
|
225
|
+
),
|
|
226
|
+
tool(
|
|
227
|
+
"browser_evaluate",
|
|
228
|
+
"Execute JavaScript in the browser page context. Use as a last resort when browser_snapshot + browser_act cannot handle the interaction.",
|
|
229
|
+
{ expression: z.string().describe("JavaScript expression to evaluate") },
|
|
230
|
+
async (args) => callTool("browser_evaluate", args)
|
|
231
|
+
),
|
|
232
|
+
tool("browser_list_tabs", "List all open tabs in the user's browser.", {}, async () =>
|
|
233
|
+
callTool("browser_list_tabs", {})
|
|
234
|
+
),
|
|
235
|
+
tool(
|
|
236
|
+
"browser_switch_tab",
|
|
237
|
+
"Switch to a different browser tab by index.",
|
|
238
|
+
{ index: z.number().describe("Tab index") },
|
|
239
|
+
async (args) => callTool("browser_switch_tab", args)
|
|
240
|
+
),
|
|
241
|
+
tool(
|
|
242
|
+
"browser_new_tab",
|
|
243
|
+
"Open a new tab in the user's browser, optionally navigating to a URL.",
|
|
244
|
+
{ url: z.string().optional().describe("URL to open (default: blank)") },
|
|
245
|
+
async (args) => callTool("browser_new_tab", args)
|
|
246
|
+
),
|
|
247
|
+
tool(
|
|
248
|
+
"browser_request_user_action",
|
|
249
|
+
"Request the user to perform an action in their browser (login, CAPTCHA, 2FA, etc.).",
|
|
250
|
+
{
|
|
251
|
+
message: z.string().describe("Clear description of what the user needs to do"),
|
|
252
|
+
wait_seconds: z.number().optional().describe("How long to wait (default: 60)"),
|
|
253
|
+
},
|
|
254
|
+
async (args) => callTool("browser_request_user_action", args)
|
|
255
|
+
),
|
|
256
|
+
],
|
|
257
|
+
});
|
|
258
|
+
}
|