aismemory 0.5.2 → 0.5.3
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/__tests__/auto-enroll-key-e2e.test.d.ts +1 -0
- package/dist/__tests__/auto-enroll-key-e2e.test.js +116 -0
- package/dist/__tests__/auto-enroll-key-e2e.test.js.map +1 -0
- package/dist/__tests__/auto-enroll-key.test.d.ts +1 -0
- package/dist/__tests__/auto-enroll-key.test.js +165 -0
- package/dist/__tests__/auto-enroll-key.test.js.map +1 -0
- package/dist/__tests__/device-flow-recovery.test.js +9 -5
- package/dist/__tests__/device-flow-recovery.test.js.map +1 -1
- package/dist/__tests__/device-poll.test.d.ts +12 -0
- package/dist/__tests__/device-poll.test.js +191 -0
- package/dist/__tests__/device-poll.test.js.map +1 -0
- package/dist/__tests__/mcp-init-no-auth.test.js +14 -1
- package/dist/__tests__/mcp-init-no-auth.test.js.map +1 -1
- package/dist/__tests__/resolve-agent-preference-e2e.test.d.ts +1 -0
- package/dist/__tests__/resolve-agent-preference-e2e.test.js +173 -0
- package/dist/__tests__/resolve-agent-preference-e2e.test.js.map +1 -0
- package/dist/__tests__/resolve-agent-preference.test.d.ts +1 -0
- package/dist/__tests__/resolve-agent-preference.test.js +83 -0
- package/dist/__tests__/resolve-agent-preference.test.js.map +1 -0
- package/dist/device-poll.d.ts +51 -0
- package/dist/device-poll.js +77 -0
- package/dist/device-poll.js.map +1 -0
- package/dist/env-agent.d.ts +27 -0
- package/dist/env-agent.js +33 -0
- package/dist/env-agent.js.map +1 -1
- package/dist/index.js +141 -56
- package/dist/index.js.map +1 -1
- package/dist/key-auth.d.ts +30 -0
- package/dist/key-auth.js +77 -1
- package/dist/key-auth.js.map +1 -1
- package/dist/telemetry.d.ts +1 -1
- package/dist/telemetry.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* E2E wiring test: the MCP auto-enrolls a DID key in its post-auth path.
|
|
3
|
+
*
|
|
4
|
+
* Spawns the built binary with HOME pointed at a fresh tmpdir containing
|
|
5
|
+
* valid bond credentials but NO key file — exactly the state every normal
|
|
6
|
+
* `npx aismemory` user was left in after bonding. Before the fix nothing
|
|
7
|
+
* happened and the user re-bonded at day ~7; after the fix the MCP registers
|
|
8
|
+
* a public key with AIS at startup and writes the private key to
|
|
9
|
+
* `~/.aismemory/keys/`, so all future re-auth is silent.
|
|
10
|
+
*/
|
|
11
|
+
import { createServer } from 'node:http';
|
|
12
|
+
import { spawn } from 'node:child_process';
|
|
13
|
+
import { mkdtempSync, mkdirSync, writeFileSync, rmSync, existsSync, readdirSync } from 'node:fs';
|
|
14
|
+
import { tmpdir } from 'node:os';
|
|
15
|
+
import { join, resolve } from 'path';
|
|
16
|
+
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
17
|
+
const USER_ID = 'user-enroll-1';
|
|
18
|
+
function mintJwt() {
|
|
19
|
+
const header = Buffer.from(JSON.stringify({ alg: 'HS256', typ: 'JWT' })).toString('base64url');
|
|
20
|
+
const payload = Buffer.from(JSON.stringify({ sub: USER_ID, user_id: USER_ID, tenant_id: 'tenant-1' })).toString('base64url');
|
|
21
|
+
return `${header}.${payload}.fakesig`;
|
|
22
|
+
}
|
|
23
|
+
describe('MCP auto-enrolls a DID key at startup post-auth', () => {
|
|
24
|
+
let fakeAis;
|
|
25
|
+
let port = 0;
|
|
26
|
+
let keyRegistrations = [];
|
|
27
|
+
beforeAll(async () => {
|
|
28
|
+
fakeAis = createServer((req, res) => {
|
|
29
|
+
let body = '';
|
|
30
|
+
req.on('data', (d) => { body += d.toString(); });
|
|
31
|
+
req.on('end', () => {
|
|
32
|
+
if (req.url === '/v1/users/me/keys' && req.method === 'POST') {
|
|
33
|
+
keyRegistrations.push({
|
|
34
|
+
auth: (req.headers['authorization'] ?? '').replace('Bearer ', ''),
|
|
35
|
+
body,
|
|
36
|
+
});
|
|
37
|
+
res.writeHead(200, { 'content-type': 'application/json' });
|
|
38
|
+
res.end(JSON.stringify({ success: true, data: { id: 'key-1' } }));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
// Hydration / refresh / handoff endpoints — generic success is fine;
|
|
42
|
+
// runPostAuth tolerates unexpected shapes.
|
|
43
|
+
res.writeHead(200, { 'content-type': 'application/json' });
|
|
44
|
+
res.end(JSON.stringify({ success: true, data: {} }));
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
await new Promise((r) => fakeAis.listen(0, '127.0.0.1', r));
|
|
48
|
+
const addr = fakeAis.address();
|
|
49
|
+
if (addr === null || typeof addr === 'string')
|
|
50
|
+
throw new Error('no port');
|
|
51
|
+
port = addr.port;
|
|
52
|
+
});
|
|
53
|
+
afterAll(async () => {
|
|
54
|
+
if (fakeAis)
|
|
55
|
+
await new Promise((r) => fakeAis.close(() => r()));
|
|
56
|
+
});
|
|
57
|
+
it('registers a public key and writes the private key file', async () => {
|
|
58
|
+
const distPath = resolve(__dirname, '..', '..', 'dist', 'index.js');
|
|
59
|
+
if (!existsSync(distPath)) {
|
|
60
|
+
throw new Error(`dist/index.js missing — run \`pnpm build\` first (looked at ${distPath})`);
|
|
61
|
+
}
|
|
62
|
+
keyRegistrations = [];
|
|
63
|
+
const fakeHome = mkdtempSync(join(tmpdir(), 'aismem-enroll-e2e-'));
|
|
64
|
+
const aisDir = join(fakeHome, '.aismemory');
|
|
65
|
+
mkdirSync(aisDir, { recursive: true });
|
|
66
|
+
const token = mintJwt();
|
|
67
|
+
writeFileSync(join(aisDir, 'credentials.json'), JSON.stringify({
|
|
68
|
+
token,
|
|
69
|
+
agentId: 'agent-1',
|
|
70
|
+
tenantId: 'tenant-1',
|
|
71
|
+
expiresAt: new Date(Date.now() + 7 * 24 * 3600 * 1000).toISOString(),
|
|
72
|
+
}));
|
|
73
|
+
let proc = null;
|
|
74
|
+
try {
|
|
75
|
+
proc = spawn('node', [distPath], {
|
|
76
|
+
env: {
|
|
77
|
+
...process.env,
|
|
78
|
+
HOME: fakeHome,
|
|
79
|
+
AIS_URL: `http://127.0.0.1:${port}`,
|
|
80
|
+
},
|
|
81
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
82
|
+
});
|
|
83
|
+
let stderrBuf = '';
|
|
84
|
+
proc.stderr.on('data', (d) => { stderrBuf += d.toString(); });
|
|
85
|
+
await new Promise((resolveOuter, reject) => {
|
|
86
|
+
const timeout = setTimeout(() => {
|
|
87
|
+
reject(new Error(`No key registration within 5s. stderr:\n${stderrBuf}`));
|
|
88
|
+
}, 5000);
|
|
89
|
+
const interval = setInterval(() => {
|
|
90
|
+
if (keyRegistrations.length > 0) {
|
|
91
|
+
clearTimeout(timeout);
|
|
92
|
+
clearInterval(interval);
|
|
93
|
+
resolveOuter();
|
|
94
|
+
}
|
|
95
|
+
}, 50);
|
|
96
|
+
});
|
|
97
|
+
// Registered with the bond token, carrying an Ed25519 public JWK.
|
|
98
|
+
expect(keyRegistrations[0].auth).toBe(token);
|
|
99
|
+
const regBody = JSON.parse(keyRegistrations[0].body);
|
|
100
|
+
expect(regBody.publicKey.kty).toBe('OKP');
|
|
101
|
+
expect(regBody.publicKey.crv).toBe('Ed25519');
|
|
102
|
+
// Private key persisted for silent re-auth on all future sessions.
|
|
103
|
+
const keysDir = join(aisDir, 'keys');
|
|
104
|
+
expect(existsSync(keysDir)).toBe(true);
|
|
105
|
+
const keyFiles = readdirSync(keysDir).filter((f) => f.endsWith('.json'));
|
|
106
|
+
expect(keyFiles).toHaveLength(1);
|
|
107
|
+
expect(keyFiles[0]).toBe(`did:web:127.0.0.1:${port}:users:${USER_ID}.json`);
|
|
108
|
+
}
|
|
109
|
+
finally {
|
|
110
|
+
if (proc && !proc.killed)
|
|
111
|
+
proc.kill('SIGKILL');
|
|
112
|
+
rmSync(fakeHome, { recursive: true, force: true });
|
|
113
|
+
}
|
|
114
|
+
}, 15_000);
|
|
115
|
+
});
|
|
116
|
+
//# sourceMappingURL=auto-enroll-key-e2e.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-enroll-key-e2e.test.js","sourceRoot":"","sources":["../../src/__tests__/auto-enroll-key-e2e.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for automatic DID-key enrollment at first bond.
|
|
3
|
+
*
|
|
4
|
+
* Root cause being fixed (AIS memory 207c39de): a normal `npx aismemory`
|
|
5
|
+
* user gets only a 7-day bearer JWT at bond and never enrolls a silent
|
|
6
|
+
* re-auth key — key enrollment was a manual, undocumented `enable-key-auth`
|
|
7
|
+
* step. Day ~7 the token expires and the user is asked to re-bond.
|
|
8
|
+
*
|
|
9
|
+
* enrollKeyIfNeeded() runs in the post-auth path and must:
|
|
10
|
+
* - generate + register a key when none exists (the fix)
|
|
11
|
+
* - be idempotent: skip cheaply when any key file already exists
|
|
12
|
+
* - skip when the token carries no user identity (agent-only token)
|
|
13
|
+
* - clean up the local key file when server registration fails, so the
|
|
14
|
+
* next session retries enrollment instead of key-auth failing forever
|
|
15
|
+
* - never throw (post-auth is best-effort)
|
|
16
|
+
*/
|
|
17
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
18
|
+
import { mkdtempSync, rmSync, existsSync, readdirSync } from 'node:fs';
|
|
19
|
+
import { tmpdir } from 'node:os';
|
|
20
|
+
import { join } from 'node:path';
|
|
21
|
+
import { enrollKeyIfNeeded, generateAndSaveKeypair } from '../key-auth.js';
|
|
22
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
23
|
+
// Fixtures
|
|
24
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
25
|
+
const TEST_USER_ID = '454e4306-423e-498d-9b85-24bafbabff9b';
|
|
26
|
+
const TEST_AIS_URL = 'https://ais.example.com';
|
|
27
|
+
const TEST_USER_DID = `did:web:ais.example.com:users:${TEST_USER_ID}`;
|
|
28
|
+
/** Fabricate an unsigned-but-well-formed JWT with the given payload. */
|
|
29
|
+
function fakeJwt(payload) {
|
|
30
|
+
const b64 = (obj) => Buffer.from(JSON.stringify(obj)).toString('base64url');
|
|
31
|
+
return `${b64({ alg: 'HS256', typ: 'JWT' })}.${b64(payload)}.sig`;
|
|
32
|
+
}
|
|
33
|
+
const OWNER_TOKEN = fakeJwt({ user_id: TEST_USER_ID, tenant_id: 't-1' });
|
|
34
|
+
/** A fetch stub that records requests and replies with a fixed response. */
|
|
35
|
+
function stubFetch(status, body) {
|
|
36
|
+
const requests = [];
|
|
37
|
+
const fetchImpl = (async (url, init) => {
|
|
38
|
+
requests.push({
|
|
39
|
+
url: String(url),
|
|
40
|
+
method: init?.method ?? 'GET',
|
|
41
|
+
headers: (init?.headers ?? {}),
|
|
42
|
+
body: init?.body ? JSON.parse(String(init.body)) : undefined,
|
|
43
|
+
});
|
|
44
|
+
return new Response(JSON.stringify(body), { status });
|
|
45
|
+
});
|
|
46
|
+
return { fetchImpl, requests };
|
|
47
|
+
}
|
|
48
|
+
let tmpKeysDir;
|
|
49
|
+
beforeEach(() => {
|
|
50
|
+
tmpKeysDir = mkdtempSync(join(tmpdir(), 'aismemory-auto-enroll-test-'));
|
|
51
|
+
});
|
|
52
|
+
afterEach(() => {
|
|
53
|
+
if (existsSync(tmpKeysDir)) {
|
|
54
|
+
rmSync(tmpKeysDir, { recursive: true, force: true });
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
58
|
+
// enrollKeyIfNeeded
|
|
59
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
60
|
+
describe('enrollKeyIfNeeded', () => {
|
|
61
|
+
it('generates a key, registers it with AIS, and keeps it on disk', async () => {
|
|
62
|
+
const { fetchImpl, requests } = stubFetch(200, {
|
|
63
|
+
success: true,
|
|
64
|
+
data: { id: 'key-1', label: 'auto' },
|
|
65
|
+
});
|
|
66
|
+
const result = await enrollKeyIfNeeded({
|
|
67
|
+
aisUrl: TEST_AIS_URL,
|
|
68
|
+
token: OWNER_TOKEN,
|
|
69
|
+
keysDir: tmpKeysDir,
|
|
70
|
+
fetchImpl,
|
|
71
|
+
});
|
|
72
|
+
expect(result.enrolled).toBe(true);
|
|
73
|
+
// Key file written for the DID derived from the AIS host + user_id claim.
|
|
74
|
+
const keyPath = join(tmpKeysDir, `${TEST_USER_DID}.json`);
|
|
75
|
+
expect(existsSync(keyPath)).toBe(true);
|
|
76
|
+
// Public key registered against the users/me/keys endpoint with the
|
|
77
|
+
// bond token as bearer auth.
|
|
78
|
+
expect(requests).toHaveLength(1);
|
|
79
|
+
const req = requests[0];
|
|
80
|
+
expect(req.url).toBe(`${TEST_AIS_URL}/v1/users/me/keys`);
|
|
81
|
+
expect(req.method).toBe('POST');
|
|
82
|
+
expect(req.headers['Authorization']).toBe(`Bearer ${OWNER_TOKEN}`);
|
|
83
|
+
const body = req.body;
|
|
84
|
+
expect(body.publicKey.kty).toBe('OKP');
|
|
85
|
+
expect(body.publicKey.crv).toBe('Ed25519');
|
|
86
|
+
expect(body.publicKey.x).toMatch(/^[A-Za-z0-9_-]+$/);
|
|
87
|
+
});
|
|
88
|
+
it('skips without any network call when a key file already exists', async () => {
|
|
89
|
+
await generateAndSaveKeypair({
|
|
90
|
+
userId: TEST_USER_ID,
|
|
91
|
+
userDid: TEST_USER_DID,
|
|
92
|
+
keysDir: tmpKeysDir,
|
|
93
|
+
});
|
|
94
|
+
const { fetchImpl, requests } = stubFetch(200, { success: true });
|
|
95
|
+
const result = await enrollKeyIfNeeded({
|
|
96
|
+
aisUrl: TEST_AIS_URL,
|
|
97
|
+
token: OWNER_TOKEN,
|
|
98
|
+
keysDir: tmpKeysDir,
|
|
99
|
+
fetchImpl,
|
|
100
|
+
});
|
|
101
|
+
expect(result.enrolled).toBe(false);
|
|
102
|
+
expect(result.reason).toBe('key-already-present');
|
|
103
|
+
expect(requests).toHaveLength(0);
|
|
104
|
+
});
|
|
105
|
+
it('skips when the token carries no user identity claim', async () => {
|
|
106
|
+
const agentOnlyToken = fakeJwt({ agent_id: 'a-1' });
|
|
107
|
+
const { fetchImpl, requests } = stubFetch(200, { success: true });
|
|
108
|
+
const result = await enrollKeyIfNeeded({
|
|
109
|
+
aisUrl: TEST_AIS_URL,
|
|
110
|
+
token: agentOnlyToken,
|
|
111
|
+
keysDir: tmpKeysDir,
|
|
112
|
+
fetchImpl,
|
|
113
|
+
});
|
|
114
|
+
expect(result.enrolled).toBe(false);
|
|
115
|
+
expect(result.reason).toBe('no-user-claim');
|
|
116
|
+
expect(requests).toHaveLength(0);
|
|
117
|
+
expect(readdirSync(tmpKeysDir)).toHaveLength(0);
|
|
118
|
+
});
|
|
119
|
+
it('treats a 409 duplicate-key response as success and keeps the key file', async () => {
|
|
120
|
+
const { fetchImpl } = stubFetch(409, {
|
|
121
|
+
success: false,
|
|
122
|
+
error: { code: 'DUPLICATE_KEY' },
|
|
123
|
+
});
|
|
124
|
+
const result = await enrollKeyIfNeeded({
|
|
125
|
+
aisUrl: TEST_AIS_URL,
|
|
126
|
+
token: OWNER_TOKEN,
|
|
127
|
+
keysDir: tmpKeysDir,
|
|
128
|
+
fetchImpl,
|
|
129
|
+
});
|
|
130
|
+
expect(result.enrolled).toBe(true);
|
|
131
|
+
expect(existsSync(join(tmpKeysDir, `${TEST_USER_DID}.json`))).toBe(true);
|
|
132
|
+
});
|
|
133
|
+
it('removes the local key file when server registration fails', async () => {
|
|
134
|
+
const { fetchImpl } = stubFetch(500, {
|
|
135
|
+
success: false,
|
|
136
|
+
error: { code: 'INTERNAL' },
|
|
137
|
+
});
|
|
138
|
+
const result = await enrollKeyIfNeeded({
|
|
139
|
+
aisUrl: TEST_AIS_URL,
|
|
140
|
+
token: OWNER_TOKEN,
|
|
141
|
+
keysDir: tmpKeysDir,
|
|
142
|
+
fetchImpl,
|
|
143
|
+
});
|
|
144
|
+
expect(result.enrolled).toBe(false);
|
|
145
|
+
expect(result.reason).toMatch(/register-failed/);
|
|
146
|
+
// Orphaned key file must not survive — it would make tryKeyAuth() throw
|
|
147
|
+
// on every future session (server has no matching public key).
|
|
148
|
+
expect(readdirSync(tmpKeysDir)).toHaveLength(0);
|
|
149
|
+
});
|
|
150
|
+
it('never throws — a network error resolves to enrolled:false and cleans up', async () => {
|
|
151
|
+
const fetchImpl = (async () => {
|
|
152
|
+
throw new Error('ECONNREFUSED');
|
|
153
|
+
});
|
|
154
|
+
const result = await enrollKeyIfNeeded({
|
|
155
|
+
aisUrl: TEST_AIS_URL,
|
|
156
|
+
token: OWNER_TOKEN,
|
|
157
|
+
keysDir: tmpKeysDir,
|
|
158
|
+
fetchImpl,
|
|
159
|
+
});
|
|
160
|
+
expect(result.enrolled).toBe(false);
|
|
161
|
+
expect(result.reason).toMatch(/register-failed/);
|
|
162
|
+
expect(readdirSync(tmpKeysDir)).toHaveLength(0);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
//# sourceMappingURL=auto-enroll-key.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-enroll-key.test.js","sourceRoot":"","sources":["../../src/__tests__/auto-enroll-key.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -26,6 +26,7 @@ describe('aismemory MCP device-flow recovery', () => {
|
|
|
26
26
|
let fakeAisServer;
|
|
27
27
|
let fakeAisPort = 0;
|
|
28
28
|
let authorizeCalls = 0;
|
|
29
|
+
let tokenPolls = 0;
|
|
29
30
|
const issuedCodes = [];
|
|
30
31
|
beforeAll(async () => {
|
|
31
32
|
fakeAisServer = createServer((req, res) => {
|
|
@@ -54,6 +55,7 @@ describe('aismemory MCP device-flow recovery', () => {
|
|
|
54
55
|
}
|
|
55
56
|
if (req.url === '/v1/oauth/token' && req.method === 'POST') {
|
|
56
57
|
// Always reject the poll so the flow dies and recovery kicks in.
|
|
58
|
+
tokenPolls += 1;
|
|
57
59
|
res.writeHead(400, { 'content-type': 'application/json' });
|
|
58
60
|
res.end(JSON.stringify({ error: 'expired_token' }));
|
|
59
61
|
return;
|
|
@@ -77,6 +79,7 @@ describe('aismemory MCP device-flow recovery', () => {
|
|
|
77
79
|
});
|
|
78
80
|
beforeEach(() => {
|
|
79
81
|
authorizeCalls = 0;
|
|
82
|
+
tokenPolls = 0;
|
|
80
83
|
issuedCodes.length = 0;
|
|
81
84
|
});
|
|
82
85
|
it('starts a fresh device flow after the previous one dies', async () => {
|
|
@@ -121,11 +124,12 @@ describe('aismemory MCP device-flow recovery', () => {
|
|
|
121
124
|
}) + '\n');
|
|
122
125
|
await waitFor(() => stdoutBuf.includes('"id":2'), 15000, 'first whoami response');
|
|
123
126
|
const firstResponse = extractResponseText(stdoutBuf, 2);
|
|
124
|
-
//
|
|
125
|
-
//
|
|
126
|
-
//
|
|
127
|
-
//
|
|
128
|
-
|
|
127
|
+
// Wait for the background poll to actually hit the token endpoint and be
|
|
128
|
+
// rejected (expired_token) — that terminal rejection is what marks the
|
|
129
|
+
// flow dead. Polling the real server-side condition replaces a fixed 8s
|
|
130
|
+
// sleep: it resolves as soon as the rejection is served (~5s, poll
|
|
131
|
+
// interval), and never waits longer than needed. (CORBOT-6A6801E6)
|
|
132
|
+
await waitFor(() => tokenPolls >= 1, 15000, 'device-flow token poll rejection');
|
|
129
133
|
// 3. Second tool call — should detect the dead flow, start a fresh
|
|
130
134
|
// device flow, and surface a DIFFERENT user_code.
|
|
131
135
|
proc.stdin.write(JSON.stringify({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"device-flow-recovery.test.js","sourceRoot":"","sources":["../../src/__tests__/device-flow-recovery.test.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"device-flow-recovery.test.js","sourceRoot":"","sources":["../../src/__tests__/device-flow-recovery.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* S2 — client-side device-poll loop tests
|
|
3
|
+
*
|
|
4
|
+
* Tests for runPollLoop() in device-poll.ts:
|
|
5
|
+
* - Returns access_token on approval
|
|
6
|
+
* - Emits heartbeat every HEARTBEAT_INTERVAL_MS during pending
|
|
7
|
+
* - When pending_reason==='page_open', heartbeat contains 'click'
|
|
8
|
+
* - On expired_token, throws DeviceFlowRestartedError (auto-restart,
|
|
9
|
+
* never throws a plain expired error)
|
|
10
|
+
* - Throws on access_denied
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* S2 — client-side device-poll loop tests
|
|
3
|
+
*
|
|
4
|
+
* Tests for runPollLoop() in device-poll.ts:
|
|
5
|
+
* - Returns access_token on approval
|
|
6
|
+
* - Emits heartbeat every HEARTBEAT_INTERVAL_MS during pending
|
|
7
|
+
* - When pending_reason==='page_open', heartbeat contains 'click'
|
|
8
|
+
* - On expired_token, throws DeviceFlowRestartedError (auto-restart,
|
|
9
|
+
* never throws a plain expired error)
|
|
10
|
+
* - Throws on access_denied
|
|
11
|
+
*/
|
|
12
|
+
import { describe, it, expect } from 'vitest';
|
|
13
|
+
import { runPollLoop, DeviceFlowRestartedError, HEARTBEAT_INTERVAL_MS, } from '../device-poll.js';
|
|
14
|
+
// ─── helpers ─────────────────────────────────────────────────────────────────
|
|
15
|
+
const HANDLE = {
|
|
16
|
+
device_code: 'test-device-code-abc123',
|
|
17
|
+
verification_uri_complete: 'https://agentsandswarms.ai/activate?user_code=BCDF-GHJK',
|
|
18
|
+
user_code: 'BCDF-GHJK',
|
|
19
|
+
};
|
|
20
|
+
const NEW_HANDLE = {
|
|
21
|
+
device_code: 'new-device-code-xyz789',
|
|
22
|
+
verification_uri_complete: 'https://agentsandswarms.ai/activate?user_code=MNPQ-RSTU',
|
|
23
|
+
user_code: 'MNPQ-RSTU',
|
|
24
|
+
};
|
|
25
|
+
function makePollOnce(impl) {
|
|
26
|
+
return impl;
|
|
27
|
+
}
|
|
28
|
+
function makeStartNewFlow(impl) {
|
|
29
|
+
return impl;
|
|
30
|
+
}
|
|
31
|
+
function makeDeps(overrides = {}) {
|
|
32
|
+
const stderrLines = [];
|
|
33
|
+
const deps = {
|
|
34
|
+
pollOnce: makePollOnce(async () => ({ access_token: 'default-token' })),
|
|
35
|
+
startNewFlow: makeStartNewFlow(async () => NEW_HANDLE),
|
|
36
|
+
emitStderr: (msg) => { stderrLines.push(msg); },
|
|
37
|
+
nowMs: () => Date.now(),
|
|
38
|
+
pollIntervalMs: 0,
|
|
39
|
+
...overrides,
|
|
40
|
+
};
|
|
41
|
+
return { deps, stderrLines };
|
|
42
|
+
}
|
|
43
|
+
// ─── tests ───────────────────────────────────────────────────────────────────
|
|
44
|
+
describe('runPollLoop()', () => {
|
|
45
|
+
describe('success path', () => {
|
|
46
|
+
it('returns the token response when access_token is present', async () => {
|
|
47
|
+
const { deps } = makeDeps({
|
|
48
|
+
pollOnce: makePollOnce(async () => ({ access_token: 'jwt-token-abc', expires_in: 604800 })),
|
|
49
|
+
});
|
|
50
|
+
const result = await runPollLoop(HANDLE, deps);
|
|
51
|
+
expect(result.access_token).toBe('jwt-token-abc');
|
|
52
|
+
expect(result.expires_in).toBe(604800);
|
|
53
|
+
});
|
|
54
|
+
it('retries on authorization_pending then succeeds', async () => {
|
|
55
|
+
let count = 0;
|
|
56
|
+
const { deps } = makeDeps({
|
|
57
|
+
pollOnce: makePollOnce(async () => {
|
|
58
|
+
count++;
|
|
59
|
+
if (count < 3)
|
|
60
|
+
return { error: 'authorization_pending', pending_reason: 'page_not_opened' };
|
|
61
|
+
return { access_token: 'approved-jwt', expires_in: 604800 };
|
|
62
|
+
}),
|
|
63
|
+
});
|
|
64
|
+
const result = await runPollLoop(HANDLE, deps);
|
|
65
|
+
expect(result.access_token).toBe('approved-jwt');
|
|
66
|
+
expect(count).toBe(3);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
describe('heartbeat behavior', () => {
|
|
70
|
+
it('emits a heartbeat line after HEARTBEAT_INTERVAL_MS elapses during pending', async () => {
|
|
71
|
+
const startTime = 1_000_000;
|
|
72
|
+
let callCount = 0;
|
|
73
|
+
const times = [
|
|
74
|
+
startTime, // deadline calc
|
|
75
|
+
startTime + HEARTBEAT_INTERVAL_MS + 1, // pending check — fires heartbeat
|
|
76
|
+
startTime + HEARTBEAT_INTERVAL_MS + 1, // deadline check
|
|
77
|
+
startTime + HEARTBEAT_INTERVAL_MS + 2,
|
|
78
|
+
];
|
|
79
|
+
let timeIdx = 0;
|
|
80
|
+
const { deps, stderrLines } = makeDeps({
|
|
81
|
+
nowMs: () => times[Math.min(timeIdx++, times.length - 1)] ?? startTime,
|
|
82
|
+
pollOnce: makePollOnce(async () => {
|
|
83
|
+
callCount++;
|
|
84
|
+
if (callCount === 1)
|
|
85
|
+
return { error: 'authorization_pending', pending_reason: 'page_not_opened' };
|
|
86
|
+
return { access_token: 'jwt-after-heartbeat', expires_in: 604800 };
|
|
87
|
+
}),
|
|
88
|
+
});
|
|
89
|
+
await runPollLoop(HANDLE, deps);
|
|
90
|
+
expect(stderrLines.some((l) => l.includes('Waiting'))).toBe(true);
|
|
91
|
+
});
|
|
92
|
+
it('emits a "click" coaching message when pending_reason is page_open', async () => {
|
|
93
|
+
const startTime = 2_000_000;
|
|
94
|
+
let callCount = 0;
|
|
95
|
+
const times = [
|
|
96
|
+
startTime,
|
|
97
|
+
startTime + HEARTBEAT_INTERVAL_MS + 1,
|
|
98
|
+
startTime + HEARTBEAT_INTERVAL_MS + 1,
|
|
99
|
+
startTime + HEARTBEAT_INTERVAL_MS + 2,
|
|
100
|
+
];
|
|
101
|
+
let timeIdx = 0;
|
|
102
|
+
const { deps, stderrLines } = makeDeps({
|
|
103
|
+
nowMs: () => times[Math.min(timeIdx++, times.length - 1)] ?? startTime,
|
|
104
|
+
pollOnce: makePollOnce(async () => {
|
|
105
|
+
callCount++;
|
|
106
|
+
if (callCount === 1)
|
|
107
|
+
return { error: 'authorization_pending', pending_reason: 'page_open' };
|
|
108
|
+
return { access_token: 'jwt-after-click', expires_in: 604800 };
|
|
109
|
+
}),
|
|
110
|
+
});
|
|
111
|
+
await runPollLoop(HANDLE, deps);
|
|
112
|
+
expect(stderrLines.some((l) => l.toLowerCase().includes('click'))).toBe(true);
|
|
113
|
+
});
|
|
114
|
+
it('does NOT emit a heartbeat on every poll (throttled)', async () => {
|
|
115
|
+
// Time frozen at 1ms: gap from lastHeartbeatAt=0 is 1ms < 30_000ms
|
|
116
|
+
let callCount = 0;
|
|
117
|
+
const { deps, stderrLines } = makeDeps({
|
|
118
|
+
nowMs: () => 1,
|
|
119
|
+
pollOnce: makePollOnce(async () => {
|
|
120
|
+
callCount++;
|
|
121
|
+
if (callCount < 5)
|
|
122
|
+
return { error: 'authorization_pending', pending_reason: 'page_not_opened' };
|
|
123
|
+
return { access_token: 'jwt-final', expires_in: 604800 };
|
|
124
|
+
}),
|
|
125
|
+
});
|
|
126
|
+
await runPollLoop(HANDLE, deps);
|
|
127
|
+
// 4 pending responses but 0 heartbeats (time never passed the interval)
|
|
128
|
+
expect(stderrLines.filter((l) => l.includes('Waiting')).length).toBe(0);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
describe('auto-restart on expired_token', () => {
|
|
132
|
+
it('throws DeviceFlowRestartedError (not a plain Error) on expired_token', async () => {
|
|
133
|
+
const { deps } = makeDeps({
|
|
134
|
+
pollOnce: makePollOnce(async () => ({ error: 'expired_token' })),
|
|
135
|
+
});
|
|
136
|
+
await expect(runPollLoop(HANDLE, deps)).rejects.toBeInstanceOf(DeviceFlowRestartedError);
|
|
137
|
+
});
|
|
138
|
+
it('starts a new flow on expired_token', async () => {
|
|
139
|
+
let startCalled = 0;
|
|
140
|
+
const { deps } = makeDeps({
|
|
141
|
+
pollOnce: makePollOnce(async () => ({ error: 'expired_token' })),
|
|
142
|
+
startNewFlow: makeStartNewFlow(async () => { startCalled++; return NEW_HANDLE; }),
|
|
143
|
+
});
|
|
144
|
+
await expect(runPollLoop(HANDLE, deps)).rejects.toThrow();
|
|
145
|
+
expect(startCalled).toBe(1);
|
|
146
|
+
});
|
|
147
|
+
it('DeviceFlowRestartedError carries the new verification URI', async () => {
|
|
148
|
+
const { deps } = makeDeps({
|
|
149
|
+
pollOnce: makePollOnce(async () => ({ error: 'expired_token' })),
|
|
150
|
+
});
|
|
151
|
+
let caught;
|
|
152
|
+
try {
|
|
153
|
+
await runPollLoop(HANDLE, deps);
|
|
154
|
+
}
|
|
155
|
+
catch (err) {
|
|
156
|
+
caught = err;
|
|
157
|
+
}
|
|
158
|
+
expect(caught).toBeInstanceOf(DeviceFlowRestartedError);
|
|
159
|
+
const restarted = caught;
|
|
160
|
+
expect(restarted.newVerificationUri).toBe(NEW_HANDLE.verification_uri_complete);
|
|
161
|
+
expect(restarted.newUserCode).toBe(NEW_HANDLE.user_code);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
describe('access_denied', () => {
|
|
165
|
+
it('throws a plain Error on access_denied (not DeviceFlowRestartedError)', async () => {
|
|
166
|
+
let startCalled = 0;
|
|
167
|
+
const { deps } = makeDeps({
|
|
168
|
+
pollOnce: makePollOnce(async () => ({ error: 'access_denied' })),
|
|
169
|
+
startNewFlow: makeStartNewFlow(async () => { startCalled++; return NEW_HANDLE; }),
|
|
170
|
+
});
|
|
171
|
+
await expect(runPollLoop(HANDLE, deps)).rejects.toThrow('denied');
|
|
172
|
+
expect(startCalled).toBe(0);
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
describe('slow_down', () => {
|
|
176
|
+
it('retries after slow_down and eventually succeeds', async () => {
|
|
177
|
+
let count = 0;
|
|
178
|
+
const { deps } = makeDeps({
|
|
179
|
+
pollOnce: makePollOnce(async () => {
|
|
180
|
+
count++;
|
|
181
|
+
if (count === 1)
|
|
182
|
+
return { error: 'slow_down' };
|
|
183
|
+
return { access_token: 'jwt-after-slowdown', expires_in: 604800 };
|
|
184
|
+
}),
|
|
185
|
+
});
|
|
186
|
+
const result = await runPollLoop(HANDLE, deps);
|
|
187
|
+
expect(result.access_token).toBe('jwt-after-slowdown');
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
//# sourceMappingURL=device-poll.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-poll.test.js","sourceRoot":"","sources":["../../src/__tests__/device-poll.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
-
import { mkdtempSync, rmSync, existsSync } from 'node:fs';
|
|
2
|
+
import { mkdtempSync, rmSync, existsSync, readFileSync } from 'node:fs';
|
|
3
3
|
import { tmpdir } from 'node:os';
|
|
4
4
|
import { join, resolve } from 'path';
|
|
5
5
|
import { describe, it, expect } from 'vitest';
|
|
6
|
+
function readPackageVersion() {
|
|
7
|
+
const packageJsonPath = resolve(__dirname, '..', '..', 'package.json');
|
|
8
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
9
|
+
if (typeof packageJson !== 'object' ||
|
|
10
|
+
packageJson === null ||
|
|
11
|
+
!('version' in packageJson) ||
|
|
12
|
+
typeof packageJson.version !== 'string' ||
|
|
13
|
+
packageJson.version.length === 0) {
|
|
14
|
+
throw new Error('Unable to load aismemory package version from package.json');
|
|
15
|
+
}
|
|
16
|
+
return packageJson.version;
|
|
17
|
+
}
|
|
6
18
|
/**
|
|
7
19
|
* Regression test for the v0.2.0/v0.3.0 bug where `npx aismemory` blocked the
|
|
8
20
|
* MCP transport handshake on user device-flow approval, exceeding Claude
|
|
@@ -64,6 +76,7 @@ describe('MCP transport boots without blocking on auth', () => {
|
|
|
64
76
|
const parsed = JSON.parse(responseLine);
|
|
65
77
|
expect(elapsed).toBeLessThan(3000);
|
|
66
78
|
expect(parsed.result?.serverInfo?.name).toBe('aismemory');
|
|
79
|
+
expect(parsed.result?.serverInfo?.version).toBe(readPackageVersion());
|
|
67
80
|
}
|
|
68
81
|
finally {
|
|
69
82
|
if (proc && !proc.killed)
|