@orkify/cli 1.0.0-beta.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 (203) hide show
  1. package/LICENSE +191 -0
  2. package/README.md +1701 -0
  3. package/bin/orkify +3 -0
  4. package/boot/systemd/orkify@.service +30 -0
  5. package/dist/agent-name.d.ts +4 -0
  6. package/dist/agent-name.js +42 -0
  7. package/dist/alerts/AlertEvaluator.d.ts +14 -0
  8. package/dist/alerts/AlertEvaluator.js +135 -0
  9. package/dist/cli/commands/autostart.d.ts +3 -0
  10. package/dist/cli/commands/autostart.js +11 -0
  11. package/dist/cli/commands/crash-test.d.ts +3 -0
  12. package/dist/cli/commands/crash-test.js +17 -0
  13. package/dist/cli/commands/daemon-reload.d.ts +3 -0
  14. package/dist/cli/commands/daemon-reload.js +72 -0
  15. package/dist/cli/commands/delete.d.ts +3 -0
  16. package/dist/cli/commands/delete.js +37 -0
  17. package/dist/cli/commands/deploy.d.ts +6 -0
  18. package/dist/cli/commands/deploy.js +266 -0
  19. package/dist/cli/commands/down.d.ts +3 -0
  20. package/dist/cli/commands/down.js +36 -0
  21. package/dist/cli/commands/flush.d.ts +3 -0
  22. package/dist/cli/commands/flush.js +28 -0
  23. package/dist/cli/commands/kill.d.ts +3 -0
  24. package/dist/cli/commands/kill.js +35 -0
  25. package/dist/cli/commands/list.d.ts +14 -0
  26. package/dist/cli/commands/list.js +361 -0
  27. package/dist/cli/commands/logs.d.ts +3 -0
  28. package/dist/cli/commands/logs.js +107 -0
  29. package/dist/cli/commands/mcp.d.ts +3 -0
  30. package/dist/cli/commands/mcp.js +151 -0
  31. package/dist/cli/commands/reload.d.ts +3 -0
  32. package/dist/cli/commands/reload.js +54 -0
  33. package/dist/cli/commands/restart.d.ts +3 -0
  34. package/dist/cli/commands/restart.js +43 -0
  35. package/dist/cli/commands/restore.d.ts +3 -0
  36. package/dist/cli/commands/restore.js +88 -0
  37. package/dist/cli/commands/run.d.ts +8 -0
  38. package/dist/cli/commands/run.js +212 -0
  39. package/dist/cli/commands/snap.d.ts +3 -0
  40. package/dist/cli/commands/snap.js +30 -0
  41. package/dist/cli/commands/up.d.ts +3 -0
  42. package/dist/cli/commands/up.js +125 -0
  43. package/dist/cli/crash-recovery.d.ts +2 -0
  44. package/dist/cli/crash-recovery.js +67 -0
  45. package/dist/cli/index.d.ts +3 -0
  46. package/dist/cli/index.js +46 -0
  47. package/dist/cli/parse.d.ts +28 -0
  48. package/dist/cli/parse.js +97 -0
  49. package/dist/cluster/ClusterWrapper.d.ts +18 -0
  50. package/dist/cluster/ClusterWrapper.js +602 -0
  51. package/dist/config/ConfigStore.d.ts +11 -0
  52. package/dist/config/ConfigStore.js +21 -0
  53. package/dist/config/schema.d.ts +103 -0
  54. package/dist/config/schema.js +49 -0
  55. package/dist/constants.d.ts +83 -0
  56. package/dist/constants.js +289 -0
  57. package/dist/cron/CronScheduler.d.ts +25 -0
  58. package/dist/cron/CronScheduler.js +149 -0
  59. package/dist/daemon/GracefulManager.d.ts +8 -0
  60. package/dist/daemon/GracefulManager.js +29 -0
  61. package/dist/daemon/ManagedProcess.d.ts +71 -0
  62. package/dist/daemon/ManagedProcess.js +1020 -0
  63. package/dist/daemon/Orchestrator.d.ts +51 -0
  64. package/dist/daemon/Orchestrator.js +416 -0
  65. package/dist/daemon/RotatingWriter.d.ts +27 -0
  66. package/dist/daemon/RotatingWriter.js +264 -0
  67. package/dist/daemon/index.d.ts +2 -0
  68. package/dist/daemon/index.js +106 -0
  69. package/dist/daemon/startDaemon.d.ts +30 -0
  70. package/dist/daemon/startDaemon.js +693 -0
  71. package/dist/deploy/CommandPoller.d.ts +13 -0
  72. package/dist/deploy/CommandPoller.js +53 -0
  73. package/dist/deploy/DeployExecutor.d.ts +33 -0
  74. package/dist/deploy/DeployExecutor.js +340 -0
  75. package/dist/deploy/config.d.ts +20 -0
  76. package/dist/deploy/config.js +161 -0
  77. package/dist/deploy/env.d.ts +2 -0
  78. package/dist/deploy/env.js +17 -0
  79. package/dist/deploy/tarball.d.ts +32 -0
  80. package/dist/deploy/tarball.js +243 -0
  81. package/dist/detect/framework.d.ts +2 -0
  82. package/dist/detect/framework.js +24 -0
  83. package/dist/ipc/DaemonClient.d.ts +31 -0
  84. package/dist/ipc/DaemonClient.js +248 -0
  85. package/dist/ipc/DaemonServer.d.ts +28 -0
  86. package/dist/ipc/DaemonServer.js +166 -0
  87. package/dist/ipc/MultiUserClient.d.ts +27 -0
  88. package/dist/ipc/MultiUserClient.js +203 -0
  89. package/dist/ipc/protocol.d.ts +7 -0
  90. package/dist/ipc/protocol.js +53 -0
  91. package/dist/ipc/restoreDaemon.d.ts +8 -0
  92. package/dist/ipc/restoreDaemon.js +19 -0
  93. package/dist/machine-id.d.ts +11 -0
  94. package/dist/machine-id.js +51 -0
  95. package/dist/mcp/auth.d.ts +118 -0
  96. package/dist/mcp/auth.js +245 -0
  97. package/dist/mcp/http.d.ts +20 -0
  98. package/dist/mcp/http.js +229 -0
  99. package/dist/mcp/index.d.ts +3 -0
  100. package/dist/mcp/index.js +8 -0
  101. package/dist/mcp/server.d.ts +37 -0
  102. package/dist/mcp/server.js +413 -0
  103. package/dist/probe/compute-fingerprint.d.ts +27 -0
  104. package/dist/probe/compute-fingerprint.js +65 -0
  105. package/dist/probe/parse-frames.d.ts +21 -0
  106. package/dist/probe/parse-frames.js +57 -0
  107. package/dist/probe/resolve-sourcemaps.d.ts +25 -0
  108. package/dist/probe/resolve-sourcemaps.js +281 -0
  109. package/dist/state/StateStore.d.ts +11 -0
  110. package/dist/state/StateStore.js +78 -0
  111. package/dist/telemetry/TelemetryReporter.d.ts +49 -0
  112. package/dist/telemetry/TelemetryReporter.js +451 -0
  113. package/dist/types/index.d.ts +373 -0
  114. package/dist/types/index.js +2 -0
  115. package/package.json +148 -0
  116. package/packages/cache/README.md +114 -0
  117. package/packages/cache/dist/CacheClient.d.ts +26 -0
  118. package/packages/cache/dist/CacheClient.d.ts.map +1 -0
  119. package/packages/cache/dist/CacheClient.js +174 -0
  120. package/packages/cache/dist/CacheClient.js.map +1 -0
  121. package/packages/cache/dist/CacheFileStore.d.ts +45 -0
  122. package/packages/cache/dist/CacheFileStore.d.ts.map +1 -0
  123. package/packages/cache/dist/CacheFileStore.js +446 -0
  124. package/packages/cache/dist/CacheFileStore.js.map +1 -0
  125. package/packages/cache/dist/CachePersistence.d.ts +9 -0
  126. package/packages/cache/dist/CachePersistence.d.ts.map +1 -0
  127. package/packages/cache/dist/CachePersistence.js +67 -0
  128. package/packages/cache/dist/CachePersistence.js.map +1 -0
  129. package/packages/cache/dist/CachePrimary.d.ts +25 -0
  130. package/packages/cache/dist/CachePrimary.d.ts.map +1 -0
  131. package/packages/cache/dist/CachePrimary.js +155 -0
  132. package/packages/cache/dist/CachePrimary.js.map +1 -0
  133. package/packages/cache/dist/CacheStore.d.ts +50 -0
  134. package/packages/cache/dist/CacheStore.d.ts.map +1 -0
  135. package/packages/cache/dist/CacheStore.js +271 -0
  136. package/packages/cache/dist/CacheStore.js.map +1 -0
  137. package/packages/cache/dist/constants.d.ts +6 -0
  138. package/packages/cache/dist/constants.d.ts.map +1 -0
  139. package/packages/cache/dist/constants.js +9 -0
  140. package/packages/cache/dist/constants.js.map +1 -0
  141. package/packages/cache/dist/index.d.ts +16 -0
  142. package/packages/cache/dist/index.d.ts.map +1 -0
  143. package/packages/cache/dist/index.js +86 -0
  144. package/packages/cache/dist/index.js.map +1 -0
  145. package/packages/cache/dist/serialize.d.ts +9 -0
  146. package/packages/cache/dist/serialize.d.ts.map +1 -0
  147. package/packages/cache/dist/serialize.js +40 -0
  148. package/packages/cache/dist/serialize.js.map +1 -0
  149. package/packages/cache/dist/types.d.ts +123 -0
  150. package/packages/cache/dist/types.d.ts.map +1 -0
  151. package/packages/cache/dist/types.js +2 -0
  152. package/packages/cache/dist/types.js.map +1 -0
  153. package/packages/cache/package.json +27 -0
  154. package/packages/cache/src/CacheClient.ts +227 -0
  155. package/packages/cache/src/CacheFileStore.ts +528 -0
  156. package/packages/cache/src/CachePersistence.ts +89 -0
  157. package/packages/cache/src/CachePrimary.ts +172 -0
  158. package/packages/cache/src/CacheStore.ts +308 -0
  159. package/packages/cache/src/constants.ts +10 -0
  160. package/packages/cache/src/index.ts +100 -0
  161. package/packages/cache/src/serialize.ts +49 -0
  162. package/packages/cache/src/types.ts +156 -0
  163. package/packages/cache/tsconfig.json +18 -0
  164. package/packages/cache/tsconfig.tsbuildinfo +1 -0
  165. package/packages/next/README.md +166 -0
  166. package/packages/next/dist/error-capture.d.ts +34 -0
  167. package/packages/next/dist/error-capture.d.ts.map +1 -0
  168. package/packages/next/dist/error-capture.js +130 -0
  169. package/packages/next/dist/error-capture.js.map +1 -0
  170. package/packages/next/dist/error-handler.d.ts +10 -0
  171. package/packages/next/dist/error-handler.d.ts.map +1 -0
  172. package/packages/next/dist/error-handler.js +186 -0
  173. package/packages/next/dist/error-handler.js.map +1 -0
  174. package/packages/next/dist/isr-cache.d.ts +9 -0
  175. package/packages/next/dist/isr-cache.d.ts.map +1 -0
  176. package/packages/next/dist/isr-cache.js +86 -0
  177. package/packages/next/dist/isr-cache.js.map +1 -0
  178. package/packages/next/dist/stream.d.ts +5 -0
  179. package/packages/next/dist/stream.d.ts.map +1 -0
  180. package/packages/next/dist/stream.js +22 -0
  181. package/packages/next/dist/stream.js.map +1 -0
  182. package/packages/next/dist/types.d.ts +33 -0
  183. package/packages/next/dist/types.d.ts.map +1 -0
  184. package/packages/next/dist/types.js +6 -0
  185. package/packages/next/dist/types.js.map +1 -0
  186. package/packages/next/dist/use-cache.d.ts +4 -0
  187. package/packages/next/dist/use-cache.d.ts.map +1 -0
  188. package/packages/next/dist/use-cache.js +86 -0
  189. package/packages/next/dist/use-cache.js.map +1 -0
  190. package/packages/next/dist/utils.d.ts +32 -0
  191. package/packages/next/dist/utils.d.ts.map +1 -0
  192. package/packages/next/dist/utils.js +88 -0
  193. package/packages/next/dist/utils.js.map +1 -0
  194. package/packages/next/package.json +52 -0
  195. package/packages/next/src/error-capture.ts +177 -0
  196. package/packages/next/src/error-handler.ts +221 -0
  197. package/packages/next/src/isr-cache.ts +100 -0
  198. package/packages/next/src/stream.ts +23 -0
  199. package/packages/next/src/types.ts +33 -0
  200. package/packages/next/src/use-cache.ts +99 -0
  201. package/packages/next/src/utils.ts +102 -0
  202. package/packages/next/tsconfig.json +19 -0
  203. package/packages/next/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,36 @@
