@leverageaiapps/gogogo 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 +21 -0
- package/README.md +167 -0
- package/dist/cloudflare-tunnel.d.ts +9 -0
- package/dist/cloudflare-tunnel.d.ts.map +1 -0
- package/dist/cloudflare-tunnel.js +209 -0
- package/dist/cloudflare-tunnel.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +62 -0
- package/dist/index.js.map +1 -0
- package/dist/pty.d.ts +22 -0
- package/dist/pty.d.ts.map +1 -0
- package/dist/pty.js +172 -0
- package/dist/pty.js.map +1 -0
- package/dist/session.d.ts +2 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +246 -0
- package/dist/session.js.map +1 -0
- package/dist/web-server.d.ts +3 -0
- package/dist/web-server.d.ts.map +1 -0
- package/dist/web-server.js +447 -0
- package/dist/web-server.js.map +1 -0
- package/package.json +67 -0
- package/public/index.html +509 -0
- package/public/js/terminal.js +729 -0
- package/scripts/postinstall.js +66 -0
- package/scripts/verify-install.js +124 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 LeverageAI Apps
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# GoGoGo
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@leverageaiapps/gogogo)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
CLI tool to forward terminal sessions to your mobile device via Cloudflare Tunnel. **Code anywhere from your pocket.**
|
|
7
|
+
|
|
8
|
+
<p align="center">
|
|
9
|
+
<img src="https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen" alt="Node.js">
|
|
10
|
+
<img src="https://img.shields.io/badge/platform-macOS%20%7C%20Linux-blue" alt="Platform">
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- **Instant Setup** - One command to start forwarding your terminal
|
|
16
|
+
- **Mobile Access** - Access your terminal from any device with a browser
|
|
17
|
+
- **Secure** - Cryptographic token auth with httpOnly cookies
|
|
18
|
+
- **No Port Forwarding** - Uses Cloudflare Quick Tunnel (no account needed)
|
|
19
|
+
- **Real-time** - WebSocket-based communication for instant feedback
|
|
20
|
+
- **PTY Support** - Full terminal emulation with node-pty
|
|
21
|
+
- **Image Paste** - Paste images from clipboard to upload file paths
|
|
22
|
+
|
|
23
|
+
## Prerequisites
|
|
24
|
+
|
|
25
|
+
### Install cloudflared
|
|
26
|
+
|
|
27
|
+
GoGoGo requires `cloudflared` to create secure tunnels:
|
|
28
|
+
|
|
29
|
+
**macOS:**
|
|
30
|
+
```bash
|
|
31
|
+
brew install cloudflared
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Ubuntu/Debian:**
|
|
35
|
+
```bash
|
|
36
|
+
sudo mkdir -p --mode=0755 /usr/share/keyrings
|
|
37
|
+
curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null
|
|
38
|
+
echo 'deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared any main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
|
|
39
|
+
sudo apt-get update && sudo apt-get install cloudflared
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**Arch Linux:**
|
|
43
|
+
```bash
|
|
44
|
+
sudo pacman -S cloudflared
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
For other systems, see the [official installation guide](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation/).
|
|
48
|
+
|
|
49
|
+
## Installation
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npm install -g @leverageaiapps/gogogo
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Verify Installation:**
|
|
56
|
+
```bash
|
|
57
|
+
gogogo --version
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Quick Start
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# Start a terminal session
|
|
64
|
+
gogogo start
|
|
65
|
+
|
|
66
|
+
# Start with a specific command
|
|
67
|
+
gogogo start claude
|
|
68
|
+
gogogo start python
|
|
69
|
+
gogogo start vim
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
A QR code will appear - scan it with your phone to access your terminal. Authentication is automatic via a secure token embedded in the URL.
|
|
73
|
+
|
|
74
|
+
## Usage
|
|
75
|
+
|
|
76
|
+
### Basic Commands
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# Start a terminal session (opens your default shell)
|
|
80
|
+
gogogo start
|
|
81
|
+
|
|
82
|
+
# Start with a machine name
|
|
83
|
+
gogogo start --name "My Laptop"
|
|
84
|
+
|
|
85
|
+
# Start a specific command
|
|
86
|
+
gogogo start claude
|
|
87
|
+
gogogo start "claude --dangerously-skip-permissions"
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Options
|
|
91
|
+
|
|
92
|
+
| Option | Short | Description |
|
|
93
|
+
|--------|-------|-------------|
|
|
94
|
+
| `--name <name>` | `-n` | Set a custom machine name (default: hostname) |
|
|
95
|
+
| `--version` | `-V` | Show version number |
|
|
96
|
+
| `--help` | `-h` | Show help |
|
|
97
|
+
|
|
98
|
+
## How It Works
|
|
99
|
+
|
|
100
|
+
1. Run `gogogo start [command]` in your terminal
|
|
101
|
+
2. GoGoGo starts a local web server and creates a Cloudflare tunnel
|
|
102
|
+
3. A QR code appears with your unique, authenticated URL
|
|
103
|
+
4. Scan the QR code with your phone
|
|
104
|
+
5. Your terminal is now accessible from your mobile device!
|
|
105
|
+
|
|
106
|
+
### Exiting
|
|
107
|
+
|
|
108
|
+
To exit GoGoGo, you can:
|
|
109
|
+
- Type `exit` in the terminal (or the command to exit your current program)
|
|
110
|
+
- Press `Ctrl+C` in the terminal where you ran `gogogo start`
|
|
111
|
+
- Close the terminal window
|
|
112
|
+
|
|
113
|
+
When you see **"Terminal session ended."**, the session has been successfully closed.
|
|
114
|
+
|
|
115
|
+
## Security
|
|
116
|
+
|
|
117
|
+
- **Token Authentication** - Each session gets a unique 128-bit cryptographic token
|
|
118
|
+
- **httpOnly Cookies** - Token is stored as a secure, httpOnly cookie after first visit
|
|
119
|
+
- **WebSocket Auth** - WebSocket connections are verified via cookie
|
|
120
|
+
- **Connection Limits** - Max 10 concurrent WebSocket connections
|
|
121
|
+
- **Rate Limiting** - Message rate limiting per connection
|
|
122
|
+
- **Security Headers** - CSP, HSTS, X-Frame-Options, and more
|
|
123
|
+
|
|
124
|
+
## Troubleshooting
|
|
125
|
+
|
|
126
|
+
### Error: posix_spawnp failed
|
|
127
|
+
|
|
128
|
+
Fix permissions on the node-pty spawn-helper:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# macOS ARM (M1/M2/M3)
|
|
132
|
+
chmod +x node_modules/node-pty/prebuilds/darwin-arm64/spawn-helper
|
|
133
|
+
|
|
134
|
+
# macOS Intel
|
|
135
|
+
chmod +x node_modules/node-pty/prebuilds/darwin-x64/spawn-helper
|
|
136
|
+
|
|
137
|
+
# Linux x64
|
|
138
|
+
chmod +x node_modules/node-pty/prebuilds/linux-x64/spawn-helper
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### cloudflared not found
|
|
142
|
+
|
|
143
|
+
Install cloudflared following the [Prerequisites](#prerequisites) section, then verify:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
cloudflared --version
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Contributing
|
|
150
|
+
|
|
151
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
152
|
+
|
|
153
|
+
1. Fork the repository
|
|
154
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
155
|
+
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
|
156
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
157
|
+
5. Open a Pull Request
|
|
158
|
+
|
|
159
|
+
## License
|
|
160
|
+
|
|
161
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
162
|
+
|
|
163
|
+
## Acknowledgments
|
|
164
|
+
|
|
165
|
+
- [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/) for secure tunneling
|
|
166
|
+
- [node-pty](https://github.com/microsoft/node-pty) for PTY support
|
|
167
|
+
- [xterm.js](https://xtermjs.org/) for terminal emulation in the browser
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Start Cloudflare Quick Tunnel
|
|
3
|
+
* Returns the generated tunnel URL
|
|
4
|
+
*/
|
|
5
|
+
export declare function startTunnel(localPort?: number): Promise<string>;
|
|
6
|
+
export declare function stopTunnel(): void;
|
|
7
|
+
/** Register a callback for when the tunnel crashes unexpectedly */
|
|
8
|
+
export declare function onTunnelCrash(callback: (code: number | null) => void): void;
|
|
9
|
+
//# sourceMappingURL=cloudflare-tunnel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudflare-tunnel.d.ts","sourceRoot":"","sources":["../src/cloudflare-tunnel.ts"],"names":[],"mappings":"AAqFA;;;GAGG;AACH,wBAAgB,WAAW,CAAC,SAAS,GAAE,MAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAcrE;AAsED,wBAAgB,UAAU,IAAI,IAAI,CAQjC;AAED,mEAAmE;AACnE,wBAAgB,aAAa,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,GAAG,IAAI,CAE3E"}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.startTunnel = startTunnel;
|
|
37
|
+
exports.stopTunnel = stopTunnel;
|
|
38
|
+
exports.onTunnelCrash = onTunnelCrash;
|
|
39
|
+
const child_process_1 = require("child_process");
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const os = __importStar(require("os"));
|
|
42
|
+
let tunnelProcess = null;
|
|
43
|
+
let tunnelUrl = null;
|
|
44
|
+
let crashCallbacks = [];
|
|
45
|
+
let intentionalStop = false;
|
|
46
|
+
/**
|
|
47
|
+
* Get platform-specific installation instructions for cloudflared
|
|
48
|
+
*/
|
|
49
|
+
function getInstallationInstructions() {
|
|
50
|
+
const platform = os.platform();
|
|
51
|
+
switch (platform) {
|
|
52
|
+
case 'darwin': // macOS
|
|
53
|
+
return `cloudflared is required for gogogo to work. Please install it using Homebrew:
|
|
54
|
+
|
|
55
|
+
brew install cloudflared
|
|
56
|
+
|
|
57
|
+
Alternatively, you can download it from: https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation/`;
|
|
58
|
+
case 'linux':
|
|
59
|
+
// Detect Linux distribution
|
|
60
|
+
let distroInstructions = '';
|
|
61
|
+
try {
|
|
62
|
+
// Check for common package managers
|
|
63
|
+
if (fs.existsSync('/usr/bin/apt') || fs.existsSync('/usr/bin/apt-get')) {
|
|
64
|
+
// Debian/Ubuntu
|
|
65
|
+
distroInstructions = ` # Add Cloudflare GPG key
|
|
66
|
+
sudo mkdir -p --mode=0755 /usr/share/keyrings
|
|
67
|
+
curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null
|
|
68
|
+
|
|
69
|
+
# Add Cloudflare repository
|
|
70
|
+
echo 'deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared any main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
|
|
71
|
+
|
|
72
|
+
# Install cloudflared
|
|
73
|
+
sudo apt-get update && sudo apt-get install cloudflared`;
|
|
74
|
+
}
|
|
75
|
+
else if (fs.existsSync('/usr/bin/yum')) {
|
|
76
|
+
// CentOS/RHEL
|
|
77
|
+
distroInstructions = ` # Add Cloudflare repository
|
|
78
|
+
curl -fsSl https://pkg.cloudflare.com/cloudflared.repo | sudo tee /etc/yum.repos.d/cloudflared.repo
|
|
79
|
+
|
|
80
|
+
# Install cloudflared
|
|
81
|
+
sudo yum update && sudo yum install cloudflared`;
|
|
82
|
+
}
|
|
83
|
+
else if (fs.existsSync('/usr/bin/dnf')) {
|
|
84
|
+
// Fedora
|
|
85
|
+
distroInstructions = ` # Add Cloudflare repository
|
|
86
|
+
sudo dnf config-manager --add-repo https://pkg.cloudflare.com/cloudflared.repo
|
|
87
|
+
|
|
88
|
+
# Install cloudflared
|
|
89
|
+
sudo dnf install cloudflared`;
|
|
90
|
+
}
|
|
91
|
+
else if (fs.existsSync('/usr/bin/pacman')) {
|
|
92
|
+
// Arch Linux
|
|
93
|
+
distroInstructions = ` # Install from community repository
|
|
94
|
+
sudo pacman -S cloudflared`;
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
// Generic Linux
|
|
98
|
+
distroInstructions = ` # Download and install manually
|
|
99
|
+
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 -o cloudflared
|
|
100
|
+
chmod +x cloudflared
|
|
101
|
+
sudo mv cloudflared /usr/local/bin/`;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
// Fallback to generic instructions
|
|
106
|
+
distroInstructions = ` # Download and install manually
|
|
107
|
+
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 -o cloudflared
|
|
108
|
+
chmod +x cloudflared
|
|
109
|
+
sudo mv cloudflared /usr/local/bin/`;
|
|
110
|
+
}
|
|
111
|
+
return `cloudflared is required for gogogo to work. Please install it:
|
|
112
|
+
|
|
113
|
+
${distroInstructions}
|
|
114
|
+
|
|
115
|
+
For other distributions, see: https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation/`;
|
|
116
|
+
default:
|
|
117
|
+
return `cloudflared is required for gogogo to work. Please install it from:
|
|
118
|
+
https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation/`;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Start Cloudflare Quick Tunnel
|
|
123
|
+
* Returns the generated tunnel URL
|
|
124
|
+
*/
|
|
125
|
+
function startTunnel(localPort = 4020) {
|
|
126
|
+
return new Promise((resolve, reject) => {
|
|
127
|
+
// Check if cloudflared is installed
|
|
128
|
+
const which = (0, child_process_1.spawn)('which', ['cloudflared']);
|
|
129
|
+
which.on('close', (code) => {
|
|
130
|
+
if (code !== 0) {
|
|
131
|
+
reject(new Error(getInstallationInstructions()));
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
startTunnelProcess(localPort, resolve, reject);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
function startTunnelProcess(localPort, resolve, reject) {
|
|
139
|
+
// Inherit process.env (proxy vars already cleaned by session.ts)
|
|
140
|
+
const env = { ...process.env };
|
|
141
|
+
// Use --url for quick tunnel (no account needed)
|
|
142
|
+
// Bypass proxy to avoid TLS handshake issues
|
|
143
|
+
tunnelProcess = (0, child_process_1.spawn)('cloudflared', ['tunnel', '--url', `http://localhost:${localPort}`], {
|
|
144
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
145
|
+
env: env,
|
|
146
|
+
});
|
|
147
|
+
let urlFound = false;
|
|
148
|
+
const timeout = setTimeout(() => {
|
|
149
|
+
if (!urlFound) {
|
|
150
|
+
reject(new Error('Timeout waiting for tunnel URL'));
|
|
151
|
+
stopTunnel();
|
|
152
|
+
}
|
|
153
|
+
}, 30000); // 30 second timeout
|
|
154
|
+
// cloudflared outputs the URL to stderr
|
|
155
|
+
tunnelProcess.stderr?.on('data', (data) => {
|
|
156
|
+
const output = data.toString();
|
|
157
|
+
const urlMatch = output.match(/https:\/\/[a-z0-9-]+\.trycloudflare\.com/);
|
|
158
|
+
if (urlMatch && !urlFound) {
|
|
159
|
+
urlFound = true;
|
|
160
|
+
clearTimeout(timeout);
|
|
161
|
+
tunnelUrl = urlMatch[0];
|
|
162
|
+
resolve(tunnelUrl);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
tunnelProcess.stdout?.on('data', (data) => {
|
|
166
|
+
const output = data.toString();
|
|
167
|
+
const urlMatch = output.match(/https:\/\/[a-z0-9-]+\.trycloudflare\.com/);
|
|
168
|
+
if (urlMatch && !urlFound) {
|
|
169
|
+
urlFound = true;
|
|
170
|
+
clearTimeout(timeout);
|
|
171
|
+
tunnelUrl = urlMatch[0];
|
|
172
|
+
resolve(tunnelUrl);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
tunnelProcess.on('error', (err) => {
|
|
176
|
+
clearTimeout(timeout);
|
|
177
|
+
console.error(' [Tunnel] Failed to start:', err);
|
|
178
|
+
reject(err);
|
|
179
|
+
});
|
|
180
|
+
tunnelProcess.on('close', (code) => {
|
|
181
|
+
if (!urlFound) {
|
|
182
|
+
clearTimeout(timeout);
|
|
183
|
+
reject(new Error(`cloudflared exited with code ${code}`));
|
|
184
|
+
}
|
|
185
|
+
else if (!intentionalStop) {
|
|
186
|
+
// Tunnel crashed after URL was found — notify listeners
|
|
187
|
+
for (const cb of crashCallbacks) {
|
|
188
|
+
cb(code);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
tunnelProcess = null;
|
|
192
|
+
tunnelUrl = null;
|
|
193
|
+
intentionalStop = false;
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
function stopTunnel() {
|
|
197
|
+
if (tunnelProcess) {
|
|
198
|
+
intentionalStop = true;
|
|
199
|
+
tunnelProcess.kill('SIGTERM');
|
|
200
|
+
tunnelProcess = null;
|
|
201
|
+
tunnelUrl = null;
|
|
202
|
+
}
|
|
203
|
+
crashCallbacks = [];
|
|
204
|
+
}
|
|
205
|
+
/** Register a callback for when the tunnel crashes unexpectedly */
|
|
206
|
+
function onTunnelCrash(callback) {
|
|
207
|
+
crashCallbacks.push(callback);
|
|
208
|
+
}
|
|
209
|
+
//# sourceMappingURL=cloudflare-tunnel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudflare-tunnel.js","sourceRoot":"","sources":["../src/cloudflare-tunnel.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
const commander_1 = require("commander");
|
|
38
|
+
const session_1 = require("./session");
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
// Read version from package.json
|
|
42
|
+
const packageJsonPath = path.join(__dirname, '..', 'package.json');
|
|
43
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
44
|
+
const version = packageJson.version;
|
|
45
|
+
const program = new commander_1.Command();
|
|
46
|
+
program
|
|
47
|
+
.name('gogogo')
|
|
48
|
+
.description('gogogo - Forward your terminal to your mobile device')
|
|
49
|
+
.version(version);
|
|
50
|
+
program
|
|
51
|
+
.command('start')
|
|
52
|
+
.description('Start a new gogogo session')
|
|
53
|
+
.argument('[command...]', 'Command to run (default: none, opens terminal only)')
|
|
54
|
+
.option('-n, --name <name>', 'Machine name to display', process.env.HOSTNAME || 'My Computer')
|
|
55
|
+
.action(async (command, options) => {
|
|
56
|
+
console.log('');
|
|
57
|
+
console.log(' 🚀 gogogo - Coding anywhere in your pocket');
|
|
58
|
+
console.log('');
|
|
59
|
+
await (0, session_1.startSession)(options.name, command);
|
|
60
|
+
});
|
|
61
|
+
program.parse();
|
|
62
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEA,yCAAoC;AACpC,uCAAyC;AACzC,uCAAyB;AACzB,2CAA6B;AAE7B,iCAAiC;AACjC,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC;AACnE,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC,CAAC;AAC1E,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC;AAEpC,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;AAE9B,OAAO;KACF,IAAI,CAAC,QAAQ,CAAC;KACd,WAAW,CAAC,sDAAsD,CAAC;KACnE,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtB,OAAO;KACF,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,4BAA4B,CAAC;KACzC,QAAQ,CAAC,cAAc,EAAE,qDAAqD,CAAC;KAC/E,MAAM,CAAC,mBAAmB,EAAE,yBAAyB,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,aAAa,CAAC;KAC7F,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE;IAC/B,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,8CAA8C,CAAC,CAAC;IAC5D,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAEhB,MAAM,IAAA,sBAAY,EAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAC9C,CAAC,CAAC,CAAC;AAEP,OAAO,CAAC,KAAK,EAAE,CAAC"}
|
package/dist/pty.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as pty from 'node-pty';
|
|
2
|
+
export interface PTYOptions {
|
|
3
|
+
cols?: number;
|
|
4
|
+
rows?: number;
|
|
5
|
+
command?: string;
|
|
6
|
+
args?: string[];
|
|
7
|
+
cwd?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function getLocalSize(): {
|
|
10
|
+
cols: number;
|
|
11
|
+
rows: number;
|
|
12
|
+
};
|
|
13
|
+
export declare function spawnPTY(options?: PTYOptions): pty.IPty;
|
|
14
|
+
export declare function writeToPTY(data: string): void;
|
|
15
|
+
export declare function resizePTY(cols: number, rows: number): void;
|
|
16
|
+
export declare function killPTY(): void;
|
|
17
|
+
export declare function onPTYData(callback: (data: string) => void): void;
|
|
18
|
+
export declare function onPTYExit(callback: (code: number) => void): void;
|
|
19
|
+
export declare function onLocalTerminalResize(callback: () => void): void;
|
|
20
|
+
/** Clear all registered callbacks (for cleanup/testing) */
|
|
21
|
+
export declare function clearCallbacks(): void;
|
|
22
|
+
//# sourceMappingURL=pty.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pty.d.ts","sourceRoot":"","sources":["../src/pty.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAEhC,MAAM,WAAW,UAAU;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;CAChB;AAcD,wBAAgB,YAAY,IAAI;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAE7D;AAED,wBAAgB,QAAQ,CAAC,OAAO,GAAE,UAAe,GAAG,GAAG,CAAC,IAAI,CA6E3D;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAI7C;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAI1D;AAED,wBAAgB,OAAO,IAAI,IAAI,CAqB9B;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAEhE;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAEhE;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI,CAEhE;AAED,2DAA2D;AAC3D,wBAAgB,cAAc,IAAI,IAAI,CAIrC"}
|