nikcli-remote 1.0.8 → 1.0.10
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/dist/chunk-GI5RMYH6.js +37 -0
- package/dist/chunk-TIYMAVGV.js +1126 -0
- package/dist/index.cjs +1820 -2881
- package/dist/index.d.cts +91 -65
- package/dist/index.d.ts +91 -65
- package/dist/index.js +228 -7
- package/dist/{localtunnel-XT32JGNN.js → localtunnel-6DCQIYU6.js} +1 -1
- package/dist/server-O3KTQ4KJ.js +7 -0
- package/package.json +1 -1
- package/src/index.ts +57 -12
- package/src/server.ts +83 -110
- package/src/tunnel.ts +24 -0
- package/src/web-client.ts +593 -236
- package/dist/chunk-FYVPBMXV.js +0 -2390
- package/dist/chunk-MCKGQKYU.js +0 -15
- package/dist/server-OYMSDTRP.js +0 -7
package/dist/index.js
CHANGED
|
@@ -2,15 +2,233 @@ import {
|
|
|
2
2
|
DEFAULT_CONFIG,
|
|
3
3
|
MessageTypes,
|
|
4
4
|
RemoteServer,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
5
|
+
getWebClient,
|
|
6
|
+
init_web_client
|
|
7
|
+
} from "./chunk-TIYMAVGV.js";
|
|
8
|
+
import "./chunk-GI5RMYH6.js";
|
|
9
|
+
|
|
10
|
+
// src/tunnel.ts
|
|
11
|
+
import { spawn } from "child_process";
|
|
12
|
+
async function createTunnel(port, provider = "cloudflared") {
|
|
13
|
+
const manager = new TunnelManager(provider);
|
|
14
|
+
const url = await manager.create(port);
|
|
15
|
+
return {
|
|
16
|
+
url,
|
|
17
|
+
provider,
|
|
18
|
+
close: () => manager.close()
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
var TunnelManager = class {
|
|
22
|
+
provider;
|
|
23
|
+
process = null;
|
|
24
|
+
url = null;
|
|
25
|
+
tunnelInstance = null;
|
|
26
|
+
constructor(provider) {
|
|
27
|
+
this.provider = provider;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Create tunnel and return public URL
|
|
31
|
+
*/
|
|
32
|
+
async create(port) {
|
|
33
|
+
switch (this.provider) {
|
|
34
|
+
case "localtunnel":
|
|
35
|
+
return this.createLocaltunnel(port);
|
|
36
|
+
case "cloudflared":
|
|
37
|
+
return this.createCloudflared(port);
|
|
38
|
+
case "ngrok":
|
|
39
|
+
return this.createNgrok(port);
|
|
40
|
+
default:
|
|
41
|
+
throw new Error(`Unknown tunnel provider: ${this.provider}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Close tunnel
|
|
46
|
+
*/
|
|
47
|
+
async close() {
|
|
48
|
+
if (this.tunnelInstance?.close) {
|
|
49
|
+
this.tunnelInstance.close();
|
|
50
|
+
this.tunnelInstance = null;
|
|
51
|
+
}
|
|
52
|
+
if (this.process) {
|
|
53
|
+
this.process.kill();
|
|
54
|
+
this.process = null;
|
|
55
|
+
}
|
|
56
|
+
this.url = null;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Get tunnel URL
|
|
60
|
+
*/
|
|
61
|
+
getUrl() {
|
|
62
|
+
return this.url;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Create localtunnel
|
|
66
|
+
*/
|
|
67
|
+
async createLocaltunnel(port) {
|
|
68
|
+
try {
|
|
69
|
+
const localtunnel = await import("./localtunnel-6DCQIYU6.js");
|
|
70
|
+
const tunnel = await localtunnel.default({ port });
|
|
71
|
+
this.tunnelInstance = tunnel;
|
|
72
|
+
this.url = tunnel.url;
|
|
73
|
+
tunnel.on("close", () => {
|
|
74
|
+
this.url = null;
|
|
75
|
+
});
|
|
76
|
+
return tunnel.url;
|
|
77
|
+
} catch {
|
|
78
|
+
return this.createLocaltunnelCli(port);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Create localtunnel via CLI
|
|
83
|
+
*/
|
|
84
|
+
createLocaltunnelCli(port) {
|
|
85
|
+
return new Promise((resolve, reject) => {
|
|
86
|
+
this.process = spawn("npx", ["localtunnel", "--port", port.toString(), "--print-requests", "false"], {
|
|
87
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
88
|
+
shell: true
|
|
89
|
+
});
|
|
90
|
+
let output = "";
|
|
91
|
+
const timeout = setTimeout(() => {
|
|
92
|
+
reject(new Error("Localtunnel timeout"));
|
|
93
|
+
}, 3e4);
|
|
94
|
+
this.process.stdout?.on("data", (data) => {
|
|
95
|
+
output += data.toString();
|
|
96
|
+
const match = output.match(/your url is:\s*(https?:\/\/[^\s]+)/i);
|
|
97
|
+
if (match) {
|
|
98
|
+
clearTimeout(timeout);
|
|
99
|
+
this.url = match[1];
|
|
100
|
+
resolve(match[1]);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
this.process.stderr?.on("data", () => {
|
|
104
|
+
});
|
|
105
|
+
this.process.on("error", (error) => {
|
|
106
|
+
clearTimeout(timeout);
|
|
107
|
+
reject(error);
|
|
108
|
+
});
|
|
109
|
+
this.process.on("exit", (code) => {
|
|
110
|
+
if (code !== 0 && !this.url) {
|
|
111
|
+
clearTimeout(timeout);
|
|
112
|
+
reject(new Error(`Localtunnel exited with code ${code}`));
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Create cloudflared tunnel
|
|
119
|
+
*/
|
|
120
|
+
createCloudflared(port) {
|
|
121
|
+
return new Promise((resolve, reject) => {
|
|
122
|
+
this.process = spawn(
|
|
123
|
+
"cloudflared",
|
|
124
|
+
["tunnel", "--url", `http://localhost:${port}`, "--metrics", "localhost:0"],
|
|
125
|
+
{
|
|
126
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
127
|
+
}
|
|
128
|
+
);
|
|
129
|
+
let output = "";
|
|
130
|
+
const timeout = setTimeout(() => {
|
|
131
|
+
reject(new Error("Cloudflared timeout"));
|
|
132
|
+
}, 3e4);
|
|
133
|
+
const handleData = (data) => {
|
|
134
|
+
output += data.toString();
|
|
135
|
+
const match = output.match(/(https:\/\/[^\s]+\.trycloudflare\.com)/i);
|
|
136
|
+
if (match) {
|
|
137
|
+
clearTimeout(timeout);
|
|
138
|
+
this.url = match[1];
|
|
139
|
+
resolve(match[1]);
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
this.process.stdout?.on("data", handleData);
|
|
143
|
+
this.process.stderr?.on("data", handleData);
|
|
144
|
+
this.process.on("error", (error) => {
|
|
145
|
+
clearTimeout(timeout);
|
|
146
|
+
reject(error);
|
|
147
|
+
});
|
|
148
|
+
this.process.on("exit", (code) => {
|
|
149
|
+
if (code !== 0 && !this.url) {
|
|
150
|
+
clearTimeout(timeout);
|
|
151
|
+
reject(new Error(`Cloudflared exited with code ${code}`));
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Create ngrok tunnel
|
|
158
|
+
*/
|
|
159
|
+
createNgrok(port) {
|
|
160
|
+
return new Promise((resolve, reject) => {
|
|
161
|
+
this.process = spawn("ngrok", ["http", port.toString(), "--log=stdout", "--log-level=info"], {
|
|
162
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
163
|
+
});
|
|
164
|
+
let output = "";
|
|
165
|
+
const timeout = setTimeout(() => {
|
|
166
|
+
reject(new Error("Ngrok timeout"));
|
|
167
|
+
}, 3e4);
|
|
168
|
+
this.process.stdout?.on("data", (data) => {
|
|
169
|
+
output += data.toString();
|
|
170
|
+
const match = output.match(/url=(https?:\/\/[^\s]+)/i);
|
|
171
|
+
if (match) {
|
|
172
|
+
clearTimeout(timeout);
|
|
173
|
+
this.url = match[1];
|
|
174
|
+
resolve(match[1]);
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
this.process.stderr?.on("data", () => {
|
|
178
|
+
});
|
|
179
|
+
this.process.on("error", (error) => {
|
|
180
|
+
clearTimeout(timeout);
|
|
181
|
+
reject(error);
|
|
182
|
+
});
|
|
183
|
+
this.process.on("exit", (code) => {
|
|
184
|
+
if (code !== 0 && !this.url) {
|
|
185
|
+
clearTimeout(timeout);
|
|
186
|
+
reject(new Error(`Ngrok exited with code ${code}`));
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
async function checkTunnelAvailability(provider) {
|
|
193
|
+
try {
|
|
194
|
+
const { execSync } = await import("child_process");
|
|
195
|
+
switch (provider) {
|
|
196
|
+
case "localtunnel":
|
|
197
|
+
try {
|
|
198
|
+
await import("./localtunnel-6DCQIYU6.js");
|
|
199
|
+
return true;
|
|
200
|
+
} catch {
|
|
201
|
+
execSync("npx localtunnel --version", { stdio: "pipe" });
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
case "cloudflared":
|
|
205
|
+
execSync("cloudflared --version", { stdio: "pipe" });
|
|
206
|
+
return true;
|
|
207
|
+
case "ngrok":
|
|
208
|
+
execSync("ngrok version", { stdio: "pipe" });
|
|
209
|
+
return true;
|
|
210
|
+
default:
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
} catch {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
async function findAvailableTunnel() {
|
|
218
|
+
const providers = ["localtunnel", "cloudflared", "ngrok"];
|
|
219
|
+
for (const provider of providers) {
|
|
220
|
+
if (await checkTunnelAvailability(provider)) {
|
|
221
|
+
return provider;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
9
226
|
|
|
10
227
|
// src/index.ts
|
|
228
|
+
init_web_client();
|
|
11
229
|
async function createRemoteServer(config = {}) {
|
|
12
|
-
const { RemoteServer:
|
|
13
|
-
const server = new
|
|
230
|
+
const { RemoteServer: RemoteServerCls } = await import("./server-O3KTQ4KJ.js");
|
|
231
|
+
const server = new RemoteServerCls(config);
|
|
14
232
|
const session = await server.start();
|
|
15
233
|
return { server, session };
|
|
16
234
|
}
|
|
@@ -18,7 +236,10 @@ export {
|
|
|
18
236
|
DEFAULT_CONFIG,
|
|
19
237
|
MessageTypes,
|
|
20
238
|
RemoteServer,
|
|
21
|
-
|
|
239
|
+
TunnelManager,
|
|
240
|
+
checkTunnelAvailability,
|
|
22
241
|
createRemoteServer,
|
|
242
|
+
createTunnel,
|
|
243
|
+
findAvailableTunnel,
|
|
23
244
|
getWebClient
|
|
24
245
|
};
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,25 +1,70 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @nikcli/remote
|
|
3
|
-
* Native remote terminal server for NikCLI
|
|
2
|
+
* @nikcli/remote - Enterprise Terminal Streaming
|
|
4
3
|
*
|
|
5
|
-
*
|
|
4
|
+
* Direct stdin/stdout streaming for true real-time terminal access.
|
|
5
|
+
* No fake PTY - mobile sees your actual NikCLI terminal.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { RemoteServer, createTunnel } from '@nikcli/remote'
|
|
10
|
+
*
|
|
11
|
+
* const server = new RemoteServer()
|
|
12
|
+
* const session = await server.start({
|
|
13
|
+
* name: 'my-terminal',
|
|
14
|
+
* processForStreaming: {
|
|
15
|
+
* stdout: process.stdout,
|
|
16
|
+
* stdin: process.stdin,
|
|
17
|
+
* },
|
|
18
|
+
* })
|
|
19
|
+
*
|
|
20
|
+
* // Create public tunnel
|
|
21
|
+
* const tunnelUrl = await createTunnel(session.port, 'cloudflared')
|
|
22
|
+
* console.log('Access your terminal:', tunnelUrl)
|
|
23
|
+
* ```
|
|
6
24
|
*/
|
|
7
25
|
|
|
8
|
-
|
|
9
|
-
export {
|
|
26
|
+
// Core server
|
|
27
|
+
export { RemoteServer } from './server'
|
|
28
|
+
export type { RemoteServerEvents } from './server'
|
|
29
|
+
|
|
30
|
+
// Tunnel management
|
|
31
|
+
export {
|
|
32
|
+
TunnelManager,
|
|
33
|
+
createTunnel,
|
|
34
|
+
checkTunnelAvailability,
|
|
35
|
+
findAvailableTunnel,
|
|
36
|
+
type TunnelResult,
|
|
37
|
+
} from './tunnel'
|
|
38
|
+
|
|
39
|
+
// Web client
|
|
10
40
|
export { getWebClient } from './web-client'
|
|
41
|
+
|
|
42
|
+
// Types - re-export for convenience
|
|
11
43
|
export type {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
44
|
+
DeviceInfo,
|
|
45
|
+
SessionStatus,
|
|
46
|
+
RemoteNotification,
|
|
47
|
+
ClientMessage,
|
|
48
|
+
BroadcastMessage,
|
|
49
|
+
ServerConfig,
|
|
50
|
+
RemoteSession,
|
|
51
|
+
TunnelProvider,
|
|
15
52
|
} from './types'
|
|
53
|
+
|
|
54
|
+
// Constants
|
|
16
55
|
export { DEFAULT_CONFIG, MessageTypes } from './types'
|
|
17
56
|
|
|
57
|
+
import type { RemoteServer } from './server'
|
|
58
|
+
import type { ServerConfig, RemoteSession } from './types'
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Create a remote server with streaming in one call
|
|
62
|
+
*/
|
|
18
63
|
export async function createRemoteServer(
|
|
19
|
-
config: Partial<
|
|
20
|
-
) {
|
|
21
|
-
const { RemoteServer } = await import('./server')
|
|
22
|
-
const server = new
|
|
64
|
+
config: Partial<ServerConfig> = {}
|
|
65
|
+
): Promise<{ server: RemoteServer; session: RemoteSession }> {
|
|
66
|
+
const { RemoteServer: RemoteServerCls } = await import('./server')
|
|
67
|
+
const server = new RemoteServerCls(config)
|
|
23
68
|
const session = await server.start()
|
|
24
69
|
return { server, session }
|
|
25
70
|
}
|