@ninhache/probe 0.10.0 → 0.11.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
CHANGED
|
@@ -121,7 +121,7 @@ of macOS.
|
|
|
121
121
|
| `e` | Edit the selected service |
|
|
122
122
|
| `d` | Remove the selected service, after a question |
|
|
123
123
|
| `:` | Find a command by what it does; `Enter` runs |
|
|
124
|
-
| `o` | Options: interval and
|
|
124
|
+
| `o` | Options: interval, timeout and notifications |
|
|
125
125
|
| `?` | Every key; `?` or `Esc` closes |
|
|
126
126
|
| `q` | Quit |
|
|
127
127
|
|
|
@@ -143,11 +143,15 @@ is replaced by `(hidden)`. None of it is written anywhere, and `probe check --js
|
|
|
143
143
|
The panels save what you change in `.probe/local.yml`, for your machine only:
|
|
144
144
|
|
|
145
145
|
```yaml
|
|
146
|
-
dashboard: { interval: 5, timeout: 3 }
|
|
146
|
+
dashboard: { interval: 5, timeout: 3, notify: true }
|
|
147
147
|
services:
|
|
148
148
|
db: { skip: true }
|
|
149
149
|
```
|
|
150
150
|
|
|
151
|
+
With `notify` on, a desktop notification says when a service goes down or comes back: one per round of checks
|
|
152
|
+
that saw a change, none when the dashboard starts. It names the services and nothing else, and needs
|
|
153
|
+
`notify-send` on Linux (`osascript`, already there, on macOS); without it the dashboard says so once and goes on.
|
|
154
|
+
|
|
151
155
|
A skipped service is left out of `probe check`, `probe wait` and the dashboard, and shown as `skipped`. Name it
|
|
152
156
|
(`probe check db`) and it is checked anyway.
|
|
153
157
|
|
|
@@ -36,6 +36,7 @@ var EXIT_ERROR = 2;
|
|
|
36
36
|
var DETECT_PORTS = [3e3, 3001, 4200, 4321, 5173, 8e3, 8080];
|
|
37
37
|
var DETECT_PORTS_ENV = "PROBE_DETECT_PORTS";
|
|
38
38
|
var DETECT_TIMEOUT_MS = 300;
|
|
39
|
+
var NOTIFY_WAIT_MS = 5e3;
|
|
39
40
|
|
|
40
41
|
// src/errors.ts
|
|
41
42
|
var ProbeError = class extends Error {
|
|
@@ -524,6 +525,7 @@ export {
|
|
|
524
525
|
DETECT_PORTS,
|
|
525
526
|
DETECT_PORTS_ENV,
|
|
526
527
|
DETECT_TIMEOUT_MS,
|
|
528
|
+
NOTIFY_WAIT_MS,
|
|
527
529
|
CURRENT_SCHEMA_VERSION,
|
|
528
530
|
SERVICE_NAME_PATTERN,
|
|
529
531
|
serviceSchemaV1,
|
package/dist/cli.js
CHANGED
|
@@ -50,7 +50,7 @@ import {
|
|
|
50
50
|
serviceSchemaV1,
|
|
51
51
|
stripStyles,
|
|
52
52
|
targetUrl
|
|
53
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-VHDD7JN7.js";
|
|
54
54
|
import "./chunk-M3ASEG5F.js";
|
|
55
55
|
|
|
56
56
|
// src/cli.ts
|
|
@@ -624,7 +624,7 @@ async function writeLock(paths, lock) {
|
|
|
624
624
|
}
|
|
625
625
|
|
|
626
626
|
// src/version.ts
|
|
627
|
-
var CLI_VERSION = true ? "0.
|
|
627
|
+
var CLI_VERSION = true ? "0.11.0" : "0.0.0-dev";
|
|
628
628
|
|
|
629
629
|
// src/makefile/generator.ts
|
|
630
630
|
import { createHash } from "crypto";
|
|
@@ -885,6 +885,7 @@ import { dirname as dirname2, join as join5 } from "path";
|
|
|
885
885
|
import { isMap as isMap2, isScalar as isScalar2, parseDocument as parseDocument2 } from "yaml";
|
|
886
886
|
var SECTION = "dashboard";
|
|
887
887
|
var KEYS = ["interval", "timeout"];
|
|
888
|
+
var NOTIFY = "notify";
|
|
888
889
|
var SERVICES = "services";
|
|
889
890
|
var SKIP = "skip";
|
|
890
891
|
function isDelay(value) {
|
|
@@ -907,18 +908,21 @@ async function readDashboardPreferences(paths) {
|
|
|
907
908
|
const value = doc.getIn([SECTION, key]);
|
|
908
909
|
if (isDelay(value)) preferences[key] = value;
|
|
909
910
|
}
|
|
911
|
+
if (doc.getIn([SECTION, NOTIFY]) === true) preferences.notify = true;
|
|
910
912
|
return preferences;
|
|
911
913
|
}
|
|
912
914
|
function initialDashboardSettings(askedIntervalMs, saved) {
|
|
913
915
|
return {
|
|
914
916
|
intervalMs: askedIntervalMs ?? (saved.interval ?? DASHBOARD_INTERVAL_SECONDS) * 1e3,
|
|
915
|
-
timeoutMs: (saved.timeout ?? DEFAULT_TIMEOUT_SECONDS) * 1e3
|
|
917
|
+
timeoutMs: (saved.timeout ?? DEFAULT_TIMEOUT_SECONDS) * 1e3,
|
|
918
|
+
...saved.notify ? { notify: true } : {}
|
|
916
919
|
};
|
|
917
920
|
}
|
|
918
921
|
function changedPreferences(previous, next) {
|
|
919
922
|
const changed = {};
|
|
920
923
|
if (next.intervalMs !== previous.intervalMs) changed.interval = next.intervalMs / 1e3;
|
|
921
924
|
if (next.timeoutMs !== previous.timeoutMs) changed.timeout = next.timeoutMs / 1e3;
|
|
925
|
+
if (Boolean(next.notify) !== Boolean(previous.notify)) changed.notify = Boolean(next.notify);
|
|
922
926
|
return changed;
|
|
923
927
|
}
|
|
924
928
|
async function editLocal(paths, edit) {
|
|
@@ -951,6 +955,11 @@ function writeDashboardPreferences(paths, preferences) {
|
|
|
951
955
|
for (const key of KEYS) {
|
|
952
956
|
if (preferences[key] !== void 0) doc.setIn([SECTION, key], preferences[key]);
|
|
953
957
|
}
|
|
958
|
+
if (preferences.notify) doc.setIn([SECTION, NOTIFY], true);
|
|
959
|
+
else if (preferences.notify === false && isMap2(section)) {
|
|
960
|
+
section.delete(NOTIFY);
|
|
961
|
+
if (section.items.length === 0) doc.delete(SECTION);
|
|
962
|
+
}
|
|
954
963
|
return true;
|
|
955
964
|
});
|
|
956
965
|
}
|
|
@@ -1796,7 +1805,7 @@ async function openedDashboard(rawArgs) {
|
|
|
1796
1805
|
if (!asked) return false;
|
|
1797
1806
|
throw new ProbeError("USAGE", "No services yet. Add one: probe add api -p 3000");
|
|
1798
1807
|
}
|
|
1799
|
-
const { openDashboard } = await import("./open-
|
|
1808
|
+
const { openDashboard } = await import("./open-QWB24XAM.js");
|
|
1800
1809
|
let settings = initialDashboardSettings(askedMs, await readDashboardPreferences(paths));
|
|
1801
1810
|
const saves = createSaveQueue();
|
|
1802
1811
|
const local = `${PROBE_DIR}/${LOCAL_FILE}`;
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
DASHBOARD_HISTORY_LENGTH,
|
|
5
5
|
DASHBOARD_INTERVAL_CHOICES_SECONDS,
|
|
6
6
|
DASHBOARD_TIMEOUT_CHOICES_SECONDS,
|
|
7
|
+
NOTIFY_WAIT_MS,
|
|
7
8
|
createColors,
|
|
8
9
|
curlCommand,
|
|
9
10
|
expectProblem,
|
|
@@ -18,7 +19,7 @@ import {
|
|
|
18
19
|
runChecks,
|
|
19
20
|
targetUrl,
|
|
20
21
|
truncate
|
|
21
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-VHDD7JN7.js";
|
|
22
23
|
import {
|
|
23
24
|
__commonJS,
|
|
24
25
|
__export,
|
|
@@ -20279,12 +20280,50 @@ async function copyText(text, platform2 = process.platform, env3 = process.env,
|
|
|
20279
20280
|
return false;
|
|
20280
20281
|
}
|
|
20281
20282
|
|
|
20283
|
+
// src/notify.ts
|
|
20284
|
+
import { spawn as nodeSpawn2 } from "child_process";
|
|
20285
|
+
function notifierName(platform2) {
|
|
20286
|
+
return platform2 === "darwin" ? "osascript" : "notify-send";
|
|
20287
|
+
}
|
|
20288
|
+
function notifierCommand(platform2, title, text) {
|
|
20289
|
+
if (platform2 !== "darwin") return ["notify-send", "--app-name=probe", "--", title, text];
|
|
20290
|
+
return [
|
|
20291
|
+
"osascript",
|
|
20292
|
+
"-e",
|
|
20293
|
+
"on run argv",
|
|
20294
|
+
"-e",
|
|
20295
|
+
"display notification (item 2 of argv) with title (item 1 of argv)",
|
|
20296
|
+
"-e",
|
|
20297
|
+
"end run",
|
|
20298
|
+
title,
|
|
20299
|
+
text
|
|
20300
|
+
];
|
|
20301
|
+
}
|
|
20302
|
+
function sendNotification(title, text, platform2 = process.platform, spawn = nodeSpawn2) {
|
|
20303
|
+
const [command = "", ...args] = notifierCommand(platform2, title, text);
|
|
20304
|
+
return new Promise((resolve) => {
|
|
20305
|
+
const child = spawn(command, args, { stdio: "ignore" });
|
|
20306
|
+
const late = setTimeout(() => {
|
|
20307
|
+
child.kill();
|
|
20308
|
+
resolve(false);
|
|
20309
|
+
}, NOTIFY_WAIT_MS);
|
|
20310
|
+
const settle = (shown) => {
|
|
20311
|
+
clearTimeout(late);
|
|
20312
|
+
resolve(shown);
|
|
20313
|
+
};
|
|
20314
|
+
child.once("error", () => settle(false));
|
|
20315
|
+
child.once("exit", (code) => settle(code === 0));
|
|
20316
|
+
});
|
|
20317
|
+
}
|
|
20318
|
+
|
|
20282
20319
|
// src/dashboard/actions.ts
|
|
20320
|
+
var NOTIFICATION_TITLE = "probe";
|
|
20283
20321
|
var system = () => ({
|
|
20284
20322
|
platform: process.platform,
|
|
20285
20323
|
env: process.env,
|
|
20286
20324
|
openUrl,
|
|
20287
|
-
copyText
|
|
20325
|
+
copyText,
|
|
20326
|
+
notify: sendNotification
|
|
20288
20327
|
});
|
|
20289
20328
|
function serviceActions(config, deps = system()) {
|
|
20290
20329
|
const service = (name) => config().services[name];
|
|
@@ -20306,6 +20345,10 @@ ${url}`;
|
|
|
20306
20345
|
const tools = clipboardTools(deps.platform, deps.env).map(([tool]) => tool);
|
|
20307
20346
|
return `No ${tools.join(" or ")} to copy with. The command:
|
|
20308
20347
|
${command}`;
|
|
20348
|
+
},
|
|
20349
|
+
async onNotify(message) {
|
|
20350
|
+
if (await deps.notify(NOTIFICATION_TITLE, message)) return void 0;
|
|
20351
|
+
return `${notifierName(deps.platform)} could not show a desktop notification`;
|
|
20309
20352
|
}
|
|
20310
20353
|
};
|
|
20311
20354
|
}
|
|
@@ -20313,6 +20356,24 @@ ${command}`;
|
|
|
20313
20356
|
// src/dashboard/Dashboard.tsx
|
|
20314
20357
|
var import_react36 = __toESM(require_react(), 1);
|
|
20315
20358
|
|
|
20359
|
+
// src/dashboard/changes.ts
|
|
20360
|
+
function stateChanges(known, results) {
|
|
20361
|
+
const checked = results.filter(({ skipped }) => !skipped);
|
|
20362
|
+
return {
|
|
20363
|
+
known: new Map(checked.map(({ name, ok }) => [name, ok])),
|
|
20364
|
+
changes: checked.filter(({ name, ok }) => known.has(name) && known.get(name) !== ok).map(({ name, ok }) => ({ name, up: ok }))
|
|
20365
|
+
};
|
|
20366
|
+
}
|
|
20367
|
+
function changeMessage(changes) {
|
|
20368
|
+
const [only] = changes;
|
|
20369
|
+
if (only && changes.length === 1) return `${only.name} is ${only.up ? "back up" : "down"}`;
|
|
20370
|
+
const names = (up) => changes.filter((change) => change.up === up).map(({ name }) => name).join(", ");
|
|
20371
|
+
return [
|
|
20372
|
+
...names(false) ? [`down: ${names(false)}`] : [],
|
|
20373
|
+
...names(true) ? [`back up: ${names(true)}`] : []
|
|
20374
|
+
].join("; ");
|
|
20375
|
+
}
|
|
20376
|
+
|
|
20316
20377
|
// src/dashboard/detail.ts
|
|
20317
20378
|
var BLOCKS = "\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588";
|
|
20318
20379
|
var HEADER_LINES = 6;
|
|
@@ -20665,6 +20726,7 @@ function stepChoice(choices, current, direction) {
|
|
|
20665
20726
|
// src/dashboard/Dashboard.tsx
|
|
20666
20727
|
var import_jsx_runtime = __toESM(require_jsx_runtime(), 1);
|
|
20667
20728
|
var SERVICE_ROWS = ["Skip", "Timeout"];
|
|
20729
|
+
var NOTIFY_ROW = SETTING_ROWS.length;
|
|
20668
20730
|
var TIMEOUT_ROW = SERVICE_ROWS.indexOf("Timeout");
|
|
20669
20731
|
var NO_TIMEOUTS = {};
|
|
20670
20732
|
var MARKER_WIDTH = 2;
|
|
@@ -20691,6 +20753,7 @@ function Dashboard({
|
|
|
20691
20753
|
onTimeoutChange,
|
|
20692
20754
|
onOpen,
|
|
20693
20755
|
onCopy,
|
|
20756
|
+
onNotify,
|
|
20694
20757
|
serviceFields,
|
|
20695
20758
|
onServiceWrite
|
|
20696
20759
|
}) {
|
|
@@ -20717,6 +20780,20 @@ function Dashboard({
|
|
|
20717
20780
|
void saved?.then(setUnsaved);
|
|
20718
20781
|
};
|
|
20719
20782
|
const { intervalMs, timeoutMs } = settings;
|
|
20783
|
+
const known = (0, import_react36.useRef)(/* @__PURE__ */ new Map());
|
|
20784
|
+
const notifying = (0, import_react36.useRef)(false);
|
|
20785
|
+
notifying.current = settings.notify === true;
|
|
20786
|
+
const warned = (0, import_react36.useRef)(false);
|
|
20787
|
+
const tell = (next) => {
|
|
20788
|
+
const { known: now, changes } = stateChanges(known.current, next);
|
|
20789
|
+
known.current = now;
|
|
20790
|
+
if (!notifying.current || changes.length === 0) return;
|
|
20791
|
+
void onNotify?.(changeMessage(changes)).then((warning) => {
|
|
20792
|
+
if (warning === void 0 || warned.current) return;
|
|
20793
|
+
warned.current = true;
|
|
20794
|
+
setNotice(warning);
|
|
20795
|
+
});
|
|
20796
|
+
};
|
|
20720
20797
|
(0, import_react36.useEffect)(() => {
|
|
20721
20798
|
const controller = new AbortController();
|
|
20722
20799
|
const { signal } = controller;
|
|
@@ -20725,6 +20802,7 @@ function Dashboard({
|
|
|
20725
20802
|
const next = await check2(signal, timeoutMs, skipped, timeouts);
|
|
20726
20803
|
if (signal.aborted) return;
|
|
20727
20804
|
setLatest(next);
|
|
20805
|
+
tell(next);
|
|
20728
20806
|
setHistory((before) => recordLatencies(before, next));
|
|
20729
20807
|
await new Promise((resolve) => {
|
|
20730
20808
|
const timer = setTimeout(resolve, intervalMs);
|
|
@@ -20793,10 +20871,9 @@ ${outcome.warning}` : done);
|
|
|
20793
20871
|
};
|
|
20794
20872
|
const change = (rowIndex, direction) => {
|
|
20795
20873
|
const row = SETTING_ROWS[rowIndex];
|
|
20796
|
-
if (!row) return;
|
|
20797
|
-
const
|
|
20798
|
-
if (
|
|
20799
|
-
const next = { ...settings, [row.key]: value };
|
|
20874
|
+
if (!row && rowIndex !== NOTIFY_ROW) return;
|
|
20875
|
+
const next = row ? { ...settings, [row.key]: stepChoice(row.choicesMs, settings[row.key], direction) } : { ...settings, notify: !settings.notify };
|
|
20876
|
+
if (row && next[row.key] === settings[row.key]) return;
|
|
20800
20877
|
setSettings(next);
|
|
20801
20878
|
track(onSettingsChange?.(next));
|
|
20802
20879
|
};
|
|
@@ -20890,7 +20967,7 @@ ${outcome.warning}` : done);
|
|
|
20890
20967
|
} else if (key === "escape" || (panel.kind === "options" ? input === "o" : enter))
|
|
20891
20968
|
setPanel(null);
|
|
20892
20969
|
else if (up2 || down) {
|
|
20893
|
-
const lastRow =
|
|
20970
|
+
const lastRow = panel.kind === "options" ? NOTIFY_ROW : SERVICE_ROWS.length - 1;
|
|
20894
20971
|
setPanel({ ...panel, row: Math.max(0, Math.min(lastRow, panel.row + (down ? 1 : -1))) });
|
|
20895
20972
|
} else if (left || right) {
|
|
20896
20973
|
if (panel.kind === "options") change(panel.row, right ? 1 : -1);
|
|
@@ -20981,7 +21058,10 @@ ${outcome.warning}` : done);
|
|
|
20981
21058
|
room: (windowRows || DEFAULT_ROWS) - taken - (unsaved === void 0 ? 0 : 1)
|
|
20982
21059
|
}) : [];
|
|
20983
21060
|
const toned = ({ text, tone }) => tone === "good" ? colors.green(text) : tone === "bad" ? colors.red(text) : tone === "dim" ? colors.dim(text) : text;
|
|
20984
|
-
const panelRows = panel?.kind === "options" ?
|
|
21061
|
+
const panelRows = panel?.kind === "options" ? [
|
|
21062
|
+
...SETTING_ROWS.map((row) => [row.label, seconds(settings[row.key])]),
|
|
21063
|
+
["Notify", settings.notify ? "on" : "off"]
|
|
21064
|
+
] : panel ? [
|
|
20985
21065
|
["Skip", skipped.has(panel.name) ? "yes" : "no"],
|
|
20986
21066
|
[
|
|
20987
21067
|
"Timeout",
|