forkoff 1.0.17 → 1.0.19

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 (158) hide show
  1. package/LICENSE +11 -7
  2. package/README.md +77 -118
  3. package/dist/approval.d.ts +1 -0
  4. package/dist/approval.js +9 -0
  5. package/dist/config.d.ts +3 -0
  6. package/dist/config.js +62 -16
  7. package/dist/crypto/e2eeManager.d.ts +49 -52
  8. package/dist/crypto/e2eeManager.js +256 -181
  9. package/dist/crypto/encryption.d.ts +8 -10
  10. package/dist/crypto/encryption.js +29 -94
  11. package/dist/crypto/index.d.ts +10 -0
  12. package/dist/crypto/index.js +22 -0
  13. package/dist/crypto/keyExchange.d.ts +6 -20
  14. package/dist/crypto/keyExchange.js +18 -110
  15. package/dist/crypto/keyGeneration.d.ts +2 -13
  16. package/dist/crypto/keyGeneration.js +14 -88
  17. package/dist/crypto/keyStorage.d.ts +32 -5
  18. package/dist/crypto/keyStorage.js +152 -8
  19. package/dist/crypto/sessionPersistence.d.ts +7 -13
  20. package/dist/crypto/sessionPersistence.js +108 -33
  21. package/dist/crypto/types.d.ts +24 -3
  22. package/dist/crypto/types.js +2 -1
  23. package/dist/crypto/websocketE2EE.d.ts +6 -17
  24. package/dist/crypto/websocketE2EE.js +21 -38
  25. package/dist/index.js +203 -280
  26. package/dist/integration.d.ts +0 -1
  27. package/dist/integration.js +2 -4
  28. package/dist/logger.d.ts +15 -0
  29. package/dist/logger.js +209 -1
  30. package/dist/server.d.ts +30 -0
  31. package/dist/server.js +162 -0
  32. package/dist/startup.js +15 -6
  33. package/dist/terminal.d.ts +1 -0
  34. package/dist/terminal.js +94 -1
  35. package/dist/tools/claude-process.d.ts +8 -0
  36. package/dist/tools/claude-process.js +199 -26
  37. package/dist/tools/claude-sessions.d.ts +1 -0
  38. package/dist/tools/claude-sessions.js +36 -10
  39. package/dist/tools/detector.js +11 -3
  40. package/dist/tools/permission-hook.js +94 -27
  41. package/dist/tools/permission-ipc.d.ts +1 -0
  42. package/dist/tools/permission-ipc.js +61 -14
  43. package/dist/transcript-streamer.d.ts +1 -0
  44. package/dist/transcript-streamer.js +18 -4
  45. package/dist/usage-tracker.d.ts +45 -0
  46. package/dist/usage-tracker.js +243 -0
  47. package/dist/websocket.d.ts +43 -12
  48. package/dist/websocket.js +418 -214
  49. package/package.json +5 -4
  50. package/dist/__tests__/cli-commands.test.d.ts +0 -6
  51. package/dist/__tests__/cli-commands.test.d.ts.map +0 -1
  52. package/dist/__tests__/cli-commands.test.js +0 -213
  53. package/dist/__tests__/cli-commands.test.js.map +0 -1
  54. package/dist/__tests__/crypto/e2e-integration.test.d.ts +0 -17
  55. package/dist/__tests__/crypto/e2e-integration.test.d.ts.map +0 -1
  56. package/dist/__tests__/crypto/e2e-integration.test.js +0 -338
  57. package/dist/__tests__/crypto/e2e-integration.test.js.map +0 -1
  58. package/dist/__tests__/crypto/e2eeManager.test.d.ts +0 -2
  59. package/dist/__tests__/crypto/e2eeManager.test.d.ts.map +0 -1
  60. package/dist/__tests__/crypto/e2eeManager.test.js +0 -242
  61. package/dist/__tests__/crypto/e2eeManager.test.js.map +0 -1
  62. package/dist/__tests__/crypto/encryption.test.d.ts +0 -2
  63. package/dist/__tests__/crypto/encryption.test.d.ts.map +0 -1
  64. package/dist/__tests__/crypto/encryption.test.js +0 -116
  65. package/dist/__tests__/crypto/encryption.test.js.map +0 -1
  66. package/dist/__tests__/crypto/keyExchange.test.d.ts +0 -2
  67. package/dist/__tests__/crypto/keyExchange.test.d.ts.map +0 -1
  68. package/dist/__tests__/crypto/keyExchange.test.js +0 -84
  69. package/dist/__tests__/crypto/keyExchange.test.js.map +0 -1
  70. package/dist/__tests__/crypto/keyGeneration.test.d.ts +0 -2
  71. package/dist/__tests__/crypto/keyGeneration.test.d.ts.map +0 -1
  72. package/dist/__tests__/crypto/keyGeneration.test.js +0 -61
  73. package/dist/__tests__/crypto/keyGeneration.test.js.map +0 -1
  74. package/dist/__tests__/crypto/keyStorage.test.d.ts +0 -2
  75. package/dist/__tests__/crypto/keyStorage.test.d.ts.map +0 -1
  76. package/dist/__tests__/crypto/keyStorage.test.js +0 -133
  77. package/dist/__tests__/crypto/keyStorage.test.js.map +0 -1
  78. package/dist/__tests__/crypto/websocketIntegration.test.d.ts +0 -2
  79. package/dist/__tests__/crypto/websocketIntegration.test.d.ts.map +0 -1
  80. package/dist/__tests__/crypto/websocketIntegration.test.js +0 -259
  81. package/dist/__tests__/crypto/websocketIntegration.test.js.map +0 -1
  82. package/dist/__tests__/startup.test.d.ts +0 -11
  83. package/dist/__tests__/startup.test.d.ts.map +0 -1
  84. package/dist/__tests__/startup.test.js +0 -241
  85. package/dist/__tests__/startup.test.js.map +0 -1
  86. package/dist/__tests__/tools/claude-process.test.d.ts +0 -8
  87. package/dist/__tests__/tools/claude-process.test.d.ts.map +0 -1
  88. package/dist/__tests__/tools/claude-process.test.js +0 -430
  89. package/dist/__tests__/tools/claude-process.test.js.map +0 -1
  90. package/dist/__tests__/tools/permission-hook.test.d.ts +0 -17
  91. package/dist/__tests__/tools/permission-hook.test.d.ts.map +0 -1
  92. package/dist/__tests__/tools/permission-hook.test.js +0 -616
  93. package/dist/__tests__/tools/permission-hook.test.js.map +0 -1
  94. package/dist/__tests__/tools/permission-ipc.test.d.ts +0 -11
  95. package/dist/__tests__/tools/permission-ipc.test.d.ts.map +0 -1
  96. package/dist/__tests__/tools/permission-ipc.test.js +0 -612
  97. package/dist/__tests__/tools/permission-ipc.test.js.map +0 -1
  98. package/dist/__tests__/websocket.test.d.ts +0 -13
  99. package/dist/__tests__/websocket.test.d.ts.map +0 -1
  100. package/dist/__tests__/websocket.test.js +0 -204
  101. package/dist/__tests__/websocket.test.js.map +0 -1
  102. package/dist/api.d.ts +0 -44
  103. package/dist/api.d.ts.map +0 -1
  104. package/dist/api.js +0 -76
  105. package/dist/api.js.map +0 -1
  106. package/dist/approval.d.ts.map +0 -1
  107. package/dist/approval.js.map +0 -1
  108. package/dist/config.d.ts.map +0 -1
  109. package/dist/config.js.map +0 -1
  110. package/dist/crypto/e2eeManager.d.ts.map +0 -1
  111. package/dist/crypto/e2eeManager.js.map +0 -1
  112. package/dist/crypto/encryption.d.ts.map +0 -1
  113. package/dist/crypto/encryption.js.map +0 -1
  114. package/dist/crypto/keyExchange.d.ts.map +0 -1
  115. package/dist/crypto/keyExchange.js.map +0 -1
  116. package/dist/crypto/keyGeneration.d.ts.map +0 -1
  117. package/dist/crypto/keyGeneration.js.map +0 -1
  118. package/dist/crypto/keyStorage.d.ts.map +0 -1
  119. package/dist/crypto/keyStorage.js.map +0 -1
  120. package/dist/crypto/sessionPersistence.d.ts.map +0 -1
  121. package/dist/crypto/sessionPersistence.js.map +0 -1
  122. package/dist/crypto/types.d.ts.map +0 -1
  123. package/dist/crypto/types.js.map +0 -1
  124. package/dist/crypto/websocketE2EE.d.ts.map +0 -1
  125. package/dist/crypto/websocketE2EE.js.map +0 -1
  126. package/dist/index.d.ts.map +0 -1
  127. package/dist/index.js.map +0 -1
  128. package/dist/integration.d.ts.map +0 -1
  129. package/dist/integration.js.map +0 -1
  130. package/dist/logger.d.ts.map +0 -1
  131. package/dist/logger.js.map +0 -1
  132. package/dist/startup.d.ts.map +0 -1
  133. package/dist/startup.js.map +0 -1
  134. package/dist/terminal.d.ts.map +0 -1
  135. package/dist/terminal.js.map +0 -1
  136. package/dist/tools/__tests__/claude-sessions.test.d.ts +0 -2
  137. package/dist/tools/__tests__/claude-sessions.test.d.ts.map +0 -1
  138. package/dist/tools/__tests__/claude-sessions.test.js +0 -306
  139. package/dist/tools/__tests__/claude-sessions.test.js.map +0 -1
  140. package/dist/tools/claude-hooks.d.ts.map +0 -1
  141. package/dist/tools/claude-hooks.js.map +0 -1
  142. package/dist/tools/claude-process.d.ts.map +0 -1
  143. package/dist/tools/claude-process.js.map +0 -1
  144. package/dist/tools/claude-sessions.d.ts.map +0 -1
  145. package/dist/tools/claude-sessions.js.map +0 -1
  146. package/dist/tools/detector.d.ts.map +0 -1
  147. package/dist/tools/detector.js.map +0 -1
  148. package/dist/tools/index.d.ts.map +0 -1
  149. package/dist/tools/index.js.map +0 -1
  150. package/dist/tools/permission-hook.d.ts.map +0 -1
  151. package/dist/tools/permission-hook.js.map +0 -1
  152. package/dist/tools/permission-ipc.d.ts.map +0 -1
  153. package/dist/tools/permission-ipc.js.map +0 -1
  154. package/dist/transcript-streamer.d.ts.map +0 -1
  155. package/dist/transcript-streamer.js.map +0 -1
  156. package/dist/websocket.d.ts.map +0 -1
  157. package/dist/websocket.js.map +0 -1
  158. package/jest.config.js +0 -18
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forkoff",
3
- "version": "1.0.17",
3
+ "version": "1.0.19",
4
4
  "description": "CLI tool to connect your AI coding tools to mobile | Open Beta - Download the app: https://testflight.apple.com/join/dhh5FrN7",
