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/index.d.cts CHANGED
@@ -284,83 +284,8 @@ declare class TerminalManager extends EventEmitter {
284
284
  }
285
285
 
286
286
  /**
287
- * @nikcli/remote - Tunnel Manager
288
- * Provides public URL access via various tunnel providers
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 ClientConnection, 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, TunnelManager, type TunnelProvider, checkTunnelAvailability, createRemoteServer, findAvailableTunnel, generateQR, generateQRDataURL, getWebClient, progressBar, renderSessionCard };
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 - Tunnel Manager
288
- * Provides public URL access via various tunnel providers
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 ClientConnection, 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, TunnelManager, type TunnelProvider, checkTunnelAvailability, createRemoteServer, findAvailableTunnel, generateQR, generateQRDataURL, getWebClient, progressBar, renderSessionCard };
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-QLGPGAPC.js";
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-ISK4MDQQ.js");
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
- findAvailableTunnel,
150
- generateQR,
151
- generateQRDataURL,
152
- getWebClient,
153
- progressBar,
154
- renderSessionCard
23
+ getWebClient
155
24
  };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  RemoteServer
3
- } from "./chunk-QLGPGAPC.js";
3
+ } from "./chunk-DRL7JX54.js";
4
4
  import "./chunk-MCKGQKYU.js";
5
5
  export {
6
6
  RemoteServer
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nikcli-remote",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Native remote terminal server for NikCLI - Mobile control via WebSocket",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
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
- TunnelProvider,
56
- DeviceInfo,
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
- ): Promise<{
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
- this.process = spawn('npx', ['localtunnel', '--port', port.toString()], {
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
- this.process.stderr?.on('data', (data: Buffer) => {
105
- output += data.toString()
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
- this.process = spawn('ngrok', ['http', port.toString(), '--log=stdout'], {
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', (data: Buffer) => {
194
- output += data.toString()
197
+ this.process.stderr?.on('data', () => {
198
+ // Suppress ngrok debug output
195
199
  })
196
200
 
197
201
  this.process.on('error', (error) => {