@ekkos/cli 1.3.1 → 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.
- package/dist/capture/jsonl-rewriter.d.ts +1 -1
- package/dist/capture/jsonl-rewriter.js +3 -3
- package/dist/capture/transcript-repair.d.ts +2 -2
- package/dist/capture/transcript-repair.js +2 -2
- package/dist/commands/claw.d.ts +13 -0
- package/dist/commands/claw.js +253 -0
- package/dist/commands/dashboard.js +742 -118
- package/dist/commands/doctor.d.ts +3 -3
- package/dist/commands/doctor.js +6 -79
- package/dist/commands/gemini.d.ts +19 -0
- package/dist/commands/gemini.js +193 -0
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.js +56 -41
- package/dist/commands/run.d.ts +0 -1
- package/dist/commands/run.js +288 -263
- package/dist/commands/scan.d.ts +21 -0
- package/dist/commands/scan.js +386 -0
- package/dist/commands/status.d.ts +4 -1
- package/dist/commands/status.js +165 -27
- package/dist/commands/swarm-dashboard.js +156 -28
- package/dist/commands/swarm.d.ts +1 -1
- package/dist/commands/swarm.js +1 -1
- package/dist/commands/test-claude.d.ts +2 -2
- package/dist/commands/test-claude.js +3 -3
- package/dist/deploy/index.d.ts +0 -2
- package/dist/deploy/index.js +0 -2
- package/dist/deploy/settings.d.ts +6 -5
- package/dist/deploy/settings.js +64 -16
- package/dist/deploy/skills.js +1 -2
- package/dist/index.js +86 -96
- package/dist/lib/usage-parser.d.ts +1 -1
- package/dist/lib/usage-parser.js +9 -6
- package/dist/local/index.d.ts +14 -0
- package/dist/local/index.js +28 -0
- package/dist/local/local-embeddings.d.ts +49 -0
- package/dist/local/local-embeddings.js +232 -0
- package/dist/local/offline-fallback.d.ts +44 -0
- package/dist/local/offline-fallback.js +159 -0
- package/dist/local/sqlite-store.d.ts +126 -0
- package/dist/local/sqlite-store.js +393 -0
- package/dist/local/sync-engine.d.ts +42 -0
- package/dist/local/sync-engine.js +223 -0
- package/dist/utils/platform.d.ts +5 -1
- package/dist/utils/platform.js +24 -4
- package/dist/utils/proxy-url.d.ts +21 -0
- package/dist/utils/proxy-url.js +34 -0
- package/dist/utils/state.d.ts +1 -1
- package/dist/utils/state.js +11 -3
- package/dist/utils/templates.js +1 -1
- package/package.json +11 -4
- package/templates/CLAUDE.md +49 -107
- package/dist/agent/daemon.d.ts +0 -130
- package/dist/agent/daemon.js +0 -606
- package/dist/agent/health-check.d.ts +0 -35
- package/dist/agent/health-check.js +0 -243
- package/dist/agent/pty-runner.d.ts +0 -53
- package/dist/agent/pty-runner.js +0 -190
- package/dist/commands/agent.d.ts +0 -50
- package/dist/commands/agent.js +0 -544
- package/dist/commands/setup-remote.d.ts +0 -20
- package/dist/commands/setup-remote.js +0 -582
- package/dist/utils/verify-remote-terminal.d.ts +0 -10
- package/dist/utils/verify-remote-terminal.js +0 -415
- package/templates/README.md +0 -378
- package/templates/claude-plugins/PHASE2_COMPLETION.md +0 -346
- package/templates/claude-plugins/PLUGIN_PROPOSALS.md +0 -1776
- package/templates/claude-plugins/README.md +0 -587
- package/templates/claude-plugins/agents/code-reviewer.json +0 -14
- package/templates/claude-plugins/agents/debug-detective.json +0 -15
- package/templates/claude-plugins/agents/git-companion.json +0 -14
- package/templates/claude-plugins/blog-manager/.claude-plugin/plugin.json +0 -8
- package/templates/claude-plugins/blog-manager/commands/blog.md +0 -691
- package/templates/claude-plugins/golden-loop-monitor/.claude-plugin/plugin.json +0 -8
- package/templates/claude-plugins/golden-loop-monitor/commands/loop-status.md +0 -434
- package/templates/claude-plugins/learning-tracker/.claude-plugin/plugin.json +0 -8
- package/templates/claude-plugins/learning-tracker/commands/my-patterns.md +0 -282
- package/templates/claude-plugins/memory-lens/.claude-plugin/plugin.json +0 -8
- package/templates/claude-plugins/memory-lens/commands/memory-search.md +0 -181
- package/templates/claude-plugins/pattern-coach/.claude-plugin/plugin.json +0 -8
- package/templates/claude-plugins/pattern-coach/commands/forge.md +0 -365
- package/templates/claude-plugins/project-schema-validator/.claude-plugin/plugin.json +0 -8
- package/templates/claude-plugins/project-schema-validator/commands/validate-schema.md +0 -582
- package/templates/claude-plugins-admin/AGENT_TEAM_PROPOSALS.md +0 -819
- package/templates/claude-plugins-admin/README.md +0 -446
- package/templates/claude-plugins-admin/autonomous-admin-agent/.claude-plugin/plugin.json +0 -8
- package/templates/claude-plugins-admin/autonomous-admin-agent/commands/agent.md +0 -595
- package/templates/claude-plugins-admin/backend-agent/.claude-plugin/plugin.json +0 -8
- package/templates/claude-plugins-admin/backend-agent/commands/backend.md +0 -798
- package/templates/claude-plugins-admin/deploy-guardian/.claude-plugin/plugin.json +0 -8
- package/templates/claude-plugins-admin/deploy-guardian/commands/deploy.md +0 -554
- package/templates/claude-plugins-admin/frontend-agent/.claude-plugin/plugin.json +0 -8
- package/templates/claude-plugins-admin/frontend-agent/commands/frontend.md +0 -881
- package/templates/claude-plugins-admin/mcp-server-manager/.claude-plugin/plugin.json +0 -8
- package/templates/claude-plugins-admin/mcp-server-manager/commands/mcp.md +0 -85
- package/templates/claude-plugins-admin/memory-system-monitor/.claude-plugin/plugin.json +0 -8
- package/templates/claude-plugins-admin/memory-system-monitor/commands/memory-health.md +0 -569
- package/templates/claude-plugins-admin/qa-agent/.claude-plugin/plugin.json +0 -8
- package/templates/claude-plugins-admin/qa-agent/commands/qa.md +0 -863
- package/templates/claude-plugins-admin/tech-lead-agent/.claude-plugin/plugin.json +0 -8
- package/templates/claude-plugins-admin/tech-lead-agent/commands/lead.md +0 -732
- package/templates/commands/continue.md +0 -47
- package/templates/cursor-rules/ekkos-memory.md +0 -127
- package/templates/ekkos-manifest.json +0 -223
- package/templates/helpers/json-parse.cjs +0 -101
- package/templates/hooks-node/lib/state.js +0 -187
- package/templates/hooks-node/stop.js +0 -416
- package/templates/hooks-node/user-prompt-submit.js +0 -337
- package/templates/plan-template.md +0 -306
- package/templates/rules/00-hooks-contract.mdc +0 -89
- package/templates/rules/30-ekkos-core.mdc +0 -188
- package/templates/rules/31-ekkos-messages.mdc +0 -78
- package/templates/shared/hooks-enabled.json +0 -22
- package/templates/shared/session-words.json +0 -45
- package/templates/skills/ekkOS_Deep_Recall/Skill.md +0 -282
- package/templates/skills/ekkOS_Learn/Skill.md +0 -265
- package/templates/skills/ekkOS_Memory_First/Skill.md +0 -206
- package/templates/skills/ekkOS_Plan_Assist/Skill.md +0 -302
- package/templates/skills/ekkOS_Preferences/Skill.md +0 -247
- package/templates/skills/ekkOS_Reflect/Skill.md +0 -257
- package/templates/skills/ekkOS_Safety/Skill.md +0 -265
- package/templates/skills/ekkOS_Schema/Skill.md +0 -251
- package/templates/skills/ekkOS_Summary/Skill.md +0 -257
- package/templates/spec-template.md +0 -159
- package/templates/windsurf-rules/ekkos-memory.md +0 -127
- package/templates/windsurf-skills/README.md +0 -58
- package/templates/windsurf-skills/ekkos-continue/SKILL.md +0 -81
- package/templates/windsurf-skills/ekkos-golden-loop/SKILL.md +0 -225
- package/templates/windsurf-skills/ekkos-insights/SKILL.md +0 -138
- package/templates/windsurf-skills/ekkos-recall/SKILL.md +0 -96
- package/templates/windsurf-skills/ekkos-safety/SKILL.md +0 -89
- 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, '&')
|
|
359
|
-
.replace(/</g, '<')
|
|
360
|
-
.replace(/>/g, '>')
|
|
361
|
-
.replace(/"/g, '"')
|
|
362
|
-
.replace(/'/g, ''');
|
|
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,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Remote Terminal Verification Script
|
|
3
|
-
*
|
|
4
|
-
* Systematically verifies that all remote terminal components are properly configured
|
|
5
|
-
* and working correctly. Run this after setup-remote to validate the installation.
|
|
6
|
-
*/
|
|
7
|
-
/**
|
|
8
|
-
* Run all verification tests
|
|
9
|
-
*/
|
|
10
|
-
export declare function verifyRemoteTerminal(): Promise<void>;
|