pidnap 0.0.0-dev.5 → 0.0.0-dev.7
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/dist/cli.mjs +337 -446
- package/dist/cli.mjs.map +1 -1
- package/dist/client.d.mts +2 -1
- package/dist/client.d.mts.map +1 -1
- package/dist/client.mjs.map +1 -1
- package/dist/{logger-BU2RmetS.mjs → env-manager-FKhvfwIR.mjs} +117 -93
- package/dist/env-manager-FKhvfwIR.mjs.map +1 -0
- package/dist/index.d.mts +2 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +3 -2
- package/src/api/client.ts +2 -0
- package/src/cli.ts +312 -423
- package/src/env-manager.ts +43 -7
- package/src/manager.ts +13 -5
- package/dist/logger-BU2RmetS.mjs.map +0 -1
package/src/cli.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { Command } from "commander";
|
|
3
2
|
import { createServer } from "node:http";
|
|
4
3
|
import { pathToFileURL } from "node:url";
|
|
5
4
|
import { resolve } from "node:path";
|
|
@@ -10,453 +9,343 @@ import { router } from "./api/server.ts";
|
|
|
10
9
|
import { Manager, ManagerConfig } from "./manager.ts";
|
|
11
10
|
import { logger } from "./logger.ts";
|
|
12
11
|
import Table from "cli-table3";
|
|
13
|
-
import { createClient } from "./api/client.ts";
|
|
12
|
+
import { createClient, type Client } from "./api/client.ts";
|
|
14
13
|
import { ProcessDefinition } from "./lazy-process.ts";
|
|
15
14
|
import { tImport } from "./utils.ts";
|
|
15
|
+
import { os as osBase } from "@orpc/server";
|
|
16
|
+
import { createCli, type TrpcCliMeta } from "trpc-cli";
|
|
17
|
+
import { format } from "node:util";
|
|
18
|
+
import pkg from "../package.json" assert { type: "json" };
|
|
16
19
|
|
|
17
|
-
const
|
|
20
|
+
const os = osBase.$context<{ client: Client }>().$meta<TrpcCliMeta>({});
|
|
18
21
|
|
|
19
|
-
|
|
20
|
-
.name("pidnap")
|
|
21
|
-
.description("Process manager with init system capabilities")
|
|
22
|
-
.version("0.0.0-dev.5");
|
|
22
|
+
const ResourceTarget = v.union([v.string(), v.number()]);
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
.
|
|
28
|
-
.
|
|
29
|
-
|
|
30
|
-
const initLogger = logger({ name: "pidnap" });
|
|
31
|
-
try {
|
|
32
|
-
// Resolve config file path
|
|
33
|
-
const configPath = resolve(process.cwd(), options.config);
|
|
34
|
-
const configUrl = pathToFileURL(configPath).href;
|
|
35
|
-
|
|
36
|
-
// Import the config file
|
|
37
|
-
const configModule = await tImport(configUrl);
|
|
38
|
-
const rawConfig =
|
|
39
|
-
configModule.default.default || configModule.default || configModule.config || {};
|
|
40
|
-
|
|
41
|
-
// Parse and validate the config with Valibot
|
|
42
|
-
const config = v.parse(ManagerConfig, rawConfig);
|
|
24
|
+
// Helper functions for table formatting (used multiple times)
|
|
25
|
+
function printProcessTable(proc: { name: string; state: string; restarts: number }) {
|
|
26
|
+
const table = new Table({ head: ["Name", "State", "Restarts"] });
|
|
27
|
+
table.push([proc.name, proc.state, proc.restarts]);
|
|
28
|
+
console.log(table.toString());
|
|
29
|
+
}
|
|
43
30
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
31
|
+
function printTaskTable(task: { id: string; state: string; processNames: string[] }) {
|
|
32
|
+
const table = new Table({ head: ["Id", "State", "Processes"] });
|
|
33
|
+
table.push([task.id, task.state, task.processNames.join(", ")]);
|
|
34
|
+
console.log(table.toString());
|
|
35
|
+
}
|
|
48
36
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
37
|
+
function printCronTable(cron: {
|
|
38
|
+
name: string;
|
|
39
|
+
state: string;
|
|
40
|
+
runCount: number;
|
|
41
|
+
failCount: number;
|
|
42
|
+
nextRun: string | null;
|
|
43
|
+
}) {
|
|
44
|
+
const table = new Table({ head: ["Name", "State", "Runs", "Fails", "Next Run"] });
|
|
45
|
+
table.push([cron.name, cron.state, cron.runCount, cron.failCount, cron.nextRun ?? "-"]);
|
|
46
|
+
console.log(table.toString());
|
|
47
|
+
}
|
|
53
48
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
49
|
+
const cliRouter = os.router({
|
|
50
|
+
init: osBase
|
|
51
|
+
.meta({
|
|
52
|
+
description: "Initialize and run the process manager with config file",
|
|
53
|
+
aliases: { options: { config: "c" } },
|
|
54
|
+
})
|
|
55
|
+
.input(
|
|
56
|
+
v.object({
|
|
57
|
+
config: v.optional(
|
|
58
|
+
v.pipe(v.string(), v.description("Path to config file")),
|
|
59
|
+
"pidnap.config.ts",
|
|
60
|
+
),
|
|
61
|
+
}),
|
|
62
|
+
)
|
|
63
|
+
.handler(async ({ input }) => {
|
|
64
|
+
process.title = "pidnap";
|
|
65
|
+
const initLogger = logger({ name: "pidnap" });
|
|
66
|
+
try {
|
|
67
|
+
// Resolve config file path
|
|
68
|
+
const configPath = resolve(process.cwd(), input.config);
|
|
69
|
+
const configUrl = pathToFileURL(configPath).href;
|
|
70
|
+
|
|
71
|
+
// Import the config file
|
|
72
|
+
const configModule = await tImport(configUrl);
|
|
73
|
+
const rawConfig =
|
|
74
|
+
configModule.default.default || configModule.default || configModule.config || {};
|
|
75
|
+
|
|
76
|
+
// Parse and validate the config with Valibot
|
|
77
|
+
const config = v.parse(ManagerConfig, rawConfig);
|
|
78
|
+
|
|
79
|
+
// Extract HTTP config with defaults
|
|
80
|
+
const host = config.http?.host ?? "127.0.0.1";
|
|
81
|
+
const port = config.http?.port ?? 9876;
|
|
82
|
+
const authToken = config.http?.authToken;
|
|
83
|
+
|
|
84
|
+
// Create manager with config
|
|
85
|
+
const logDir = config.logDir ?? resolve(process.cwd(), "logs");
|
|
86
|
+
const managerLogger = logger({ name: "pidnap", logFile: resolve(logDir, "pidnap.log") });
|
|
87
|
+
const manager = new Manager(config, managerLogger);
|
|
88
|
+
|
|
89
|
+
// Setup ORPC server with optional auth token middleware
|
|
90
|
+
const handler = new RPCHandler(router, {
|
|
91
|
+
interceptors: [
|
|
92
|
+
onError((error) => {
|
|
93
|
+
managerLogger.error(error);
|
|
94
|
+
}),
|
|
95
|
+
],
|
|
96
|
+
});
|
|
62
97
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
98
|
+
const server = createServer(async (req, res) => {
|
|
99
|
+
// Check auth token if configured
|
|
100
|
+
if (authToken) {
|
|
101
|
+
const providedToken = req.headers["authorization"]?.replace("Bearer ", "");
|
|
102
|
+
if (providedToken !== authToken) {
|
|
103
|
+
res.statusCode = 401;
|
|
104
|
+
res.end("Unauthorized");
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
71
107
|
}
|
|
72
|
-
}
|
|
73
108
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
109
|
+
const { matched } = await handler.handle(req, res, {
|
|
110
|
+
prefix: "/rpc",
|
|
111
|
+
context: { manager },
|
|
112
|
+
});
|
|
113
|
+
if (matched) return;
|
|
114
|
+
res.statusCode = 404;
|
|
115
|
+
res.end("Not found\n");
|
|
77
116
|
});
|
|
78
|
-
if (matched) return;
|
|
79
|
-
res.statusCode = 404;
|
|
80
|
-
res.end("Not found\n");
|
|
81
|
-
});
|
|
82
117
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
118
|
+
server.listen(port, host, async () => {
|
|
119
|
+
managerLogger.info(`pidnap RPC server running on http://${host}:${port}`);
|
|
120
|
+
if (authToken) {
|
|
121
|
+
managerLogger.info("Auth token required for API access");
|
|
122
|
+
}
|
|
88
123
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
124
|
+
try {
|
|
125
|
+
await manager.start();
|
|
126
|
+
} catch (err) {
|
|
127
|
+
managerLogger.error("Failed to start manager:", err);
|
|
128
|
+
server.close();
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
97
132
|
|
|
98
|
-
|
|
99
|
-
|
|
133
|
+
// Wait for shutdown
|
|
134
|
+
await manager.waitForShutdown();
|
|
100
135
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
136
|
+
// Close server on shutdown
|
|
137
|
+
server.close();
|
|
138
|
+
} catch (error) {
|
|
139
|
+
initLogger.error("Failed to start pidnap:", error);
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
}),
|
|
108
143
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
.option("-u, --url <url>", "RPC server URL")
|
|
113
|
-
.action(async (options) => {
|
|
114
|
-
try {
|
|
115
|
-
const client = createClient(options.url);
|
|
144
|
+
status: os
|
|
145
|
+
.meta({ description: "Show manager status" })
|
|
146
|
+
.handler(async ({ context: { client } }) => {
|
|
116
147
|
const status = await client.manager.status();
|
|
117
|
-
|
|
118
148
|
const table = new Table({ head: ["State", "Processes", "Crons", "Tasks"] });
|
|
119
149
|
table.push([status.state, status.processCount, status.cronCount, status.taskCount]);
|
|
120
150
|
console.log(table.toString());
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
}
|
|
222
|
-
console.error("Failed to stop process:", error);
|
|
223
|
-
process.exit(1);
|
|
224
|
-
}
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
processGroup
|
|
228
|
-
.command("restart")
|
|
229
|
-
.description("Restart a restarting process")
|
|
230
|
-
.argument("<target>", "Process name or index")
|
|
231
|
-
.option("-f, --force", "Force restart")
|
|
232
|
-
.option("-u, --url <url>", "RPC server URL")
|
|
233
|
-
.action(async (target, options) => {
|
|
234
|
-
try {
|
|
235
|
-
const client = createClient(options.url);
|
|
236
|
-
const proc = await client.processes.restart({
|
|
237
|
-
target: parseTarget(target),
|
|
238
|
-
force: options.force,
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
const table = new Table({ head: ["Name", "State", "Restarts"] });
|
|
242
|
-
table.push([proc.name, proc.state, proc.restarts]);
|
|
243
|
-
console.log(table.toString());
|
|
244
|
-
} catch (error) {
|
|
245
|
-
console.error("Failed to restart process:", error);
|
|
246
|
-
process.exit(1);
|
|
247
|
-
}
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
processGroup
|
|
251
|
-
.command("remove")
|
|
252
|
-
.description("Remove a restarting process")
|
|
253
|
-
.argument("<target>", "Process name or index")
|
|
254
|
-
.option("-u, --url <url>", "RPC server URL")
|
|
255
|
-
.action(async (target, options) => {
|
|
256
|
-
try {
|
|
257
|
-
const client = createClient(options.url);
|
|
258
|
-
await client.processes.remove({ target: parseTarget(target) });
|
|
259
|
-
console.log("Process removed");
|
|
260
|
-
} catch (error) {
|
|
261
|
-
console.error("Failed to remove process:", error);
|
|
262
|
-
process.exit(1);
|
|
263
|
-
}
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
const crons = program.command("crons").description("Manage cron processes");
|
|
267
|
-
|
|
268
|
-
crons
|
|
269
|
-
.command("list")
|
|
270
|
-
.description("List cron processes")
|
|
271
|
-
.option("-u, --url <url>", "RPC server URL")
|
|
272
|
-
.action(async (options) => {
|
|
273
|
-
try {
|
|
274
|
-
const client = createClient(options.url);
|
|
275
|
-
const crons = await client.crons.list();
|
|
276
|
-
|
|
277
|
-
const table = new Table({ head: ["Name", "State", "Runs", "Fails", "Next Run"] });
|
|
278
|
-
for (const cron of crons) {
|
|
279
|
-
table.push([cron.name, cron.state, cron.runCount, cron.failCount, cron.nextRun ?? "-"]);
|
|
280
|
-
}
|
|
281
|
-
console.log(table.toString());
|
|
282
|
-
} catch (error) {
|
|
283
|
-
console.error("Failed to list crons:", error);
|
|
284
|
-
process.exit(1);
|
|
285
|
-
}
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
crons
|
|
289
|
-
.command("get")
|
|
290
|
-
.description("Get a cron process by name or index")
|
|
291
|
-
.argument("<target>", "Cron name or index")
|
|
292
|
-
.option("-u, --url <url>", "RPC server URL")
|
|
293
|
-
.action(async (target, options) => {
|
|
294
|
-
try {
|
|
295
|
-
const client = createClient(options.url);
|
|
296
|
-
const cron = await client.crons.get({ target: parseTarget(target) });
|
|
297
|
-
|
|
298
|
-
const table = new Table({ head: ["Name", "State", "Runs", "Fails", "Next Run"] });
|
|
299
|
-
table.push([cron.name, cron.state, cron.runCount, cron.failCount, cron.nextRun ?? "-"]);
|
|
300
|
-
console.log(table.toString());
|
|
301
|
-
} catch (error) {
|
|
302
|
-
console.error("Failed to get cron:", error);
|
|
303
|
-
process.exit(1);
|
|
304
|
-
}
|
|
305
|
-
});
|
|
306
|
-
|
|
307
|
-
crons
|
|
308
|
-
.command("trigger")
|
|
309
|
-
.description("Trigger a cron process")
|
|
310
|
-
.argument("<target>", "Cron name or index")
|
|
311
|
-
.option("-u, --url <url>", "RPC server URL")
|
|
312
|
-
.action(async (target, options) => {
|
|
313
|
-
try {
|
|
314
|
-
const client = createClient(options.url);
|
|
315
|
-
const cron = await client.crons.trigger({ target: parseTarget(target) });
|
|
316
|
-
|
|
317
|
-
const table = new Table({ head: ["Name", "State", "Runs", "Fails", "Next Run"] });
|
|
318
|
-
table.push([cron.name, cron.state, cron.runCount, cron.failCount, cron.nextRun ?? "-"]);
|
|
319
|
-
console.log(table.toString());
|
|
320
|
-
} catch (error) {
|
|
321
|
-
console.error("Failed to trigger cron:", error);
|
|
322
|
-
process.exit(1);
|
|
323
|
-
}
|
|
324
|
-
});
|
|
325
|
-
|
|
326
|
-
crons
|
|
327
|
-
.command("start")
|
|
328
|
-
.description("Start a cron process")
|
|
329
|
-
.argument("<target>", "Cron name or index")
|
|
330
|
-
.option("-u, --url <url>", "RPC server URL")
|
|
331
|
-
.action(async (target, options) => {
|
|
332
|
-
try {
|
|
333
|
-
const client = createClient(options.url);
|
|
334
|
-
const cron = await client.crons.start({ target: parseTarget(target) });
|
|
335
|
-
|
|
336
|
-
const table = new Table({ head: ["Name", "State", "Runs", "Fails", "Next Run"] });
|
|
337
|
-
table.push([cron.name, cron.state, cron.runCount, cron.failCount, cron.nextRun ?? "-"]);
|
|
338
|
-
console.log(table.toString());
|
|
339
|
-
} catch (error) {
|
|
340
|
-
console.error("Failed to start cron:", error);
|
|
341
|
-
process.exit(1);
|
|
342
|
-
}
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
crons
|
|
346
|
-
.command("stop")
|
|
347
|
-
.description("Stop a cron process")
|
|
348
|
-
.argument("<target>", "Cron name or index")
|
|
349
|
-
.option("-u, --url <url>", "RPC server URL")
|
|
350
|
-
.action(async (target, options) => {
|
|
351
|
-
try {
|
|
352
|
-
const client = createClient(options.url);
|
|
353
|
-
const cron = await client.crons.stop({ target: parseTarget(target) });
|
|
354
|
-
|
|
355
|
-
const table = new Table({ head: ["Name", "State", "Runs", "Fails", "Next Run"] });
|
|
356
|
-
table.push([cron.name, cron.state, cron.runCount, cron.failCount, cron.nextRun ?? "-"]);
|
|
357
|
-
console.log(table.toString());
|
|
358
|
-
} catch (error) {
|
|
359
|
-
console.error("Failed to stop cron:", error);
|
|
360
|
-
process.exit(1);
|
|
361
|
-
}
|
|
362
|
-
});
|
|
363
|
-
|
|
364
|
-
const tasks = program.command("tasks").description("Manage tasks");
|
|
365
|
-
|
|
366
|
-
tasks
|
|
367
|
-
.command("list")
|
|
368
|
-
.description("List tasks")
|
|
369
|
-
.option("-u, --url <url>", "RPC server URL")
|
|
370
|
-
.action(async (options) => {
|
|
371
|
-
try {
|
|
372
|
-
const client = createClient(options.url);
|
|
151
|
+
}),
|
|
152
|
+
|
|
153
|
+
process: os.router({
|
|
154
|
+
list: os
|
|
155
|
+
.meta({ description: "List restarting processes" })
|
|
156
|
+
.handler(async ({ context: { client } }) => {
|
|
157
|
+
const processes = await client.processes.list();
|
|
158
|
+
const table = new Table({ head: ["Name", "State", "Restarts"] });
|
|
159
|
+
for (const proc of processes) {
|
|
160
|
+
table.push([proc.name, proc.state, proc.restarts]);
|
|
161
|
+
}
|
|
162
|
+
console.log(table.toString());
|
|
163
|
+
}),
|
|
164
|
+
get: os
|
|
165
|
+
.meta({
|
|
166
|
+
description: "Get a restarting process by name or index",
|
|
167
|
+
aliases: { options: { target: "t" } },
|
|
168
|
+
})
|
|
169
|
+
.input(v.object({ target: v.pipe(ResourceTarget, v.description("Process name or index")) }))
|
|
170
|
+
.handler(async ({ input, context: { client } }) => {
|
|
171
|
+
const proc = await client.processes.get({ target: input.target });
|
|
172
|
+
printProcessTable(proc);
|
|
173
|
+
}),
|
|
174
|
+
add: os
|
|
175
|
+
.meta({
|
|
176
|
+
description: "Add a restarting process",
|
|
177
|
+
aliases: { options: { name: "n", definition: "d" } },
|
|
178
|
+
})
|
|
179
|
+
.input(
|
|
180
|
+
v.object({
|
|
181
|
+
name: v.pipe(v.string(), v.description("Process name")),
|
|
182
|
+
definition: v.pipe(ProcessDefinition, v.description("Process definition JSON")),
|
|
183
|
+
}),
|
|
184
|
+
)
|
|
185
|
+
.handler(async ({ input, context: { client } }) => {
|
|
186
|
+
const proc = await client.processes.add({ name: input.name, definition: input.definition });
|
|
187
|
+
printProcessTable(proc);
|
|
188
|
+
}),
|
|
189
|
+
start: os
|
|
190
|
+
.meta({ description: "Start a restarting process", aliases: { options: { target: "t" } } })
|
|
191
|
+
.input(v.object({ target: v.pipe(ResourceTarget, v.description("Process name or index")) }))
|
|
192
|
+
.handler(async ({ input, context: { client } }) => {
|
|
193
|
+
const proc = await client.processes.start({ target: input.target });
|
|
194
|
+
printProcessTable(proc);
|
|
195
|
+
}),
|
|
196
|
+
stop: os
|
|
197
|
+
.meta({ description: "Stop a restarting process", aliases: { options: { target: "t" } } })
|
|
198
|
+
.input(v.object({ target: v.pipe(ResourceTarget, v.description("Process name or index")) }))
|
|
199
|
+
.handler(async ({ input, context: { client } }) => {
|
|
200
|
+
const proc = await client.processes.stop({ target: input.target });
|
|
201
|
+
printProcessTable(proc);
|
|
202
|
+
}),
|
|
203
|
+
restart: os
|
|
204
|
+
.meta({
|
|
205
|
+
description: "Restart a restarting process",
|
|
206
|
+
aliases: { options: { target: "t", force: "f" } },
|
|
207
|
+
})
|
|
208
|
+
.input(
|
|
209
|
+
v.object({
|
|
210
|
+
target: v.pipe(ResourceTarget, v.description("Process name or index")),
|
|
211
|
+
force: v.optional(v.pipe(v.boolean(), v.description("Force restart"))),
|
|
212
|
+
}),
|
|
213
|
+
)
|
|
214
|
+
.handler(async ({ input, context: { client } }) => {
|
|
215
|
+
const proc = await client.processes.restart({ target: input.target, force: input.force });
|
|
216
|
+
printProcessTable(proc);
|
|
217
|
+
}),
|
|
218
|
+
reload: os
|
|
219
|
+
.meta({
|
|
220
|
+
description: "Reload a restarting process definition",
|
|
221
|
+
aliases: { options: { target: "t", definition: "d", restartImmediately: "r" } },
|
|
222
|
+
})
|
|
223
|
+
.input(
|
|
224
|
+
v.object({
|
|
225
|
+
target: v.pipe(ResourceTarget, v.description("Process name or index")),
|
|
226
|
+
definition: v.pipe(ProcessDefinition, v.description("Process definition JSON")),
|
|
227
|
+
restartImmediately: v.optional(
|
|
228
|
+
v.pipe(v.boolean(), v.description("Restart immediately after reload")),
|
|
229
|
+
),
|
|
230
|
+
}),
|
|
231
|
+
)
|
|
232
|
+
.handler(async ({ input, context: { client } }) => {
|
|
233
|
+
const proc = await client.processes.reload({
|
|
234
|
+
target: input.target,
|
|
235
|
+
definition: input.definition,
|
|
236
|
+
restartImmediately: input.restartImmediately,
|
|
237
|
+
});
|
|
238
|
+
printProcessTable(proc);
|
|
239
|
+
}),
|
|
240
|
+
remove: os
|
|
241
|
+
.meta({ description: "Remove a restarting process", aliases: { options: { target: "t" } } })
|
|
242
|
+
.input(v.object({ target: v.pipe(ResourceTarget, v.description("Process name or index")) }))
|
|
243
|
+
.handler(async ({ input, context: { client } }) => {
|
|
244
|
+
await client.processes.remove({ target: input.target });
|
|
245
|
+
console.log("Process removed");
|
|
246
|
+
}),
|
|
247
|
+
}),
|
|
248
|
+
|
|
249
|
+
// Tasks
|
|
250
|
+
tasks: os.router({
|
|
251
|
+
list: os.meta({ description: "List tasks" }).handler(async ({ context: { client } }) => {
|
|
373
252
|
const tasks = await client.tasks.list();
|
|
374
|
-
|
|
375
253
|
const table = new Table({ head: ["Id", "State", "Processes"] });
|
|
376
254
|
for (const task of tasks) {
|
|
377
255
|
table.push([task.id, task.state, task.processNames.join(", ")]);
|
|
378
256
|
}
|
|
379
257
|
console.log(table.toString());
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
.
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
258
|
+
}),
|
|
259
|
+
get: os
|
|
260
|
+
.meta({ description: "Get a task by id or index", aliases: { options: { target: "t" } } })
|
|
261
|
+
.input(v.object({ target: v.pipe(ResourceTarget, v.description("Task id or index")) }))
|
|
262
|
+
.handler(async ({ input, context: { client } }) => {
|
|
263
|
+
const task = await client.tasks.get({ target: input.target });
|
|
264
|
+
printTaskTable(task);
|
|
265
|
+
}),
|
|
266
|
+
add: os
|
|
267
|
+
.meta({ description: "Add a task", aliases: { options: { name: "n", definition: "d" } } })
|
|
268
|
+
.input(
|
|
269
|
+
v.object({
|
|
270
|
+
name: v.pipe(v.string(), v.description("Task name")),
|
|
271
|
+
definition: v.pipe(ProcessDefinition, v.description("Process definition JSON")),
|
|
272
|
+
}),
|
|
273
|
+
)
|
|
274
|
+
.handler(async ({ input, context: { client } }) => {
|
|
275
|
+
const task = await client.tasks.add({ name: input.name, definition: input.definition });
|
|
276
|
+
printTaskTable(task);
|
|
277
|
+
}),
|
|
278
|
+
remove: os
|
|
279
|
+
.meta({ description: "Remove a task by id or index", aliases: { options: { target: "t" } } })
|
|
280
|
+
.input(v.object({ target: v.pipe(ResourceTarget, v.description("Task id or index")) }))
|
|
281
|
+
.handler(async ({ input, context: { client } }) => {
|
|
282
|
+
const task = await client.tasks.remove({ target: input.target });
|
|
283
|
+
printTaskTable(task);
|
|
284
|
+
}),
|
|
285
|
+
}),
|
|
286
|
+
|
|
287
|
+
// Crons
|
|
288
|
+
crons: os.router({
|
|
289
|
+
list: os
|
|
290
|
+
.meta({ description: "List cron processes" })
|
|
291
|
+
.handler(async ({ context: { client } }) => {
|
|
292
|
+
const crons = await client.crons.list();
|
|
293
|
+
const table = new Table({ head: ["Name", "State", "Runs", "Fails", "Next Run"] });
|
|
294
|
+
for (const cron of crons) {
|
|
295
|
+
table.push([cron.name, cron.state, cron.runCount, cron.failCount, cron.nextRun ?? "-"]);
|
|
296
|
+
}
|
|
297
|
+
console.log(table.toString());
|
|
298
|
+
}),
|
|
299
|
+
get: os
|
|
300
|
+
.meta({
|
|
301
|
+
description: "Get a cron process by name or index",
|
|
302
|
+
aliases: { options: { target: "t" } },
|
|
303
|
+
})
|
|
304
|
+
.input(v.object({ target: v.pipe(ResourceTarget, v.description("Cron name or index")) }))
|
|
305
|
+
.handler(async ({ input, context: { client } }) => {
|
|
306
|
+
const cron = await client.crons.get({ target: input.target });
|
|
307
|
+
printCronTable(cron);
|
|
308
|
+
}),
|
|
309
|
+
trigger: os
|
|
310
|
+
.meta({ description: "Trigger a cron process", aliases: { options: { target: "t" } } })
|
|
311
|
+
.input(v.object({ target: v.pipe(ResourceTarget, v.description("Cron name or index")) }))
|
|
312
|
+
.handler(async ({ input, context: { client } }) => {
|
|
313
|
+
const cron = await client.crons.trigger({ target: input.target });
|
|
314
|
+
printCronTable(cron);
|
|
315
|
+
}),
|
|
316
|
+
start: os
|
|
317
|
+
.meta({ description: "Start a cron process", aliases: { options: { target: "t" } } })
|
|
318
|
+
.input(v.object({ target: v.pipe(ResourceTarget, v.description("Cron name or index")) }))
|
|
319
|
+
.handler(async ({ input, context: { client } }) => {
|
|
320
|
+
const cron = await client.crons.start({ target: input.target });
|
|
321
|
+
printCronTable(cron);
|
|
322
|
+
}),
|
|
323
|
+
stop: os
|
|
324
|
+
.meta({ description: "Stop a cron process", aliases: { options: { target: "t" } } })
|
|
325
|
+
.input(v.object({ target: v.pipe(ResourceTarget, v.description("Cron name or index")) }))
|
|
326
|
+
.handler(async ({ input, context: { client } }) => {
|
|
327
|
+
const cron = await client.crons.stop({ target: input.target });
|
|
328
|
+
printCronTable(cron);
|
|
329
|
+
}),
|
|
330
|
+
}),
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
const client = createClient();
|
|
334
|
+
const cli = createCli({
|
|
335
|
+
name: "pidnap",
|
|
336
|
+
version: pkg.version,
|
|
337
|
+
router: cliRouter,
|
|
338
|
+
context: { client },
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
cli.run({
|
|
342
|
+
formatError(error: any) {
|
|
343
|
+
if (error?.cause?.code === "ECONNREFUSED") {
|
|
344
|
+
return (
|
|
345
|
+
`Failed to connect to RPC server, are you sure the server is running? If the Server is running of different url, use PIDNAP_RPC_URL environment variable to set it.\n` +
|
|
346
|
+
format(error)
|
|
347
|
+
);
|
|
442
348
|
}
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
function parseTarget(value: string): string | number {
|
|
448
|
-
const asNumber = Number(value);
|
|
449
|
-
return v.parse(TargetSchema, Number.isNaN(asNumber) ? value : asNumber);
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
function parseDefinition(raw: string): ProcessDefinition {
|
|
453
|
-
try {
|
|
454
|
-
const parsed = JSON.parse(raw);
|
|
455
|
-
return v.parse(ProcessDefinition, parsed);
|
|
456
|
-
} catch (error) {
|
|
457
|
-
console.error("Invalid --definition JSON. Expected a ProcessDefinition.");
|
|
458
|
-
throw error;
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
program.parse();
|
|
349
|
+
return format(error);
|
|
350
|
+
},
|
|
351
|
+
});
|