1
+ import chalk from 'chalk';
2
+ import { Command } from 'commander';
3
+ import { IPCMessageType } from '../../constants.js';
4
+ import { daemonClient } from '../../ipc/DaemonClient.js';
5
+ export const downCommand = new Command('down')
6
+ .description('Stop process(es)')
7
+ .argument('<target>', 'Process name, id, or "all"')
8
+ .action(async (target) => {
9
+ try {
10
+ const payload = {
11
+ target: target === 'all' ? 'all' : isNaN(Number(target)) ? target : Number(target),
12
+ };
13
+ const response = await daemonClient.request(IPCMessageType.DOWN, payload);
14
+ if (response.success) {
15
+ const results = response.data;
16
+ for (const info of results) {
17
+ console.log(chalk.yellow(`⏹ Process "${info.name}" stopped`));
18
+ }
19
+ if (results.length === 0) {
20
+ console.log(chalk.gray('No processes to stop'));
21
+ }
22
+ }
23
+ else {
24
+ console.error(chalk.red(`✗ Failed to stop: ${response.error}`));
25
+ process.exit(1);
26
+ }
27
+ }
28
+ catch (err) {
29
+ console.error(chalk.red(`✗ Error: ${err.message}`));
30
+ process.exit(1);
31
+ }
32
+ finally {
33
+ daemonClient.disconnect();
34
+ }
35
+ });
36
+ //# sourceMappingURL=down.js.map
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const flushCommand: Command;
3
+ //# sourceMappingURL=flush.d.ts.map
@@ -0,0 +1,28 @@
1
+ import chalk from 'chalk';
2
+ import { Command } from 'commander';
3
+ import { IPCMessageType } from '../../constants.js';
4
+ import { daemonClient } from '../../ipc/DaemonClient.js';
5
+ export const flushCommand = new Command('flush')
6
+ .description('Truncate log files and remove rotated archives')
7
+ .argument('[target]', 'Process name or id (default: all)', 'all')
8
+ .action(async (target) => {
9
+ try {
10
+ const payload = { target };
11
+ const response = await daemonClient.request(IPCMessageType.FLUSH, payload);
12
+ if (response.success) {
13
+ console.log(chalk.green(`✓ Logs flushed for ${target === 'all' ? 'all processes' : `"${target}"`}`));
14
+ }
15
+ else {
16
+ console.error(chalk.red(`✗ Failed to flush logs: ${response.error}`));
17
+ process.exit(1);
18
+ }
19
+ }
20
+ catch (err) {
21
+ console.error(chalk.red(`✗ Error: ${err.message}`));
22
+ process.exit(1);
23
+ }
24
+ finally {
25
+ daemonClient.disconnect();
26
+ }
27
+ });
28
+ //# sourceMappingURL=flush.js.map
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const killCommand: Command;
3
+ //# sourceMappingURL=kill.d.ts.map
@@ -0,0 +1,35 @@
1
+ import chalk from 'chalk';
2
+ import { Command } from 'commander';
3
+ import { IPCMessageType } from '../../constants.js';
4
+ import { daemonClient } from '../../ipc/DaemonClient.js';
5
+ export const killCommand = new Command('kill')
6
+ .description('Kill the ORKIFY daemon')
7
+ .option('-f, --force', 'Skip graceful shutdown and SIGKILL all processes immediately')
8
+ .action(async (opts) => {
9
+ try {
10
+ const response = await daemonClient.request(IPCMessageType.KILL_DAEMON, {
11
+ force: opts.force === true,
12
+ });
13
+ if (response.success) {
14
+ console.log(chalk.yellow('⏹ ORKIFY daemon killed'));
15
+ }
16
+ else {
17
+ console.error(chalk.red(`✗ Failed to kill daemon: ${response.error}`));
18
+ process.exit(1);
19
+ }
20
+ }
21
+ catch (err) {
22
+ const message = err.message;
23
+ if (message.includes('ECONNREFUSED') || message.includes('ENOENT')) {
24
+ console.log(chalk.gray('Daemon is not running'));
25
+ }
26
+ else {
27
+ console.error(chalk.red(`✗ Error: ${message}`));
28
+ process.exit(1);
29
+ }
30
+ }
31
+ finally {
32
+ daemonClient.disconnect();
33
+ }
34
+ });
35
+ //# sourceMappingURL=kill.js.map
@@ -0,0 +1,14 @@
1
+ import { Command } from 'commander';
2
+ import type { ProcessInfo } from '../../types/index.js';
3
+ export declare function formatProcessTable(processes: ProcessInfo[], options?: {
4
+ cache?: boolean;
5
+ showUser?: string;
6
+ verbose?: boolean;
7
+ }): string;
8
+ export declare function renderProcessTable(processes: ProcessInfo[], options?: {
9
+ cache?: boolean;
10
+ showUser?: string;
11
+ verbose?: boolean;
12
+ }): void;
13
+ export declare const listCommand: Command;
14
+ //# sourceMappingURL=list.d.ts.map
@@ -0,0 +1,361 @@
1
+ import chalk from 'chalk';
2
+ import Table from 'cli-table3';
3
+ import { Command } from 'commander';
4
+ import { IPCMessageType, ProcessStatus } from '../../constants.js';
5
+ import { daemonClient } from '../../ipc/DaemonClient.js';
6
+ import { isElevated, listAllUsers } from '../../ipc/MultiUserClient.js';
7
+ import { sleep } from '../parse.js';
8
+ function formatUptime(ms) {
9
+ const seconds = Math.floor(ms / 1000);
10
+ const minutes = Math.floor(seconds / 60);
11
+ const hours = Math.floor(minutes / 60);
12
+ const days = Math.floor(hours / 24);
13
+ if (days > 0)
14
+ return `${days}d ${hours % 24}h`;
15
+ if (hours > 0)
16
+ return `${hours}h ${minutes % 60}m`;
17
+ if (minutes > 0)
18
+ return `${minutes}m ${seconds % 60}s`;
19
+ return `${seconds}s`;
20
+ }
21
+ function formatMemory(bytes) {
22
+ if (bytes === 0)
23
+ return '0 B';
24
+ const k = 1024;
25
+ const sizes = ['B', 'KB', 'MB', 'GB'];
26
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
27
+ return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`;
28
+ }
29
+ function formatCpu(cpu) {
30
+ return `${cpu.toFixed(1)}%`;
31
+ }
32
+ function getStatusColor(status, stale) {
33
+ const label = stale && status === ProcessStatus.ONLINE ? `${status} (stale)` : status;
34
+ switch (status) {
35
+ case ProcessStatus.ONLINE:
36
+ return stale ? chalk.yellow(label) : chalk.green(label);
37
+ case ProcessStatus.STOPPING:
38
+ return chalk.yellow(label);
39
+ case ProcessStatus.STOPPED:
40
+ return chalk.gray(label);
41
+ case ProcessStatus.ERRORED:
42
+ return chalk.red(label);
43
+ case ProcessStatus.LAUNCHING:
44
+ return chalk.blue(label);
45
+ default:
46
+ return label;
47
+ }
48
+ }
49
+ function joinTreeViewLines(tableStr) {
50
+ const lines = tableStr.split('\n');
51
+ for (let i = 1; i < lines.length - 1; i++) {
52
+ const line = lines[i];
53
+ if (!line.includes('┼'))
54
+ continue;
55
+ // Check if the row below is a worker row (contains tree characters)
56
+ const below = lines[i + 1];
57
+ if (!below || (!/├─/.test(below) && !/└─/.test(below)))
58
+ continue;
59
+ // Find the tree character position in the row below to locate the ID column
60
+ const treePos = below.search(/[├└]─/);
61
+ if (treePos === -1)
62
+ continue;
63
+ // Find all ┼ positions in the border line
64
+ const crosses = [...line.matchAll(/┼/g)].map((m) => m.index);
65
+ // The ID column ends at the first ┼ after the tree character position
66
+ const idColEnd = crosses.find((pos) => pos > treePos);
67
+ if (idColEnd === undefined)
68
+ continue;
69
+ // The ID column starts after the previous separator (├ at pos 0, or a preceding ┼)
70
+ const idColEndIdx = crosses.indexOf(idColEnd);
71
+ const idColStart = idColEndIdx === 0 ? 0 : (crosses[idColEndIdx - 1] ?? 0);
72
+ // Build the tree trunk continuation for the ID column segment
73
+ // " │" at tree trunk position + spaces + "├" reconnecting to horizontal border
74
+ const contentStart = idColStart + 1;
75
+ const contentWidth = idColEnd - contentStart;
76
+ const treeTrunk = ' │' + ' '.repeat(contentWidth - 2);
77
+ // For the first column, change the left border ├ to │ (vertical continuation)
78
+ const left = idColStart === 0 ? '│' : line.slice(0, contentStart);
79
+ lines[i] = left + treeTrunk + '├' + line.slice(idColEnd + 1);
80
+ }
81
+ return lines.join('\n');
82
+ }
83
+ function formatNumber(n) {
84
+ return n.toLocaleString('en-US');
85
+ }
86
+ export function formatProcessTable(processes, options) {
87
+ const includeUser = options?.showUser !== undefined;
88
+ const verbose = options?.verbose ?? false;
89
+ const showCache = options?.cache ?? false;
90
+ // Only show cache columns if --cache flag is set AND at least one worker has cache data
91
+ const hasCacheData = showCache && processes.some((p) => p.workers.some((w) => w.cacheSize !== undefined));
92
+ const headers = [
93
+ ...(includeUser ? [chalk.cyan('user')] : []),
94
+ chalk.cyan('id'),
95
+ chalk.cyan('name'),
96
+ chalk.cyan('mode'),
97
+ ...(verbose ? [chalk.cyan('pid')] : []),
98
+ chalk.cyan('↺'),
99
+ chalk.cyan('✘'),
100
+ chalk.cyan('status'),
101
+ chalk.cyan('port'),
102
+ chalk.cyan('cpu'),
103
+ chalk.cyan('mem'),
104
+ ...(hasCacheData
105
+ ? [chalk.cyan('size'), chalk.cyan('hits'), chalk.cyan('misses'), chalk.cyan('hit%')]
106
+ : []),
107
+ chalk.cyan('uptime'),
108
+ ];
109
+ const table = new Table({
110
+ head: headers,
111
+ style: {
112
+ head: [],
113
+ border: [],
114
+ },
115
+ });
116
+ for (const proc of processes) {
117
+ const userCol = includeUser ? [chalk.blue(options?.showUser)] : [];
118
+ // For cluster mode, show each worker
119
+ if (proc.workers.length > 1) {
120
+ // Summary row
121
+ const totalMem = proc.workers.reduce((sum, w) => sum + w.memory, 0);
122
+ const avgCpu = proc.workers.reduce((sum, w) => sum + w.cpu, 0) / proc.workers.length;
123
+ const totalRestarts = proc.workers.reduce((sum, w) => sum + w.restarts, 0);
124
+ const totalCrashes = proc.workers.reduce((sum, w) => sum + w.crashes, 0);
125
+ const hasStale = proc.workers.some((w) => w.stale);
126
+ // Cache aggregates for cluster summary
127
+ const cacheWorkers = proc.workers.filter((w) => w.cacheSize !== undefined);
128
+ const totalCacheSize = cacheWorkers.reduce((s, w) => s + (w.cacheSize ?? 0), 0);
129
+ const totalCacheHits = cacheWorkers.reduce((s, w) => s + (w.cacheHits ?? 0), 0);
130
+ const totalCacheMisses = cacheWorkers.reduce((s, w) => s + (w.cacheMisses ?? 0), 0);
131
+ const totalCacheReqs = totalCacheHits + totalCacheMisses;
132
+ const aggHitRate = totalCacheReqs > 0 ? (totalCacheHits / totalCacheReqs) * 100 : 0;
133
+ table.push([
134
+ ...userCol,
135
+ proc.id,
136
+ chalk.bold(proc.name),
137
+ proc.execMode,
138
+ ...(verbose ? [proc.pid ?? '-'] : []),
139
+ totalRestarts,
140
+ totalCrashes || chalk.gray(0),
141
+ getStatusColor(proc.status, hasStale),
142
+ proc.port ?? chalk.gray('-'),
143
+ formatCpu(avgCpu),
144
+ formatMemory(totalMem),
145
+ ...(hasCacheData
146
+ ? cacheWorkers.length > 0
147
+ ? [
148
+ formatNumber(totalCacheSize),
149
+ formatNumber(totalCacheHits),
150
+ formatNumber(totalCacheMisses),
151
+ `${aggHitRate.toFixed(1)}%`,
152
+ ]
153
+ : [chalk.gray('-'), chalk.gray('-'), chalk.gray('-'), chalk.gray('-')]
154
+ : []),
155
+ '-',
156
+ ]);
157
+ // Worker rows
158
+ for (let i = 0; i < proc.workers.length; i++) {
159
+ const worker = proc.workers[i];
160
+ const isLast = i === proc.workers.length - 1;
161
+ const prefix = isLast ? '└─' : '├─';
162
+ table.push([
163
+ ...(includeUser ? [''] : []),
164
+ `${prefix} ${worker.id}`,
165
+ chalk.gray(`worker ${worker.id}`),
166
+ '',
167
+ ...(verbose ? [worker.pid || '-'] : []),
168
+ worker.restarts,
169
+ worker.crashes || chalk.gray(0),
170
+ getStatusColor(worker.status, worker.stale),
171
+ '',
172
+ formatCpu(worker.cpu),
173
+ formatMemory(worker.memory),
174
+ ...(hasCacheData
175
+ ? worker.cacheSize !== undefined
176
+ ? [
177
+ formatNumber(worker.cacheSize),
178
+ formatNumber(worker.cacheHits ?? 0),
179
+ formatNumber(worker.cacheMisses ?? 0),
180
+ `${(worker.cacheHitRate ?? 0).toFixed(1)}%`,
181
+ ]
182
+ : [chalk.gray('-'), chalk.gray('-'), chalk.gray('-'), chalk.gray('-')]
183
+ : []),
184
+ formatUptime(worker.uptime),
185
+ ]);
186
+ }
187
+ }
188
+ else {
189
+ // Single process
190
+ const worker = proc.workers[0];
191
+ table.push([
192
+ ...userCol,
193
+ proc.id,
194
+ proc.name,
195
+ proc.execMode,
196
+ ...(verbose ? [worker?.pid || '-'] : []),
197
+ worker?.restarts || 0,
198
+ worker?.crashes || chalk.gray(0),
199
+ getStatusColor(proc.status),
200
+ proc.port ?? chalk.gray('-'),
201
+ formatCpu(worker?.cpu || 0),
202
+ formatMemory(worker?.memory || 0),
203
+ ...(hasCacheData
204
+ ? worker?.cacheSize !== undefined
205
+ ? [
206
+ formatNumber(worker.cacheSize),
207
+ formatNumber(worker.cacheHits ?? 0),
208
+ formatNumber(worker.cacheMisses ?? 0),
209
+ `${(worker.cacheHitRate ?? 0).toFixed(1)}%`,
210
+ ]
211
+ : [chalk.gray('-'), chalk.gray('-'), chalk.gray('-'), chalk.gray('-')]
212
+ : []),
213
+ worker ? formatUptime(worker.uptime) : '-',
214
+ ]);
215
+ }
216
+ }
217
+ return joinTreeViewLines(table.toString());
218
+ }
219
+ export function renderProcessTable(processes, options) {
220
+ console.log(formatProcessTable(processes, options));
221
+ }
222
+ async function followList(verbose, cache) {
223
+ // Test daemon connectivity before entering the loop
224
+ try {
225
+ await daemonClient.request(IPCMessageType.LIST);
226
+ }
227
+ catch (err) {
228
+ const message = err.message;
229
+ if (message.includes('ECONNREFUSED') || message.includes('ENOENT')) {
230
+ console.log(chalk.gray('No processes running (daemon not started)'));
231
+ }
232
+ else {
233
+ console.error(chalk.red(`✗ Error: ${message}`));
234
+ }
235
+ return;
236
+ }
237
+ let prevLines = 0;
238
+ let stopped = false;
239
+ let forceExit = false;
240
+ const onSigint = () => {
241
+ if (stopped) {
242
+ forceExit = true;
243
+ return;
244
+ }
245
+ stopped = true;
246
+ };
247
+ process.on('SIGINT', onSigint);
248
+ while (!stopped && !forceExit) {
249
+ try {
250
+ const response = await daemonClient.request(IPCMessageType.LIST);
251
+ if (!response.success)
252
+ break;
253
+ const processes = response.data;
254
+ const tableStr = processes.length === 0
255
+ ? chalk.gray('No processes running')
256
+ : formatProcessTable(processes, { verbose, cache });
257
+ if (process.stdout.isTTY && prevLines > 0) {
258
+ process.stdout.write(`\x1B[${prevLines}A\x1B[J`);
259
+ }
260
+ process.stdout.write(tableStr + '\n');
261
+ prevLines = tableStr.split('\n').length;
262
+ }
263
+ catch {
264
+ break;
265
+ }
266
+ await sleep(1000);
267
+ }
268
+ process.removeListener('SIGINT', onSigint);
269
+ }
270
+ export const listCommand = new Command('list')
271
+ .alias('ls')
272
+ .alias('status')
273
+ .description('List all processes')
274
+ .option('-v, --verbose', 'Show additional details including PIDs')
275
+ .option('-c, --cache', 'Show cache statistics (size, hits, misses, hit rate)')
276
+ .option('-f, --follow', 'Live-update the process table (Ctrl+C to stop)')
277
+ .option('--all-users', 'List processes from all users (requires sudo on Unix)')
278
+ .action(async (options) => {
279
+ if (options.allUsers) {
280
+ // Check for elevated privileges on Unix
281
+ if (process.platform !== 'win32' && !isElevated()) {
282
+ console.error(chalk.red('✗ This command requires elevated privileges'));
283
+ console.error(chalk.red(' Run with: sudo orkify list --all-users'));
284
+ process.exit(1);
285
+ }
286
+ // List processes from all users
287
+ const result = await listAllUsers();
288
+ // Show warnings first
289
+ for (const warning of result.warnings) {
290
+ console.log(chalk.yellow(`⚠ ${warning}`));
291
+ }
292
+ if (result.users.length === 0 && result.inaccessibleUsers.length === 0) {
293
+ if (result.warnings.length === 0) {
294
+ console.log(chalk.gray('No processes running on this system'));
295
+ }
296
+ }
297
+ else {
298
+ // Show accessible processes
299
+ let totalProcesses = 0;
300
+ for (const userList of result.users) {
301
+ if (userList.processes.length > 0) {
302
+ totalProcesses += userList.processes.length;
303
+ renderProcessTable(userList.processes, {
304
+ showUser: userList.user,
305
+ verbose: options.verbose,
306
+ });
307
+ }
308
+ }
309
+ if (totalProcesses === 0 && result.inaccessibleUsers.length === 0) {
310
+ console.log(chalk.gray('No processes running'));
311
+ }
312
+ }
313
+ // Show errors at the end (important - results may be incomplete)
314
+ if (result.inaccessibleUsers.length > 0) {
315
+ console.log('');
316
+ console.error(chalk.red(`✗ Could not access processes for: ${result.inaccessibleUsers.join(', ')}`));
317
+ process.exit(1);
318
+ }
319
+ return;
320
+ }
321
+ // Follow mode — live-updating table
322
+ if (options.follow) {
323
+ await followList(options.verbose ?? false, options.cache ?? false);
324
+ daemonClient.disconnect();
325
+ return;
326
+ }
327
+ // Normal single-user list
328
+ try {
329
+ const response = await daemonClient.request(IPCMessageType.LIST);
330
+ if (response.success) {
331
+ const processes = response.data;
332
+ if (processes.length === 0) {
333
+ console.log(chalk.gray('No processes running'));
334
+ return;
335
+ }
336
+ renderProcessTable(processes, { verbose: options.verbose, cache: options.cache });
337
+ if (options.cache &&
338
+ !processes.some((p) => p.workers.some((w) => w.cacheSize !== undefined))) {
339
+ console.log(chalk.gray('\nNo active cache detected. Import orkify/cache in your app to enable cache statistics.'));
340
+ }
341
+ }
342
+ else {
343
+ console.error(chalk.red(`✗ Failed to list: ${response.error}`));
344
+ process.exit(1);
345
+ }
346
+ }
347
+ catch (err) {
348
+ const message = err.message;
349
+ if (message.includes('ECONNREFUSED') || message.includes('ENOENT')) {
350
+ console.log(chalk.gray('No processes running (daemon not started)'));
351
+ }
352
+ else {
353
+ console.error(chalk.red(`✗ Error: ${message}`));
354
+ process.exit(1);
355
+ }
356
+ }
357
+ finally {
358
+ daemonClient.disconnect();
359
+ }
360
+ });
361
+ //# sourceMappingURL=list.js.map
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const logsCommand: Command;
3
+ //# sourceMappingURL=logs.d.ts.map
@@ -0,0 +1,107 @@
1
+ import chalk from 'chalk';
2
+ import { Command } from 'commander';
3
+ import { createReadStream, existsSync } from 'node:fs';
4
+ import { join } from 'node:path';
5
+ import { createInterface } from 'node:readline';
6
+ import { LOGS_DIR } from '../../constants.js';
7
+ import { daemonClient } from '../../ipc/DaemonClient.js';
8
+ export const logsCommand = new Command('logs')
9
+ .description('Stream logs from process(es)')
10
+ .argument('[name]', 'Process name (optional, shows all if omitted)')
11
+ .option('-n, --lines <number>', 'Number of lines to show', '100')
12
+ .option('-f, --follow', 'Follow log output (stream new logs)')
13
+ .option('--err', 'Show error logs only')
14
+ .option('--out', 'Show stdout logs only')
15
+ .action(async (name, options) => {
16
+ try {
17
+ const lines = parseInt(options.lines, 10);
18
+ // If not following, just read from log files
19
+ if (!options.follow) {
20
+ await showLogFile(name, lines, options.err, options.out);
21
+ return;
22
+ }
23
+ // Follow mode: subscribe to live logs
24
+ console.log(chalk.blue('Streaming logs... (Ctrl+C to stop)\n'));
25
+ const unsubscribe = await daemonClient.streamLogs(name, (data) => {
26
+ const logData = data;
27
+ const prefix = logData.processName
28
+ ? chalk.cyan(`[${logData.processName}:${logData.workerId}]`)
29
+ : chalk.cyan(`[worker:${logData.workerId}]`);
30
+ const output = logData.type === 'err' ? chalk.red(logData.data) : logData.data;
31
+ process.stdout.write(`${prefix} ${output}`);
32
+ });
33
+ // Keep the process running until interrupted
34
+ process.on('SIGINT', () => {
35
+ unsubscribe();
36
+ daemonClient.disconnect();
37
+ process.exit(0);
38
+ });
39
+ // Keep alive
40
+ await new Promise(() => { });
41
+ }
42
+ catch (err) {
43
+ console.error(chalk.red(`✗ Error: ${err.message}`));
44
+ process.exit(1);
45
+ }
46
+ });
47
+ async function showLogFile(name, lines, errOnly, outOnly) {
48
+ if (!existsSync(LOGS_DIR)) {
49
+ console.log(chalk.gray('No logs found'));
50
+ return;
51
+ }
52
+ const files = [];
53
+ if (name) {
54
+ if (!errOnly) {
55
+ const outFile = join(LOGS_DIR, `${name}.stdout.log`);
56
+ if (existsSync(outFile))
57
+ files.push(outFile);
58
+ }
59
+ if (!outOnly) {
60
+ const errFile = join(LOGS_DIR, `${name}.stderr.log`);
61
+ if (existsSync(errFile))
62
+ files.push(errFile);
63
+ }
64
+ }
65
+ else {
66
+ // Show all log files
67
+ const { readdirSync } = await import('node:fs');
68
+ const allFiles = readdirSync(LOGS_DIR);
69
+ for (const file of allFiles) {
70
+ if (errOnly && !file.endsWith('.stderr.log'))
71
+ continue;
72
+ if (outOnly && !file.endsWith('.stdout.log'))
73
+ continue;
74
+ if (!file.endsWith('.stdout.log') && !file.endsWith('.stderr.log'))
75
+ continue;
76
+ files.push(join(LOGS_DIR, file));
77
+ }
78
+ }
79
+ if (files.length === 0) {
80
+ console.log(chalk.gray('No logs found'));
81
+ return;
82
+ }
83
+ for (const file of files) {
84
+ await tailFile(file, lines);
85
+ }
86
+ }
87
+ async function tailFile(filePath, lines) {
88
+ return new Promise((resolve, reject) => {
89
+ const buffer = [];
90
+ const stream = createReadStream(filePath, { encoding: 'utf8' });
91
+ const rl = createInterface({ input: stream });
92
+ rl.on('line', (line) => {
93
+ buffer.push(line);
94
+ if (buffer.length > lines) {
95
+ buffer.shift();
96
+ }
97
+ });
98
+ rl.on('close', () => {
99
+ for (const line of buffer) {
100
+ console.log(line);
101
+ }
102
+ resolve();
103
+ });
104
+ rl.on('error', reject);
105
+ });
106
+ }
107
+ //# sourceMappingURL=logs.js.map
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const mcpCommand: Command;
3
+ //# sourceMappingURL=mcp.d.ts.map