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.
- package/dist/chunk-TTEGHE2E.js +47 -0
- package/dist/chunk-UWE5WVQI.js +289 -0
- package/dist/config-PUIS2TQL.js +12 -0
- package/dist/index.js +434 -669
- package/dist/job-runner-N4XAAWLJ.js +7 -0
- package/package.json +1 -2
- package/src/agent/job-runner.ts +33 -71
- package/src/agent/mcp-servers.ts +26 -149
- package/src/agent/memory.test.ts +41 -65
- package/src/agent/memory.ts +33 -134
- package/src/agent/scheduler.ts +47 -93
- package/src/agent/session.test.ts +8 -12
- package/src/agent/session.ts +10 -53
- package/src/agent/skills.ts +89 -488
- package/src/commands/job.ts +6 -6
- package/src/commands/status.ts +3 -10
- package/src/db/api-client.ts +68 -0
- package/src/db/supabase.test.ts +71 -184
- package/src/db/supabase.ts +140 -243
- package/dist/chunk-XY3LGAOY.js +0 -580
- package/dist/job-runner-XTGLMPZ3.js +0 -6
package/src/commands/job.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import chalk from "chalk";
|
|
3
|
-
import { getCurrentUserId
|
|
3
|
+
import { getCurrentUserId } from "../db/supabase.js";
|
|
4
|
+
import { callMcpHandler } from "../db/api-client.js";
|
|
4
5
|
import { log } from "../utils/logger.js";
|
|
5
6
|
import {
|
|
6
7
|
createScheduledTask,
|
|
@@ -150,11 +151,10 @@ export function registerJobCommands(program: Command): void {
|
|
|
150
151
|
);
|
|
151
152
|
|
|
152
153
|
// Link job_id
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
.
|
|
156
|
-
|
|
157
|
-
.eq("id", task.id);
|
|
154
|
+
await callMcpHandler("schedule.link_job", {
|
|
155
|
+
task_id: task.id,
|
|
156
|
+
job_id: job.jobId,
|
|
157
|
+
});
|
|
158
158
|
|
|
159
159
|
log.success(`Job "${name}" scheduled: ${opts.cron} (${tz})`);
|
|
160
160
|
console.log(` Skills: ${job.skills.length}`);
|
package/src/commands/status.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import chalk from "chalk";
|
|
3
|
-
import { getCurrentUserId,
|
|
3
|
+
import { getCurrentUserId, getActiveSessions } from "../db/supabase.js";
|
|
4
4
|
import { log } from "../utils/logger.js";
|
|
5
5
|
|
|
6
6
|
export function registerStatusCommand(program: Command): void {
|
|
@@ -9,16 +9,9 @@ export function registerStatusCommand(program: Command): void {
|
|
|
9
9
|
.description("Check the status of the current agent session")
|
|
10
10
|
.action(async () => {
|
|
11
11
|
try {
|
|
12
|
-
|
|
13
|
-
const sb = getSupabase();
|
|
12
|
+
await getCurrentUserId();
|
|
14
13
|
|
|
15
|
-
const
|
|
16
|
-
.from("agent_sessions")
|
|
17
|
-
.select("*")
|
|
18
|
-
.eq("user_id", userId)
|
|
19
|
-
.in("status", ["online", "busy"])
|
|
20
|
-
.order("started_at", { ascending: false })
|
|
21
|
-
.limit(5);
|
|
14
|
+
const sessions = await getActiveSessions(5);
|
|
22
15
|
|
|
23
16
|
if (!sessions || sessions.length === 0) {
|
|
24
17
|
console.log(chalk.yellow("No active sessions found."));
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { getConfig } from "../utils/config.js";
|
|
2
|
+
import { existsSync, readFileSync } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { homedir } from "os";
|
|
5
|
+
|
|
6
|
+
// ── Auth Store (shared with supabase.ts) ────────────────────────────
|
|
7
|
+
|
|
8
|
+
const AUTH_DIR = join(homedir(), ".config", "assistme");
|
|
9
|
+
const AUTH_FILE = join(AUTH_DIR, "auth.json");
|
|
10
|
+
|
|
11
|
+
function readAuthStore(): Record<string, string> {
|
|
12
|
+
try {
|
|
13
|
+
if (existsSync(AUTH_FILE)) {
|
|
14
|
+
return JSON.parse(readFileSync(AUTH_FILE, "utf-8"));
|
|
15
|
+
}
|
|
16
|
+
} catch {
|
|
17
|
+
// Corrupted file — start fresh
|
|
18
|
+
}
|
|
19
|
+
return {};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Get the raw MCP token from the local auth store.
|
|
24
|
+
* Throws if not authenticated.
|
|
25
|
+
*/
|
|
26
|
+
export function getRawToken(): string {
|
|
27
|
+
const store = readAuthStore();
|
|
28
|
+
const token = store["mcp_token"];
|
|
29
|
+
if (!token || !token.startsWith("am_")) {
|
|
30
|
+
throw new Error("Not authenticated. Run `assistme login`.");
|
|
31
|
+
}
|
|
32
|
+
return token;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Call the mcp-handler edge function.
|
|
37
|
+
*
|
|
38
|
+
* @param action Dot-separated action (e.g. "session.create", "task.poll_and_claim")
|
|
39
|
+
* @param params Action parameters
|
|
40
|
+
* @param overrideToken Optional token override (used during login before token is persisted)
|
|
41
|
+
*/
|
|
42
|
+
export async function callMcpHandler<T = unknown>(
|
|
43
|
+
action: string,
|
|
44
|
+
params: Record<string, unknown> = {},
|
|
45
|
+
overrideToken?: string,
|
|
46
|
+
): Promise<T> {
|
|
47
|
+
const config = getConfig();
|
|
48
|
+
const token = overrideToken || getRawToken();
|
|
49
|
+
const url = `${config.supabaseUrl}/functions/v1/mcp-handler`;
|
|
50
|
+
|
|
51
|
+
const response = await fetch(url, {
|
|
52
|
+
method: "POST",
|
|
53
|
+
headers: {
|
|
54
|
+
"Content-Type": "application/json",
|
|
55
|
+
Authorization: `Bearer ${token}`,
|
|
56
|
+
apikey: config.supabaseAnonKey,
|
|
57
|
+
},
|
|
58
|
+
body: JSON.stringify({ action, params }),
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const body = await response.json();
|
|
62
|
+
|
|
63
|
+
if (!response.ok || body.error) {
|
|
64
|
+
throw new Error(body.error || `Request failed: ${response.status}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return body.data as T;
|
|
68
|
+
}
|
package/src/db/supabase.test.ts
CHANGED
|
@@ -1,81 +1,11 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
2
|
|
|
3
|
-
// ──
|
|
4
|
-
|
|
5
|
-
// that persists across tests, with `_setResult()` to reconfigure per-test.
|
|
3
|
+
// ── callMcpHandler Mock ─────────────────────────────────────────
|
|
4
|
+
const mockCallMcpHandler = vi.fn();
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const chainMethods = [
|
|
11
|
-
"select",
|
|
12
|
-
"insert",
|
|
13
|
-
"update",
|
|
14
|
-
"delete",
|
|
15
|
-
"eq",
|
|
16
|
-
"neq",
|
|
17
|
-
"not",
|
|
18
|
-
"or",
|
|
19
|
-
"in",
|
|
20
|
-
"order",
|
|
21
|
-
"limit",
|
|
22
|
-
"single",
|
|
23
|
-
"from",
|
|
24
|
-
"lt",
|
|
25
|
-
];
|
|
26
|
-
for (const method of chainMethods) {
|
|
27
|
-
chain[method] = vi.fn().mockImplementation(() => chain);
|
|
28
|
-
}
|
|
29
|
-
// Make chain thenable — always resolves to current finalResult
|
|
30
|
-
chain.then = function (resolve: (value: unknown) => void) {
|
|
31
|
-
return resolve(finalResult);
|
|
32
|
-
};
|
|
33
|
-
chain.single = vi.fn().mockImplementation(() => ({
|
|
34
|
-
...chain,
|
|
35
|
-
then: (resolve: (value: unknown) => void) => resolve(finalResult),
|
|
36
|
-
}));
|
|
37
|
-
|
|
38
|
-
const mockFrom = vi.fn().mockReturnValue(chain);
|
|
39
|
-
const mockRpc = vi.fn().mockImplementation(() => ({
|
|
40
|
-
then: (resolve: (value: unknown) => void) => resolve(finalResult),
|
|
41
|
-
}));
|
|
42
|
-
const mockAuth = {
|
|
43
|
-
setSession: vi.fn().mockResolvedValue({
|
|
44
|
-
data: { user: { id: "user-123" } },
|
|
45
|
-
error: null,
|
|
46
|
-
}),
|
|
47
|
-
getSession: vi.fn().mockResolvedValue({
|
|
48
|
-
data: { session: { access_token: "test" } },
|
|
49
|
-
}),
|
|
50
|
-
getUser: vi.fn().mockResolvedValue({
|
|
51
|
-
data: { user: { id: "user-123" } },
|
|
52
|
-
error: null,
|
|
53
|
-
}),
|
|
54
|
-
signOut: vi.fn().mockResolvedValue(undefined),
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
const stableMockClient = {
|
|
58
|
-
from: mockFrom,
|
|
59
|
-
rpc: mockRpc,
|
|
60
|
-
auth: mockAuth,
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
function setResult(result: Record<string, unknown>) {
|
|
64
|
-
finalResult = result;
|
|
65
|
-
chain.then = function (resolve: (value: unknown) => void) {
|
|
66
|
-
return resolve(result);
|
|
67
|
-
};
|
|
68
|
-
chain.single = vi.fn().mockImplementation(() => ({
|
|
69
|
-
...chain,
|
|
70
|
-
then: (resolve: (value: unknown) => void) => resolve(result),
|
|
71
|
-
}));
|
|
72
|
-
mockRpc.mockImplementation(() => ({
|
|
73
|
-
then: (resolve: (value: unknown) => void) => resolve(result),
|
|
74
|
-
}));
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
vi.mock("@supabase/supabase-js", () => ({
|
|
78
|
-
createClient: () => stableMockClient,
|
|
6
|
+
vi.mock("./api-client.js", () => ({
|
|
7
|
+
callMcpHandler: (...args: unknown[]) => mockCallMcpHandler(...args),
|
|
8
|
+
getRawToken: () => "am_test_token_123",
|
|
79
9
|
}));
|
|
80
10
|
|
|
81
11
|
vi.mock("../utils/config.js", () => ({
|
|
@@ -112,7 +42,6 @@ const {
|
|
|
112
42
|
updateHeartbeat,
|
|
113
43
|
endSession,
|
|
114
44
|
setSessionBusy,
|
|
115
|
-
pollPendingTasks,
|
|
116
45
|
claimTask,
|
|
117
46
|
completeTask,
|
|
118
47
|
failTask,
|
|
@@ -122,48 +51,38 @@ const {
|
|
|
122
51
|
getCurrentUserId,
|
|
123
52
|
} = await import("./supabase.js");
|
|
124
53
|
|
|
125
|
-
describe("Supabase DB Layer", () => {
|
|
54
|
+
describe("Supabase DB Layer (edge function)", () => {
|
|
126
55
|
beforeEach(() => {
|
|
127
56
|
vi.clearAllMocks();
|
|
128
|
-
// Restore chain mocks (clearAllMocks resets them)
|
|
129
|
-
for (const method of chainMethods) {
|
|
130
|
-
chain[method] = vi.fn().mockImplementation(() => chain);
|
|
131
|
-
}
|
|
132
|
-
chain.single = vi.fn().mockImplementation(() => ({
|
|
133
|
-
...chain,
|
|
134
|
-
then: (resolve: (value: unknown) => void) => resolve(finalResult),
|
|
135
|
-
}));
|
|
136
|
-
mockFrom.mockReturnValue(chain);
|
|
137
|
-
setResult({ data: [], error: null });
|
|
138
57
|
resetEventSequence();
|
|
139
58
|
});
|
|
140
59
|
|
|
141
60
|
describe("createSession()", () => {
|
|
142
|
-
it("
|
|
61
|
+
it("calls session.create action and returns session data", async () => {
|
|
143
62
|
const sessionData = {
|
|
144
63
|
id: "sess-001",
|
|
145
64
|
user_id: "user-123",
|
|
146
65
|
session_name: "Test",
|
|
147
66
|
status: "online",
|
|
148
67
|
};
|
|
149
|
-
|
|
68
|
+
mockCallMcpHandler.mockResolvedValueOnce(sessionData);
|
|
150
69
|
|
|
151
70
|
const result = await createSession("user-123", "Test", "/tmp", "0.1.0");
|
|
152
71
|
|
|
153
|
-
expect(
|
|
154
|
-
"
|
|
72
|
+
expect(mockCallMcpHandler).toHaveBeenCalledWith(
|
|
73
|
+
"session.create",
|
|
155
74
|
expect.objectContaining({
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
75
|
+
session_name: "Test",
|
|
76
|
+
workspace_path: "/tmp",
|
|
77
|
+
version: "0.1.0",
|
|
159
78
|
})
|
|
160
79
|
);
|
|
161
80
|
expect(result.id).toBe("sess-001");
|
|
162
81
|
expect(result.status).toBe("online");
|
|
163
82
|
});
|
|
164
83
|
|
|
165
|
-
it("throws on
|
|
166
|
-
|
|
84
|
+
it("throws on edge function error", async () => {
|
|
85
|
+
mockCallMcpHandler.mockRejectedValueOnce(new Error("Failed to create session"));
|
|
167
86
|
|
|
168
87
|
await expect(createSession("user-123", "Test", "/tmp", "0.1.0")).rejects.toThrow(
|
|
169
88
|
"Failed to create session"
|
|
@@ -172,152 +91,120 @@ describe("Supabase DB Layer", () => {
|
|
|
172
91
|
});
|
|
173
92
|
|
|
174
93
|
describe("updateHeartbeat()", () => {
|
|
175
|
-
it("
|
|
176
|
-
|
|
94
|
+
it("calls session.heartbeat action", async () => {
|
|
95
|
+
mockCallMcpHandler.mockResolvedValueOnce(undefined);
|
|
177
96
|
await updateHeartbeat("sess-001");
|
|
178
|
-
expect(
|
|
179
|
-
"
|
|
97
|
+
expect(mockCallMcpHandler).toHaveBeenCalledWith(
|
|
98
|
+
"session.heartbeat",
|
|
180
99
|
expect.objectContaining({
|
|
181
|
-
|
|
100
|
+
session_id: "sess-001",
|
|
182
101
|
})
|
|
183
102
|
);
|
|
184
103
|
});
|
|
185
104
|
});
|
|
186
105
|
|
|
187
106
|
describe("endSession()", () => {
|
|
188
|
-
it("
|
|
189
|
-
|
|
107
|
+
it("calls session.end action", async () => {
|
|
108
|
+
mockCallMcpHandler.mockResolvedValueOnce(undefined);
|
|
190
109
|
await endSession("sess-001");
|
|
191
|
-
expect(
|
|
192
|
-
"
|
|
110
|
+
expect(mockCallMcpHandler).toHaveBeenCalledWith(
|
|
111
|
+
"session.end",
|
|
193
112
|
expect.objectContaining({
|
|
194
|
-
|
|
113
|
+
session_id: "sess-001",
|
|
195
114
|
})
|
|
196
115
|
);
|
|
197
116
|
});
|
|
198
117
|
});
|
|
199
118
|
|
|
200
119
|
describe("setSessionBusy()", () => {
|
|
201
|
-
it("
|
|
202
|
-
|
|
120
|
+
it("calls session.set_busy action", async () => {
|
|
121
|
+
mockCallMcpHandler.mockResolvedValueOnce(undefined);
|
|
203
122
|
await setSessionBusy("sess-001", true);
|
|
204
|
-
expect(
|
|
205
|
-
"
|
|
123
|
+
expect(mockCallMcpHandler).toHaveBeenCalledWith(
|
|
124
|
+
"session.set_busy",
|
|
206
125
|
expect.objectContaining({
|
|
207
|
-
|
|
208
|
-
|
|
126
|
+
session_id: "sess-001",
|
|
127
|
+
busy: true,
|
|
209
128
|
})
|
|
210
129
|
);
|
|
211
130
|
});
|
|
212
131
|
});
|
|
213
132
|
|
|
214
|
-
describe("pollPendingTasks()", () => {
|
|
215
|
-
it("returns empty array when no tasks", async () => {
|
|
216
|
-
setResult({ data: [], error: null });
|
|
217
|
-
const tasks = await pollPendingTasks("sess-001");
|
|
218
|
-
expect(tasks).toEqual([]);
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
it("returns tasks with prompt extracted from metadata", async () => {
|
|
222
|
-
setResult({
|
|
223
|
-
data: [
|
|
224
|
-
{
|
|
225
|
-
id: "msg-001",
|
|
226
|
-
session_id: "sess-001",
|
|
227
|
-
status: "pending",
|
|
228
|
-
content: "",
|
|
229
|
-
metadata: { prompt: "hello world" },
|
|
230
|
-
},
|
|
231
|
-
],
|
|
232
|
-
error: null,
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
const tasks = await pollPendingTasks("sess-001");
|
|
236
|
-
expect(tasks).toHaveLength(1);
|
|
237
|
-
expect(tasks[0].prompt).toBe("hello world");
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
it("returns empty array on error", async () => {
|
|
241
|
-
setResult({ data: null, error: { message: "query failed" } });
|
|
242
|
-
const tasks = await pollPendingTasks("sess-001");
|
|
243
|
-
expect(tasks).toEqual([]);
|
|
244
|
-
});
|
|
245
|
-
});
|
|
246
|
-
|
|
247
133
|
describe("claimTask()", () => {
|
|
248
|
-
it("
|
|
249
|
-
|
|
134
|
+
it("calls task.claim action", async () => {
|
|
135
|
+
mockCallMcpHandler.mockResolvedValueOnce(true);
|
|
250
136
|
await claimTask("msg-001");
|
|
251
|
-
expect(
|
|
252
|
-
"
|
|
137
|
+
expect(mockCallMcpHandler).toHaveBeenCalledWith(
|
|
138
|
+
"task.claim",
|
|
253
139
|
expect.objectContaining({
|
|
254
|
-
|
|
140
|
+
message_id: "msg-001",
|
|
255
141
|
})
|
|
256
142
|
);
|
|
257
143
|
});
|
|
258
|
-
|
|
259
|
-
it("throws on DB error", async () => {
|
|
260
|
-
setResult({ error: { message: "claim failed" } });
|
|
261
|
-
await expect(claimTask("msg-001")).rejects.toThrow("Failed to claim task");
|
|
262
|
-
});
|
|
263
144
|
});
|
|
264
145
|
|
|
265
146
|
describe("completeTask()", () => {
|
|
266
|
-
it("
|
|
267
|
-
|
|
147
|
+
it("calls task.complete action", async () => {
|
|
148
|
+
mockCallMcpHandler.mockResolvedValueOnce(undefined);
|
|
268
149
|
await completeTask("msg-001", "Task completed successfully");
|
|
269
|
-
expect(
|
|
270
|
-
"
|
|
150
|
+
expect(mockCallMcpHandler).toHaveBeenCalledWith(
|
|
151
|
+
"task.complete",
|
|
271
152
|
expect.objectContaining({
|
|
272
|
-
|
|
273
|
-
|
|
153
|
+
message_id: "msg-001",
|
|
154
|
+
result: "Task completed successfully",
|
|
274
155
|
})
|
|
275
156
|
);
|
|
276
157
|
});
|
|
277
158
|
});
|
|
278
159
|
|
|
279
160
|
describe("failTask()", () => {
|
|
280
|
-
it("
|
|
281
|
-
|
|
161
|
+
it("calls task.fail action", async () => {
|
|
162
|
+
mockCallMcpHandler.mockResolvedValueOnce(undefined);
|
|
282
163
|
await failTask("msg-001", "Something went wrong");
|
|
283
|
-
expect(
|
|
284
|
-
"
|
|
164
|
+
expect(mockCallMcpHandler).toHaveBeenCalledWith(
|
|
165
|
+
"task.fail",
|
|
285
166
|
expect.objectContaining({
|
|
286
|
-
|
|
287
|
-
|
|
167
|
+
message_id: "msg-001",
|
|
168
|
+
error: "Something went wrong",
|
|
288
169
|
})
|
|
289
170
|
);
|
|
290
171
|
});
|
|
291
172
|
});
|
|
292
173
|
|
|
293
174
|
describe("emitEvent()", () => {
|
|
294
|
-
it("
|
|
295
|
-
|
|
175
|
+
it("calls event.emit action with sequence number", async () => {
|
|
176
|
+
mockCallMcpHandler.mockResolvedValue(undefined);
|
|
296
177
|
|
|
297
178
|
await emitEvent("msg-001", "text_delta", { text: "hello" });
|
|
298
179
|
await emitEvent("msg-001", "text_delta", { text: "world" });
|
|
299
180
|
|
|
300
|
-
expect(
|
|
301
|
-
"
|
|
181
|
+
expect(mockCallMcpHandler).toHaveBeenCalledWith(
|
|
182
|
+
"event.emit",
|
|
183
|
+
expect.objectContaining({
|
|
184
|
+
message_id: "msg-001",
|
|
185
|
+
event_type: "text_delta",
|
|
186
|
+
seq: 1,
|
|
187
|
+
})
|
|
188
|
+
);
|
|
189
|
+
expect(mockCallMcpHandler).toHaveBeenCalledWith(
|
|
190
|
+
"event.emit",
|
|
302
191
|
expect.objectContaining({
|
|
303
|
-
|
|
304
|
-
p_event_type: "text_delta",
|
|
192
|
+
seq: 2,
|
|
305
193
|
})
|
|
306
194
|
);
|
|
307
195
|
});
|
|
308
196
|
});
|
|
309
197
|
|
|
310
198
|
describe("loginWithToken()", () => {
|
|
311
|
-
it("validates am_ token via
|
|
312
|
-
|
|
199
|
+
it("validates am_ token via edge function and returns user ID", async () => {
|
|
200
|
+
mockCallMcpHandler.mockResolvedValueOnce({ user_id: "user-123", email: null });
|
|
313
201
|
|
|
314
202
|
const userId = await loginWithToken("am_valid_test_token");
|
|
315
203
|
expect(userId).toBe("user-123");
|
|
316
|
-
expect(
|
|
317
|
-
"
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
})
|
|
204
|
+
expect(mockCallMcpHandler).toHaveBeenCalledWith(
|
|
205
|
+
"auth.validate_token",
|
|
206
|
+
{},
|
|
207
|
+
"am_valid_test_token"
|
|
321
208
|
);
|
|
322
209
|
});
|
|
323
210
|
|
|
@@ -325,16 +212,16 @@ describe("Supabase DB Layer", () => {
|
|
|
325
212
|
await expect(loginWithToken("not-am-token")).rejects.toThrow("Invalid token format");
|
|
326
213
|
});
|
|
327
214
|
|
|
328
|
-
it("throws on
|
|
329
|
-
|
|
215
|
+
it("throws on edge function validation failure", async () => {
|
|
216
|
+
mockCallMcpHandler.mockRejectedValueOnce(new Error("Token validation failed"));
|
|
330
217
|
|
|
331
218
|
await expect(loginWithToken("am_bad_token")).rejects.toThrow("Token validation failed");
|
|
332
219
|
});
|
|
333
220
|
});
|
|
334
221
|
|
|
335
222
|
describe("getCurrentUserId()", () => {
|
|
336
|
-
it("returns user ID
|
|
337
|
-
|
|
223
|
+
it("returns user ID from auth.validate_token", async () => {
|
|
224
|
+
mockCallMcpHandler.mockResolvedValueOnce({ user_id: "user-123" });
|
|
338
225
|
const userId = await getCurrentUserId();
|
|
339
226
|
expect(userId).toBe("user-123");
|
|
340
227
|
});
|