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.
Files changed (44) hide show
  1. package/PLAN.md +14 -3
  2. package/dist/{chunk-UWE5WVQI.js → chunk-KX7ITO55.js} +20 -11
  3. package/dist/index.js +1791 -572
  4. package/dist/{job-runner-N4XAAWLJ.js → job-runner-P2L6MOOX.js} +1 -1
  5. package/package.json +5 -3
  6. package/src/agent/job-runner.ts +9 -13
  7. package/src/agent/mcp-servers.ts +6 -1020
  8. package/src/agent/memory.ts +2 -11
  9. package/src/agent/processor.ts +18 -108
  10. package/src/agent/scheduler.ts +2 -3
  11. package/src/agent/session.ts +20 -36
  12. package/src/agent/skills.ts +167 -61
  13. package/src/agent/system-prompt.ts +126 -0
  14. package/src/browser/chrome-launcher.ts +555 -0
  15. package/src/browser/controller.ts +1386 -0
  16. package/src/browser/types.ts +70 -0
  17. package/src/commands/credential.ts +190 -0
  18. package/src/commands/job.ts +14 -45
  19. package/src/commands/memory.ts +16 -29
  20. package/src/commands/schedule.ts +15 -37
  21. package/src/commands/start.ts +11 -43
  22. package/src/credentials/credential-store.test.ts +162 -0
  23. package/src/credentials/credential-store.ts +266 -0
  24. package/src/credentials/encryption.test.ts +98 -0
  25. package/src/credentials/encryption.ts +82 -0
  26. package/src/credentials/index.ts +15 -0
  27. package/src/credentials/local-store.ts +89 -0
  28. package/src/db/action.ts +19 -0
  29. package/src/db/api-client.ts +3 -32
  30. package/src/db/auth-store.ts +41 -0
  31. package/src/db/auth.ts +38 -0
  32. package/src/db/conversation.ts +39 -0
  33. package/src/db/event.ts +52 -0
  34. package/src/db/job-poll.ts +18 -0
  35. package/src/db/session.ts +60 -0
  36. package/src/db/supabase.ts +40 -383
  37. package/src/db/task.ts +69 -0
  38. package/src/db/types.ts +54 -0
  39. package/src/index.ts +2 -0
  40. package/src/mcp/agent-tools-server.ts +1047 -0
  41. package/src/mcp/browser-server.ts +258 -0
  42. package/src/tools/browser.ts +28 -1208
  43. package/src/tools/index.ts +32 -263
  44. package/src/tools/web.ts +0 -73
