nikcli-remote 1.0.1 → 1.0.2
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-QLGPGAPC.js → chunk-DRL7JX54.js} +225 -639
- package/dist/index.cjs +225 -772
- package/dist/index.d.cts +3 -78
- package/dist/index.d.ts +3 -78
- package/dist/index.js +4 -135
- package/dist/{server-ISK4MDQQ.js → server-J7SPDGZO.js} +1 -1
- package/package.json +1 -1
- package/src/index.ts +4 -61
- package/src/server.ts +3 -6
- package/src/tunnel.ts +11 -7
- package/src/web-client.ts +214 -593
package/dist/index.d.cts
CHANGED
|
@@ -284,83 +284,8 @@ declare class TerminalManager extends EventEmitter {
|
|
|
284
284
|
}
|
|
285
285
|
|
|
286
286
|
/**
|
|
287
|
-
* @nikcli/remote -
|
|
288
|
-
*
|
|
289
|
-
*/
|
|
290
|
-
|
|
291
|
-
declare class TunnelManager {
|
|
292
|
-
private provider;
|
|
293
|
-
private process;
|
|
294
|
-
private url;
|
|
295
|
-
private tunnelInstance;
|
|
296
|
-
constructor(provider: TunnelProvider);
|
|
297
|
-
/**
|
|
298
|
-
* Create tunnel and return public URL
|
|
299
|
-
*/
|
|
300
|
-
create(port: number): Promise<string>;
|
|
301
|
-
/**
|
|
302
|
-
* Close tunnel
|
|
303
|
-
*/
|
|
304
|
-
close(): Promise<void>;
|
|
305
|
-
/**
|
|
306
|
-
* Get tunnel URL
|
|
307
|
-
*/
|
|
308
|
-
getUrl(): string | null;
|
|
309
|
-
/**
|
|
310
|
-
* Create localtunnel
|
|
311
|
-
*/
|
|
312
|
-
private createLocaltunnel;
|
|
313
|
-
/**
|
|
314
|
-
* Create localtunnel via CLI
|
|
315
|
-
*/
|
|
316
|
-
private createLocaltunnelCli;
|
|
317
|
-
/**
|
|
318
|
-
* Create cloudflared tunnel
|
|
319
|
-
*/
|
|
320
|
-
private createCloudflared;
|
|
321
|
-
/**
|
|
322
|
-
* Create ngrok tunnel
|
|
323
|
-
*/
|
|
324
|
-
private createNgrok;
|
|
325
|
-
}
|
|
326
|
-
/**
|
|
327
|
-
* Check if a tunnel provider is available
|
|
328
|
-
*/
|
|
329
|
-
declare function checkTunnelAvailability(provider: TunnelProvider): Promise<boolean>;
|
|
330
|
-
/**
|
|
331
|
-
* Find best available tunnel provider
|
|
332
|
-
*/
|
|
333
|
-
declare function findAvailableTunnel(): Promise<TunnelProvider | null>;
|
|
334
|
-
|
|
335
|
-
/**
|
|
336
|
-
* @nikcli/remote - QR Code Generator
|
|
337
|
-
* Terminal QR code rendering for session URLs
|
|
338
|
-
*/
|
|
339
|
-
|
|
340
|
-
interface QROptions {
|
|
341
|
-
small?: boolean;
|
|
342
|
-
margin?: number;
|
|
343
|
-
}
|
|
344
|
-
/**
|
|
345
|
-
* Generate QR code string for terminal display
|
|
346
|
-
*/
|
|
347
|
-
declare function generateQR(url: string, options?: QROptions): Promise<string>;
|
|
348
|
-
/**
|
|
349
|
-
* Generate QR code as data URL (for web/image)
|
|
350
|
-
*/
|
|
351
|
-
declare function generateQRDataURL(url: string): Promise<string | null>;
|
|
352
|
-
/**
|
|
353
|
-
* Render session info with QR code
|
|
354
|
-
*/
|
|
355
|
-
declare function renderSessionCard(session: RemoteSession): Promise<string>;
|
|
356
|
-
/**
|
|
357
|
-
* Simple progress bar
|
|
358
|
-
*/
|
|
359
|
-
declare function progressBar(current: number, total: number, width?: number): string;
|
|
360
|
-
|
|
361
|
-
/**
|
|
362
|
-
* @nikcli/remote - Mobile Web Client
|
|
363
|
-
* Touch-friendly terminal interface for mobile devices
|
|
287
|
+
* @nikcli/remote - Ghostty-web Style Terminal Client
|
|
288
|
+
* Uses hterm for full terminal emulation
|
|
364
289
|
*/
|
|
365
290
|
declare function getWebClient(): string;
|
|
366
291
|
|
|
@@ -369,4 +294,4 @@ declare function createRemoteServer(config?: Partial<ServerConfig>): Promise<{
|
|
|
369
294
|
session: RemoteSession;
|
|
370
295
|
}>;
|
|
371
296
|
|
|
372
|
-
export { type BroadcastMessage, type
|
|
297
|
+
export { type BroadcastMessage, type ClientMessage, type CommandMessage, DEFAULT_CONFIG, type DeviceInfo, MessageTypes, type RemoteNotification, RemoteServer, type RemoteServerEvents, type RemoteSession, type ServerConfig, type ServerMessage, type SessionStatus, type TerminalConfig, type TerminalData, TerminalManager, type TerminalResize, type TunnelProvider, createRemoteServer, getWebClient };
|
package/dist/index.d.ts
CHANGED
|
@@ -284,83 +284,8 @@ declare class TerminalManager extends EventEmitter {
|
|
|
284
284
|
}
|
|
285
285
|
|
|
286
286
|
/**
|
|
287
|
-
* @nikcli/remote -
|
|
288
|
-
*
|
|
289
|
-
*/
|
|
290
|
-
|
|
291
|
-
declare class TunnelManager {
|
|
292
|
-
private provider;
|
|
293
|
-
private process;
|
|
294
|
-
private url;
|
|
295
|
-
private tunnelInstance;
|
|
296
|
-
constructor(provider: TunnelProvider);
|
|
297
|
-
/**
|
|
298
|
-
* Create tunnel and return public URL
|
|
299
|
-
*/
|
|
300
|
-
create(port: number): Promise<string>;
|
|
301
|
-
/**
|
|
302
|
-
* Close tunnel
|
|
303
|
-
*/
|
|
304
|
-
close(): Promise<void>;
|
|
305
|
-
/**
|
|
306
|
-
* Get tunnel URL
|
|
307
|
-
*/
|
|
308
|
-
getUrl(): string | null;
|
|
309
|
-
/**
|
|
310
|
-
* Create localtunnel
|
|
311
|
-
*/
|
|
312
|
-
private createLocaltunnel;
|
|
313
|
-
/**
|
|
314
|
-
* Create localtunnel via CLI
|
|
315
|
-
*/
|
|
316
|
-
private createLocaltunnelCli;
|
|
317
|
-
/**
|
|
318
|
-
* Create cloudflared tunnel
|
|
319
|
-
*/
|
|
320
|
-
private createCloudflared;
|
|
321
|
-
/**
|
|
322
|
-
* Create ngrok tunnel
|
|
323
|
-
*/
|
|
324
|
-
private createNgrok;
|
|
325
|
-
}
|
|
326
|
-
/**
|
|
327
|
-
* Check if a tunnel provider is available
|
|
328
|
-
*/
|
|
329
|
-
declare function checkTunnelAvailability(provider: TunnelProvider): Promise<boolean>;
|
|
330
|
-
/**
|
|
331
|
-
* Find best available tunnel provider
|
|
332
|
-
*/
|
|
333
|
-
declare function findAvailableTunnel(): Promise<TunnelProvider | null>;
|
|
334
|
-
|
|
335
|
-
/**
|
|
336
|
-
* @nikcli/remote - QR Code Generator
|
|
337
|
-
* Terminal QR code rendering for session URLs
|
|
338
|
-
*/
|
|
339
|
-
|
|
340
|
-
interface QROptions {
|
|
341
|
-
small?: boolean;
|
|
342
|
-
margin?: number;
|
|
343
|
-
}
|
|
344
|
-
/**
|
|
345
|
-
* Generate QR code string for terminal display
|
|
346
|
-
*/
|
|
347
|
-
declare function generateQR(url: string, options?: QROptions): Promise<string>;
|
|
348
|
-
/**
|
|
349
|
-
* Generate QR code as data URL (for web/image)
|
|
350
|
-
*/
|
|
351
|
-
declare function generateQRDataURL(url: string): Promise<string | null>;
|
|
352
|
-
/**
|
|
353
|
-
* Render session info with QR code
|
|
354
|
-
*/
|
|
355
|
-
declare function renderSessionCard(session: RemoteSession): Promise<string>;
|
|
356
|
-
/**
|
|
357
|
-
* Simple progress bar
|
|
358
|
-
*/
|
|
359
|
-
declare function progressBar(current: number, total: number, width?: number): string;
|
|
360
|
-
|
|
361
|
-
/**
|
|
362
|
-
* @nikcli/remote - Mobile Web Client
|
|
363
|
-
* Touch-friendly terminal interface for mobile devices
|
|
287
|
+
* @nikcli/remote - Ghostty-web Style Terminal Client
|
|
288
|
+
* Uses hterm for full terminal emulation
|
|
364
289
|
*/
|
|
365
290
|
declare function getWebClient(): string;
|
|
366
291
|
|
|
@@ -369,4 +294,4 @@ declare function createRemoteServer(config?: Partial<ServerConfig>): Promise<{
|
|
|
369
294
|
session: RemoteSession;
|
|
370
295
|
}>;
|
|
371
296
|
|
|
372
|
-
export { type BroadcastMessage, type
|
|
297
|
+
export { type BroadcastMessage, type ClientMessage, type CommandMessage, DEFAULT_CONFIG, type DeviceInfo, MessageTypes, type RemoteNotification, RemoteServer, type RemoteServerEvents, type RemoteSession, type ServerConfig, type ServerMessage, type SessionStatus, type TerminalConfig, type TerminalData, TerminalManager, type TerminalResize, type TunnelProvider, createRemoteServer, getWebClient };
|
package/dist/index.js
CHANGED
|
@@ -3,137 +3,13 @@ import {
|
|
|
3
3
|
MessageTypes,
|
|
4
4
|
RemoteServer,
|
|
5
5
|
TerminalManager,
|
|
6
|
-
TunnelManager,
|
|
7
|
-
checkTunnelAvailability,
|
|
8
|
-
findAvailableTunnel,
|
|
9
6
|
getWebClient
|
|
10
|
-
} from "./chunk-
|
|
11
|
-
import
|
|
12
|
-
__require
|
|
13
|
-
} from "./chunk-MCKGQKYU.js";
|
|
14
|
-
|
|
15
|
-
// src/qrcode.ts
|
|
16
|
-
var QRCode = null;
|
|
17
|
-
try {
|
|
18
|
-
QRCode = __require("qrcode");
|
|
19
|
-
} catch {
|
|
20
|
-
}
|
|
21
|
-
async function generateQR(url, options = {}) {
|
|
22
|
-
if (!QRCode) {
|
|
23
|
-
return generateFallbackQR(url);
|
|
24
|
-
}
|
|
25
|
-
try {
|
|
26
|
-
const qrString = await QRCode.toString(url, {
|
|
27
|
-
type: "terminal",
|
|
28
|
-
small: options.small ?? true,
|
|
29
|
-
margin: options.margin ?? 1
|
|
30
|
-
});
|
|
31
|
-
return qrString;
|
|
32
|
-
} catch {
|
|
33
|
-
return generateFallbackQR(url);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
async function generateQRDataURL(url) {
|
|
37
|
-
if (!QRCode) return null;
|
|
38
|
-
try {
|
|
39
|
-
return await QRCode.toDataURL(url, {
|
|
40
|
-
margin: 2,
|
|
41
|
-
width: 256,
|
|
42
|
-
color: {
|
|
43
|
-
dark: "#000000",
|
|
44
|
-
light: "#ffffff"
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
} catch {
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
function generateFallbackQR(url) {
|
|
52
|
-
return `
|
|
53
|
-
\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
|
|
54
|
-
\u2502 \u2502
|
|
55
|
-
\u2502 QR Code generation unavailable \u2502
|
|
56
|
-
\u2502 \u2502
|
|
57
|
-
\u2502 Install 'qrcode' package or \u2502
|
|
58
|
-
\u2502 visit the URL directly: \u2502
|
|
59
|
-
\u2502 \u2502
|
|
60
|
-
\u2502 ${url.substring(0, 35)}${url.length > 35 ? "..." : ""}
|
|
61
|
-
\u2502 \u2502
|
|
62
|
-
\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
|
|
63
|
-
`;
|
|
64
|
-
}
|
|
65
|
-
async function renderSessionCard(session) {
|
|
66
|
-
const qr = await generateQR(session.qrUrl);
|
|
67
|
-
const statusIcon = getStatusIcon(session.status);
|
|
68
|
-
const statusColor = getStatusColor(session.status);
|
|
69
|
-
const lines = [
|
|
70
|
-
"",
|
|
71
|
-
"\u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E",
|
|
72
|
-
"\u2502 NikCLI Remote Session \u2502",
|
|
73
|
-
"\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F",
|
|
74
|
-
""
|
|
75
|
-
];
|
|
76
|
-
const qrLines = qr.split("\n").filter((l) => l.trim());
|
|
77
|
-
for (const line of qrLines) {
|
|
78
|
-
lines.push(" " + line);
|
|
79
|
-
}
|
|
80
|
-
lines.push("");
|
|
81
|
-
lines.push("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
82
|
-
lines.push("");
|
|
83
|
-
lines.push(` Session: ${session.id}`);
|
|
84
|
-
lines.push(` Status: ${statusColor}${statusIcon} ${session.status}\x1B[0m`);
|
|
85
|
-
lines.push(` Devices: ${session.connectedDevices.length} connected`);
|
|
86
|
-
lines.push("");
|
|
87
|
-
if (session.tunnelUrl) {
|
|
88
|
-
lines.push(` \x1B[36mPublic URL:\x1B[0m`);
|
|
89
|
-
lines.push(` ${session.tunnelUrl}`);
|
|
90
|
-
} else {
|
|
91
|
-
lines.push(` \x1B[36mLocal URL:\x1B[0m`);
|
|
92
|
-
lines.push(` ${session.localUrl}`);
|
|
93
|
-
}
|
|
94
|
-
lines.push("");
|
|
95
|
-
lines.push(` \x1B[90mScan QR code or open URL on your phone\x1B[0m`);
|
|
96
|
-
lines.push("");
|
|
97
|
-
lines.push("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
98
|
-
lines.push(" [q] Stop [r] Refresh [c] Copy URL");
|
|
99
|
-
lines.push("");
|
|
100
|
-
return lines.join("\n");
|
|
101
|
-
}
|
|
102
|
-
function getStatusIcon(status) {
|
|
103
|
-
const icons = {
|
|
104
|
-
starting: "\u25EF",
|
|
105
|
-
waiting: "\u25C9",
|
|
106
|
-
connected: "\u25CF",
|
|
107
|
-
stopped: "\u25CB",
|
|
108
|
-
error: "\u2716"
|
|
109
|
-
};
|
|
110
|
-
return icons[status] || "?";
|
|
111
|
-
}
|
|
112
|
-
function getStatusColor(status) {
|
|
113
|
-
const colors = {
|
|
114
|
-
starting: "\x1B[33m",
|
|
115
|
-
// Yellow
|
|
116
|
-
waiting: "\x1B[33m",
|
|
117
|
-
// Yellow
|
|
118
|
-
connected: "\x1B[32m",
|
|
119
|
-
// Green
|
|
120
|
-
stopped: "\x1B[90m",
|
|
121
|
-
// Gray
|
|
122
|
-
error: "\x1B[31m"
|
|
123
|
-
// Red
|
|
124
|
-
};
|
|
125
|
-
return colors[status] || "";
|
|
126
|
-
}
|
|
127
|
-
function progressBar(current, total, width = 30) {
|
|
128
|
-
const percent = Math.round(current / total * 100);
|
|
129
|
-
const filled = Math.round(current / total * width);
|
|
130
|
-
const empty = width - filled;
|
|
131
|
-
return `[${"\u2588".repeat(filled)}${"\u2591".repeat(empty)}] ${percent}%`;
|
|
132
|
-
}
|
|
7
|
+
} from "./chunk-DRL7JX54.js";
|
|
8
|
+
import "./chunk-MCKGQKYU.js";
|
|
133
9
|
|
|
134
10
|
// src/index.ts
|
|
135
11
|
async function createRemoteServer(config = {}) {
|
|
136
|
-
const { RemoteServer: RemoteServer2 } = await import("./server-
|
|
12
|
+
const { RemoteServer: RemoteServer2 } = await import("./server-J7SPDGZO.js");
|
|
137
13
|
const server = new RemoteServer2(config);
|
|
138
14
|
const session = await server.start();
|
|
139
15
|
return { server, session };
|
|
@@ -143,13 +19,6 @@ export {
|
|
|
143
19
|
MessageTypes,
|
|
144
20
|
RemoteServer,
|
|
145
21
|
TerminalManager,
|
|
146
|
-
TunnelManager,
|
|
147
|
-
checkTunnelAvailability,
|
|
148
22
|
createRemoteServer,
|
|
149
|
-
|
|
150
|
-
generateQR,
|
|
151
|
-
generateQRDataURL,
|
|
152
|
-
getWebClient,
|
|
153
|
-
progressBar,
|
|
154
|
-
renderSessionCard
|
|
23
|
+
getWebClient
|
|
155
24
|
};
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -3,78 +3,21 @@
|
|
|
3
3
|
* Native remote terminal server for NikCLI
|
|
4
4
|
*
|
|
5
5
|
* Provides WebSocket-based remote access to NikCLI from mobile devices.
|
|
6
|
-
* Features:
|
|
7
|
-
* - PTY-based terminal with full color support
|
|
8
|
-
* - Mobile-friendly web client
|
|
9
|
-
* - Tunnel support for public access (localtunnel/cloudflared/ngrok)
|
|
10
|
-
* - QR code generation for easy mobile connection
|
|
11
|
-
* - Real-time notifications and events
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* ```typescript
|
|
15
|
-
* import { RemoteServer } from '@nikcli/remote'
|
|
16
|
-
*
|
|
17
|
-
* const server = new RemoteServer({
|
|
18
|
-
* enableTunnel: true,
|
|
19
|
-
* tunnelProvider: 'localtunnel'
|
|
20
|
-
* })
|
|
21
|
-
*
|
|
22
|
-
* const session = await server.start({ name: 'my-session' })
|
|
23
|
-
* console.log('Connect at:', session.qrUrl)
|
|
24
|
-
*
|
|
25
|
-
* // Send notifications
|
|
26
|
-
* server.notify({
|
|
27
|
-
* type: 'success',
|
|
28
|
-
* title: 'Task Complete',
|
|
29
|
-
* body: 'Build finished successfully'
|
|
30
|
-
* })
|
|
31
|
-
*
|
|
32
|
-
* // Stop when done
|
|
33
|
-
* await server.stop()
|
|
34
|
-
* ```
|
|
35
6
|
*/
|
|
36
7
|
|
|
37
|
-
// Main server
|
|
38
8
|
export { RemoteServer, type RemoteServerEvents } from './server'
|
|
39
|
-
|
|
40
|
-
// Terminal management
|
|
41
9
|
export { TerminalManager, type TerminalConfig } from './terminal'
|
|
42
|
-
|
|
43
|
-
// Tunnel management
|
|
44
|
-
export { TunnelManager, checkTunnelAvailability, findAvailableTunnel } from './tunnel'
|
|
45
|
-
|
|
46
|
-
// QR code generation
|
|
47
|
-
export { generateQR, generateQRDataURL, renderSessionCard, progressBar } from './qrcode'
|
|
48
|
-
|
|
49
|
-
// Web client
|
|
50
10
|
export { getWebClient } from './web-client'
|
|
51
|
-
|
|
52
|
-
// Types
|
|
53
11
|
export type {
|
|
54
|
-
SessionStatus,
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
RemoteSession,
|
|
58
|
-
ServerConfig,
|
|
59
|
-
BroadcastMessage,
|
|
60
|
-
RemoteNotification,
|
|
61
|
-
ClientMessage,
|
|
62
|
-
ServerMessage,
|
|
63
|
-
TerminalData,
|
|
64
|
-
TerminalResize,
|
|
65
|
-
CommandMessage,
|
|
66
|
-
ClientConnection,
|
|
12
|
+
SessionStatus, TunnelProvider, DeviceInfo, RemoteSession,
|
|
13
|
+
ServerConfig, BroadcastMessage, RemoteNotification, ClientMessage,
|
|
14
|
+
ServerMessage, TerminalData, TerminalResize, CommandMessage,
|
|
67
15
|
} from './types'
|
|
68
|
-
|
|
69
16
|
export { DEFAULT_CONFIG, MessageTypes } from './types'
|
|
70
17
|
|
|
71
|
-
// Convenience function to create and start server
|
|
72
18
|
export async function createRemoteServer(
|
|
73
19
|
config: Partial<import('./types').ServerConfig> = {}
|
|
74
|
-
)
|
|
75
|
-
server: import('./server').RemoteServer
|
|
76
|
-
session: import('./types').RemoteSession
|
|
77
|
-
}> {
|
|
20
|
+
) {
|
|
78
21
|
const { RemoteServer } = await import('./server')
|
|
79
22
|
const server = new RemoteServer(config)
|
|
80
23
|
const session = await server.start()
|
package/src/server.ts
CHANGED
|
@@ -127,7 +127,10 @@ export class RemoteServer extends EventEmitter {
|
|
|
127
127
|
})
|
|
128
128
|
|
|
129
129
|
this.terminal.on('data', (data: string) => {
|
|
130
|
+
// Broadcast to all connected clients
|
|
130
131
|
this.broadcast({ type: MessageTypes.TERMINAL_OUTPUT, payload: { data } })
|
|
132
|
+
// Also emit for NikCLI to capture
|
|
133
|
+
this.emit('terminal:output', data)
|
|
131
134
|
})
|
|
132
135
|
|
|
133
136
|
this.terminal.on('exit', (code: number) => {
|
|
@@ -403,10 +406,6 @@ export class RemoteServer extends EventEmitter {
|
|
|
403
406
|
* Handle authentication
|
|
404
407
|
*/
|
|
405
408
|
private handleAuth(client: ClientConnection & { ws: WebSocket }, token: string): void {
|
|
406
|
-
console.log(`[Tunnel] Auth attempt from ${client.id}, token length: ${token?.length || 0}`)
|
|
407
|
-
console.log(`[Tunnel] Expected secret: ${this.sessionSecret?.substring(0, 8)}...`)
|
|
408
|
-
console.log(`[Tunnel] Received token: ${token?.substring(0, 8)}...`)
|
|
409
|
-
|
|
410
409
|
if (token === this.sessionSecret) {
|
|
411
410
|
client.authenticated = true
|
|
412
411
|
|
|
@@ -432,9 +431,7 @@ export class RemoteServer extends EventEmitter {
|
|
|
432
431
|
}
|
|
433
432
|
|
|
434
433
|
this.emit('client:connected', client.device)
|
|
435
|
-
console.log(`[Tunnel] Auth success for ${client.id}`)
|
|
436
434
|
} else {
|
|
437
|
-
console.log(`[Tunnel] Auth failed for ${client.id}`)
|
|
438
435
|
client.ws.send(JSON.stringify({ type: MessageTypes.AUTH_FAILED, timestamp: Date.now() }))
|
|
439
436
|
setTimeout(() => client.ws.close(1008, 'Authentication failed'), 100)
|
|
440
437
|
}
|
package/src/tunnel.ts
CHANGED
|
@@ -81,7 +81,8 @@ export class TunnelManager {
|
|
|
81
81
|
*/
|
|
82
82
|
private createLocaltunnelCli(port: number): Promise<string> {
|
|
83
83
|
return new Promise((resolve, reject) => {
|
|
84
|
-
|
|
84
|
+
// Suppress output - redirect to /dev/null so it doesn't clutter NikCLI display
|
|
85
|
+
this.process = spawn('npx', ['localtunnel', '--port', port.toString(), '--print-requests', 'false'], {
|
|
85
86
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
86
87
|
shell: true,
|
|
87
88
|
})
|
|
@@ -101,8 +102,9 @@ export class TunnelManager {
|
|
|
101
102
|
}
|
|
102
103
|
})
|
|
103
104
|
|
|
104
|
-
|
|
105
|
-
|
|
105
|
+
// Suppress stderr output
|
|
106
|
+
this.process.stderr?.on('data', () => {
|
|
107
|
+
// Ignore stderr - localtunnel debug output
|
|
106
108
|
})
|
|
107
109
|
|
|
108
110
|
this.process.on('error', (error) => {
|
|
@@ -124,9 +126,10 @@ export class TunnelManager {
|
|
|
124
126
|
*/
|
|
125
127
|
private createCloudflared(port: number): Promise<string> {
|
|
126
128
|
return new Promise((resolve, reject) => {
|
|
129
|
+
// Suppress verbose logging with --metrics flag
|
|
127
130
|
this.process = spawn(
|
|
128
131
|
'cloudflared',
|
|
129
|
-
['tunnel', '--url', `http://localhost:${port}
|
|
132
|
+
['tunnel', '--url', `http://localhost:${port}`, '--metrics', 'localhost:0'],
|
|
130
133
|
{
|
|
131
134
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
132
135
|
}
|
|
@@ -170,7 +173,8 @@ export class TunnelManager {
|
|
|
170
173
|
*/
|
|
171
174
|
private createNgrok(port: number): Promise<string> {
|
|
172
175
|
return new Promise((resolve, reject) => {
|
|
173
|
-
|
|
176
|
+
// Use log level to suppress verbose output
|
|
177
|
+
this.process = spawn('ngrok', ['http', port.toString(), '--log=stdout', '--log-level=info'], {
|
|
174
178
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
175
179
|
})
|
|
176
180
|
|
|
@@ -190,8 +194,8 @@ export class TunnelManager {
|
|
|
190
194
|
}
|
|
191
195
|
})
|
|
192
196
|
|
|
193
|
-
this.process.stderr?.on('data', (
|
|
194
|
-
|
|
197
|
+
this.process.stderr?.on('data', () => {
|
|
198
|
+
// Suppress ngrok debug output
|
|
195
199
|
})
|
|
196
200
|
|
|
197
201
|
this.process.on('error', (error) => {
|