recall-mcp-v3 3.9.1 → 3.9.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.
Files changed (157) hide show
  1. package/dist/__tests__/api-get-token.test.d.ts +18 -0
  2. package/dist/__tests__/api-get-token.test.d.ts.map +1 -0
  3. package/dist/__tests__/api-get-token.test.js +195 -0
  4. package/dist/__tests__/api-get-token.test.js.map +1 -0
  5. package/dist/__tests__/cli-help.test.d.ts +12 -0
  6. package/dist/__tests__/cli-help.test.d.ts.map +1 -0
  7. package/dist/__tests__/cli-help.test.js +70 -0
  8. package/dist/__tests__/cli-help.test.js.map +1 -0
  9. package/dist/__tests__/cli-save.test.d.ts +12 -0
  10. package/dist/__tests__/cli-save.test.d.ts.map +1 -0
  11. package/dist/__tests__/cli-save.test.js +109 -0
  12. package/dist/__tests__/cli-save.test.js.map +1 -0
  13. package/dist/__tests__/codex-transcript.test.d.ts +11 -0
  14. package/dist/__tests__/codex-transcript.test.d.ts.map +1 -0
  15. package/dist/__tests__/codex-transcript.test.js +372 -0
  16. package/dist/__tests__/codex-transcript.test.js.map +1 -0
  17. package/dist/__tests__/enrollment-isError.test.d.ts +9 -0
  18. package/dist/__tests__/enrollment-isError.test.d.ts.map +1 -0
  19. package/dist/__tests__/enrollment-isError.test.js +119 -0
  20. package/dist/__tests__/enrollment-isError.test.js.map +1 -0
  21. package/dist/__tests__/format.test.d.ts +2 -0
  22. package/dist/__tests__/format.test.d.ts.map +1 -0
  23. package/dist/__tests__/format.test.js +229 -0
  24. package/dist/__tests__/format.test.js.map +1 -0
  25. package/dist/__tests__/gemini-transcript.test.d.ts +12 -0
  26. package/dist/__tests__/gemini-transcript.test.d.ts.map +1 -0
  27. package/dist/__tests__/gemini-transcript.test.js +322 -0
  28. package/dist/__tests__/gemini-transcript.test.js.map +1 -0
  29. package/dist/__tests__/get-context-repo-resolution.test.d.ts +11 -0
  30. package/dist/__tests__/get-context-repo-resolution.test.d.ts.map +1 -0
  31. package/dist/__tests__/get-context-repo-resolution.test.js +310 -0
  32. package/dist/__tests__/get-context-repo-resolution.test.js.map +1 -0
  33. package/dist/__tests__/integration.test.d.ts +11 -0
  34. package/dist/__tests__/integration.test.d.ts.map +1 -0
  35. package/dist/__tests__/integration.test.js +323 -0
  36. package/dist/__tests__/integration.test.js.map +1 -0
  37. package/dist/__tests__/no-git-repo.test.d.ts +9 -0
  38. package/dist/__tests__/no-git-repo.test.d.ts.map +1 -0
  39. package/dist/__tests__/no-git-repo.test.js +125 -0
  40. package/dist/__tests__/no-git-repo.test.js.map +1 -0
  41. package/dist/__tests__/preprocessor.test.d.ts +12 -0
  42. package/dist/__tests__/preprocessor.test.d.ts.map +1 -0
  43. package/dist/__tests__/preprocessor.test.js +690 -0
  44. package/dist/__tests__/preprocessor.test.js.map +1 -0
  45. package/dist/__tests__/repo-detection-guard.test.d.ts +9 -0
  46. package/dist/__tests__/repo-detection-guard.test.d.ts.map +1 -0
  47. package/dist/__tests__/repo-detection-guard.test.js +153 -0
  48. package/dist/__tests__/repo-detection-guard.test.js.map +1 -0
  49. package/dist/__tests__/repo-enrollment.test.d.ts +9 -0
  50. package/dist/__tests__/repo-enrollment.test.d.ts.map +1 -0
  51. package/dist/__tests__/repo-enrollment.test.js +107 -0
  52. package/dist/__tests__/repo-enrollment.test.js.map +1 -0
  53. package/dist/__tests__/repo-override-save-log.test.d.ts +11 -0
  54. package/dist/__tests__/repo-override-save-log.test.d.ts.map +1 -0
  55. package/dist/__tests__/repo-override-save-log.test.js +352 -0
  56. package/dist/__tests__/repo-override-save-log.test.js.map +1 -0
  57. package/dist/api.d.ts +353 -0
  58. package/dist/api.d.ts.map +1 -0
  59. package/dist/api.js +727 -0
  60. package/dist/api.js.map +1 -0
  61. package/dist/auth/__tests__/auth-store.test.d.ts +6 -0
  62. package/dist/auth/__tests__/auth-store.test.d.ts.map +1 -0
  63. package/dist/auth/__tests__/auth-store.test.js +161 -0
  64. package/dist/auth/__tests__/auth-store.test.js.map +1 -0
  65. package/dist/auth/__tests__/device-client.test.d.ts +10 -0
  66. package/dist/auth/__tests__/device-client.test.d.ts.map +1 -0
  67. package/dist/auth/__tests__/device-client.test.js +327 -0
  68. package/dist/auth/__tests__/device-client.test.js.map +1 -0
  69. package/dist/auth/__tests__/heartbeat.test.d.ts +12 -0
  70. package/dist/auth/__tests__/heartbeat.test.d.ts.map +1 -0
  71. package/dist/auth/__tests__/heartbeat.test.js +140 -0
  72. package/dist/auth/__tests__/heartbeat.test.js.map +1 -0
  73. package/dist/auth/__tests__/preflight.test.d.ts +2 -0
  74. package/dist/auth/__tests__/preflight.test.d.ts.map +1 -0
  75. package/dist/auth/__tests__/preflight.test.js +37 -0
  76. package/dist/auth/__tests__/preflight.test.js.map +1 -0
  77. package/dist/auth/__tests__/run-auth.test.d.ts +11 -0
  78. package/dist/auth/__tests__/run-auth.test.d.ts.map +1 -0
  79. package/dist/auth/__tests__/run-auth.test.js +387 -0
  80. package/dist/auth/__tests__/run-auth.test.js.map +1 -0
  81. package/dist/auth/__tests__/tool-detection.test.d.ts +10 -0
  82. package/dist/auth/__tests__/tool-detection.test.d.ts.map +1 -0
  83. package/dist/auth/__tests__/tool-detection.test.js +122 -0
  84. package/dist/auth/__tests__/tool-detection.test.js.map +1 -0
  85. package/dist/auth/__tests__/tool-patching.test.d.ts +13 -0
  86. package/dist/auth/__tests__/tool-patching.test.d.ts.map +1 -0
  87. package/dist/auth/__tests__/tool-patching.test.js +385 -0
  88. package/dist/auth/__tests__/tool-patching.test.js.map +1 -0
  89. package/dist/auth/auth-store.d.ts +61 -0
  90. package/dist/auth/auth-store.d.ts.map +1 -0
  91. package/dist/auth/auth-store.js +158 -0
  92. package/dist/auth/auth-store.js.map +1 -0
  93. package/dist/auth/device-client.d.ts +85 -0
  94. package/dist/auth/device-client.d.ts.map +1 -0
  95. package/dist/auth/device-client.js +269 -0
  96. package/dist/auth/device-client.js.map +1 -0
  97. package/dist/auth/heartbeat.d.ts +51 -0
  98. package/dist/auth/heartbeat.d.ts.map +1 -0
  99. package/dist/auth/heartbeat.js +79 -0
  100. package/dist/auth/heartbeat.js.map +1 -0
  101. package/dist/auth/preflight.d.ts +10 -0
  102. package/dist/auth/preflight.d.ts.map +1 -0
  103. package/dist/auth/preflight.js +35 -0
  104. package/dist/auth/preflight.js.map +1 -0
  105. package/dist/auth/run-auth.d.ts +56 -0
  106. package/dist/auth/run-auth.d.ts.map +1 -0
  107. package/dist/auth/run-auth.js +288 -0
  108. package/dist/auth/run-auth.js.map +1 -0
  109. package/dist/auth/tool-detection.d.ts +47 -0
  110. package/dist/auth/tool-detection.d.ts.map +1 -0
  111. package/dist/auth/tool-detection.js +99 -0
  112. package/dist/auth/tool-detection.js.map +1 -0
  113. package/dist/auth/tool-patching.d.ts +85 -0
  114. package/dist/auth/tool-patching.d.ts.map +1 -0
  115. package/dist/auth/tool-patching.js +353 -0
  116. package/dist/auth/tool-patching.js.map +1 -0
  117. package/dist/cli.d.ts +32 -0
  118. package/dist/cli.d.ts.map +1 -0
  119. package/dist/cli.js +474 -0
  120. package/dist/cli.js.map +1 -0
  121. package/dist/codex-transcript.d.ts +31 -0
  122. package/dist/codex-transcript.d.ts.map +1 -0
  123. package/dist/codex-transcript.js +304 -0
  124. package/dist/codex-transcript.js.map +1 -0
  125. package/dist/crypto.d.ts +36 -0
  126. package/dist/crypto.d.ts.map +1 -0
  127. package/dist/crypto.js +104 -0
  128. package/dist/crypto.js.map +1 -0
  129. package/dist/detect.d.ts +40 -0
  130. package/dist/detect.d.ts.map +1 -0
  131. package/dist/detect.js +165 -0
  132. package/dist/detect.js.map +1 -0
  133. package/dist/format.d.ts +53 -0
  134. package/dist/format.d.ts.map +1 -0
  135. package/dist/format.js +177 -0
  136. package/dist/format.js.map +1 -0
  137. package/dist/gemini-transcript.d.ts +29 -0
  138. package/dist/gemini-transcript.d.ts.map +1 -0
  139. package/dist/gemini-transcript.js +303 -0
  140. package/dist/gemini-transcript.js.map +1 -0
  141. package/dist/index.d.ts +12 -0
  142. package/dist/index.d.ts.map +1 -0
  143. package/dist/index.js +291 -0
  144. package/dist/index.js.map +1 -0
  145. package/dist/preprocessor.d.ts +39 -0
  146. package/dist/preprocessor.d.ts.map +1 -0
  147. package/dist/preprocessor.js +514 -0
  148. package/dist/preprocessor.js.map +1 -0
  149. package/dist/tools.d.ts +106 -0
  150. package/dist/tools.d.ts.map +1 -0
  151. package/dist/tools.js +784 -0
  152. package/dist/tools.js.map +1 -0
  153. package/dist/transcript-finder.d.ts +27 -0
  154. package/dist/transcript-finder.d.ts.map +1 -0
  155. package/dist/transcript-finder.js +143 -0
  156. package/dist/transcript-finder.js.map +1 -0
  157. package/package.json +3 -2
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Tests for the `getToken()` resolution in api.ts.
3
+ *
4
+ * Exercises:
5
+ * - Auth store wins over env var when both are set.
6
+ * - Env var used as fallback when auth store absent.
7
+ * - Corrupt auth store logs once + falls through to env (does not throw).
8
+ * - Missing both paths throws with an actionable error.
9
+ * - Result is memoized: subsequent calls don't re-read the filesystem.
10
+ * - "No token found" is also memoized so we don't hammer stderr on retries.
11
+ *
12
+ * `getToken` itself is not exported, but every request-making function calls
13
+ * it before issuing a fetch. We use `saveSession`'s code path by mocking the
14
+ * network layer — any call that reaches `fetch` has already resolved the
15
+ * token. The `__resetTokenCache` export is the test seam.
16
+ */
17
+ export {};
18
+ //# sourceMappingURL=api-get-token.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-get-token.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/api-get-token.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG"}
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Tests for the `getToken()` resolution in api.ts.
3
+ *
4
+ * Exercises:
5
+ * - Auth store wins over env var when both are set.
6
+ * - Env var used as fallback when auth store absent.
7
+ * - Corrupt auth store logs once + falls through to env (does not throw).
8
+ * - Missing both paths throws with an actionable error.
9
+ * - Result is memoized: subsequent calls don't re-read the filesystem.
10
+ * - "No token found" is also memoized so we don't hammer stderr on retries.
11
+ *
12
+ * `getToken` itself is not exported, but every request-making function calls
13
+ * it before issuing a fetch. We use `saveSession`'s code path by mocking the
14
+ * network layer — any call that reaches `fetch` has already resolved the
15
+ * token. The `__resetTokenCache` export is the test seam.
16
+ */
17
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
18
+ import * as fs from 'node:fs';
19
+ import * as os from 'node:os';
20
+ import * as path from 'node:path';
21
+ import { __resetTokenCache, getTokenSource, RecallApiError } from '../api.js';
22
+ import { writeAuthStore } from '../auth/auth-store.js';
23
+ // ---------------------------------------------------------------------------
24
+ // Fixture: the encryption layer and /v1/keys/team endpoint are mocked so we
25
+ // can isolate the token-resolution behavior from everything downstream.
26
+ // ---------------------------------------------------------------------------
27
+ vi.mock('../crypto.js', () => ({
28
+ encrypt: vi.fn().mockReturnValue('encrypted'),
29
+ decrypt: vi.fn().mockReturnValue('decrypted'),
30
+ isEncrypted: vi.fn().mockReturnValue(false),
31
+ safeDecrypt: vi.fn().mockReturnValue(''),
32
+ normalizeToString: vi.fn((s) => s),
33
+ }));
34
+ // ---------------------------------------------------------------------------
35
+ // readAuthStoreSync resolves its path via process.env at call time, so each
36
+ // test points RECALL_AUTH_DIR at its own tmp dir and resets the cache.
37
+ // ---------------------------------------------------------------------------
38
+ let tmp;
39
+ let originalEnv;
40
+ beforeEach(() => {
41
+ tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'recall-gettoken-'));
42
+ originalEnv = {
43
+ token: process.env.RECALL_API_TOKEN,
44
+ authDir: process.env.RECALL_AUTH_DIR,
45
+ };
46
+ // Start each test with a clean slate.
47
+ delete process.env.RECALL_API_TOKEN;
48
+ process.env.RECALL_AUTH_DIR = path.join(tmp, 'recall');
49
+ __resetTokenCache();
50
+ vi.restoreAllMocks();
51
+ });
52
+ afterEach(() => {
53
+ if (originalEnv.token !== undefined) {
54
+ process.env.RECALL_API_TOKEN = originalEnv.token;
55
+ }
56
+ else {
57
+ delete process.env.RECALL_API_TOKEN;
58
+ }
59
+ if (originalEnv.authDir !== undefined) {
60
+ process.env.RECALL_AUTH_DIR = originalEnv.authDir;
61
+ }
62
+ else {
63
+ delete process.env.RECALL_AUTH_DIR;
64
+ }
65
+ fs.rmSync(tmp, { recursive: true, force: true });
66
+ __resetTokenCache();
67
+ });
68
+ async function writeStore(token) {
69
+ await writeAuthStore({
70
+ token,
71
+ issued_at: '2026-04-23T00:00:00.000Z',
72
+ api_base: 'https://api-v3.recall.team',
73
+ client_version: '3.9.0',
74
+ }, process.env);
75
+ }
76
+ // Helper to force a call through getToken + observe the token it attached
77
+ // to the request. We shim global fetch so no real network call happens.
78
+ async function captureTokenViaFetch() {
79
+ const fetchSpy = vi
80
+ .spyOn(globalThis, 'fetch')
81
+ .mockImplementation(() => Promise.resolve(new Response(JSON.stringify({
82
+ hasAccess: true,
83
+ encryptionKey: 'k',
84
+ keyVersion: 1,
85
+ teamId: 't',
86
+ teamName: 'tt',
87
+ teamSlug: 'ts',
88
+ tier: 'free',
89
+ }), { status: 200, headers: { 'Content-Type': 'application/json' } })));
90
+ const { getTeamKey, clearKeyCache } = await import('../api.js');
91
+ clearKeyCache();
92
+ try {
93
+ await getTeamKey();
94
+ }
95
+ catch {
96
+ // If getToken throws NO_TOKEN the fetch was never called — that's valid
97
+ // in the "no token found" test which asserts by catching the error.
98
+ }
99
+ const call = fetchSpy.mock.calls[0];
100
+ if (!call)
101
+ return null;
102
+ const init = call[1];
103
+ const auth = init?.headers?.['Authorization'];
104
+ return auth?.replace(/^Bearer\s+/, '') ?? null;
105
+ }
106
+ // ---------------------------------------------------------------------------
107
+ // Tests
108
+ // ---------------------------------------------------------------------------
109
+ describe('getToken: auth store precedence', () => {
110
+ it('prefers the auth store over RECALL_API_TOKEN when both are set', async () => {
111
+ await writeStore('rcl_from_store');
112
+ process.env.RECALL_API_TOKEN = 'rcl_from_env';
113
+ const tokenUsed = await captureTokenViaFetch();
114
+ expect(tokenUsed).toBe('rcl_from_store');
115
+ expect(getTokenSource()).toBe('auth_store');
116
+ });
117
+ it('falls back to RECALL_API_TOKEN when the auth store is absent', async () => {
118
+ process.env.RECALL_API_TOKEN = 'rcl_from_env_only';
119
+ const tokenUsed = await captureTokenViaFetch();
120
+ expect(tokenUsed).toBe('rcl_from_env_only');
121
+ expect(getTokenSource()).toBe('env');
122
+ });
123
+ it('uses the auth store even when the env var is empty string', async () => {
124
+ await writeStore('rcl_store_only');
125
+ process.env.RECALL_API_TOKEN = '';
126
+ const tokenUsed = await captureTokenViaFetch();
127
+ expect(tokenUsed).toBe('rcl_store_only');
128
+ expect(getTokenSource()).toBe('auth_store');
129
+ });
130
+ });
131
+ describe('getToken: error handling', () => {
132
+ it('throws NO_TOKEN when neither store nor env is present', async () => {
133
+ const { getTeamKey, clearKeyCache } = await import('../api.js');
134
+ clearKeyCache();
135
+ await expect(getTeamKey()).rejects.toMatchObject({
136
+ name: 'RecallApiError',
137
+ code: 'NO_TOKEN',
138
+ status: 401,
139
+ });
140
+ });
141
+ it('the NO_TOKEN error message suggests both `auth` and the env var', async () => {
142
+ const { getTeamKey, clearKeyCache } = await import('../api.js');
143
+ clearKeyCache();
144
+ await expect(getTeamKey()).rejects.toThrow(/recall-mcp-v3 auth/);
145
+ await expect(getTeamKey()).rejects.toThrow(/RECALL_API_TOKEN/);
146
+ });
147
+ it('falls through to env on a corrupt auth store and logs once', async () => {
148
+ const dir = path.join(tmp, 'recall');
149
+ fs.mkdirSync(dir, { recursive: true });
150
+ fs.writeFileSync(path.join(dir, 'auth.json'), 'not valid json {{{');
151
+ process.env.RECALL_API_TOKEN = 'rcl_env_rescue';
152
+ const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
153
+ const tokenUsed = await captureTokenViaFetch();
154
+ expect(tokenUsed).toBe('rcl_env_rescue');
155
+ expect(getTokenSource()).toBe('env');
156
+ expect(consoleSpy).toHaveBeenCalledTimes(1);
157
+ expect(consoleSpy.mock.calls[0][0]).toMatch(/auth store/i);
158
+ });
159
+ it('corrupt store + no env throws NO_TOKEN but still only logs the corruption once', async () => {
160
+ const dir = path.join(tmp, 'recall');
161
+ fs.mkdirSync(dir, { recursive: true });
162
+ fs.writeFileSync(path.join(dir, 'auth.json'), '{corrupt');
163
+ const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
164
+ const { getTeamKey, clearKeyCache } = await import('../api.js');
165
+ clearKeyCache();
166
+ await expect(getTeamKey()).rejects.toBeInstanceOf(RecallApiError);
167
+ // Second call should NOT re-log: the "no token" outcome is memoized.
168
+ await expect(getTeamKey()).rejects.toBeInstanceOf(RecallApiError);
169
+ expect(consoleSpy).toHaveBeenCalledTimes(1);
170
+ });
171
+ });
172
+ describe('getToken: memoization', () => {
173
+ it('does not re-read the auth store on subsequent calls', async () => {
174
+ // Observation: delete the auth store after the first call. If the token
175
+ // cache is working, the second call still returns the first token; if
176
+ // the second call re-read the file, it would fall through to env (unset)
177
+ // and throw NO_TOKEN.
178
+ await writeStore('rcl_memo_test');
179
+ const first = await captureTokenViaFetch();
180
+ expect(first).toBe('rcl_memo_test');
181
+ fs.rmSync(path.join(tmp, 'recall'), { recursive: true, force: true });
182
+ const second = await captureTokenViaFetch();
183
+ expect(second).toBe('rcl_memo_test');
184
+ });
185
+ it('__resetTokenCache forces a fresh read (test seam works)', async () => {
186
+ await writeStore('rcl_first');
187
+ expect(await captureTokenViaFetch()).toBe('rcl_first');
188
+ await writeStore('rcl_second');
189
+ // Without reset, the old token is memoized.
190
+ expect(await captureTokenViaFetch()).toBe('rcl_first');
191
+ __resetTokenCache();
192
+ expect(await captureTokenViaFetch()).toBe('rcl_second');
193
+ });
194
+ });
195
+ //# sourceMappingURL=api-get-token.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-get-token.test.js","sourceRoot":"","sources":["../../src/__tests__/api-get-token.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Tests for subcommand --help short-circuit (W1 fix).
3
+ *
4
+ * Bug: `recall-mcp-v3 auth --help` falls into `case 'auth':` and calls
5
+ * runAuth() — minting a token, writing auth.json, patching tool configs.
6
+ * Help should never mutate local state.
7
+ *
8
+ * These tests pin: --help / -h on a subcommand prints help and does NOT
9
+ * invoke the subcommand's side-effecting code.
10
+ */
11
+ export {};
12
+ //# sourceMappingURL=cli-help.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-help.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/cli-help.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Tests for subcommand --help short-circuit (W1 fix).
3
+ *
4
+ * Bug: `recall-mcp-v3 auth --help` falls into `case 'auth':` and calls
5
+ * runAuth() — minting a token, writing auth.json, patching tool configs.
6
+ * Help should never mutate local state.
7
+ *
8
+ * These tests pin: --help / -h on a subcommand prints help and does NOT
9
+ * invoke the subcommand's side-effecting code.
10
+ */
11
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
12
+ // Mock runAuth so we can assert it's never called for --help paths.
13
+ // vi.mock is hoisted; the factory must NOT reference outer-scope variables.
14
+ vi.mock('../auth/run-auth.js', () => ({
15
+ runAuth: vi.fn(async () => 0),
16
+ }));
17
+ import { runCli } from '../cli.js';
18
+ import { runAuth } from '../auth/run-auth.js';
19
+ const runAuthMock = vi.mocked(runAuth);
20
+ describe('runCli — subcommand --help short-circuit', () => {
21
+ // Type annotations omitted: vi.spyOn return types are narrower than the
22
+ // generic ReturnType<typeof vi.spyOn> and TS strict mode rejects them.
23
+ let exitSpy;
24
+ let logSpy;
25
+ beforeEach(() => {
26
+ runAuthMock.mockClear();
27
+ runAuthMock.mockResolvedValue(0);
28
+ // Prevent process.exit from killing the test process. Capture the code.
29
+ exitSpy = vi
30
+ .spyOn(process, 'exit')
31
+ .mockImplementation(((_code) => undefined));
32
+ logSpy = vi.spyOn(console, 'log').mockImplementation(() => undefined);
33
+ });
34
+ afterEach(() => {
35
+ exitSpy.mockRestore();
36
+ logSpy.mockRestore();
37
+ });
38
+ it('"auth --help" prints auth help and does NOT call runAuth', async () => {
39
+ await runCli(['auth', '--help']);
40
+ expect(runAuthMock).not.toHaveBeenCalled();
41
+ const printed = logSpy.mock.calls.map((c) => String(c[0])).join('\n');
42
+ expect(printed).toMatch(/Usage:\s+recall-mcp-v3 auth/);
43
+ expect(printed).toMatch(/Authorize this machine/i);
44
+ // Specifically reassures the user that --help is safe.
45
+ expect(printed).toMatch(/-h, --help/);
46
+ });
47
+ it('"auth -h" behaves the same as --help', async () => {
48
+ await runCli(['auth', '-h']);
49
+ expect(runAuthMock).not.toHaveBeenCalled();
50
+ const printed = logSpy.mock.calls.map((c) => String(c[0])).join('\n');
51
+ expect(printed).toMatch(/Usage:\s+recall-mcp-v3 auth/);
52
+ });
53
+ it('"save-transcript --help" prints save-transcript help and does NOT save', async () => {
54
+ await runCli(['save-transcript', '--help']);
55
+ const printed = logSpy.mock.calls.map((c) => String(c[0])).join('\n');
56
+ expect(printed).toMatch(/Usage:\s+recall-mcp-v3 save-transcript/);
57
+ expect(printed).toMatch(/--file/);
58
+ });
59
+ it('"enrich-session --help" prints enrich-session help and does NOT enrich', async () => {
60
+ await runCli(['enrich-session', '--help']);
61
+ const printed = logSpy.mock.calls.map((c) => String(c[0])).join('\n');
62
+ expect(printed).toMatch(/Usage:\s+recall-mcp-v3 enrich-session/);
63
+ expect(printed).toMatch(/--session-id/);
64
+ });
65
+ it('"auth" with no flags still invokes runAuth (regression guard)', async () => {
66
+ await runCli(['auth']);
67
+ expect(runAuthMock).toHaveBeenCalledOnce();
68
+ });
69
+ });
70
+ //# sourceMappingURL=cli-help.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-help.test.js","sourceRoot":"","sources":["../../src/__tests__/cli-help.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Tests for CLI save pipeline alignment (Phase 4).
3
+ *
4
+ * Verifies that the SessionEnd hook (cli.ts saveTranscript) uses the same
5
+ * server-side pipeline as the MCP recall_save_session tool:
6
+ * JSONL transcript → preprocess → api.resolveRepo → api.saveSessionAsync
7
+ *
8
+ * NOT the old path:
9
+ * JSONL → preprocess → /v1/summarize (sync) → encrypt client-side → POST /sessions
10
+ */
11
+ export {};
12
+ //# sourceMappingURL=cli-save.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-save.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/cli-save.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Tests for CLI save pipeline alignment (Phase 4).
3
+ *
4
+ * Verifies that the SessionEnd hook (cli.ts saveTranscript) uses the same
5
+ * server-side pipeline as the MCP recall_save_session tool:
6
+ * JSONL transcript → preprocess → api.resolveRepo → api.saveSessionAsync
7
+ *
8
+ * NOT the old path:
9
+ * JSONL → preprocess → /v1/summarize (sync) → encrypt client-side → POST /sessions
10
+ */
11
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
12
+ // Mock api module - this is what cli.ts uses
13
+ vi.mock('../api.js', () => ({
14
+ resolveRepo: vi.fn().mockResolvedValue({ repoId: 'repo-123', teamId: 'team-456' }),
15
+ saveSessionAsync: vi.fn().mockResolvedValue({ id: 'session-789', created_at: '2026-02-09' }),
16
+ }));
17
+ // Mock fs for transcript reading
18
+ vi.mock('fs', async (importOriginal) => {
19
+ const actual = await importOriginal();
20
+ return {
21
+ ...actual,
22
+ readFileSync: vi.fn().mockReturnValue('{"type":"message","role":"user"}\n{"type":"message","role":"assistant"}'),
23
+ existsSync: vi.fn().mockReturnValue(true),
24
+ readdirSync: vi.fn().mockReturnValue([]),
25
+ };
26
+ });
27
+ // Mock child_process for git commands
28
+ vi.mock('child_process', () => ({
29
+ execSync: vi.fn((cmd) => {
30
+ if (cmd.includes('remote.origin.url'))
31
+ return 'https://github.com/testorg/testrepo.git\n';
32
+ if (cmd.includes('rev-parse --abbrev-ref'))
33
+ return 'main\n';
34
+ return '';
35
+ }),
36
+ }));
37
+ // Mock preprocessor - content must be >= 100 chars (cli.ts threshold)
38
+ vi.mock('../preprocessor.js', () => ({
39
+ preprocessTranscript: vi.fn().mockResolvedValue({
40
+ content: '## Assistant\nFixed the login bug by updating the auth middleware. The root cause was an expired JWT token check that was comparing timestamps incorrectly. Updated the comparison logic and added tests.',
41
+ stats: { originalSize: 5000, processedSize: 500, messagesExtracted: 10, errorsFound: 0, toolCallsLogged: 3 },
42
+ }),
43
+ }));
44
+ // Mock format
45
+ vi.mock('../format.js', () => ({
46
+ extractPreliminarySummary: vi.fn().mockReturnValue('Fixed login bug'),
47
+ }));
48
+ import { saveTranscript } from '../cli.js';
49
+ import * as api from '../api.js';
50
+ describe('CLI saveTranscript pipeline alignment', () => {
51
+ beforeEach(() => {
52
+ vi.clearAllMocks();
53
+ process.env.RECALL_API_TOKEN = 'rcl_test_token_12345';
54
+ process.env.RECALL_API_URL = 'https://api-v3.recall.team';
55
+ });
56
+ it('should call api.saveSessionAsync (the async pipeline)', async () => {
57
+ await saveTranscript({
58
+ transcriptPath: '/test/transcript.jsonl',
59
+ cwd: '/test/project',
60
+ quiet: true,
61
+ });
62
+ // Verify saveSessionAsync was called (same pipeline as typing save)
63
+ expect(api.saveSessionAsync).toHaveBeenCalledOnce();
64
+ expect(api.saveSessionAsync).toHaveBeenCalledWith('repo-123', expect.any(String), expect.objectContaining({
65
+ tool: 'claude-code',
66
+ }));
67
+ });
68
+ it('should send preprocessed transcript content to saveSessionAsync', async () => {
69
+ await saveTranscript({
70
+ transcriptPath: '/test/transcript.jsonl',
71
+ cwd: '/test/project',
72
+ quiet: true,
73
+ });
74
+ // The second arg to saveSessionAsync is the transcript content
75
+ const transcript = api.saveSessionAsync.mock.calls[0]?.[1];
76
+ expect(transcript).toBeDefined();
77
+ expect(typeof transcript).toBe('string');
78
+ expect(transcript.length).toBeGreaterThan(0);
79
+ // Should be the preprocessed content, not raw JSONL
80
+ expect(transcript).toContain('Fixed the login bug');
81
+ expect(transcript).toContain('expired JWT token');
82
+ });
83
+ it('should call api.resolveRepo before saving', async () => {
84
+ await saveTranscript({
85
+ transcriptPath: '/test/transcript.jsonl',
86
+ cwd: '/test/project',
87
+ quiet: true,
88
+ });
89
+ // Verify resolveRepo was called with extracted git info
90
+ expect(api.resolveRepo).toHaveBeenCalledOnce();
91
+ expect(api.resolveRepo).toHaveBeenCalledWith('testorg/testrepo', 'main');
92
+ });
93
+ it('should NOT use old summarize-then-encrypt path', async () => {
94
+ // Mock global fetch to detect any direct API calls
95
+ const mockFetch = vi.fn();
96
+ vi.stubGlobal('fetch', mockFetch);
97
+ await saveTranscript({
98
+ transcriptPath: '/test/transcript.jsonl',
99
+ cwd: '/test/project',
100
+ quiet: true,
101
+ });
102
+ // cli.ts should NOT call fetch directly - everything goes through api module
103
+ // No calls to /v1/summarize or direct /sessions POST
104
+ const summarizeCall = mockFetch.mock.calls.find((call) => typeof call[0] === 'string' && call[0].includes('/v1/summarize'));
105
+ expect(summarizeCall).toBeUndefined();
106
+ vi.unstubAllGlobals();
107
+ });
108
+ });
109
+ //# sourceMappingURL=cli-save.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-save.test.js","sourceRoot":"","sources":["../../src/__tests__/cli-save.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Tests for Codex Transcript Finder & Preprocessor
3
+ *
4
+ * Verifies:
5
+ * 1. CWD matching from session_meta
6
+ * 2. Noise filtering (event_msg, developer messages, session_meta, turn_context)
7
+ * 3. Signal preservation (user, assistant, reasoning, function_call, function_call_output)
8
+ * 4. Output format is clean markdown with no YAML
9
+ */
10
+ export {};
11
+ //# sourceMappingURL=codex-transcript.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex-transcript.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/codex-transcript.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}