sessioncast-cli 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.
Files changed (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +137 -0
  3. package/dist/agent/api-client.d.ts +27 -0
  4. package/dist/agent/api-client.js +295 -0
  5. package/dist/agent/exec-service.d.ts +6 -0
  6. package/dist/agent/exec-service.js +126 -0
  7. package/dist/agent/index.d.ts +8 -0
  8. package/dist/agent/index.js +24 -0
  9. package/dist/agent/llm-service.d.ts +9 -0
  10. package/dist/agent/llm-service.js +156 -0
  11. package/dist/agent/runner.d.ts +16 -0
  12. package/dist/agent/runner.js +187 -0
  13. package/dist/agent/session-handler.d.ts +28 -0
  14. package/dist/agent/session-handler.js +184 -0
  15. package/dist/agent/tmux.d.ts +29 -0
  16. package/dist/agent/tmux.js +157 -0
  17. package/dist/agent/types.d.ts +72 -0
  18. package/dist/agent/types.js +2 -0
  19. package/dist/agent/websocket.d.ts +45 -0
  20. package/dist/agent/websocket.js +288 -0
  21. package/dist/api.d.ts +31 -0
  22. package/dist/api.js +78 -0
  23. package/dist/commands/agent.d.ts +5 -0
  24. package/dist/commands/agent.js +19 -0
  25. package/dist/commands/agents.d.ts +1 -0
  26. package/dist/commands/agents.js +77 -0
  27. package/dist/commands/login.d.ts +5 -0
  28. package/dist/commands/login.js +41 -0
  29. package/dist/commands/project.d.ts +33 -0
  30. package/dist/commands/project.js +359 -0
  31. package/dist/commands/sendkeys.d.ts +3 -0
  32. package/dist/commands/sendkeys.js +66 -0
  33. package/dist/commands/sessions.d.ts +1 -0
  34. package/dist/commands/sessions.js +89 -0
  35. package/dist/config.d.ts +13 -0
  36. package/dist/config.js +37 -0
  37. package/dist/index.d.ts +2 -0
  38. package/dist/index.js +125 -0
  39. package/dist/project/executor.d.ts +118 -0
  40. package/dist/project/executor.js +893 -0
  41. package/dist/project/index.d.ts +4 -0
  42. package/dist/project/index.js +20 -0
  43. package/dist/project/manager.d.ts +79 -0
  44. package/dist/project/manager.js +397 -0
  45. package/dist/project/relay-client.d.ts +87 -0
  46. package/dist/project/relay-client.js +200 -0
  47. package/dist/project/types.d.ts +43 -0
  48. package/dist/project/types.js +3 -0
  49. package/package.json +59 -0
@@ -0,0 +1,157 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.scanSessions = scanSessions;
4
+ exports.listSessions = listSessions;
5
+ exports.capturePane = capturePane;
6
+ exports.sendKeys = sendKeys;
7
+ exports.resizeWindow = resizeWindow;
8
+ exports.createSession = createSession;
9
+ exports.killSession = killSession;
10
+ const child_process_1 = require("child_process");
11
+ /**
12
+ * Scan for all tmux sessions
13
+ */
14
+ function scanSessions() {
15
+ try {
16
+ const output = (0, child_process_1.execSync)('tmux ls -F "#{session_name}"', {
17
+ encoding: 'utf-8',
18
+ stdio: ['pipe', 'pipe', 'pipe']
19
+ });
20
+ return output
21
+ .trim()
22
+ .split('\n')
23
+ .filter(s => s.length > 0);
24
+ }
25
+ catch {
26
+ // tmux not running or no sessions
27
+ return [];
28
+ }
29
+ }
30
+ /**
31
+ * Get detailed session info
32
+ */
33
+ function listSessions() {
34
+ try {
35
+ const output = (0, child_process_1.execSync)('tmux list-sessions -F "#{session_name}|#{session_windows}|#{session_created}|#{session_attached}"', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
36
+ return output
37
+ .trim()
38
+ .split('\n')
39
+ .filter(line => line.length > 0)
40
+ .map(line => {
41
+ const [name, windows, created, attached] = line.split('|');
42
+ return {
43
+ name,
44
+ windows: parseInt(windows, 10) || 1,
45
+ created: created || undefined,
46
+ attached: attached === '1'
47
+ };
48
+ });
49
+ }
50
+ catch {
51
+ return [];
52
+ }
53
+ }
54
+ /**
55
+ * Capture tmux pane content with escape sequences (colors)
56
+ */
57
+ function capturePane(sessionName) {
58
+ try {
59
+ const output = (0, child_process_1.execSync)(`tmux capture-pane -t "${sessionName}" -p -e -N`, {
60
+ encoding: 'utf-8',
61
+ stdio: ['pipe', 'pipe', 'pipe'],
62
+ maxBuffer: 10 * 1024 * 1024 // 10MB
63
+ });
64
+ // Normalize line endings
65
+ return output.replace(/\n/g, '\r\n');
66
+ }
67
+ catch {
68
+ return null;
69
+ }
70
+ }
71
+ /**
72
+ * Send keys to tmux session
73
+ */
74
+ function sendKeys(target, keys, enter = true) {
75
+ try {
76
+ // Handle special keys
77
+ if (keys === '\x03') {
78
+ // Ctrl+C
79
+ (0, child_process_1.execSync)(`tmux send-keys -t "${target}" C-c`, { stdio: 'pipe' });
80
+ return true;
81
+ }
82
+ if (keys === '\x04') {
83
+ // Ctrl+D
84
+ (0, child_process_1.execSync)(`tmux send-keys -t "${target}" C-d`, { stdio: 'pipe' });
85
+ return true;
86
+ }
87
+ // For Enter key only
88
+ if (keys === '\n' || keys === '\r\n') {
89
+ (0, child_process_1.execSync)(`tmux send-keys -t "${target}" Enter`, { stdio: 'pipe' });
90
+ return true;
91
+ }
92
+ // For text with newline at end (command + enter)
93
+ if (keys.endsWith('\n')) {
94
+ const cmd = keys.slice(0, -1);
95
+ if (cmd) {
96
+ (0, child_process_1.execSync)(`tmux send-keys -t "${target}" -l "${escapeForShell(cmd)}"`, { stdio: 'pipe' });
97
+ }
98
+ (0, child_process_1.execSync)(`tmux send-keys -t "${target}" Enter`, { stdio: 'pipe' });
99
+ return true;
100
+ }
101
+ // Regular text input
102
+ (0, child_process_1.execSync)(`tmux send-keys -t "${target}" -l "${escapeForShell(keys)}"`, { stdio: 'pipe' });
103
+ if (enter) {
104
+ (0, child_process_1.execSync)(`tmux send-keys -t "${target}" Enter`, { stdio: 'pipe' });
105
+ }
106
+ return true;
107
+ }
108
+ catch {
109
+ return false;
110
+ }
111
+ }
112
+ /**
113
+ * Resize tmux window
114
+ */
115
+ function resizeWindow(sessionName, cols, rows) {
116
+ try {
117
+ (0, child_process_1.execSync)(`tmux resize-window -t "${sessionName}" -x ${cols} -y ${rows}`, { stdio: 'pipe' });
118
+ return true;
119
+ }
120
+ catch {
121
+ return false;
122
+ }
123
+ }
124
+ /**
125
+ * Create new tmux session
126
+ */
127
+ function createSession(sessionName) {
128
+ try {
129
+ // Sanitize session name
130
+ const sanitized = sessionName.replace(/[^a-zA-Z0-9_-]/g, '_');
131
+ if (!sanitized)
132
+ return false;
133
+ (0, child_process_1.execSync)(`tmux new-session -d -s "${sanitized}"`, { stdio: 'pipe' });
134
+ return true;
135
+ }
136
+ catch {
137
+ return false;
138
+ }
139
+ }
140
+ /**
141
+ * Kill tmux session
142
+ */
143
+ function killSession(sessionName) {
144
+ try {
145
+ (0, child_process_1.execSync)(`tmux kill-session -t "${sessionName}"`, { stdio: 'pipe' });
146
+ return true;
147
+ }
148
+ catch {
149
+ return false;
150
+ }
151
+ }
152
+ /**
153
+ * Escape string for shell
154
+ */
155
+ function escapeForShell(str) {
156
+ return str.replace(/"/g, '\\"').replace(/\$/g, '\\$').replace(/`/g, '\\`');
157
+ }
@@ -0,0 +1,72 @@
1
+ export interface AgentConfig {
2
+ machineId: string;
3
+ relay: string;
4
+ token: string;
5
+ api?: ApiConfig;
6
+ }
7
+ export interface ApiConfig {
8
+ enabled: boolean;
9
+ agentId?: string;
10
+ exec?: ExecConfig;
11
+ llm?: LlmConfig;
12
+ }
13
+ export interface ExecConfig {
14
+ enabled: boolean;
15
+ shell: string;
16
+ workingDir?: string;
17
+ allowedCommands?: string[];
18
+ defaultTimeout: number;
19
+ }
20
+ export interface LlmConfig {
21
+ enabled: boolean;
22
+ provider: 'ollama' | 'openai';
23
+ baseUrl: string;
24
+ model: string;
25
+ apiKey?: string;
26
+ }
27
+ export interface Message {
28
+ type: string;
29
+ role?: string;
30
+ session?: string;
31
+ payload?: string;
32
+ meta?: Record<string, string>;
33
+ }
34
+ export interface TmuxSession {
35
+ name: string;
36
+ windows: number;
37
+ created?: string;
38
+ attached: boolean;
39
+ }
40
+ export interface ExecResult {
41
+ exitCode: number;
42
+ stdout: string;
43
+ stderr: string;
44
+ duration: number;
45
+ }
46
+ export interface LlmMessage {
47
+ role: 'system' | 'user' | 'assistant';
48
+ content: string;
49
+ }
50
+ export interface LlmResponse {
51
+ id: string;
52
+ object: string;
53
+ created: number;
54
+ model: string;
55
+ choices: Array<{
56
+ index: number;
57
+ message: {
58
+ role: string;
59
+ content: string;
60
+ };
61
+ finish_reason: string;
62
+ }>;
63
+ usage?: {
64
+ prompt_tokens: number;
65
+ completion_tokens: number;
66
+ total_tokens: number;
67
+ };
68
+ error?: {
69
+ message: string;
70
+ type: string;
71
+ };
72
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,45 @@
1
+ import { EventEmitter } from 'events';
2
+ import { Message } from './types';
3
+ interface WebSocketClientOptions {
4
+ url: string;
5
+ sessionId: string;
6
+ machineId: string;
7
+ token: string;
8
+ label?: string;
9
+ autoReconnect?: boolean;
10
+ }
11
+ export declare class RelayWebSocketClient extends EventEmitter {
12
+ private ws;
13
+ private url;
14
+ private sessionId;
15
+ private machineId;
16
+ private token;
17
+ private label;
18
+ private autoReconnect;
19
+ private isConnected;
20
+ private reconnectAttempts;
21
+ private circuitBreakerOpen;
22
+ private circuitBreakerResetTime;
23
+ private reconnectTimer;
24
+ private destroyed;
25
+ constructor(options: WebSocketClientOptions);
26
+ connect(): void;
27
+ private registerAsHost;
28
+ private handleMessage;
29
+ private handleError;
30
+ private scheduleReconnect;
31
+ send(message: Message): boolean;
32
+ sendScreen(data: Buffer): boolean;
33
+ sendScreenCompressed(data: Buffer): boolean;
34
+ /**
35
+ * Send file content to be displayed in the web FileViewer
36
+ * @param filename - The name of the file
37
+ * @param content - The file content (UTF-8 for text, base64 for images)
38
+ * @param contentType - MIME type (e.g., 'text/markdown', 'text/html', 'image/png')
39
+ * @param path - Optional file path
40
+ */
41
+ sendFileView(filename: string, content: string, contentType: string, path?: string): boolean;
42
+ getConnected(): boolean;
43
+ destroy(): void;
44
+ }
45
+ export {};
@@ -0,0 +1,288 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.RelayWebSocketClient = void 0;
40
+ const ws_1 = __importDefault(require("ws"));
41
+ const events_1 = require("events");
42
+ const zlib = __importStar(require("zlib"));
43
+ const MAX_RECONNECT_ATTEMPTS = 5;
44
+ const BASE_RECONNECT_DELAY_MS = 2000;
45
+ const MAX_RECONNECT_DELAY_MS = 60000;
46
+ const CIRCUIT_BREAKER_DURATION_MS = 120000;
47
+ class RelayWebSocketClient extends events_1.EventEmitter {
48
+ constructor(options) {
49
+ super();
50
+ this.ws = null;
51
+ this.isConnected = false;
52
+ this.reconnectAttempts = 0;
53
+ this.circuitBreakerOpen = false;
54
+ this.circuitBreakerResetTime = 0;
55
+ this.reconnectTimer = null;
56
+ this.destroyed = false;
57
+ this.url = options.url;
58
+ this.sessionId = options.sessionId;
59
+ this.machineId = options.machineId;
60
+ this.token = options.token;
61
+ this.label = options.label || options.sessionId;
62
+ this.autoReconnect = options.autoReconnect ?? true;
63
+ }
64
+ connect() {
65
+ if (this.destroyed)
66
+ return;
67
+ try {
68
+ this.ws = new ws_1.default(this.url);
69
+ this.ws.on('open', () => {
70
+ this.isConnected = true;
71
+ this.reconnectAttempts = 0;
72
+ this.circuitBreakerOpen = false;
73
+ this.emit('connected');
74
+ this.registerAsHost();
75
+ });
76
+ this.ws.on('message', (data) => {
77
+ try {
78
+ const message = JSON.parse(data.toString());
79
+ this.handleMessage(message);
80
+ }
81
+ catch (e) {
82
+ console.error('Failed to parse message:', e);
83
+ }
84
+ });
85
+ this.ws.on('close', (code, reason) => {
86
+ this.isConnected = false;
87
+ this.emit('disconnected', { code, reason: reason.toString() });
88
+ if (this.autoReconnect && !this.destroyed) {
89
+ this.scheduleReconnect();
90
+ }
91
+ });
92
+ this.ws.on('error', (error) => {
93
+ this.emit('error', error);
94
+ });
95
+ }
96
+ catch (error) {
97
+ this.emit('error', error);
98
+ if (this.autoReconnect && !this.destroyed) {
99
+ this.scheduleReconnect();
100
+ }
101
+ }
102
+ }
103
+ registerAsHost() {
104
+ const meta = {
105
+ label: this.label,
106
+ machineId: this.machineId,
107
+ };
108
+ if (this.token) {
109
+ meta.token = this.token;
110
+ }
111
+ this.send({
112
+ type: 'register',
113
+ role: 'host',
114
+ session: this.sessionId,
115
+ meta
116
+ });
117
+ }
118
+ handleMessage(message) {
119
+ switch (message.type) {
120
+ case 'keys':
121
+ if (message.session === this.sessionId && message.payload) {
122
+ this.emit('keys', message.payload);
123
+ }
124
+ break;
125
+ case 'resize':
126
+ if (message.session === this.sessionId && message.meta) {
127
+ const cols = parseInt(message.meta.cols, 10);
128
+ const rows = parseInt(message.meta.rows, 10);
129
+ if (!isNaN(cols) && !isNaN(rows)) {
130
+ this.emit('resize', { cols, rows });
131
+ }
132
+ }
133
+ break;
134
+ case 'createSession':
135
+ if (message.meta?.sessionName) {
136
+ this.emit('createSession', message.meta.sessionName);
137
+ }
138
+ break;
139
+ case 'killSession':
140
+ if (message.session === this.sessionId) {
141
+ this.emit('killSession');
142
+ }
143
+ break;
144
+ case 'error':
145
+ this.handleError(message);
146
+ break;
147
+ default:
148
+ this.emit('message', message);
149
+ }
150
+ }
151
+ handleError(message) {
152
+ const meta = message.meta;
153
+ if (!meta)
154
+ return;
155
+ if (meta.code === 'LIMIT_EXCEEDED') {
156
+ console.error('============================================================');
157
+ console.error('SESSION LIMIT EXCEEDED');
158
+ console.error('============================================================');
159
+ console.error(`Resource: ${meta.resource}`);
160
+ console.error(`Current: ${meta.current}, Max: ${meta.max}`);
161
+ console.error(`Message: ${meta.messageEn}`);
162
+ console.error(`한국어: ${meta.messageKo}`);
163
+ console.error(`Upgrade at: ${meta.upgradeUrl}`);
164
+ console.error('============================================================');
165
+ // Stop reconnection and exit
166
+ this.autoReconnect = false;
167
+ this.destroy();
168
+ process.exit(1);
169
+ }
170
+ else {
171
+ console.error(`Error: code=${meta.code}, message=${meta.messageEn}`);
172
+ }
173
+ }
174
+ scheduleReconnect() {
175
+ if (this.destroyed)
176
+ return;
177
+ // Check circuit breaker
178
+ if (this.circuitBreakerOpen) {
179
+ const now = Date.now();
180
+ if (now < this.circuitBreakerResetTime) {
181
+ const remainingSeconds = Math.ceil((this.circuitBreakerResetTime - now) / 1000);
182
+ console.log(`Circuit breaker open. Retry in ${remainingSeconds} seconds`);
183
+ this.reconnectTimer = setTimeout(() => this.scheduleReconnect(), this.circuitBreakerResetTime - now);
184
+ return;
185
+ }
186
+ else {
187
+ console.log('Circuit breaker reset');
188
+ this.circuitBreakerOpen = false;
189
+ this.reconnectAttempts = 0;
190
+ }
191
+ }
192
+ this.reconnectAttempts++;
193
+ // Check max attempts
194
+ if (this.reconnectAttempts > MAX_RECONNECT_ATTEMPTS) {
195
+ console.error(`Max reconnect attempts (${MAX_RECONNECT_ATTEMPTS}) reached. Circuit breaker active for ${CIRCUIT_BREAKER_DURATION_MS / 1000} seconds`);
196
+ this.circuitBreakerOpen = true;
197
+ this.circuitBreakerResetTime = Date.now() + CIRCUIT_BREAKER_DURATION_MS;
198
+ this.reconnectAttempts = 0;
199
+ this.reconnectTimer = setTimeout(() => this.scheduleReconnect(), CIRCUIT_BREAKER_DURATION_MS);
200
+ return;
201
+ }
202
+ // Exponential backoff with jitter
203
+ const delay = Math.min(BASE_RECONNECT_DELAY_MS * Math.pow(2, this.reconnectAttempts - 1), MAX_RECONNECT_DELAY_MS);
204
+ const jitter = Math.random() * delay * 0.5;
205
+ const reconnectDelay = Math.floor(delay + jitter);
206
+ console.log(`Reconnecting in ${reconnectDelay}ms (attempt ${this.reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS})`);
207
+ this.reconnectTimer = setTimeout(() => {
208
+ if (!this.isConnected && !this.destroyed) {
209
+ this.connect();
210
+ }
211
+ }, reconnectDelay);
212
+ }
213
+ send(message) {
214
+ if (!this.ws || this.ws.readyState !== ws_1.default.OPEN) {
215
+ return false;
216
+ }
217
+ try {
218
+ this.ws.send(JSON.stringify(message));
219
+ return true;
220
+ }
221
+ catch {
222
+ return false;
223
+ }
224
+ }
225
+ sendScreen(data) {
226
+ if (!this.isConnected)
227
+ return false;
228
+ const base64Data = data.toString('base64');
229
+ return this.send({
230
+ type: 'screen',
231
+ session: this.sessionId,
232
+ payload: base64Data
233
+ });
234
+ }
235
+ sendScreenCompressed(data) {
236
+ if (!this.isConnected)
237
+ return false;
238
+ try {
239
+ const compressed = zlib.gzipSync(data);
240
+ const base64Data = compressed.toString('base64');
241
+ return this.send({
242
+ type: 'screenGz',
243
+ session: this.sessionId,
244
+ payload: base64Data
245
+ });
246
+ }
247
+ catch {
248
+ return this.sendScreen(data);
249
+ }
250
+ }
251
+ /**
252
+ * Send file content to be displayed in the web FileViewer
253
+ * @param filename - The name of the file
254
+ * @param content - The file content (UTF-8 for text, base64 for images)
255
+ * @param contentType - MIME type (e.g., 'text/markdown', 'text/html', 'image/png')
256
+ * @param path - Optional file path
257
+ */
258
+ sendFileView(filename, content, contentType, path) {
259
+ if (!this.isConnected)
260
+ return false;
261
+ return this.send({
262
+ type: 'file_view',
263
+ session: this.sessionId,
264
+ meta: {
265
+ filename,
266
+ contentType,
267
+ path: path || ''
268
+ },
269
+ payload: content
270
+ });
271
+ }
272
+ getConnected() {
273
+ return this.isConnected;
274
+ }
275
+ destroy() {
276
+ this.destroyed = true;
277
+ this.autoReconnect = false;
278
+ if (this.reconnectTimer) {
279
+ clearTimeout(this.reconnectTimer);
280
+ this.reconnectTimer = null;
281
+ }
282
+ if (this.ws) {
283
+ this.ws.close();
284
+ this.ws = null;
285
+ }
286
+ }
287
+ }
288
+ exports.RelayWebSocketClient = RelayWebSocketClient;
package/dist/api.d.ts ADDED
@@ -0,0 +1,31 @@
1
+ export interface Agent {
2
+ id: string;
3
+ label: string | null;
4
+ machineId: string | null;
5
+ isActive: boolean;
6
+ apiEnabled: boolean;
7
+ lastConnectedAt: string | null;
8
+ createdAt: string;
9
+ }
10
+ export interface TmuxSession {
11
+ name: string;
12
+ windows: number;
13
+ created: string | null;
14
+ attached: boolean;
15
+ }
16
+ export interface SendKeysResult {
17
+ success: boolean;
18
+ agentId: string;
19
+ target: string;
20
+ error?: string;
21
+ }
22
+ declare class ApiClient {
23
+ private getHeaders;
24
+ listAgents(): Promise<Agent[]>;
25
+ getAgent(agentId: string): Promise<Agent>;
26
+ listSessions(agentId: string): Promise<TmuxSession[]>;
27
+ sendKeys(agentId: string, target: string, keys: string, enter?: boolean): Promise<SendKeysResult>;
28
+ findAgentByName(name: string): Promise<Agent | null>;
29
+ }
30
+ export declare const api: ApiClient;
31
+ export {};
package/dist/api.js ADDED
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.api = void 0;
7
+ const node_fetch_1 = __importDefault(require("node-fetch"));
8
+ const config_1 = require("./config");
9
+ class ApiClient {
10
+ getHeaders() {
11
+ const apiKey = (0, config_1.getApiKey)();
12
+ if (!apiKey) {
13
+ throw new Error('Not logged in. Run: sessioncast login');
14
+ }
15
+ return {
16
+ 'Authorization': `Bearer ${apiKey}`,
17
+ 'Content-Type': 'application/json'
18
+ };
19
+ }
20
+ async listAgents() {
21
+ const url = `${(0, config_1.getApiUrl)()}/api/v1/agents`;
22
+ const response = await (0, node_fetch_1.default)(url, {
23
+ method: 'GET',
24
+ headers: this.getHeaders()
25
+ });
26
+ if (!response.ok) {
27
+ const error = await response.json().catch(() => ({ message: response.statusText }));
28
+ throw new Error(error.message || 'Failed to list agents');
29
+ }
30
+ const data = await response.json();
31
+ return data.agents;
32
+ }
33
+ async getAgent(agentId) {
34
+ const url = `${(0, config_1.getApiUrl)()}/api/v1/agents/${agentId}`;
35
+ const response = await (0, node_fetch_1.default)(url, {
36
+ method: 'GET',
37
+ headers: this.getHeaders()
38
+ });
39
+ if (!response.ok) {
40
+ throw new Error('Agent not found');
41
+ }
42
+ return await response.json();
43
+ }
44
+ async listSessions(agentId) {
45
+ const url = `${(0, config_1.getApiUrl)()}/api/v1/agents/${agentId}/sessions`;
46
+ const response = await (0, node_fetch_1.default)(url, {
47
+ method: 'GET',
48
+ headers: this.getHeaders()
49
+ });
50
+ if (!response.ok) {
51
+ const error = await response.json().catch(() => ({ message: response.statusText }));
52
+ throw new Error(error.message || 'Failed to list sessions');
53
+ }
54
+ const data = await response.json();
55
+ return data.sessions;
56
+ }
57
+ async sendKeys(agentId, target, keys, enter = true) {
58
+ const url = `${(0, config_1.getApiUrl)()}/api/v1/agents/${agentId}/send-keys`;
59
+ const response = await (0, node_fetch_1.default)(url, {
60
+ method: 'POST',
61
+ headers: this.getHeaders(),
62
+ body: JSON.stringify({ target, keys, enter })
63
+ });
64
+ if (!response.ok) {
65
+ const error = await response.json().catch(() => ({ message: response.statusText }));
66
+ throw new Error(error.message || 'Failed to send keys');
67
+ }
68
+ return await response.json();
69
+ }
70
+ async findAgentByName(name) {
71
+ const agents = await this.listAgents();
72
+ // Search by label or machineId
73
+ return agents.find(a => a.label?.toLowerCase() === name.toLowerCase() ||
74
+ a.machineId?.toLowerCase() === name.toLowerCase() ||
75
+ a.id === name) || null;
76
+ }
77
+ }
78
+ exports.api = new ApiClient();
@@ -0,0 +1,5 @@
1
+ interface AgentOptions {
2
+ config?: string;
3
+ }
4
+ export declare function startAgent(options: AgentOptions): Promise<void>;
5
+ export {};