@rubytech/taskmaster 1.0.104 → 1.0.106
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build-info.json +3 -3
- package/dist/control-ui/assets/{index-BVS_Pain.js → index-DtuDNTAC.js} +56 -71
- package/dist/control-ui/assets/index-DtuDNTAC.js.map +1 -0
- package/dist/control-ui/index.html +1 -1
- package/dist/gateway/server-close.js +2 -0
- package/dist/gateway/server-wifi-watchdog.js +105 -0
- package/dist/gateway/server.impl.js +3 -0
- package/package.json +1 -1
- package/dist/control-ui/assets/index-BVS_Pain.js.map +0 -1
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<title>Taskmaster Control</title>
|
|
7
7
|
<meta name="color-scheme" content="dark light" />
|
|
8
8
|
<link rel="icon" type="image/png" href="./favicon.png" />
|
|
9
|
-
<script type="module" crossorigin src="./assets/index-
|
|
9
|
+
<script type="module" crossorigin src="./assets/index-DtuDNTAC.js"></script>
|
|
10
10
|
<link rel="stylesheet" crossorigin href="./assets/index-DjhCZlZd.css">
|
|
11
11
|
</head>
|
|
12
12
|
<body>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { listChannelPlugins } from "../channels/plugins/index.js";
|
|
2
2
|
import { stopGmailWatcher } from "../hooks/gmail-watcher.js";
|
|
3
|
+
import { stopWifiWatchdog } from "./server-wifi-watchdog.js";
|
|
3
4
|
export function createGatewayCloseHandler(params) {
|
|
4
5
|
return async (opts) => {
|
|
5
6
|
const reasonRaw = typeof opts?.reason === "string" ? opts.reason.trim() : "";
|
|
@@ -54,6 +55,7 @@ export function createGatewayCloseHandler(params) {
|
|
|
54
55
|
clearInterval(params.tickInterval);
|
|
55
56
|
clearInterval(params.healthInterval);
|
|
56
57
|
clearInterval(params.dedupeCleanup);
|
|
58
|
+
stopWifiWatchdog();
|
|
57
59
|
if (params.agentUnsub) {
|
|
58
60
|
try {
|
|
59
61
|
params.agentUnsub();
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-side WiFi watchdog for Raspberry Pi.
|
|
3
|
+
*
|
|
4
|
+
* Runs every 30 seconds on Linux. If a saved NetworkManager WiFi profile
|
|
5
|
+
* with autoconnect=yes exists but wlan0 is disconnected, nudges NM to
|
|
6
|
+
* reconnect and disables WiFi power save.
|
|
7
|
+
*
|
|
8
|
+
* This runs in the gateway process itself — independent of any UI polling —
|
|
9
|
+
* so WiFi recovers even when no browser is connected.
|
|
10
|
+
*/
|
|
11
|
+
import os from "node:os";
|
|
12
|
+
import { runExec } from "../process/exec.js";
|
|
13
|
+
const WATCHDOG_INTERVAL_MS = 30_000;
|
|
14
|
+
let timer = null;
|
|
15
|
+
export function startWifiWatchdog(log) {
|
|
16
|
+
if (os.platform() !== "linux")
|
|
17
|
+
return;
|
|
18
|
+
if (timer)
|
|
19
|
+
return;
|
|
20
|
+
// Check immediately on startup, then every 30s
|
|
21
|
+
void runWifiCheck(log);
|
|
22
|
+
timer = setInterval(() => void runWifiCheck(log), WATCHDOG_INTERVAL_MS);
|
|
23
|
+
}
|
|
24
|
+
export function stopWifiWatchdog() {
|
|
25
|
+
if (timer) {
|
|
26
|
+
clearInterval(timer);
|
|
27
|
+
timer = null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async function runWifiCheck(log) {
|
|
31
|
+
try {
|
|
32
|
+
// Verify nmcli is available
|
|
33
|
+
try {
|
|
34
|
+
await runExec("nmcli", ["--version"], { timeoutMs: 3_000 });
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return; // nmcli not installed — nothing to do
|
|
38
|
+
}
|
|
39
|
+
// Check wlan0 device state
|
|
40
|
+
const { connected } = await getWlan0State();
|
|
41
|
+
if (connected)
|
|
42
|
+
return; // All good
|
|
43
|
+
// Check for a saved WiFi profile with autoconnect enabled
|
|
44
|
+
const saved = await getSavedAutoconnectProfile();
|
|
45
|
+
if (!saved)
|
|
46
|
+
return; // No saved profile — nothing to reconnect to
|
|
47
|
+
// Saved profile exists but WiFi is down — nudge NM to reconnect
|
|
48
|
+
log.info(`wifi watchdog: reconnecting to "${saved}"…`);
|
|
49
|
+
try {
|
|
50
|
+
await runExec("nmcli", ["con", "up", saved], { timeoutMs: 30_000 });
|
|
51
|
+
log.info(`wifi watchdog: reconnected to "${saved}"`);
|
|
52
|
+
// Disable power save to prevent future drops
|
|
53
|
+
try {
|
|
54
|
+
await runExec("iw", ["dev", "wlan0", "set", "power_save", "off"], { timeoutMs: 5_000 });
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// Non-critical
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
log.warn(`wifi watchdog: reconnect failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
log.warn(`wifi watchdog: check failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async function getWlan0State() {
|
|
69
|
+
try {
|
|
70
|
+
const { stdout } = await runExec("nmcli", ["-t", "-f", "GENERAL.STATE", "dev", "show", "wlan0"], { timeoutMs: 5_000 });
|
|
71
|
+
for (const line of stdout.split("\n")) {
|
|
72
|
+
if (line.startsWith("GENERAL.STATE:")) {
|
|
73
|
+
return { connected: line.includes("100") };
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// wlan0 might not exist
|
|
79
|
+
}
|
|
80
|
+
return { connected: false };
|
|
81
|
+
}
|
|
82
|
+
async function getSavedAutoconnectProfile() {
|
|
83
|
+
try {
|
|
84
|
+
const { stdout } = await runExec("nmcli", ["-t", "-f", "NAME,TYPE,AUTOCONNECT", "con", "show"], { timeoutMs: 5_000 });
|
|
85
|
+
for (const line of stdout.split("\n")) {
|
|
86
|
+
if (!line.trim())
|
|
87
|
+
continue;
|
|
88
|
+
const placeholder = "\x00";
|
|
89
|
+
const safe = line.replace(/\\:/g, placeholder);
|
|
90
|
+
const parts = safe.split(":");
|
|
91
|
+
if (parts.length < 3)
|
|
92
|
+
continue;
|
|
93
|
+
const name = parts[0].replace(new RegExp(placeholder, "g"), ":").trim();
|
|
94
|
+
const type = parts[1].trim();
|
|
95
|
+
const autoconnect = parts[2].trim().toLowerCase() === "yes";
|
|
96
|
+
if (type === "802-11-wireless" && autoconnect) {
|
|
97
|
+
return name;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
// Non-critical
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
@@ -50,6 +50,7 @@ import { startGatewaySidecars } from "./server-startup.js";
|
|
|
50
50
|
import { logGatewayStartup } from "./server-startup-log.js";
|
|
51
51
|
import { ensureWatchdogUnitOnStartup, scheduleWatchdogStabilityConfirmation, } from "./server-watchdog.js";
|
|
52
52
|
import { startGatewayTailscaleExposure } from "./server-tailscale.js";
|
|
53
|
+
import { startWifiWatchdog } from "./server-wifi-watchdog.js";
|
|
53
54
|
import { loadGatewayTlsRuntime } from "./server/tls.js";
|
|
54
55
|
import { createWizardSessionTracker } from "./server-wizard-sessions.js";
|
|
55
56
|
import { attachGatewayWsHandlers } from "./server-ws-runtime.js";
|
|
@@ -421,6 +422,8 @@ export async function startGatewayServer(port = 18789, opts = {}) {
|
|
|
421
422
|
logChannels,
|
|
422
423
|
logBrowser,
|
|
423
424
|
}));
|
|
425
|
+
// Start WiFi watchdog on Linux — auto-reconnects saved WiFi if connection drops
|
|
426
|
+
startWifiWatchdog(log.child("wifi"));
|
|
424
427
|
// Initialize memory managers for all agents and sync indexes at gateway startup
|
|
425
428
|
const logMemory = log.child("memory");
|
|
426
429
|
const agentIds = listAgentIds(cfgAtStart);
|