ensure-running 1.0.0
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 +21 -0
- package/README.md +355 -0
- package/dist/bin/ensure-running.js +959 -0
- package/dist/bin/ensure-running.js.map +1 -0
- package/dist/index.cjs +1184 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +356 -0
- package/dist/index.d.ts +356 -0
- package/dist/index.js +1112 -0
- package/dist/index.js.map +1 -0
- package/package.json +72 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1112 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __esm = (fn, res) => function __init() {
|
|
4
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
5
|
+
};
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// src/services/docker/utils/Exec.ts
|
|
12
|
+
import { execFile as nodeExecFile } from "child_process";
|
|
13
|
+
async function exec(command, args = []) {
|
|
14
|
+
return new Promise((resolve, reject) => {
|
|
15
|
+
nodeExecFile(
|
|
16
|
+
command,
|
|
17
|
+
args,
|
|
18
|
+
{
|
|
19
|
+
encoding: "utf8",
|
|
20
|
+
windowsHide: true
|
|
21
|
+
},
|
|
22
|
+
(err, stdout, stderr) => {
|
|
23
|
+
if (err) {
|
|
24
|
+
reject(err);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
resolve({
|
|
28
|
+
stdout: String(stdout ?? ""),
|
|
29
|
+
stderr: String(stderr ?? "")
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
async function execShell(shellCommand) {
|
|
36
|
+
const shell = process.platform === "win32" ? "cmd.exe" : "/bin/sh";
|
|
37
|
+
const shellFlag = process.platform === "win32" ? "/c" : "-c";
|
|
38
|
+
return exec(shell, [shellFlag, shellCommand]);
|
|
39
|
+
}
|
|
40
|
+
var init_Exec = __esm({
|
|
41
|
+
"src/services/docker/utils/Exec.ts"() {
|
|
42
|
+
"use strict";
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// src/services/docker/utils/Spawn.ts
|
|
47
|
+
import { spawn as nodeSpawn } from "child_process";
|
|
48
|
+
function spawn2(command, args = [], options = {}) {
|
|
49
|
+
return nodeSpawn(command, args, {
|
|
50
|
+
detached: options.detached ?? false,
|
|
51
|
+
stdio: options.stdio ?? "ignore",
|
|
52
|
+
windowsHide: options.windowsHide ?? true
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
function spawnDetached(command, args = []) {
|
|
56
|
+
const child = spawn2(command, args, {
|
|
57
|
+
detached: true,
|
|
58
|
+
stdio: "ignore"
|
|
59
|
+
});
|
|
60
|
+
child.unref();
|
|
61
|
+
return child;
|
|
62
|
+
}
|
|
63
|
+
var init_Spawn = __esm({
|
|
64
|
+
"src/services/docker/utils/Spawn.ts"() {
|
|
65
|
+
"use strict";
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// src/services/docker/utils/Sleep.ts
|
|
70
|
+
function sleep(ms) {
|
|
71
|
+
return new Promise((resolve) => {
|
|
72
|
+
setTimeout(resolve, ms);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
var init_Sleep = __esm({
|
|
76
|
+
"src/services/docker/utils/Sleep.ts"() {
|
|
77
|
+
"use strict";
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// src/services/docker/utils/Timeout.ts
|
|
82
|
+
async function withTimeout(promise, timeoutMs, message = "Operation timed out") {
|
|
83
|
+
let timer;
|
|
84
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
85
|
+
timer = setTimeout(() => {
|
|
86
|
+
reject(new Error(message));
|
|
87
|
+
}, timeoutMs);
|
|
88
|
+
});
|
|
89
|
+
try {
|
|
90
|
+
return await Promise.race([promise, timeoutPromise]);
|
|
91
|
+
} finally {
|
|
92
|
+
if (timer !== void 0) {
|
|
93
|
+
clearTimeout(timer);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
var init_Timeout = __esm({
|
|
98
|
+
"src/services/docker/utils/Timeout.ts"() {
|
|
99
|
+
"use strict";
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// src/services/docker/utils/Which.ts
|
|
104
|
+
async function which(command) {
|
|
105
|
+
try {
|
|
106
|
+
if (process.platform === "win32") {
|
|
107
|
+
const result = await exec("where.exe", [command]);
|
|
108
|
+
const firstLine = result.stdout.split(/\r?\n/).find((line) => line.trim().length > 0);
|
|
109
|
+
return firstLine?.trim();
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
const result = await exec("which", [command]);
|
|
113
|
+
const path3 = result.stdout.trim();
|
|
114
|
+
return path3.length > 0 ? path3 : void 0;
|
|
115
|
+
} catch {
|
|
116
|
+
const result = await execShell(`command -v ${command}`);
|
|
117
|
+
const path3 = result.stdout.trim();
|
|
118
|
+
return path3.length > 0 ? path3 : void 0;
|
|
119
|
+
}
|
|
120
|
+
} catch {
|
|
121
|
+
return void 0;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
async function commandExists(command) {
|
|
125
|
+
const path3 = await which(command);
|
|
126
|
+
return path3 !== void 0;
|
|
127
|
+
}
|
|
128
|
+
var init_Which = __esm({
|
|
129
|
+
"src/services/docker/utils/Which.ts"() {
|
|
130
|
+
"use strict";
|
|
131
|
+
init_Exec();
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// src/services/docker/utils/Poll.ts
|
|
136
|
+
async function poll(predicate, options) {
|
|
137
|
+
const deadline = Date.now() + options.timeout;
|
|
138
|
+
while (Date.now() < deadline) {
|
|
139
|
+
if (await predicate()) {
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
const remaining = deadline - Date.now();
|
|
143
|
+
if (remaining <= 0) {
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
await sleep(Math.min(options.interval, remaining));
|
|
147
|
+
}
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
var init_Poll = __esm({
|
|
151
|
+
"src/services/docker/utils/Poll.ts"() {
|
|
152
|
+
"use strict";
|
|
153
|
+
init_Sleep();
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// src/services/docker/utils/Retry.ts
|
|
158
|
+
async function retry(fn, options) {
|
|
159
|
+
let lastError;
|
|
160
|
+
for (let attempt = 1; attempt <= options.attempts; attempt++) {
|
|
161
|
+
try {
|
|
162
|
+
return await fn();
|
|
163
|
+
} catch (err) {
|
|
164
|
+
lastError = err;
|
|
165
|
+
if (attempt < options.attempts) {
|
|
166
|
+
await sleep(options.delayMs);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
throw lastError;
|
|
171
|
+
}
|
|
172
|
+
var init_Retry = __esm({
|
|
173
|
+
"src/services/docker/utils/Retry.ts"() {
|
|
174
|
+
"use strict";
|
|
175
|
+
init_Sleep();
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// src/services/docker/utils/index.ts
|
|
180
|
+
var utils_exports = {};
|
|
181
|
+
__export(utils_exports, {
|
|
182
|
+
commandExists: () => commandExists,
|
|
183
|
+
exec: () => exec,
|
|
184
|
+
execShell: () => execShell,
|
|
185
|
+
poll: () => poll,
|
|
186
|
+
retry: () => retry,
|
|
187
|
+
sleep: () => sleep,
|
|
188
|
+
spawn: () => spawn2,
|
|
189
|
+
spawnDetached: () => spawnDetached,
|
|
190
|
+
which: () => which,
|
|
191
|
+
withTimeout: () => withTimeout
|
|
192
|
+
});
|
|
193
|
+
var init_utils = __esm({
|
|
194
|
+
"src/services/docker/utils/index.ts"() {
|
|
195
|
+
"use strict";
|
|
196
|
+
init_Exec();
|
|
197
|
+
init_Spawn();
|
|
198
|
+
init_Sleep();
|
|
199
|
+
init_Timeout();
|
|
200
|
+
init_Which();
|
|
201
|
+
init_Poll();
|
|
202
|
+
init_Retry();
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// src/core/argv/Argv.ts
|
|
207
|
+
function normalizeArgv(argv) {
|
|
208
|
+
const prefix = argv.slice(0, Math.min(argv.length, 4));
|
|
209
|
+
let scriptIndex = -1;
|
|
210
|
+
for (let index = 0; index < prefix.length; index++) {
|
|
211
|
+
const arg = prefix[index] ?? "";
|
|
212
|
+
if (arg.endsWith(".ts")) {
|
|
213
|
+
scriptIndex = index;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (scriptIndex === -1) {
|
|
217
|
+
for (let index = 0; index < prefix.length; index++) {
|
|
218
|
+
const arg = prefix[index] ?? "";
|
|
219
|
+
if ((arg.endsWith(".mjs") || arg.endsWith(".cjs") || arg.endsWith(".js")) && (arg.includes("/") || arg.includes("\\"))) {
|
|
220
|
+
scriptIndex = index;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
if (scriptIndex >= 0) {
|
|
225
|
+
return argv.slice(scriptIndex + 1);
|
|
226
|
+
}
|
|
227
|
+
if (argv.length >= 2 && (argv[0].includes("node") || argv[0].endsWith("node.exe"))) {
|
|
228
|
+
return argv.slice(2);
|
|
229
|
+
}
|
|
230
|
+
return [...argv];
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// src/core/service/DefineEnsureService.ts
|
|
234
|
+
function defineEnsureService(service) {
|
|
235
|
+
return service;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// src/core/registry/ServiceRegistry.ts
|
|
239
|
+
var ServiceRegistry = class {
|
|
240
|
+
services = /* @__PURE__ */ new Map();
|
|
241
|
+
/**
|
|
242
|
+
* Registers a service by id and optional aliases.
|
|
243
|
+
*/
|
|
244
|
+
register(service) {
|
|
245
|
+
this.services.set(service.id, service);
|
|
246
|
+
for (const alias of service.aliases ?? []) {
|
|
247
|
+
this.services.set(alias, service);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Resolves a service by id or alias.
|
|
252
|
+
*/
|
|
253
|
+
resolve(name) {
|
|
254
|
+
return this.services.get(name);
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Returns registered services without alias duplicates.
|
|
258
|
+
*/
|
|
259
|
+
list() {
|
|
260
|
+
const seen = /* @__PURE__ */ new Set();
|
|
261
|
+
for (const service of this.services.values()) {
|
|
262
|
+
seen.add(service);
|
|
263
|
+
}
|
|
264
|
+
return [...seen];
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
// src/core/runner/Runner.ts
|
|
269
|
+
import { spawn } from "child_process";
|
|
270
|
+
var CommandSeparator = "--";
|
|
271
|
+
var CommandSeparatorError = class extends Error {
|
|
272
|
+
constructor(token) {
|
|
273
|
+
super(
|
|
274
|
+
`Unexpected argument "${token}". Use "${CommandSeparator}" before the command (e.g. er docker postgres ${CommandSeparator} vite dev).`
|
|
275
|
+
);
|
|
276
|
+
this.name = "CommandSeparatorError";
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
var MissingCommandError = class extends Error {
|
|
280
|
+
constructor() {
|
|
281
|
+
super(`Command separator "${CommandSeparator}" must be followed by a command.`);
|
|
282
|
+
this.name = "MissingCommandError";
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
function parseInvocation(argv, registry) {
|
|
286
|
+
let remaining = normalizeArgv(argv);
|
|
287
|
+
const services = [];
|
|
288
|
+
while (remaining.length > 0) {
|
|
289
|
+
const service = registry.resolve(remaining[0] ?? "");
|
|
290
|
+
if (service === void 0) {
|
|
291
|
+
break;
|
|
292
|
+
}
|
|
293
|
+
remaining = remaining.slice(1);
|
|
294
|
+
const parsed = service.parseArgs(remaining);
|
|
295
|
+
services.push({
|
|
296
|
+
service,
|
|
297
|
+
options: parsed.options
|
|
298
|
+
});
|
|
299
|
+
remaining = parsed.remaining;
|
|
300
|
+
}
|
|
301
|
+
let command;
|
|
302
|
+
if (remaining.length === 0) {
|
|
303
|
+
command = void 0;
|
|
304
|
+
} else if (remaining[0] === CommandSeparator) {
|
|
305
|
+
command = remaining.slice(1);
|
|
306
|
+
if (command.length === 0) {
|
|
307
|
+
throw new MissingCommandError();
|
|
308
|
+
}
|
|
309
|
+
} else {
|
|
310
|
+
throw new CommandSeparatorError(remaining[0] ?? "");
|
|
311
|
+
}
|
|
312
|
+
return {
|
|
313
|
+
services,
|
|
314
|
+
command
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
async function runInvocation(invocation) {
|
|
318
|
+
for (const entry of invocation.services) {
|
|
319
|
+
const exitCode = await entry.service.run(entry.options);
|
|
320
|
+
if (exitCode !== 0) {
|
|
321
|
+
return exitCode;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
if (invocation.command === void 0) {
|
|
325
|
+
return 0;
|
|
326
|
+
}
|
|
327
|
+
return runCommand(invocation.command);
|
|
328
|
+
}
|
|
329
|
+
function runCommand(command) {
|
|
330
|
+
const [executable, ...args] = command;
|
|
331
|
+
if (executable === void 0) {
|
|
332
|
+
return Promise.resolve(1);
|
|
333
|
+
}
|
|
334
|
+
return new Promise((resolve) => {
|
|
335
|
+
const child = spawn(executable, args, {
|
|
336
|
+
stdio: "inherit",
|
|
337
|
+
shell: process.platform === "win32"
|
|
338
|
+
});
|
|
339
|
+
child.on("error", () => {
|
|
340
|
+
resolve(1);
|
|
341
|
+
});
|
|
342
|
+
child.on("close", (code) => {
|
|
343
|
+
resolve(code ?? 1);
|
|
344
|
+
});
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// src/services/docker/commands/Commands.ts
|
|
349
|
+
init_utils();
|
|
350
|
+
var DockerCliVersionPattern = /Docker version\s+([^\s,]+)/i;
|
|
351
|
+
function parseDockerCliVersion(stdout) {
|
|
352
|
+
const match = stdout.match(DockerCliVersionPattern);
|
|
353
|
+
return match?.[1];
|
|
354
|
+
}
|
|
355
|
+
async function resolveDockerExecutable() {
|
|
356
|
+
return which("docker");
|
|
357
|
+
}
|
|
358
|
+
async function isDockerDaemonReachable(executable = "docker") {
|
|
359
|
+
try {
|
|
360
|
+
await exec(executable, ["info"]);
|
|
361
|
+
return true;
|
|
362
|
+
} catch {
|
|
363
|
+
return false;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
async function runDockerVersion(executable = "docker") {
|
|
367
|
+
let clientVersion;
|
|
368
|
+
try {
|
|
369
|
+
const result = await exec(executable, ["--version"]);
|
|
370
|
+
clientVersion = parseDockerCliVersion(result.stdout);
|
|
371
|
+
} catch {
|
|
372
|
+
return { serverReachable: false };
|
|
373
|
+
}
|
|
374
|
+
if (clientVersion === void 0) {
|
|
375
|
+
return { serverReachable: false };
|
|
376
|
+
}
|
|
377
|
+
let serverReachable = false;
|
|
378
|
+
try {
|
|
379
|
+
const serverResult = await exec(executable, [
|
|
380
|
+
"version",
|
|
381
|
+
"--format",
|
|
382
|
+
"{{.Server.Version}}"
|
|
383
|
+
]);
|
|
384
|
+
serverReachable = serverResult.stdout.trim().length > 0;
|
|
385
|
+
} catch {
|
|
386
|
+
serverReachable = false;
|
|
387
|
+
}
|
|
388
|
+
return { clientVersion, serverReachable };
|
|
389
|
+
}
|
|
390
|
+
async function validateDockerInstallation(executable) {
|
|
391
|
+
try {
|
|
392
|
+
const result = await exec(executable, ["--version"]);
|
|
393
|
+
return parseDockerCliVersion(result.stdout) !== void 0;
|
|
394
|
+
} catch {
|
|
395
|
+
return false;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
async function runDockerDesktopStart(executable = "docker") {
|
|
399
|
+
await exec(executable, ["desktop", "start"]);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// src/services/docker/detect/Detect.ts
|
|
403
|
+
async function detectDocker() {
|
|
404
|
+
const executable = await resolveDockerExecutable();
|
|
405
|
+
if (executable === void 0) {
|
|
406
|
+
return {
|
|
407
|
+
installed: false,
|
|
408
|
+
running: false
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
const installed = await validateDockerInstallation(executable);
|
|
412
|
+
if (!installed) {
|
|
413
|
+
return {
|
|
414
|
+
installed: false,
|
|
415
|
+
running: false,
|
|
416
|
+
executable
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
const running = await isDockerDaemonReachable(executable);
|
|
420
|
+
let version;
|
|
421
|
+
if (installed) {
|
|
422
|
+
const versionInfo = await runDockerVersion(executable);
|
|
423
|
+
version = versionInfo.clientVersion;
|
|
424
|
+
}
|
|
425
|
+
return {
|
|
426
|
+
installed: true,
|
|
427
|
+
running,
|
|
428
|
+
version,
|
|
429
|
+
executable
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// src/services/docker/errors/Errors.ts
|
|
434
|
+
var DockerErrorCode = /* @__PURE__ */ ((DockerErrorCode2) => {
|
|
435
|
+
DockerErrorCode2["NOT_INSTALLED"] = "NOT_INSTALLED";
|
|
436
|
+
DockerErrorCode2["NOT_RUNNING"] = "NOT_RUNNING";
|
|
437
|
+
DockerErrorCode2["START_FAILED"] = "START_FAILED";
|
|
438
|
+
DockerErrorCode2["TIMEOUT"] = "TIMEOUT";
|
|
439
|
+
return DockerErrorCode2;
|
|
440
|
+
})(DockerErrorCode || {});
|
|
441
|
+
var DockerError = class extends Error {
|
|
442
|
+
code;
|
|
443
|
+
constructor(message, code, options) {
|
|
444
|
+
super(message, options);
|
|
445
|
+
this.name = "DockerError";
|
|
446
|
+
this.code = code;
|
|
447
|
+
}
|
|
448
|
+
};
|
|
449
|
+
var DockerNotInstalledError = class extends DockerError {
|
|
450
|
+
constructor(message = "Docker is not installed. Install Docker and ensure the CLI is on your PATH.") {
|
|
451
|
+
super(message, "NOT_INSTALLED" /* NOT_INSTALLED */);
|
|
452
|
+
this.name = "DockerNotInstalledError";
|
|
453
|
+
}
|
|
454
|
+
};
|
|
455
|
+
var DockerNotRunningError = class extends DockerError {
|
|
456
|
+
constructor(message = "Docker is installed but the daemon is not running. Start Docker or pass autoStart: true.") {
|
|
457
|
+
super(message, "NOT_RUNNING" /* NOT_RUNNING */);
|
|
458
|
+
this.name = "DockerNotRunningError";
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
var DockerStartError = class extends DockerError {
|
|
462
|
+
constructor(message = "Failed to start Docker. Start Docker manually and try again.", options) {
|
|
463
|
+
super(message, "START_FAILED" /* START_FAILED */, options);
|
|
464
|
+
this.name = "DockerStartError";
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
|
+
var DockerTimeoutError = class extends DockerError {
|
|
468
|
+
constructor(timeoutMs) {
|
|
469
|
+
const seconds = Math.round(timeoutMs / 1e3);
|
|
470
|
+
super(
|
|
471
|
+
`Timed out after ${seconds}s waiting for the Docker daemon to become ready.`,
|
|
472
|
+
"TIMEOUT" /* TIMEOUT */
|
|
473
|
+
);
|
|
474
|
+
this.name = "DockerTimeoutError";
|
|
475
|
+
}
|
|
476
|
+
};
|
|
477
|
+
|
|
478
|
+
// src/services/docker/logger/Logger.ts
|
|
479
|
+
var noop = () => void 0;
|
|
480
|
+
function resolveLogger(logger) {
|
|
481
|
+
return {
|
|
482
|
+
info: logger?.info ?? noop,
|
|
483
|
+
warn: logger?.warn ?? noop,
|
|
484
|
+
debug: logger?.debug ?? noop
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// src/services/docker/platforms/Platforms.ts
|
|
489
|
+
import { access } from "fs/promises";
|
|
490
|
+
import path from "path";
|
|
491
|
+
|
|
492
|
+
// src/services/docker/process/Process.ts
|
|
493
|
+
init_utils();
|
|
494
|
+
function startDockerdDetached() {
|
|
495
|
+
spawnDetached("dockerd", []);
|
|
496
|
+
}
|
|
497
|
+
async function openMacApplication(appName) {
|
|
498
|
+
const { exec: exec2 } = await Promise.resolve().then(() => (init_utils(), utils_exports));
|
|
499
|
+
await exec2("open", ["-a", appName]);
|
|
500
|
+
}
|
|
501
|
+
function launchWindowsExecutable(executablePath) {
|
|
502
|
+
spawnDetached(executablePath, []);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// src/services/docker/platforms/Platforms.ts
|
|
506
|
+
init_utils();
|
|
507
|
+
async function startDockerLinux(logger, _dockerExecutable = "docker") {
|
|
508
|
+
const errors = [];
|
|
509
|
+
if (await commandExists("systemctl")) {
|
|
510
|
+
try {
|
|
511
|
+
logger.debug("Attempting: systemctl start docker");
|
|
512
|
+
await exec("systemctl", ["start", "docker"]);
|
|
513
|
+
logger.info("Started Docker via systemctl.");
|
|
514
|
+
return;
|
|
515
|
+
} catch (err) {
|
|
516
|
+
errors.push(err instanceof Error ? err : new Error(String(err)));
|
|
517
|
+
logger.warn("systemctl start docker failed.");
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
if (await commandExists("service")) {
|
|
521
|
+
try {
|
|
522
|
+
logger.debug("Attempting: service docker start");
|
|
523
|
+
await exec("service", ["docker", "start"]);
|
|
524
|
+
logger.info("Started Docker via service.");
|
|
525
|
+
return;
|
|
526
|
+
} catch (err) {
|
|
527
|
+
errors.push(err instanceof Error ? err : new Error(String(err)));
|
|
528
|
+
logger.warn("service docker start failed.");
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
if (await commandExists("dockerd")) {
|
|
532
|
+
try {
|
|
533
|
+
logger.debug("Attempting: dockerd (detached)");
|
|
534
|
+
startDockerdDetached();
|
|
535
|
+
logger.info("Started dockerd in the background.");
|
|
536
|
+
return;
|
|
537
|
+
} catch (err) {
|
|
538
|
+
errors.push(err instanceof Error ? err : new Error(String(err)));
|
|
539
|
+
logger.warn("dockerd detached start failed.");
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
const aggregate = new Error(
|
|
543
|
+
"No supported Docker start method found on Linux (systemctl, service, or dockerd)."
|
|
544
|
+
);
|
|
545
|
+
if (errors.length > 0) {
|
|
546
|
+
aggregate.cause = errors[errors.length - 1];
|
|
547
|
+
}
|
|
548
|
+
throw aggregate;
|
|
549
|
+
}
|
|
550
|
+
function getWindowsDockerDesktopCandidates() {
|
|
551
|
+
const programFiles = process.env.ProgramFiles ?? "C:\\Program Files";
|
|
552
|
+
const programFilesX86 = process.env["ProgramFiles(x86)"] ?? "C:\\Program Files (x86)";
|
|
553
|
+
const localAppData = process.env.LOCALAPPDATA ?? "";
|
|
554
|
+
const programData = process.env.ProgramData ?? "";
|
|
555
|
+
const candidates = [
|
|
556
|
+
path.join(programFiles, "Docker", "Docker", "Docker Desktop.exe"),
|
|
557
|
+
path.join(programFilesX86, "Docker", "Docker", "Docker Desktop.exe"),
|
|
558
|
+
localAppData ? path.join(localAppData, "Docker", "Docker Desktop.exe") : void 0,
|
|
559
|
+
programData ? path.join(programData, "Docker", "Docker Desktop.exe") : void 0
|
|
560
|
+
];
|
|
561
|
+
return candidates.filter((candidate) => candidate !== void 0);
|
|
562
|
+
}
|
|
563
|
+
async function findWindowsDockerDesktop() {
|
|
564
|
+
for (const candidate of getWindowsDockerDesktopCandidates()) {
|
|
565
|
+
try {
|
|
566
|
+
await access(candidate);
|
|
567
|
+
return candidate;
|
|
568
|
+
} catch {
|
|
569
|
+
continue;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
const pathHit = await Promise.resolve().then(() => (init_utils(), utils_exports)).then((utils) => utils.which("Docker Desktop.exe"));
|
|
573
|
+
return pathHit;
|
|
574
|
+
}
|
|
575
|
+
async function startDockerWindows(logger, dockerExecutable = "docker") {
|
|
576
|
+
try {
|
|
577
|
+
logger.debug("Attempting: docker desktop start");
|
|
578
|
+
await runDockerDesktopStart(dockerExecutable);
|
|
579
|
+
logger.info("Started Docker Desktop via CLI.");
|
|
580
|
+
return;
|
|
581
|
+
} catch (err) {
|
|
582
|
+
logger.warn("docker desktop start failed, searching for Docker Desktop.exe.");
|
|
583
|
+
const desktopPath = await findWindowsDockerDesktop();
|
|
584
|
+
if (desktopPath === void 0) {
|
|
585
|
+
throw err instanceof Error ? err : new Error(String(err));
|
|
586
|
+
}
|
|
587
|
+
logger.debug(`Launching: ${desktopPath}`);
|
|
588
|
+
launchWindowsExecutable(desktopPath);
|
|
589
|
+
logger.info("Launched Docker Desktop executable.");
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
async function startDockerMacOS(logger, dockerExecutable = "docker") {
|
|
593
|
+
try {
|
|
594
|
+
logger.debug("Attempting: docker desktop start");
|
|
595
|
+
await runDockerDesktopStart(dockerExecutable);
|
|
596
|
+
logger.info("Started Docker Desktop via CLI.");
|
|
597
|
+
return;
|
|
598
|
+
} catch {
|
|
599
|
+
logger.warn("docker desktop start failed, trying open -a Docker.");
|
|
600
|
+
await openMacApplication("Docker");
|
|
601
|
+
logger.info("Opened Docker.app.");
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
async function startDocker(logger, dockerExecutable = "docker") {
|
|
605
|
+
if (process.platform === "win32") {
|
|
606
|
+
await startDockerWindows(logger, dockerExecutable);
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
if (process.platform === "darwin") {
|
|
610
|
+
await startDockerMacOS(logger, dockerExecutable);
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
await startDockerLinux(logger, dockerExecutable);
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
// src/services/docker/types/Types.ts
|
|
617
|
+
var DefaultEnsureDockerOptions = {
|
|
618
|
+
timeout: 12e4,
|
|
619
|
+
interval: 1e3,
|
|
620
|
+
autoStart: true
|
|
621
|
+
};
|
|
622
|
+
|
|
623
|
+
// src/services/docker/wait/Wait.ts
|
|
624
|
+
init_utils();
|
|
625
|
+
async function waitForDocker(options) {
|
|
626
|
+
const executable = options.executable ?? "docker";
|
|
627
|
+
const logger = options.logger;
|
|
628
|
+
logger?.debug("Waiting for Docker daemon...");
|
|
629
|
+
const ready = await poll(async () => isDockerDaemonReachable(executable), {
|
|
630
|
+
timeout: options.timeout,
|
|
631
|
+
interval: options.interval
|
|
632
|
+
});
|
|
633
|
+
if (!ready) {
|
|
634
|
+
throw new DockerTimeoutError(options.timeout);
|
|
635
|
+
}
|
|
636
|
+
logger?.info("Docker is ready.");
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
// src/services/docker/ensure/Ensure.ts
|
|
640
|
+
async function isDockerRunning() {
|
|
641
|
+
try {
|
|
642
|
+
const result = await detectDocker();
|
|
643
|
+
return result.installed && result.running;
|
|
644
|
+
} catch {
|
|
645
|
+
return false;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
async function ensureDockerRunning(options) {
|
|
649
|
+
const timeout = options?.timeout ?? DefaultEnsureDockerOptions.timeout;
|
|
650
|
+
const interval = options?.interval ?? DefaultEnsureDockerOptions.interval;
|
|
651
|
+
const autoStart = options?.autoStart ?? DefaultEnsureDockerOptions.autoStart;
|
|
652
|
+
const logger = resolveLogger(options?.logger);
|
|
653
|
+
logger.info("Checking Docker installation...");
|
|
654
|
+
const detection = await detectDocker();
|
|
655
|
+
if (!detection.installed) {
|
|
656
|
+
throw new DockerNotInstalledError();
|
|
657
|
+
}
|
|
658
|
+
logger.info("Docker found.");
|
|
659
|
+
if (detection.running) {
|
|
660
|
+
logger.info("Docker daemon is already running.");
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
logger.warn("Docker daemon not running.");
|
|
664
|
+
if (!autoStart) {
|
|
665
|
+
throw new DockerNotRunningError();
|
|
666
|
+
}
|
|
667
|
+
logger.info("Attempting to start Docker...");
|
|
668
|
+
const executable = detection.executable ?? "docker";
|
|
669
|
+
try {
|
|
670
|
+
await startDocker(logger, executable);
|
|
671
|
+
} catch (err) {
|
|
672
|
+
throw new DockerStartError(void 0, {
|
|
673
|
+
cause: err instanceof Error ? err : new Error(String(err))
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
await waitForDocker({
|
|
677
|
+
timeout,
|
|
678
|
+
interval,
|
|
679
|
+
executable,
|
|
680
|
+
logger
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
// src/services/docker/service/DockerService.ts
|
|
685
|
+
var DockerServiceMode = /* @__PURE__ */ ((DockerServiceMode2) => {
|
|
686
|
+
DockerServiceMode2["ENSURE"] = "ENSURE";
|
|
687
|
+
DockerServiceMode2["CHECK"] = "CHECK";
|
|
688
|
+
return DockerServiceMode2;
|
|
689
|
+
})(DockerServiceMode || {});
|
|
690
|
+
function parseDockerServiceArgs(argv) {
|
|
691
|
+
const remaining = [...argv];
|
|
692
|
+
let mode = "ENSURE" /* ENSURE */;
|
|
693
|
+
let timeout = DefaultEnsureDockerOptions.timeout;
|
|
694
|
+
let interval = DefaultEnsureDockerOptions.interval;
|
|
695
|
+
let autoStart = DefaultEnsureDockerOptions.autoStart;
|
|
696
|
+
let quiet = false;
|
|
697
|
+
while (remaining.length > 0) {
|
|
698
|
+
const arg = remaining[0];
|
|
699
|
+
if (arg === "--check") {
|
|
700
|
+
mode = "CHECK" /* CHECK */;
|
|
701
|
+
remaining.shift();
|
|
702
|
+
continue;
|
|
703
|
+
}
|
|
704
|
+
if (arg === "-q" || arg === "--quiet") {
|
|
705
|
+
quiet = true;
|
|
706
|
+
remaining.shift();
|
|
707
|
+
continue;
|
|
708
|
+
}
|
|
709
|
+
if (arg === "--no-auto-start" || arg === "--no-start") {
|
|
710
|
+
autoStart = false;
|
|
711
|
+
remaining.shift();
|
|
712
|
+
continue;
|
|
713
|
+
}
|
|
714
|
+
if (arg === "--timeout") {
|
|
715
|
+
remaining.shift();
|
|
716
|
+
timeout = parsePositiveInteger(remaining.shift(), "--timeout");
|
|
717
|
+
continue;
|
|
718
|
+
}
|
|
719
|
+
if (arg === "--interval") {
|
|
720
|
+
remaining.shift();
|
|
721
|
+
interval = parsePositiveInteger(remaining.shift(), "--interval");
|
|
722
|
+
continue;
|
|
723
|
+
}
|
|
724
|
+
if (arg === CommandSeparator) {
|
|
725
|
+
break;
|
|
726
|
+
}
|
|
727
|
+
if (arg.startsWith("-")) {
|
|
728
|
+
throw new Error(`Unknown docker option: ${arg}`);
|
|
729
|
+
}
|
|
730
|
+
break;
|
|
731
|
+
}
|
|
732
|
+
return {
|
|
733
|
+
options: {
|
|
734
|
+
mode,
|
|
735
|
+
timeout,
|
|
736
|
+
interval,
|
|
737
|
+
autoStart,
|
|
738
|
+
quiet
|
|
739
|
+
},
|
|
740
|
+
remaining
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
async function runDockerService(options) {
|
|
744
|
+
const logger = options.quiet ? void 0 : createConsoleLogger();
|
|
745
|
+
try {
|
|
746
|
+
if (options.mode === "CHECK" /* CHECK */) {
|
|
747
|
+
const running = await isDockerRunning();
|
|
748
|
+
if (!running && !options.quiet) {
|
|
749
|
+
console.error("Docker is not running.");
|
|
750
|
+
}
|
|
751
|
+
return running ? 0 : 1;
|
|
752
|
+
}
|
|
753
|
+
await ensureDockerRunning({
|
|
754
|
+
timeout: options.timeout,
|
|
755
|
+
interval: options.interval,
|
|
756
|
+
autoStart: options.autoStart,
|
|
757
|
+
logger
|
|
758
|
+
});
|
|
759
|
+
return 0;
|
|
760
|
+
} catch (err) {
|
|
761
|
+
if (!options.quiet) {
|
|
762
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
763
|
+
console.error(message);
|
|
764
|
+
}
|
|
765
|
+
return 1;
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
function printDockerServiceHelp() {
|
|
769
|
+
console.log(`docker [options]
|
|
770
|
+
|
|
771
|
+
Ensure Docker is installed, started, and ready.
|
|
772
|
+
|
|
773
|
+
Options:
|
|
774
|
+
--check Exit 0 when the daemon is reachable; do not auto-start
|
|
775
|
+
--timeout <ms> Max wait for daemon readiness (default: ${DefaultEnsureDockerOptions.timeout})
|
|
776
|
+
--interval <ms> Poll interval between checks (default: ${DefaultEnsureDockerOptions.interval})
|
|
777
|
+
--no-auto-start Fail when the daemon is down instead of starting Docker
|
|
778
|
+
-q, --quiet Suppress progress output`);
|
|
779
|
+
}
|
|
780
|
+
var dockerService = defineEnsureService({
|
|
781
|
+
id: "docker",
|
|
782
|
+
aliases: ["d"],
|
|
783
|
+
parseArgs: parseDockerServiceArgs,
|
|
784
|
+
run: runDockerService,
|
|
785
|
+
printHelp: printDockerServiceHelp
|
|
786
|
+
});
|
|
787
|
+
function parsePositiveInteger(value, flag) {
|
|
788
|
+
if (value === void 0) {
|
|
789
|
+
throw new Error(`Missing value for ${flag}`);
|
|
790
|
+
}
|
|
791
|
+
const parsed = Number(value);
|
|
792
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
793
|
+
throw new Error(`${flag} expects a positive integer, received: ${value}`);
|
|
794
|
+
}
|
|
795
|
+
return parsed;
|
|
796
|
+
}
|
|
797
|
+
function createConsoleLogger() {
|
|
798
|
+
return {
|
|
799
|
+
info: (message) => {
|
|
800
|
+
console.log(message);
|
|
801
|
+
},
|
|
802
|
+
warn: (message) => {
|
|
803
|
+
console.warn(message);
|
|
804
|
+
},
|
|
805
|
+
debug: (message) => {
|
|
806
|
+
console.debug(message);
|
|
807
|
+
}
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
// src/services/LoadCustomServices.ts
|
|
812
|
+
import { existsSync } from "fs";
|
|
813
|
+
import { readdir } from "fs/promises";
|
|
814
|
+
import path2 from "path";
|
|
815
|
+
import { pathToFileURL } from "url";
|
|
816
|
+
|
|
817
|
+
// src/services/AssertEnsureService.ts
|
|
818
|
+
function assertEnsureService(value, source) {
|
|
819
|
+
if (value === null || typeof value !== "object") {
|
|
820
|
+
throw new Error(`${source}: export default must be an object`);
|
|
821
|
+
}
|
|
822
|
+
const candidate = value;
|
|
823
|
+
if (typeof candidate.id !== "string" || candidate.id.length === 0) {
|
|
824
|
+
throw new Error(`${source}: export default must define a non-empty string "id"`);
|
|
825
|
+
}
|
|
826
|
+
if (typeof candidate.parseArgs !== "function") {
|
|
827
|
+
throw new Error(`${source}: export default must define "parseArgs(argv)"`);
|
|
828
|
+
}
|
|
829
|
+
if (typeof candidate.run !== "function") {
|
|
830
|
+
throw new Error(`${source}: export default must define "run(options)"`);
|
|
831
|
+
}
|
|
832
|
+
if (candidate.printHelp !== void 0 && typeof candidate.printHelp !== "function") {
|
|
833
|
+
throw new Error(`${source}: "printHelp" must be a function when provided`);
|
|
834
|
+
}
|
|
835
|
+
return value;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
// src/services/LoadCustomServices.ts
|
|
839
|
+
var SERVICE_FILE_PATTERN = /\.(?:[cm]?js|mjs|ts)$/i;
|
|
840
|
+
function findCustomServicesDir(startDir) {
|
|
841
|
+
let current = path2.resolve(startDir);
|
|
842
|
+
while (true) {
|
|
843
|
+
const candidate = path2.join(current, ".er", "services");
|
|
844
|
+
if (existsSync(candidate)) {
|
|
845
|
+
return candidate;
|
|
846
|
+
}
|
|
847
|
+
const parent = path2.dirname(current);
|
|
848
|
+
if (parent === current) {
|
|
849
|
+
return void 0;
|
|
850
|
+
}
|
|
851
|
+
current = parent;
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
async function loadCustomServices(registry, cwd = process.cwd()) {
|
|
855
|
+
const dir = findCustomServicesDir(cwd);
|
|
856
|
+
if (dir === void 0) {
|
|
857
|
+
return;
|
|
858
|
+
}
|
|
859
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
860
|
+
const files = entries.filter((entry) => entry.isFile() && SERVICE_FILE_PATTERN.test(entry.name)).map((entry) => path2.join(dir, entry.name)).sort();
|
|
861
|
+
for (const filePath of files) {
|
|
862
|
+
const service = await importCustomService(filePath);
|
|
863
|
+
registry.register(service);
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
async function importCustomService(filePath) {
|
|
867
|
+
const ext = path2.extname(filePath).toLowerCase();
|
|
868
|
+
let loaded;
|
|
869
|
+
if (ext === ".ts" || ext === ".mts" || ext === ".cts") {
|
|
870
|
+
const { createJiti } = await import("jiti");
|
|
871
|
+
const jiti = createJiti(import.meta.url, { interopDefault: true });
|
|
872
|
+
loaded = jiti(filePath);
|
|
873
|
+
} else {
|
|
874
|
+
const module = await import(pathToFileURL(filePath).href);
|
|
875
|
+
loaded = module.default ?? module;
|
|
876
|
+
}
|
|
877
|
+
return assertEnsureService(loaded, filePath);
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
// src/services/CreateServiceRegistry.ts
|
|
881
|
+
async function createServiceRegistry(cwd = process.cwd()) {
|
|
882
|
+
const registry = createBuiltInServiceRegistry();
|
|
883
|
+
await loadCustomServices(registry, cwd);
|
|
884
|
+
return registry;
|
|
885
|
+
}
|
|
886
|
+
function createBuiltInServiceRegistry() {
|
|
887
|
+
const registry = new ServiceRegistry();
|
|
888
|
+
registry.register(dockerService);
|
|
889
|
+
return registry;
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
// src/api/EnsureRunning.ts
|
|
893
|
+
var EnsureRunningError = class extends Error {
|
|
894
|
+
serviceId;
|
|
895
|
+
exitCode;
|
|
896
|
+
constructor(serviceId, exitCode, message = `Service "${serviceId}" failed with exit code ${exitCode}.`) {
|
|
897
|
+
super(message);
|
|
898
|
+
this.name = "EnsureRunningError";
|
|
899
|
+
this.serviceId = serviceId;
|
|
900
|
+
this.exitCode = exitCode;
|
|
901
|
+
}
|
|
902
|
+
};
|
|
903
|
+
var EnsureServiceId = /* @__PURE__ */ ((EnsureServiceId2) => {
|
|
904
|
+
EnsureServiceId2["DOCKER"] = "docker";
|
|
905
|
+
return EnsureServiceId2;
|
|
906
|
+
})(EnsureServiceId || {});
|
|
907
|
+
async function ensureRunning(input) {
|
|
908
|
+
const request = normalizeRequest(input);
|
|
909
|
+
const registry = request.registry ?? await createServiceRegistry(request.cwd);
|
|
910
|
+
for (const serviceInput of request.services) {
|
|
911
|
+
const entry = resolveServiceInput(serviceInput, registry);
|
|
912
|
+
const exitCode = await entry.service.run(entry.options);
|
|
913
|
+
if (exitCode !== 0) {
|
|
914
|
+
throw new EnsureRunningError(entry.service.id, exitCode);
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
async function runEnsureRunning(request) {
|
|
919
|
+
await ensureRunning(request);
|
|
920
|
+
if (request.command === void 0 || request.command.length === 0) {
|
|
921
|
+
return 0;
|
|
922
|
+
}
|
|
923
|
+
return runCommand(request.command);
|
|
924
|
+
}
|
|
925
|
+
async function ensureDocker(options) {
|
|
926
|
+
if (options?.check === true) {
|
|
927
|
+
const running = await isDockerRunning();
|
|
928
|
+
if (!running) {
|
|
929
|
+
throw new DockerNotRunningError();
|
|
930
|
+
}
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
933
|
+
await ensureDockerRunning({
|
|
934
|
+
timeout: options?.timeout,
|
|
935
|
+
interval: options?.interval,
|
|
936
|
+
autoStart: options?.autoStart,
|
|
937
|
+
logger: options?.quiet === true ? void 0 : options?.logger
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
var ensure = {
|
|
941
|
+
docker: ensureDocker,
|
|
942
|
+
running: ensureRunning
|
|
943
|
+
};
|
|
944
|
+
function normalizeRequest(input) {
|
|
945
|
+
if (Array.isArray(input)) {
|
|
946
|
+
return {
|
|
947
|
+
services: input
|
|
948
|
+
};
|
|
949
|
+
}
|
|
950
|
+
return input;
|
|
951
|
+
}
|
|
952
|
+
function resolveServiceInput(input, registry) {
|
|
953
|
+
if (typeof input === "string") {
|
|
954
|
+
const service2 = registry.resolve(input);
|
|
955
|
+
if (service2 === void 0) {
|
|
956
|
+
throw new EnsureRunningError(input, 1, `Unknown service: ${input}`);
|
|
957
|
+
}
|
|
958
|
+
return {
|
|
959
|
+
service: service2,
|
|
960
|
+
options: defaultServiceOptions(service2.id)
|
|
961
|
+
};
|
|
962
|
+
}
|
|
963
|
+
const service = registry.resolve(input.service);
|
|
964
|
+
if (service === void 0) {
|
|
965
|
+
throw new EnsureRunningError(String(input.service), 1, `Unknown service: ${input.service}`);
|
|
966
|
+
}
|
|
967
|
+
return {
|
|
968
|
+
service,
|
|
969
|
+
options: toServiceOptions(service.id, input.options)
|
|
970
|
+
};
|
|
971
|
+
}
|
|
972
|
+
function defaultServiceOptions(serviceId) {
|
|
973
|
+
if (serviceId === "docker" /* DOCKER */) {
|
|
974
|
+
return toDockerServiceOptions();
|
|
975
|
+
}
|
|
976
|
+
return {};
|
|
977
|
+
}
|
|
978
|
+
function toServiceOptions(serviceId, options) {
|
|
979
|
+
if (serviceId === "docker" /* DOCKER */) {
|
|
980
|
+
return toDockerServiceOptions(options);
|
|
981
|
+
}
|
|
982
|
+
return options ?? {};
|
|
983
|
+
}
|
|
984
|
+
function toDockerServiceOptions(options) {
|
|
985
|
+
return {
|
|
986
|
+
mode: options?.check === true ? "CHECK" /* CHECK */ : "ENSURE" /* ENSURE */,
|
|
987
|
+
timeout: options?.timeout ?? DefaultEnsureDockerOptions.timeout,
|
|
988
|
+
interval: options?.interval ?? DefaultEnsureDockerOptions.interval,
|
|
989
|
+
autoStart: options?.autoStart ?? DefaultEnsureDockerOptions.autoStart,
|
|
990
|
+
quiet: options?.quiet ?? false
|
|
991
|
+
};
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
// src/cli/Cli.ts
|
|
995
|
+
import { readFileSync } from "fs";
|
|
996
|
+
import { fileURLToPath } from "url";
|
|
997
|
+
var CliMode = /* @__PURE__ */ ((CliMode2) => {
|
|
998
|
+
CliMode2["HELP"] = "HELP";
|
|
999
|
+
CliMode2["VERSION"] = "VERSION";
|
|
1000
|
+
CliMode2["RUN"] = "RUN";
|
|
1001
|
+
return CliMode2;
|
|
1002
|
+
})(CliMode || {});
|
|
1003
|
+
function readPackageVersion() {
|
|
1004
|
+
const packageJsonPath = fileURLToPath(new URL("../../package.json", import.meta.url));
|
|
1005
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
1006
|
+
return packageJson.version;
|
|
1007
|
+
}
|
|
1008
|
+
function printCliHelp(registry) {
|
|
1009
|
+
const serviceLines = registry.list().map((service) => ` ${service.id.padEnd(10)} ${getServiceSummary(service)}`).join("\n");
|
|
1010
|
+
console.log(`Usage: ensure-running | er [service [flags...]]... [--] [command...]
|
|
1011
|
+
|
|
1012
|
+
Ensure local services are ready, then optionally run a command.
|
|
1013
|
+
|
|
1014
|
+
Services:
|
|
1015
|
+
${serviceLines}
|
|
1016
|
+
|
|
1017
|
+
Custom services:
|
|
1018
|
+
Place files in .er/services/ with export default defineEnsureService({ ... })
|
|
1019
|
+
|
|
1020
|
+
Examples:
|
|
1021
|
+
er docker
|
|
1022
|
+
er docker --check
|
|
1023
|
+
er docker postgres -- vite dev
|
|
1024
|
+
er docker --timeout 60000 -- npm test
|
|
1025
|
+
|
|
1026
|
+
Use "--" to separate services from the command you want to run.
|
|
1027
|
+
|
|
1028
|
+
Global options:
|
|
1029
|
+
-h, --help Show this help message
|
|
1030
|
+
-v, --version Print CLI version
|
|
1031
|
+
service --help Show service-specific help`);
|
|
1032
|
+
}
|
|
1033
|
+
async function runCli(argv, registry) {
|
|
1034
|
+
const resolvedRegistry = registry ?? await createServiceRegistry();
|
|
1035
|
+
const normalized = normalizeArgv(argv);
|
|
1036
|
+
if (normalized.length === 0 || normalized[0] === "-h" || normalized[0] === "--help") {
|
|
1037
|
+
printCliHelp(resolvedRegistry);
|
|
1038
|
+
return 0;
|
|
1039
|
+
}
|
|
1040
|
+
if (normalized[0] === "-v" || normalized[0] === "--version") {
|
|
1041
|
+
console.log(readPackageVersion());
|
|
1042
|
+
return 0;
|
|
1043
|
+
}
|
|
1044
|
+
if (normalized.length >= 2 && (normalized[1] === "--help" || normalized[1] === "-h")) {
|
|
1045
|
+
const service = resolvedRegistry.resolve(normalized[0] ?? "");
|
|
1046
|
+
if (service?.printHelp !== void 0) {
|
|
1047
|
+
service.printHelp();
|
|
1048
|
+
return 0;
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
try {
|
|
1052
|
+
const invocation = parseInvocation(normalized, resolvedRegistry);
|
|
1053
|
+
if (invocation.services.length === 0 && invocation.command === void 0) {
|
|
1054
|
+
printCliHelp(resolvedRegistry);
|
|
1055
|
+
return 1;
|
|
1056
|
+
}
|
|
1057
|
+
return await runInvocation(invocation);
|
|
1058
|
+
} catch (err) {
|
|
1059
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1060
|
+
console.error(message);
|
|
1061
|
+
printCliHelp(resolvedRegistry);
|
|
1062
|
+
return 1;
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
function getServiceSummary(service) {
|
|
1066
|
+
if (service.id === "docker") {
|
|
1067
|
+
return "Ensure Docker CLI is installed and the daemon is ready";
|
|
1068
|
+
}
|
|
1069
|
+
return "Ensure service is ready";
|
|
1070
|
+
}
|
|
1071
|
+
export {
|
|
1072
|
+
CliMode,
|
|
1073
|
+
CommandSeparator,
|
|
1074
|
+
CommandSeparatorError,
|
|
1075
|
+
ServiceRegistry as CoreServiceRegistry,
|
|
1076
|
+
DefaultEnsureDockerOptions,
|
|
1077
|
+
DockerError,
|
|
1078
|
+
DockerErrorCode,
|
|
1079
|
+
DockerNotInstalledError,
|
|
1080
|
+
DockerNotRunningError,
|
|
1081
|
+
DockerServiceMode,
|
|
1082
|
+
DockerStartError,
|
|
1083
|
+
DockerTimeoutError,
|
|
1084
|
+
EnsureRunningError,
|
|
1085
|
+
EnsureServiceId,
|
|
1086
|
+
MissingCommandError,
|
|
1087
|
+
assertEnsureService,
|
|
1088
|
+
createBuiltInServiceRegistry,
|
|
1089
|
+
createServiceRegistry,
|
|
1090
|
+
defineEnsureService,
|
|
1091
|
+
detectDocker,
|
|
1092
|
+
dockerService,
|
|
1093
|
+
ensure,
|
|
1094
|
+
ensureDocker,
|
|
1095
|
+
ensureDockerRunning,
|
|
1096
|
+
ensureRunning,
|
|
1097
|
+
findCustomServicesDir,
|
|
1098
|
+
isDockerRunning,
|
|
1099
|
+
loadCustomServices,
|
|
1100
|
+
normalizeArgv,
|
|
1101
|
+
parseDockerServiceArgs,
|
|
1102
|
+
parseInvocation,
|
|
1103
|
+
printCliHelp,
|
|
1104
|
+
printDockerServiceHelp,
|
|
1105
|
+
readPackageVersion,
|
|
1106
|
+
runCli,
|
|
1107
|
+
runCommand,
|
|
1108
|
+
runDockerService,
|
|
1109
|
+
runEnsureRunning,
|
|
1110
|
+
runInvocation
|
|
1111
|
+
};
|
|
1112
|
+
//# sourceMappingURL=index.js.map
|