assistme 0.1.4 → 0.1.6

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.
@@ -138,22 +138,6 @@ function writeAuthStore(data) {
138
138
  ensureAuthDir();
139
139
  writeFileSync(AUTH_FILE, JSON.stringify(data, null, 2), { mode: 384 });
140
140
  }
141
- var fileStorage = {
142
- getItem(key) {
143
- const store = readAuthStore();
144
- return store[key] ?? null;
145
- },
146
- setItem(key, value) {
147
- const store = readAuthStore();
148
- store[key] = value;
149
- writeAuthStore(store);
150
- },
151
- removeItem(key) {
152
- const store = readAuthStore();
153
- delete store[key];
154
- writeAuthStore(store);
155
- }
156
- };
157
141
  var supabase = null;
158
142
  function getSupabase() {
159
143
  if (!supabase) {
@@ -164,12 +148,7 @@ function getSupabase() {
164
148
  );
165
149
  }
166
150
  supabase = createClient(config2.supabaseUrl, config2.supabaseAnonKey, {
167
- auth: {
168
- storage: fileStorage,
169
- autoRefreshToken: false,
170
- // We handle refresh via am_ token
171
- persistSession: true
172
- }
151
+ auth: { persistSession: false }
173
152
  });
174
153
  }
175
154
  return supabase;
