forkoff 1.0.0

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 (56) hide show
  1. package/LICENSE +17 -0
  2. package/README.md +173 -0
  3. package/dist/api.d.ts +44 -0
  4. package/dist/api.d.ts.map +1 -0
  5. package/dist/api.js +76 -0
  6. package/dist/api.js.map +1 -0
  7. package/dist/approval.d.ts +46 -0
  8. package/dist/approval.d.ts.map +1 -0
  9. package/dist/approval.js +119 -0
  10. package/dist/approval.js.map +1 -0
  11. package/dist/config.d.ts +36 -0
  12. package/dist/config.d.ts.map +1 -0
  13. package/dist/config.js +209 -0
  14. package/dist/config.js.map +1 -0
  15. package/dist/index.d.ts +3 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +868 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/integration.d.ts +30 -0
  20. package/dist/integration.d.ts.map +1 -0
  21. package/dist/integration.js +84 -0
  22. package/dist/integration.js.map +1 -0
  23. package/dist/terminal.d.ts +25 -0
  24. package/dist/terminal.d.ts.map +1 -0
  25. package/dist/terminal.js +171 -0
  26. package/dist/terminal.js.map +1 -0
  27. package/dist/tools/claude-hooks.d.ts +97 -0
  28. package/dist/tools/claude-hooks.d.ts.map +1 -0
  29. package/dist/tools/claude-hooks.js +348 -0
  30. package/dist/tools/claude-hooks.js.map +1 -0
  31. package/dist/tools/claude-process.d.ts +271 -0
  32. package/dist/tools/claude-process.d.ts.map +1 -0
  33. package/dist/tools/claude-process.js +931 -0
  34. package/dist/tools/claude-process.js.map +1 -0
  35. package/dist/tools/claude-sessions.d.ts +60 -0
  36. package/dist/tools/claude-sessions.d.ts.map +1 -0
  37. package/dist/tools/claude-sessions.js +285 -0
  38. package/dist/tools/claude-sessions.js.map +1 -0
  39. package/dist/tools/detector.d.ts +64 -0
  40. package/dist/tools/detector.d.ts.map +1 -0
  41. package/dist/tools/detector.js +383 -0
  42. package/dist/tools/detector.js.map +1 -0
  43. package/dist/tools/index.d.ts +8 -0
  44. package/dist/tools/index.d.ts.map +1 -0
  45. package/dist/tools/index.js +15 -0
  46. package/dist/tools/index.js.map +1 -0
  47. package/dist/transcript-streamer.d.ts +68 -0
  48. package/dist/transcript-streamer.d.ts.map +1 -0
  49. package/dist/transcript-streamer.js +459 -0
  50. package/dist/transcript-streamer.js.map +1 -0
  51. package/dist/websocket.d.ts +133 -0
  52. package/dist/websocket.d.ts.map +1 -0
  53. package/dist/websocket.js +247 -0
  54. package/dist/websocket.js.map +1 -0
  55. package/nul +0 -0
  56. package/package.json +54 -0
