@getfootgun/keys 0.2.0-rc.1 → 0.2.0-rc.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/lib/schedule.d.mts +2 -1
- package/lib/schedule.mjs +21 -3
- package/lib/setup.mjs +11 -4
- package/package.json +1 -1
package/lib/schedule.d.mts
CHANGED
|
@@ -8,4 +8,5 @@ export function registerNativeHost(opts: {
|
|
|
8
8
|
manifest: { name: string; [key: string]: unknown };
|
|
9
9
|
}): Promise<void>;
|
|
10
10
|
export function healthTaskXml(launcher: string, now?: Date): string;
|
|
11
|
-
export function scheduleDaily(opts: { platform: string; exec: Exec; home: string; launcher: string; uid?: number; support?: string; now?: () => Date }): Promise<
|
|
11
|
+
export function scheduleDaily(opts: { platform: string; exec: Exec; home: string; launcher: string; uid?: number; support?: string; now?: () => Date }): Promise<{ when: "daily" } | { when: "signin"; reason: string }>;
|
|
12
|
+
export function firstLine(e: unknown): string;
|
package/lib/schedule.mjs
CHANGED
|
@@ -6,6 +6,8 @@ import { launchdPlist } from "./launchd.mjs";
|
|
|
6
6
|
const LABEL = "com.getfootgun.keys.health";
|
|
7
7
|
const WIN_HOST_KEY = "HKCU\\Software\\Google\\Chrome\\NativeMessagingHosts\\com.getfootgun.keys";
|
|
8
8
|
const TASK = "Footgun Keys health";
|
|
9
|
+
const WIN_RUN_KEY = "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run";
|
|
10
|
+
const WIN_RUN_VALUE = "FootgunKeysHealth";
|
|
9
11
|
|
|
10
12
|
async function loadAgent(exec, uid, plist) {
|
|
11
13
|
try {
|
|
@@ -79,19 +81,35 @@ export function healthTaskXml(launcher, now = new Date()) {
|
|
|
79
81
|
].join("\r\n");
|
|
80
82
|
}
|
|
81
83
|
|
|
82
|
-
// Daily 9:30 run of "<launcher> health".
|
|
84
|
+
// Daily 9:30 run of "<launcher> health". Resolves { when: "daily" }, or on a Windows PC that blocks
|
|
85
|
+
// Task Scheduler { when: "signin", reason } after falling back to a per-user Run entry that checks
|
|
86
|
+
// at each sign-in. Throws if neither works.
|
|
83
87
|
export async function scheduleDaily({ platform, exec, home, launcher, uid, support, now = () => new Date() }) {
|
|
84
88
|
if (platform === "win32") {
|
|
85
89
|
// schtasks reads /XML files as UTF-16LE with a byte order mark.
|
|
86
90
|
const xmlPath = win32.join(support, "health-task.xml");
|
|
87
91
|
const body = Buffer.from(healthTaskXml(launcher, now()), "utf16le");
|
|
88
92
|
await writePrivate(xmlPath, Buffer.concat([Buffer.from([0xff, 0xfe]), body]));
|
|
89
|
-
|
|
90
|
-
|
|
93
|
+
try {
|
|
94
|
+
await exec("schtasks", ["/Create", "/F", "/TN", TASK, "/XML", xmlPath]);
|
|
95
|
+
} catch (e) {
|
|
96
|
+
// Some PCs (security software, policy) refuse every new task, even for an administrator.
|
|
97
|
+
await exec("reg", ["add", WIN_RUN_KEY, "/v", WIN_RUN_VALUE, "/t", "REG_SZ", "/d", `conhost.exe --headless "${launcher}" health`, "/f"]).catch(() => { throw e; });
|
|
98
|
+
return { when: "signin", reason: firstLine(e) };
|
|
99
|
+
}
|
|
100
|
+
// A sign-in entry from an earlier blocked run would now double the checks.
|
|
101
|
+
await exec("reg", ["delete", WIN_RUN_KEY, "/v", WIN_RUN_VALUE, "/f"]).catch(() => {});
|
|
102
|
+
return { when: "daily" };
|
|
91
103
|
}
|
|
92
104
|
const agents = join(home, "Library", "LaunchAgents");
|
|
93
105
|
await mkdir(agents, { recursive: true });
|
|
94
106
|
const plist = join(agents, `${LABEL}.plist`);
|
|
95
107
|
await writePrivate(plist, launchdPlist(launcher));
|
|
96
108
|
await loadAgent(exec, uid, plist);
|
|
109
|
+
return { when: "daily" };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// The OS tool's own message (schtasks/launchctl stderr), which names no key.
|
|
113
|
+
export function firstLine(e) {
|
|
114
|
+
return String(e?.stderr || (e instanceof Error ? e.message : "") || "").trim().split(/\r?\n/)[0];
|
|
97
115
|
}
|
package/lib/setup.mjs
CHANGED
|
@@ -8,7 +8,7 @@ import { Writable } from "node:stream";
|
|
|
8
8
|
import { promisify } from "node:util";
|
|
9
9
|
import { EXTENSION_IDS } from "../shared/extension-ids.mjs";
|
|
10
10
|
import { SITE, ensureSupportDir, writePrivate } from "./paths.mjs";
|
|
11
|
-
import { registerNativeHost, scheduleDaily } from "./schedule.mjs";
|
|
11
|
+
import { firstLine, registerNativeHost, scheduleDaily } from "./schedule.mjs";
|
|
12
12
|
import { keychainStore } from "./vault.mjs";
|
|
13
13
|
import { WIN_NOTIFY_SCRIPT } from "./platform.mjs";
|
|
14
14
|
import { makeServer } from "./server.mjs";
|
|
@@ -134,11 +134,18 @@ export async function setup({ home, platform, prompt, exec, installApp, server,
|
|
|
134
134
|
|
|
135
135
|
let scheduled = true;
|
|
136
136
|
try {
|
|
137
|
-
await scheduleDaily({ platform, exec, home, launcher, uid, support, now });
|
|
138
|
-
|
|
139
|
-
|
|
137
|
+
const result = await scheduleDaily({ platform, exec, home, launcher, uid, support, now });
|
|
138
|
+
if (result?.when === "signin") {
|
|
139
|
+
print(`Windows would not create a scheduled task on this PC${result.reason ? ` (${result.reason})` : ""}.`);
|
|
140
|
+
print("Footgun will check your keys each time you sign in to Windows instead.");
|
|
141
|
+
} else {
|
|
142
|
+
print("Scheduled the daily key check.");
|
|
143
|
+
}
|
|
144
|
+
} catch (e) {
|
|
140
145
|
scheduled = false;
|
|
141
146
|
print("Could not schedule the daily key check. Run setup again to retry.");
|
|
147
|
+
const reason = firstLine(e);
|
|
148
|
+
if (reason) print(`The scheduler said: ${reason}`);
|
|
142
149
|
}
|
|
143
150
|
// A failed schedule must not end in apparent success, so it is repeated last and fails the run.
|
|
144
151
|
const finish = (code) => {
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{ "name": "@getfootgun/keys", "version": "0.2.0-rc.
|
|
1
|
+
{ "name": "@getfootgun/keys", "version": "0.2.0-rc.2", "description": "Footgun Keys: guided API key setup that saves keys to the Mac Keychain or Windows Credential Manager.", "bin": { "footgun-keys": "bin/footgun-keys.mjs" }, "type": "module", "license": "MIT", "files": ["bin", "lib", "shared", "README.md"], "dependencies": { "@modelcontextprotocol/server": "2.3.1", "@napi-rs/keyring": "2.1.2", "zod": "4.6.7" }, "engines": { "node": ">=20" } }
|