@ekkos/cli 1.3.2 → 1.3.5

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 (119) hide show
  1. package/dist/capture/jsonl-rewriter.d.ts +1 -1
  2. package/dist/capture/jsonl-rewriter.js +3 -3
  3. package/dist/capture/transcript-repair.d.ts +2 -2
  4. package/dist/capture/transcript-repair.js +2 -2
  5. package/dist/commands/claw.d.ts +13 -0
  6. package/dist/commands/claw.js +253 -0
  7. package/dist/commands/dashboard.js +617 -83
  8. package/dist/commands/doctor.d.ts +3 -3
  9. package/dist/commands/doctor.js +6 -79
  10. package/dist/commands/gemini.d.ts +19 -0
  11. package/dist/commands/gemini.js +193 -0
  12. package/dist/commands/init.js +2 -25
  13. package/dist/commands/run.d.ts +0 -1
  14. package/dist/commands/run.js +147 -241
  15. package/dist/commands/scan.d.ts +21 -0
  16. package/dist/commands/scan.js +386 -0
  17. package/dist/commands/swarm-dashboard.js +156 -28
  18. package/dist/commands/swarm.d.ts +1 -1
  19. package/dist/commands/swarm.js +1 -1
  20. package/dist/commands/test-claude.d.ts +2 -2
  21. package/dist/commands/test-claude.js +3 -3
  22. package/dist/deploy/index.d.ts +0 -2
  23. package/dist/deploy/index.js +0 -2
  24. package/dist/deploy/settings.d.ts +2 -2
  25. package/dist/deploy/settings.js +42 -4
  26. package/dist/deploy/skills.js +1 -2
  27. package/dist/index.js +79 -19
  28. package/dist/lib/usage-parser.js +4 -3
  29. package/dist/utils/proxy-url.d.ts +12 -1
  30. package/dist/utils/proxy-url.js +16 -1
  31. package/dist/utils/templates.js +1 -1
  32. package/package.json +4 -6
  33. package/templates/CLAUDE.md +49 -107
  34. package/dist/agent/daemon.d.ts +0 -130
  35. package/dist/agent/daemon.js +0 -606
  36. package/dist/agent/health-check.d.ts +0 -35
  37. package/dist/agent/health-check.js +0 -243
  38. package/dist/agent/pty-runner.d.ts +0 -53
  39. package/dist/agent/pty-runner.js +0 -190
  40. package/dist/commands/agent.d.ts +0 -50
  41. package/dist/commands/agent.js +0 -544
  42. package/dist/commands/setup-remote.d.ts +0 -20
  43. package/dist/commands/setup-remote.js +0 -582
  44. package/dist/commands/synk.d.ts +0 -7
  45. package/dist/commands/synk.js +0 -339
  46. package/dist/synk/api.d.ts +0 -22
  47. package/dist/synk/api.js +0 -133
  48. package/dist/synk/auth.d.ts +0 -7
  49. package/dist/synk/auth.js +0 -30
  50. package/dist/synk/config.d.ts +0 -18
  51. package/dist/synk/config.js +0 -37
  52. package/dist/synk/daemon/control-client.d.ts +0 -11
  53. package/dist/synk/daemon/control-client.js +0 -101
  54. package/dist/synk/daemon/control-server.d.ts +0 -24
  55. package/dist/synk/daemon/control-server.js +0 -91
  56. package/dist/synk/daemon/run.d.ts +0 -14
  57. package/dist/synk/daemon/run.js +0 -338
  58. package/dist/synk/encryption.d.ts +0 -17
  59. package/dist/synk/encryption.js +0 -133
  60. package/dist/synk/index.d.ts +0 -13
  61. package/dist/synk/index.js +0 -36
  62. package/dist/synk/machine-client.d.ts +0 -42
  63. package/dist/synk/machine-client.js +0 -218
  64. package/dist/synk/persistence.d.ts +0 -51
  65. package/dist/synk/persistence.js +0 -211
  66. package/dist/synk/qr.d.ts +0 -5
  67. package/dist/synk/qr.js +0 -33
  68. package/dist/synk/session-bridge.d.ts +0 -58
  69. package/dist/synk/session-bridge.js +0 -171
  70. package/dist/synk/session-client.d.ts +0 -46
  71. package/dist/synk/session-client.js +0 -240
  72. package/dist/synk/types.d.ts +0 -574
  73. package/dist/synk/types.js +0 -74
  74. package/dist/utils/verify-remote-terminal.d.ts +0 -10
  75. package/dist/utils/verify-remote-terminal.js +0 -415
  76. package/templates/README.md +0 -378
  77. package/templates/claude-plugins/PHASE2_COMPLETION.md +0 -346
  78. package/templates/claude-plugins/PLUGIN_PROPOSALS.md +0 -1776
  79. package/templates/claude-plugins/README.md +0 -587
  80. package/templates/claude-plugins/agents/code-reviewer.json +0 -14
  81. package/templates/claude-plugins/agents/debug-detective.json +0 -15
  82. package/templates/claude-plugins/agents/git-companion.json +0 -14
  83. package/templates/claude-plugins/blog-manager/.claude-plugin/plugin.json +0 -8
  84. package/templates/claude-plugins/blog-manager/commands/blog.md +0 -691
  85. package/templates/claude-plugins/golden-loop-monitor/.claude-plugin/plugin.json +0 -8
  86. package/templates/claude-plugins/golden-loop-monitor/commands/loop-status.md +0 -434
  87. package/templates/claude-plugins/learning-tracker/.claude-plugin/plugin.json +0 -8
  88. package/templates/claude-plugins/learning-tracker/commands/my-patterns.md +0 -282
  89. package/templates/claude-plugins/memory-lens/.claude-plugin/plugin.json +0 -8
  90. package/templates/claude-plugins/memory-lens/commands/memory-search.md +0 -181
  91. package/templates/claude-plugins/pattern-coach/.claude-plugin/plugin.json +0 -8
  92. package/templates/claude-plugins/pattern-coach/commands/forge.md +0 -365
  93. package/templates/claude-plugins/project-schema-validator/.claude-plugin/plugin.json +0 -8
  94. package/templates/claude-plugins/project-schema-validator/commands/validate-schema.md +0 -582
  95. package/templates/commands/continue.md +0 -47
  96. package/templates/cursor-rules/ekkos-memory.md +0 -127
  97. package/templates/ekkos-manifest.json +0 -223
  98. package/templates/helpers/json-parse.cjs +0 -101
  99. package/templates/plan-template.md +0 -306
  100. package/templates/shared/hooks-enabled.json +0 -22
  101. package/templates/shared/session-words.json +0 -45
  102. package/templates/skills/ekkOS_Deep_Recall/Skill.md +0 -282
  103. package/templates/skills/ekkOS_Learn/Skill.md +0 -265
  104. package/templates/skills/ekkOS_Memory_First/Skill.md +0 -206
  105. package/templates/skills/ekkOS_Plan_Assist/Skill.md +0 -302
  106. package/templates/skills/ekkOS_Preferences/Skill.md +0 -247
  107. package/templates/skills/ekkOS_Reflect/Skill.md +0 -257
  108. package/templates/skills/ekkOS_Safety/Skill.md +0 -265
  109. package/templates/skills/ekkOS_Schema/Skill.md +0 -251
  110. package/templates/skills/ekkOS_Summary/Skill.md +0 -257
  111. package/templates/spec-template.md +0 -159
  112. package/templates/windsurf-rules/ekkos-memory.md +0 -127
  113. package/templates/windsurf-skills/README.md +0 -58
  114. package/templates/windsurf-skills/ekkos-continue/SKILL.md +0 -81
  115. package/templates/windsurf-skills/ekkos-golden-loop/SKILL.md +0 -225
  116. package/templates/windsurf-skills/ekkos-insights/SKILL.md +0 -138
  117. package/templates/windsurf-skills/ekkos-recall/SKILL.md +0 -96
  118. package/templates/windsurf-skills/ekkos-safety/SKILL.md +0 -89
  119. package/templates/windsurf-skills/ekkos-vault/SKILL.md +0 -86
