@kortix/agent-tunnel 0.1.4 → 0.12.7

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 (67) hide show
  1. package/README.md +65 -0
  2. package/dist/agent-cli.js +4676 -3174
  3. package/dist/client-cli.js +202 -493
  4. package/package.json +24 -29
  5. package/src/agent/agent.ts +67 -17
  6. package/src/agent/capabilities/desktop/cua-driver.ts +284 -0
  7. package/src/agent/capabilities/desktop.ts +100 -167
  8. package/src/agent/capabilities/enabled-registry.ts +24 -0
  9. package/src/agent/capabilities/filesystem.ts +136 -32
  10. package/src/agent/capabilities/index.ts +1 -1
  11. package/src/agent/capabilities/security.test.ts +204 -0
  12. package/src/agent/capabilities/shell.ts +37 -3
  13. package/src/agent/cli-device-auth.test.ts +179 -0
  14. package/src/agent/cli-help.test.ts +25 -0
  15. package/src/agent/cli.ts +475 -38
  16. package/src/agent/config.test.ts +53 -0
  17. package/src/agent/config.ts +169 -7
  18. package/src/agent/index.ts +1 -0
  19. package/src/agent/security/command-validator.ts +4 -2
  20. package/src/agent/security/path-validator.ts +73 -18
  21. package/src/agent/security/permission-guard.test.ts +52 -0
  22. package/src/agent/security/permission-guard.ts +35 -8
  23. package/src/agent/service.test.ts +63 -0
  24. package/src/agent/service.ts +410 -0
  25. package/src/client/cli.test.ts +150 -547
  26. package/src/client/cli.ts +116 -539
  27. package/src/client/index.ts +1 -1
  28. package/src/client/tools.ts +95 -356
  29. package/src/client/tunnel-client.ts +50 -80
  30. package/src/index.ts +7 -1
  31. package/src/node-ws-polyfill.test.ts +18 -0
  32. package/src/node-ws-polyfill.ts +5 -3
  33. package/src/server/heartbeat.ts +13 -6
  34. package/src/server/relay.test.ts +72 -0
  35. package/src/server/relay.ts +50 -9
  36. package/src/server/server.test.ts +33 -0
  37. package/src/server/server.ts +26 -6
  38. package/src/server/ws-handler.test.ts +158 -0
  39. package/src/server/ws-handler.ts +94 -36
  40. package/src/shared/crypto.ts +2 -3
  41. package/src/shared/index.ts +8 -0
  42. package/src/shared/permissions.ts +292 -0
  43. package/src/shared/types.ts +70 -41
  44. package/dist/agent/index.d.ts +0 -140
  45. package/dist/agent/index.js +0 -21
  46. package/dist/agent/index.js.map +0 -1
  47. package/dist/chunk-7N7GSU6K.js +0 -34
  48. package/dist/client/index.d.ts +0 -183
  49. package/dist/client/index.js +0 -8
  50. package/dist/client/index.js.map +0 -1
  51. package/dist/index.d.ts +0 -7
  52. package/dist/index.js +0 -55
  53. package/dist/index.js.map +0 -1
  54. package/dist/server/index.d.ts +0 -89
  55. package/dist/server/index.js +0 -14
  56. package/dist/server/index.js.map +0 -1
  57. package/dist/shared/index.d.ts +0 -10
  58. package/dist/shared/index.js +0 -20
  59. package/dist/shared/index.js.map +0 -1
  60. package/dist/types-Dpwrd8Ai.d.ts +0 -194
  61. package/src/agent/capabilities/desktop/atspi-helper.ts +0 -345
  62. package/src/agent/capabilities/desktop/csharp-helper.ts +0 -914
  63. package/src/agent/capabilities/desktop/linux-driver.ts +0 -368
  64. package/src/agent/capabilities/desktop/macos-driver.ts +0 -601
  65. package/src/agent/capabilities/desktop/swift-helper.ts +0 -736
  66. package/src/agent/capabilities/desktop/types.ts +0 -201
  67. package/src/agent/capabilities/desktop/windows-driver.ts +0 -220