@@ -177,16 +156,13 @@ function getSupabase() {
177
156
  function hashToken(token) {
178
157
  return createHash("sha256").update(token).digest("hex");
179
158
  }
180
- async function authenticateWithMcpToken(mcpToken) {
181
- const tokenHash = hashToken(mcpToken);
182
- const sb = getSupabase();
183
- const { data, error } = await sb.rpc("login_with_mcp_token", {
184
- p_token_hash: tokenHash
185
- });
186
- if (error) throw new Error(`Token validation failed: ${error.message}`);
187
- if (data?.error) throw new Error(data.error);
188
- if (!data?.access_token) throw new Error("Token validation returned no session");
189
- return data;
159
+ function getTokenHash() {
160
+ const store = readAuthStore();
161
+ const token = store["mcp_token"];
162
+ if (!token || !token.startsWith("am_")) {
163
+ throw new Error("Not authenticated. Run `assistme login`.");
164
+ }
165
+ return hashToken(token);
190
166
  }
191
167
  async function loginWithToken(mcpToken) {
192
168
  if (!mcpToken.startsWith("am_")) {
@@ -194,195 +170,149 @@ async function loginWithToken(mcpToken) {
194
170
  "Invalid token format. Use an am_ token from the web page."
195
171
  );
196
172
  }
197
- const { access_token, user_id } = await authenticateWithMcpToken(mcpToken);
198
- const store = readAuthStore();
199
- store["mcp_token"] = mcpToken;
200
- writeAuthStore(store);
173
+ const hash = hashToken(mcpToken);
201
174
  const sb = getSupabase();
202
- const { data, error } = await sb.auth.setSession({
203
- access_token,
204
- refresh_token: "mcp_managed"
205
- // placeholder; we refresh via am_ token
175
+ const { data, error } = await sb.rpc("validate_mcp_token", {
176
+ p_token_hash: hash
206
177
  });
207
- if (error) throw new Error(`Login failed: ${error.message}`);
208
- if (!data.user) throw new Error("Login failed: no user returned");
209
- return data.user.id;
210
- }
211
- async function refreshWithCliToken() {
178
+ if (error) throw new Error(`Token validation failed: ${error.message}`);
179
+ if (!data || data.length === 0) throw new Error("Invalid or expired token");
212
180
  const store = readAuthStore();
213
- const mcpToken = store["mcp_token"];
214
- if (!mcpToken || !mcpToken.startsWith("am_")) return null;
215
- try {
216
- const { access_token } = await authenticateWithMcpToken(mcpToken);
217
- const sb = getSupabase();
218
- const { data, error } = await sb.auth.setSession({
219
- access_token,
220
- refresh_token: "mcp_managed"
221
- });
222
- if (error || !data.user) return null;
223
- return data.user.id;
224
- } catch {
225
- return null;
226
- }
227
- }
228
- async function getSession() {
229
- const sb = getSupabase();
230
- const { data } = await sb.auth.getSession();
231
- return data.session;
181
+ store["mcp_token"] = mcpToken;
182
+ writeAuthStore(store);
183
+ return data[0].out_user_id;
232
184
  }
233
185
  async function getCurrentUserId() {
186
+ const hash = getTokenHash();
234
187
  const sb = getSupabase();
235
- const { data, error } = await sb.auth.getUser();
236
- if (error || !data.user) {
237
- const refreshed = await refreshWithCliToken();
238
- if (refreshed) return refreshed;
239
- throw new Error("Not authenticated. Run `assistme login`.");
188
+ const { data, error } = await sb.rpc("validate_mcp_token", {
189
+ p_token_hash: hash
190
+ });
191
+ if (error || !data || data.length === 0) {
192
+ throw new Error("Token expired or revoked. Run `assistme login`.");
240
193
  }
241
- return data.user.id;
194
+ return data[0].out_user_id;
242
195
  }
243
196
  async function logout() {
244
- const sb = getSupabase();
245
- await sb.auth.signOut();
246
197
  try {
247
198
  writeAuthStore({});
248
199
  } catch {
249
200
  }
250
201
  }
251
- async function createSession(userId, sessionName, workspacePath, version) {
202
+ async function createSession(_userId, sessionName, workspacePath, version) {
252
203
  const sb = getSupabase();
253
- const { data, error } = await sb.from("agent_sessions").insert({
254
- user_id: userId,
255
- session_name: sessionName,
256
- status: "online",
257
- workspace_path: workspacePath,
258
- agent_version: version,
259
- metadata: { model: getConfig().model }
260
- }).select().single();
204
+ const { data, error } = await sb.rpc("mcp_create_session", {
205
+ p_token_hash: getTokenHash(),
206
+ p_session_name: sessionName,
207
+ p_workspace_path: workspacePath,
208
+ p_version: version,
209
+ p_model: getConfig().model || null
210
+ });
261
211
  if (error) throw new Error(`Failed to create session: ${error.message}`);
262
212
  return data;
263
213
  }
264
214
  async function updateHeartbeat(sessionId) {
265
215
  const sb = getSupabase();
266
- const { error } = await sb.from("agent_sessions").update({ last_heartbeat_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", sessionId);
216
+ const { error } = await sb.rpc("mcp_heartbeat", {
217
+ p_token_hash: getTokenHash(),
218
+ p_session_id: sessionId
219
+ });
267
220
  if (error) log.warn(`Heartbeat update failed: ${error.message}`);
268
221
  }
269
222
  async function endSession(sessionId) {
270
223
  const sb = getSupabase();
271
- const { error } = await sb.from("agent_sessions").update({
272
- status: "offline",
273
- ended_at: (/* @__PURE__ */ new Date()).toISOString()
274
- }).eq("id", sessionId);
224
+ const { error } = await sb.rpc("mcp_end_session", {
225
+ p_token_hash: getTokenHash(),
226
+ p_session_id: sessionId
227
+ });
275
228
  if (error) log.error(`Failed to end session: ${error.message}`);
276
229
  }
277
230
  async function setSessionBusy(sessionId, busy) {
278
231
  const sb = getSupabase();
279
- await sb.from("agent_sessions").update({ status: busy ? "busy" : "online" }).eq("id", sessionId);
232
+ await sb.rpc("mcp_set_session_busy", {
233
+ p_token_hash: getTokenHash(),
234
+ p_session_id: sessionId,
235
+ p_busy: busy
236
+ });
280
237
  }
281
- async function getOrCreateCliConversation(userId, sessionId) {
238
+ async function getOrCreateCliConversation(_userId, _sessionId) {
282
239
  const sb = getSupabase();
283
- const { data: existing } = await sb.from("conversations").select("id").eq("agent", "cli").eq("created_by", userId).order("created_at", { ascending: false }).limit(1);
284
- if (existing && existing.length > 0) {
285
- return existing[0].id;
286
- }
287
- const { data: conv, error: convErr } = await sb.from("conversations").insert({
288
- conversation_type: "direct",
289
- agent: "cli",
290
- created_by: userId
291
- }).select("id").single();
292
- if (convErr || !conv) {
293
- throw new Error(`Failed to create conversation: ${convErr?.message}`);
294
- }
295
- await sb.from("conversation_participants").insert([
296
- { conversation_id: conv.id, user_id: userId, role: "member" },
297
- { conversation_id: conv.id, user_id: CLI_AGENT_ID, role: "member" }
298
- ]);
299
- return conv.id;
240
+ const { data, error } = await sb.rpc("mcp_get_or_create_conversation", {
241
+ p_token_hash: getTokenHash()
242
+ });
243
+ if (error) throw new Error(`Failed to get conversation: ${error.message}`);
244
+ return data;
300
245
  }
301
- async function createTask(conversationId, userId, sessionId, prompt) {
246
+ async function createTask(conversationId, _userId, sessionId, prompt) {
302
247
  const sb = getSupabase();
303
- const { data: rpcResult, error: rpcError } = await sb.rpc(
304
- "create_task_pair",
305
- {
306
- p_conversation_id: conversationId,
307
- p_user_id: userId,
308
- p_agent_id: CLI_AGENT_ID,
309
- p_session_id: sessionId,
310
- p_prompt: prompt
311
- }
312
- );
313
- if (!rpcError && rpcResult) {
314
- return { ...rpcResult, prompt };
315
- }
316
- if (rpcError) {
317
- log.debug(`create_task_pair RPC unavailable, using fallback: ${rpcError.message}`);
318
- }
319
- const { error: userErr } = await sb.from("conversation_messages").insert({
320
- conversation_id: conversationId,
321
- sender_id: userId,
322
- role: "user",
323
- content: prompt
248
+ const { data, error } = await sb.rpc("mcp_create_task", {
249
+ p_token_hash: getTokenHash(),
250
+ p_conversation_id: conversationId,
251
+ p_session_id: sessionId,
252
+ p_prompt: prompt
324
253
  });
325
- if (userErr) throw new Error(`Failed to create user message: ${userErr.message}`);
326
- const { data, error } = await sb.from("conversation_messages").insert({
327
- conversation_id: conversationId,
328
- sender_id: CLI_AGENT_ID,
329
- role: "assistant",
330
- content: "",
331
- status: "pending",
332
- session_id: sessionId,
333
- metadata: { prompt }
334
- }).select().single();
335
254
  if (error) throw new Error(`Failed to create task: ${error.message}`);
336
255
  return { ...data, prompt };
337
256
  }
338
257
  async function pollPendingTasks(sessionId) {
339
258
  const sb = getSupabase();
340
- const { data, error } = await sb.from("conversation_messages").select("*").eq("session_id", sessionId).eq("status", "pending").order("created_at", { ascending: true }).limit(1);
259
+ const { data, error } = await sb.rpc("mcp_poll_tasks", {
260
+ p_token_hash: getTokenHash(),
261
+ p_session_id: sessionId
262
+ });
341
263
  if (error) {
342
264
  log.warn(`Task poll failed: ${error.message}`);
343
265
  return [];
344
266
  }
345
- return (data || []).map((row) => ({
267
+ const rows = data || [];
268
+ return rows.map((row) => ({
346
269
  ...row,
347
270
  prompt: row.metadata?.prompt || row.content || ""
348
271
  }));
349
272
  }
273
+ async function pollAndClaimTask(sessionId) {
274
+ const sb = getSupabase();
275
+ const { data, error } = await sb.rpc("mcp_poll_and_claim_task", {
276
+ p_token_hash: getTokenHash(),
277
+ p_session_id: sessionId
278
+ });
279
+ if (error) {
280
+ log.warn(`Poll-and-claim failed: ${error.message}`);
281
+ return null;
282
+ }
283
+ if (!data) return null;
284
+ const row = data;
285
+ return {
286
+ ...row,
287
+ prompt: row.metadata?.prompt || row.content || ""
288
+ };
289
+ }
350
290
  async function claimTask(messageId) {
351
291
  const sb = getSupabase();
352
- const { error } = await sb.from("conversation_messages").update({
353
- status: "running",
354
- metadata: { started_at: (/* @__PURE__ */ new Date()).toISOString() }
355
- }).eq("id", messageId).eq("status", "pending");
292
+ const { data, error } = await sb.rpc("mcp_claim_task", {
293
+ p_token_hash: getTokenHash(),
294
+ p_message_id: messageId
295
+ });
356
296
  if (error) throw new Error(`Failed to claim task: ${error.message}`);
297
+ return data;
357
298
  }
358
299
  async function completeTask(messageId, resultSummary, tokenUsage) {
359
300
  const sb = getSupabase();
360
- const { data: current } = await sb.from("conversation_messages").select("metadata").eq("id", messageId).single();
361
- const existingMeta = current?.metadata || {};
362
- const { error } = await sb.from("conversation_messages").update({
363
- content: resultSummary,
364
- status: "completed",
365
- metadata: {
366
- ...existingMeta,
367
- completed_at: (/* @__PURE__ */ new Date()).toISOString(),
368
- token_usage: tokenUsage || null
369
- }
370
- }).eq("id", messageId);
301
+ const { error } = await sb.rpc("mcp_complete_task", {
302
+ p_token_hash: getTokenHash(),
303
+ p_message_id: messageId,
304
+ p_result: resultSummary,
305
+ p_token_usage: tokenUsage || null
306
+ });
371
307
  if (error) throw new Error(`Failed to complete task: ${error.message}`);
372
308
  }
373
309
  async function failTask(messageId, errorMessage) {
374
310
  const sb = getSupabase();
375
- const { data: current } = await sb.from("conversation_messages").select("metadata").eq("id", messageId).single();
376
- const existingMeta = current?.metadata || {};
377
- const { error } = await sb.from("conversation_messages").update({
378
- status: "failed",
379
- content: errorMessage,
380
- metadata: {
381
- ...existingMeta,
382
- completed_at: (/* @__PURE__ */ new Date()).toISOString(),
383
- error_message: errorMessage
384
- }
385
- }).eq("id", messageId);
311
+ const { error } = await sb.rpc("mcp_fail_task", {
312
+ p_token_hash: getTokenHash(),
313
+ p_message_id: messageId,
314
+ p_error: errorMessage
315
+ });
386
316
  if (error) log.error(`Failed to update task status: ${error.message}`);
387
317
  }
388
318
  var eventSequence = 0;
@@ -392,26 +322,26 @@ function resetEventSequence() {
392
322
  async function emitEvent(messageId, eventType, eventData) {
393
323
  const sb = getSupabase();
394
324
  eventSequence++;
395
- const { error } = await sb.from("message_events").insert({
396
- message_id: messageId,
397
- event_type: eventType,
398
- event_data: eventData,
399
- sequence_number: eventSequence
325
+ const { error } = await sb.rpc("mcp_emit_event", {
326
+ p_token_hash: getTokenHash(),
327
+ p_message_id: messageId,
328
+ p_event_type: eventType,
329
+ p_event_data: eventData,
330
+ p_seq: eventSequence
400
331
  });
401
332
  if (error) log.warn(`Failed to emit event: ${error.message}`);
402
333
  }
403
334
  async function emitEvents(messageId, events) {
404
335
  const sb = getSupabase();
405
- const rows = events.map((e) => {
336
+ const eventsJson = events.map((e) => {
406
337
  eventSequence++;
407
- return {
408
- message_id: messageId,
409
- event_type: e.type,
410
- event_data: e.data,
411
- sequence_number: eventSequence
412
- };
338
+ return { type: e.type, data: e.data, seq: eventSequence };
339
+ });
340
+ const { error } = await sb.rpc("mcp_emit_events", {
341
+ p_token_hash: getTokenHash(),
342
+ p_message_id: messageId,
343
+ p_events: eventsJson
413
344
  });
414
- const { error } = await sb.from("message_events").insert(rows);
415
345
  if (error) log.warn(`Failed to emit events batch: ${error.message}`);
416
346
  }
417
347
 
@@ -428,8 +358,6 @@ export {
428
358
  DAYBOX_AGENT_ID,
429
359
  getSupabase,
430
360
  loginWithToken,
431
- refreshWithCliToken,
432
- getSession,
433
361
  getCurrentUserId,
434
362
  logout,
435
363
  createSession,
@@ -439,6 +367,7 @@ export {
439
367
  getOrCreateCliConversation,
440
368
  createTask,
441
369
  pollPendingTasks,
370
+ pollAndClaimTask,
442
371
  claimTask,
443
372
  completeTask,
444
373
  failTask,