@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,101 +0,0 @@
1
- "use strict";
2
- /**
3
- * HTTP client for communicating with a running synk daemon
4
- */
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.notifyDaemonSessionStarted = notifyDaemonSessionStarted;
7
- exports.listDaemonSessions = listDaemonSessions;
8
- exports.stopDaemonSession = stopDaemonSession;
9
- exports.spawnDaemonSession = spawnDaemonSession;
10
- exports.stopDaemonHttp = stopDaemonHttp;
11
- exports.checkIfDaemonRunning = checkIfDaemonRunning;
12
- exports.stopDaemon = stopDaemon;
13
- const persistence_1 = require("../persistence");
14
- async function daemonPost(path, body) {
15
- const state = await (0, persistence_1.readDaemonState)();
16
- if (!state?.httpPort) {
17
- return { error: 'No daemon running, no state file found' };
18
- }
19
- try {
20
- process.kill(state.pid, 0);
21
- }
22
- catch {
23
- return { error: 'Daemon is not running, state file is stale' };
24
- }
25
- try {
26
- const timeout = process.env.SYNK_DAEMON_HTTP_TIMEOUT
27
- ? parseInt(process.env.SYNK_DAEMON_HTTP_TIMEOUT)
28
- : 10000;
29
- const response = await fetch(`http://127.0.0.1:${state.httpPort}${path}`, {
30
- method: 'POST',
31
- headers: { 'Content-Type': 'application/json' },
32
- body: JSON.stringify(body || {}),
33
- signal: AbortSignal.timeout(timeout),
34
- });
35
- if (!response.ok) {
36
- return { error: `Request failed: ${path}, HTTP ${response.status}` };
37
- }
38
- return await response.json();
39
- }
40
- catch (error) {
41
- return { error: `Request failed: ${path}, ${error instanceof Error ? error.message : 'Unknown error'}` };
42
- }
43
- }
44
- async function notifyDaemonSessionStarted(sessionId, metadata) {
45
- return await daemonPost('/session-started', { sessionId, metadata });
46
- }
47
- async function listDaemonSessions() {
48
- const result = await daemonPost('/list');
49
- return result.children || [];
50
- }
51
- async function stopDaemonSession(sessionId) {
52
- const result = await daemonPost('/stop-session', { sessionId });
53
- return result.success || false;
54
- }
55
- async function spawnDaemonSession(directory, sessionId) {
56
- return await daemonPost('/spawn-session', { directory, sessionId });
57
- }
58
- async function stopDaemonHttp() {
59
- await daemonPost('/stop');
60
- }
61
- async function checkIfDaemonRunning() {
62
- const state = await (0, persistence_1.readDaemonState)();
63
- if (!state)
64
- return false;
65
- try {
66
- process.kill(state.pid, 0);
67
- return true;
68
- }
69
- catch {
70
- await (0, persistence_1.clearDaemonState)();
71
- return false;
72
- }
73
- }
74
- async function stopDaemon() {
75
- try {
76
- const state = await (0, persistence_1.readDaemonState)();
77
- if (!state)
78
- return;
79
- // Try HTTP graceful stop
80
- try {
81
- await stopDaemonHttp();
82
- const start = Date.now();
83
- while (Date.now() - start < 2000) {
84
- try {
85
- process.kill(state.pid, 0);
86
- await new Promise(resolve => setTimeout(resolve, 100));
87
- }
88
- catch {
89
- return; // Process is dead
90
- }
91
- }
92
- }
93
- catch { }
94
- // Force kill
95
- try {
96
- process.kill(state.pid, 'SIGKILL');
97
- }
98
- catch { }
99
- }
100
- catch { }
101
- }
@@ -1,24 +0,0 @@
1
- /**
2
- * Local HTTP control server for synk daemon
3
- * Listens on 127.0.0.1 only — provides endpoints for session management and shutdown
4
- */
5
- import type { Metadata } from '../types';
6
- export interface TrackedSession {
7
- pid: number;
8
- startedBy: string;
9
- synkSessionId?: string;
10
- startedAt: number;
11
- }
12
- export declare function startDaemonControlServer(opts: {
13
- getChildren: () => TrackedSession[];
14
- stopSession: (sessionId: string) => boolean;
15
- spawnSession: (options: {
16
- directory: string;
17
- sessionId?: string;
18
- }) => Promise<any>;
19
- requestShutdown: () => void;
20
- onSessionWebhook: (sessionId: string, metadata: Metadata) => void;
21
- }): Promise<{
22
- port: number;
23
- stop: () => Promise<void>;
24
- }>;
@@ -1,91 +0,0 @@
1
- "use strict";
2
- /**
3
- * Local HTTP control server for synk daemon
4
- * Listens on 127.0.0.1 only — provides endpoints for session management and shutdown
5
- */
6
- var __importDefault = (this && this.__importDefault) || function (mod) {
7
- return (mod && mod.__esModule) ? mod : { "default": mod };
8
- };
9
- Object.defineProperty(exports, "__esModule", { value: true });
10
- exports.startDaemonControlServer = startDaemonControlServer;
11
- const node_http_1 = __importDefault(require("node:http"));
12
- function startDaemonControlServer(opts) {
13
- return new Promise((resolve, reject) => {
14
- const server = node_http_1.default.createServer(async (req, res) => {
15
- if (req.method !== 'POST') {
16
- res.writeHead(405, { 'Content-Type': 'application/json' });
17
- res.end(JSON.stringify({ error: 'Method not allowed' }));
18
- return;
19
- }
20
- let body = '';
21
- req.on('data', (chunk) => { body += chunk; });
22
- req.on('end', async () => {
23
- let parsed = {};
24
- try {
25
- parsed = body ? JSON.parse(body) : {};
26
- }
27
- catch { }
28
- try {
29
- switch (req.url) {
30
- case '/session-started': {
31
- const { sessionId, metadata } = parsed;
32
- if (sessionId)
33
- opts.onSessionWebhook(sessionId, metadata);
34
- res.writeHead(200, { 'Content-Type': 'application/json' });
35
- res.end(JSON.stringify({ status: 'ok' }));
36
- break;
37
- }
38
- case '/list': {
39
- const children = opts.getChildren()
40
- .filter(c => c.synkSessionId)
41
- .map(c => ({ startedBy: c.startedBy, synkSessionId: c.synkSessionId, pid: c.pid }));
42
- res.writeHead(200, { 'Content-Type': 'application/json' });
43
- res.end(JSON.stringify({ children }));
44
- break;
45
- }
46
- case '/stop-session': {
47
- const success = opts.stopSession(parsed.sessionId || '');
48
- res.writeHead(200, { 'Content-Type': 'application/json' });
49
- res.end(JSON.stringify({ success }));
50
- break;
51
- }
52
- case '/spawn-session': {
53
- const result = await opts.spawnSession({
54
- directory: parsed.directory || '.',
55
- sessionId: parsed.sessionId,
56
- });
57
- res.writeHead(200, { 'Content-Type': 'application/json' });
58
- res.end(JSON.stringify(result));
59
- break;
60
- }
61
- case '/stop': {
62
- res.writeHead(200, { 'Content-Type': 'application/json' });
63
- res.end(JSON.stringify({ status: 'stopping' }));
64
- setTimeout(() => opts.requestShutdown(), 50);
65
- break;
66
- }
67
- default: {
68
- res.writeHead(404, { 'Content-Type': 'application/json' });
69
- res.end(JSON.stringify({ error: 'Not found' }));
70
- }
71
- }
72
- }
73
- catch (error) {
74
- if (!res.headersSent) {
75
- res.writeHead(500, { 'Content-Type': 'application/json' });
76
- }
77
- res.end(JSON.stringify({ error: error instanceof Error ? error.message : 'Internal error' }));
78
- }
79
- });
80
- });
81
- server.listen(0, '127.0.0.1', () => {
82
- const addr = server.address();
83
- const port = typeof addr === 'object' && addr ? addr.port : 0;
84
- resolve({
85
- port,
86
- stop: () => new Promise((res) => server.close(() => res())),
87
- });
88
- });
89
- server.on('error', reject);
90
- });
91
- }
@@ -1,14 +0,0 @@
1
- /**
2
- * Synk daemon — background process for session management and remote control
3
- *
4
- * Lifecycle:
5
- * 1. Acquire lock file (prevent multiple daemons)
6
- * 2. Authenticate with synk-server
7
- * 3. Start local HTTP control server
8
- * 4. Register machine via REST API
9
- * 5. Connect MachineClient WebSocket
10
- * 6. Heartbeat loop
11
- * 7. Await shutdown signal
12
- * 8. Cleanup
13
- */
14
- export declare function startDaemon(): Promise<void>;
@@ -1,338 +0,0 @@
1
- "use strict";
2
- /**
3
- * Synk daemon — background process for session management and remote control
4
- *
5
- * Lifecycle:
6
- * 1. Acquire lock file (prevent multiple daemons)
7
- * 2. Authenticate with synk-server
8
- * 3. Start local HTTP control server
9
- * 4. Register machine via REST API
10
- * 5. Connect MachineClient WebSocket
11
- * 6. Heartbeat loop
12
- * 7. Await shutdown signal
13
- * 8. Cleanup
14
- */
15
- Object.defineProperty(exports, "__esModule", { value: true });
16
- exports.startDaemon = startDaemon;
17
- const node_os_1 = require("node:os");
18
- const node_path_1 = require("node:path");
19
- const node_fs_1 = require("node:fs");
20
- const node_child_process_1 = require("node:child_process");
21
- const node_crypto_1 = require("node:crypto");
22
- const config_1 = require("../config");
23
- const persistence_1 = require("../persistence");
24
- const api_1 = require("../api");
25
- const machine_client_1 = require("../machine-client");
26
- const control_server_1 = require("./control-server");
27
- const control_client_1 = require("./control-client");
28
- const proxy_url_1 = require("../../utils/proxy-url");
29
- const state_1 = require("../../utils/state");
30
- const HEARTBEAT_INTERVAL = parseInt(process.env.SYNK_DAEMON_HEARTBEAT_INTERVAL || '60000');
31
- let logStream = null;
32
- function getCliVersion() {
33
- try {
34
- const pkgPath = (0, node_path_1.resolve)(__dirname, '../../../package.json');
35
- const pkg = JSON.parse((0, node_fs_1.readFileSync)(pkgPath, 'utf-8'));
36
- return pkg.version || '0.0.0';
37
- }
38
- catch {
39
- return '0.0.0';
40
- }
41
- }
42
- function daemonLog(message, ...args) {
43
- const line = `[${new Date().toISOString()}] ${message} ${args.map(a => typeof a === 'string' ? a : JSON.stringify(a)).join(' ')}`;
44
- if (logStream)
45
- logStream.write(line + '\n');
46
- if (!config_1.synkConfig.isDaemonProcess)
47
- console.log(line);
48
- }
49
- async function startDaemon() {
50
- config_1.synkConfig.ensureDirectories();
51
- const cliVersion = getCliVersion();
52
- // Setup log file
53
- const logPath = (0, node_path_1.join)(config_1.synkConfig.logsDir, `${new Date().toISOString().replace(/[:.]/g, '-')}-daemon.log`);
54
- if (!(0, node_fs_1.existsSync)(config_1.synkConfig.logsDir))
55
- (0, node_fs_1.mkdirSync)(config_1.synkConfig.logsDir, { recursive: true });
56
- logStream = (0, node_fs_1.createWriteStream)(logPath, { flags: 'a' });
57
- daemonLog(`Starting synk daemon v${cliVersion}`);
58
- // Check if daemon already running
59
- const existingState = await (0, persistence_1.readDaemonState)();
60
- if (existingState) {
61
- try {
62
- process.kill(existingState.pid, 0);
63
- daemonLog('Daemon already running, stopping old instance');
64
- await (0, control_client_1.stopDaemon)();
65
- }
66
- catch {
67
- daemonLog('Stale daemon state, cleaning up');
68
- await (0, persistence_1.clearDaemonState)();
69
- }
70
- }
71
- // Acquire lock
72
- const lockHandle = await (0, persistence_1.acquireDaemonLock)();
73
- if (!lockHandle) {
74
- daemonLog('Failed to acquire daemon lock — another daemon may be running');
75
- process.exit(1);
76
- }
77
- // Authenticate
78
- const credentials = await (0, persistence_1.readCredentials)();
79
- if (!credentials) {
80
- daemonLog('No credentials found. Run "ekkos synk auth" first.');
81
- await (0, persistence_1.releaseDaemonLock)(lockHandle);
82
- process.exit(1);
83
- }
84
- // Get or create machine ID
85
- const settings = await (0, persistence_1.readSettings)();
86
- let machineId = settings.machineId;
87
- if (!machineId) {
88
- machineId = (0, node_crypto_1.randomUUID)();
89
- await (0, persistence_1.updateSettings)(s => ({ ...s, machineId }));
90
- daemonLog('Generated new machine ID:', machineId);
91
- }
92
- // Session tracking — keyed by sessionId (not PID) to avoid overwrite bugs
93
- const trackedSessions = new Map();
94
- let sessionCounter = 0;
95
- // Shutdown handling
96
- let shutdownRequested = false;
97
- let resolveShutdown;
98
- const shutdownPromise = new Promise(resolve => { resolveShutdown = resolve; });
99
- const requestShutdown = () => {
100
- if (shutdownRequested)
101
- return;
102
- shutdownRequested = true;
103
- daemonLog('Shutdown requested');
104
- resolveShutdown();
105
- };
106
- process.on('SIGINT', requestShutdown);
107
- process.on('SIGTERM', requestShutdown);
108
- process.on('uncaughtException', (error) => {
109
- daemonLog('Uncaught exception:', error);
110
- requestShutdown();
111
- });
112
- // --- Spawn session implementation ---
113
- const spawnSession = async (options) => {
114
- const { directory } = options;
115
- daemonLog('Spawning session in:', directory);
116
- if (!(0, node_fs_1.existsSync)(directory)) {
117
- return { type: 'error', errorMessage: `Directory does not exist: ${directory}` };
118
- }
119
- // Generate session identity
120
- const sessionId = options.sessionId || (0, node_crypto_1.randomUUID)();
121
- const sessionName = (0, state_1.uuidToWords)(sessionId);
122
- // Build proxy env vars (unless disabled)
123
- const proxyEnv = {};
124
- if (process.env.SYNK_DISABLE_PROXY !== '1') {
125
- const ekkosConfig = (0, state_1.getConfig)();
126
- let userId = ekkosConfig?.userId || 'anonymous';
127
- if (userId === 'anonymous') {
128
- const authToken = (0, state_1.getAuthToken)();
129
- if (authToken?.startsWith('ekk_')) {
130
- const parts = authToken.split('_');
131
- if (parts.length >= 2)
132
- userId = parts[1];
133
- }
134
- }
135
- proxyEnv.ANTHROPIC_BASE_URL = (0, proxy_url_1.buildProxyUrl)(userId, sessionName, directory, sessionId);
136
- proxyEnv.EKKOS_PROXY_MODE = '1';
137
- proxyEnv.EKKOS_ULTRA_MINIMAL = '1';
138
- }
139
- // Merge env vars: process.env + options.environmentVariables + proxy (highest priority)
140
- const childEnv = {
141
- ...process.env,
142
- ...(options.environmentVariables || {}),
143
- ...proxyEnv,
144
- };
145
- try {
146
- // Create log file for spawned session
147
- const sessionLogPath = (0, node_path_1.join)(config_1.synkConfig.logsDir, `${new Date().toISOString().replace(/[:.]/g, '-')}-session-${sessionName}.log`);
148
- const sessionLogFd = (0, node_fs_1.createWriteStream)(sessionLogPath, { flags: 'a' });
149
- const child = (0, node_child_process_1.spawn)('ekkos', ['run', '--started-by', 'daemon'], {
150
- cwd: directory,
151
- env: childEnv,
152
- detached: true,
153
- stdio: ['ignore', sessionLogFd, sessionLogFd],
154
- });
155
- child.unref();
156
- const trackKey = `spawn-${++sessionCounter}`;
157
- const tracked = {
158
- pid: child.pid || 0,
159
- startedBy: 'daemon',
160
- startedAt: Date.now(),
161
- };
162
- trackedSessions.set(trackKey, tracked);
163
- // Wait for session to report itself via webhook (max 15s)
164
- const webhookSessionId = await new Promise((resolve) => {
165
- const timeout = setTimeout(() => resolve(null), 15000);
166
- const checkInterval = setInterval(() => {
167
- if (tracked.synkSessionId) {
168
- clearInterval(checkInterval);
169
- clearTimeout(timeout);
170
- resolve(tracked.synkSessionId);
171
- }
172
- }, 200);
173
- });
174
- if (webhookSessionId) {
175
- // Re-key tracked session by its actual session ID
176
- trackedSessions.delete(trackKey);
177
- trackedSessions.set(webhookSessionId, tracked);
178
- daemonLog('Session spawned and registered:', webhookSessionId);
179
- return { type: 'success', sessionId: webhookSessionId };
180
- }
181
- else {
182
- daemonLog('Session spawned but webhook timeout — PID:', child.pid);
183
- return { type: 'success', sessionId: `pending-${child.pid}` };
184
- }
185
- }
186
- catch (error) {
187
- daemonLog('Failed to spawn session:', error);
188
- return { type: 'error', errorMessage: error instanceof Error ? error.message : 'Failed to spawn session' };
189
- }
190
- };
191
- // Start HTTP control server
192
- const controlServer = await (0, control_server_1.startDaemonControlServer)({
193
- getChildren: () => Array.from(trackedSessions.values()),
194
- stopSession: (sessionId) => {
195
- for (const [key, session] of trackedSessions) {
196
- if (session.synkSessionId === sessionId || key === sessionId) {
197
- try {
198
- process.kill(session.pid, 'SIGTERM');
199
- }
200
- catch { }
201
- return true;
202
- }
203
- }
204
- return false;
205
- },
206
- spawnSession: async (options) => {
207
- const result = await spawnSession(options);
208
- if (result.type === 'success')
209
- return { success: true, sessionId: result.sessionId };
210
- return { success: false, error: result.errorMessage };
211
- },
212
- requestShutdown,
213
- onSessionWebhook: (sessionId, metadata) => {
214
- daemonLog('Session webhook:', sessionId);
215
- // Find tracked session by PID from metadata
216
- const hostPid = metadata?.hostPid;
217
- let matched = false;
218
- if (hostPid) {
219
- for (const [, session] of trackedSessions) {
220
- if (session.pid === hostPid && !session.synkSessionId) {
221
- session.synkSessionId = sessionId;
222
- matched = true;
223
- break;
224
- }
225
- }
226
- }
227
- if (!matched) {
228
- // Terminal-spawned session reporting itself
229
- trackedSessions.set(sessionId, {
230
- pid: hostPid || process.pid,
231
- startedBy: 'terminal',
232
- synkSessionId: sessionId,
233
- startedAt: Date.now(),
234
- });
235
- }
236
- },
237
- });
238
- // Write daemon state
239
- (0, persistence_1.writeDaemonState)({
240
- pid: process.pid,
241
- httpPort: controlServer.port,
242
- startTime: new Date().toLocaleString(),
243
- startedWithCliVersion: cliVersion,
244
- daemonLogPath: logPath,
245
- });
246
- daemonLog(`Control server on port ${controlServer.port}`);
247
- // Register machine with synk-server via REST
248
- const machineMetadata = {
249
- host: (0, node_os_1.hostname)(),
250
- platform: (0, node_os_1.platform)(),
251
- synkCliVersion: cliVersion,
252
- homeDir: (0, node_os_1.homedir)(),
253
- synkHomeDir: config_1.synkConfig.synkHomeDir,
254
- };
255
- const initialDaemonState = {
256
- status: 'starting',
257
- pid: process.pid,
258
- httpPort: controlServer.port,
259
- startedAt: Date.now(),
260
- };
261
- const apiClient = await api_1.ApiClient.create(credentials);
262
- const machine = await apiClient.getOrCreateMachine({
263
- machineId,
264
- metadata: machineMetadata,
265
- daemonState: initialDaemonState,
266
- });
267
- daemonLog('Machine registered:', machine.id);
268
- // Connect WebSocket via MachineClient
269
- const machineClient = new machine_client_1.MachineClient(credentials.token, machine, daemonLog);
270
- machineClient.setRPCHandlers({
271
- spawnSession,
272
- stopSession: (sessionId) => {
273
- for (const [key, session] of trackedSessions) {
274
- if (session.synkSessionId === sessionId || key === sessionId) {
275
- try {
276
- process.kill(session.pid, 'SIGTERM');
277
- }
278
- catch { }
279
- return true;
280
- }
281
- }
282
- return false;
283
- },
284
- requestShutdown,
285
- });
286
- machineClient.connect();
287
- // Heartbeat loop
288
- const heartbeatInterval = setInterval(async () => {
289
- if (shutdownRequested)
290
- return;
291
- // Update last heartbeat
292
- try {
293
- const state = await (0, persistence_1.readDaemonState)();
294
- if (state) {
295
- (0, persistence_1.writeDaemonState)({ ...state, lastHeartbeat: new Date().toLocaleString() });
296
- }
297
- }
298
- catch { }
299
- // Prune dead sessions
300
- for (const [key, session] of trackedSessions) {
301
- if (session.pid === 0 || session.pid === process.pid)
302
- continue;
303
- try {
304
- process.kill(session.pid, 0);
305
- }
306
- catch {
307
- daemonLog('Pruning dead session:', session.synkSessionId || key);
308
- trackedSessions.delete(key);
309
- }
310
- }
311
- }, HEARTBEAT_INTERVAL);
312
- daemonLog('Daemon ready. Waiting for shutdown signal...');
313
- // Wait for shutdown
314
- await shutdownPromise;
315
- daemonLog('Shutting down...');
316
- // Cleanup
317
- clearInterval(heartbeatInterval);
318
- // Update daemon state on server via MachineClient
319
- try {
320
- await machineClient.updateDaemonState(() => ({
321
- status: 'shutting-down',
322
- shutdownRequestedAt: Date.now(),
323
- }));
324
- }
325
- catch { }
326
- // Close WebSocket
327
- machineClient.shutdown();
328
- // Stop control server
329
- await controlServer.stop();
330
- // Clear daemon state file
331
- await (0, persistence_1.clearDaemonState)();
332
- // Release lock
333
- await (0, persistence_1.releaseDaemonLock)(lockHandle);
334
- if (logStream)
335
- logStream.end();
336
- daemonLog('Daemon stopped cleanly');
337
- process.exit(0);
338
- }
@@ -1,17 +0,0 @@
1
- export declare function encodeBase64(buffer: Uint8Array, variant?: 'base64' | 'base64url'): string;
2
- export declare function encodeBase64Url(buffer: Uint8Array): string;
3
- export declare function decodeBase64(base64: string, variant?: 'base64' | 'base64url'): Uint8Array;
4
- export declare function getRandomBytes(size: number): Uint8Array;
5
- export declare function libsodiumPublicKeyFromSecretKey(seed: Uint8Array): Uint8Array;
6
- export declare function libsodiumEncryptForPublicKey(data: Uint8Array, recipientPublicKey: Uint8Array): Uint8Array;
7
- export declare function encryptLegacy(data: any, secret: Uint8Array): Uint8Array;
8
- export declare function decryptLegacy(data: Uint8Array, secret: Uint8Array): any | null;
9
- export declare function encryptWithDataKey(data: any, dataKey: Uint8Array): Uint8Array;
10
- export declare function decryptWithDataKey(bundle: Uint8Array, dataKey: Uint8Array): any | null;
11
- export declare function encrypt(key: Uint8Array, variant: 'legacy' | 'dataKey', data: any): Uint8Array;
12
- export declare function decrypt(key: Uint8Array, variant: 'legacy' | 'dataKey', data: Uint8Array): any | null;
13
- export declare function authChallenge(secret: Uint8Array): {
14
- challenge: Uint8Array;
15
- publicKey: Uint8Array;
16
- signature: Uint8Array;
17
- };