@@ -1,386 +1,43 @@
1
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
2
- import { join } from "path";
3
- import { homedir } from "os";
4
- import { callMcpHandler } from "./api-client.js";
5
- import { log } from "../utils/logger.js";
6
-
7
- // ── Auth Store (persists am_ token to disk) ──────────────────────────
8
-
9
- const AUTH_DIR = join(homedir(), ".config", "assistme");
10
- const AUTH_FILE = join(AUTH_DIR, "auth.json");
11
-
12
- function ensureAuthDir() {
13
- if (!existsSync(AUTH_DIR)) {
14
- mkdirSync(AUTH_DIR, { recursive: true, mode: 0o700 });
15
- }
16
- }
17
-
18
- function readAuthStore(): Record<string, string> {
19
- try {
20
- if (existsSync(AUTH_FILE)) {
21
- return JSON.parse(readFileSync(AUTH_FILE, "utf-8"));
22
- }
23
- } catch {
24
- // Corrupted file — start fresh
25
- }
26
- return {};
27
- }
28
-
29
- function writeAuthStore(data: Record<string, string>) {
30
- ensureAuthDir();
31
- writeFileSync(AUTH_FILE, JSON.stringify(data, null, 2), { mode: 0o600 });
32
- }
33
-
34
- // ── Auth ─────────────────────────────────────────────────────────────
35
-
36
- /**
37
- * Login using an am_ MCP token.
38
- * Validates against DB via edge function, stores locally.
39
- */
40
- export async function loginWithToken(mcpToken: string): Promise<string> {
41
- if (!mcpToken.startsWith("am_")) {
42
- throw new Error(
43
- "Invalid token format. Use an am_ token from the web page."
44
- );
45
- }
46
-
47
- const result = await callMcpHandler<{ user_id: string; email: string | null }>(
48
- "auth.validate_token",
49
- {},
50
- mcpToken,
51
- );
52
-
53
- // Persist token
54
- const store = readAuthStore();
55
- store["mcp_token"] = mcpToken;
56
- writeAuthStore(store);
57
-
58
- return result.user_id;
59
- }
60
-
61
- export async function getCurrentUserId(): Promise<string> {
62
- const result = await callMcpHandler<{ user_id: string }>(
63
- "auth.validate_token",
64
- );
65
- return result.user_id;
66
- }
67
-
68
- export async function logout(): Promise<void> {
69
- try {
70
- writeAuthStore({});
71
- } catch {
72
- // ignore
73
- }
74
- }
75
-
76
- // ── Session Management ─────────────────────────────────────────────
77
-
78
- export interface AgentSession {
79
- id: string;
80
- user_id: string;
81
- session_name: string;
82
- status: "online" | "offline" | "busy";
83
- workspace_path: string | null;
84
- agent_version: string | null;
85
- started_at: string;
86
- last_heartbeat_at: string;
87
- ended_at: string | null;
88
- metadata: Record<string, unknown>;
89
- }
90
-
91
- export async function createSession(
92
- _userId: string,
93
- sessionName: string,
94
- workspacePath: string,
95
- version: string
96
- ): Promise<AgentSession> {
97
- const { getConfig } = await import("../utils/config.js");
98
- const data = await callMcpHandler<AgentSession>("session.create", {
99
- session_name: sessionName,
100
- workspace_path: workspacePath,
101
- version,
102
- model: getConfig().model || null,
103
- });
104
- return data;
105
- }
106
-
107
- export async function updateHeartbeat(sessionId: string): Promise<void> {
108
- try {
109
- await callMcpHandler("session.heartbeat", { session_id: sessionId });
110
- } catch (err) {
111
- log.warn(`Heartbeat update failed: ${err instanceof Error ? err.message : err}`);
112
- }
113
- }
114
-
115
- export async function endSession(sessionId: string): Promise<void> {
116
- try {
117
- await callMcpHandler("session.end", { session_id: sessionId });
118
- } catch (err) {
119
- log.error(`Failed to end session: ${err instanceof Error ? err.message : err}`);
120
- }
121
- }
122
-
123
- export async function setSessionBusy(
124
- sessionId: string,
125
- busy: boolean
126
- ): Promise<void> {
127
- await callMcpHandler("session.set_busy", {
128
- session_id: sessionId,
129
- busy,
130
- });
131
- }
132
-
133
- export async function cleanupStaleSessions(
134
- currentSessionId: string,
135
- thresholdMs = 120_000
136
- ): Promise<number> {
137
- try {
138
- const result = await callMcpHandler<{ cleaned: number }>(
139
- "session.cleanup_stale",
140
- { current_session_id: currentSessionId, threshold_ms: thresholdMs },
141
- );
142
- return result.cleaned;
143
- } catch {
144
- return 0;
145
- }
146
- }
147
-
148
- export async function getActiveSessions(
149
- limit = 5
150
- ): Promise<AgentSession[]> {
151
- return callMcpHandler<AgentSession[]>("session.get_active", { limit });
152
- }
153
-
154
- // ── Conversation Management ─────────────────────────────────────────
155
-
156
- export async function getOrCreateCliConversation(
157
- _userId: string,
158
- _sessionId: string
159
- ): Promise<string> {
160
- const data = await callMcpHandler<string>("conversation.get_or_create");
161
- return data;
162
- }
163
-
164
- // ── Message / Task Management ────────────────────────────────────────
165
-
166
- export interface ConversationMessage {
167
- id: string;
168
- conversation_id: string;
169
- sender_id: string;
170
- role: "user" | "assistant" | "system";
171
- content: string;
172
- status: "pending" | "running" | "completed" | "failed" | "cancelled" | null;
173
- session_id: string | null;
174
- tool_calls: Record<string, unknown> | null;
175
- tool_results: Record<string, unknown> | null;
176
- metadata: Record<string, unknown>;
177
- created_at: string;
178
- updated_at: string;
179
- prompt: string;
180
- }
181
-
182
- export type AgentTask = ConversationMessage;
183
-
184
- export async function createTask(
185
- conversationId: string,
186
- _userId: string,
187
- sessionId: string,
188
- prompt: string
189
- ): Promise<ConversationMessage> {
190
- const data = await callMcpHandler<Record<string, unknown>>("task.create", {
191
- conversation_id: conversationId,
192
- session_id: sessionId,
193
- prompt,
194
- });
195
- return { ...data, prompt } as ConversationMessage;
196
- }
197
-
198
- /**
199
- * Atomically poll ONE pending task and claim it in a single call.
200
- * Uses FOR UPDATE SKIP LOCKED — concurrent CLIs will never grab the same task.
201
- * Returns null if no pending task exists.
202
- */
203
- export async function pollAndClaimTask(
204
- sessionId: string
205
- ): Promise<ConversationMessage | null> {
206
- try {
207
- const data = await callMcpHandler<Record<string, unknown> | null>(
208
- "task.poll_and_claim",
209
- { session_id: sessionId },
210
- );
211
-
212
- if (!data) return null;
213
-
214
- return {
215
- ...data,
216
- prompt:
217
- (data.metadata as Record<string, unknown>)?.prompt || data.content || "",
218
- } as ConversationMessage;
219
- } catch (err) {
220
- log.warn(`Poll-and-claim failed: ${err instanceof Error ? err.message : err}`);
221
- return null;
222
- }
223
- }
224
-
225
- export async function claimTask(messageId: string): Promise<boolean> {
226
- const data = await callMcpHandler<boolean>("task.claim", {
227
- message_id: messageId,
228
- });
229
- return data;
230
- }
231
-
232
- export async function completeTask(
233
- messageId: string,
234
- resultSummary: string,
235
- tokenUsage?: Record<string, number>
236
- ): Promise<void> {
237
- await callMcpHandler("task.complete", {
238
- message_id: messageId,
239
- result: resultSummary,
240
- token_usage: tokenUsage || null,
241
- });
242
- }
243
-
244
- export async function failTask(
245
- messageId: string,
246
- errorMessage: string
247
- ): Promise<void> {
248
- try {
249
- await callMcpHandler("task.fail", {
250
- message_id: messageId,
251
- error: errorMessage,
252
- });
253
- } catch (err) {
254
- log.error(`Failed to update task status: ${err instanceof Error ? err.message : err}`);
255
- }
256
- }
257
-
258
- // ── Job Run Polling ─────────────────────────────────────────────────
259
-
260
- export interface PendingJobRun {
261
- id: string;
262
- job_id: string;
263
- job_name: string;
264
- trigger_type: string;
265
- }
266
-
267
1
  /**
268
- * Atomically claim one pending job run.
269
- * Uses FOR UPDATE SKIP LOCKED concurrent CLIs will never grab the same run.
270
- * Returns null if no pending job run exists.
2
+ * Re-export barrel all consumers import from "db/supabase.js" today.
3
+ * This file preserves backward compatibility while the actual logic
4
+ * lives in focused modules under db/.
271
5
  */
