remote-cli-agent 0.1.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/dist/connection.d.ts +3 -0
- package/dist/connection.js +116 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +172 -0
- package/dist/terminal.d.ts +22 -0
- package/dist/terminal.js +139 -0
- package/package.json +45 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import WebSocket from 'ws';
|
|
2
|
+
import { getSessionList, writeToSession, resizeSession, attachSession, spawnSession, setOutputHandler, setSessionsChangeHandler } from './terminal.js';
|
|
3
|
+
let ws = null;
|
|
4
|
+
let reconnectTimer = null;
|
|
5
|
+
let reconnectAttempts = 0;
|
|
6
|
+
const MAX_RECONNECT_DELAY = 30000;
|
|
7
|
+
const BASE_RECONNECT_DELAY = 1000;
|
|
8
|
+
let serverUrl = '';
|
|
9
|
+
let agentToken = '';
|
|
10
|
+
let isConnected = false;
|
|
11
|
+
export function connect(url, token) {
|
|
12
|
+
serverUrl = url;
|
|
13
|
+
agentToken = token;
|
|
14
|
+
// Setup terminal handlers
|
|
15
|
+
setOutputHandler((sessionId, data) => {
|
|
16
|
+
sendMessage({ type: 'output', sessionId, data });
|
|
17
|
+
});
|
|
18
|
+
setSessionsChangeHandler(() => {
|
|
19
|
+
sendMessage({ type: 'sessions_update', sessions: getSessionList() });
|
|
20
|
+
});
|
|
21
|
+
doConnect();
|
|
22
|
+
}
|
|
23
|
+
function doConnect() {
|
|
24
|
+
if (ws) {
|
|
25
|
+
ws.removeAllListeners();
|
|
26
|
+
ws.close();
|
|
27
|
+
}
|
|
28
|
+
const wsUrl = `${serverUrl}/ws/agent?token=${encodeURIComponent(agentToken)}`;
|
|
29
|
+
console.log(`Connecting to ${serverUrl}...`);
|
|
30
|
+
ws = new WebSocket(wsUrl);
|
|
31
|
+
ws.on('open', () => {
|
|
32
|
+
console.log('Connected to server');
|
|
33
|
+
isConnected = true;
|
|
34
|
+
reconnectAttempts = 0;
|
|
35
|
+
// Register current sessions
|
|
36
|
+
sendMessage({ type: 'register', sessions: getSessionList() });
|
|
37
|
+
});
|
|
38
|
+
ws.on('message', (data) => {
|
|
39
|
+
try {
|
|
40
|
+
const msg = JSON.parse(data.toString());
|
|
41
|
+
handleMessage(msg);
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
console.error('Failed to parse server message:', err);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
ws.on('close', (code, reason) => {
|
|
48
|
+
console.log(`Disconnected from server (code: ${code}, reason: ${reason.toString() || 'unknown'})`);
|
|
49
|
+
isConnected = false;
|
|
50
|
+
scheduleReconnect();
|
|
51
|
+
});
|
|
52
|
+
ws.on('error', (err) => {
|
|
53
|
+
console.error('WebSocket error:', err.message);
|
|
54
|
+
isConnected = false;
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
function handleMessage(msg) {
|
|
58
|
+
switch (msg.type) {
|
|
59
|
+
case 'input': {
|
|
60
|
+
writeToSession(msg.sessionId, msg.data);
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
case 'resize': {
|
|
64
|
+
resizeSession(msg.sessionId, msg.cols, msg.rows);
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
case 'attach': {
|
|
68
|
+
const exists = attachSession(msg.sessionId);
|
|
69
|
+
if (!exists) {
|
|
70
|
+
console.warn(`Session ${msg.sessionId} not found for attach`);
|
|
71
|
+
}
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
case 'spawn': {
|
|
75
|
+
const command = msg.command || undefined;
|
|
76
|
+
const session = spawnSession(command);
|
|
77
|
+
console.log(`Spawned session: ${session.name} (${session.id})`);
|
|
78
|
+
// Notify server of updated sessions
|
|
79
|
+
sendMessage({ type: 'sessions_update', sessions: getSessionList() });
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
default:
|
|
83
|
+
console.warn('Unknown message type from server:', msg.type);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function sendMessage(msg) {
|
|
87
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
88
|
+
ws.send(JSON.stringify(msg));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function scheduleReconnect() {
|
|
92
|
+
if (reconnectTimer)
|
|
93
|
+
return;
|
|
94
|
+
const delay = Math.min(BASE_RECONNECT_DELAY * Math.pow(2, reconnectAttempts), MAX_RECONNECT_DELAY);
|
|
95
|
+
reconnectAttempts++;
|
|
96
|
+
console.log(`Reconnecting in ${delay / 1000}s (attempt ${reconnectAttempts})...`);
|
|
97
|
+
reconnectTimer = setTimeout(() => {
|
|
98
|
+
reconnectTimer = null;
|
|
99
|
+
doConnect();
|
|
100
|
+
}, delay);
|
|
101
|
+
}
|
|
102
|
+
export function disconnect() {
|
|
103
|
+
if (reconnectTimer) {
|
|
104
|
+
clearTimeout(reconnectTimer);
|
|
105
|
+
reconnectTimer = null;
|
|
106
|
+
}
|
|
107
|
+
if (ws) {
|
|
108
|
+
ws.removeAllListeners();
|
|
109
|
+
ws.close(1000, 'Agent shutting down');
|
|
110
|
+
ws = null;
|
|
111
|
+
}
|
|
112
|
+
isConnected = false;
|
|
113
|
+
}
|
|
114
|
+
export function getConnectionStatus() {
|
|
115
|
+
return isConnected;
|
|
116
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { connect, disconnect } from './connection.js';
|
|
4
|
+
import { listTmuxSessions, attachTmuxSession, spawnSession, cleanupAll, getSessionList } from './terminal.js';
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import os from 'os';
|
|
8
|
+
const CONFIG_DIR = path.join(os.homedir(), '.remote-cli');
|
|
9
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
10
|
+
function loadConfig() {
|
|
11
|
+
try {
|
|
12
|
+
if (fs.existsSync(CONFIG_FILE)) {
|
|
13
|
+
return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8'));
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
// ignore
|
|
18
|
+
}
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
function saveConfig(config) {
|
|
22
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
23
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
24
|
+
fs.chmodSync(CONFIG_FILE, 0o600);
|
|
25
|
+
}
|
|
26
|
+
const program = new Command();
|
|
27
|
+
program
|
|
28
|
+
.name('remote-cli')
|
|
29
|
+
.description('Remote CLI agent - connects local terminal sessions to the remote server')
|
|
30
|
+
.version('1.0.0');
|
|
31
|
+
program
|
|
32
|
+
.command('configure')
|
|
33
|
+
.description('Configure the agent with server URL and token')
|
|
34
|
+
.requiredOption('-s, --server <url>', 'Server URL (e.g., https://remote-cli.example.com)')
|
|
35
|
+
.requiredOption('-t, --token <token>', 'Agent token from the web UI')
|
|
36
|
+
.action((opts) => {
|
|
37
|
+
const server = opts.server.replace(/\/$/, ''); // Remove trailing slash
|
|
38
|
+
saveConfig({ server, token: opts.token });
|
|
39
|
+
console.log(`Configuration saved to ${CONFIG_FILE}`);
|
|
40
|
+
console.log(`Server: ${server}`);
|
|
41
|
+
console.log(`Token: ${opts.token.slice(0, 8)}...`);
|
|
42
|
+
});
|
|
43
|
+
program
|
|
44
|
+
.command('connect')
|
|
45
|
+
.description('Connect to the server and register available sessions')
|
|
46
|
+
.option('-s, --server <url>', 'Server URL (overrides config)')
|
|
47
|
+
.option('-t, --token <token>', 'Agent token (overrides config)')
|
|
48
|
+
.option('--spawn [command]', 'Spawn a new session on connect')
|
|
49
|
+
.action((opts) => {
|
|
50
|
+
const config = loadConfig();
|
|
51
|
+
const server = opts.server || config?.server;
|
|
52
|
+
const token = opts.token || config?.token;
|
|
53
|
+
if (!server || !token) {
|
|
54
|
+
console.error('Error: Server URL and token are required.');
|
|
55
|
+
console.error('Run "remote-cli configure -s <url> -t <token>" first, or pass --server and --token flags.');
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
// Convert http(s) to ws(s) for WebSocket connection
|
|
59
|
+
const wsUrl = server.replace(/^http/, 'ws');
|
|
60
|
+
console.log(`Connecting to ${server}...`);
|
|
61
|
+
// List tmux sessions
|
|
62
|
+
const tmuxSessions = listTmuxSessions();
|
|
63
|
+
if (tmuxSessions.length > 0) {
|
|
64
|
+
console.log(`Found ${tmuxSessions.length} tmux session(s):`);
|
|
65
|
+
tmuxSessions.forEach(s => console.log(` - ${s}`));
|
|
66
|
+
// Auto-attach all tmux sessions
|
|
67
|
+
for (const sessionName of tmuxSessions) {
|
|
68
|
+
attachTmuxSession(sessionName);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// Spawn a session if requested
|
|
72
|
+
if (opts.spawn !== undefined) {
|
|
73
|
+
const command = typeof opts.spawn === 'string' ? opts.spawn : undefined;
|
|
74
|
+
spawnSession(command);
|
|
75
|
+
}
|
|
76
|
+
// If no sessions available, spawn a default one
|
|
77
|
+
if (getSessionList().length === 0) {
|
|
78
|
+
console.log('No tmux sessions found. Spawning a default shell session...');
|
|
79
|
+
spawnSession();
|
|
80
|
+
}
|
|
81
|
+
// Connect to server
|
|
82
|
+
connect(wsUrl, token);
|
|
83
|
+
// Handle shutdown
|
|
84
|
+
const shutdown = () => {
|
|
85
|
+
console.log('\nShutting down...');
|
|
86
|
+
disconnect();
|
|
87
|
+
cleanupAll();
|
|
88
|
+
process.exit(0);
|
|
89
|
+
};
|
|
90
|
+
process.on('SIGINT', shutdown);
|
|
91
|
+
process.on('SIGTERM', shutdown);
|
|
92
|
+
// Keep process alive
|
|
93
|
+
setInterval(() => { }, 1000);
|
|
94
|
+
});
|
|
95
|
+
program
|
|
96
|
+
.command('attach <session>')
|
|
97
|
+
.description('Attach a tmux session and connect to server')
|
|
98
|
+
.option('-s, --server <url>', 'Server URL')
|
|
99
|
+
.option('-t, --token <token>', 'Agent token')
|
|
100
|
+
.action((sessionName, opts) => {
|
|
101
|
+
const config = loadConfig();
|
|
102
|
+
const server = opts.server || config?.server;
|
|
103
|
+
const token = opts.token || config?.token;
|
|
104
|
+
if (!server || !token) {
|
|
105
|
+
console.error('Error: Server URL and token are required.');
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
const wsUrl = server.replace(/^http/, 'ws');
|
|
109
|
+
const session = attachTmuxSession(sessionName);
|
|
110
|
+
if (!session) {
|
|
111
|
+
console.error(`Failed to attach tmux session "${sessionName}"`);
|
|
112
|
+
const available = listTmuxSessions();
|
|
113
|
+
if (available.length > 0) {
|
|
114
|
+
console.log('Available tmux sessions:');
|
|
115
|
+
available.forEach(s => console.log(` - ${s}`));
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
console.log('No tmux sessions available.');
|
|
119
|
+
}
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
connect(wsUrl, token);
|
|
123
|
+
const shutdown = () => {
|
|
124
|
+
console.log('\nShutting down...');
|
|
125
|
+
disconnect();
|
|
126
|
+
cleanupAll();
|
|
127
|
+
process.exit(0);
|
|
128
|
+
};
|
|
129
|
+
process.on('SIGINT', shutdown);
|
|
130
|
+
process.on('SIGTERM', shutdown);
|
|
131
|
+
setInterval(() => { }, 1000);
|
|
132
|
+
});
|
|
133
|
+
program
|
|
134
|
+
.command('spawn [command]')
|
|
135
|
+
.description('Spawn a new PTY session and connect to server')
|
|
136
|
+
.option('-s, --server <url>', 'Server URL')
|
|
137
|
+
.option('-t, --token <token>', 'Agent token')
|
|
138
|
+
.action((command, opts) => {
|
|
139
|
+
const config = loadConfig();
|
|
140
|
+
const server = opts.server || config?.server;
|
|
141
|
+
const token = opts.token || config?.token;
|
|
142
|
+
if (!server || !token) {
|
|
143
|
+
console.error('Error: Server URL and token are required.');
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
const wsUrl = server.replace(/^http/, 'ws');
|
|
147
|
+
spawnSession(command);
|
|
148
|
+
connect(wsUrl, token);
|
|
149
|
+
const shutdown = () => {
|
|
150
|
+
console.log('\nShutting down...');
|
|
151
|
+
disconnect();
|
|
152
|
+
cleanupAll();
|
|
153
|
+
process.exit(0);
|
|
154
|
+
};
|
|
155
|
+
process.on('SIGINT', shutdown);
|
|
156
|
+
process.on('SIGTERM', shutdown);
|
|
157
|
+
setInterval(() => { }, 1000);
|
|
158
|
+
});
|
|
159
|
+
program
|
|
160
|
+
.command('sessions')
|
|
161
|
+
.description('List available tmux sessions')
|
|
162
|
+
.action(() => {
|
|
163
|
+
const sessions = listTmuxSessions();
|
|
164
|
+
if (sessions.length === 0) {
|
|
165
|
+
console.log('No tmux sessions found.');
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
console.log('Available tmux sessions:');
|
|
169
|
+
sessions.forEach(s => console.log(` - ${s}`));
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
program.parse();
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { IPty } from 'node-pty';
|
|
2
|
+
export interface TerminalSession {
|
|
3
|
+
id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
command?: string;
|
|
6
|
+
pty?: IPty;
|
|
7
|
+
tmuxSession?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function setOutputHandler(handler: (sessionId: string, data: string) => void): void;
|
|
10
|
+
export declare function setSessionsChangeHandler(handler: () => void): void;
|
|
11
|
+
export declare function getSessionList(): {
|
|
12
|
+
id: string;
|
|
13
|
+
name: string;
|
|
14
|
+
command?: string;
|
|
15
|
+
}[];
|
|
16
|
+
export declare function listTmuxSessions(): string[];
|
|
17
|
+
export declare function attachTmuxSession(sessionName: string): TerminalSession | null;
|
|
18
|
+
export declare function spawnSession(command?: string): TerminalSession;
|
|
19
|
+
export declare function writeToSession(sessionId: string, data: string): boolean;
|
|
20
|
+
export declare function resizeSession(sessionId: string, cols: number, rows: number): boolean;
|
|
21
|
+
export declare function attachSession(sessionId: string): boolean;
|
|
22
|
+
export declare function cleanupAll(): void;
|
package/dist/terminal.js
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { spawn as nodePtySpawn } from 'node-pty';
|
|
2
|
+
import { execSync } from 'child_process';
|
|
3
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
4
|
+
// Active sessions
|
|
5
|
+
const sessions = new Map();
|
|
6
|
+
// Callbacks
|
|
7
|
+
let onOutput = null;
|
|
8
|
+
let onSessionsChange = null;
|
|
9
|
+
export function setOutputHandler(handler) {
|
|
10
|
+
onOutput = handler;
|
|
11
|
+
}
|
|
12
|
+
export function setSessionsChangeHandler(handler) {
|
|
13
|
+
onSessionsChange = handler;
|
|
14
|
+
}
|
|
15
|
+
export function getSessionList() {
|
|
16
|
+
return Array.from(sessions.values()).map(s => ({
|
|
17
|
+
id: s.id,
|
|
18
|
+
name: s.name,
|
|
19
|
+
command: s.command
|
|
20
|
+
}));
|
|
21
|
+
}
|
|
22
|
+
// List available tmux sessions
|
|
23
|
+
export function listTmuxSessions() {
|
|
24
|
+
try {
|
|
25
|
+
const output = execSync('tmux list-sessions -F "#{session_name}"', { encoding: 'utf-8' });
|
|
26
|
+
return output.trim().split('\n').filter(s => s.length > 0);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// Attach to a tmux session
|
|
33
|
+
export function attachTmuxSession(sessionName) {
|
|
34
|
+
try {
|
|
35
|
+
// Verify session exists
|
|
36
|
+
const sessions_list = listTmuxSessions();
|
|
37
|
+
if (!sessions_list.includes(sessionName)) {
|
|
38
|
+
console.error(`tmux session "${sessionName}" not found`);
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
const id = uuidv4();
|
|
42
|
+
const shell = process.env.SHELL || '/bin/bash';
|
|
43
|
+
// Spawn a PTY that attaches to the tmux session
|
|
44
|
+
const pty = nodePtySpawn(shell, ['-c', `tmux attach-session -t ${sessionName}`], {
|
|
45
|
+
name: 'xterm-256color',
|
|
46
|
+
cols: 80,
|
|
47
|
+
rows: 24,
|
|
48
|
+
cwd: process.env.HOME || '/',
|
|
49
|
+
env: process.env
|
|
50
|
+
});
|
|
51
|
+
const session = {
|
|
52
|
+
id,
|
|
53
|
+
name: `tmux: ${sessionName}`,
|
|
54
|
+
command: `tmux attach -t ${sessionName}`,
|
|
55
|
+
pty,
|
|
56
|
+
tmuxSession: sessionName
|
|
57
|
+
};
|
|
58
|
+
setupPtyHandlers(session);
|
|
59
|
+
sessions.set(id, session);
|
|
60
|
+
onSessionsChange?.();
|
|
61
|
+
console.log(`Attached to tmux session "${sessionName}" with id ${id}`);
|
|
62
|
+
return session;
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
console.error('Failed to attach tmux session:', err);
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Spawn a new PTY session
|
|
70
|
+
export function spawnSession(command) {
|
|
71
|
+
const id = uuidv4();
|
|
72
|
+
const shell = process.env.SHELL || '/bin/bash';
|
|
73
|
+
const cmd = command || shell;
|
|
74
|
+
const args = command ? ['-c', command] : [];
|
|
75
|
+
const pty = nodePtySpawn(command ? shell : shell, args, {
|
|
76
|
+
name: 'xterm-256color',
|
|
77
|
+
cols: 80,
|
|
78
|
+
rows: 24,
|
|
79
|
+
cwd: process.env.HOME || '/',
|
|
80
|
+
env: process.env
|
|
81
|
+
});
|
|
82
|
+
const session = {
|
|
83
|
+
id,
|
|
84
|
+
name: command || shell,
|
|
85
|
+
command: cmd,
|
|
86
|
+
pty
|
|
87
|
+
};
|
|
88
|
+
setupPtyHandlers(session);
|
|
89
|
+
sessions.set(id, session);
|
|
90
|
+
onSessionsChange?.();
|
|
91
|
+
console.log(`Spawned session "${session.name}" with id ${id}`);
|
|
92
|
+
return session;
|
|
93
|
+
}
|
|
94
|
+
function setupPtyHandlers(session) {
|
|
95
|
+
if (!session.pty)
|
|
96
|
+
return;
|
|
97
|
+
session.pty.onData((data) => {
|
|
98
|
+
onOutput?.(session.id, data);
|
|
99
|
+
});
|
|
100
|
+
session.pty.onExit(({ exitCode }) => {
|
|
101
|
+
console.log(`Session "${session.name}" exited with code ${exitCode}`);
|
|
102
|
+
sessions.delete(session.id);
|
|
103
|
+
onSessionsChange?.();
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
// Write input to a session
|
|
107
|
+
export function writeToSession(sessionId, data) {
|
|
108
|
+
const session = sessions.get(sessionId);
|
|
109
|
+
if (!session?.pty)
|
|
110
|
+
return false;
|
|
111
|
+
session.pty.write(data);
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
// Resize a session
|
|
115
|
+
export function resizeSession(sessionId, cols, rows) {
|
|
116
|
+
const session = sessions.get(sessionId);
|
|
117
|
+
if (!session?.pty)
|
|
118
|
+
return false;
|
|
119
|
+
try {
|
|
120
|
+
session.pty.resize(cols, rows);
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// Attach to session (no-op if already active, used when browser connects)
|
|
128
|
+
export function attachSession(sessionId) {
|
|
129
|
+
return sessions.has(sessionId);
|
|
130
|
+
}
|
|
131
|
+
// Cleanup all sessions
|
|
132
|
+
export function cleanupAll() {
|
|
133
|
+
for (const session of sessions.values()) {
|
|
134
|
+
if (session.pty) {
|
|
135
|
+
session.pty.kill();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
sessions.clear();
|
|
139
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "remote-cli-agent",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"bin": {
|
|
6
|
+
"remote-cli": "./dist/index.js"
|
|
7
|
+
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsc",
|
|
10
|
+
"start": "node dist/index.js",
|
|
11
|
+
"dev": "tsx src/index.ts",
|
|
12
|
+
"prepublishOnly": "npm run build"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"commander": "^12.0.0",
|
|
16
|
+
"node-pty": "^1.0.0",
|
|
17
|
+
"uuid": "^9.0.0",
|
|
18
|
+
"ws": "^8.16.0"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/node": "^20.11.5",
|
|
22
|
+
"@types/uuid": "^9.0.7",
|
|
23
|
+
"@types/ws": "^8.5.10",
|
|
24
|
+
"tsx": "^4.7.0",
|
|
25
|
+
"typescript": "^5.3.3"
|
|
26
|
+
},
|
|
27
|
+
"description": "Remote CLI agent - access local terminal sessions from the browser",
|
|
28
|
+
"files": [
|
|
29
|
+
"dist/**/*"
|
|
30
|
+
],
|
|
31
|
+
"keywords": [
|
|
32
|
+
"terminal",
|
|
33
|
+
"remote",
|
|
34
|
+
"tmux",
|
|
35
|
+
"pty",
|
|
36
|
+
"cli",
|
|
37
|
+
"websocket"
|
|
38
|
+
],
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"repository": {
|
|
41
|
+
"type": "git",
|
|
42
|
+
"url": "https://github.com/aim-research/remote-cli.git",
|
|
43
|
+
"directory": "agent"
|
|
44
|
+
}
|
|
45
|
+
}
|