assistme 0.2.7 → 0.2.9

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.
@@ -1,580 +0,0 @@
1
- // src/db/supabase.ts
2
- import { createClient } from "@supabase/supabase-js";
3
- import { createHash } from "crypto";
4
-
5
- // src/utils/config.ts
6
- import Conf from "conf";
7
- import { resolve } from "path";
8
- var SUPABASE_URL_DEFAULT = "https://msgplwbgohpokajtibew.supabase.co";
9
- var SUPABASE_ANON_KEY_DEFAULT = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im1zZ3Bsd2Jnb2hwb2thanRpYmV3Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjE3MTMzNTEsImV4cCI6MjA3NzI4OTM1MX0.YqiluL_mIBWKv5dteIcPAPQ_jRp9rzZlvAXvkQttDjs";
10
- var CONFIG_DEFAULTS = {
11
- supabaseUrl: SUPABASE_URL_DEFAULT,
12
- supabaseAnonKey: SUPABASE_ANON_KEY_DEFAULT,
13
- sessionName: "Default",
14
- model: "claude-sonnet-4-20250514",
15
- maxTurns: 50
16
- };
17
- var config = new Conf({
18
- projectName: "assistme",
19
- defaults: CONFIG_DEFAULTS
20
- });
21
- function getConfig() {
22
- const supabaseUrl = process.env.SUPABASE_URL || config.get("supabaseUrl") || SUPABASE_URL_DEFAULT;
23
- const supabaseAnonKey = process.env.SUPABASE_ANON_KEY || config.get("supabaseAnonKey") || SUPABASE_ANON_KEY_DEFAULT;
24
- const anthropicApiKey = process.env.ANTHROPIC_API_KEY || config.get("anthropicApiKey") || "";
25
- const workspacePath = config.get("workspacePath") || process.cwd();
26
- return {
27
- supabaseUrl,
28
- supabaseAnonKey,
29
- anthropicApiKey,
30
- workspacePath: resolve(workspacePath),
31
- sessionName: config.get("sessionName") || "Default",
32
- model: config.get("model") || "claude-sonnet-4-20250514",
33
- maxTurns: config.get("maxTurns") || 50
34
- };
35
- }
36
- function setConfig(key, value) {
37
- config.set(key, value);
38
- }
39
- function clearConfig() {
40
- config.clear();
41
- }
42
- function getConfigPath() {
43
- return config.path;
44
- }
45
-
46
- // src/utils/logger.ts
47
- import chalk from "chalk";
48
- import { randomUUID } from "crypto";
49
- var currentLevel = "info";
50
- var currentCorrelationId = null;
51
- var LEVEL_ORDER = {
52
- debug: 0,
53
- info: 1,
54
- warn: 2,
55
- error: 3
56
- };
57
- function setLogLevel(level) {
58
- currentLevel = level;
59
- }
60
- function setCorrelationId(id) {
61
- currentCorrelationId = id;
62
- }
63
- function newCorrelationId() {
64
- const id = randomUUID().slice(0, 8);
65
- currentCorrelationId = id;
66
- return id;
67
- }
68
- function shouldLog(level) {
69
- return LEVEL_ORDER[level] >= LEVEL_ORDER[currentLevel];
70
- }
71
- function timestamp() {
72
- return (/* @__PURE__ */ new Date()).toISOString().slice(11, 23);
73
- }
74
- function prefix() {
75
- const ts = timestamp();
76
- return currentCorrelationId ? `${ts} ${currentCorrelationId}` : ts;
77
- }
78
- var log = {
79
- debug(msg, ...args) {
80
- if (shouldLog("debug")) {
81
- console.log(chalk.gray(`[${prefix()}] DEBUG`), msg, ...args);
82
- }
83
- },
84
- info(msg, ...args) {
85
- if (shouldLog("info")) {
86
- console.log(chalk.blue(`[${prefix()}]`), msg, ...args);
87
- }
88
- },
89
- success(msg, ...args) {
90
- if (shouldLog("info")) {
91
- console.log(chalk.green(`[${prefix()}] \u2713`), msg, ...args);
92
- }
93
- },
94
- warn(msg, ...args) {
95
- if (shouldLog("warn")) {
96
- console.log(chalk.yellow(`[${prefix()}] WARN`), msg, ...args);
97
- }
98
- },
99
- error(msg, ...args) {
100
- if (shouldLog("error")) {
101
- console.error(chalk.red(`[${prefix()}] ERROR`), msg, ...args);
102
- }
103
- },
104
- agent(msg) {
105
- console.log(chalk.cyan(" \u25B8"), msg);
106
- },
107
- tool(name, msg) {
108
- console.log(chalk.magenta(` \u26A1 ${name}:`), msg);
109
- },
110
- result(msg) {
111
- console.log(chalk.green(" \u2190"), msg);
112
- }
113
- };
114
-
115
- // src/db/supabase.ts
116
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
117
- import { join } from "path";
118
- import { homedir } from "os";
119
- var AUTH_DIR = join(homedir(), ".config", "assistme");
120
- var AUTH_FILE = join(AUTH_DIR, "auth.json");
121
- function ensureAuthDir() {
122
- if (!existsSync(AUTH_DIR)) {
123
- mkdirSync(AUTH_DIR, { recursive: true, mode: 448 });
124
- }
125
- }
126
- function readAuthStore() {
127
- try {
128
- if (existsSync(AUTH_FILE)) {
129
- return JSON.parse(readFileSync(AUTH_FILE, "utf-8"));
130
- }
131
- } catch {
132
- }
133
- return {};
134
- }
135
- function writeAuthStore(data) {
136
- ensureAuthDir();
137
- writeFileSync(AUTH_FILE, JSON.stringify(data, null, 2), { mode: 384 });
138
- }
139
- var supabase = null;
140
- function getSupabase() {
141
- if (!supabase) {
142
- const config2 = getConfig();
143
- if (!config2.supabaseUrl || !config2.supabaseAnonKey) {
144
- throw new Error(
145
- "Supabase not configured. Run `assistme config set supabaseUrl <url>` first."
146
- );
147
- }
148
- supabase = createClient(config2.supabaseUrl, config2.supabaseAnonKey, {
149
- auth: { persistSession: false }
150
- });
151
- }
152
- return supabase;
153
- }
154
- function hashToken(token) {
155
- return createHash("sha256").update(token).digest("hex");
156
- }
157
- function getTokenHash() {
158
- const store = readAuthStore();
159
- const token = store["mcp_token"];
160
- if (!token || !token.startsWith("am_")) {
161
- throw new Error("Not authenticated. Run `assistme login`.");
162
- }
163
- return hashToken(token);
164
- }
165
- async function loginWithToken(mcpToken) {
166
- if (!mcpToken.startsWith("am_")) {
167
- throw new Error(
168
- "Invalid token format. Use an am_ token from the web page."
169
- );
170
- }
171
- const hash = hashToken(mcpToken);
172
- const sb = getSupabase();
173
- const { data, error } = await sb.rpc("validate_mcp_token", {
174
- p_token_hash: hash
175
- });
176
- if (error) throw new Error(`Token validation failed: ${error.message}`);
177
- if (!data || data.length === 0) throw new Error("Invalid or expired token");
178
- const store = readAuthStore();
179
- store["mcp_token"] = mcpToken;
180
- writeAuthStore(store);
181
- return data[0].out_user_id;
182
- }
183
- async function getCurrentUserId() {
184
- const hash = getTokenHash();
185
- const sb = getSupabase();
186
- const { data, error } = await sb.rpc("validate_mcp_token", {
187
- p_token_hash: hash
188
- });
189
- if (error || !data || data.length === 0) {
190
- throw new Error("Token expired or revoked. Run `assistme login`.");
191
- }
192
- return data[0].out_user_id;
193
- }
194
- async function logout() {
195
- try {
196
- writeAuthStore({});
197
- } catch {
198
- }
199
- }
200
- async function createSession(_userId, sessionName, workspacePath, version) {
201
- const sb = getSupabase();
202
- const { data, error } = await sb.rpc("mcp_create_session", {
203
- p_token_hash: getTokenHash(),
204
- p_session_name: sessionName,
205
- p_workspace_path: workspacePath,
206
- p_version: version,
207
- p_model: getConfig().model || null
208
- });
209
- if (error) throw new Error(`Failed to create session: ${error.message}`);
210
- return data;
211
- }
212
- async function updateHeartbeat(sessionId) {
213
- const sb = getSupabase();
214
- const { error } = await sb.rpc("mcp_heartbeat", {
215
- p_token_hash: getTokenHash(),
216
- p_session_id: sessionId
217
- });
218
- if (error) log.warn(`Heartbeat update failed: ${error.message}`);
219
- }
220
- async function endSession(sessionId) {
221
- const sb = getSupabase();
222
- const { error } = await sb.rpc("mcp_end_session", {
223
- p_token_hash: getTokenHash(),
224
- p_session_id: sessionId
225
- });
226
- if (error) log.error(`Failed to end session: ${error.message}`);
227
- }
228
- async function setSessionBusy(sessionId, busy) {
229
- const sb = getSupabase();
230
- await sb.rpc("mcp_set_session_busy", {
231
- p_token_hash: getTokenHash(),
232
- p_session_id: sessionId,
233
- p_busy: busy
234
- });
235
- }
236
- async function getOrCreateCliConversation(_userId, _sessionId) {
237
- const sb = getSupabase();
238
- const { data, error } = await sb.rpc("mcp_get_or_create_conversation", {
239
- p_token_hash: getTokenHash()
240
- });
241
- if (error) throw new Error(`Failed to get conversation: ${error.message}`);
242
- return data;
243
- }
244
- async function createTask(conversationId, _userId, sessionId, prompt) {
245
- const sb = getSupabase();
246
- const { data, error } = await sb.rpc("mcp_create_task", {
247
- p_token_hash: getTokenHash(),
248
- p_conversation_id: conversationId,
249
- p_session_id: sessionId,
250
- p_prompt: prompt
251
- });
252
- if (error) throw new Error(`Failed to create task: ${error.message}`);
253
- return { ...data, prompt };
254
- }
255
- async function pollAndClaimTask(sessionId) {
256
- const sb = getSupabase();
257
- const { data, error } = await sb.rpc("mcp_poll_and_claim_task", {
258
- p_token_hash: getTokenHash(),
259
- p_session_id: sessionId
260
- });
261
- if (error) {
262
- log.warn(`Poll-and-claim failed: ${error.message}`);
263
- return null;
264
- }
265
- if (!data) return null;
266
- const row = data;
267
- return {
268
- ...row,
269
- prompt: row.metadata?.prompt || row.content || ""
270
- };
271
- }
272
- async function claimTask(messageId) {
273
- const sb = getSupabase();
274
- const { data, error } = await sb.rpc("mcp_claim_task", {
275
- p_token_hash: getTokenHash(),
276
- p_message_id: messageId
277
- });
278
- if (error) throw new Error(`Failed to claim task: ${error.message}`);
279
- return data;
280
- }
281
- async function completeTask(messageId, resultSummary, tokenUsage) {
282
- const sb = getSupabase();
283
- const { error } = await sb.rpc("mcp_complete_task", {
284
- p_token_hash: getTokenHash(),
285
- p_message_id: messageId,
286
- p_result: resultSummary,
287
- p_token_usage: tokenUsage || null
288
- });
289
- if (error) throw new Error(`Failed to complete task: ${error.message}`);
290
- }
291
- async function failTask(messageId, errorMessage) {
292
- const sb = getSupabase();
293
- const { error } = await sb.rpc("mcp_fail_task", {
294
- p_token_hash: getTokenHash(),
295
- p_message_id: messageId,
296
- p_error: errorMessage
297
- });
298
- if (error) log.error(`Failed to update task status: ${error.message}`);
299
- }
300
- async function pollAndClaimJobRun(userId) {
301
- const sb = getSupabase();
302
- const { data, error } = await sb.rpc("claim_pending_job_run", {
303
- p_user_id: userId
304
- });
305
- if (error) {
306
- log.debug(`Job run poll failed: ${error.message}`);
307
- return null;
308
- }
309
- if (!data) return null;
310
- return data;
311
- }
312
- async function getConversationHistory(conversationId, excludeMessageId, limit = 20) {
313
- const sb = getSupabase();
314
- const { data, error } = await sb.from("conversation_messages").select("id, role, content, status, metadata, created_at").eq("conversation_id", conversationId).in("status", ["completed", "failed"]).neq("id", excludeMessageId).order("created_at", { ascending: false }).limit(limit);
315
- if (error) {
316
- log.debug(`Failed to fetch conversation history: ${error.message}`);
317
- return [];
318
- }
319
- const rows = data || [];
320
- return rows.reverse().map((row) => {
321
- const prompt = row.metadata?.prompt || "";
322
- const content = row.content || "";
323
- const response = row.status === "failed" ? `[Task failed] ${content}` : content;
324
- return { prompt, response };
325
- }).filter((entry) => entry.prompt && entry.response);
326
- }
327
- var eventSequence = 0;
328
- function resetEventSequence() {
329
- eventSequence = 0;
330
- }
331
- async function emitEvent(messageId, eventType, eventData) {
332
- const sb = getSupabase();
333
- eventSequence++;
334
- const { error } = await sb.rpc("mcp_emit_event", {
335
- p_token_hash: getTokenHash(),
336
- p_message_id: messageId,
337
- p_event_type: eventType,
338
- p_event_data: eventData,
339
- p_seq: eventSequence
340
- });
341
- if (error) log.warn(`Failed to emit event: ${error.message}`);
342
- }
343
- async function setActionRequest(messageId, actionData) {
344
- const sb = getSupabase();
345
- const { error } = await sb.rpc("mcp_set_action_request", {
346
- p_token_hash: getTokenHash(),
347
- p_message_id: messageId,
348
- p_action_data: actionData
349
- });
350
- if (error) throw new Error(`Failed to set action request: ${error.message}`);
351
- }
352
- async function pollActionResponse(messageId) {
353
- const sb = getSupabase();
354
- const { data, error } = await sb.rpc("mcp_poll_action_response", {
355
- p_token_hash: getTokenHash(),
356
- p_message_id: messageId
357
- });
358
- if (error) throw new Error(`Failed to poll action response: ${error.message}`);
359
- return data;
360
- }
361
-
362
- // src/agent/job-runner.ts
363
- var JobRunner = class {
364
- userId;
365
- constructor(userId) {
366
- this.userId = userId;
367
- }
368
- /**
369
- * Load a job and its linked skills from the database.
370
- * Skills are the agent's *capabilities* — not a fixed execution plan.
371
- */
372
- async loadJob(jobName) {
373
- try {
374
- const sb = getSupabase();
375
- const { data, error } = await sb.rpc("get_job_with_skills", {
376
- p_user_id: this.userId,
377
- p_job_name: jobName
378
- });
379
- if (error || !data || data.length === 0) {
380
- return null;
381
- }
382
- const rows = data;
383
- const first = rows[0];
384
- return {
385
- jobId: first.job_id,
386
- jobName: first.job_name,
387
- jobDescription: first.job_description,
388
- skills: rows.map((row) => ({
389
- skillId: row.skill_id,
390
- skillName: row.skill_name,
391
- skillDescription: row.skill_description || "",
392
- skillEmoji: row.skill_emoji || "",
393
- skillContent: row.skill_content || ""
394
- }))
395
- };
396
- } catch (err) {
397
- log.debug(`Failed to load job "${jobName}": ${err}`);
398
- return null;
399
- }
400
- }
401
- /**
402
- * List all jobs for the user.
403
- */
404
- async listJobs() {
405
- try {
406
- const sb = getSupabase();
407
- const { data: jobs, error } = await sb.from("agent_jobs").select("id, name, description").eq("user_id", this.userId).eq("is_active", true).order("name");
408
- if (error || !jobs) return [];
409
- const result = [];
410
- for (const job of jobs) {
411
- const { count } = await sb.from("agent_job_skills").select("id", { count: "exact", head: true }).eq("job_id", job.id);
412
- result.push({
413
- id: job.id,
414
- name: job.name,
415
- description: job.description,
416
- skillCount: count || 0
417
- });
418
- }
419
- return result;
420
- } catch (err) {
421
- log.debug(`Failed to list jobs: ${err}`);
422
- return [];
423
- }
424
- }
425
- /**
426
- * Create a job run record.
427
- * This is just a timestamp + status tracker. The actual execution trace
428
- * lives in the conversation transcript (message_events).
429
- */
430
- async createRun(jobId, options) {
431
- try {
432
- const sb = getSupabase();
433
- const { data, error } = await sb.rpc("create_job_run", {
434
- p_user_id: this.userId,
435
- p_job_id: jobId,
436
- p_session_id: options?.sessionId || null,
437
- p_message_id: options?.messageId || null,
438
- p_trigger_type: options?.triggerType || "manual"
439
- });
440
- if (error) {
441
- log.debug(`Failed to create job run: ${error.message}`);
442
- return null;
443
- }
444
- return data;
445
- } catch (err) {
446
- log.debug(`Job run creation error: ${err}`);
447
- return null;
448
- }
449
- }
450
- /**
451
- * Mark a job run as completed/failed.
452
- * Throws on failure so callers can handle (e.g. avoid duplicate execution).
453
- */
454
- async completeRun(runId, status = "completed", summary) {
455
- const sb = getSupabase();
456
- const { error } = await sb.rpc("complete_job_run", {
457
- p_run_id: runId,
458
- p_status: status,
459
- p_summary: summary?.slice(0, 1e4) || null,
460
- p_user_id: this.userId
461
- });
462
- if (error) {
463
- log.error(`Failed to mark run ${runId} as ${status}: ${error.message}`);
464
- throw new Error(`Run completion failed: ${error.message}`);
465
- }
466
- }
467
- /**
468
- * Get recent job run history.
469
- */
470
- async getRunHistory(jobName, limit = 10) {
471
- try {
472
- const sb = getSupabase();
473
- const { data, error } = await sb.rpc("get_job_runs", {
474
- p_user_id: this.userId,
475
- p_job_name: jobName || null,
476
- p_limit: limit
477
- });
478
- if (error || !data) return [];
479
- return data.map((row) => ({
480
- runId: row.run_id,
481
- jobName: row.job_name,
482
- status: row.status,
483
- triggerType: row.trigger_type,
484
- startedAt: row.started_at,
485
- completedAt: row.completed_at || null,
486
- summary: row.summary || null
487
- }));
488
- } catch (err) {
489
- log.debug(`Run history error: ${err}`);
490
- return [];
491
- }
492
- }
493
- /**
494
- * Build the agentic prompt for the agent to execute a job.
495
- *
496
- * This does NOT prescribe a fixed sequence. Instead, it tells the agent
497
- * the job's goal and lists available skills as capabilities. The agent
498
- * decides dynamically which skills to use, in what order, and how to
499
- * chain them based on what it discovers at runtime.
500
- */
501
- buildJobPrompt(job, runId) {
502
- let prompt = `## Job: ${job.jobName}
503
- `;
504
- prompt += `*${job.jobDescription}*
505
-
506
- `;
507
- prompt += `**Run ID:** ${runId}
508
-
509
- `;
510
- prompt += `You are now acting as "${job.jobName}". `;
511
- prompt += `Your goal is to accomplish the objectives described above using the skills and tools available to you.
512
-
513
- `;
514
- prompt += `### Available Skills
515
- `;
516
- prompt += `These skills are your capabilities for this job. `;
517
- prompt += `Use \`skill_invoke\` to load any skill's full instructions when you need it. `;
518
- prompt += `You do NOT need to use all of them \u2014 pick the ones relevant to the current situation.
519
-
520
- `;
521
- for (const skill of job.skills) {
522
- const emoji = skill.skillEmoji ? `${skill.skillEmoji} ` : "";
523
- prompt += `- **${emoji}${skill.skillName}**: ${skill.skillDescription}
524
- `;
525
- }
526
- prompt += `
527
- ### How to Work
528
- `;
529
- prompt += `- **Be agentic**: Decide what to do based on what you discover. `;
530
- prompt += `If checking Slack reveals a request that requires GitHub work, go do the GitHub work immediately \u2014 don't just note it for later.
531
- `;
532
- prompt += `- **Chain dynamically**: One skill's output should inform your next action. `;
533
- prompt += `For example, if you find an assigned GitHub issue, use your coding skills to implement it.
534
- `;
535
- prompt += `- **Skip what's irrelevant**: If a capability doesn't apply right now, skip it.
536
- `;
537
- prompt += `- **Use tools directly too**: You also have browser, file, and shell tools. `;
538
- prompt += `Use them directly when a skill isn't needed \u2014 skills are guides, not mandatory steps.
539
- `;
540
- prompt += `- **Respond and act**: If you find messages or issues that need replies, reply to them. `;
541
- prompt += `If you find code tasks, implement them.
542
-
543
- `;
544
- prompt += `When finished, provide a summary of what you accomplished and any items that need the user's attention.
545
- `;
546
- return prompt;
547
- }
548
- };
549
-
550
- export {
551
- getConfig,
552
- setConfig,
553
- clearConfig,
554
- getConfigPath,
555
- setLogLevel,
556
- setCorrelationId,
557
- newCorrelationId,
558
- log,
559
- getSupabase,
560
- loginWithToken,
561
- getCurrentUserId,
562
- logout,
563
- createSession,
564
- updateHeartbeat,
565
- endSession,
566
- setSessionBusy,
567
- getOrCreateCliConversation,
568
- createTask,
569
- pollAndClaimTask,
570
- claimTask,
571
- completeTask,
572
- failTask,
573
- pollAndClaimJobRun,
574
- getConversationHistory,
575
- resetEventSequence,
576
- emitEvent,
577
- setActionRequest,
578
- pollActionResponse,
579
- JobRunner
580
- };
@@ -1,6 +0,0 @@
1
- import {
2
- JobRunner
3
- } from "./chunk-XY3LGAOY.js";
4
- export {
5
- JobRunner
6
- };