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.
- package/LICENSE +17 -0
- package/README.md +173 -0
- package/dist/api.d.ts +44 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +76 -0
- package/dist/api.js.map +1 -0
- package/dist/approval.d.ts +46 -0
- package/dist/approval.d.ts.map +1 -0
- package/dist/approval.js +119 -0
- package/dist/approval.js.map +1 -0
- package/dist/config.d.ts +36 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +209 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +868 -0
- package/dist/index.js.map +1 -0
- package/dist/integration.d.ts +30 -0
- package/dist/integration.d.ts.map +1 -0
- package/dist/integration.js +84 -0
- package/dist/integration.js.map +1 -0
- package/dist/terminal.d.ts +25 -0
- package/dist/terminal.d.ts.map +1 -0
- package/dist/terminal.js +171 -0
- package/dist/terminal.js.map +1 -0
- package/dist/tools/claude-hooks.d.ts +97 -0
- package/dist/tools/claude-hooks.d.ts.map +1 -0
- package/dist/tools/claude-hooks.js +348 -0
- package/dist/tools/claude-hooks.js.map +1 -0
- package/dist/tools/claude-process.d.ts +271 -0
- package/dist/tools/claude-process.d.ts.map +1 -0
- package/dist/tools/claude-process.js +931 -0
- package/dist/tools/claude-process.js.map +1 -0
- package/dist/tools/claude-sessions.d.ts +60 -0
- package/dist/tools/claude-sessions.d.ts.map +1 -0
- package/dist/tools/claude-sessions.js +285 -0
- package/dist/tools/claude-sessions.js.map +1 -0
- package/dist/tools/detector.d.ts +64 -0
- package/dist/tools/detector.d.ts.map +1 -0
- package/dist/tools/detector.js +383 -0
- package/dist/tools/detector.js.map +1 -0
- package/dist/tools/index.d.ts +8 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +15 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/transcript-streamer.d.ts +68 -0
- package/dist/transcript-streamer.d.ts.map +1 -0
- package/dist/transcript-streamer.js +459 -0
- package/dist/transcript-streamer.js.map +1 -0
- package/dist/websocket.d.ts +133 -0
- package/dist/websocket.d.ts.map +1 -0
- package/dist/websocket.js +247 -0
- package/dist/websocket.js.map +1 -0
- package/nul +0 -0
- 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
|
package/dist/api.js.map
ADDED
|
@@ -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"}
|
package/dist/approval.js
ADDED
|
@@ -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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"}
|
package/dist/config.d.ts
ADDED
|
@@ -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"}
|