@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,166 @@
|
|
|
1
|
+
import { existsSync, unlinkSync } from 'node:fs';
|
|
2
|
+
import { createServer } from 'node:net';
|
|
3
|
+
import { IPCMessageType, SOCKET_PATH } from '../constants.js';
|
|
4
|
+
import { createMessageParser, createResponse, serialize } from './protocol.js';
|
|
5
|
+
export class ClientConnection {
|
|
6
|
+
socket;
|
|
7
|
+
messageParser = createMessageParser();
|
|
8
|
+
constructor(socket) {
|
|
9
|
+
this.socket = socket;
|
|
10
|
+
}
|
|
11
|
+
send(message) {
|
|
12
|
+
if (!this.socket.destroyed) {
|
|
13
|
+
this.socket.write(serialize(message));
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
onData(handler) {
|
|
17
|
+
this.socket.on('data', (chunk) => {
|
|
18
|
+
const messages = this.messageParser(chunk);
|
|
19
|
+
handler(messages);
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
onClose(handler) {
|
|
23
|
+
this.socket.on('close', handler);
|
|
24
|
+
}
|
|
25
|
+
onError(handler) {
|
|
26
|
+
this.socket.on('error', handler);
|
|
27
|
+
}
|
|
28
|
+
close() {
|
|
29
|
+
this.socket.end();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export class DaemonServer {
|
|
33
|
+
server = null;
|
|
34
|
+
clients = new Set();
|
|
35
|
+
handlers = new Map();
|
|
36
|
+
logSubscribers = new Map();
|
|
37
|
+
registerHandler(type, handler) {
|
|
38
|
+
this.handlers.set(type, handler);
|
|
39
|
+
}
|
|
40
|
+
subscribeToLogs(processName, client, requestId) {
|
|
41
|
+
let subscribers = this.logSubscribers.get(processName);
|
|
42
|
+
if (!subscribers) {
|
|
43
|
+
subscribers = new Set();
|
|
44
|
+
this.logSubscribers.set(processName, subscribers);
|
|
45
|
+
}
|
|
46
|
+
subscribers.add({ client, requestId });
|
|
47
|
+
}
|
|
48
|
+
unsubscribeFromLogs(processName, client) {
|
|
49
|
+
const subscribers = this.logSubscribers.get(processName);
|
|
50
|
+
if (subscribers) {
|
|
51
|
+
for (const sub of subscribers) {
|
|
52
|
+
if (sub.client === client) {
|
|
53
|
+
subscribers.delete(sub);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
broadcastLog(processName, data) {
|
|
59
|
+
const subscribers = this.logSubscribers.get(processName);
|
|
60
|
+
if (subscribers) {
|
|
61
|
+
for (const { client, requestId } of subscribers) {
|
|
62
|
+
client.send({
|
|
63
|
+
type: IPCMessageType.LOG_DATA,
|
|
64
|
+
id: requestId,
|
|
65
|
+
success: true,
|
|
66
|
+
data,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Also broadcast to 'all' subscribers
|
|
71
|
+
const allSubscribers = this.logSubscribers.get('all');
|
|
72
|
+
if (allSubscribers) {
|
|
73
|
+
for (const { client, requestId } of allSubscribers) {
|
|
74
|
+
client.send({
|
|
75
|
+
type: IPCMessageType.LOG_DATA,
|
|
76
|
+
id: requestId,
|
|
77
|
+
success: true,
|
|
78
|
+
data: { processName, ...data },
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async start() {
|
|
84
|
+
// Clean up existing socket
|
|
85
|
+
if (existsSync(SOCKET_PATH)) {
|
|
86
|
+
try {
|
|
87
|
+
unlinkSync(SOCKET_PATH);
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
// Ignore errors
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return new Promise((resolve, reject) => {
|
|
94
|
+
this.server = createServer((socket) => {
|
|
95
|
+
const client = new ClientConnection(socket);
|
|
96
|
+
this.clients.add(client);
|
|
97
|
+
client.onData(async (messages) => {
|
|
98
|
+
for (const request of messages) {
|
|
99
|
+
await this.handleRequest(request, client);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
client.onClose(() => {
|
|
103
|
+
this.clients.delete(client);
|
|
104
|
+
// Clean up log subscriptions
|
|
105
|
+
for (const [name] of this.logSubscribers) {
|
|
106
|
+
this.unsubscribeFromLogs(name, client);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
client.onError((err) => {
|
|
110
|
+
console.error('Client error:', err.message);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
this.server.on('error', (err) => {
|
|
114
|
+
reject(err);
|
|
115
|
+
});
|
|
116
|
+
this.server.listen(SOCKET_PATH, () => {
|
|
117
|
+
resolve();
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
async handleRequest(request, client) {
|
|
122
|
+
const handler = this.handlers.get(request.type);
|
|
123
|
+
if (!handler) {
|
|
124
|
+
client.send(createResponse(request.id, false, undefined, `Unknown command: ${request.type}`));
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
try {
|
|
128
|
+
const response = await handler(request, client);
|
|
129
|
+
client.send(response);
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
const message = err instanceof Error ? err.message : 'Unknown error';
|
|
133
|
+
client.send(createResponse(request.id, false, undefined, message));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
broadcast(message) {
|
|
137
|
+
for (const client of this.clients) {
|
|
138
|
+
client.send(message);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
stop() {
|
|
142
|
+
return new Promise((resolve) => {
|
|
143
|
+
for (const client of this.clients) {
|
|
144
|
+
client.close();
|
|
145
|
+
}
|
|
146
|
+
this.clients.clear();
|
|
147
|
+
if (this.server) {
|
|
148
|
+
this.server.close(() => {
|
|
149
|
+
if (existsSync(SOCKET_PATH)) {
|
|
150
|
+
try {
|
|
151
|
+
unlinkSync(SOCKET_PATH);
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
// Ignore
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
resolve();
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
resolve();
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=DaemonServer.js.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ProcessInfo } from '../types/index.js';
|
|
2
|
+
export interface UserProcessList {
|
|
3
|
+
user: string;
|
|
4
|
+
processes: ProcessInfo[];
|
|
5
|
+
error?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ListAllUsersResult {
|
|
8
|
+
users: UserProcessList[];
|
|
9
|
+
/** Informational messages (e.g., no sockets found) */
|
|
10
|
+
warnings: string[];
|
|
11
|
+
/** Users whose sockets couldn't be accessed (permission denied) */
|
|
12
|
+
inaccessibleUsers: string[];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Check if running with elevated privileges
|
|
16
|
+
*/
|
|
17
|
+
declare function isElevated(): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* List processes from all users on the system
|
|
20
|
+
* On Unix, requires elevated privileges (sudo) to see other users' processes
|
|
21
|
+
*/
|
|
22
|
+
export declare function listAllUsers(): Promise<ListAllUsersResult>;
|
|
23
|
+
/**
|
|
24
|
+
* Check if the current user has elevated privileges
|
|
25
|
+
*/
|
|
26
|
+
export { isElevated };
|
|
27
|
+
//# sourceMappingURL=MultiUserClient.d.ts.map
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import { readdirSync } from 'node:fs';
|
|
3
|
+
import { connect } from 'node:net';
|
|
4
|
+
import { platform } from 'node:os';
|
|
5
|
+
import { IPC_CONNECT_TIMEOUT, IPC_RESPONSE_TIMEOUT, IPCMessageType } from '../constants.js';
|
|
6
|
+
import { createMessageParser, createRequest, serialize } from './protocol.js';
|
|
7
|
+
/**
|
|
8
|
+
* Check if running with elevated privileges
|
|
9
|
+
*/
|
|
10
|
+
function isElevated() {
|
|
11
|
+
if (platform() === 'win32') {
|
|
12
|
+
// On Windows, check if we can access a protected location
|
|
13
|
+
try {
|
|
14
|
+
execSync('net session', { stdio: 'ignore' });
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
// Unix: check if running as root
|
|
22
|
+
return process.getuid?.() === 0;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Extract username from socket path
|
|
26
|
+
*/
|
|
27
|
+
function extractUserFromSocketPath(socketPath) {
|
|
28
|
+
// Unix: /home/username/.orkify/orkify.sock or /Users/username/.orkify/orkify.sock
|
|
29
|
+
// Also handles /root/.orkify/orkify.sock
|
|
30
|
+
const match = socketPath.match(/^\/(?:home|Users|root)\/([^/]+)\/\.orkify\/orkify\.sock$/);
|
|
31
|
+
if (match) {
|
|
32
|
+
return match[1];
|
|
33
|
+
}
|
|
34
|
+
// Handle /root specifically
|
|
35
|
+
if (socketPath === '/root/.orkify/orkify.sock') {
|
|
36
|
+
return 'root';
|
|
37
|
+
}
|
|
38
|
+
// Fallback: use directory name
|
|
39
|
+
const parts = socketPath.split('/');
|
|
40
|
+
const orkifyIndex = parts.indexOf('.orkify');
|
|
41
|
+
if (orkifyIndex > 0) {
|
|
42
|
+
return parts[orkifyIndex - 1];
|
|
43
|
+
}
|
|
44
|
+
return 'unknown';
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Discover orkify sockets on Unix using lsof
|
|
48
|
+
* Requires elevated privileges to see other users' sockets
|
|
49
|
+
*/
|
|
50
|
+
function discoverUnixSockets() {
|
|
51
|
+
try {
|
|
52
|
+
// lsof -U lists all Unix domain sockets
|
|
53
|
+
// We filter for orkify.sock
|
|
54
|
+
const output = execSync('lsof -U 2>/dev/null', {
|
|
55
|
+
encoding: 'utf-8',
|
|
56
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
57
|
+
});
|
|
58
|
+
const sockets = [];
|
|
59
|
+
const seen = new Set();
|
|
60
|
+
for (const line of output.split('\n')) {
|
|
61
|
+
// Look for lines containing orkify.sock
|
|
62
|
+
if (line.includes('orkify.sock')) {
|
|
63
|
+
// lsof output format varies, but socket path is typically at the end
|
|
64
|
+
const match = line.match(/(\/\S+orkify\.sock)/);
|
|
65
|
+
if (match && !seen.has(match[1])) {
|
|
66
|
+
seen.add(match[1]);
|
|
67
|
+
sockets.push({
|
|
68
|
+
user: extractUserFromSocketPath(match[1]),
|
|
69
|
+
socketPath: match[1],
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return sockets;
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return [];
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Discover orkify named pipes on Windows
|
|
82
|
+
*/
|
|
83
|
+
function discoverWindowsPipes() {
|
|
84
|
+
try {
|
|
85
|
+
// Named pipes are enumerable in \\.\pipe\
|
|
86
|
+
const pipes = readdirSync('\\\\.\\pipe');
|
|
87
|
+
return pipes
|
|
88
|
+
.filter((name) => name.startsWith('orkify-'))
|
|
89
|
+
.map((name) => ({
|
|
90
|
+
user: name.replace('orkify-', ''),
|
|
91
|
+
socketPath: `\\\\.\\pipe\\${name}`,
|
|
92
|
+
}));
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return [];
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Connect to a socket and get the process list
|
|
100
|
+
*/
|
|
101
|
+
async function getProcessListFromSocket(socketPath) {
|
|
102
|
+
return new Promise((resolve) => {
|
|
103
|
+
const timeout = setTimeout(() => {
|
|
104
|
+
socket.destroy();
|
|
105
|
+
resolve({ error: 'Connection timeout' });
|
|
106
|
+
}, IPC_CONNECT_TIMEOUT);
|
|
107
|
+
const socket = connect(socketPath);
|
|
108
|
+
const messageParser = createMessageParser();
|
|
109
|
+
socket.on('connect', () => {
|
|
110
|
+
clearTimeout(timeout);
|
|
111
|
+
const responseTimeout = setTimeout(() => {
|
|
112
|
+
socket.destroy();
|
|
113
|
+
resolve({ error: 'Response timeout' });
|
|
114
|
+
}, IPC_RESPONSE_TIMEOUT);
|
|
115
|
+
const request = createRequest(IPCMessageType.LIST);
|
|
116
|
+
socket.write(serialize(request));
|
|
117
|
+
socket.on('data', (chunk) => {
|
|
118
|
+
const messages = messageParser(chunk);
|
|
119
|
+
for (const message of messages) {
|
|
120
|
+
clearTimeout(responseTimeout);
|
|
121
|
+
const response = message;
|
|
122
|
+
socket.end();
|
|
123
|
+
if (response.success) {
|
|
124
|
+
resolve({ processes: response.data });
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
resolve({ error: response.error || 'Unknown error' });
|
|
128
|
+
}
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
socket.on('error', (err) => {
|
|
134
|
+
clearTimeout(timeout);
|
|
135
|
+
socket.destroy();
|
|
136
|
+
const errorCode = err.code;
|
|
137
|
+
if (errorCode === 'EACCES' || errorCode === 'EPERM') {
|
|
138
|
+
resolve({ error: 'Permission denied' });
|
|
139
|
+
}
|
|
140
|
+
else if (errorCode === 'ECONNREFUSED') {
|
|
141
|
+
resolve({ error: 'Daemon not running' });
|
|
142
|
+
}
|
|
143
|
+
else if (errorCode === 'ENOENT') {
|
|
144
|
+
resolve({ error: 'Socket not found' });
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
resolve({ error: err.message });
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* List processes from all users on the system
|
|
154
|
+
* On Unix, requires elevated privileges (sudo) to see other users' processes
|
|
155
|
+
*/
|
|
156
|
+
export async function listAllUsers() {
|
|
157
|
+
const os = platform();
|
|
158
|
+
const users = [];
|
|
159
|
+
const warnings = [];
|
|
160
|
+
const inaccessibleUsers = [];
|
|
161
|
+
// Check elevation on Unix
|
|
162
|
+
if (os !== 'win32' && !isElevated()) {
|
|
163
|
+
return {
|
|
164
|
+
users: [],
|
|
165
|
+
warnings: [],
|
|
166
|
+
inaccessibleUsers: [],
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
// Discover sockets
|
|
170
|
+
const sockets = os === 'win32' ? discoverWindowsPipes() : discoverUnixSockets();
|
|
171
|
+
if (sockets.length === 0) {
|
|
172
|
+
warnings.push('No orkify processes found on this system');
|
|
173
|
+
return { users, warnings, inaccessibleUsers };
|
|
174
|
+
}
|
|
175
|
+
// Connect to each socket and get process list
|
|
176
|
+
for (const { user, socketPath } of sockets) {
|
|
177
|
+
const result = await getProcessListFromSocket(socketPath);
|
|
178
|
+
if ('error' in result) {
|
|
179
|
+
if (result.error === 'Permission denied') {
|
|
180
|
+
inaccessibleUsers.push(user);
|
|
181
|
+
}
|
|
182
|
+
else if (result.error !== 'Socket not found' && result.error !== 'Daemon not running') {
|
|
183
|
+
warnings.push(`Cannot access ${user}'s processes: ${result.error}`);
|
|
184
|
+
}
|
|
185
|
+
// Skip users with no running daemon (not an error)
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
users.push({
|
|
189
|
+
user,
|
|
190
|
+
processes: result.processes,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (users.length === 0 && warnings.length === 0 && inaccessibleUsers.length === 0) {
|
|
195
|
+
warnings.push('No orkify daemons running on this system');
|
|
196
|
+
}
|
|
197
|
+
return { users, warnings, inaccessibleUsers };
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Check if the current user has elevated privileges
|
|
201
|
+
*/
|
|
202
|
+
export { isElevated };
|
|
203
|
+
//# sourceMappingURL=MultiUserClient.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { IPCMessage, IPCRequest, IPCResponse } from '../types/index.js';
|
|
2
|
+
import { IPCMessageType } from '../constants.js';
|
|
3
|
+
export declare function createRequest(type: (typeof IPCMessageType)[keyof typeof IPCMessageType], payload?: IPCRequest['payload']): IPCRequest;
|
|
4
|
+
export declare function createResponse(requestId: string, success: boolean, data?: unknown, error?: string): IPCResponse;
|
|
5
|
+
export declare function serialize(message: IPCMessage): string;
|
|
6
|
+
export declare function createMessageParser(): (chunk: Buffer) => IPCMessage[];
|
|
7
|
+
//# sourceMappingURL=protocol.d.ts.map
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { IPCMessageType } from '../constants.js';
|
|
3
|
+
const DELIMITER = '\n';
|
|
4
|
+
export function createRequest(type, payload) {
|
|
5
|
+
return {
|
|
6
|
+
type,
|
|
7
|
+
id: randomUUID(),
|
|
8
|
+
payload,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export function createResponse(requestId, success, data, error) {
|
|
12
|
+
return {
|
|
13
|
+
type: success ? IPCMessageType.SUCCESS : IPCMessageType.ERROR,
|
|
14
|
+
id: requestId,
|
|
15
|
+
success,
|
|
16
|
+
data,
|
|
17
|
+
error,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export function serialize(message) {
|
|
21
|
+
return JSON.stringify(message) + DELIMITER;
|
|
22
|
+
}
|
|
23
|
+
// Max buffer size: 10MB. Prevents unbounded memory growth from
|
|
24
|
+
// malformed data that never includes a newline delimiter.
|
|
25
|
+
const MAX_BUFFER_SIZE = 10 * 1024 * 1024;
|
|
26
|
+
export function createMessageParser() {
|
|
27
|
+
let buffer = '';
|
|
28
|
+
return (chunk) => {
|
|
29
|
+
buffer += chunk.toString();
|
|
30
|
+
// Guard against unbounded buffer growth
|
|
31
|
+
if (buffer.length >= MAX_BUFFER_SIZE) {
|
|
32
|
+
console.error(`IPC message buffer exceeded ${MAX_BUFFER_SIZE} bytes, discarding`);
|
|
33
|
+
buffer = '';
|
|
34
|
+
throw new Error('IPC message buffer overflow');
|
|
35
|
+
}
|
|
36
|
+
const messages = [];
|
|
37
|
+
let delimiterIndex;
|
|
38
|
+
while ((delimiterIndex = buffer.indexOf(DELIMITER)) !== -1) {
|
|
39
|
+
const messageStr = buffer.slice(0, delimiterIndex);
|
|
40
|
+
buffer = buffer.slice(delimiterIndex + 1);
|
|
41
|
+
if (messageStr.trim()) {
|
|
42
|
+
try {
|
|
43
|
+
messages.push(JSON.parse(messageStr));
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
console.error('Failed to parse IPC message:', messageStr.slice(0, 200));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return messages;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=protocol.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { IPCResponse, ProcessConfig } from '../types/index.js';
|
|
2
|
+
import { DaemonClient } from './DaemonClient.js';
|
|
3
|
+
/**
|
|
4
|
+
* Wait for old daemon to exit, start a new one, and restore processes.
|
|
5
|
+
* Shared by daemon-reload command and crash-recovery script.
|
|
6
|
+
*/
|
|
7
|
+
export declare function restoreDaemon(client: DaemonClient, configs: ProcessConfig[], daemonEnv?: Record<string, string>): Promise<IPCResponse>;
|
|
8
|
+
//# sourceMappingURL=restoreDaemon.d.ts.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { DAEMON_PID_FILE, IPCMessageType } from '../constants.js';
|
|
3
|
+
/**
|
|
4
|
+
* Wait for old daemon to exit, start a new one, and restore processes.
|
|
5
|
+
* Shared by daemon-reload command and crash-recovery script.
|
|
6
|
+
*/
|
|
7
|
+
export async function restoreDaemon(client, configs, daemonEnv) {
|
|
8
|
+
if (daemonEnv)
|
|
9
|
+
client.setSpawnEnv(daemonEnv);
|
|
10
|
+
// Wait for old daemon to fully exit
|
|
11
|
+
const deadline = Date.now() + 10_000;
|
|
12
|
+
while (existsSync(DAEMON_PID_FILE) && Date.now() < deadline) {
|
|
13
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
14
|
+
}
|
|
15
|
+
// Start new daemon (auto-start on connect) and restore configs
|
|
16
|
+
const response = await client.request(IPCMessageType.RESTORE_CONFIGS, configs);
|
|
17
|
+
return response;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=restoreDaemon.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read a platform-specific machine identifier without elevated permissions.
|
|
3
|
+
* - Linux: /etc/machine-id (systemd, readable by all users)
|
|
4
|
+
* - macOS: IOPlatformUUID via ioreg
|
|
5
|
+
* - Windows: MachineGuid from registry
|
|
6
|
+
* Returns null if the ID can't be read (containers, restricted environments).
|
|
7
|
+
*/
|
|
8
|
+
export declare function getMachineId(): null | string;
|
|
9
|
+
/** Reset cached value — for testing only */
|
|
10
|
+
export declare function _resetMachineIdCache(): void;
|
|
11
|
+
//# sourceMappingURL=machine-id.d.ts.map
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
let cached = null;
|
|
4
|
+
/**
|
|
5
|
+
* Read a platform-specific machine identifier without elevated permissions.
|
|
6
|
+
* - Linux: /etc/machine-id (systemd, readable by all users)
|
|
7
|
+
* - macOS: IOPlatformUUID via ioreg
|
|
8
|
+
* - Windows: MachineGuid from registry
|
|
9
|
+
* Returns null if the ID can't be read (containers, restricted environments).
|
|
10
|
+
*/
|
|
11
|
+
export function getMachineId() {
|
|
12
|
+
if (cached !== null)
|
|
13
|
+
return cached || null;
|
|
14
|
+
try {
|
|
15
|
+
const id = readMachineId();
|
|
16
|
+
cached = id ?? '';
|
|
17
|
+
return id;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
cached = '';
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function readMachineId() {
|
|
25
|
+
switch (process.platform) {
|
|
26
|
+
case 'linux': {
|
|
27
|
+
const id = readFileSync('/etc/machine-id', 'utf8').trim();
|
|
28
|
+
return id || null;
|
|
29
|
+
}
|
|
30
|
+
case 'darwin': {
|
|
31
|
+
const out = execFileSync('ioreg', ['-rd1', '-c', 'IOPlatformExpertDevice'], {
|
|
32
|
+
encoding: 'utf8',
|
|
33
|
+
timeout: 3000,
|
|
34
|
+
});
|
|
35
|
+
const match = out.match(/"IOPlatformUUID"\s*=\s*"([^"]+)"/);
|
|
36
|
+
return match?.[1] ?? null;
|
|
37
|
+
}
|
|
38
|
+
case 'win32': {
|
|
39
|
+
const out = execFileSync('reg', ['query', 'HKLM\\SOFTWARE\\Microsoft\\Cryptography', '/v', 'MachineGuid'], { encoding: 'utf8', timeout: 3000 });
|
|
40
|
+
const match = out.match(/MachineGuid\s+REG_SZ\s+(\S+)/);
|
|
41
|
+
return match?.[1] ?? null;
|
|
42
|
+
}
|
|
43
|
+
default:
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** Reset cached value — for testing only */
|
|
48
|
+
export function _resetMachineIdCache() {
|
|
49
|
+
cached = null;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=machine-id.js.map
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import type { OAuthTokenVerifier } from '@modelcontextprotocol/sdk/server/auth/provider.js';
|
|
2
|
+
import type { AuthInfo } from '@modelcontextprotocol/sdk/server/auth/types.js';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import type { ConfigStore } from '../config/ConfigStore.js';
|
|
5
|
+
/**
|
|
6
|
+
* Valid MCP tool names — kept in sync with tools registered in server.ts.
|
|
7
|
+
*/
|
|
8
|
+
export declare const TOOL_NAMES: readonly ["list", "logs", "snap", "listAllUsers", "up", "down", "restart", "reload", "delete", "restore", "kill"];
|
|
9
|
+
declare const mcpKeySchema: z.ZodObject<{
|
|
10
|
+
name: z.ZodString;
|
|
11
|
+
token: z.ZodString;
|
|
12
|
+
tools: z.ZodArray<z.ZodString, "many">;
|
|
13
|
+
allowedIps: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
14
|
+
}, "strip", z.ZodTypeAny, {
|
|
15
|
+
name: string;
|
|
16
|
+
token: string;
|
|
17
|
+
tools: string[];
|
|
18
|
+
allowedIps?: string[] | undefined;
|
|
19
|
+
}, {
|
|
20
|
+
name: string;
|
|
21
|
+
token: string;
|
|
22
|
+
tools: string[];
|
|
23
|
+
allowedIps?: string[] | undefined;
|
|
24
|
+
}>;
|
|
25
|
+
declare const mcpConfigSchema: z.ZodObject<{
|
|
26
|
+
keys: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
27
|
+
name: z.ZodString;
|
|
28
|
+
token: z.ZodString;
|
|
29
|
+
tools: z.ZodArray<z.ZodString, "many">;
|
|
30
|
+
allowedIps: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
31
|
+
}, "strip", z.ZodTypeAny, {
|
|
32
|
+
name: string;
|
|
33
|
+
token: string;
|
|
34
|
+
tools: string[];
|
|
35
|
+
allowedIps?: string[] | undefined;
|
|
36
|
+
}, {
|
|
37
|
+
name: string;
|
|
38
|
+
token: string;
|
|
39
|
+
tools: string[];
|
|
40
|
+
allowedIps?: string[] | undefined;
|
|
41
|
+
}>, "many">>;
|
|
42
|
+
}, "strip", z.ZodTypeAny, {
|
|
43
|
+
keys: {
|
|
44
|
+
name: string;
|
|
45
|
+
token: string;
|
|
46
|
+
tools: string[];
|
|
47
|
+
allowedIps?: string[] | undefined;
|
|
48
|
+
}[];
|
|
49
|
+
}, {
|
|
50
|
+
keys?: {
|
|
51
|
+
name: string;
|
|
52
|
+
token: string;
|
|
53
|
+
tools: string[];
|
|
54
|
+
allowedIps?: string[] | undefined;
|
|
55
|
+
}[] | undefined;
|
|
56
|
+
}>;
|
|
57
|
+
export type McpConfig = z.infer<typeof mcpConfigSchema>;
|
|
58
|
+
export type McpKey = z.infer<typeof mcpKeySchema>;
|
|
59
|
+
/**
|
|
60
|
+
* Load and validate the MCP config from ~/.orkify/mcp.yml.
|
|
61
|
+
* Returns cached version if available; cache is invalidated on SIGHUP or file change.
|
|
62
|
+
*/
|
|
63
|
+
export declare function loadMcpConfig(configPath?: string): McpConfig;
|
|
64
|
+
/**
|
|
65
|
+
* Start watching the config file for changes and listen for SIGHUP to reload.
|
|
66
|
+
* Called once when the HTTP server starts.
|
|
67
|
+
*/
|
|
68
|
+
export declare function startConfigWatcher(): void;
|
|
69
|
+
/**
|
|
70
|
+
* Token verifier that reads keys from the local YAML config.
|
|
71
|
+
* Implements the MCP SDK's OAuthTokenVerifier interface.
|
|
72
|
+
*/
|
|
73
|
+
export declare class LocalConfigVerifier implements OAuthTokenVerifier {
|
|
74
|
+
private configPath;
|
|
75
|
+
constructor(configPath?: string);
|
|
76
|
+
verifyAccessToken(token: string): Promise<AuthInfo>;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Token verifier that reads keys from the remote ProjectConfig via ConfigStore.
|
|
80
|
+
* Matches tokens by SHA-256 hash (the dashboard stores hashes, not raw tokens).
|
|
81
|
+
*/
|
|
82
|
+
export declare class RemoteConfigVerifier implements OAuthTokenVerifier {
|
|
83
|
+
private configStore;
|
|
84
|
+
constructor(configStore: ConfigStore);
|
|
85
|
+
verifyAccessToken(token: string): Promise<AuthInfo>;
|
|
86
|
+
getAllowedIpsForToken(token: string): string[] | undefined;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Generate a new MCP token: prefix + 48 hex chars (24 random bytes).
|
|
90
|
+
*/
|
|
91
|
+
export declare function generateToken(): string;
|
|
92
|
+
/**
|
|
93
|
+
* Append a new key to the MCP config file.
|
|
94
|
+
* Creates the file with 0o600 permissions if it doesn't exist.
|
|
95
|
+
*/
|
|
96
|
+
export declare function appendKeyToConfig(key: McpKey, configPath?: string): void;
|
|
97
|
+
/**
|
|
98
|
+
* Warn to stderr if the MCP config file has permissions more open than 0600.
|
|
99
|
+
* Skipped on Windows where Unix mode bits don't apply.
|
|
100
|
+
*/
|
|
101
|
+
export declare function warnIfConfigInsecure(configPath?: string): void;
|
|
102
|
+
/**
|
|
103
|
+
* Strip the `::ffff:` prefix from IPv4-mapped IPv6 addresses.
|
|
104
|
+
* Express may report `::ffff:127.0.0.1` for IPv4 clients.
|
|
105
|
+
*/
|
|
106
|
+
export declare function normalizeIp(ip: string): string;
|
|
107
|
+
/**
|
|
108
|
+
* Check if a client IP is allowed by the key's `allowedIps` list.
|
|
109
|
+
* Returns `true` if `allowedIps` is absent or empty (all IPs allowed).
|
|
110
|
+
* Uses Node.js `BlockList` as an allowlist — `check()` returns `true` for listed IPs.
|
|
111
|
+
*/
|
|
112
|
+
export declare function isIpAllowed(clientIp: string, allowedIps?: string[]): boolean;
|
|
113
|
+
/**
|
|
114
|
+
* Look up a key by name from the config.
|
|
115
|
+
*/
|
|
116
|
+
export declare function findKeyByName(name: string, configPath?: string): McpKey | undefined;
|
|
117
|
+
export {};
|
|
118
|
+
//# sourceMappingURL=auth.d.ts.map
|