73ec6ed9ef
The LocalService runner cannot write anywhere under %ProgramData%\punktfunk (the config dir is Users-read-only), so a state-writing plugin's saveCache / config-edit / first-run mkdir all fail EPERM — proven on-glass (rom-manager only looked fine because its state dir was pre-created by an admin run and a 0-title reconcile skipped the write). Add the one writable grant the model was missing, keeping the split crisp — code dirs RX+WA, secrets R, and now a dedicated state root RW: - plugins enable / build-scripting.ps1: create %ProgramData%\punktfunk plugin-state and grant LocalService (OI)(CI)(M); disable revokes. Users stay read-only, so another non-admin still can't tamper with a plugin's launch templates. - SDK: export pluginStateDir(name) -> <config_dir>/plugin-state/<name>. Same path on Linux (the systemd --user runner owns the config dir, writable with no grant), so plugins use one branch-free helper. Plugins must persist under pluginStateDir(), not straight under the config dir. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
30 lines
1.2 KiB
TypeScript
30 lines
1.2 KiB
TypeScript
// Connection/config resolution helpers. `pluginStateDir` is the writable location a supervised
|
|
// plugin persists into — the one dir the de-privileged Windows runner may write.
|
|
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
import * as path from "node:path";
|
|
import { pluginStateDir } from "../src/config.js";
|
|
|
|
describe("pluginStateDir", () => {
|
|
let saved: string | undefined;
|
|
beforeEach(() => {
|
|
saved = process.env.PUNKTFUNK_CONFIG_DIR;
|
|
});
|
|
afterEach(() => {
|
|
if (saved === undefined) delete process.env.PUNKTFUNK_CONFIG_DIR;
|
|
else process.env.PUNKTFUNK_CONFIG_DIR = saved;
|
|
});
|
|
|
|
test("resolves <config_dir>/plugin-state[/name] and honors the config-dir override", () => {
|
|
process.env.PUNKTFUNK_CONFIG_DIR = path.join("/tmp", "pf-cfg");
|
|
expect(pluginStateDir()).toBe(path.join("/tmp", "pf-cfg", "plugin-state"));
|
|
expect(pluginStateDir("rom-manager")).toBe(
|
|
path.join("/tmp", "pf-cfg", "plugin-state", "rom-manager"),
|
|
);
|
|
});
|
|
|
|
test("the per-plugin dir is nested under the shared root", () => {
|
|
process.env.PUNKTFUNK_CONFIG_DIR = path.join("/tmp", "pf-cfg2");
|
|
expect(pluginStateDir("x").startsWith(pluginStateDir())).toBe(true);
|
|
});
|
|
});
|