@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.
- package/LICENSE +191 -0
- package/README.md +1701 -0
- package/bin/orkify +3 -0
- package/boot/systemd/orkify@.service +30 -0
- package/dist/agent-name.d.ts +4 -0
- package/dist/agent-name.js +42 -0
- package/dist/alerts/AlertEvaluator.d.ts +14 -0
- package/dist/alerts/AlertEvaluator.js +135 -0
- package/dist/cli/commands/autostart.d.ts +3 -0
- package/dist/cli/commands/autostart.js +11 -0
- package/dist/cli/commands/crash-test.d.ts +3 -0
- package/dist/cli/commands/crash-test.js +17 -0
- package/dist/cli/commands/daemon-reload.d.ts +3 -0
- package/dist/cli/commands/daemon-reload.js +72 -0
- package/dist/cli/commands/delete.d.ts +3 -0
- package/dist/cli/commands/delete.js +37 -0
- package/dist/cli/commands/deploy.d.ts +6 -0
- package/dist/cli/commands/deploy.js +266 -0
- package/dist/cli/commands/down.d.ts +3 -0
- package/dist/cli/commands/down.js +36 -0
- package/dist/cli/commands/flush.d.ts +3 -0
- package/dist/cli/commands/flush.js +28 -0
- package/dist/cli/commands/kill.d.ts +3 -0
- package/dist/cli/commands/kill.js +35 -0
- package/dist/cli/commands/list.d.ts +14 -0
- package/dist/cli/commands/list.js +361 -0
- package/dist/cli/commands/logs.d.ts +3 -0
- package/dist/cli/commands/logs.js +107 -0
- package/dist/cli/commands/mcp.d.ts +3 -0
- package/dist/cli/commands/mcp.js +151 -0
- package/dist/cli/commands/reload.d.ts +3 -0
- package/dist/cli/commands/reload.js +54 -0
- package/dist/cli/commands/restart.d.ts +3 -0
- package/dist/cli/commands/restart.js +43 -0
- package/dist/cli/commands/restore.d.ts +3 -0
- package/dist/cli/commands/restore.js +88 -0
- package/dist/cli/commands/run.d.ts +8 -0
- package/dist/cli/commands/run.js +212 -0
- package/dist/cli/commands/snap.d.ts +3 -0
- package/dist/cli/commands/snap.js +30 -0
- package/dist/cli/commands/up.d.ts +3 -0
- package/dist/cli/commands/up.js +125 -0
- package/dist/cli/crash-recovery.d.ts +2 -0
- package/dist/cli/crash-recovery.js +67 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.js +46 -0
- package/dist/cli/parse.d.ts +28 -0
- package/dist/cli/parse.js +97 -0
- package/dist/cluster/ClusterWrapper.d.ts +18 -0
- package/dist/cluster/ClusterWrapper.js +602 -0
- package/dist/config/ConfigStore.d.ts +11 -0
- package/dist/config/ConfigStore.js +21 -0
- package/dist/config/schema.d.ts +103 -0
- package/dist/config/schema.js +49 -0
- package/dist/constants.d.ts +83 -0
- package/dist/constants.js +289 -0
- package/dist/cron/CronScheduler.d.ts +25 -0
- package/dist/cron/CronScheduler.js +149 -0
- package/dist/daemon/GracefulManager.d.ts +8 -0
- package/dist/daemon/GracefulManager.js +29 -0
- package/dist/daemon/ManagedProcess.d.ts +71 -0
- package/dist/daemon/ManagedProcess.js +1020 -0
- package/dist/daemon/Orchestrator.d.ts +51 -0
- package/dist/daemon/Orchestrator.js +416 -0
- package/dist/daemon/RotatingWriter.d.ts +27 -0
- package/dist/daemon/RotatingWriter.js +264 -0
- package/dist/daemon/index.d.ts +2 -0
- package/dist/daemon/index.js +106 -0
- package/dist/daemon/startDaemon.d.ts +30 -0
- package/dist/daemon/startDaemon.js +693 -0
- package/dist/deploy/CommandPoller.d.ts +13 -0
- package/dist/deploy/CommandPoller.js +53 -0
- package/dist/deploy/DeployExecutor.d.ts +33 -0
- package/dist/deploy/DeployExecutor.js +340 -0
- package/dist/deploy/config.d.ts +20 -0
- package/dist/deploy/config.js +161 -0
- package/dist/deploy/env.d.ts +2 -0
- package/dist/deploy/env.js +17 -0
- package/dist/deploy/tarball.d.ts +32 -0
- package/dist/deploy/tarball.js +243 -0
- package/dist/detect/framework.d.ts +2 -0
- package/dist/detect/framework.js +24 -0
- package/dist/ipc/DaemonClient.d.ts +31 -0
- package/dist/ipc/DaemonClient.js +248 -0
- package/dist/ipc/DaemonServer.d.ts +28 -0
- package/dist/ipc/DaemonServer.js +166 -0
- package/dist/ipc/MultiUserClient.d.ts +27 -0
- package/dist/ipc/MultiUserClient.js +203 -0
- package/dist/ipc/protocol.d.ts +7 -0
- package/dist/ipc/protocol.js +53 -0
- package/dist/ipc/restoreDaemon.d.ts +8 -0
- package/dist/ipc/restoreDaemon.js +19 -0
- package/dist/machine-id.d.ts +11 -0
- package/dist/machine-id.js +51 -0
- package/dist/mcp/auth.d.ts +118 -0
- package/dist/mcp/auth.js +245 -0
- package/dist/mcp/http.d.ts +20 -0
- package/dist/mcp/http.js +229 -0
- package/dist/mcp/index.d.ts +3 -0
- package/dist/mcp/index.js +8 -0
- package/dist/mcp/server.d.ts +37 -0
- package/dist/mcp/server.js +413 -0
- package/dist/probe/compute-fingerprint.d.ts +27 -0
- package/dist/probe/compute-fingerprint.js +65 -0
- package/dist/probe/parse-frames.d.ts +21 -0
- package/dist/probe/parse-frames.js +57 -0
- package/dist/probe/resolve-sourcemaps.d.ts +25 -0
- package/dist/probe/resolve-sourcemaps.js +281 -0
- package/dist/state/StateStore.d.ts +11 -0
- package/dist/state/StateStore.js +78 -0
- package/dist/telemetry/TelemetryReporter.d.ts +49 -0
- package/dist/telemetry/TelemetryReporter.js +451 -0
- package/dist/types/index.d.ts +373 -0
- package/dist/types/index.js +2 -0
- package/package.json +148 -0
- package/packages/cache/README.md +114 -0
- package/packages/cache/dist/CacheClient.d.ts +26 -0
- package/packages/cache/dist/CacheClient.d.ts.map +1 -0
- package/packages/cache/dist/CacheClient.js +174 -0
- package/packages/cache/dist/CacheClient.js.map +1 -0
- package/packages/cache/dist/CacheFileStore.d.ts +45 -0
- package/packages/cache/dist/CacheFileStore.d.ts.map +1 -0
- package/packages/cache/dist/CacheFileStore.js +446 -0
- package/packages/cache/dist/CacheFileStore.js.map +1 -0
- package/packages/cache/dist/CachePersistence.d.ts +9 -0
- package/packages/cache/dist/CachePersistence.d.ts.map +1 -0
- package/packages/cache/dist/CachePersistence.js +67 -0
- package/packages/cache/dist/CachePersistence.js.map +1 -0
- package/packages/cache/dist/CachePrimary.d.ts +25 -0
- package/packages/cache/dist/CachePrimary.d.ts.map +1 -0
- package/packages/cache/dist/CachePrimary.js +155 -0
- package/packages/cache/dist/CachePrimary.js.map +1 -0
- package/packages/cache/dist/CacheStore.d.ts +50 -0
- package/packages/cache/dist/CacheStore.d.ts.map +1 -0
- package/packages/cache/dist/CacheStore.js +271 -0
- package/packages/cache/dist/CacheStore.js.map +1 -0
- package/packages/cache/dist/constants.d.ts +6 -0
- package/packages/cache/dist/constants.d.ts.map +1 -0
- package/packages/cache/dist/constants.js +9 -0
- package/packages/cache/dist/constants.js.map +1 -0
- package/packages/cache/dist/index.d.ts +16 -0
- package/packages/cache/dist/index.d.ts.map +1 -0
- package/packages/cache/dist/index.js +86 -0
- package/packages/cache/dist/index.js.map +1 -0
- package/packages/cache/dist/serialize.d.ts +9 -0
- package/packages/cache/dist/serialize.d.ts.map +1 -0
- package/packages/cache/dist/serialize.js +40 -0
- package/packages/cache/dist/serialize.js.map +1 -0
- package/packages/cache/dist/types.d.ts +123 -0
- package/packages/cache/dist/types.d.ts.map +1 -0
- package/packages/cache/dist/types.js +2 -0
- package/packages/cache/dist/types.js.map +1 -0
- package/packages/cache/package.json +27 -0
- package/packages/cache/src/CacheClient.ts +227 -0
- package/packages/cache/src/CacheFileStore.ts +528 -0
- package/packages/cache/src/CachePersistence.ts +89 -0
- package/packages/cache/src/CachePrimary.ts +172 -0
- package/packages/cache/src/CacheStore.ts +308 -0
- package/packages/cache/src/constants.ts +10 -0
- package/packages/cache/src/index.ts +100 -0
- package/packages/cache/src/serialize.ts +49 -0
- package/packages/cache/src/types.ts +156 -0
- package/packages/cache/tsconfig.json +18 -0
- package/packages/cache/tsconfig.tsbuildinfo +1 -0
- package/packages/next/README.md +166 -0
- package/packages/next/dist/error-capture.d.ts +34 -0
- package/packages/next/dist/error-capture.d.ts.map +1 -0
- package/packages/next/dist/error-capture.js +130 -0
- package/packages/next/dist/error-capture.js.map +1 -0
- package/packages/next/dist/error-handler.d.ts +10 -0
- package/packages/next/dist/error-handler.d.ts.map +1 -0
- package/packages/next/dist/error-handler.js +186 -0
- package/packages/next/dist/error-handler.js.map +1 -0
- package/packages/next/dist/isr-cache.d.ts +9 -0
- package/packages/next/dist/isr-cache.d.ts.map +1 -0
- package/packages/next/dist/isr-cache.js +86 -0
- package/packages/next/dist/isr-cache.js.map +1 -0
- package/packages/next/dist/stream.d.ts +5 -0
- package/packages/next/dist/stream.d.ts.map +1 -0
- package/packages/next/dist/stream.js +22 -0
- package/packages/next/dist/stream.js.map +1 -0
- package/packages/next/dist/types.d.ts +33 -0
- package/packages/next/dist/types.d.ts.map +1 -0
- package/packages/next/dist/types.js +6 -0
- package/packages/next/dist/types.js.map +1 -0
- package/packages/next/dist/use-cache.d.ts +4 -0
- package/packages/next/dist/use-cache.d.ts.map +1 -0
- package/packages/next/dist/use-cache.js +86 -0
- package/packages/next/dist/use-cache.js.map +1 -0
- package/packages/next/dist/utils.d.ts +32 -0
- package/packages/next/dist/utils.d.ts.map +1 -0
- package/packages/next/dist/utils.js +88 -0
- package/packages/next/dist/utils.js.map +1 -0
- package/packages/next/package.json +52 -0
- package/packages/next/src/error-capture.ts +177 -0
- package/packages/next/src/error-handler.ts +221 -0
- package/packages/next/src/isr-cache.ts +100 -0
- package/packages/next/src/stream.ts +23 -0
- package/packages/next/src/types.ts +33 -0
- package/packages/next/src/use-cache.ts +99 -0
- package/packages/next/src/utils.ts +102 -0
- package/packages/next/tsconfig.json +19 -0
- 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,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,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,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
|