272
- export async function pollAndClaimJobRun(
273
- _userId: string
274
- ): Promise<PendingJobRun | null> {
275
- try {
276
- const data = await callMcpHandler<PendingJobRun | null>(
277
- "job.claim_pending_run",
278
- );
279
- return data;
280
- } catch (err) {
281
- log.debug(`Job run poll failed: ${err instanceof Error ? err.message : err}`);
282
- return null;
283
- }
284
- }
285
-
286
- // ── Conversation History ─────────────────────────────────────────────
287
-
288
- export interface HistoryEntry {
289
- prompt: string;
290
- response: string;
291
- }
292
-
293
- /**
294
- * Fetch completed messages from a conversation to build history context.
295
- * Returns messages in chronological order (oldest first).
296
- */
297
- export async function getConversationHistory(
298
- conversationId: string,
299
- excludeMessageId: string,
300
- limit: number = 20
301
- ): Promise<HistoryEntry[]> {
302
- try {
303
- const rows = await callMcpHandler<Array<Record<string, unknown>>>(
304
- "conversation.get_history",
305
- {
306
- conversation_id: conversationId,
307
- exclude_message_id: excludeMessageId,
308
- limit,
309
- },
310
- );
311
-
312
- return (rows || [])
313
- .reverse() // chronological order (oldest first)
314
- .map((row) => {
315
- const prompt =
316
- ((row.metadata as Record<string, unknown>)?.prompt as string) || "";
317
- const content = (row.content as string) || "";
318
- const response =
319
- row.status === "failed"
320
- ? `[Task failed] ${content}`
321
- : content;
322
- return { prompt, response };
323
- })
324
- .filter((entry) => entry.prompt && entry.response);
325
- } catch (err) {
326
- log.debug(`Failed to fetch conversation history: ${err instanceof Error ? err.message : err}`);
327
- return [];
328
- }
329
- }
330
-
331
- // ── Event Streaming ─────────────────────────────────────────────────
332
-
333
- export type EventType =
334
- | "text_delta"
335
- | "tool_use_start"
336
- | "tool_use_input"
337
- | "tool_result"
338
- | "thinking"
339
- | "error"
340
- | "status_change"
341
- | "user_action_request";
342
-
343
- let eventSequence = 0;
344
-
345
- export function resetEventSequence() {
346
- eventSequence = 0;
347
- }
348
-
349
- export async function emitEvent(
350
- messageId: string,
351
- eventType: EventType,
352
- eventData: Record<string, unknown>
353
- ): Promise<void> {
354
- eventSequence++;
355
- try {
356
- await callMcpHandler("event.emit", {
357
- message_id: messageId,
358
- event_type: eventType,
359
- event_data: eventData,
360
- seq: eventSequence,
361
- });
362
- } catch (err) {
363
- log.warn(`Failed to emit event: ${err instanceof Error ? err.message : err}`);
364
- }
365
- }
366
-
367
- // ── Action Request Helpers ──────────────────────────────────────────
368
-
369
- export async function setActionRequest(
370
- messageId: string,
371
- actionData: Record<string, unknown>
372
- ): Promise<void> {
373
- await callMcpHandler("action.set_request", {
374
- message_id: messageId,
375
- action_data: actionData,
376
- });
377
- }
378
6
 
