@rubytech/taskmaster 1.0.105 → 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.
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.0.105",
3
- "commit": "5fc4d328a106550321dded2754c14635222ca467",
4
- "builtAt": "2026-02-23T08:17:33.862Z"
2
+ "version": "1.0.106",
3
+ "commit": "6c7e43fa0df93753260cac243239df6d5d8fc554",
4
+ "builtAt": "2026-02-23T08:24:09.270Z"
5
5
  }
@@ -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();
@@ -227,16 +227,6 @@ export const wifiHandlers = {
227
227
  }
228
228
  // Check for saved connection profile
229
229
  const saved = await getSavedWifiConnection();
230
- // Auto-reconnect: if a saved profile with autoconnect exists but device
231
- // is disconnected, nudge NetworkManager to bring the connection up.
232
- // Fire-and-forget — the next status poll will reflect the result.
233
- if (saved && saved.autoconnect && !deviceState.connected) {
234
- runExec("nmcli", ["con", "up", saved.name], { timeoutMs: 30_000 })
235
- .then(() => disableWifiPowerSave(context.logGateway))
236
- .catch((err) => {
237
- context.logGateway.warn(`wifi auto-reconnect failed: ${err instanceof Error ? err.message : String(err)}`);
238
- });
239
- }
240
230
  respond(true, {
241
231
  available: true,
242
232
  connected: deviceState.connected,
@@ -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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/taskmaster",
3
- "version": "1.0.105",
3
+ "version": "1.0.106",
4
4
  "description": "AI-powered business assistant for small businesses",
5
5
  "publishConfig": {
6
6
  "access": "public"