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.
- package/dist/{chunk-VWSNGP65.js → chunk-QXT7DH44.js} +109 -180
- package/dist/index.js +918 -918
- package/dist/{supabase-XHDOQMOM.js → supabase-QU7MFNDI.js} +3 -5
- package/package.json +3 -2
- package/src/agent/event-hooks.test.ts +172 -0
- package/src/agent/event-hooks.ts +81 -0
- package/src/agent/mcp-servers.test.ts +128 -0
- package/src/agent/mcp-servers.ts +367 -0
- package/src/agent/processor.test.ts +176 -56
- package/src/agent/processor.ts +169 -331
- package/src/agent/session.test.ts +34 -12
- package/src/agent/session.ts +43 -24
- package/src/db/supabase.ts +133 -312
- package/src/index.ts +1 -13
- package/src/utils/rate-limiter.ts +11 -5
|
@@ -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
|
-
|
|
181
|
-
const
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
}
|
|
186
|
-
|
|
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
|
|
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.
|
|
203
|
-
|
|
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(`
|
|
208
|
-
if (!data.
|
|
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
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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.
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
throw new Error("
|
|
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.
|
|
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(
|
|
202
|
+
async function createSession(_userId, sessionName, workspacePath, version) {
|
|
252
203
|
const sb = getSupabase();
|
|
253
|
-
const { data, error } = await sb.
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
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.
|
|
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.
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
})
|
|
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.
|
|
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(
|
|
238
|
+
async function getOrCreateCliConversation(_userId, _sessionId) {
|
|
282
239
|
const sb = getSupabase();
|
|
283
|
-
const { data
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
}
|
|
287
|
-
|
|
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,
|
|
246
|
+
async function createTask(conversationId, _userId, sessionId, prompt) {
|
|
302
247
|
const sb = getSupabase();
|
|
303
|
-
const { data
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
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.
|
|
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
|
-
|
|
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.
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
})
|
|
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 {
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
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 {
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
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.
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
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
|
|
336
|
+
const eventsJson = events.map((e) => {
|
|
406
337
|
eventSequence++;
|
|
407
|
-
return {
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
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,
|