5
5
  "main": "dist/integration.js",
6
6
  "types": "dist/integration.d.ts",
@@ -48,9 +48,8 @@
48
48
  "email": "support@forkoff.app",
49
49
  "url": "https://forkoff.app"
50
50
  },
51
- "license": "Proprietary",
51
+ "license": "MIT",
52
52
  "dependencies": {
53
- "axios": "^1.13.2",
54
53
  "chalk": "^4.1.2",
55
54
  "chokidar": "^3.6.0",
56
55
  "commander": "^14.0.2",
@@ -60,7 +59,9 @@
60
59
  "node-machine-id": "^1.1.12",
61
60
  "ora": "^5.4.1",
62
61
  "qrcode-terminal": "^0.12.0",
63
- "socket.io-client": "^4.8.3",
62
+ "socket.io": "^4.8.3",
63
+ "tweetnacl": "^1.0.3",
64
+ "tweetnacl-util": "^0.15.1",
64
65
  "uuid": "^8.3.2"
65
66
  },
66
67
  "devDependencies": {
@@ -1,6 +0,0 @@
1
- /**
2
- * Tests for CLI commands: help, unknown command, version
3
- * Uses createProgram() exported from index.ts
4
- */
5
- export {};
6
- //# sourceMappingURL=cli-commands.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"cli-commands.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/cli-commands.test.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
@@ -1,213 +0,0 @@
1
- "use strict";
2
- /**
3
- * Tests for CLI commands: help, unknown command, version
4
- * Uses createProgram() exported from index.ts
5
- */
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- // Mock all heavy dependencies so importing index.ts doesn't trigger real connections
8
- jest.mock('../config', () => ({
9
- config: {
10
- deviceId: null,
11
- deviceName: 'test',
12
- apiUrl: 'http://localhost:3000',
13
- wsUrl: 'ws://localhost:3000',
14
- pairingCode: null,
15
- pairedAt: null,
16
- userId: null,
17
- isPaired: false,
18
- startupEnabled: null,
19
- startupBinaryPath: null,
20
- getPath: () => '/tmp/config.json',
21
- reset: jest.fn(),
22
- },
23
- }));
24
- jest.mock('../api', () => ({
25
- api: {
26
- healthCheck: jest.fn(),
27
- registerDevice: jest.fn(),
28
- refreshPairingCode: jest.fn(),
29
- checkPairingStatus: jest.fn(),
30
- reportConnectedTools: jest.fn(),
31
- },
32
- }));
33
- jest.mock('../websocket', () => ({
34
- wsClient: {
35
- connect: jest.fn(),
36
- disconnect: jest.fn(),
37
- isConnected: false,
38
- on: jest.fn(),
39
- sendTerminalOutput: jest.fn(),
40
- sendTerminalCwd: jest.fn(),
41
- sendClaudeSessionUpdate: jest.fn(),
42
- sendClaudeSessions: jest.fn(),
43
- sendToolStatusUpdate: jest.fn(),
44
- sendDirectoryListResponse: jest.fn(),
45
- sendReadFileResponse: jest.fn(),
46
- sendTranscriptHistory: jest.fn(),
47
- sendTranscriptUpdate: jest.fn(),
48
- sendClaudeApprovalRequest: jest.fn(),
49
- sendToolActivity: jest.fn(),
50
- sendPermissionPrompt: jest.fn(),
51
- sendClaudeSessionEvent: jest.fn(),
52
- sendRpcResponse: jest.fn(),
53
- sendPendingPermissionsSync: jest.fn(),
54
- sendThinkingContent: jest.fn(),
55
- sendTokenUsage: jest.fn(),
56
- sendTaskProgress: jest.fn(),
57
- },
58
- }));
59
- jest.mock('../terminal', () => ({
60
- terminalManager: {
61
- on: jest.fn(),
62
- createSession: jest.fn(),
63
- executeCommand: jest.fn(),
64
- },
65
- }));
66
- jest.mock('../approval', () => ({
67
- approvalManager: {
68
- on: jest.fn(),
69
- handleApprovalResponse: jest.fn(),
70
- },
71
- }));
72
- jest.mock('../tools', () => ({
73
- toolDetector: { detectAll: jest.fn(), watchToolStatus: jest.fn() },
74
- claudeHooksManager: { canConfigure: jest.fn(), installHooks: jest.fn(), uninstallHooks: jest.fn(), isHookConfigured: jest.fn() },
75
- claudeSessionDetector: {
76
- isClaudeInstalled: jest.fn().mockReturnValue(false),
77
- scanSessions: jest.fn().mockReturnValue([]),
78
- getSessions: jest.fn().mockReturnValue([]),
79
- startWatching: jest.fn(),
80
- stopWatching: jest.fn(),
81
- seedKnownSessions: jest.fn(),
82
- on: jest.fn(),
83
- },
84
- claudeProcessManager: {
85
- isClaudeSession: jest.fn(),
86
- sendInput: jest.fn(),
87
- registerSession: jest.fn(),
88
- markTakenOver: jest.fn(),
89
- isTakenOver: jest.fn(),
90
- startSession: jest.fn(),
91
- startAndSendMessage: jest.fn(),
92
- handleApprovalResponse: jest.fn(),
93
- handlePermissionResponse: jest.fn(),
94
- updatePermissionRules: jest.fn(),
95
- clearAllTakenOver: jest.fn(),
96
- autoAllowAllPendingPrompts: jest.fn(),
97
- cleanupAllPermissionState: jest.fn(),
98
- getAllPendingPrompts: jest.fn().mockReturnValue([]),
99
- on: jest.fn(),
100
- },
101
- PermissionIpcManager: {
102
- cleanupStaleTempFiles: jest.fn(),
103
- },
104
- }));
105
- jest.mock('../transcript-streamer', () => ({
106
- transcriptStreamer: {
107
- fetchHistory: jest.fn(),
108
- subscribeToUpdates: jest.fn(),
109
- unsubscribeFromUpdates: jest.fn(),
110
- cleanup: jest.fn(),
111
- on: jest.fn(),
112
- },
113
- }));
114
- jest.mock('../logger', () => ({
115
- setQuiet: jest.fn(),
116
- createSpinner: jest.fn(() => ({
117
- start: jest.fn().mockReturnThis(),
118
- stop: jest.fn(),
119
- succeed: jest.fn(),
120
- fail: jest.fn(),
121
- info: jest.fn(),
122
- warn: jest.fn(),
123
- text: '',
124
- })),
125
- }));
126
- jest.mock('chalk', () => {
127
- const handler = {
128
- get: () => new Proxy((s) => s, handler),
129
- apply: (_target, _thisArg, args) => args[0],
130
- };
131
- return new Proxy((s) => s, handler);
132
- });
133
- jest.mock('qrcode-terminal', () => ({
134
- generate: jest.fn(),
135
- }));
136
- jest.mock('../startup', () => ({
137
- enableStartup: jest.fn(),
138
- disableStartup: jest.fn(),
139
- isStartupRegistered: jest.fn().mockReturnValue(false),
140
- getBinaryPath: jest.fn().mockReturnValue('/usr/local/bin/forkoff'),
141
- }));
142
- const index_1 = require("../index");
143
- describe('CLI commands', () => {
144
- let consoleLogSpy;
145
- let consoleErrorSpy;
146
- beforeEach(() => {
147
- consoleLogSpy = jest.spyOn(console, 'log').mockImplementation();
148
- consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
149
- process.exitCode = undefined;
150
- });
151
- afterEach(() => {
152
- consoleLogSpy.mockRestore();
153
- consoleErrorSpy.mockRestore();
154
- process.exitCode = undefined;
155
- });
156
- describe('help command', () => {
157
- it('outputs available commands', async () => {
158
- const program = (0, index_1.createProgram)();
159
- // Prevent commander from calling process.exit on help
160
- program.exitOverride();
161
- let helpOutput = '';
162
- program.configureOutput({
163
- writeOut: (str) => { helpOutput += str; },
164
- writeErr: (str) => { helpOutput += str; },
165
- });
166
- try {
167
- await program.parseAsync(['node', 'forkoff', 'help']);
168
- }
169
- catch {
170
- // Commander throws on exitOverride
171
- }
172
- // The help action calls program.outputHelp(), check console.log calls
173
- const allOutput = helpOutput + consoleLogSpy.mock.calls.map((c) => c.join(' ')).join('\n');
174
- expect(allOutput).toContain('pair');
175
- expect(allOutput).toContain('connect');
176
- expect(allOutput).toContain('disconnect');
177
- expect(allOutput).toContain('status');
178
- expect(allOutput).toContain('startup');
179
- expect(allOutput).toContain('config');
180
- expect(allOutput).toContain('tools');
181
- expect(allOutput).toContain('help');
182
- });
183
- });
184
- describe('--version flag', () => {
185
- it('shows version from package.json', async () => {
186
- const program = (0, index_1.createProgram)();
187
- program.exitOverride();
188
- let versionOutput = '';
189
- program.configureOutput({
190
- writeOut: (str) => { versionOutput += str; },
191
- writeErr: (str) => { versionOutput += str; },
192
- });
193
- try {
194
- await program.parseAsync(['node', 'forkoff', '--version']);
195
- }
196
- catch {
197
- // Commander throws on exitOverride
198
- }
199
- const pkg = require('../../package.json');
200
- expect(versionOutput).toContain(pkg.version);
201
- });
202
- });
203
- describe('unknown command', () => {
204
- it('sets process.exitCode = 1 and shows error message', async () => {
205
- const program = (0, index_1.createProgram)();
206
- await program.parseAsync(['node', 'forkoff', 'gibberish']);
207
- expect(process.exitCode).toBe(1);
208
- const errorOutput = consoleErrorSpy.mock.calls.map((c) => c.join(' ')).join('\n');
209
- expect(errorOutput).toContain('Unknown command: gibberish');
210
- });
211
- });
212
- });
213
- //# sourceMappingURL=cli-commands.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"cli-commands.test.js","sourceRoot":"","sources":["../../src/__tests__/cli-commands.test.ts"],"names":[],"mappings":"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"}
@@ -1,17 +0,0 @@
1
- /**
2
- * End-to-End Integration Test for E2EE
3
- *
4
- * Simulates the complete flow:
5
- * 1. Mobile generates keys → uploads public key
6
- * 2. CLI generates keys → uploads public key
7
- * 3. Mobile initiates key exchange with CLI
8
- * 4. CLI completes key exchange
9
- * 5. Mobile encrypts message → sends to CLI
10
- * 6. CLI receives → decrypts → verifies content matches
11
- * 7. CLI encrypts reply → sends to Mobile
12
- * 8. Mobile receives → decrypts → verifies content matches
13
- *
14
- * This test verifies the complete encrypted communication flow.
15
- */
16
- export {};
17
- //# sourceMappingURL=e2e-integration.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"e2e-integration.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/crypto/e2e-integration.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG"}
@@ -1,338 +0,0 @@
1
- "use strict";
2
- /**
3
- * End-to-End Integration Test for E2EE
4
- *
5
- * Simulates the complete flow:
6
- * 1. Mobile generates keys → uploads public key
7
- * 2. CLI generates keys → uploads public key
8
- * 3. Mobile initiates key exchange with CLI
9
- * 4. CLI completes key exchange
10
- * 5. Mobile encrypts message → sends to CLI
11
- * 6. CLI receives → decrypts → verifies content matches
12
- * 7. CLI encrypts reply → sends to Mobile
13
- * 8. Mobile receives → decrypts → verifies content matches
14
- *
15
- * This test verifies the complete encrypted communication flow.
16
- */
17
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
18
- if (k2 === undefined) k2 = k;
19
- var desc = Object.getOwnPropertyDescriptor(m, k);
20
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
21
- desc = { enumerable: true, get: function() { return m[k]; } };
22
- }
23
- Object.defineProperty(o, k2, desc);
24
- }) : (function(o, m, k, k2) {
25
- if (k2 === undefined) k2 = k;
26
- o[k2] = m[k];
27
- }));
28
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
29
- Object.defineProperty(o, "default", { enumerable: true, value: v });
30
- }) : function(o, v) {
31
- o["default"] = v;
32
- });
33
- var __importStar = (this && this.__importStar) || (function () {
34
- var ownKeys = function(o) {
35
- ownKeys = Object.getOwnPropertyNames || function (o) {
36
- var ar = [];
37
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
38
- return ar;
39
- };
40
- return ownKeys(o);
41
- };
42
- return function (mod) {
43
- if (mod && mod.__esModule) return mod;
44
- var result = {};
45
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
46
- __setModuleDefault(result, mod);
47
- return result;
48
- };
49
- })();
50
- var __importDefault = (this && this.__importDefault) || function (mod) {
51
- return (mod && mod.__esModule) ? mod : { "default": mod };
52
- };
53
- Object.defineProperty(exports, "__esModule", { value: true });
54
- const e2eeManager_1 = require("../../crypto/e2eeManager");
55
- const keyStorage = __importStar(require("../../crypto/keyStorage"));
56
- // Mock keytar
57
- jest.mock('keytar');
58
- // Mock axios
59
- jest.mock('axios');
60
- const axios_1 = __importDefault(require("axios"));
61
- const mockAxios = axios_1.default;
62
- describe('E2EE End-to-End Integration Test', () => {
63
- let mobileManager;
64
- let cliManager;
65
- const mobileDeviceId = 'mobile-device-123';
66
- const cliDeviceId = 'cli-device-456';
67
- const apiUrl = 'https://api.forkoff.app/api';
68
- const sessionId = 'test-session-abc';
69
- // Mock axios instance
70
- const mockAxiosInstance = {
71
- put: jest.fn(),
72
- get: jest.fn(),
73
- };
74
- beforeEach(async () => {
75
- jest.clearAllMocks();
76
- keyStorage.clearSessionKeys();
77
- // Mock axios.create
78
- mockAxios.create = jest.fn().mockReturnValue(mockAxiosInstance);
79
- // Mock successful public key uploads
80
- mockAxiosInstance.put.mockResolvedValue({ data: { success: true, keyVersion: 1 } });
81
- // Mock public key retrieval - return the actual public keys generated by each manager
82
- let mobilePublicKey;
83
- let cliPublicKey;
84
- const devicePublicKeys = new Map();
85
- mockAxiosInstance.get.mockImplementation((url) => {
86
- // Extract device ID from URL
87
- const match = url.match(/devices\/([^/]+)\/public-key/);
88
- if (match) {
89
- const deviceId = match[1];
90
- if (deviceId === cliDeviceId && cliPublicKey) {
91
- return Promise.resolve({
92
- data: { publicKey: cliPublicKey, keyVersion: 1 },
93
- });
94
- }
95
- else if (deviceId === mobileDeviceId && mobilePublicKey) {
96
- return Promise.resolve({
97
- data: { publicKey: mobilePublicKey, keyVersion: 1 },
98
- });
99
- }
100
- else if (devicePublicKeys.has(deviceId)) {
101
- return Promise.resolve({
102
- data: { publicKey: devicePublicKeys.get(deviceId), keyVersion: 1 },
103
- });
104
- }
105
- }
106
- // For unknown devices, return a mock public key
107
- return Promise.resolve({
108
- data: { publicKey: 'BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ=', keyVersion: 1 },
109
- });
110
- });
111
- // Mock key storage for both devices
112
- jest.spyOn(keyStorage, 'getPrivateKey').mockResolvedValue(null);
113
- jest.spyOn(keyStorage, 'storePrivateKey').mockResolvedValue();
114
- // Initialize Mobile E2EE Manager
115
- mobileManager = new e2eeManager_1.E2EEManager(mobileDeviceId, apiUrl, 'mobile-token');
116
- await mobileManager.initialize();
117
- // Capture mobile's public key
118
- const mobileInitCall = mockAxiosInstance.put.mock.calls.find((call) => call[0].includes(mobileDeviceId));
119
- mobilePublicKey = mobileInitCall?.[1]?.publicKey;
120
- // Initialize CLI E2EE Manager
121
- cliManager = new e2eeManager_1.E2EEManager(cliDeviceId, apiUrl, 'cli-token');
122
- await cliManager.initialize();
123
- // Capture CLI's public key
124
- const cliInitCall = mockAxiosInstance.put.mock.calls.find((call) => call[0].includes(cliDeviceId));
125
- cliPublicKey = cliInitCall?.[1]?.publicKey;
126
- });
127
- afterEach(() => {
128
- mobileManager.cleanup();
129
- cliManager.cleanup();
130
- });
131
- describe('Complete E2EE Flow', () => {
132
- it('completes full bidirectional encrypted communication flow', async () => {
133
- // ============================================================
134
- // STEP 1: Mobile initiates key exchange with CLI
135
- // ============================================================
136
- console.log('\n[TEST] Step 1: Mobile initiates key exchange with CLI');
137
- const mobileInitPayload = await mobileManager.initiateKeyExchange(cliDeviceId);
138
- expect(mobileInitPayload).toHaveProperty('senderDeviceId', mobileDeviceId);
139
- expect(mobileInitPayload).toHaveProperty('ephemeralPublicKey');
140
- console.log('[TEST] ✓ Mobile generated ephemeral key pair and initiated exchange');
141
- // ============================================================
142
- // STEP 2: CLI receives key exchange init and sends ack
143
- // ============================================================
144
- console.log('\n[TEST] Step 2: CLI receives key exchange init and sends ack');
145
- const cliAckPayload = await cliManager.handleKeyExchangeInit(mobileDeviceId, mobileInitPayload.ephemeralPublicKey);
146
- expect(cliAckPayload).toHaveProperty('recipientDeviceId', cliDeviceId);
147
- expect(cliAckPayload).toHaveProperty('ephemeralPublicKey');
148
- expect(cliManager.hasSessionKey(mobileDeviceId)).toBe(true);
149
- console.log('[TEST] ✓ CLI derived session key and sent ack');
150
- // ============================================================
151
- // STEP 3: Mobile receives ack and completes key exchange
152
- // ============================================================
153
- console.log('\n[TEST] Step 3: Mobile receives ack and completes key exchange');
154
- await mobileManager.handleKeyExchangeAck(cliDeviceId, cliAckPayload.ephemeralPublicKey);
155
- expect(mobileManager.hasSessionKey(cliDeviceId)).toBe(true);
156
- console.log('[TEST] ✓ Mobile completed key exchange and derived session key');
157
- // ============================================================
158
- // STEP 4: Mobile encrypts message and sends to CLI
159
- // ============================================================
160
- console.log('\n[TEST] Step 4: Mobile encrypts message and sends to CLI');
161
- const mobileMessage = 'Hello from mobile! This is a secret message. 🔒';
162
- const encryptedFromMobile = mobileManager.encryptMessage(mobileMessage, cliDeviceId, sessionId);
163
- expect(encryptedFromMobile.payload.ciphertext).toBeDefined();
164
- expect(encryptedFromMobile.payload.ciphertext).not.toContain(mobileMessage);
165
- expect(encryptedFromMobile.messageCounter).toBe(0);
166
- console.log('[TEST] ✓ Mobile encrypted message (counter: 0)');
167
- // ============================================================
168
- // STEP 5: CLI receives and decrypts mobile's message
169
- // ============================================================
170
- console.log('\n[TEST] Step 5: CLI receives and decrypts mobile\'s message');
171
- const decryptedAtCli = cliManager.decryptMessage(encryptedFromMobile, mobileDeviceId);
172
- expect(decryptedAtCli).toBe(mobileMessage);
173
- console.log('[TEST] ✓ CLI successfully decrypted: "' + decryptedAtCli + '"');
174
- // ============================================================
175
- // STEP 6: CLI encrypts reply and sends to Mobile
176
- // ============================================================
177
- console.log('\n[TEST] Step 6: CLI encrypts reply and sends to Mobile');
178
- const cliReply = 'Hello from CLI! Message received and understood. ✅';
179
- const encryptedFromCli = cliManager.encryptMessage(cliReply, mobileDeviceId, sessionId);
180
- expect(encryptedFromCli.payload.ciphertext).toBeDefined();
181
- expect(encryptedFromCli.payload.ciphertext).not.toContain(cliReply);
182
- expect(encryptedFromCli.messageCounter).toBe(0);
183
- console.log('[TEST] ✓ CLI encrypted reply (counter: 0)');
184
- // ============================================================
185
- // STEP 7: Mobile receives and decrypts CLI's reply
186
- // ============================================================
187
- console.log('\n[TEST] Step 7: Mobile receives and decrypts CLI\'s reply');
188
- const decryptedAtMobile = mobileManager.decryptMessage(encryptedFromCli, cliDeviceId);
189
- expect(decryptedAtMobile).toBe(cliReply);
190
- console.log('[TEST] ✓ Mobile successfully decrypted: "' + decryptedAtMobile + '"');
191
- // ============================================================
192
- // STEP 8: Verify bidirectional communication continues
193
- // ============================================================
194
- console.log('\n[TEST] Step 8: Verify bidirectional communication continues');
195
- const mobileMessage2 = 'Second message from mobile';
196
- const encrypted2FromMobile = mobileManager.encryptMessage(mobileMessage2, cliDeviceId, sessionId);
197
- expect(encrypted2FromMobile.messageCounter).toBe(1); // Counter incremented
198
- const decrypted2AtCli = cliManager.decryptMessage(encrypted2FromMobile, mobileDeviceId);
199
- expect(decrypted2AtCli).toBe(mobileMessage2);
200
- console.log('[TEST] ✓ Second message successfully encrypted and decrypted (counter: 1)');
201
- console.log('\n[TEST] ========================================');
202
- console.log('[TEST] ✅ FULL E2EE FLOW COMPLETED SUCCESSFULLY');
203
- console.log('[TEST] ========================================\n');
204
- });
205
- it('preserves unicode and emoji in encrypted messages', async () => {
206
- // Set up E2EE session
207
- const initPayload = await mobileManager.initiateKeyExchange(cliDeviceId);
208
- const ackPayload = await cliManager.handleKeyExchangeInit(mobileDeviceId, initPayload.ephemeralPublicKey);
209
- await mobileManager.handleKeyExchangeAck(cliDeviceId, ackPayload.ephemeralPublicKey);
210
- // Test unicode and emoji
211
- const unicodeMessage = 'Hello 世界 🌍 Привет мир 🚀 مرحبا بالعالم ✨';
212
- const encrypted = mobileManager.encryptMessage(unicodeMessage, cliDeviceId, sessionId);
213
- const decrypted = cliManager.decryptMessage(encrypted, mobileDeviceId);
214
- expect(decrypted).toBe(unicodeMessage);
215
- });
216
- it('handles large messages (10KB)', async () => {
217
- // Set up E2EE session
218
- const initPayload = await mobileManager.initiateKeyExchange(cliDeviceId);
219
- const ackPayload = await cliManager.handleKeyExchangeInit(mobileDeviceId, initPayload.ephemeralPublicKey);
220
- await mobileManager.handleKeyExchangeAck(cliDeviceId, ackPayload.ephemeralPublicKey);
221
- // Test large message
222
- const largeMessage = 'A'.repeat(10 * 1024); // 10KB
223
- const encrypted = mobileManager.encryptMessage(largeMessage, cliDeviceId, sessionId);
224
- const decrypted = cliManager.decryptMessage(encrypted, mobileDeviceId);
225
- expect(decrypted).toBe(largeMessage);
226
- expect(decrypted.length).toBe(10 * 1024);
227
- });
228
- });
229
- describe('Security Properties', () => {
230
- beforeEach(async () => {
231
- // Set up E2EE session for security tests
232
- const initPayload = await mobileManager.initiateKeyExchange(cliDeviceId);
233
- const ackPayload = await cliManager.handleKeyExchangeInit(mobileDeviceId, initPayload.ephemeralPublicKey);
234
- await mobileManager.handleKeyExchangeAck(cliDeviceId, ackPayload.ephemeralPublicKey);
235
- });
236
- it('rejects replayed messages (replay attack protection)', () => {
237
- const message1 = 'First message';
238
- const message2 = 'Second message';
239
- const encrypted1 = mobileManager.encryptMessage(message1, cliDeviceId, sessionId);
240
- const encrypted2 = mobileManager.encryptMessage(message2, cliDeviceId, sessionId);
241
- // Decrypt in order
242
- cliManager.decryptMessage(encrypted1, mobileDeviceId);
243
- cliManager.decryptMessage(encrypted2, mobileDeviceId);
244
- // Try to replay message 1 - should fail
245
- expect(() => cliManager.decryptMessage(encrypted1, mobileDeviceId)).toThrow(/counter/i);
246
- });
247
- it('detects tampered ciphertext', () => {
248
- const message = 'Secret message';
249
- const encrypted = mobileManager.encryptMessage(message, cliDeviceId, sessionId);
250
- // Tamper with ciphertext
251
- const tamperedCiphertext = Buffer.from(encrypted.payload.ciphertext, 'base64');
252
- tamperedCiphertext[0] ^= 0xFF;
253
- encrypted.payload.ciphertext = tamperedCiphertext.toString('base64');
254
- // Should fail on decryption
255
- expect(() => cliManager.decryptMessage(encrypted, mobileDeviceId)).toThrow();
256
- });
257
- it('detects tampered nonce', () => {
258
- const message = 'Secret message';
259
- const encrypted = mobileManager.encryptMessage(message, cliDeviceId, sessionId);
260
- // Tamper with nonce
261
- const tamperedNonce = Buffer.from(encrypted.payload.nonce, 'base64');
262
- tamperedNonce[0] ^= 0xFF;
263
- encrypted.payload.nonce = tamperedNonce.toString('base64');
264
- // Should fail on decryption
265
- expect(() => cliManager.decryptMessage(encrypted, mobileDeviceId)).toThrow();
266
- });
267
- it('detects tampered auth tag', () => {
268
- const message = 'Secret message';
269
- const encrypted = mobileManager.encryptMessage(message, cliDeviceId, sessionId);
270
- // Tamper with auth tag
271
- const tamperedAuthTag = Buffer.from(encrypted.payload.authTag, 'base64');
272
- tamperedAuthTag[0] ^= 0xFF;
273
- encrypted.payload.authTag = tamperedAuthTag.toString('base64');
274
- // Should fail on decryption
275
- expect(() => cliManager.decryptMessage(encrypted, mobileDeviceId)).toThrow();
276
- });
277
- it('prevents message decryption with wrong session key', async () => {
278
- // Create a third device (attacker)
279
- const attackerManager = new e2eeManager_1.E2EEManager('attacker-device', apiUrl, 'attacker-token');
280
- await attackerManager.initialize();
281
- // Mobile sends encrypted message to CLI
282
- const message = 'Secret message';
283
- const encrypted = mobileManager.encryptMessage(message, cliDeviceId, sessionId);
284
- // Attacker tries to set up their own session with mobile
285
- const attackerInit = await attackerManager.initiateKeyExchange(mobileDeviceId);
286
- const attackerAck = await mobileManager.handleKeyExchangeInit('attacker-device', attackerInit.ephemeralPublicKey);
287
- await attackerManager.handleKeyExchangeAck(mobileDeviceId, attackerAck.ephemeralPublicKey);
288
- // Attacker intercepts message meant for CLI and tries to decrypt
289
- // This should fail because the message was encrypted with CLI's session key
290
- expect(() => attackerManager.decryptMessage(encrypted, mobileDeviceId)).toThrow();
291
- attackerManager.cleanup();
292
- });
293
- });
294
- describe('Multi-device Support', () => {
295
- it('maintains multiple concurrent E2EE sessions independently', async () => {
296
- // Simulate having two separate conversations:
297
- // 1. Mobile ↔ CLI (already set up)
298
- // 2. Mobile ↔ Another CLI instance
299
- // First session: Mobile ↔ CLI
300
- const init1 = await mobileManager.initiateKeyExchange(cliDeviceId);
301
- const ack1 = await cliManager.handleKeyExchangeInit(mobileDeviceId, init1.ephemeralPublicKey);
302
- await mobileManager.handleKeyExchangeAck(cliDeviceId, ack1.ephemeralPublicKey);
303
- // Verify first session is active
304
- expect(mobileManager.hasSessionKey(cliDeviceId)).toBe(true);
305
- expect(cliManager.hasSessionKey(mobileDeviceId)).toBe(true);
306
- // Simulate a second CLI device (using a fake device ID)
307
- const cli2DeviceId = 'cli-device-second';
308
- // Since we can't easily mock a third device with our current setup,
309
- // let's just verify that the manager can track multiple session keys
310
- // by checking the state after establishing one session
311
- // Verify mobileManager is tracking the CLI session
312
- expect(mobileManager.hasSessionKey(cliDeviceId)).toBe(true);
313
- // Verify it doesn't have a session with a non-existent device
314
- expect(mobileManager.hasSessionKey('non-existent-device')).toBe(false);
315
- // Verify we can send multiple messages to the same device
316
- const message1 = 'First message to CLI';
317
- const message2 = 'Second message to CLI';
318
- const message3 = 'Third message to CLI';
319
- const encrypted1 = mobileManager.encryptMessage(message1, cliDeviceId, sessionId);
320
- const encrypted2 = mobileManager.encryptMessage(message2, cliDeviceId, sessionId);
321
- const encrypted3 = mobileManager.encryptMessage(message3, cliDeviceId, sessionId);
322
- // Verify message counters increment
323
- expect(encrypted1.messageCounter).toBe(0);
324
- expect(encrypted2.messageCounter).toBe(1);
325
- expect(encrypted3.messageCounter).toBe(2);
326
- // Verify all messages can be decrypted in order
327
- const decrypted1 = cliManager.decryptMessage(encrypted1, mobileDeviceId);
328
- const decrypted2 = cliManager.decryptMessage(encrypted2, mobileDeviceId);
329
- const decrypted3 = cliManager.decryptMessage(encrypted3, mobileDeviceId);
330
- expect(decrypted1).toBe(message1);
331
- expect(decrypted2).toBe(message2);
332
- expect(decrypted3).toBe(message3);
333
- // This demonstrates that the session is maintained correctly
334
- // and can handle multiple sequential messages
335
- });
336
- });
337
- });
338
- //# sourceMappingURL=e2e-integration.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"e2e-integration.test.js","sourceRoot":"","sources":["../../../src/__tests__/crypto/e2e-integration.test.ts"],"names":[],"mappings":"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"}