379
- export async function pollActionResponse(
380
- messageId: string
381
- ): Promise<Record<string, unknown> | null> {
382
- return callMcpHandler<Record<string, unknown> | null>(
383
- "action.poll_response",
384
- { message_id: messageId },
385
- );
386
- }
7
+ // Types
8
+ export type {
9
+ AgentSession,
10
+ ConversationMessage,
11
+ AgentTask,
12
+ PendingJobRun,
13
+ HistoryEntry,
14
+ EventType,
15
+ } from "./types.js";
16
+
17
+ // Auth
18
+ export { loginWithToken, getCurrentUserId, logout } from "./auth.js";
19
+
20
+ // Session
21
+ export {
22
+ createSession,
23
+ updateHeartbeat,
24
+ endSession,
25
+ setSessionBusy,
26
+ cleanupStaleSessions,
27
+ getActiveSessions,
28
+ } from "./session.js";
29
+
30
+ // Task
31
+ export { createTask, pollAndClaimTask, claimTask, completeTask, failTask } from "./task.js";
32
+
33
+ // Conversation
34
+ export { getOrCreateCliConversation, getConversationHistory } from "./conversation.js";
35
+
36
+ // Event
37
+ export { TaskEventEmitter, resetEventSequence, emitEvent } from "./event.js";
38
+
39
+ // Action
40
+ export { setActionRequest, pollActionResponse } from "./action.js";
41
+
42
+ // Job polling
43
+ export { pollAndClaimJobRun } from "./job-poll.js";
package/src/db/task.ts ADDED
@@ -0,0 +1,69 @@
1
+ import { callMcpHandler } from "./api-client.js";
2
+ import { log } from "../utils/logger.js";
3
+ import type { ConversationMessage } from "./types.js";
4
+
5
+ export async function createTask(
6
+ conversationId: string,
7
+ sessionId: string,
8
+ prompt: string
9
+ ): Promise<ConversationMessage> {
10
+ const data = await callMcpHandler<Record<string, unknown>>("task.create", {
11
+ conversation_id: conversationId,
12
+ session_id: sessionId,
13
+ prompt,
14
+ });
15
+ return { ...data, prompt } as ConversationMessage;
16
+ }
17
+
18
+ /**
19
+ * Atomically poll ONE pending task and claim it in a single call.
20
+ * Uses FOR UPDATE SKIP LOCKED — concurrent CLIs will never grab the same task.
21
+ * Returns null if no pending task exists.
22
+ */
23
+ export async function pollAndClaimTask(sessionId: string): Promise<ConversationMessage | null> {
24
+ try {
25
+ const data = await callMcpHandler<Record<string, unknown> | null>("task.poll_and_claim", {
26
+ session_id: sessionId,
27
+ });
28
+
29
+ if (!data) return null;
30
+
31
+ return {
32
+ ...data,
33
+ prompt: (data.metadata as Record<string, unknown>)?.prompt || data.content || "",
34
+ } as ConversationMessage;
35
+ } catch (err) {
36
+ log.warn(`Poll-and-claim failed: ${err instanceof Error ? err.message : err}`);
37
+ return null;
38
+ }
39
+ }
40
+
41
+ export async function claimTask(messageId: string): Promise<boolean> {
42
+ const data = await callMcpHandler<boolean>("task.claim", {
43
+ message_id: messageId,
44
+ });
45
+ return data;
46
+ }
47
+
48
+ export async function completeTask(
49
+ messageId: string,
50
+ resultSummary: string,
51
+ tokenUsage?: Record<string, number>
52
+ ): Promise<void> {
53
+ await callMcpHandler("task.complete", {
54
+ message_id: messageId,
55
+ result: resultSummary,
56
+ token_usage: tokenUsage || null,
57
+ });
58
+ }
59
+
60
+ export async function failTask(messageId: string, errorMessage: string): Promise<void> {
61
+ try {
62
+ await callMcpHandler("task.fail", {
63
+ message_id: messageId,
64
+ error: errorMessage,
65
+ });
66
+ } catch (err) {
67
+ log.error(`Failed to update task status: ${err instanceof Error ? err.message : err}`);
68
+ }
69
+ }
@@ -0,0 +1,54 @@
1
+ // ── Shared Types ─────────────────────────────────────────────────────
2
+
3
+ export interface AgentSession {
4
+ id: string;
5
+ user_id: string;
6
+ session_name: string;
7
+ status: "online" | "offline" | "busy";
8
+ workspace_path: string | null;
9
+ agent_version: string | null;
10
+ started_at: string;
11
+ last_heartbeat_at: string;
12
+ ended_at: string | null;
13
+ metadata: Record<string, unknown>;
14
+ }
15
+
16
+ export interface ConversationMessage {
17
+ id: string;
18
+ conversation_id: string;
19
+ sender_id: string;
20
+ role: "user" | "assistant" | "system";
21
+ content: string;
22
+ status: "pending" | "running" | "completed" | "failed" | "cancelled" | null;
23
+ session_id: string | null;
24
+ tool_calls: Record<string, unknown> | null;
25
+ tool_results: Record<string, unknown> | null;
26
+ metadata: Record<string, unknown>;
27
+ created_at: string;
28
+ updated_at: string;
29
+ prompt: string;
30
+ }
31
+
32
+ export type AgentTask = ConversationMessage;
33
+
34
+ export interface PendingJobRun {
35
+ id: string;
36
+ job_id: string;
37
+ job_name: string;
38
+ trigger_type: string;
39
+ }
40
+
41
+ export interface HistoryEntry {
42
+ prompt: string;
43
+ response: string;
44
+ }
45
+
46
+ export type EventType =
47
+ | "text_delta"
48
+ | "tool_use_start"
49
+ | "tool_use_input"
50
+ | "tool_result"
51
+ | "thinking"
52
+ | "error"
53
+ | "status_change"
54
+ | "user_action_request";
package/src/index.ts CHANGED
@@ -12,6 +12,7 @@ import { registerScheduleCommands } from "./commands/schedule.js";
12
12
  import { registerMemoryCommands } from "./commands/memory.js";
13
13
  import { registerSkillCommands } from "./commands/skill.js";
14
14
  import { registerJobCommands } from "./commands/job.js";
15
+ import { registerCredentialCommands } from "./commands/credential.js";
15
16
 
16
17
  loadEnv();
17
18
 
@@ -35,5 +36,6 @@ registerScheduleCommands(program); // schedule add/list/toggle/remove
35
36
  registerMemoryCommands(program); // memory list/add/search/remove/clear
36
37
  registerSkillCommands(program); // skill list/search/create/show/remove
37
38
  registerJobCommands(program); // job list/status/schedule
39
+ registerCredentialCommands(program); // credential list/set/get/remove/clear
38
40
 
39
41
  program.parse();