package/LICENSE ADDED
@@ -0,0 +1,17 @@
1
+ Copyright (c) 2026 ForkOff. All rights reserved.
2
+
3
+ This software and associated documentation files (the "Software") are the
4
+ proprietary property of ForkOff. The Software is provided for viewing and
5
+ reference purposes only.
6
+
7
+ No part of the Software may be copied, modified, merged, published,
8
+ distributed, sublicensed, sold, or otherwise used without the prior written
9
+ permission of ForkOff.
10
+
11
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
15
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
16
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
17
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,173 @@
1
+ <p align="center">
2
+ <img src="assets/logo.png" alt="ForkOff Logo" width="200"/>
3
+ </p>
4
+
5
+ <h1 align="center">ForkOff CLI</h1>
6
+
7
+ <p align="center">
8
+ <strong>Bridge your AI coding tools to your mobile device</strong>
9
+ </p>
10
+
11
+ <p align="center">
12
+ <a href="#installation">Installation</a> •
13
+ <a href="#quick-start">Quick Start</a> •
14
+ <a href="#commands">Commands</a> •
15
+ <a href="#programmatic-usage">API</a> •
16
+ <a href="#configuration">Configuration</a>
17
+ </p>
18
+
19
+ ---
20
+
21
+ ## Overview
22
+
23
+ ForkOff CLI connects your development machine to the ForkOff mobile app, enabling you to:
24
+
25
+ - 📱 **Control AI coding sessions** from your phone
26
+ - ✅ **Approve code changes** on the go
27
+ - 💬 **Send prompts** to Claude, Cursor, and other AI tools
28
+ - 📊 **Monitor progress** in real-time
29
+ - 🔔 **Get notifications** for permission requests
30
+
31
+ ## Installation
32
+
33
+ ```bash
34
+ # Install globally via npm
35
+ npm install -g forkoff-cli
36
+
37
+ # Or clone and link for development
38
+ git clone https://github.com/Forkoff-app/forkoff-cli.git
39
+ cd forkoff-cli
40
+ npm install
41
+ npm link
42
+ ```
43
+
44
+ ## Quick Start
45
+
46
+ ### 1. Pair with Mobile App
47
+
48
+ ```bash
49
+ forkoff pair
50
+ ```
51
+
52
+ Scan the QR code with your ForkOff mobile app to link your device.
53
+
54
+ ### 2. Stay Connected
55
+
56
+ ```bash
57
+ forkoff connect
58
+ ```
59
+
60
+ Keep this running to receive commands from your mobile app.
61
+
62
+ ---
63
+
64
+ ## Commands
65
+
66
+ | Command | Description |
67
+ |---------|-------------|
68
+ | `forkoff pair` | Generate QR code to pair with mobile app |
69
+ | `forkoff connect` | Connect and listen for commands |
70
+ | `forkoff status` | Check connection status |
71
+ | `forkoff disconnect` | Disconnect from server |
72
+ | `forkoff config` | View/modify configuration |
73
+
74
+ ### Configuration Options
75
+
76
+ ```bash
77
+ # Show current configuration
78
+ forkoff config --show
79
+
80
+ # Set custom API URL
81
+ forkoff config --api https://your-server.com/api
82
+
83
+ # Set custom WebSocket URL
84
+ forkoff config --ws wss://your-server.com
85
+
86
+ # Set device name
87
+ forkoff config --name "My MacBook Pro"
88
+
89
+ # Reset all configuration
90
+ forkoff config --reset
91
+ ```
92
+
93
+ ---
94
+
95
+ ## Programmatic Usage
96
+
97
+ Integrate ForkOff into your AI coding tools:
98
+
99
+ ```typescript
100
+ import { createIntegration } from 'forkoff-cli';
101
+
102
+ const forkoff = createIntegration();
103
+
104
+ // Connect to ForkOff server
105
+ await forkoff.connect();
106
+
107
+ // Handle incoming messages from mobile app
108
+ forkoff.onMessageReceived((sessionId, content, requestedBy) => {
109
+ console.log(`Message from ${requestedBy}: ${content}`);
110
+
111
+ // Send a response
112
+ forkoff.sendMessage(sessionId, 'Processing your request...');
113
+ });
114
+
115
+ // Stream responses in real-time
116
+ const stream = forkoff.startStreaming(sessionId);
117
+ stream.write('Here is ');
118
+ stream.write('a streaming ');
119
+ stream.write('response.');
120
+ stream.end();
121
+
122
+ // Request approval for code changes
123
+ const approval = await forkoff.requestApproval(
124
+ sessionId,
125
+ messageId,
126
+ 'CODE_CHANGE',
127
+ 'Add authentication middleware',
128
+ { filePath: 'src/middleware/auth.ts', diff: '...' }
129
+ );
130
+
131
+ if (approval.status === 'approved') {
132
+ // Apply the changes
133
+ }
134
+
135
+ // Send terminal output
136
+ forkoff.sendTerminalOutput(sessionId, '> npm install\n✓ Done', 'stdout');
137
+ forkoff.sendTerminalExit(sessionId, 0);
138
+
139
+ // Update device status
140
+ forkoff.setStatus('busy');
141
+ ```
142
+
143
+ ---
144
+
145
+ ## Configuration
146
+
147
+ Configuration files are stored at:
148
+
149
+ | Platform | Location |
150
+ |----------|----------|
151
+ | **Windows** | `%APPDATA%\forkoff-cli\config.json` |
152
+ | **macOS** | `~/Library/Preferences/forkoff-cli/config.json` |
153
+ | **Linux** | `~/.config/forkoff-cli/config.json` |
154
+
155
+ ---
156
+
157
+ ## Requirements
158
+
159
+ - Node.js 18+
160
+ - ForkOff mobile app ([iOS](https://apps.apple.com/app/forkoff) / [Android](https://play.google.com/store/apps/details?id=com.forkoff.app))
161
+
162
+ ---
163
+
164
+ ## Related Projects
165
+
166
+ - [ForkOff Mobile App](https://github.com/Forkoff-app/forkoff-react-native) - React Native mobile app
167
+ - [ForkOff Backend](https://github.com/Forkoff-app/forkoff-backend) - API server
168
+
169
+ ---
170
+
171
+ <p align="center">
172
+ Made with ❤️ by the ForkOff team
173
+ </p>
package/dist/api.d.ts ADDED
@@ -0,0 +1,44 @@
1
+ interface RegisterResponse {
2
+ pairingCode: string;
3
+ expiresAt: string;
4
+ device: {
5
+ id: string;
6
+ name: string;
7
+ status: string;
8
+ };
9
+ }
10
+ interface DeviceResponse {
11
+ id: string;
12
+ name: string;
13
+ type: string;
14
+ platform: string;
15
+ status: string;
16
+ userId: string | null;
17
+ lastSeenAt: string;
18
+ connectedTools: Array<{
19
+ id: string;
20
+ name: string;
21
+ type: string;
22
+ }>;
23
+ }
24
+ declare class ApiClient {
25
+ private client;
26
+ constructor();
27
+ private getBaseUrl;
28
+ registerDevice(): Promise<RegisterResponse>;
29
+ refreshPairingCode(deviceId: string): Promise<RegisterResponse>;
30
+ getDeviceStatus(deviceId: string): Promise<DeviceResponse>;
31
+ checkPairingStatus(deviceId: string): Promise<{
32
+ isPaired: boolean;
33
+ userId: string | null;
34
+ }>;
35
+ healthCheck(): Promise<boolean>;
36
+ reportConnectedTools(deviceId: string, tools: Array<{
37
+ type: string;
38
+ name: string;
39
+ version: string | null;
40
+ }>): Promise<void>;
41
+ }
42
+ export declare const api: ApiClient;
43
+ export default api;
44
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAGA,UAAU,gBAAgB;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE;QACN,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAED,UAAU,cAAc;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,KAAK,CAAC;QACpB,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;KACd,CAAC,CAAC;CACJ;AAED,cAAM,SAAS;IACb,OAAO,CAAC,MAAM,CAAgB;;IAW9B,OAAO,CAAC,UAAU;IAKZ,cAAc,IAAI,OAAO,CAAC,gBAAgB,CAAC;IAY3C,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAS/D,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAS1D,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QAClD,QAAQ,EAAE,OAAO,CAAC;QAClB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;KACvB,CAAC;IAoBI,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAa/B,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC;QACxD,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;KACxB,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;CAWnB;AAED,eAAO,MAAM,GAAG,WAAkB,CAAC;AACnC,eAAe,GAAG,CAAC"}
package/dist/api.js ADDED
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.api = void 0;
7
+ const axios_1 = __importDefault(require("axios"));
8
+ const config_1 = require("./config");
9
+ class ApiClient {
10
+ constructor() {
11
+ this.client = axios_1.default.create({
12
+ timeout: 30000,
13
+ headers: {
14
+ 'Content-Type': 'application/json',
15
+ },
16
+ });
17
+ }
18
+ getBaseUrl() {
19
+ return config_1.config.apiUrl;
20
+ }
21
+ // Register device and get pairing code
22
+ async registerDevice() {
23
+ const deviceInfo = config_1.config.getDeviceInfo();
24
+ const response = await this.client.post(`${this.getBaseUrl()}/devices/register`, deviceInfo);
25
+ return response.data;
26
+ }
27
+ // Refresh pairing code for existing device
28
+ async refreshPairingCode(deviceId) {
29
+ const response = await this.client.post(`${this.getBaseUrl()}/devices/${deviceId}/refresh`);
30
+ return response.data;
31
+ }
32
+ // Get device status
33
+ async getDeviceStatus(deviceId) {
34
+ const response = await this.client.get(`${this.getBaseUrl()}/devices/${deviceId}`);
35
+ return response.data;
36
+ }
37
+ // Check if device is paired (has userId) - uses public endpoint
38
+ async checkPairingStatus(deviceId) {
39
+ try {
40
+ const response = await this.client.get(`${this.getBaseUrl()}/devices/${deviceId}/public`);
41
+ return {
42
+ isPaired: response.data.isPaired,
43
+ userId: response.data.userId,
44
+ };
45
+ }
46
+ catch (error) {
47
+ return { isPaired: false, userId: null };
48
+ }
49
+ }
50
+ // Health check
51
+ async healthCheck() {
52
+ try {
53
+ const response = await this.client.get(`${this.getBaseUrl().replace('/api', '')}/`, {
54
+ validateStatus: () => true, // Accept any status code
55
+ });
56
+ // Server is up if we get any response (even 404)
57
+ return response.status < 500;
58
+ }
59
+ catch {
60
+ return false;
61
+ }
62
+ }
63
+ // Report connected tools for a device
64
+ async reportConnectedTools(deviceId, tools) {
65
+ try {
66
+ await this.client.post(`${this.getBaseUrl()}/devices/${deviceId}/tools`, { tools });
67
+ }
68
+ catch (error) {
69
+ // Non-critical, just log
70
+ console.error('Failed to report connected tools:', error);
71
+ }
72
+ }
73
+ }
74
+ exports.api = new ApiClient();
75
+ exports.default = exports.api;
76
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":";;;;;;AAAA,kDAA6C;AAC7C,qCAAkC;AA2BlC,MAAM,SAAS;IAGb;QACE,IAAI,CAAC,MAAM,GAAG,eAAK,CAAC,MAAM,CAAC;YACzB,OAAO,EAAE,KAAK;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;aACnC;SACF,CAAC,CAAC;IACL,CAAC;IAEO,UAAU;QAChB,OAAO,eAAM,CAAC,MAAM,CAAC;IACvB,CAAC;IAED,uCAAuC;IACvC,KAAK,CAAC,cAAc;QAClB,MAAM,UAAU,GAAG,eAAM,CAAC,aAAa,EAAE,CAAC;QAE1C,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CACrC,GAAG,IAAI,CAAC,UAAU,EAAE,mBAAmB,EACvC,UAAU,CACX,CAAC;QAEF,OAAO,QAAQ,CAAC,IAAI,CAAC;IACvB,CAAC;IAED,2CAA2C;IAC3C,KAAK,CAAC,kBAAkB,CAAC,QAAgB;QACvC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CACrC,GAAG,IAAI,CAAC,UAAU,EAAE,YAAY,QAAQ,UAAU,CACnD,CAAC;QAEF,OAAO,QAAQ,CAAC,IAAI,CAAC;IACvB,CAAC;IAED,oBAAoB;IACpB,KAAK,CAAC,eAAe,CAAC,QAAgB;QACpC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CACpC,GAAG,IAAI,CAAC,UAAU,EAAE,YAAY,QAAQ,EAAE,CAC3C,CAAC;QAEF,OAAO,QAAQ,CAAC,IAAI,CAAC;IACvB,CAAC;IAED,gEAAgE;IAChE,KAAK,CAAC,kBAAkB,CAAC,QAAgB;QAIvC,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAMnC,GAAG,IAAI,CAAC,UAAU,EAAE,YAAY,QAAQ,SAAS,CAAC,CAAC;YAEtD,OAAO;gBACL,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,QAAQ;gBAChC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,MAAM;aAC7B,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;QAC3C,CAAC;IACH,CAAC;IAED,eAAe;IACf,KAAK,CAAC,WAAW;QACf,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,EAAE;gBAClF,cAAc,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,yBAAyB;aACtD,CAAC,CAAC;YACH,iDAAiD;YACjD,OAAO,QAAQ,CAAC,MAAM,GAAG,GAAG,CAAC;QAC/B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,sCAAsC;IACtC,KAAK,CAAC,oBAAoB,CAAC,QAAgB,EAAE,KAI3C;QACA,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CACpB,GAAG,IAAI,CAAC,UAAU,EAAE,YAAY,QAAQ,QAAQ,EAChD,EAAE,KAAK,EAAE,CACV,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,yBAAyB;YACzB,OAAO,CAAC,KAAK,CAAC,mCAAmC,EAAE,KAAK,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC;CACF;AAEY,QAAA,GAAG,GAAG,IAAI,SAAS,EAAE,CAAC;AACnC,kBAAe,WAAG,CAAC"}
@@ -0,0 +1,46 @@
1
+ import { EventEmitter } from 'events';
2
+ export interface PendingApproval {
3
+ id: string;
4
+ sessionId: string;
5
+ messageId: string;
6
+ type: 'CODE_CHANGE' | 'FILE_OPERATION' | 'COMMAND_EXECUTION' | 'OTHER';
7
+ description: string;
8
+ changes: any;
9
+ createdAt: Date;
10
+ status: 'pending' | 'approved' | 'rejected';
11
+ }
12
+ declare class ApprovalManager extends EventEmitter {
13
+ private pendingApprovals;
14
+ private approvalCounter;
15
+ /**
16
+ * Create a new approval request and send to mobile app
17
+ */
18
+ createApprovalRequest(sessionId: string, messageId: string, type: PendingApproval['type'], description: string, changes: any): PendingApproval;
19
+ /**
20
+ * Handle approval response from mobile app
21
+ */
22
+ handleApprovalResponse(approvalId: string, status: 'APPROVED' | 'REJECTED'): void;
23
+ /**
24
+ * Wait for an approval to be resolved
25
+ */
26
+ waitForApproval(approvalId: string, timeoutMs?: number): Promise<PendingApproval>;
27
+ /**
28
+ * Get all pending approvals
29
+ */
30
+ getPendingApprovals(): PendingApproval[];
31
+ /**
32
+ * Get approval by ID
33
+ */
34
+ getApproval(id: string): PendingApproval | undefined;
35
+ /**
36
+ * Clear all approvals
37
+ */
38
+ clear(): void;
39
+ /**
40
+ * Clear resolved approvals older than specified age
41
+ */
42
+ clearOldApprovals(maxAgeMs?: number): void;
43
+ }
44
+ export declare const approvalManager: ApprovalManager;
45
+ export default approvalManager;
46
+ //# sourceMappingURL=approval.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approval.d.ts","sourceRoot":"","sources":["../src/approval.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAGtC,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,aAAa,GAAG,gBAAgB,GAAG,mBAAmB,GAAG,OAAO,CAAC;IACvE,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,GAAG,CAAC;IACb,SAAS,EAAE,IAAI,CAAC;IAChB,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,UAAU,CAAC;CAC7C;AAED,cAAM,eAAgB,SAAQ,YAAY;IACxC,OAAO,CAAC,gBAAgB,CAA2C;IACnE,OAAO,CAAC,eAAe,CAAK;IAE5B;;OAEG;IACH,qBAAqB,CACnB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,eAAe,CAAC,MAAM,CAAC,EAC7B,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,GAAG,GACX,eAAe;IAiClB;;OAEG;IACH,sBAAsB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,UAAU,GAAG,IAAI;IAajF;;OAEG;IACH,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,GAAE,MAAe,GAAG,OAAO,CAAC,eAAe,CAAC;IAmCzF;;OAEG;IACH,mBAAmB,IAAI,eAAe,EAAE;IAMxC;;OAEG;IACH,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS;IAIpD;;OAEG;IACH,KAAK,IAAI,IAAI;IAKb;;OAEG;IACH,iBAAiB,CAAC,QAAQ,GAAE,MAAgB,GAAG,IAAI;CAYpD;AAED,eAAO,MAAM,eAAe,iBAAwB,CAAC;AACrD,eAAe,eAAe,CAAC"}
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.approvalManager = void 0;
4
+ const events_1 = require("events");
5
+ const websocket_1 = require("./websocket");
6
+ class ApprovalManager extends events_1.EventEmitter {
7
+ constructor() {
8
+ super(...arguments);
9
+ this.pendingApprovals = new Map();
10
+ this.approvalCounter = 0;
11
+ }
12
+ /**
13
+ * Create a new approval request and send to mobile app
14
+ */
15
+ createApprovalRequest(sessionId, messageId, type, description, changes) {
16
+ const id = `approval_${++this.approvalCounter}_${Date.now()}`;
17
+ const approval = {
18
+ id,
19
+ sessionId,
20
+ messageId,
21
+ type,
22
+ description,
23
+ changes,
24
+ createdAt: new Date(),
25
+ status: 'pending',
26
+ };
27
+ this.pendingApprovals.set(id, approval);
28
+ // Send to mobile app via WebSocket
29
+ websocket_1.wsClient.sendApprovalRequest({
30
+ sessionId,
31
+ messageId,
32
+ type,
33
+ description,
34
+ changes: {
35
+ ...changes,
36
+ approvalId: id,
37
+ },
38
+ });
39
+ this.emit('approval_created', approval);
40
+ return approval;
41
+ }
42
+ /**
43
+ * Handle approval response from mobile app
44
+ */
45
+ handleApprovalResponse(approvalId, status) {
46
+ const approval = this.pendingApprovals.get(approvalId);
47
+ if (!approval) {
48
+ return;
49
+ }
50
+ approval.status = status === 'APPROVED' ? 'approved' : 'rejected';
51
+ this.emit('approval_resolved', approval);
52
+ this.emit(status === 'APPROVED' ? 'approved' : 'rejected', approval);
53
+ }
54
+ /**
55
+ * Wait for an approval to be resolved
56
+ */
57
+ waitForApproval(approvalId, timeoutMs = 300000) {
58
+ return new Promise((resolve, reject) => {
59
+ const approval = this.pendingApprovals.get(approvalId);
60
+ if (!approval) {
61
+ reject(new Error('Approval not found'));
62
+ return;
63
+ }
64
+ if (approval.status !== 'pending') {
65
+ resolve(approval);
66
+ return;
67
+ }
68
+ const timeout = setTimeout(() => {
69
+ cleanup();
70
+ reject(new Error('Approval request timed out'));
71
+ }, timeoutMs);
72
+ const onResolved = (resolved) => {
73
+ if (resolved.id === approvalId) {
74
+ cleanup();
75
+ resolve(resolved);
76
+ }
77
+ };
78
+ const cleanup = () => {
79
+ clearTimeout(timeout);
80
+ this.off('approval_resolved', onResolved);
81
+ };
82
+ this.on('approval_resolved', onResolved);
83
+ });
84
+ }
85
+ /**
86
+ * Get all pending approvals
87
+ */
88
+ getPendingApprovals() {
89
+ return Array.from(this.pendingApprovals.values()).filter((a) => a.status === 'pending');
90
+ }
91
+ /**
92
+ * Get approval by ID
93
+ */
94
+ getApproval(id) {
95
+ return this.pendingApprovals.get(id);
96
+ }
97
+ /**
98
+ * Clear all approvals
99
+ */
100
+ clear() {
101
+ this.pendingApprovals.clear();
102
+ this.approvalCounter = 0;
103
+ }
104
+ /**
105
+ * Clear resolved approvals older than specified age
106
+ */
107
+ clearOldApprovals(maxAgeMs = 3600000) {
108
+ const now = Date.now();
109
+ for (const [id, approval] of this.pendingApprovals) {
110
+ if (approval.status !== 'pending' &&
111
+ now - approval.createdAt.getTime() > maxAgeMs) {
112
+ this.pendingApprovals.delete(id);
113
+ }
114
+ }
115
+ }
116
+ }
117
+ exports.approvalManager = new ApprovalManager();
118
+ exports.default = exports.approvalManager;
119
+ //# sourceMappingURL=approval.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approval.js","sourceRoot":"","sources":["../src/approval.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ declare class Config {
2
+ private configPath;
3
+ private data;
4
+ constructor();
5
+ private getConfigPath;
6
+ private load;
7
+ private save;
8
+ get deviceId(): string | null;
9
+ set deviceId(value: string | null);
10
+ get deviceName(): string;
11
+ set deviceName(value: string);
12
+ get apiUrl(): string;
13
+ set apiUrl(value: string);
14
+ get wsUrl(): string;
15
+ set wsUrl(value: string);
16
+ get pairingCode(): string | null;
17
+ set pairingCode(value: string | null);
18
+ get pairedAt(): string | null;
19
+ set pairedAt(value: string | null);
20
+ get userId(): string | null;
21
+ set userId(value: string | null);
22
+ get isPaired(): boolean;
23
+ getMachineId(): string;
24
+ getDeviceInfo(): {
25
+ name: string;
26
+ type: "desktop";
27
+ platform: NodeJS.Platform;
28
+ hostname: string;
29
+ machineId: string;
30
+ };
31
+ reset(): void;
32
+ getPath(): string;
33
+ }
34
+ export declare const config: Config;
35
+ export default config;
36
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAmEA,cAAM,MAAM;IACV,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,IAAI,CAAe;;IAO3B,OAAO,CAAC,aAAa;IAYrB,OAAO,CAAC,IAAI;IAYZ,OAAO,CAAC,IAAI;IAIZ,IAAI,QAAQ,IAAI,MAAM,GAAG,IAAI,CAE5B;IAED,IAAI,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAGhC;IAED,IAAI,UAAU,IAAI,MAAM,CAEvB;IAED,IAAI,UAAU,CAAC,KAAK,EAAE,MAAM,EAG3B;IAED,IAAI,MAAM,IAAI,MAAM,CAGnB;IAED,IAAI,MAAM,CAAC,KAAK,EAAE,MAAM,EAIvB;IAED,IAAI,KAAK,IAAI,MAAM,CAGlB;IAED,IAAI,KAAK,CAAC,KAAK,EAAE,MAAM,EAItB;IAED,IAAI,WAAW,IAAI,MAAM,GAAG,IAAI,CAE/B;IAED,IAAI,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAGnC;IAED,IAAI,QAAQ,IAAI,MAAM,GAAG,IAAI,CAE5B;IAED,IAAI,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAGhC;IAED,IAAI,MAAM,IAAI,MAAM,GAAG,IAAI,CAE1B;IAED,IAAI,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAG9B;IAED,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAGD,YAAY,IAAI,MAAM;IActB,aAAa;;;;;;;IAWb,KAAK,IAAI,IAAI;IAMb,OAAO,IAAI,MAAM;CAGlB;AAED,eAAO,MAAM,MAAM,QAAe,CAAC;AACnC,eAAe,MAAM,CAAC"}