@@ -0,0 +1,410 @@
1
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'fs';
2
+ import { homedir, platform, userInfo } from 'os';
3
+ import { dirname, join } from 'path';
4
+ import { spawnSync } from 'child_process';
5
+
6
+ export const SERVICE_LABEL = 'ai.kortix.agent-tunnel';
7
+ export const DEFAULT_INSTALL_BACKGROUND_SERVICE = true;
8
+
9
+ export interface ServicePaths {
10
+ configDir: string;
11
+ logDir: string;
12
+ launchdPlist: string;
13
+ systemdUnit: string;
14
+ windowsScript: string;
15
+ }
16
+
17
+ export interface ServiceStatus {
18
+ platform: NodeJS.Platform;
19
+ installed: boolean;
20
+ active: boolean | null;
21
+ path?: string;
22
+ detail?: string;
23
+ }
24
+
25
+ export function getServicePaths(): ServicePaths {
26
+ const home = homedir();
27
+ const configDir = join(home, '.agent-tunnel');
28
+ return {
29
+ configDir,
30
+ logDir: join(configDir, 'logs'),
31
+ launchdPlist: join(home, 'Library', 'LaunchAgents', `${SERVICE_LABEL}.plist`),
32
+ systemdUnit: join(home, '.config', 'systemd', 'user', `${SERVICE_LABEL}.service`),
33
+ windowsScript: join(configDir, 'agent-tunnel-service.ps1'),
34
+ };
35
+ }
36
+
37
+ function shellQuote(value: string): string {
38
+ return `'${value.replace(/'/g, `'\\''`)}'`;
39
+ }
40
+
41
+ function powershellQuote(value: string): string {
42
+ return `'${value.replace(/'/g, "''")}'`;
43
+ }
44
+
45
+ function xmlEscape(value: string): string {
46
+ return value
47
+ .replace(/&/g, '&')
48
+ .replace(/</g, '&lt;')
49
+ .replace(/>/g, '&gt;')
50
+ .replace(/"/g, '&quot;')
51
+ .replace(/'/g, '&apos;');
52
+ }
53
+
54
+ function currentRunnerParts(): { command: string; args: string[] } {
55
+ const exec = process.execPath;
56
+ const script = process.argv[1];
57
+ if (script && existsSync(script)) {
58
+ return { command: exec, args: [script, 'run', '--service'] };
59
+ }
60
+ throw new Error(
61
+ 'Cannot install the background service because the current Agent Tunnel executable was not found',
62
+ );
63
+ }
64
+
65
+ function currentRunnerCommand(): string {
66
+ const runner = currentRunnerParts();
67
+ return [runner.command, ...runner.args].map(shellQuote).join(' ');
68
+ }
69
+
70
+ export function buildServiceShellCommand(): string {
71
+ return `exec ${currentRunnerCommand()}`;
72
+ }
73
+
74
+ export function renderWindowsPowerShellScript(
75
+ runner = currentRunnerParts(),
76
+ ): string {
77
+ const command = powershellQuote(runner.command);
78
+ const args = runner.args.map(powershellQuote).join(' ');
79
+
80
+ return `$ErrorActionPreference = 'Continue'
81
+ while ($true) {
82
+ & ${command}${args ? ` ${args}` : ''}
83
+ Start-Sleep -Seconds 5
84
+ }
85
+ `;
86
+ }
87
+
88
+ function windowsTaskCommand(paths: ServicePaths): string {
89
+ return `powershell.exe -NoProfile -ExecutionPolicy Bypass -File "${paths.windowsScript}"`;
90
+ }
91
+
92
+ export function renderLaunchdPlist(command: string, paths: ServicePaths = getServicePaths()): string {
93
+ const stdout = join(paths.logDir, 'agent-tunnel.out.log');
94
+ const stderr = join(paths.logDir, 'agent-tunnel.err.log');
95
+ return `<?xml version="1.0" encoding="UTF-8"?>
96
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
97
+ <plist version="1.0">
98
+ <dict>
99
+ <key>Label</key>
100
+ <string>${xmlEscape(SERVICE_LABEL)}</string>
101
+ <key>ProgramArguments</key>
102
+ <array>
103
+ <string>/bin/sh</string>
104
+ <string>-lc</string>
105
+ <string>${xmlEscape(command)}</string>
106
+ </array>
107
+ <key>RunAtLoad</key>
108
+ <true/>
109
+ <key>KeepAlive</key>
110
+ <true/>
111
+ <key>Umask</key>
112
+ <integer>63</integer>
113
+ <key>StandardOutPath</key>
114
+ <string>${xmlEscape(stdout)}</string>
115
+ <key>StandardErrorPath</key>
116
+ <string>${xmlEscape(stderr)}</string>
117
+ <key>WorkingDirectory</key>
118
+ <string>${xmlEscape(homedir())}</string>
119
+ <key>EnvironmentVariables</key>
120
+ <dict>
121
+ <key>PATH</key>
122
+ <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
123
+ </dict>
124
+ </dict>
125
+ </plist>
126
+ `;
127
+ }
128
+
129
+ export function renderSystemdUnit(command: string, paths: ServicePaths = getServicePaths()): string {
130
+ const stdout = join(paths.logDir, 'agent-tunnel.out.log');
131
+ const stderr = join(paths.logDir, 'agent-tunnel.err.log');
132
+ return `[Unit]
133
+ Description=Kortix Agent Tunnel
134
+ After=network-online.target
135
+ Wants=network-online.target
136
+
137
+ [Service]
138
+ Type=simple
139
+ UMask=0077
140
+ ExecStart=/bin/sh -lc ${shellQuote(command)}
141
+ Restart=always
142
+ RestartSec=5
143
+ WorkingDirectory=${homedir()}
144
+ Environment=PATH=/usr/local/bin:/usr/bin:/bin
145
+ StandardOutput=append:${stdout}
146
+ StandardError=append:${stderr}
147
+
148
+ [Install]
149
+ WantedBy=default.target
150
+ `;
151
+ }
152
+
153
+ function run(command: string, args: string[]): { ok: boolean; detail: string } {
154
+ const result = spawnSync(command, args, { encoding: 'utf8' });
155
+ const detail = [result.stdout, result.stderr].filter(Boolean).join('\n').trim();
156
+ return { ok: result.status === 0, detail };
157
+ }
158
+
159
+ function launchdTarget(): string {
160
+ const uid = typeof process.getuid === 'function' ? process.getuid() : userInfo().uid;
161
+ return `gui/${uid}`;
162
+ }
163
+
164
+ export function installService(): ServiceStatus {
165
+ const paths = getServicePaths();
166
+ mkdirSync(paths.configDir, { recursive: true, mode: 0o700 });
167
+ mkdirSync(paths.logDir, { recursive: true, mode: 0o700 });
168
+
169
+ const command = buildServiceShellCommand();
170
+
171
+ if (platform() === 'darwin') {
172
+ mkdirSync(dirname(paths.launchdPlist), { recursive: true });
173
+ writeFileSync(paths.launchdPlist, renderLaunchdPlist(command, paths), { mode: 0o600 });
174
+ run('launchctl', ['bootout', launchdTarget(), paths.launchdPlist]);
175
+ const boot = run('launchctl', ['bootstrap', launchdTarget(), paths.launchdPlist]);
176
+ const kick = run('launchctl', ['kickstart', '-k', `${launchdTarget()}/${SERVICE_LABEL}`]);
177
+ return {
178
+ platform: platform(),
179
+ installed: true,
180
+ active: boot.ok || kick.ok ? true : null,
181
+ path: paths.launchdPlist,
182
+ detail: [boot.detail, kick.detail].filter(Boolean).join('\n'),
183
+ };
184
+ }
185
+
186
+ if (platform() === 'linux') {
187
+ mkdirSync(dirname(paths.systemdUnit), { recursive: true });
188
+ writeFileSync(paths.systemdUnit, renderSystemdUnit(command, paths), { mode: 0o600 });
189
+ const reload = run('systemctl', ['--user', 'daemon-reload']);
190
+ const enable = run('systemctl', ['--user', 'enable', '--now', `${SERVICE_LABEL}.service`]);
191
+ return {
192
+ platform: platform(),
193
+ installed: true,
194
+ active: enable.ok ? true : null,
195
+ path: paths.systemdUnit,
196
+ detail: [reload.detail, enable.detail].filter(Boolean).join('\n'),
197
+ };
198
+ }
199
+
200
+ if (platform() === 'win32') {
201
+ writeFileSync(paths.windowsScript, renderWindowsPowerShellScript(), { mode: 0o600 });
202
+ const create = run('schtasks.exe', [
203
+ '/Create',
204
+ '/TN',
205
+ SERVICE_LABEL,
206
+ '/TR',
207
+ windowsTaskCommand(paths),
208
+ '/SC',
209
+ 'ONLOGON',
210
+ '/F',
211
+ '/RL',
212
+ 'LIMITED',
213
+ ]);
214
+ const start = run('schtasks.exe', ['/Run', '/TN', SERVICE_LABEL]);
215
+ return {
216
+ platform: platform(),
217
+ installed: create.ok,
218
+ active: start.ok ? true : null,
219
+ path: paths.windowsScript,
220
+ detail: [create.detail, start.detail].filter(Boolean).join('\n'),
221
+ };
222
+ }
223
+
224
+ throw new Error('Background service install is currently supported on macOS launchd, Linux systemd user services, and Windows Scheduled Tasks.');
225
+ }
226
+
227
+ export function uninstallService(): ServiceStatus {
228
+ const paths = getServicePaths();
229
+
230
+ if (platform() === 'darwin') {
231
+ const existed = existsSync(paths.launchdPlist);
232
+ const stop = run('launchctl', ['bootout', launchdTarget(), paths.launchdPlist]);
233
+ if (existed) rmSync(paths.launchdPlist, { force: true });
234
+ return {
235
+ platform: platform(),
236
+ installed: false,
237
+ active: false,
238
+ path: paths.launchdPlist,
239
+ detail: stop.detail,
240
+ };
241
+ }
242
+
243
+ if (platform() === 'linux') {
244
+ const existed = existsSync(paths.systemdUnit);
245
+ const disable = run('systemctl', ['--user', 'disable', '--now', `${SERVICE_LABEL}.service`]);
246
+ if (existed) rmSync(paths.systemdUnit, { force: true });
247
+ run('systemctl', ['--user', 'daemon-reload']);
248
+ return {
249
+ platform: platform(),
250
+ installed: false,
251
+ active: false,
252
+ path: paths.systemdUnit,
253
+ detail: disable.detail,
254
+ };
255
+ }
256
+
257
+ if (platform() === 'win32') {
258
+ const existed = existsSync(paths.windowsScript);
259
+ const stop = run('schtasks.exe', ['/End', '/TN', SERVICE_LABEL]);
260
+ const del = run('schtasks.exe', ['/Delete', '/TN', SERVICE_LABEL, '/F']);
261
+ if (existed) rmSync(paths.windowsScript, { force: true });
262
+ return {
263
+ platform: platform(),
264
+ installed: false,
265
+ active: false,
266
+ path: paths.windowsScript,
267
+ detail: [stop.detail, del.detail].filter(Boolean).join('\n'),
268
+ };
269
+ }
270
+
271
+ throw new Error('Background service uninstall is currently supported on macOS launchd, Linux systemd user services, and Windows Scheduled Tasks.');
272
+ }
273
+
274
+ export function startService(): ServiceStatus {
275
+ const paths = getServicePaths();
276
+
277
+ if (platform() === 'darwin') {
278
+ const installed = existsSync(paths.launchdPlist);
279
+ const boot = installed ? run('launchctl', ['bootstrap', launchdTarget(), paths.launchdPlist]) : { ok: false, detail: 'LaunchAgent is not installed.' };
280
+ const kick = run('launchctl', ['kickstart', '-k', `${launchdTarget()}/${SERVICE_LABEL}`]);
281
+ return {
282
+ platform: platform(),
283
+ installed,
284
+ active: boot.ok || kick.ok ? true : null,
285
+ path: paths.launchdPlist,
286
+ detail: [boot.detail, kick.detail].filter(Boolean).join('\n'),
287
+ };
288
+ }
289
+
290
+ if (platform() === 'linux') {
291
+ const installed = existsSync(paths.systemdUnit);
292
+ const start = installed ? run('systemctl', ['--user', 'start', `${SERVICE_LABEL}.service`]) : { ok: false, detail: 'systemd unit is not installed.' };
293
+ return {
294
+ platform: platform(),
295
+ installed,
296
+ active: start.ok ? true : null,
297
+ path: paths.systemdUnit,
298
+ detail: start.detail,
299
+ };
300
+ }
301
+
302
+ if (platform() === 'win32') {
303
+ const installed = existsSync(paths.windowsScript);
304
+ const start = installed ? run('schtasks.exe', ['/Run', '/TN', SERVICE_LABEL]) : { ok: false, detail: 'Scheduled Task is not installed.' };
305
+ return {
306
+ platform: platform(),
307
+ installed,
308
+ active: start.ok ? true : null,
309
+ path: paths.windowsScript,
310
+ detail: start.detail,
311
+ };
312
+ }
313
+
314
+ throw new Error('Background service start is currently supported on macOS, Linux, and Windows.');
315
+ }
316
+
317
+ export function stopService(): ServiceStatus {
318
+ const paths = getServicePaths();
319
+
320
+ if (platform() === 'darwin') {
321
+ const installed = existsSync(paths.launchdPlist);
322
+ const stop = installed ? run('launchctl', ['bootout', launchdTarget(), paths.launchdPlist]) : { ok: false, detail: 'LaunchAgent is not installed.' };
323
+ return {
324
+ platform: platform(),
325
+ installed,
326
+ active: false,
327
+ path: paths.launchdPlist,
328
+ detail: stop.detail,
329
+ };
330
+ }
331
+
332
+ if (platform() === 'linux') {
333
+ const installed = existsSync(paths.systemdUnit);
334
+ const stop = installed ? run('systemctl', ['--user', 'stop', `${SERVICE_LABEL}.service`]) : { ok: false, detail: 'systemd unit is not installed.' };
335
+ return {
336
+ platform: platform(),
337
+ installed,
338
+ active: false,
339
+ path: paths.systemdUnit,
340
+ detail: stop.detail,
341
+ };
342
+ }
343
+
344
+ if (platform() === 'win32') {
345
+ const installed = existsSync(paths.windowsScript);
346
+ const stop = installed ? run('schtasks.exe', ['/End', '/TN', SERVICE_LABEL]) : { ok: false, detail: 'Scheduled Task is not installed.' };
347
+ return {
348
+ platform: platform(),
349
+ installed,
350
+ active: false,
351
+ path: paths.windowsScript,
352
+ detail: stop.detail,
353
+ };
354
+ }
355
+
356
+ throw new Error('Background service stop is currently supported on macOS, Linux, and Windows.');
357
+ }
358
+
359
+ export function restartService(): ServiceStatus {
360
+ stopService();
361
+ return startService();
362
+ }
363
+
364
+ export function getServiceStatus(): ServiceStatus {
365
+ const paths = getServicePaths();
366
+
367
+ if (platform() === 'darwin') {
368
+ const installed = existsSync(paths.launchdPlist);
369
+ const status = run('launchctl', ['print', `${launchdTarget()}/${SERVICE_LABEL}`]);
370
+ return {
371
+ platform: platform(),
372
+ installed,
373
+ active: status.ok,
374
+ path: paths.launchdPlist,
375
+ detail: status.detail || (installed ? readFileSync(paths.launchdPlist, 'utf8') : undefined),
376
+ };
377
+ }
378
+
379
+ if (platform() === 'linux') {
380
+ const installed = existsSync(paths.systemdUnit);
381
+ const status = run('systemctl', ['--user', 'is-active', `${SERVICE_LABEL}.service`]);
382
+ return {
383
+ platform: platform(),
384
+ installed,
385
+ active: status.ok,
386
+ path: paths.systemdUnit,
387
+ detail: status.detail,
388
+ };
389
+ }
390
+
391
+ if (platform() === 'win32') {
392
+ const installed = existsSync(paths.windowsScript);
393
+ const status = run('schtasks.exe', ['/Query', '/TN', SERVICE_LABEL, '/FO', 'LIST', '/V']);
394
+ const detail = status.detail || (installed ? readFileSync(paths.windowsScript, 'utf8') : undefined);
395
+ return {
396
+ platform: platform(),
397
+ installed,
398
+ active: status.ok ? /Status:\s*Running/i.test(detail ?? '') : false,
399
+ path: paths.windowsScript,
400
+ detail,
401
+ };
402
+ }
403
+
404
+ return {
405
+ platform: platform(),
406
+ installed: false,
407
+ active: null,
408
+ detail: 'Background service status is currently supported on macOS launchd, Linux systemd user services, and Windows Scheduled Tasks.',
409
+ };
410
+ }