nikcli-remote 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.
@@ -0,0 +1,7 @@
1
+ import {
2
+ RemoteServer
3
+ } from "./chunk-TJTKYXIZ.js";
4
+ import "./chunk-MCKGQKYU.js";
5
+ export {
6
+ RemoteServer
7
+ };
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "nikcli-remote",
3
+ "version": "1.0.0",
4
+ "description": "Native remote terminal server for NikCLI - Mobile control via WebSocket",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "type": "module",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "require": "./dist/index.cjs"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "src"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsup src/index.ts --format esm,cjs --dts --clean",
21
+ "dev": "tsup src/index.ts --format esm --watch",
22
+ "typecheck": "tsc --noEmit",
23
+ "test": "vitest run",
24
+ "test:watch": "vitest"
25
+ },
26
+ "keywords": [
27
+ "remote",
28
+ "terminal",
29
+ "mobile",
30
+ "websocket",
31
+ "pty",
32
+ "nikcli"
33
+ ],
34
+ "author": "NikCLI Team",
35
+ "license": "MIT",
36
+ "dependencies": {
37
+ "qrcode": "^1.5.4",
38
+ "ws": "^8.18.0"
39
+ },
40
+ "optionalDependencies": {
41
+ "localtunnel": "^2.0.2",
42
+ "node-pty": "^1.0.0"
43
+ },
44
+ "devDependencies": {
45
+ "@types/node": "^22.19.3",
46
+ "@types/qrcode": "^1.5.5",
47
+ "@types/ws": "^8.18.1",
48
+ "tsup": "^8.3.5",
49
+ "typescript": "^5.7.3",
50
+ "vitest": "^2.1.8"
51
+ },
52
+ "peerDependencies": {
53
+ "typescript": ">=5.0.0"
54
+ },
55
+ "engines": {
56
+ "node": ">=18.0.0"
57
+ }
58
+ }
package/src/index.ts ADDED
@@ -0,0 +1,82 @@
1
+ /**
2
+ * @nikcli/remote
3
+ * Native remote terminal server for NikCLI
4
+ *
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
+ */
36
+
37
+ // Main server
38
+ export { RemoteServer, type RemoteServerEvents } from './server'
39
+
40
+ // Terminal management
41
+ 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
+ export { getWebClient } from './web-client'
51
+
52
+ // Types
53
+ 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,
67
+ } from './types'
68
+
69
+ export { DEFAULT_CONFIG, MessageTypes } from './types'
70
+
71
+ // Convenience function to create and start server
72
+ export async function createRemoteServer(
73
+ config: Partial<import('./types').ServerConfig> = {}
74
+ ): Promise<{
75
+ server: import('./server').RemoteServer
76
+ session: import('./types').RemoteSession
77
+ }> {
78
+ const { RemoteServer } = await import('./server')
79
+ const server = new RemoteServer(config)
80
+ const session = await server.start()
81
+ return { server, session }
82
+ }
package/src/qrcode.ts ADDED
@@ -0,0 +1,164 @@
1
+ /**
2
+ * @nikcli/remote - QR Code Generator
3
+ * Terminal QR code rendering for session URLs
4
+ */
5
+
6
+ import type { RemoteSession } from './types'
7
+
8
+ // Try to import qrcode
9
+ let QRCode: typeof import('qrcode') | null = null
10
+ try {
11
+ QRCode = require('qrcode')
12
+ } catch {
13
+ // qrcode not available
14
+ }
15
+
16
+ export interface QROptions {
17
+ small?: boolean
18
+ margin?: number
19
+ }
20
+
21
+ /**
22
+ * Generate QR code string for terminal display
23
+ */
24
+ export async function generateQR(url: string, options: QROptions = {}): Promise<string> {
25
+ if (!QRCode) {
26
+ return generateFallbackQR(url)
27
+ }
28
+
29
+ try {
30
+ const qrString = await QRCode.toString(url, {
31
+ type: 'terminal',
32
+ small: options.small ?? true,
33
+ margin: options.margin ?? 1,
34
+ })
35
+ return qrString
36
+ } catch {
37
+ return generateFallbackQR(url)
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Generate QR code as data URL (for web/image)
43
+ */
44
+ export async function generateQRDataURL(url: string): Promise<string | null> {
45
+ if (!QRCode) return null
46
+
47
+ try {
48
+ return await QRCode.toDataURL(url, {
49
+ margin: 2,
50
+ width: 256,
51
+ color: {
52
+ dark: '#000000',
53
+ light: '#ffffff',
54
+ },
55
+ })
56
+ } catch {
57
+ return null
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Fallback QR display when qrcode package not available
63
+ */
64
+ function generateFallbackQR(url: string): string {
65
+ return `
66
+ ┌─────────────────────────────────────┐
67
+ │ │
68
+ │ QR Code generation unavailable │
69
+ │ │
70
+ │ Install 'qrcode' package or │
71
+ │ visit the URL directly: │
72
+ │ │
73
+ │ ${url.substring(0, 35)}${url.length > 35 ? '...' : ''}
74
+ │ │
75
+ └─────────────────────────────────────┘
76
+ `
77
+ }
78
+
79
+ /**
80
+ * Render session info with QR code
81
+ */
82
+ export async function renderSessionCard(session: RemoteSession): Promise<string> {
83
+ const qr = await generateQR(session.qrUrl)
84
+ const statusIcon = getStatusIcon(session.status)
85
+ const statusColor = getStatusColor(session.status)
86
+
87
+ const lines = [
88
+ '',
89
+ '╭─────────────────────────────────────────────╮',
90
+ '│ NikCLI Remote Session │',
91
+ '╰─────────────────────────────────────────────╯',
92
+ '',
93
+ ]
94
+
95
+ // Add QR code
96
+ const qrLines = qr.split('\n').filter(l => l.trim())
97
+ for (const line of qrLines) {
98
+ lines.push(' ' + line)
99
+ }
100
+
101
+ lines.push('')
102
+ lines.push('─────────────────────────────────────────────')
103
+ lines.push('')
104
+ lines.push(` Session: ${session.id}`)
105
+ lines.push(` Status: ${statusColor}${statusIcon} ${session.status}\x1b[0m`)
106
+ lines.push(` Devices: ${session.connectedDevices.length} connected`)
107
+ lines.push('')
108
+
109
+ if (session.tunnelUrl) {
110
+ lines.push(` \x1b[36mPublic URL:\x1b[0m`)
111
+ lines.push(` ${session.tunnelUrl}`)
112
+ } else {
113
+ lines.push(` \x1b[36mLocal URL:\x1b[0m`)
114
+ lines.push(` ${session.localUrl}`)
115
+ }
116
+
117
+ lines.push('')
118
+ lines.push(` \x1b[90mScan QR code or open URL on your phone\x1b[0m`)
119
+ lines.push('')
120
+ lines.push('─────────────────────────────────────────────')
121
+ lines.push(' [q] Stop [r] Refresh [c] Copy URL')
122
+ lines.push('')
123
+
124
+ return lines.join('\n')
125
+ }
126
+
127
+ /**
128
+ * Get status icon
129
+ */
130
+ function getStatusIcon(status: string): string {
131
+ const icons: Record<string, string> = {
132
+ starting: '◯',
133
+ waiting: '◉',
134
+ connected: '●',
135
+ stopped: '○',
136
+ error: '✖',
137
+ }
138
+ return icons[status] || '?'
139
+ }
140
+
141
+ /**
142
+ * Get status ANSI color
143
+ */
144
+ function getStatusColor(status: string): string {
145
+ const colors: Record<string, string> = {
146
+ starting: '\x1b[33m', // Yellow
147
+ waiting: '\x1b[33m', // Yellow
148
+ connected: '\x1b[32m', // Green
149
+ stopped: '\x1b[90m', // Gray
150
+ error: '\x1b[31m', // Red
151
+ }
152
+ return colors[status] || ''
153
+ }
154
+
155
+ /**
156
+ * Simple progress bar
157
+ */
158
+ export function progressBar(current: number, total: number, width: number = 30): string {
159
+ const percent = Math.round((current / total) * 100)
160
+ const filled = Math.round((current / total) * width)
161
+ const empty = width - filled
162
+
163
+ return `[${'█'.repeat(filled)}${'░'.repeat(empty)}] ${percent}%`
164
+ }