@ninhache/probe 0.5.0 → 0.7.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/README.md +34 -3
- package/dist/{chunk-4VXL4OTD.js → chunk-YTTYVEYF.js} +10 -0
- package/dist/cli.js +467 -92
- package/dist/dist-SITQEC3X.js +2894 -0
- package/dist/{open-WH6VGPQT.js → open-T3OQH2O4.js} +1 -1
- package/package.json +2 -1
package/dist/cli.js
CHANGED
|
@@ -3,6 +3,7 @@ import { createRequire as __createRequire } from "node:module";
|
|
|
3
3
|
const require = __createRequire(import.meta.url);
|
|
4
4
|
import {
|
|
5
5
|
BACKUP_GLOB,
|
|
6
|
+
COMPLETE_COMMAND,
|
|
6
7
|
CONFIG_FILE,
|
|
7
8
|
DASHBOARD_INTERVAL_SECONDS,
|
|
8
9
|
DEFAULT_MAKEFILE,
|
|
@@ -28,18 +29,20 @@ import {
|
|
|
28
29
|
WAIT_BACKOFF_FACTOR,
|
|
29
30
|
WAIT_LAST_PASS_MS,
|
|
30
31
|
createColors,
|
|
32
|
+
curlCommand,
|
|
31
33
|
detectTerminal,
|
|
32
34
|
formatCheckResults,
|
|
33
35
|
formatServices,
|
|
34
36
|
isInteractive,
|
|
35
37
|
runChecks,
|
|
36
|
-
stripStyles
|
|
37
|
-
|
|
38
|
+
stripStyles,
|
|
39
|
+
targetUrl
|
|
40
|
+
} from "./chunk-YTTYVEYF.js";
|
|
38
41
|
import "./chunk-M3ASEG5F.js";
|
|
39
42
|
|
|
40
43
|
// src/cli.ts
|
|
41
44
|
import {
|
|
42
|
-
defineCommand as
|
|
45
|
+
defineCommand as defineCommand13,
|
|
43
46
|
renderUsage,
|
|
44
47
|
runCommand,
|
|
45
48
|
runMain
|
|
@@ -48,6 +51,53 @@ import {
|
|
|
48
51
|
// src/commands/add.ts
|
|
49
52
|
import { defineCommand } from "citty";
|
|
50
53
|
|
|
54
|
+
// src/config/schema.ts
|
|
55
|
+
import { z } from "zod";
|
|
56
|
+
var CURRENT_SCHEMA_VERSION = 1;
|
|
57
|
+
var SERVICE_NAME_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9_-]*$/;
|
|
58
|
+
var statusCode = z.number().int().min(100).max(599);
|
|
59
|
+
var serviceSchemaV1 = z.strictObject({
|
|
60
|
+
host: z.string().min(1).default("localhost"),
|
|
61
|
+
port: z.number().int().min(1).max(65535),
|
|
62
|
+
path: z.string().startsWith("/").default("/"),
|
|
63
|
+
expect: z.union([statusCode, z.array(statusCode).min(1)]).optional(),
|
|
64
|
+
/** Seconds given to this service to answer, instead of the default. */
|
|
65
|
+
timeout: z.number().positive().max(MAX_TIMEOUT_SECONDS).optional()
|
|
66
|
+
});
|
|
67
|
+
var configSchemaV1 = z.strictObject({
|
|
68
|
+
schemaVersion: z.literal(1),
|
|
69
|
+
services: z.record(
|
|
70
|
+
z.string().regex(SERVICE_NAME_PATTERN, "letters, digits, _ and - only"),
|
|
71
|
+
serviceSchemaV1
|
|
72
|
+
).nullish().transform((services) => services ?? {})
|
|
73
|
+
});
|
|
74
|
+
var schemas = {
|
|
75
|
+
1: configSchemaV1
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// src/ui/prompt.ts
|
|
79
|
+
async function confirm(message, streams = {}) {
|
|
80
|
+
const { confirm: ask2, isCancel } = await import("./dist-SITQEC3X.js");
|
|
81
|
+
const answer = await ask2({ message, initialValue: false, ...streams });
|
|
82
|
+
return !isCancel(answer) && answer;
|
|
83
|
+
}
|
|
84
|
+
async function ask(message, question = {}, streams = {}) {
|
|
85
|
+
const { text, isCancel } = await import("./dist-SITQEC3X.js");
|
|
86
|
+
const { placeholder, defaultValue, validate: validate2 } = question;
|
|
87
|
+
const answer = await text({
|
|
88
|
+
message,
|
|
89
|
+
placeholder,
|
|
90
|
+
defaultValue,
|
|
91
|
+
validate: (typed) => {
|
|
92
|
+
const value = typed ?? "";
|
|
93
|
+
return value === "" && defaultValue !== void 0 ? void 0 : validate2?.(value);
|
|
94
|
+
},
|
|
95
|
+
...streams
|
|
96
|
+
});
|
|
97
|
+
if (isCancel(answer)) return null;
|
|
98
|
+
return answer || (defaultValue ?? "");
|
|
99
|
+
}
|
|
100
|
+
|
|
51
101
|
// src/config/discovery.ts
|
|
52
102
|
import { statSync } from "fs";
|
|
53
103
|
import { dirname, join, resolve } from "path";
|
|
@@ -89,30 +139,6 @@ import { copyFile, readFile, writeFile } from "fs/promises";
|
|
|
89
139
|
import { join as join2, relative } from "path";
|
|
90
140
|
import { parseDocument } from "yaml";
|
|
91
141
|
|
|
92
|
-
// src/config/schema.ts
|
|
93
|
-
import { z } from "zod";
|
|
94
|
-
var CURRENT_SCHEMA_VERSION = 1;
|
|
95
|
-
var SERVICE_NAME_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9_-]*$/;
|
|
96
|
-
var statusCode = z.number().int().min(100).max(599);
|
|
97
|
-
var serviceSchemaV1 = z.strictObject({
|
|
98
|
-
host: z.string().min(1).default("localhost"),
|
|
99
|
-
port: z.number().int().min(1).max(65535),
|
|
100
|
-
path: z.string().startsWith("/").default("/"),
|
|
101
|
-
expect: z.union([statusCode, z.array(statusCode).min(1)]).optional(),
|
|
102
|
-
/** Seconds given to this service to answer, instead of the default. */
|
|
103
|
-
timeout: z.number().positive().max(MAX_TIMEOUT_SECONDS).optional()
|
|
104
|
-
});
|
|
105
|
-
var configSchemaV1 = z.strictObject({
|
|
106
|
-
schemaVersion: z.literal(1),
|
|
107
|
-
services: z.record(
|
|
108
|
-
z.string().regex(SERVICE_NAME_PATTERN, "letters, digits, _ and - only"),
|
|
109
|
-
serviceSchemaV1
|
|
110
|
-
).nullish().transform((services) => services ?? {})
|
|
111
|
-
});
|
|
112
|
-
var schemas = {
|
|
113
|
-
1: configSchemaV1
|
|
114
|
-
};
|
|
115
|
-
|
|
116
142
|
// src/config/migrations.ts
|
|
117
143
|
var migrations = {};
|
|
118
144
|
function formatIssues(error) {
|
|
@@ -316,7 +342,7 @@ async function writeLock(paths, lock) {
|
|
|
316
342
|
}
|
|
317
343
|
|
|
318
344
|
// src/version.ts
|
|
319
|
-
var CLI_VERSION = true ? "0.
|
|
345
|
+
var CLI_VERSION = true ? "0.7.0" : "0.0.0-dev";
|
|
320
346
|
|
|
321
347
|
// src/makefile/generator.ts
|
|
322
348
|
import { createHash } from "crypto";
|
|
@@ -331,13 +357,15 @@ function renderBlock(serviceNames) {
|
|
|
331
357
|
return [
|
|
332
358
|
BLOCK_START,
|
|
333
359
|
`PROBE ?= $(shell command -v probe >/dev/null 2>&1 && echo probe || echo "${fallback}")`,
|
|
334
|
-
`.PHONY: ${["help", "check", "status", ...targets].join(" ")}`,
|
|
360
|
+
`.PHONY: ${["help", "check", "status", "probe", ...targets].join(" ")}`,
|
|
335
361
|
"help: ## List available targets",
|
|
336
362
|
` ${HELP_RECIPE}`,
|
|
337
363
|
"check: ## Check that every service responds",
|
|
338
364
|
" @$(PROBE) check",
|
|
339
365
|
"status: ## Show probe status",
|
|
340
366
|
" @$(PROBE) status",
|
|
367
|
+
"probe: ## Open the dashboard",
|
|
368
|
+
" @$(PROBE)",
|
|
341
369
|
...serviceNames.flatMap((name) => [
|
|
342
370
|
`check-${name}: ## Check ${name}`,
|
|
343
371
|
` @$(PROBE) check ${name}`
|
|
@@ -426,6 +454,11 @@ function secondsToMs(option, value) {
|
|
|
426
454
|
}
|
|
427
455
|
return Math.ceil(seconds2 * 1e3);
|
|
428
456
|
}
|
|
457
|
+
function namedService(config, name) {
|
|
458
|
+
const service = Object.hasOwn(config.services, name) ? config.services[name] : void 0;
|
|
459
|
+
if (!service) throw new ProbeError("SERVICE_UNKNOWN", `unknown service "${name}". See: probe ls`);
|
|
460
|
+
return service;
|
|
461
|
+
}
|
|
429
462
|
function rawServices(loaded) {
|
|
430
463
|
return loaded.raw.services ?? {};
|
|
431
464
|
}
|
|
@@ -453,6 +486,65 @@ function parseExpect(value) {
|
|
|
453
486
|
const codes = value.split(",").map((code) => parseInteger(code.trim(), "--expect"));
|
|
454
487
|
return codes.length === 1 ? codes[0] : codes;
|
|
455
488
|
}
|
|
489
|
+
var INTEGER = /^\d+$/;
|
|
490
|
+
var DEFAULT_PATH = "/";
|
|
491
|
+
function schemaProblem(schema, value) {
|
|
492
|
+
const result = schema.safeParse(value);
|
|
493
|
+
return result.success ? void 0 : result.error.issues[0]?.message;
|
|
494
|
+
}
|
|
495
|
+
function nameProblem(name, services) {
|
|
496
|
+
if (!SERVICE_NAME_PATTERN.test(name)) return "Letters, digits, _ and - only, e.g. api";
|
|
497
|
+
if (Object.hasOwn(services, name)) return `${name} already exists`;
|
|
498
|
+
return void 0;
|
|
499
|
+
}
|
|
500
|
+
function portProblem(port) {
|
|
501
|
+
if (!INTEGER.test(port)) return "A port number, e.g. 3000";
|
|
502
|
+
return schemaProblem(serviceSchemaV1.shape.port, Number(port)) && "A port between 1 and 65535";
|
|
503
|
+
}
|
|
504
|
+
function expectProblem(expect) {
|
|
505
|
+
const codes = expect.split(",").map((code) => code.trim());
|
|
506
|
+
if (!codes.every((code) => INTEGER.test(code))) return "A status, or several: 200 or 200,204";
|
|
507
|
+
return schemaProblem(serviceSchemaV1.shape.expect, parseExpect(expect)) && "Statuses go from 100 to 599";
|
|
508
|
+
}
|
|
509
|
+
async function guidedAdd() {
|
|
510
|
+
const loaded = await openProject();
|
|
511
|
+
const services = rawServices(loaded);
|
|
512
|
+
const answers = [];
|
|
513
|
+
const questions = [
|
|
514
|
+
{
|
|
515
|
+
message: "Service name",
|
|
516
|
+
placeholder: "api",
|
|
517
|
+
validate: (v) => nameProblem(v, services)
|
|
518
|
+
},
|
|
519
|
+
{ message: "Port", placeholder: "3000", validate: portProblem },
|
|
520
|
+
{
|
|
521
|
+
message: "Path to request",
|
|
522
|
+
placeholder: DEFAULT_PATH,
|
|
523
|
+
defaultValue: DEFAULT_PATH,
|
|
524
|
+
validate: (v) => schemaProblem(serviceSchemaV1.shape.path, v)
|
|
525
|
+
},
|
|
526
|
+
{
|
|
527
|
+
message: "Expected status",
|
|
528
|
+
placeholder: "any 2xx",
|
|
529
|
+
defaultValue: "",
|
|
530
|
+
validate: expectProblem
|
|
531
|
+
}
|
|
532
|
+
];
|
|
533
|
+
for (const { message, ...question } of questions) {
|
|
534
|
+
const answer = await ask(message, question);
|
|
535
|
+
if (answer === null) {
|
|
536
|
+
console.log("Nothing added.");
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
answers.push(answer);
|
|
540
|
+
}
|
|
541
|
+
const [name, port, path, expect] = answers;
|
|
542
|
+
const service = { port: Number(port) };
|
|
543
|
+
if (path !== DEFAULT_PATH) service.path = path;
|
|
544
|
+
if (expect) service.expect = parseExpect(expect);
|
|
545
|
+
await saveServices(loaded, { ...services, [name]: service });
|
|
546
|
+
console.log(`Added ${name}. Try: probe check ${name}`);
|
|
547
|
+
}
|
|
456
548
|
var add_default = defineCommand({
|
|
457
549
|
meta: { name: "add", description: "Add a service" },
|
|
458
550
|
args: {
|
|
@@ -466,25 +558,25 @@ var add_default = defineCommand({
|
|
|
466
558
|
description: `Seconds to wait for this service (default: ${DEFAULT_TIMEOUT_SECONDS})`
|
|
467
559
|
}
|
|
468
560
|
},
|
|
469
|
-
async run({ args }) {
|
|
561
|
+
async run({ args: args2 }) {
|
|
470
562
|
const loaded = await openProject();
|
|
471
563
|
const services = rawServices(loaded);
|
|
472
|
-
if (Object.hasOwn(services,
|
|
564
|
+
if (Object.hasOwn(services, args2.name)) {
|
|
473
565
|
throw new ProbeError(
|
|
474
566
|
"SERVICE_EXISTS",
|
|
475
|
-
`service "${
|
|
567
|
+
`service "${args2.name}" already exists. Remove it first: probe rm ${args2.name}`
|
|
476
568
|
);
|
|
477
569
|
}
|
|
478
|
-
const service = { port: parseInteger(String(
|
|
479
|
-
if (
|
|
480
|
-
if (
|
|
481
|
-
if (
|
|
482
|
-
if (
|
|
483
|
-
secondsToMs("--timeout", String(
|
|
484
|
-
service.timeout = Number(
|
|
570
|
+
const service = { port: parseInteger(String(args2.port), "--port") };
|
|
571
|
+
if (args2.host) service.host = args2.host;
|
|
572
|
+
if (args2.path) service.path = args2.path;
|
|
573
|
+
if (args2.expect) service.expect = parseExpect(String(args2.expect));
|
|
574
|
+
if (args2.timeout !== void 0) {
|
|
575
|
+
secondsToMs("--timeout", String(args2.timeout));
|
|
576
|
+
service.timeout = Number(args2.timeout);
|
|
485
577
|
}
|
|
486
|
-
await saveServices(loaded, { ...services, [
|
|
487
|
-
console.log(`Added ${
|
|
578
|
+
await saveServices(loaded, { ...services, [args2.name]: service });
|
|
579
|
+
console.log(`Added ${args2.name}. Try: probe check ${args2.name}`);
|
|
488
580
|
}
|
|
489
581
|
});
|
|
490
582
|
|
|
@@ -606,13 +698,13 @@ var check_default = defineCommand2({
|
|
|
606
698
|
description: `Seconds to wait for each service (default: its own timeout, else ${DEFAULT_TIMEOUT_SECONDS})`
|
|
607
699
|
}
|
|
608
700
|
},
|
|
609
|
-
async run({ args }) {
|
|
610
|
-
const forceTimeout =
|
|
611
|
-
const timeoutMs = secondsToMs("--timeout",
|
|
701
|
+
async run({ args: args2 }) {
|
|
702
|
+
const forceTimeout = args2.timeout !== void 0;
|
|
703
|
+
const timeoutMs = secondsToMs("--timeout", args2.timeout ?? String(DEFAULT_TIMEOUT_SECONDS));
|
|
612
704
|
const { config, paths } = await openProject();
|
|
613
705
|
const skipped = await readSkippedServices(paths);
|
|
614
|
-
const results = await runChecks(config,
|
|
615
|
-
if (
|
|
706
|
+
const results = await runChecks(config, args2._, { timeoutMs, forceTimeout }, skipped);
|
|
707
|
+
if (args2.json) {
|
|
616
708
|
console.log(JSON.stringify(results, null, 2));
|
|
617
709
|
} else if (results.length === 0) {
|
|
618
710
|
console.log("No services yet. Add one: probe add api -p 3000");
|
|
@@ -623,11 +715,190 @@ var check_default = defineCommand2({
|
|
|
623
715
|
}
|
|
624
716
|
});
|
|
625
717
|
|
|
718
|
+
// src/commands/completion.ts
|
|
719
|
+
import { defineCommand as defineCommand3 } from "citty";
|
|
720
|
+
|
|
721
|
+
// src/completion/spec.ts
|
|
722
|
+
var SHELLS = ["zsh", "bash", "fish"];
|
|
723
|
+
var COMPLETION_COMMAND = "completion";
|
|
724
|
+
var SERVICE_COMMANDS = ["rm", "check", "wait", "open", "curl"];
|
|
725
|
+
var ROOT_OPTIONS = ["--interval", "--help", "--version"];
|
|
726
|
+
var SERVICES_QUERY = `probe ${COMPLETE_COMMAND} services 2>/dev/null`;
|
|
727
|
+
var HELP_OPTION = "--help";
|
|
728
|
+
function completionSpec(commands2) {
|
|
729
|
+
return Object.entries(commands2).map(([name, command]) => {
|
|
730
|
+
const meta2 = command.meta ?? {};
|
|
731
|
+
const args2 = command.args ?? {};
|
|
732
|
+
const options = Object.entries(args2).filter(([, arg]) => arg.type !== "positional").map(([option]) => `--${option}`);
|
|
733
|
+
return {
|
|
734
|
+
name,
|
|
735
|
+
description: meta2.description ?? "",
|
|
736
|
+
options: [...options, HELP_OPTION],
|
|
737
|
+
words: name === COMPLETION_COMMAND ? [...SHELLS] : [],
|
|
738
|
+
services: SERVICE_COMMANDS.includes(name)
|
|
739
|
+
};
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
// src/completion/bash.ts
|
|
744
|
+
function bashScript(commands2) {
|
|
745
|
+
const cases = (pick) => commands2.filter((command) => pick(command).length > 0).map((command) => ` ${command.name}) words="${pick(command).join(" ")}" ;;`);
|
|
746
|
+
const withServices = commands2.filter((command) => command.services).map(({ name }) => name);
|
|
747
|
+
return [
|
|
748
|
+
'# bash completion for probe. Load it with: eval "$(probe completion bash)"',
|
|
749
|
+
"_probe() {",
|
|
750
|
+
` local cur="\${COMP_WORDS[COMP_CWORD]}" cmd="" words="" i`,
|
|
751
|
+
" for ((i = 1; i < COMP_CWORD; i++)); do",
|
|
752
|
+
` case "\${COMP_WORDS[i]}" in`,
|
|
753
|
+
" -*) ;;",
|
|
754
|
+
` *) cmd="\${COMP_WORDS[i]}"; break ;;`,
|
|
755
|
+
" esac",
|
|
756
|
+
" done",
|
|
757
|
+
' if [ -z "$cmd" ]; then',
|
|
758
|
+
' case "$cur" in',
|
|
759
|
+
` -*) words="${ROOT_OPTIONS.join(" ")}" ;;`,
|
|
760
|
+
` *) words="${commands2.map(({ name }) => name).join(" ")}" ;;`,
|
|
761
|
+
" esac",
|
|
762
|
+
` elif [ "\${cur#-}" != "$cur" ]; then`,
|
|
763
|
+
' case "$cmd" in',
|
|
764
|
+
...cases((command) => command.options),
|
|
765
|
+
" esac",
|
|
766
|
+
" else",
|
|
767
|
+
' case "$cmd" in',
|
|
768
|
+
` ${withServices.join("|")}) words="$(${SERVICES_QUERY})" ;;`,
|
|
769
|
+
...cases((command) => command.words),
|
|
770
|
+
" esac",
|
|
771
|
+
" fi",
|
|
772
|
+
' COMPREPLY=($(compgen -W "$words" -- "$cur"))',
|
|
773
|
+
"}",
|
|
774
|
+
"complete -F _probe probe",
|
|
775
|
+
""
|
|
776
|
+
].join("\n");
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
// src/completion/fish.ts
|
|
780
|
+
var quoted = (text) => `'${text.replaceAll("\\", "\\\\").replaceAll("'", "\\'")}'`;
|
|
781
|
+
var longOption = (option) => `-l ${option.replace(/^--/, "")}`;
|
|
782
|
+
function fishScript(commands2) {
|
|
783
|
+
const inCommand = (names) => `-n '__fish_seen_subcommand_from ${names.join(" ")}'`;
|
|
784
|
+
const withServices = commands2.filter((command) => command.services).map(({ name }) => name);
|
|
785
|
+
return [
|
|
786
|
+
"# fish completion for probe. Load it with: probe completion fish | source",
|
|
787
|
+
"function __probe_services",
|
|
788
|
+
` ${SERVICES_QUERY}`,
|
|
789
|
+
"end",
|
|
790
|
+
// No file names: probe takes none.
|
|
791
|
+
"complete -c probe -f",
|
|
792
|
+
...ROOT_OPTIONS.map(
|
|
793
|
+
(option) => `complete -c probe -n __fish_use_subcommand ${longOption(option)}`
|
|
794
|
+
),
|
|
795
|
+
...commands2.map(
|
|
796
|
+
({ name, description }) => `complete -c probe -n __fish_use_subcommand -a ${name} -d ${quoted(description)}`
|
|
797
|
+
),
|
|
798
|
+
`complete -c probe ${inCommand(withServices)} -a '(__probe_services)'`,
|
|
799
|
+
...commands2.flatMap((command) => [
|
|
800
|
+
...command.options.map(
|
|
801
|
+
(option) => `complete -c probe ${inCommand([command.name])} ${longOption(option)}`
|
|
802
|
+
),
|
|
803
|
+
...command.words.length > 0 ? [`complete -c probe ${inCommand([command.name])} -a ${quoted(command.words.join(" "))}`] : []
|
|
804
|
+
]),
|
|
805
|
+
""
|
|
806
|
+
].join("\n");
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
// src/completion/zsh.ts
|
|
810
|
+
function described({ name, description }) {
|
|
811
|
+
const text = `${name}:${description.replaceAll(":", "\\:")}`;
|
|
812
|
+
return `'${text.replaceAll("'", "'\\''")}'`;
|
|
813
|
+
}
|
|
814
|
+
function zshScript(commands2) {
|
|
815
|
+
const cases = (pick) => commands2.filter((command) => pick(command).length > 0).map((command) => ` ${command.name}) compadd -- ${pick(command).join(" ")} ;;`);
|
|
816
|
+
const withServices = commands2.filter((command) => command.services).map(({ name }) => name);
|
|
817
|
+
return [
|
|
818
|
+
"#compdef probe",
|
|
819
|
+
'# zsh completion for probe. Load it after compinit with: eval "$(probe completion zsh)"',
|
|
820
|
+
"_probe() {",
|
|
821
|
+
" local -a commands services",
|
|
822
|
+
" commands=(",
|
|
823
|
+
...commands2.map((command) => ` ${described(command)}`),
|
|
824
|
+
" )",
|
|
825
|
+
" if (( CURRENT == 2 )); then",
|
|
826
|
+
" if [[ $words[CURRENT] == -* ]]; then",
|
|
827
|
+
` compadd -- ${ROOT_OPTIONS.join(" ")}`,
|
|
828
|
+
" else",
|
|
829
|
+
" _describe 'command' commands",
|
|
830
|
+
" fi",
|
|
831
|
+
" return",
|
|
832
|
+
" fi",
|
|
833
|
+
" if [[ $words[CURRENT] == -* ]]; then",
|
|
834
|
+
" case $words[2] in",
|
|
835
|
+
...cases((command) => command.options).map((line) => ` ${line}`),
|
|
836
|
+
" esac",
|
|
837
|
+
" return",
|
|
838
|
+
" fi",
|
|
839
|
+
" case $words[2] in",
|
|
840
|
+
` ${withServices.join("|")})`,
|
|
841
|
+
` services=(\${(f)"$(${SERVICES_QUERY})"})`,
|
|
842
|
+
" compadd -a services ;;",
|
|
843
|
+
...cases((command) => command.words),
|
|
844
|
+
" esac",
|
|
845
|
+
"}",
|
|
846
|
+
"if (( $+functions[compdef] )); then compdef _probe probe; fi",
|
|
847
|
+
"# Saved as _probe in $fpath, this file is the function itself: run it.",
|
|
848
|
+
'if [[ $funcstack[1] == _probe ]]; then _probe "$@"; fi',
|
|
849
|
+
""
|
|
850
|
+
].join("\n");
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
// src/commands/completion.ts
|
|
854
|
+
var SCRIPTS = {
|
|
855
|
+
zsh: zshScript,
|
|
856
|
+
bash: bashScript,
|
|
857
|
+
fish: fishScript
|
|
858
|
+
};
|
|
859
|
+
var meta = {
|
|
860
|
+
name: COMPLETION_COMMAND,
|
|
861
|
+
description: `Print the shell completion script (${SHELLS.join(", ")})`
|
|
862
|
+
};
|
|
863
|
+
var args = {
|
|
864
|
+
shell: { type: "positional", description: SHELLS.join(", "), required: true }
|
|
865
|
+
};
|
|
866
|
+
function completion(others) {
|
|
867
|
+
const commands2 = { ...others, [COMPLETION_COMMAND]: { meta, args } };
|
|
868
|
+
return defineCommand3({
|
|
869
|
+
meta,
|
|
870
|
+
args,
|
|
871
|
+
run({ args: { shell } }) {
|
|
872
|
+
const script = Object.hasOwn(SCRIPTS, shell) ? SCRIPTS[shell] : void 0;
|
|
873
|
+
if (!script) {
|
|
874
|
+
throw new ProbeError(
|
|
875
|
+
"USAGE",
|
|
876
|
+
`no completion for the shell "${shell}". Supported: ${SHELLS.join(", ")}.`
|
|
877
|
+
);
|
|
878
|
+
}
|
|
879
|
+
process.stdout.write(script(completionSpec(commands2)));
|
|
880
|
+
}
|
|
881
|
+
});
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
// src/commands/curl.ts
|
|
885
|
+
import { defineCommand as defineCommand4 } from "citty";
|
|
886
|
+
var curl_default = defineCommand4({
|
|
887
|
+
meta: { name: "curl", description: "Print the curl command that requests a service" },
|
|
888
|
+
args: {
|
|
889
|
+
name: { type: "positional", description: "Service name", required: true }
|
|
890
|
+
},
|
|
891
|
+
async run({ args: args2 }) {
|
|
892
|
+
const { config } = await openProject();
|
|
893
|
+
console.log(curlCommand(namedService(config, args2.name)));
|
|
894
|
+
}
|
|
895
|
+
});
|
|
896
|
+
|
|
626
897
|
// src/commands/init.ts
|
|
627
898
|
import { existsSync } from "fs";
|
|
628
899
|
import { mkdir, readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
|
|
629
900
|
import { join as join5 } from "path";
|
|
630
|
-
import { defineCommand as
|
|
901
|
+
import { defineCommand as defineCommand5 } from "citty";
|
|
631
902
|
var IGNORED = [`${PROBE_DIR}/${LOCAL_FILE}`, `${PROBE_DIR}/${BACKUP_GLOB}`];
|
|
632
903
|
var INITIAL_CONFIG = `# probe - local services to health-check. Edit by hand or with \`probe add\`.
|
|
633
904
|
schemaVersion: ${CURRENT_SCHEMA_VERSION}
|
|
@@ -643,26 +914,29 @@ async function ignoreLocalFiles(root) {
|
|
|
643
914
|
await writeFile5(file, `${current}${separator}${missing.join("\n")}
|
|
644
915
|
`);
|
|
645
916
|
}
|
|
646
|
-
|
|
917
|
+
async function initProject(root) {
|
|
918
|
+
const paths = pathsFor(root);
|
|
919
|
+
if (existsSync(paths.dir)) {
|
|
920
|
+
throw new ProbeError("ALREADY_INITIALIZED", `${PROBE_DIR}/ already exists here.`);
|
|
921
|
+
}
|
|
922
|
+
await mkdir(paths.dir);
|
|
923
|
+
await writeFile5(paths.config, INITIAL_CONFIG);
|
|
924
|
+
await ignoreLocalFiles(paths.root);
|
|
925
|
+
}
|
|
926
|
+
var init_default = defineCommand5({
|
|
647
927
|
meta: {
|
|
648
928
|
name: "init",
|
|
649
929
|
description: "Create .probe/ and the .gitignore entries"
|
|
650
930
|
},
|
|
651
931
|
async run() {
|
|
652
|
-
|
|
653
|
-
if (existsSync(paths.dir)) {
|
|
654
|
-
throw new ProbeError("ALREADY_INITIALIZED", `${PROBE_DIR}/ already exists here.`);
|
|
655
|
-
}
|
|
656
|
-
await mkdir(paths.dir);
|
|
657
|
-
await writeFile5(paths.config, INITIAL_CONFIG);
|
|
658
|
-
await ignoreLocalFiles(paths.root);
|
|
932
|
+
await initProject(process.cwd());
|
|
659
933
|
console.log(`Created ${PROBE_DIR}/. Next: probe add api -p 3000`);
|
|
660
934
|
}
|
|
661
935
|
});
|
|
662
936
|
|
|
663
937
|
// src/commands/ls.ts
|
|
664
|
-
import { defineCommand as
|
|
665
|
-
var ls_default =
|
|
938
|
+
import { defineCommand as defineCommand6 } from "citty";
|
|
939
|
+
var ls_default = defineCommand6({
|
|
666
940
|
meta: { name: "ls", description: "List services" },
|
|
667
941
|
async run() {
|
|
668
942
|
const { config, paths } = await openProject();
|
|
@@ -675,8 +949,8 @@ var ls_default = defineCommand4({
|
|
|
675
949
|
});
|
|
676
950
|
|
|
677
951
|
// src/commands/migrate.ts
|
|
678
|
-
import { defineCommand as
|
|
679
|
-
var migrate_default =
|
|
952
|
+
import { defineCommand as defineCommand7 } from "citty";
|
|
953
|
+
var migrate_default = defineCommand7({
|
|
680
954
|
meta: { name: "migrate", description: "Migrate the config to the current schema version" },
|
|
681
955
|
async run() {
|
|
682
956
|
const paths = requireProbeDir(process.cwd());
|
|
@@ -688,27 +962,71 @@ var migrate_default = defineCommand5({
|
|
|
688
962
|
}
|
|
689
963
|
});
|
|
690
964
|
|
|
965
|
+
// src/commands/open.ts
|
|
966
|
+
import { defineCommand as defineCommand8 } from "citty";
|
|
967
|
+
|
|
968
|
+
// src/open-url.ts
|
|
969
|
+
import { spawn as nodeSpawn } from "child_process";
|
|
970
|
+
function openerCommand(platform) {
|
|
971
|
+
return platform === "darwin" ? "open" : "xdg-open";
|
|
972
|
+
}
|
|
973
|
+
function openUrl(url, platform = process.platform, spawn2 = nodeSpawn) {
|
|
974
|
+
return new Promise((resolve2) => {
|
|
975
|
+
const child = spawn2(openerCommand(platform), [url], { detached: true, stdio: "ignore" });
|
|
976
|
+
child.once("error", () => resolve2(false));
|
|
977
|
+
child.once("spawn", () => {
|
|
978
|
+
child.unref();
|
|
979
|
+
resolve2(true);
|
|
980
|
+
});
|
|
981
|
+
});
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
// src/commands/open.ts
|
|
985
|
+
var open_default = defineCommand8({
|
|
986
|
+
meta: { name: "open", description: "Open the URL of a service in the browser" },
|
|
987
|
+
args: {
|
|
988
|
+
name: { type: "positional", description: "Service name", required: true }
|
|
989
|
+
},
|
|
990
|
+
async run({ args: args2 }) {
|
|
991
|
+
const { config } = await openProject();
|
|
992
|
+
const url = targetUrl(namedService(config, args2.name));
|
|
993
|
+
if (await openUrl(url)) {
|
|
994
|
+
console.log(`Opened ${url}`);
|
|
995
|
+
return;
|
|
996
|
+
}
|
|
997
|
+
console.log(url);
|
|
998
|
+
console.error(`probe: ${openerCommand(process.platform)} not found, open the URL yourself.`);
|
|
999
|
+
process.exitCode = EXIT_DOWN;
|
|
1000
|
+
}
|
|
1001
|
+
});
|
|
1002
|
+
|
|
691
1003
|
// src/commands/rm.ts
|
|
692
|
-
import { defineCommand as
|
|
693
|
-
var rm_default =
|
|
1004
|
+
import { defineCommand as defineCommand9 } from "citty";
|
|
1005
|
+
var rm_default = defineCommand9({
|
|
694
1006
|
meta: { name: "rm", description: "Remove a service" },
|
|
695
1007
|
args: {
|
|
696
|
-
name: { type: "positional", description: "Service name", required: true }
|
|
1008
|
+
name: { type: "positional", description: "Service name", required: true },
|
|
1009
|
+
yes: { type: "boolean", alias: "y", description: "Remove without asking" }
|
|
697
1010
|
},
|
|
698
|
-
async run({ args }) {
|
|
1011
|
+
async run({ args: args2 }) {
|
|
699
1012
|
const loaded = await openProject();
|
|
700
1013
|
const services = rawServices(loaded);
|
|
701
|
-
if (!Object.hasOwn(services,
|
|
702
|
-
throw new ProbeError("SERVICE_UNKNOWN", `unknown service "${
|
|
1014
|
+
if (!Object.hasOwn(services, args2.name)) {
|
|
1015
|
+
throw new ProbeError("SERVICE_UNKNOWN", `unknown service "${args2.name}". See: probe ls`);
|
|
1016
|
+
}
|
|
1017
|
+
const asks = !args2.yes && isInteractive(process.stdin, process.stdout, process.env);
|
|
1018
|
+
if (asks && !await confirm(`Remove ${args2.name}?`)) {
|
|
1019
|
+
console.log("Nothing removed.");
|
|
1020
|
+
return;
|
|
703
1021
|
}
|
|
704
|
-
const { [
|
|
1022
|
+
const { [args2.name]: _removed, ...rest } = services;
|
|
705
1023
|
await saveServices(loaded, rest);
|
|
706
|
-
console.log(`Removed ${
|
|
1024
|
+
console.log(`Removed ${args2.name}.`);
|
|
707
1025
|
}
|
|
708
1026
|
});
|
|
709
1027
|
|
|
710
1028
|
// src/commands/status.ts
|
|
711
|
-
import { defineCommand as
|
|
1029
|
+
import { defineCommand as defineCommand10 } from "citty";
|
|
712
1030
|
|
|
713
1031
|
// src/update/notifier.ts
|
|
714
1032
|
import { spawn } from "child_process";
|
|
@@ -734,8 +1052,8 @@ function writeCache(dir, cache) {
|
|
|
734
1052
|
writeFileSync(join6(dir, UPDATE_CACHE_FILE), JSON.stringify(cache));
|
|
735
1053
|
}
|
|
736
1054
|
function isNewer(latest, current) {
|
|
737
|
-
const
|
|
738
|
-
const [a, b] = [
|
|
1055
|
+
const parse3 = (version) => (version.split("-")[0] ?? "").split(".").map(Number);
|
|
1056
|
+
const [a, b] = [parse3(latest), parse3(current)];
|
|
739
1057
|
for (let index = 0; index < 3; index++) {
|
|
740
1058
|
const difference = (a[index] || 0) - (b[index] || 0);
|
|
741
1059
|
if (difference !== 0) return difference > 0;
|
|
@@ -797,7 +1115,7 @@ function describeUpdate(colors) {
|
|
|
797
1115
|
if (!readCache(cacheDir())?.latest) return colors.dim(`not checked yet (${CLI_VERSION})`);
|
|
798
1116
|
return colors.green(`up to date (${CLI_VERSION})`);
|
|
799
1117
|
}
|
|
800
|
-
var status_default =
|
|
1118
|
+
var status_default = defineCommand10({
|
|
801
1119
|
meta: { name: "status", description: "Show pending migration, Makefile state and updates" },
|
|
802
1120
|
async run() {
|
|
803
1121
|
const colors = stdoutColors();
|
|
@@ -819,8 +1137,8 @@ var status_default = defineCommand7({
|
|
|
819
1137
|
|
|
820
1138
|
// src/commands/sync.ts
|
|
821
1139
|
import { relative as relative2 } from "path";
|
|
822
|
-
import { defineCommand as
|
|
823
|
-
var sync_default =
|
|
1140
|
+
import { defineCommand as defineCommand11 } from "citty";
|
|
1141
|
+
var sync_default = defineCommand11({
|
|
824
1142
|
meta: {
|
|
825
1143
|
name: "sync",
|
|
826
1144
|
description: "Add or regenerate the probe block of the Makefile (optional)"
|
|
@@ -836,7 +1154,7 @@ var sync_default = defineCommand8({
|
|
|
836
1154
|
});
|
|
837
1155
|
|
|
838
1156
|
// src/commands/wait.ts
|
|
839
|
-
import { defineCommand as
|
|
1157
|
+
import { defineCommand as defineCommand12 } from "citty";
|
|
840
1158
|
|
|
841
1159
|
// src/checkers/poll.ts
|
|
842
1160
|
import { setTimeout as delay } from "timers/promises";
|
|
@@ -885,7 +1203,7 @@ function progressReporter() {
|
|
|
885
1203
|
return lines;
|
|
886
1204
|
};
|
|
887
1205
|
}
|
|
888
|
-
var wait_default =
|
|
1206
|
+
var wait_default = defineCommand12({
|
|
889
1207
|
meta: {
|
|
890
1208
|
name: "wait",
|
|
891
1209
|
description: "Check again and again until services are up (all of them by default)"
|
|
@@ -908,14 +1226,14 @@ var wait_default = defineCommand9({
|
|
|
908
1226
|
default: String(DEFAULT_WAIT_MAX_INTERVAL_SECONDS)
|
|
909
1227
|
}
|
|
910
1228
|
},
|
|
911
|
-
async run({ args }) {
|
|
912
|
-
const deadlineMs = secondsToMs("--timeout",
|
|
913
|
-
const intervalMs = secondsToMs("--interval",
|
|
914
|
-
const maxIntervalMs = secondsToMs("--max-interval",
|
|
1229
|
+
async run({ args: args2 }) {
|
|
1230
|
+
const deadlineMs = secondsToMs("--timeout", args2.timeout);
|
|
1231
|
+
const intervalMs = secondsToMs("--interval", args2.interval);
|
|
1232
|
+
const maxIntervalMs = secondsToMs("--max-interval", args2["max-interval"]);
|
|
915
1233
|
if (intervalMs > maxIntervalMs) {
|
|
916
1234
|
throw new ProbeError(
|
|
917
1235
|
"USAGE",
|
|
918
|
-
`--interval (${
|
|
1236
|
+
`--interval (${args2.interval}s) cannot be longer than --max-interval (${args2["max-interval"]}s).`
|
|
919
1237
|
);
|
|
920
1238
|
}
|
|
921
1239
|
const { config, paths } = await openProject();
|
|
@@ -925,7 +1243,7 @@ var wait_default = defineCommand9({
|
|
|
925
1243
|
// A service with its own timeout keeps it here, within what is left of the total.
|
|
926
1244
|
(_passMs, remainingMs) => runChecks(
|
|
927
1245
|
config,
|
|
928
|
-
|
|
1246
|
+
args2._,
|
|
929
1247
|
{ timeoutMs: DEFAULT_TIMEOUT_SECONDS * 1e3, maxTimeoutMs: remainingMs },
|
|
930
1248
|
skipped
|
|
931
1249
|
),
|
|
@@ -939,7 +1257,7 @@ var wait_default = defineCommand9({
|
|
|
939
1257
|
}
|
|
940
1258
|
}
|
|
941
1259
|
);
|
|
942
|
-
if (
|
|
1260
|
+
if (args2.json) {
|
|
943
1261
|
console.log(JSON.stringify(results, null, 2));
|
|
944
1262
|
} else if (results.length === 0) {
|
|
945
1263
|
console.log("No services yet. Add one: probe add api -p 3000");
|
|
@@ -954,6 +1272,22 @@ var wait_default = defineCommand9({
|
|
|
954
1272
|
}
|
|
955
1273
|
});
|
|
956
1274
|
|
|
1275
|
+
// src/completion/services.ts
|
|
1276
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
1277
|
+
import { parse as parse2 } from "yaml";
|
|
1278
|
+
async function completableServices(cwd) {
|
|
1279
|
+
try {
|
|
1280
|
+
const paths = findProbeDir(cwd);
|
|
1281
|
+
if (!paths) return [];
|
|
1282
|
+
const raw = parse2(await readFile6(paths.config, "utf8"));
|
|
1283
|
+
const services = raw?.services;
|
|
1284
|
+
if (typeof services !== "object" || services === null) return [];
|
|
1285
|
+
return Object.keys(services).filter((name) => SERVICE_NAME_PATTERN.test(name));
|
|
1286
|
+
} catch {
|
|
1287
|
+
return [];
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
|
|
957
1291
|
// src/dashboard/saves.ts
|
|
958
1292
|
function describe(file, error) {
|
|
959
1293
|
const code = error?.code;
|
|
@@ -984,7 +1318,7 @@ function createSaveQueue() {
|
|
|
984
1318
|
}
|
|
985
1319
|
|
|
986
1320
|
// src/suggest.ts
|
|
987
|
-
var SHORT_WORD =
|
|
1321
|
+
var SHORT_WORD = 4;
|
|
988
1322
|
var TYPOS_SHORT = 1;
|
|
989
1323
|
var TYPOS_LONG = 2;
|
|
990
1324
|
function typoDistance(a, b) {
|
|
@@ -1036,7 +1370,10 @@ var EXAMPLES = {
|
|
|
1036
1370
|
{ command: "probe add admin -p 8080 --expect 200,204", note: "Accept these statuses only" },
|
|
1037
1371
|
{ command: "probe add db -p 5432 --timeout 10", note: "Give it 10 s to answer" }
|
|
1038
1372
|
],
|
|
1039
|
-
rm: [
|
|
1373
|
+
rm: [
|
|
1374
|
+
{ command: "probe rm api", note: "Remove the service named api" },
|
|
1375
|
+
{ command: "probe rm api --yes", note: "Remove it without the question" }
|
|
1376
|
+
],
|
|
1040
1377
|
ls: [{ command: "probe ls", note: "List the services and their URL" }],
|
|
1041
1378
|
check: [
|
|
1042
1379
|
{ command: "probe check", note: "Check every service" },
|
|
@@ -1049,9 +1386,19 @@ var EXAMPLES = {
|
|
|
1049
1386
|
{ command: "probe wait api --timeout 120", note: "Wait for api, 2 minutes at most" },
|
|
1050
1387
|
{ command: "probe wait --interval 1 --max-interval 10", note: "Check less often" }
|
|
1051
1388
|
],
|
|
1389
|
+
open: [{ command: "probe open front", note: "Open the URL of front in the browser" }],
|
|
1390
|
+
curl: [
|
|
1391
|
+
{ command: "probe curl api", note: "Print the curl command for api" },
|
|
1392
|
+
{ command: "probe curl api | sh", note: "Run it" }
|
|
1393
|
+
],
|
|
1052
1394
|
status: [{ command: "probe status", note: "Version, migration, Makefile block" }],
|
|
1053
1395
|
sync: [{ command: "probe sync", note: "Write the probe block of the Makefile" }],
|
|
1054
|
-
migrate: [{ command: "probe migrate", note: "Bring .probe/config.yml up to date" }]
|
|
1396
|
+
migrate: [{ command: "probe migrate", note: "Bring .probe/config.yml up to date" }],
|
|
1397
|
+
completion: [
|
|
1398
|
+
{ command: "probe completion zsh", note: 'In ~/.zshrc: eval "$(probe completion zsh)"' },
|
|
1399
|
+
{ command: "probe completion bash", note: 'In ~/.bashrc: eval "$(probe completion bash)"' },
|
|
1400
|
+
{ command: "probe completion fish | source", note: "In ~/.config/fish/config.fish" }
|
|
1401
|
+
]
|
|
1055
1402
|
};
|
|
1056
1403
|
function formatExamples(name, color) {
|
|
1057
1404
|
const examples = EXAMPLES[name ?? ROOT] ?? [];
|
|
@@ -1067,7 +1414,8 @@ function formatExamples(name, color) {
|
|
|
1067
1414
|
}
|
|
1068
1415
|
|
|
1069
1416
|
// src/cli.ts
|
|
1070
|
-
var
|
|
1417
|
+
var commands = { init: init_default, add: add_default, rm: rm_default, ls: ls_default, check: check_default, wait: wait_default, open: open_default, curl: curl_default, status: status_default, sync: sync_default, migrate: migrate_default };
|
|
1418
|
+
var main = defineCommand13({
|
|
1071
1419
|
meta: {
|
|
1072
1420
|
name: "probe",
|
|
1073
1421
|
version: CLI_VERSION,
|
|
@@ -1081,13 +1429,13 @@ var main = defineCommand10({
|
|
|
1081
1429
|
default: String(DASHBOARD_INTERVAL_SECONDS)
|
|
1082
1430
|
}
|
|
1083
1431
|
},
|
|
1084
|
-
subCommands: {
|
|
1432
|
+
subCommands: { ...commands, completion: completion(commands) }
|
|
1085
1433
|
});
|
|
1086
1434
|
async function printUsage(command, parent) {
|
|
1087
1435
|
const usage = await renderUsage(command, parent);
|
|
1088
1436
|
const { color } = stdoutTerminal();
|
|
1089
|
-
const
|
|
1090
|
-
const examples = formatExamples(command === main ? void 0 :
|
|
1437
|
+
const meta2 = await (typeof command.meta === "function" ? command.meta() : command.meta);
|
|
1438
|
+
const examples = formatExamples(command === main ? void 0 : meta2?.name, color);
|
|
1091
1439
|
console.log(`${color ? usage : stripStyles(usage)}
|
|
1092
1440
|
${examples ? `
|
|
1093
1441
|
${examples}
|
|
@@ -1095,7 +1443,7 @@ ${examples}
|
|
|
1095
1443
|
}
|
|
1096
1444
|
function notifyUpdate(rawArgs) {
|
|
1097
1445
|
const cliPath = process.argv[1];
|
|
1098
|
-
if (updateCheckDisabled() || !cliPath) return;
|
|
1446
|
+
if (updateCheckDisabled() || !cliPath || rawArgs[0] === COMPLETION_COMMAND) return;
|
|
1099
1447
|
scheduleRefresh(cacheDir(), cliPath);
|
|
1100
1448
|
if (!detectTerminal(process.stderr, process.env).isTTY || rawArgs.includes("--json") || rawArgs[0] === "status")
|
|
1101
1449
|
return;
|
|
@@ -1131,7 +1479,7 @@ async function openedDashboard(rawArgs) {
|
|
|
1131
1479
|
if (!asked) return false;
|
|
1132
1480
|
throw new ProbeError("USAGE", "No services yet. Add one: probe add api -p 3000");
|
|
1133
1481
|
}
|
|
1134
|
-
const { openDashboard } = await import("./open-
|
|
1482
|
+
const { openDashboard } = await import("./open-T3OQH2O4.js");
|
|
1135
1483
|
let settings = initialDashboardSettings(askedMs, await readDashboardPreferences(paths));
|
|
1136
1484
|
const saves = createSaveQueue();
|
|
1137
1485
|
const local = `${PROBE_DIR}/${LOCAL_FILE}`;
|
|
@@ -1155,6 +1503,23 @@ async function openedDashboard(rawArgs) {
|
|
|
1155
1503
|
for (const failure of saves.failures()) console.error(`probe: not saved: ${failure}`);
|
|
1156
1504
|
return true;
|
|
1157
1505
|
}
|
|
1506
|
+
async function offeredInit(error, rawArgs) {
|
|
1507
|
+
if (!(error instanceof ProbeError) || error.code !== "NO_PROBE_DIR") return false;
|
|
1508
|
+
if (rawArgs.includes("--json")) return false;
|
|
1509
|
+
if (!isInteractive(process.stdin, process.stdout, process.env)) return false;
|
|
1510
|
+
const root = process.cwd();
|
|
1511
|
+
if (!await confirm(`No ${PROBE_DIR}/ here. Create it in ${root}?`)) return false;
|
|
1512
|
+
await initProject(root);
|
|
1513
|
+
console.log(`Created ${PROBE_DIR}/.`);
|
|
1514
|
+
return true;
|
|
1515
|
+
}
|
|
1516
|
+
function wantsGuidedAdd(rawArgs) {
|
|
1517
|
+
return rawArgs.length === 1 && rawArgs[0] === "add" && isInteractive(process.stdin, process.stdout, process.env);
|
|
1518
|
+
}
|
|
1519
|
+
async function dispatch(rawArgs) {
|
|
1520
|
+
if (wantsGuidedAdd(rawArgs)) return guidedAdd();
|
|
1521
|
+
if (!await openedDashboard(rawArgs)) await runCommand(main, { rawArgs });
|
|
1522
|
+
}
|
|
1158
1523
|
var UNKNOWN_COMMAND = "E_UNKNOWN_COMMAND";
|
|
1159
1524
|
function didYouMean(error, rawArgs) {
|
|
1160
1525
|
if (error.code !== UNKNOWN_COMMAND) return "";
|
|
@@ -1167,13 +1532,23 @@ function flushed(stream) {
|
|
|
1167
1532
|
stream.write("", () => done());
|
|
1168
1533
|
});
|
|
1169
1534
|
}
|
|
1535
|
+
async function printCompletions(kind) {
|
|
1536
|
+
if (kind !== "services") return;
|
|
1537
|
+
for (const name of await completableServices(process.cwd())) console.log(name);
|
|
1538
|
+
}
|
|
1170
1539
|
async function run(rawArgs) {
|
|
1171
1540
|
if (rawArgs[0] === REFRESH_COMMAND) return refreshCache(cacheDir());
|
|
1541
|
+
if (rawArgs[0] === COMPLETE_COMMAND) return printCompletions(rawArgs[1]);
|
|
1172
1542
|
const wantsHelp = rawArgs.includes("--help") || rawArgs.includes("-h");
|
|
1173
1543
|
const wantsVersion = rawArgs.length === 1 && ["--version", "-v"].includes(rawArgs[0]);
|
|
1174
1544
|
if (wantsHelp || wantsVersion) return runMain(main, { rawArgs, showUsage: printUsage });
|
|
1175
1545
|
try {
|
|
1176
|
-
|
|
1546
|
+
try {
|
|
1547
|
+
await dispatch(rawArgs);
|
|
1548
|
+
} catch (error) {
|
|
1549
|
+
if (!await offeredInit(error, rawArgs)) throw error;
|
|
1550
|
+
await dispatch(rawArgs);
|
|
1551
|
+
}
|
|
1177
1552
|
} catch (error) {
|
|
1178
1553
|
process.exitCode = EXIT_ERROR;
|
|
1179
1554
|
if (error instanceof ProbeError) {
|