@lifeaitools/clauth 1.4.1 → 1.4.2
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/cli/assets/watchdog.ps1 +42 -0
- package/cli/commands/watchdog.js +144 -0
- package/cli/index.js +10 -0
- package/package.json +1 -1
- package/scripts/postinstall.js +106 -14
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# clauth-watchdog.ps1
|
|
2
|
+
# Monitors clauth daemon on port 52437. Restarts it if not responding.
|
|
3
|
+
# Installed by: npm install -g @lifeaitools/clauth
|
|
4
|
+
# Managed by: clauth watchdog [install|uninstall|status]
|
|
5
|
+
|
|
6
|
+
$clauth = "$env:APPDATA\npm\clauth.cmd"
|
|
7
|
+
$logFile = "$env:APPDATA\clauth\watchdog.log"
|
|
8
|
+
$pingUrl = "http://127.0.0.1:52437/ping"
|
|
9
|
+
$interval = 15
|
|
10
|
+
|
|
11
|
+
function Write-Log($msg) {
|
|
12
|
+
$ts = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
|
|
13
|
+
$line = "[$ts] $msg"
|
|
14
|
+
Add-Content -Path $logFile -Value $line -ErrorAction SilentlyContinue
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
Write-Log "Watchdog started (PID $PID, clauth=$clauth)"
|
|
18
|
+
|
|
19
|
+
while ($true) {
|
|
20
|
+
$alive = $false
|
|
21
|
+
try {
|
|
22
|
+
$r = Invoke-WebRequest -Uri $pingUrl -TimeoutSec 3 -UseBasicParsing -ErrorAction Stop
|
|
23
|
+
if ($r.StatusCode -eq 200) { $alive = $true }
|
|
24
|
+
} catch { }
|
|
25
|
+
|
|
26
|
+
if (-not $alive) {
|
|
27
|
+
Write-Log "Daemon not responding — restarting..."
|
|
28
|
+
Start-Process -FilePath "cmd.exe" `
|
|
29
|
+
-ArgumentList "/c `"$clauth`" serve start" `
|
|
30
|
+
-WindowStyle Hidden
|
|
31
|
+
Start-Sleep -Seconds 5
|
|
32
|
+
try {
|
|
33
|
+
$r = Invoke-WebRequest -Uri $pingUrl -TimeoutSec 5 -UseBasicParsing -ErrorAction Stop
|
|
34
|
+
if ($r.StatusCode -eq 200) { Write-Log "Daemon restarted OK." }
|
|
35
|
+
else { Write-Log "WARNING: Daemon still not responding." }
|
|
36
|
+
} catch {
|
|
37
|
+
Write-Log "WARNING: Daemon still not responding after restart."
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
Start-Sleep -Seconds $interval
|
|
42
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// cli/commands/watchdog.js
|
|
2
|
+
// Manages the clauth watchdog Task Scheduler job on Windows.
|
|
3
|
+
// clauth watchdog install — register job (UAC elevation)
|
|
4
|
+
// clauth watchdog uninstall — remove job (UAC elevation)
|
|
5
|
+
// clauth watchdog status — check if job is registered and running
|
|
6
|
+
// clauth watchdog start — start the job now without waiting for login
|
|
7
|
+
|
|
8
|
+
import { execSync, spawnSync } from "child_process";
|
|
9
|
+
import fs from "fs";
|
|
10
|
+
import path from "path";
|
|
11
|
+
import os from "os";
|
|
12
|
+
import { fileURLToPath } from "url";
|
|
13
|
+
|
|
14
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
const TASK_NAME = "ClautWatchdog";
|
|
16
|
+
const APPDATA = process.env.APPDATA || path.join(os.homedir(), "AppData", "Roaming");
|
|
17
|
+
const CLAUTH_DIR = path.join(APPDATA, "clauth");
|
|
18
|
+
const DEST_PS1 = path.join(CLAUTH_DIR, "watchdog.ps1");
|
|
19
|
+
const SOURCE_PS1 = path.join(__dirname, "..", "assets", "watchdog.ps1");
|
|
20
|
+
const PS_EXE = "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe";
|
|
21
|
+
|
|
22
|
+
function ensureWatchdogScript() {
|
|
23
|
+
fs.mkdirSync(CLAUTH_DIR, { recursive: true });
|
|
24
|
+
fs.copyFileSync(SOURCE_PS1, DEST_PS1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function isElevated() {
|
|
28
|
+
try {
|
|
29
|
+
execSync(`${PS_EXE} -NoProfile -Command "[Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)"`,
|
|
30
|
+
{ stdio: "pipe", encoding: "utf8" });
|
|
31
|
+
return true;
|
|
32
|
+
} catch { return false; }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Build the PowerShell registration command as a single string
|
|
36
|
+
function buildRegisterCmd(ps1Path) {
|
|
37
|
+
const escaped = ps1Path.replace(/'/g, "''");
|
|
38
|
+
return [
|
|
39
|
+
`$action = New-ScheduledTaskAction -Execute 'powershell.exe'`,
|
|
40
|
+
` -Argument '-NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File \\"${escaped}\\"';`,
|
|
41
|
+
`$trigger = New-ScheduledTaskTrigger -AtLogOn;`,
|
|
42
|
+
`$settings = New-ScheduledTaskSettingsSet -ExecutionTimeLimit 0 -RestartCount 3`,
|
|
43
|
+
` -RestartInterval (New-TimeSpan -Minutes 1) -MultipleInstances IgnoreNew;`,
|
|
44
|
+
`Unregister-ScheduledTask -TaskName '${TASK_NAME}' -Confirm:$false -ErrorAction SilentlyContinue;`,
|
|
45
|
+
`Register-ScheduledTask -TaskName '${TASK_NAME}' -Action $action -Trigger $trigger`,
|
|
46
|
+
` -Settings $settings -RunLevel Highest`,
|
|
47
|
+
` -Description 'clauth daemon watchdog — restarts on port 52437 if not responding.';`,
|
|
48
|
+
`Write-Host 'Watchdog registered.'`,
|
|
49
|
+
].join(" ");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function buildUnregisterCmd() {
|
|
53
|
+
return `Unregister-ScheduledTask -TaskName '${TASK_NAME}' -Confirm:$false -ErrorAction SilentlyContinue; Write-Host 'Watchdog removed.'`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function runElevated(psCmd) {
|
|
57
|
+
// Wrap in Start-Process -Verb RunAs to trigger UAC dialog
|
|
58
|
+
const inner = psCmd.replace(/"/g, '\\"');
|
|
59
|
+
const result = spawnSync(PS_EXE, [
|
|
60
|
+
"-NoProfile", "-Command",
|
|
61
|
+
`Start-Process '${PS_EXE}' -Verb RunAs -Wait -ArgumentList '-NoProfile -ExecutionPolicy Bypass -Command "${inner}"'`,
|
|
62
|
+
], { stdio: "inherit", encoding: "utf8" });
|
|
63
|
+
return result.status === 0;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function runDirect(psCmd) {
|
|
67
|
+
const result = spawnSync(PS_EXE, [
|
|
68
|
+
"-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", psCmd,
|
|
69
|
+
], { stdio: "inherit", encoding: "utf8" });
|
|
70
|
+
return result.status === 0;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export async function runWatchdog(action) {
|
|
74
|
+
if (os.platform() !== "win32") {
|
|
75
|
+
console.log("Watchdog auto-start is Windows-only. On Linux/macOS, use systemd or launchd.");
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (action === "status" || !action) {
|
|
80
|
+
try {
|
|
81
|
+
const out = execSync(
|
|
82
|
+
`${PS_EXE} -NoProfile -Command "Get-ScheduledTask -TaskName '${TASK_NAME}' -ErrorAction SilentlyContinue | Select-Object -ExpandProperty State"`,
|
|
83
|
+
{ encoding: "utf8", stdio: ["pipe","pipe","pipe"], timeout: 5000 }
|
|
84
|
+
).trim();
|
|
85
|
+
if (out) {
|
|
86
|
+
console.log(`Watchdog task: ${TASK_NAME} — State: ${out}`);
|
|
87
|
+
console.log(`Script: ${DEST_PS1}`);
|
|
88
|
+
console.log(`Log: ${path.join(CLAUTH_DIR, "watchdog.log")}`);
|
|
89
|
+
} else {
|
|
90
|
+
console.log(`Watchdog task '${TASK_NAME}' is NOT registered.`);
|
|
91
|
+
console.log("Run: clauth watchdog install");
|
|
92
|
+
}
|
|
93
|
+
} catch {
|
|
94
|
+
console.log(`Watchdog task '${TASK_NAME}' is NOT registered.`);
|
|
95
|
+
console.log("Run: clauth watchdog install");
|
|
96
|
+
}
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (action === "install") {
|
|
101
|
+
ensureWatchdogScript();
|
|
102
|
+
console.log(`\n Installing clauth watchdog...`);
|
|
103
|
+
console.log(` Script: ${DEST_PS1}`);
|
|
104
|
+
console.log(` Task: ${TASK_NAME} (runs at login)\n`);
|
|
105
|
+
console.log(" Windows will ask for administrator approval — please click Yes.\n");
|
|
106
|
+
|
|
107
|
+
const cmd = buildRegisterCmd(DEST_PS1);
|
|
108
|
+
const ok = runElevated(cmd);
|
|
109
|
+
|
|
110
|
+
if (ok) {
|
|
111
|
+
console.log("\n Watchdog installed. Starting now...");
|
|
112
|
+
spawnSync(PS_EXE, [
|
|
113
|
+
"-NoProfile", "-Command", `Start-ScheduledTask -TaskName '${TASK_NAME}'`,
|
|
114
|
+
], { stdio: "pipe" });
|
|
115
|
+
console.log(" Done. Watchdog is running.\n");
|
|
116
|
+
} else {
|
|
117
|
+
console.log("\n Installation cancelled or failed.");
|
|
118
|
+
console.log(` To install manually, run as Administrator:`);
|
|
119
|
+
console.log(` clauth watchdog install\n`);
|
|
120
|
+
}
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (action === "uninstall") {
|
|
125
|
+
console.log("\n Removing clauth watchdog...");
|
|
126
|
+
console.log(" Windows will ask for administrator approval — please click Yes.\n");
|
|
127
|
+
const ok = runElevated(buildUnregisterCmd());
|
|
128
|
+
if (ok) console.log("\n Watchdog removed.\n");
|
|
129
|
+
else console.log("\n Removal cancelled or failed.\n");
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (action === "start") {
|
|
134
|
+
console.log(` Starting ${TASK_NAME}...`);
|
|
135
|
+
const r = spawnSync(PS_EXE, [
|
|
136
|
+
"-NoProfile", "-Command", `Start-ScheduledTask -TaskName '${TASK_NAME}'`,
|
|
137
|
+
], { stdio: "inherit", encoding: "utf8" });
|
|
138
|
+
if (r.status === 0) console.log(" Watchdog started.");
|
|
139
|
+
else console.log(" Could not start — is it registered? Run: clauth watchdog install");
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
console.log("Usage: clauth watchdog [install|uninstall|status|start]");
|
|
144
|
+
}
|
package/cli/index.js
CHANGED
|
@@ -462,6 +462,16 @@ Examples:
|
|
|
462
462
|
});
|
|
463
463
|
|
|
464
464
|
// ──────────────────────────────────────────────
|
|
465
|
+
// clauth watchdog
|
|
466
|
+
// ──────────────────────────────────────────────
|
|
467
|
+
program
|
|
468
|
+
.command("watchdog [action]")
|
|
469
|
+
.description("Manage auto-restart watchdog (install|uninstall|status|start)")
|
|
470
|
+
.action(async (action) => {
|
|
471
|
+
const { runWatchdog } = await import("./commands/watchdog.js");
|
|
472
|
+
await runWatchdog(action);
|
|
473
|
+
});
|
|
474
|
+
|
|
465
475
|
// clauth doctor
|
|
466
476
|
// ──────────────────────────────────────────────
|
|
467
477
|
program
|
package/package.json
CHANGED
package/scripts/postinstall.js
CHANGED
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// scripts/postinstall.js
|
|
3
3
|
// Runs after npm install -g @lifeaitools/clauth
|
|
4
|
-
//
|
|
4
|
+
// On Windows: writes watchdog.ps1 to %APPDATA%\clauth\ and registers
|
|
5
|
+
// the Task Scheduler job via UAC elevation.
|
|
5
6
|
|
|
6
7
|
import fs from "fs";
|
|
7
8
|
import path from "path";
|
|
8
9
|
import os from "os";
|
|
9
10
|
import { fileURLToPath } from "url";
|
|
11
|
+
import { spawnSync } from "child_process";
|
|
10
12
|
|
|
11
|
-
const __dirname
|
|
13
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
const APPDATA = process.env.APPDATA || path.join(os.homedir(), "AppData", "Roaming");
|
|
15
|
+
const CLAUTH_DIR = path.join(APPDATA, "clauth");
|
|
16
|
+
const DEST_PS1 = path.join(CLAUTH_DIR, "watchdog.ps1");
|
|
17
|
+
const SOURCE_PS1 = path.join(__dirname, "..", "cli", "assets", "watchdog.ps1");
|
|
18
|
+
const PS_EXE = "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe";
|
|
19
|
+
const TASK_NAME = "ClautWatchdog";
|
|
12
20
|
|
|
13
21
|
const CONFIG_DIR = os.platform() === "win32"
|
|
14
|
-
? path.join(
|
|
22
|
+
? path.join(APPDATA, "clauth-nodejs", "Config")
|
|
15
23
|
: path.join(os.homedir(), ".config", "clauth-nodejs");
|
|
16
24
|
|
|
17
25
|
let version = "1.0.0";
|
|
@@ -20,33 +28,117 @@ try {
|
|
|
20
28
|
version = pkg.version;
|
|
21
29
|
} catch {}
|
|
22
30
|
|
|
31
|
+
// ----------------------------------------------------------------
|
|
32
|
+
// Watchdog helpers (Windows only)
|
|
33
|
+
// ----------------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
function writeWatchdogScript() {
|
|
36
|
+
try {
|
|
37
|
+
fs.mkdirSync(CLAUTH_DIR, { recursive: true });
|
|
38
|
+
fs.copyFileSync(SOURCE_PS1, DEST_PS1);
|
|
39
|
+
return true;
|
|
40
|
+
} catch (err) {
|
|
41
|
+
console.log(` ! Could not write watchdog script: ${err.message}`);
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function isWatchdogRegistered() {
|
|
47
|
+
try {
|
|
48
|
+
const r = spawnSync(PS_EXE, [
|
|
49
|
+
"-NoProfile", "-Command",
|
|
50
|
+
`(Get-ScheduledTask -TaskName '${TASK_NAME}' -ErrorAction SilentlyContinue) -ne $null`,
|
|
51
|
+
], { encoding: "utf8", stdio: ["pipe","pipe","pipe"], timeout: 5000 });
|
|
52
|
+
return (r.stdout || "").trim().toLowerCase() === "true";
|
|
53
|
+
} catch { return false; }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function registerWatchdog() {
|
|
57
|
+
const escaped = DEST_PS1.replace(/'/g, "''");
|
|
58
|
+
const psCmd = [
|
|
59
|
+
`$a = New-ScheduledTaskAction -Execute 'powershell.exe'`,
|
|
60
|
+
` -Argument '-NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File \\"${escaped}\\"';`,
|
|
61
|
+
`$t = New-ScheduledTaskTrigger -AtLogOn;`,
|
|
62
|
+
`$s = New-ScheduledTaskSettingsSet -ExecutionTimeLimit 0 -RestartCount 3`,
|
|
63
|
+
` -RestartInterval (New-TimeSpan -Minutes 1) -MultipleInstances IgnoreNew;`,
|
|
64
|
+
`Unregister-ScheduledTask -TaskName '${TASK_NAME}' -Confirm:$false -ErrorAction SilentlyContinue;`,
|
|
65
|
+
`Register-ScheduledTask -TaskName '${TASK_NAME}' -Action $a -Trigger $t`,
|
|
66
|
+
` -Settings $s -RunLevel Highest -Description 'clauth daemon watchdog';`,
|
|
67
|
+
].join(" ");
|
|
68
|
+
|
|
69
|
+
// Try direct first (works if already running as Administrator)
|
|
70
|
+
const direct = spawnSync(PS_EXE, [
|
|
71
|
+
"-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", psCmd,
|
|
72
|
+
], { encoding: "utf8", stdio: ["pipe","pipe","pipe"], timeout: 10000 });
|
|
73
|
+
|
|
74
|
+
if (direct.status === 0) return "ok";
|
|
75
|
+
|
|
76
|
+
// Trigger UAC elevation — opens the Windows approval dialog
|
|
77
|
+
const inner = psCmd.replace(/"/g, '\\"');
|
|
78
|
+
const elevated = spawnSync(PS_EXE, [
|
|
79
|
+
"-NoProfile", "-Command",
|
|
80
|
+
`Start-Process '${PS_EXE}' -Verb RunAs -Wait -ArgumentList '-NoProfile -ExecutionPolicy Bypass -Command "${inner}"'`,
|
|
81
|
+
], { stdio: "inherit", encoding: "utf8", timeout: 60000 });
|
|
82
|
+
|
|
83
|
+
return elevated.status === 0 ? "ok" : "denied";
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function startWatchdog() {
|
|
87
|
+
spawnSync(PS_EXE, [
|
|
88
|
+
"-NoProfile", "-Command",
|
|
89
|
+
`Start-ScheduledTask -TaskName '${TASK_NAME}' -ErrorAction SilentlyContinue`,
|
|
90
|
+
], { stdio: "pipe", timeout: 5000 });
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ----------------------------------------------------------------
|
|
94
|
+
// Main
|
|
95
|
+
// ----------------------------------------------------------------
|
|
96
|
+
|
|
23
97
|
async function main() {
|
|
24
98
|
const configPath = path.join(CONFIG_DIR, "config.json");
|
|
25
|
-
const isUpgrade
|
|
99
|
+
const isUpgrade = fs.existsSync(configPath);
|
|
26
100
|
|
|
27
101
|
if (isUpgrade) {
|
|
28
102
|
console.log(`\n \u2713 clauth upgraded to v${version}`);
|
|
29
103
|
console.log(` \u2713 Config preserved at ${CONFIG_DIR}`);
|
|
30
|
-
|
|
31
|
-
// Try to detect running daemon
|
|
32
104
|
try {
|
|
33
105
|
const controller = new AbortController();
|
|
34
|
-
const
|
|
106
|
+
const to = setTimeout(() => controller.abort(), 2000);
|
|
35
107
|
const res = await fetch("http://127.0.0.1:52437/ping", { signal: controller.signal });
|
|
36
|
-
clearTimeout(
|
|
37
|
-
if (res.ok)
|
|
38
|
-
console.log(" \u21BB Daemon is running \u2014 restart it with: clauth serve restart");
|
|
39
|
-
}
|
|
108
|
+
clearTimeout(to);
|
|
109
|
+
if (res.ok) console.log(" \u21BB Daemon running \u2014 restart: clauth serve restart");
|
|
40
110
|
} catch {
|
|
41
111
|
console.log(" \u25CB Daemon not running");
|
|
42
112
|
}
|
|
43
|
-
|
|
44
|
-
console.log(" Run 'clauth doctor' to verify installation\n");
|
|
45
113
|
} else {
|
|
46
114
|
console.log(`\n Welcome to clauth v${version}!`);
|
|
47
115
|
console.log(" Run 'clauth install' to set up your vault");
|
|
48
|
-
console.log(" Run 'clauth doctor' to check prerequisites\n");
|
|
49
116
|
}
|
|
117
|
+
|
|
118
|
+
// Windows: register watchdog on fresh install; refresh script on upgrade
|
|
119
|
+
if (os.platform() === "win32") {
|
|
120
|
+
const registered = isWatchdogRegistered();
|
|
121
|
+
if (!registered) {
|
|
122
|
+
console.log("\n Setting up auto-restart watchdog...");
|
|
123
|
+
console.log(" Windows will ask for administrator approval \u2014 please click Yes.\n");
|
|
124
|
+
if (writeWatchdogScript()) {
|
|
125
|
+
const result = registerWatchdog();
|
|
126
|
+
if (result === "ok") {
|
|
127
|
+
startWatchdog();
|
|
128
|
+
console.log(" \u2713 Watchdog registered and started (Task: ClautWatchdog)");
|
|
129
|
+
console.log(` \u2713 Script: ${DEST_PS1}`);
|
|
130
|
+
} else {
|
|
131
|
+
console.log(" ! Watchdog registration skipped (approval denied).");
|
|
132
|
+
console.log(" Install later with: clauth watchdog install");
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
} else {
|
|
136
|
+
writeWatchdogScript(); // refresh script on upgrade
|
|
137
|
+
console.log(" \u2713 Watchdog script updated");
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
console.log(" Run 'clauth doctor' to verify installation\n");
|
|
50
142
|
}
|
|
51
143
|
|
|
52
144
|
main().catch(() => {});
|