@ekkos/cli 0.2.18 → 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.
- package/README.md +57 -0
- package/dist/agent/daemon.d.ts +27 -0
- package/dist/agent/daemon.js +254 -29
- package/dist/agent/health-check.d.ts +35 -0
- package/dist/agent/health-check.js +243 -0
- package/dist/agent/pty-runner.d.ts +1 -0
- package/dist/agent/pty-runner.js +6 -1
- package/dist/capture/eviction-client.d.ts +139 -0
- package/dist/capture/eviction-client.js +454 -0
- package/dist/capture/index.d.ts +2 -0
- package/dist/capture/index.js +2 -0
- package/dist/capture/jsonl-rewriter.d.ts +96 -0
- package/dist/capture/jsonl-rewriter.js +1369 -0
- package/dist/capture/transcript-repair.d.ts +51 -0
- package/dist/capture/transcript-repair.js +319 -0
- package/dist/commands/agent.d.ts +6 -0
- package/dist/commands/agent.js +244 -0
- package/dist/commands/dashboard.d.ts +25 -0
- package/dist/commands/dashboard.js +1175 -0
- package/dist/commands/doctor.js +23 -1
- package/dist/commands/run.d.ts +5 -0
- package/dist/commands/run.js +1605 -516
- package/dist/commands/setup-remote.js +146 -37
- package/dist/commands/swarm-dashboard.d.ts +20 -0
- package/dist/commands/swarm-dashboard.js +735 -0
- package/dist/commands/swarm-setup.d.ts +10 -0
- package/dist/commands/swarm-setup.js +956 -0
- package/dist/commands/swarm.d.ts +46 -0
- package/dist/commands/swarm.js +441 -0
- package/dist/commands/test-claude.d.ts +16 -0
- package/dist/commands/test-claude.js +156 -0
- package/dist/commands/usage/blocks.d.ts +8 -0
- package/dist/commands/usage/blocks.js +60 -0
- package/dist/commands/usage/daily.d.ts +9 -0
- package/dist/commands/usage/daily.js +96 -0
- package/dist/commands/usage/dashboard.d.ts +8 -0
- package/dist/commands/usage/dashboard.js +104 -0
- package/dist/commands/usage/formatters.d.ts +41 -0
- package/dist/commands/usage/formatters.js +147 -0
- package/dist/commands/usage/index.d.ts +13 -0
- package/dist/commands/usage/index.js +87 -0
- package/dist/commands/usage/monthly.d.ts +8 -0
- package/dist/commands/usage/monthly.js +66 -0
- package/dist/commands/usage/session.d.ts +11 -0
- package/dist/commands/usage/session.js +193 -0
- package/dist/commands/usage/weekly.d.ts +9 -0
- package/dist/commands/usage/weekly.js +61 -0
- package/dist/commands/usage.d.ts +7 -0
- package/dist/commands/usage.js +214 -0
- package/dist/cron/index.d.ts +7 -0
- package/dist/cron/index.js +13 -0
- package/dist/cron/promoter.d.ts +70 -0
- package/dist/cron/promoter.js +403 -0
- package/dist/deploy/instructions.d.ts +5 -2
- package/dist/deploy/instructions.js +11 -8
- package/dist/index.js +262 -5
- package/dist/lib/tmux-scrollbar.d.ts +14 -0
- package/dist/lib/tmux-scrollbar.js +296 -0
- package/dist/lib/usage-monitor.d.ts +47 -0
- package/dist/lib/usage-monitor.js +124 -0
- package/dist/lib/usage-parser.d.ts +162 -0
- package/dist/lib/usage-parser.js +583 -0
- package/dist/restore/RestoreOrchestrator.d.ts +4 -0
- package/dist/restore/RestoreOrchestrator.js +118 -30
- package/dist/utils/log-rotate.d.ts +18 -0
- package/dist/utils/log-rotate.js +74 -0
- package/dist/utils/platform.d.ts +2 -0
- package/dist/utils/platform.js +3 -1
- package/dist/utils/session-binding.d.ts +5 -0
- package/dist/utils/session-binding.js +46 -0
- package/dist/utils/state.js +4 -0
- package/dist/utils/verify-remote-terminal.d.ts +10 -0
- package/dist/utils/verify-remote-terminal.js +415 -0
- package/package.json +9 -2
- package/templates/CLAUDE.md +135 -23
- package/templates/ekkos-manifest.json +5 -5
- package/templates/hooks/lib/contract.sh +43 -31
- package/templates/hooks/lib/count-tokens.cjs +86 -0
- package/templates/hooks/lib/ekkos-reminders.sh +98 -0
- package/templates/hooks/lib/state.sh +53 -1
- package/templates/hooks/stop.sh +150 -388
- package/templates/hooks/user-prompt-submit.sh +353 -443
- package/templates/windsurf-hooks/README.md +212 -0
- package/templates/windsurf-hooks/hooks.json +9 -2
- package/templates/windsurf-hooks/install.sh +148 -0
- package/templates/windsurf-hooks/lib/contract.sh +2 -0
- package/templates/windsurf-hooks/post-cascade-response.sh +251 -0
- package/templates/windsurf-hooks/pre-user-prompt.sh +435 -0
- package/templates/windsurf-skills/ekkos-memory/SKILL.md +219 -0
- package/templates/agents/README.md +0 -182
- package/templates/agents/code-reviewer.md +0 -166
- package/templates/agents/debug-detective.md +0 -169
- package/templates/agents/ekkOS_Vercel.md +0 -99
- package/templates/agents/extension-manager.md +0 -229
- package/templates/agents/git-companion.md +0 -185
- package/templates/agents/github-test-agent.md +0 -321
- package/templates/agents/railway-manager.md +0 -215
- package/templates/windsurf-hooks/before-submit-prompt.sh +0 -238
|
@@ -189,68 +189,177 @@ async function installService(verbose) {
|
|
|
189
189
|
async function installMacOSService(verbose) {
|
|
190
190
|
const plistName = 'dev.ekkos.agent';
|
|
191
191
|
const plistPath = path.join(os.homedir(), 'Library', 'LaunchAgents', `${plistName}.plist`);
|
|
192
|
-
// Find node path
|
|
192
|
+
// Find node path - use execPath of current process
|
|
193
193
|
const nodePath = process.execPath;
|
|
194
|
-
|
|
194
|
+
if (verbose) {
|
|
195
|
+
console.log(chalk_1.default.gray(` Node path: ${nodePath}`));
|
|
196
|
+
}
|
|
197
|
+
// Find ekkos CLI path
|
|
195
198
|
let ekkosPath;
|
|
199
|
+
let useNpx = false;
|
|
196
200
|
try {
|
|
197
201
|
ekkosPath = (0, child_process_1.execSync)('which ekkos', { encoding: 'utf-8' }).trim();
|
|
202
|
+
if (!ekkosPath)
|
|
203
|
+
throw new Error('Empty path');
|
|
198
204
|
}
|
|
199
205
|
catch {
|
|
200
206
|
// Fall back to npx
|
|
201
|
-
ekkosPath = 'npx
|
|
207
|
+
ekkosPath = 'npx';
|
|
208
|
+
useNpx = true;
|
|
202
209
|
}
|
|
203
|
-
|
|
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"?>
|
|
204
281
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
205
282
|
<plist version="1.0">
|
|
206
283
|
<dict>
|
|
207
284
|
<key>Label</key>
|
|
208
|
-
<string>${
|
|
285
|
+
<string>${escapeXml(label)}</string>
|
|
286
|
+
|
|
209
287
|
<key>ProgramArguments</key>
|
|
210
288
|
<array>
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
<string>agent</string>
|
|
215
|
-
<string>daemon</string>
|
|
216
|
-
</array>
|
|
289
|
+
${argXml} </array>
|
|
290
|
+
|
|
291
|
+
<!-- Run at system login and on demand -->
|
|
217
292
|
<key>RunAtLoad</key>
|
|
218
293
|
<true/>
|
|
294
|
+
|
|
295
|
+
<!-- Keep alive: restart immediately if it exits or crashes -->
|
|
219
296
|
<key>KeepAlive</key>
|
|
220
|
-
<
|
|
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 -->
|
|
221
307
|
<key>StandardErrorPath</key>
|
|
222
|
-
<string>${path.join(
|
|
308
|
+
<string>${escapeXml(path.join(ekkosDir, 'agent.err.log'))}</string>
|
|
309
|
+
|
|
223
310
|
<key>StandardOutPath</key>
|
|
224
|
-
<string>${path.join(
|
|
311
|
+
<string>${escapeXml(path.join(ekkosDir, 'agent.out.log'))}</string>
|
|
312
|
+
|
|
313
|
+
<!-- Environment variables -->
|
|
225
314
|
<key>EnvironmentVariables</key>
|
|
226
315
|
<dict>
|
|
227
316
|
<key>PATH</key>
|
|
228
|
-
<string
|
|
317
|
+
<string>${escapeXml(launchPath)}</string>
|
|
318
|
+
<key>NODE_ENV</key>
|
|
319
|
+
<string>production</string>
|
|
320
|
+
<key>HOME</key>
|
|
321
|
+
<string>${escapeXml(homeDir)}</string>
|
|
229
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>
|
|
230
350
|
</dict>
|
|
231
351
|
</plist>`;
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
try {
|
|
244
|
-
(0, child_process_1.execSync)(`launchctl unload "${plistPath}" 2>/dev/null`, { encoding: 'utf-8' });
|
|
245
|
-
}
|
|
246
|
-
catch {
|
|
247
|
-
// Ignore if not loaded
|
|
248
|
-
}
|
|
249
|
-
// Load the service
|
|
250
|
-
(0, child_process_1.execSync)(`launchctl load "${plistPath}"`, { encoding: 'utf-8' });
|
|
251
|
-
if (verbose) {
|
|
252
|
-
console.log(chalk_1.default.gray(` Loaded: ${plistName}`));
|
|
253
|
-
}
|
|
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, ''');
|
|
254
363
|
}
|
|
255
364
|
/**
|
|
256
365
|
* Install Windows service
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ekkos swarm dashboard
|
|
3
|
+
*
|
|
4
|
+
* Live TUI dashboard for monitoring a swarm of parallel Claude Code workers.
|
|
5
|
+
* Uses blessed-contrib for rich terminal widgets.
|
|
6
|
+
*
|
|
7
|
+
* Layout:
|
|
8
|
+
* Header: Swarm name + worker count + total cost + duration
|
|
9
|
+
* Workers: Per-worker status cards (context bar, cost, turns, model)
|
|
10
|
+
* Chart: Combined token usage from all workers
|
|
11
|
+
* Table: All turns from all workers (with worker column)
|
|
12
|
+
* Usage: Anthropic rate limit windows
|
|
13
|
+
* Footer: Aggregate totals + routing breakdown + keybindings
|
|
14
|
+
*
|
|
15
|
+
* Usage:
|
|
16
|
+
* ekkos swarm dashboard Auto-detect active swarm
|
|
17
|
+
* ekkos swarm dashboard --launch-ts 123 Use specific launch timestamp
|
|
18
|
+
*/
|
|
19
|
+
import { Command } from 'commander';
|
|
20
|
+
export declare const swarmDashboardCommand: Command;
|