@@ -1,582 +0,0 @@
1
- "use strict";
2
- /**
3
- * ekkos setup-remote - One-command setup for remote terminal access
4
- *
5
- * This command does everything automatically:
6
- * 1. Checks if user is logged in (prompts login if not)
7
- * 2. Generates unique device ID and fingerprint
8
- * 3. Opens browser for device pairing approval
9
- * 4. Installs background agent as system service
10
- * 5. Starts agent immediately
11
- * 6. Verifies connection to cloud relay
12
- */
13
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
14
- if (k2 === undefined) k2 = k;
15
- var desc = Object.getOwnPropertyDescriptor(m, k);
16
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
17
- desc = { enumerable: true, get: function() { return m[k]; } };
18
- }
19
- Object.defineProperty(o, k2, desc);
20
- }) : (function(o, m, k, k2) {
21
- if (k2 === undefined) k2 = k;
22
- o[k2] = m[k];
23
- }));
24
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
25
- Object.defineProperty(o, "default", { enumerable: true, value: v });
26
- }) : function(o, v) {
27
- o["default"] = v;
28
- });
29
- var __importStar = (this && this.__importStar) || (function () {
30
- var ownKeys = function(o) {
31
- ownKeys = Object.getOwnPropertyNames || function (o) {
32
- var ar = [];
33
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
34
- return ar;
35
- };
36
- return ownKeys(o);
37
- };
38
- return function (mod) {
39
- if (mod && mod.__esModule) return mod;
40
- var result = {};
41
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
42
- __setModuleDefault(result, mod);
43
- return result;
44
- };
45
- })();
46
- var __importDefault = (this && this.__importDefault) || function (mod) {
47
- return (mod && mod.__esModule) ? mod : { "default": mod };
48
- };
49
- Object.defineProperty(exports, "__esModule", { value: true });
50
- exports.setupRemote = setupRemote;
51
- const chalk_1 = __importDefault(require("chalk"));
52
- const os = __importStar(require("os"));
53
- const fs = __importStar(require("fs"));
54
- const path = __importStar(require("path"));
55
- const crypto = __importStar(require("crypto"));
56
- const child_process_1 = require("child_process");
57
- const open_1 = __importDefault(require("open"));
58
- const state_1 = require("../utils/state");
59
- const init_1 = require("./init");
60
- // Get version from package.json (CommonJS compatible)
61
- const pkgPath = path.resolve(__dirname, '../../package.json');
62
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
63
- const PLATFORM_URL = process.env.PLATFORM_URL || 'https://platform.ekkos.dev';
64
- const RELAY_API_URL = process.env.RELAY_URL || 'https://ekkos-relay-production.up.railway.app';
65
- const POLLING_INTERVAL = 2000; // 2 seconds
66
- const POLLING_TIMEOUT = 600000; // 10 minutes
67
- /**
68
- * Get or create device info
69
- */
70
- function getOrCreateDeviceInfo() {
71
- const deviceFilePath = path.join(state_1.EKKOS_DIR, 'device.json');
72
- // Check if device already registered
73
- if (fs.existsSync(deviceFilePath)) {
74
- const existing = JSON.parse(fs.readFileSync(deviceFilePath, 'utf-8'));
75
- if (existing.deviceId) {
76
- return existing;
77
- }
78
- }
79
- // Generate new device ID
80
- const deviceId = crypto.randomUUID();
81
- // Get device name from hostname or system info
82
- let deviceName = os.hostname();
83
- if (os.platform() === 'darwin') {
84
- try {
85
- const computerName = (0, child_process_1.execSync)('scutil --get ComputerName', { encoding: 'utf-8' }).trim();
86
- if (computerName)
87
- deviceName = computerName;
88
- }
89
- catch {
90
- // Fall back to hostname
91
- }
92
- }
93
- const deviceInfo = {
94
- deviceId,
95
- deviceName,
96
- hostname: os.hostname(),
97
- platform: os.platform(),
98
- arch: os.arch(),
99
- nodeVersion: process.version,
100
- cliVersion: pkg.version,
101
- };
102
- // Save device info
103
- if (!fs.existsSync(state_1.EKKOS_DIR)) {
104
- fs.mkdirSync(state_1.EKKOS_DIR, { recursive: true });
105
- }
106
- fs.writeFileSync(deviceFilePath, JSON.stringify(deviceInfo, null, 2));
107
- return deviceInfo;
108
- }
109
- /**
110
- * Request device pairing code from server
111
- */
112
- async function requestPairingCode(deviceInfo, authToken) {
113
- const response = await fetch(`${RELAY_API_URL}/api/v1/relay/pair`, {
114
- method: 'POST',
115
- headers: {
116
- 'Authorization': `Bearer ${authToken}`,
117
- 'Content-Type': 'application/json',
118
- },
119
- body: JSON.stringify({
120
- deviceId: deviceInfo.deviceId,
121
- deviceName: deviceInfo.deviceName,
122
- hostname: deviceInfo.hostname,
123
- platform: deviceInfo.platform,
124
- arch: deviceInfo.arch,
125
- fingerprint: deviceInfo,
126
- }),
127
- });
128
- if (!response.ok) {
129
- const errorData = await response.json().catch(() => ({ error: 'Unknown error' }));
130
- const errorMessage = typeof errorData === 'string'
131
- ? errorData
132
- : errorData.error || errorData.message || JSON.stringify(errorData);
133
- throw new Error(errorMessage || `Failed to request pairing code: ${response.status}`);
134
- }
135
- return response.json();
136
- }
137
- /**
138
- * Poll for pairing approval
139
- */
140
- async function pollForApproval(deviceCode, authToken) {
141
- const startTime = Date.now();
142
- while (Date.now() - startTime < POLLING_TIMEOUT) {
143
- const response = await fetch(`${RELAY_API_URL}/api/v1/relay/pair/${deviceCode}`, {
144
- headers: {
145
- 'Authorization': `Bearer ${authToken}`,
146
- },
147
- });
148
- if (response.status === 200) {
149
- const data = await response.json();
150
- if (data.status === 'approved') {
151
- return { approved: true, deviceToken: data.deviceToken };
152
- }
153
- }
154
- else if (response.status === 403) {
155
- // Denied
156
- return { approved: false };
157
- }
158
- else if (response.status === 410) {
159
- // Expired
160
- throw new Error('Pairing code expired');
161
- }
162
- // Wait before next poll
163
- await new Promise(resolve => setTimeout(resolve, POLLING_INTERVAL));
164
- process.stdout.write('.');
165
- }
166
- throw new Error('Pairing timeout');
167
- }
168
- /**
169
- * Install agent as system service
170
- */
171
- async function installService(verbose) {
172
- const platform = os.platform();
173
- if (platform === 'darwin') {
174
- await installMacOSService(verbose);
175
- }
176
- else if (platform === 'win32') {
177
- await installWindowsService(verbose);
178
- }
179
- else if (platform === 'linux') {
180
- await installLinuxService(verbose);
181
- }
182
- else {
183
- throw new Error(`Unsupported platform: ${platform}`);
184
- }
185
- }
186
- /**
187
- * Install macOS launchd service
188
- */
189
- async function installMacOSService(verbose) {
190
- const plistName = 'dev.ekkos.agent';
191
- const plistPath = path.join(os.homedir(), 'Library', 'LaunchAgents', `${plistName}.plist`);
192
- // Find node path - use execPath of current process
193
- const nodePath = process.execPath;
194
- if (verbose) {
195
- console.log(chalk_1.default.gray(` Node path: ${nodePath}`));
196
- }
197
- // Find ekkos CLI path
198
- let ekkosPath;
199
- let useNpx = false;
200
- try {
201
- ekkosPath = (0, child_process_1.execSync)('which ekkos', { encoding: 'utf-8' }).trim();
202
- if (!ekkosPath)
203
- throw new Error('Empty path');
204
- }
205
- catch {
206
- // Fall back to npx
207
- ekkosPath = 'npx';
208
- useNpx = true;
209
- }
210
- if (verbose) {
211
- console.log(chalk_1.default.gray(` ekkOS path: ${ekkosPath}${useNpx ? ' (via npx)' : ''}`));
212
- }
213
- // Build program arguments
214
- const programArgs = [nodePath];
215
- if (useNpx) {
216
- programArgs.push(ekkosPath);
217
- programArgs.push('@ekkos/cli');
218
- }
219
- else {
220
- programArgs.push(ekkosPath);
221
- }
222
- programArgs.push('agent');
223
- programArgs.push('daemon');
224
- // Build plist XML programmatically to avoid string template issues
225
- const plistContent = buildPlist(plistName, programArgs, nodePath);
226
- // Ensure directory exists
227
- const launchAgentsDir = path.dirname(plistPath);
228
- if (!fs.existsSync(launchAgentsDir)) {
229
- fs.mkdirSync(launchAgentsDir, { recursive: true });
230
- }
231
- // Unload if already loaded
232
- try {
233
- (0, child_process_1.execSync)(`launchctl unload "${plistPath}" 2>/dev/null`, { encoding: 'utf-8' });
234
- if (verbose) {
235
- console.log(chalk_1.default.gray(` Unloaded existing: ${plistName}`));
236
- }
237
- }
238
- catch {
239
- // Ignore if not loaded
240
- }
241
- // Write plist
242
- fs.writeFileSync(plistPath, plistContent);
243
- if (verbose) {
244
- console.log(chalk_1.default.gray(` Created: ${plistPath}`));
245
- }
246
- // Load the service
247
- try {
248
- (0, child_process_1.execSync)(`launchctl load "${plistPath}"`, { encoding: 'utf-8' });
249
- if (verbose) {
250
- console.log(chalk_1.default.gray(` Loaded: ${plistName}`));
251
- }
252
- }
253
- catch (err) {
254
- throw new Error(`Failed to load launchd service: ${err.message}`);
255
- }
256
- // Verify it's loaded
257
- try {
258
- const output = (0, child_process_1.execSync)('launchctl list | grep dev.ekkos.agent', { encoding: 'utf-8' });
259
- if (verbose && output.trim()) {
260
- console.log(chalk_1.default.gray(` Service status: ${output.trim()}`));
261
- }
262
- }
263
- catch {
264
- // Ignore if grep finds nothing
265
- }
266
- }
267
- /**
268
- * Build plist content with proper XML escaping
269
- */
270
- function buildPlist(label, args, nodePath) {
271
- const homeDir = os.homedir();
272
- const ekkosDir = state_1.EKKOS_DIR;
273
- const nodeBinDir = path.dirname(nodePath);
274
- const launchPath = `${nodeBinDir}:${path.join(homeDir, '.local', 'bin')}:/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin`;
275
- // Build ProgramArguments array
276
- let argXml = '';
277
- for (const arg of args) {
278
- argXml += ` <string>${escapeXml(arg)}</string>\n`;
279
- }
280
- return `<?xml version="1.0" encoding="UTF-8"?>
281
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
282
- <plist version="1.0">
283
- <dict>
284
- <key>Label</key>
285
- <string>${escapeXml(label)}</string>
286
-
287
- <key>ProgramArguments</key>
288
- <array>
289
- ${argXml} </array>
290
-
291
- <!-- Run at system login and on demand -->
292
- <key>RunAtLoad</key>
293
- <true/>
294
-
295
- <!-- Keep alive: restart immediately if it exits or crashes -->
296
- <key>KeepAlive</key>
297
- <dict>
298
- <!-- Restart even if exit code is 0 (successful exit) -->
299
- <key>SuccessfulExit</key>
300
- <false/>
301
- <!-- Restart if process is terminated by signal -->
302
- <key>Crashed</key>
303
- <true/>
304
- </dict>
305
-
306
- <!-- Logging -->
307
- <key>StandardErrorPath</key>
308
- <string>${escapeXml(path.join(ekkosDir, 'agent.err.log'))}</string>
309
-
310
- <key>StandardOutPath</key>
311
- <string>${escapeXml(path.join(ekkosDir, 'agent.out.log'))}</string>
312
-
313
- <!-- Environment variables -->
314
- <key>EnvironmentVariables</key>
315
- <dict>
316
- <key>PATH</key>
317
- <string>${escapeXml(launchPath)}</string>
318
- <key>NODE_ENV</key>
319
- <string>production</string>
320
- <key>HOME</key>
321
- <string>${escapeXml(homeDir)}</string>
322
- </dict>
323
-
324
- <!-- Process management -->
325
- <key>ProcessType</key>
326
- <string>Background</string>
327
-
328
- <!-- Critical: Allow daemon to handle network reconnections -->
329
- <key>AbandonProcessGroup</key>
330
- <true/>
331
-
332
- <!-- Restart throttle: wait 10s before restarting crashed process -->
333
- <key>ThrottleInterval</key>
334
- <integer>10</integer>
335
-
336
- <!-- Higher priority for reliable connectivity -->
337
- <key>Nice</key>
338
- <integer>-5</integer>
339
-
340
- <!-- File descriptor limits -->
341
- <key>SoftResourceLimits</key>
342
- <dict>
343
- <key>NumberOfFiles</key>
344
- <integer>2048</integer>
345
- </dict>
346
-
347
- <!-- Working directory -->
348
- <key>WorkingDirectory</key>
349
- <string>${escapeXml(homeDir)}</string>
350
- </dict>
351
- </plist>`;
352
- }
353
- /**
354
- * Escape XML special characters
355
- */
356
- function escapeXml(str) {
357
- return str
358
- .replace(/&/g, '&amp;')
359
- .replace(/</g, '&lt;')
360
- .replace(/>/g, '&gt;')
361
- .replace(/"/g, '&quot;')
362
- .replace(/'/g, '&apos;');
363
- }
364
- /**
365
- * Install Windows service
366
- */
367
- async function installWindowsService(verbose) {
368
- // Windows uses a scheduled task that runs at login
369
- const taskName = 'ekkOS Agent';
370
- // Find ekkos CLI path
371
- let ekkosPath;
372
- try {
373
- ekkosPath = (0, child_process_1.execSync)('where ekkos', { encoding: 'utf-8' }).trim().split('\n')[0];
374
- }
375
- catch {
376
- // Fall back to npx
377
- ekkosPath = 'npx';
378
- }
379
- // Create scheduled task
380
- const command = ekkosPath.includes('npx')
381
- ? `npx @ekkos/cli agent daemon`
382
- : `"${ekkosPath}" agent daemon`;
383
- try {
384
- // Delete existing task
385
- (0, child_process_1.execSync)(`schtasks /delete /tn "${taskName}" /f 2>nul`, { encoding: 'utf-8' });
386
- }
387
- catch {
388
- // Ignore if doesn't exist
389
- }
390
- // Create task that runs at login
391
- (0, child_process_1.execSync)(`schtasks /create /tn "${taskName}" /tr "${command}" /sc onlogon /rl limited`, { encoding: 'utf-8' });
392
- // Start the task now
393
- (0, child_process_1.execSync)(`schtasks /run /tn "${taskName}"`, { encoding: 'utf-8' });
394
- if (verbose) {
395
- console.log(chalk_1.default.gray(` Created scheduled task: ${taskName}`));
396
- }
397
- }
398
- /**
399
- * Install Linux systemd service
400
- */
401
- async function installLinuxService(verbose) {
402
- const serviceName = 'ekkos-agent';
403
- const serviceDir = path.join(os.homedir(), '.config', 'systemd', 'user');
404
- const servicePath = path.join(serviceDir, `${serviceName}.service`);
405
- // Find ekkos CLI path
406
- let ekkosPath;
407
- try {
408
- ekkosPath = (0, child_process_1.execSync)('which ekkos', { encoding: 'utf-8' }).trim();
409
- }
410
- catch {
411
- ekkosPath = 'npx @ekkos/cli';
412
- }
413
- const serviceContent = `[Unit]
414
- Description=ekkOS Remote Terminal Agent
415
- After=network.target
416
-
417
- [Service]
418
- Type=simple
419
- ExecStart=${process.execPath} ${ekkosPath} agent daemon
420
- Restart=always
421
- RestartSec=10
422
- StandardOutput=append:${path.join(state_1.EKKOS_DIR, 'agent.out.log')}
423
- StandardError=append:${path.join(state_1.EKKOS_DIR, 'agent.err.log')}
424
-
425
- [Install]
426
- WantedBy=default.target
427
- `;
428
- // Ensure directory exists
429
- if (!fs.existsSync(serviceDir)) {
430
- fs.mkdirSync(serviceDir, { recursive: true });
431
- }
432
- // Write service file
433
- fs.writeFileSync(servicePath, serviceContent);
434
- // Reload systemd and enable service
435
- (0, child_process_1.execSync)('systemctl --user daemon-reload', { encoding: 'utf-8' });
436
- (0, child_process_1.execSync)(`systemctl --user enable ${serviceName}`, { encoding: 'utf-8' });
437
- (0, child_process_1.execSync)(`systemctl --user restart ${serviceName}`, { encoding: 'utf-8' });
438
- if (verbose) {
439
- console.log(chalk_1.default.gray(` Created: ${servicePath}`));
440
- console.log(chalk_1.default.gray(` Enabled and started: ${serviceName}`));
441
- }
442
- }
443
- /**
444
- * Verify agent is connected
445
- */
446
- async function verifyConnection(deviceInfo, authToken) {
447
- // Wait a bit for agent to start
448
- await new Promise(resolve => setTimeout(resolve, 3000));
449
- // Check device status
450
- const response = await fetch(`${RELAY_API_URL}/api/v1/relay/devices/${(await (0, state_1.getState)())?.userId}`, {
451
- headers: {
452
- 'Authorization': `Bearer ${authToken}`,
453
- },
454
- });
455
- if (!response.ok) {
456
- return false;
457
- }
458
- const data = await response.json();
459
- const device = data.devices?.find((d) => d.deviceId === deviceInfo.deviceId);
460
- return device?.online === true;
461
- }
462
- /**
463
- * Main setup command
464
- */
465
- async function setupRemote(options = {}) {
466
- const verbose = options.verbose || false;
467
- console.log('');
468
- console.log(chalk_1.default.cyan.bold(' 🔧 ekkOS Remote Setup'));
469
- console.log('');
470
- // Step 1: Check if logged in
471
- let authToken = (0, state_1.getAuthToken)();
472
- if (!authToken) {
473
- console.log(chalk_1.default.yellow(' You need to log in first.'));
474
- console.log('');
475
- await (0, init_1.init)({ ide: 'claude' });
476
- authToken = (0, state_1.getAuthToken)();
477
- if (!authToken) {
478
- console.log(chalk_1.default.red(' Failed to authenticate. Please run `ekkos init` first.'));
479
- process.exit(1);
480
- }
481
- }
482
- const state = (0, state_1.getState)();
483
- console.log(chalk_1.default.green(` ✓ Logged in as ${state?.userEmail || 'unknown'}`));
484
- // Step 2: Get or create device info
485
- const deviceInfo = getOrCreateDeviceInfo();
486
- console.log(chalk_1.default.green(` ✓ Device: ${deviceInfo.deviceName} (${deviceInfo.arch})`));
487
- // Step 3: Check if already paired
488
- const deviceFilePath = path.join(state_1.EKKOS_DIR, 'device.json');
489
- const deviceData = JSON.parse(fs.readFileSync(deviceFilePath, 'utf-8'));
490
- if (deviceData.deviceToken && !options.force) {
491
- console.log(chalk_1.default.green(' ✓ Device already paired'));
492
- }
493
- else {
494
- // Request pairing code
495
- console.log('');
496
- console.log(chalk_1.default.cyan(' Requesting pairing code...'));
497
- let pairingResponse;
498
- try {
499
- pairingResponse = await requestPairingCode(deviceInfo, authToken);
500
- }
501
- catch (err) {
502
- console.log(chalk_1.default.red(` Error: ${err.message}`));
503
- process.exit(1);
504
- }
505
- // Open browser for approval
506
- const verificationUrl = pairingResponse.verificationUrl || `${PLATFORM_URL}/verify/${pairingResponse.userCode}`;
507
- console.log('');
508
- console.log(chalk_1.default.white(` Verification code: ${chalk_1.default.bold.yellow(pairingResponse.userCode)}`));
509
- console.log(chalk_1.default.gray(` Opening browser to approve...`));
510
- console.log('');
511
- try {
512
- await (0, open_1.default)(verificationUrl);
513
- }
514
- catch {
515
- console.log(chalk_1.default.yellow(` Could not open browser. Please visit:`));
516
- console.log(chalk_1.default.cyan(` ${verificationUrl}`));
517
- }
518
- // Poll for approval
519
- console.log(chalk_1.default.gray(' Waiting for approval'));
520
- process.stdout.write(' ');
521
- try {
522
- const result = await pollForApproval(pairingResponse.deviceCode, authToken);
523
- console.log('');
524
- if (!result.approved) {
525
- console.log(chalk_1.default.red(' Pairing was denied.'));
526
- process.exit(1);
527
- }
528
- // Save device token
529
- deviceData.deviceToken = result.deviceToken;
530
- deviceData.pairedAt = new Date().toISOString();
531
- fs.writeFileSync(deviceFilePath, JSON.stringify(deviceData, null, 2));
532
- console.log(chalk_1.default.green(' ✓ Device paired'));
533
- }
534
- catch (err) {
535
- console.log('');
536
- console.log(chalk_1.default.red(` Error: ${err.message}`));
537
- process.exit(1);
538
- }
539
- }
540
- // Step 4: Install service (unless skipped)
541
- if (!options.skipService) {
542
- console.log(chalk_1.default.cyan(' Installing agent service...'));
543
- try {
544
- await installService(verbose);
545
- console.log(chalk_1.default.green(' ✓ Agent installed and running'));
546
- }
547
- catch (err) {
548
- console.log(chalk_1.default.yellow(` Warning: Could not install service: ${err.message}`));
549
- console.log(chalk_1.default.gray(' You can start the agent manually with: ekkos agent start'));
550
- }
551
- }
552
- // Step 5: Verify connection
553
- console.log(chalk_1.default.cyan(' Verifying connection...'));
554
- const connected = await verifyConnection(deviceInfo, authToken);
555
- if (connected) {
556
- console.log(chalk_1.default.green(' ✓ Connected to ekkOS cloud'));
557
- }
558
- else {
559
- console.log(chalk_1.default.yellow(' ⚠ Agent not connected yet (may take a moment)'));
560
- }
561
- // Success message
562
- console.log('');
563
- console.log(chalk_1.default.green.bold(' 🎉 Setup complete!'));
564
- console.log('');
565
- console.log(chalk_1.default.white(' Access your terminal from anywhere at:'));
566
- console.log(chalk_1.default.cyan(` ${PLATFORM_URL}/dashboard/terminal`));
567
- console.log('');
568
- console.log(chalk_1.default.gray(` Your device will appear as "${deviceInfo.deviceName}" in the device list.`));
569
- console.log(chalk_1.default.gray(' The agent runs in background and auto-starts on boot.'));
570
- console.log('');
571
- console.log(chalk_1.default.dim(' How it works:'));
572
- console.log(chalk_1.default.dim(' • Claude Code runs on THIS machine (uses your subscription)'));
573
- console.log(chalk_1.default.dim(' • ekkOS provides secure relay to access it from any browser'));
574
- console.log(chalk_1.default.dim(' • Your code/files stay local - only terminal I/O is relayed'));
575
- console.log(chalk_1.default.dim(' • ekkOS learns from every session - faster, smarter, better over time'));
576
- console.log('');
577
- console.log(chalk_1.default.gray(' Commands:'));
578
- console.log(chalk_1.default.gray(' ekkos agent status - Check agent status'));
579
- console.log(chalk_1.default.gray(' ekkos agent stop - Stop agent temporarily'));
580
- console.log(chalk_1.default.gray(' ekkos agent uninstall - Remove agent completely'));
581
- console.log('');
582
- }
@@ -1,7 +0,0 @@
1
- /**
2
- * ekkOS_synk commands — remote session sync for Claude Code
3
- *
4
- * Provides: ekkos synk auth, connect, disconnect, sessions, daemon start/stop/status, doctor
5
- */
6
- import { Command } from 'commander';
7
- export declare function registerSynkCommands(program: Command): void;