failproofai 1.0.0-beta.0 → 1.0.0-beta.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/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/required-server-files.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +14 -14
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +9 -9
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +14 -14
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +9 -9
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +9 -9
- package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0id_xf7._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__19120tr._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1fwl2mz._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1myjm-d._.js +1 -1
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0114ewg._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02r5bgf._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0e8sjqm._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0lkzqax._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0259h38._.js → [root-of-the-server]__0nb0syg._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qucxyj._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0u02miy._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__170799-._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ath6v_._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1psjybo._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1zqz4v8._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules_html-to-image_es_index_1-upb14.js → node_modules_html-to-image_es_index_0oy_-36.js} +1 -1
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +11 -11
- package/.next/standalone/.next/static/chunks/{1s-_swlc80y43.js → 0fi645si-uiiq.js} +1 -1
- package/.next/standalone/.next/static/chunks/{29k6ef49zppxj.js → 18rvdw5v72364.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2vr7aazw6k2-n.js → 2j94uwkb4s3qr.js} +1 -1
- package/.next/standalone/.next/static/chunks/{29ydr-8q0zis6.js → 2sbelanzvxvv0.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1d6ncspjagm53.js → 31feojxhjyjpl.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1grd7y94-igz9.js → 3hp8ulqknybil.js} +1 -1
- package/.next/standalone/.next/static/chunks/{3wrgbih-516uj.js → 3s7myk_co0gbh.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0ip1n8xmgxarv.js → 3vs4ay36tjds_.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0u9hc6oxx-gpj.js → 40k1_v4os4cw6.js} +1 -1
- package/.next/standalone/Cargo.lock +2 -2
- package/.next/standalone/Cargo.toml +1 -1
- package/.next/standalone/package.json +1 -1
- package/.next/standalone/server.js +1 -1
- package/dist/cli.mjs +225 -42
- package/dist/worker.mjs +1 -1
- package/package.json +1 -1
- package/src/hooks/configure-wizard.ts +91 -13
- package/src/hooks/daemon-service.ts +360 -58
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1zj-87g._.js +0 -3
- /package/.next/standalone/.next/static/{7Lxtd352rSaHB-pmm6G8M → NWAjs0tHFyzk97gRGG09r}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{7Lxtd352rSaHB-pmm6G8M → NWAjs0tHFyzk97gRGG09r}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{7Lxtd352rSaHB-pmm6G8M → NWAjs0tHFyzk97gRGG09r}/_ssgManifest.js +0 -0
|
@@ -8,8 +8,16 @@
|
|
|
8
8
|
* `configure-wizard.ts` calls the functions here directly, the same
|
|
9
9
|
* relationship it already has with `manager.ts`'s `installHooks()`.
|
|
10
10
|
*/
|
|
11
|
-
import {
|
|
12
|
-
|
|
11
|
+
import {
|
|
12
|
+
existsSync,
|
|
13
|
+
mkdirSync,
|
|
14
|
+
mkdtempSync,
|
|
15
|
+
readFileSync,
|
|
16
|
+
writeFileSync,
|
|
17
|
+
unlinkSync,
|
|
18
|
+
rmSync,
|
|
19
|
+
} from "node:fs";
|
|
20
|
+
import { homedir, tmpdir, userInfo } from "node:os";
|
|
13
21
|
import { resolve, dirname } from "node:path";
|
|
14
22
|
import { execFileSync } from "node:child_process";
|
|
15
23
|
import { hookLogWarn } from "./hook-logger";
|
|
@@ -45,6 +53,14 @@ const SERVICE_START_POLL_MS = 100;
|
|
|
45
53
|
*/
|
|
46
54
|
const SERVICE_SETTLE_MS = 750;
|
|
47
55
|
|
|
56
|
+
/**
|
|
57
|
+
* How long `sudo -v` may sit at its password prompt. Unlike every other
|
|
58
|
+
* timeout here, this one is waiting on a human typing, not on a service
|
|
59
|
+
* manager — three attempts at a password is well inside two minutes, and
|
|
60
|
+
* sudo gives up on its own long before that.
|
|
61
|
+
*/
|
|
62
|
+
const SUDO_PROMPT_TIMEOUT_MS = 120_000;
|
|
63
|
+
|
|
48
64
|
/**
|
|
49
65
|
* Writes (or clears) the machine-wide `daemonConfigured` marker in
|
|
50
66
|
* `~/.failproofai/policies-config.json`.
|
|
@@ -168,24 +184,105 @@ export async function ensureFailproofaidBinary(): Promise<{ path?: string; reaso
|
|
|
168
184
|
* here, once, at install time, and handing it to the daemon via the
|
|
169
185
|
* environment closes that gap entirely.
|
|
170
186
|
*/
|
|
171
|
-
function resolveWorkerCommand(): string | null {
|
|
187
|
+
export function resolveWorkerCommand(): string | null {
|
|
172
188
|
if (process.env.FAILPROOFAI_WORKER_CMD) return process.env.FAILPROOFAI_WORKER_CMD;
|
|
173
189
|
|
|
174
190
|
const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT;
|
|
175
191
|
if (!packageRoot) return null;
|
|
176
192
|
const workerScript = resolve(packageRoot, "dist", "worker.mjs");
|
|
177
193
|
if (!existsSync(workerScript)) return null;
|
|
178
|
-
|
|
194
|
+
// Both paths are shell-quoted: the daemon runs this value through `sh -c`
|
|
195
|
+
// (`WorkerCommand::Shell` in crates/failproofaid/src/worker.rs), so an
|
|
196
|
+
// unquoted path splits on its spaces and the worker never starts. Not
|
|
197
|
+
// hypothetical on macOS, where `/Users/First Last/...` is ordinary — and
|
|
198
|
+
// more exposed since execPath replaced a bare `node`, because execPath is
|
|
199
|
+
// home-derived. systemd's own `Environment="..."` quoting does not help
|
|
200
|
+
// here; that protects the unit parse, not the later shell split.
|
|
201
|
+
//
|
|
202
|
+
// `process.execPath`, not a bare `node`. A system-scope service does not
|
|
203
|
+
// inherit a login environment, so its PATH is the system default — and the
|
|
204
|
+
// single most common way to install Node is nvm, which puts it under
|
|
205
|
+
// ~/.nvm/versions/node/*/bin and nowhere on that PATH. A bare `node` would
|
|
206
|
+
// resolve fine when the wizard runs it and then fail inside the service,
|
|
207
|
+
// silently, on exactly the machines least likely to notice. execPath is
|
|
208
|
+
// whatever runtime is executing this CLI right now (node for the published
|
|
209
|
+
// bin, bun in a source checkout), absolute either way.
|
|
210
|
+
return `${shellQuote(process.execPath)} ${shellQuote(workerScript)}`;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** POSIX single-quoting: everything is literal inside '…', and a literal
|
|
214
|
+
* quote is closed, escaped and reopened. */
|
|
215
|
+
function shellQuote(value: string): string {
|
|
216
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* The account the daemon runs as. The service is root-*installed* but never
|
|
221
|
+
* root-*run*: everything it touches (the socket, the lock, the policy config)
|
|
222
|
+
* lives in one user's home and is peer-checked against that user's uid.
|
|
223
|
+
*/
|
|
224
|
+
function serviceUser(): string {
|
|
225
|
+
return userInfo().username;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* `/etc/systemd/system/failproofaid@<user>.service`.
|
|
230
|
+
*
|
|
231
|
+
* The `@<user>` suffix is systemd's convention for a per-user instance, but
|
|
232
|
+
* this is a concrete unit file rather than an instance of a template: every
|
|
233
|
+
* field that matters is user-specific (the ExecStart path is under the user's
|
|
234
|
+
* own ~/.failproofai/bin, so is HOME, so is the worker command), so a shared
|
|
235
|
+
* template would need a per-instance drop-in for all of them and buy nothing.
|
|
236
|
+
* Naming it per-user is what keeps a second user's install from silently
|
|
237
|
+
* stealing the first's unit — a single `failproofaid.service` would.
|
|
238
|
+
*/
|
|
239
|
+
function systemdUnitName(user: string = serviceUser()): string {
|
|
240
|
+
return `failproofaid@${user}.service`;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function systemdUnitPath(user: string = serviceUser()): string {
|
|
244
|
+
return resolve("/etc/systemd/system", systemdUnitName(user));
|
|
179
245
|
}
|
|
180
246
|
|
|
181
|
-
|
|
247
|
+
/**
|
|
248
|
+
* The pre-1.0.0-beta.1 user-scope unit. Still removed on install and
|
|
249
|
+
* uninstall: it holds the same flock the new service needs, so leaving one
|
|
250
|
+
* behind means the system unit starts, loses the singleton race, and the
|
|
251
|
+
* machine sits fail-closed against a daemon that never came up.
|
|
252
|
+
*/
|
|
253
|
+
function legacySystemdUserUnitPath(): string {
|
|
182
254
|
return resolve(homedir(), ".config", "systemd", "user", "failproofaid.service");
|
|
183
255
|
}
|
|
184
256
|
|
|
185
|
-
|
|
257
|
+
/**
|
|
258
|
+
* Per user, for the same reason the systemd unit is: a LaunchDaemon's
|
|
259
|
+
* contents are entirely user-specific — `UserName`, the ExecStart path under
|
|
260
|
+
* that user's `~/.failproofai/bin`, the log paths under that user's home — so
|
|
261
|
+
* one shared label means the second user's install silently overwrites the
|
|
262
|
+
* first's daemon, and their uninstall deletes it.
|
|
263
|
+
*/
|
|
264
|
+
function launchdLabel(user: string = serviceUser()): string {
|
|
265
|
+
return `ai.failproof.failproofaid.${user}`;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function launchdPlistPath(user: string = serviceUser()): string {
|
|
269
|
+
return `/Library/LaunchDaemons/${launchdLabel(user)}.plist`;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/** The pre-1.0.0-beta.1 LaunchAgent. */
|
|
273
|
+
function legacyLaunchAgentPlistPath(): string {
|
|
186
274
|
return resolve(homedir(), "Library", "LaunchAgents", "ai.failproof.failproofaid.plist");
|
|
187
275
|
}
|
|
188
276
|
|
|
277
|
+
/**
|
|
278
|
+
* The 1.0.0-beta.1 LaunchDaemon, before the label was namespaced. Removed on
|
|
279
|
+
* install like the LaunchAgent is: it holds the same singleton flock, so
|
|
280
|
+
* leaving it would leave two daemons racing for one socket.
|
|
281
|
+
*/
|
|
282
|
+
function legacySharedLaunchDaemonPath(): string {
|
|
283
|
+
return "/Library/LaunchDaemons/ai.failproof.failproofaid.plist";
|
|
284
|
+
}
|
|
285
|
+
|
|
189
286
|
/**
|
|
190
287
|
* The service-definition file `installDaemonService` would write on this
|
|
191
288
|
* platform (the systemd unit or the launchd plist) — exposed so the config
|
|
@@ -197,23 +294,180 @@ export function daemonServiceFilePath(): string | null {
|
|
|
197
294
|
return process.platform === "linux" ? systemdUnitPath() : launchdPlistPath();
|
|
198
295
|
}
|
|
199
296
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
297
|
+
/**
|
|
298
|
+
* The command a user runs to inspect their own daemon — surfaced so the
|
|
299
|
+
* wizard can print it instead of leaving people to discover which service
|
|
300
|
+
* manager, and which scope, is involved.
|
|
301
|
+
*/
|
|
302
|
+
export function daemonStatusCommand(): string | null {
|
|
303
|
+
if (!isDaemonSupportedPlatform()) return null;
|
|
304
|
+
return process.platform === "linux"
|
|
305
|
+
? `systemctl status ${systemdUnitName()}`
|
|
306
|
+
: `sudo launchctl print system/${launchdLabel()}`;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Acquires sudo credentials up front, prompting in the terminal if needed.
|
|
311
|
+
*
|
|
312
|
+
* `sudo -v` refreshes the user's sudo timestamp and returns; every later
|
|
313
|
+
* `sudo -n` in this run then succeeds against that cached credential. Doing it
|
|
314
|
+
* here, once, at a known point in the wizard is what makes the rest of the
|
|
315
|
+
* install non-interactive — the alternative, an interactive sudo firing from
|
|
316
|
+
* underneath a half-drawn TUI screen, is a password prompt the user cannot
|
|
317
|
+
* see.
|
|
318
|
+
*
|
|
319
|
+
* NOT the same as running the whole CLI under sudo, which is actively wrong:
|
|
320
|
+
* `homedir()` would become /root, so the hooks, the policy config, the
|
|
321
|
+
* downloaded binary and the unit's own `User=` would all be root's rather
|
|
322
|
+
* than the user's, quietly configuring the wrong account.
|
|
323
|
+
*
|
|
324
|
+
* `stdio: "inherit"` so the prompt and the typed password go straight to the
|
|
325
|
+
* real terminal. The timeout is generous because a human is typing.
|
|
326
|
+
*/
|
|
327
|
+
export function primeElevation(): boolean {
|
|
328
|
+
if (typeof process.getuid === "function" && process.getuid() === 0) return true;
|
|
329
|
+
if (canElevate()) return true; // NOPASSWD, or already primed
|
|
330
|
+
try {
|
|
331
|
+
execFileSync("sudo", ["-v"], { stdio: "inherit", timeout: SUDO_PROMPT_TIMEOUT_MS });
|
|
332
|
+
return canElevate();
|
|
333
|
+
} catch {
|
|
334
|
+
// Wrong password, no sudo rights, or the user pressed ctrl-C. All of them
|
|
335
|
+
// mean the same thing here: carry on without a daemon.
|
|
336
|
+
return false;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/** True when privileged commands can run without prompting for a password. */
|
|
341
|
+
function canElevate(): boolean {
|
|
342
|
+
if (typeof process.getuid === "function" && process.getuid() === 0) return true;
|
|
343
|
+
try {
|
|
344
|
+
execFileSync("sudo", ["-n", "true"], { stdio: "ignore", timeout: SERVICE_CMD_TIMEOUT_MS });
|
|
345
|
+
return true;
|
|
346
|
+
} catch {
|
|
347
|
+
return false;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Runs one privileged command, non-interactively.
|
|
353
|
+
*
|
|
354
|
+
* `sudo -n` on purpose: the wizard owns the terminal, and a sudo password
|
|
355
|
+
* prompt fired from underneath a TUI is unreadable at best. A machine that
|
|
356
|
+
* cannot elevate silently gets a clear reason and the manual commands
|
|
357
|
+
* instead — it keeps working exactly as it did, on the in-process path.
|
|
358
|
+
*/
|
|
359
|
+
function runPrivileged(command: string, args: string[]): void {
|
|
360
|
+
const root = typeof process.getuid === "function" && process.getuid() === 0;
|
|
361
|
+
const [cmd, argv] = root ? [command, args] : ["sudo", ["-n", command, ...args]];
|
|
362
|
+
execFileSync(cmd, argv, { stdio: "ignore", timeout: SERVICE_CMD_TIMEOUT_MS });
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Installs a file into a root-owned location via a temp file, because the
|
|
367
|
+
* caller is not root and cannot write there directly. `install -m` sets the
|
|
368
|
+
* mode in the same step, so the file is never briefly world-writable.
|
|
369
|
+
*/
|
|
370
|
+
function writePrivilegedFile(destination: string, contents: string, mode = "0644"): void {
|
|
371
|
+
// mkdtempSync, not a name built from pid + timestamp in the shared tmpdir.
|
|
372
|
+
// That name is guessable, and `writeFileSync` follows a symlink already
|
|
373
|
+
// sitting at the path — so on a multi-user box another local user could
|
|
374
|
+
// pre-create it and have `install` copy content they control into
|
|
375
|
+
// /etc/systemd/system as root. mkdtemp gives a 0700 directory with a name
|
|
376
|
+
// they cannot predict, so there is nothing to pre-create.
|
|
377
|
+
const stagingDir = mkdtempSync(resolve(tmpdir(), "failproofaid-stage-"));
|
|
378
|
+
const staging = resolve(stagingDir, "service-definition");
|
|
379
|
+
try {
|
|
380
|
+
writeFileSync(staging, contents, "utf8");
|
|
381
|
+
runPrivileged("install", ["-m", mode, staging, destination]);
|
|
382
|
+
} finally {
|
|
383
|
+
rmSync(stagingDir, { recursive: true, force: true });
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Removes a daemon installed by an earlier version into the user's own
|
|
389
|
+
* session scope. Best-effort and never privileged — these paths are all
|
|
390
|
+
* inside the user's home.
|
|
391
|
+
*/
|
|
392
|
+
function removeLegacyUserService(): void {
|
|
393
|
+
try {
|
|
394
|
+
if (process.platform === "linux") {
|
|
395
|
+
const legacy = legacySystemdUserUnitPath();
|
|
396
|
+
if (!existsSync(legacy)) return;
|
|
397
|
+
try {
|
|
398
|
+
execFileSync("systemctl", ["--user", "disable", "--now", "failproofaid.service"], {
|
|
399
|
+
stdio: "ignore",
|
|
400
|
+
timeout: SERVICE_CMD_TIMEOUT_MS,
|
|
401
|
+
});
|
|
402
|
+
} catch {
|
|
403
|
+
// Not loaded — removing the file is still the point.
|
|
404
|
+
}
|
|
405
|
+
unlinkSync(legacy);
|
|
406
|
+
try {
|
|
407
|
+
execFileSync("systemctl", ["--user", "daemon-reload"], {
|
|
408
|
+
stdio: "ignore",
|
|
409
|
+
timeout: SERVICE_CMD_TIMEOUT_MS,
|
|
410
|
+
});
|
|
411
|
+
} catch {
|
|
412
|
+
/* best-effort */
|
|
413
|
+
}
|
|
414
|
+
} else {
|
|
415
|
+
const legacyAgent = legacyLaunchAgentPlistPath();
|
|
416
|
+
if (existsSync(legacyAgent)) {
|
|
417
|
+
try {
|
|
418
|
+
execFileSync("launchctl", ["unload", "-w", legacyAgent], {
|
|
419
|
+
stdio: "ignore",
|
|
420
|
+
timeout: SERVICE_CMD_TIMEOUT_MS,
|
|
421
|
+
});
|
|
422
|
+
} catch {
|
|
423
|
+
/* not loaded */
|
|
424
|
+
}
|
|
425
|
+
unlinkSync(legacyAgent);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// The 1.0.0-beta.1 daemon, before the label was per-user. Privileged,
|
|
429
|
+
// and only reachable on a path that has already elevated.
|
|
430
|
+
const legacyDaemon = legacySharedLaunchDaemonPath();
|
|
431
|
+
if (existsSync(legacyDaemon)) {
|
|
432
|
+
try {
|
|
433
|
+
runPrivileged("launchctl", ["unload", "-w", legacyDaemon]);
|
|
434
|
+
} catch {
|
|
435
|
+
/* not loaded */
|
|
436
|
+
}
|
|
437
|
+
runPrivileged("rm", ["-f", legacyDaemon]);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
} catch (err) {
|
|
441
|
+
hookLogWarn(`could not remove the legacy user-scope daemon: ${err instanceof Error ? err.message : String(err)}`);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
export function systemdUnitContents(binaryPath: string, workerCmd: string | null): string {
|
|
446
|
+
// Quoted because both values contain a space or a path — systemd's
|
|
447
|
+
// Environment= requires quoting whenever the value does.
|
|
204
448
|
const envLine = workerCmd ? `Environment="FAILPROOFAI_WORKER_CMD=${workerCmd}"\n` : "";
|
|
449
|
+
const user = serviceUser();
|
|
205
450
|
return `[Unit]
|
|
206
|
-
Description=failproofai background daemon (failproofaid)
|
|
451
|
+
Description=failproofai background daemon (failproofaid) for ${user}
|
|
207
452
|
After=network.target
|
|
208
453
|
|
|
209
454
|
[Service]
|
|
210
455
|
Type=simple
|
|
456
|
+
User=${user}
|
|
457
|
+
# Set explicitly rather than relying on systemd deriving it from User=:
|
|
458
|
+
# failproofaid is user-scope by construction and refuses to start without
|
|
459
|
+
# HOME ("HOME is not set; failproofaid is user-scope only"), so the one
|
|
460
|
+
# variable it cannot do without is not left to a version-dependent default.
|
|
461
|
+
Environment="HOME=${homedir()}"
|
|
211
462
|
${envLine}ExecStart=${binaryPath}
|
|
212
463
|
Restart=on-failure
|
|
213
464
|
RestartSec=2
|
|
214
465
|
|
|
215
466
|
[Install]
|
|
216
|
-
|
|
467
|
+
# multi-user.target, not default.target: this is the whole point of a
|
|
468
|
+
# system unit — it starts at boot, with no login and no lingering, and
|
|
469
|
+
# keeps running after the installing user logs out.
|
|
470
|
+
WantedBy=multi-user.target
|
|
217
471
|
`;
|
|
218
472
|
}
|
|
219
473
|
|
|
@@ -235,7 +489,7 @@ function launchdPlistContents(binaryPath: string, logDir: string, workerCmd: str
|
|
|
235
489
|
<plist version="1.0">
|
|
236
490
|
<dict>
|
|
237
491
|
<key>Label</key>
|
|
238
|
-
<string
|
|
492
|
+
<string>${escapeXml(launchdLabel())}</string>
|
|
239
493
|
<key>ProgramArguments</key>
|
|
240
494
|
<array>
|
|
241
495
|
<string>${escapeXml(binaryPath)}</string>
|
|
@@ -244,6 +498,12 @@ function launchdPlistContents(binaryPath: string, logDir: string, workerCmd: str
|
|
|
244
498
|
<true/>
|
|
245
499
|
<key>KeepAlive</key>
|
|
246
500
|
<true/>
|
|
501
|
+
<!-- A LaunchDaemon runs as root unless told otherwise. This is the
|
|
502
|
+
launchd half of the systemd unit's User=: loaded at boot by the
|
|
503
|
+
system, executed as the one user whose home, socket and lock it
|
|
504
|
+
is allowed to touch. -->
|
|
505
|
+
<key>UserName</key>
|
|
506
|
+
<string>${escapeXml(serviceUser())}</string>
|
|
247
507
|
${envBlock} <key>StandardOutPath</key>
|
|
248
508
|
<string>${escapeXml(resolve(logDir, "failproofaid.log"))}</string>
|
|
249
509
|
<key>StandardErrorPath</key>
|
|
@@ -258,6 +518,25 @@ export interface DaemonInstallResult {
|
|
|
258
518
|
reason?: string;
|
|
259
519
|
}
|
|
260
520
|
|
|
521
|
+
/**
|
|
522
|
+
* The privileged commands an install performs — returned verbatim so a
|
|
523
|
+
* machine that cannot elevate can be told exactly what to run rather than
|
|
524
|
+
* just that something failed.
|
|
525
|
+
*/
|
|
526
|
+
function daemonInstallCommands(binaryPath: string, workerCmd: string | null): string[] {
|
|
527
|
+
if (process.platform === "linux") {
|
|
528
|
+
return [
|
|
529
|
+
`sudo tee ${systemdUnitPath()} <<'EOF'\n${systemdUnitContents(binaryPath, workerCmd)}EOF`,
|
|
530
|
+
"sudo systemctl daemon-reload",
|
|
531
|
+
`sudo systemctl enable --now ${systemdUnitName()}`,
|
|
532
|
+
];
|
|
533
|
+
}
|
|
534
|
+
return [
|
|
535
|
+
`sudo tee ${launchdPlistPath()} < the plist failproofai config would write`,
|
|
536
|
+
`sudo launchctl load -w ${launchdPlistPath()}`,
|
|
537
|
+
];
|
|
538
|
+
}
|
|
539
|
+
|
|
261
540
|
/**
|
|
262
541
|
* Writes and enables the service unit, starting it immediately. Safe to
|
|
263
542
|
* call repeatedly — re-running replaces the unit file (picking up a
|
|
@@ -284,40 +563,45 @@ export async function installDaemonService(): Promise<DaemonInstallResult> {
|
|
|
284
563
|
// worker regardless of what cwd the service manager starts it from.
|
|
285
564
|
const workerCmd = resolveWorkerCommand();
|
|
286
565
|
|
|
566
|
+
// The service is installed system-wide, which needs root. Check before
|
|
567
|
+
// writing anything, so a machine that cannot elevate gets the exact
|
|
568
|
+
// commands to run instead of a half-installed service.
|
|
569
|
+
if (!canElevate()) {
|
|
570
|
+
return {
|
|
571
|
+
installed: false,
|
|
572
|
+
reason:
|
|
573
|
+
"root privileges are required to install the failproofaid system service, and sudo credentials were not available. " +
|
|
574
|
+
"Re-run `failproofai config` and approve the sudo prompt — do NOT run the CLI itself under sudo, which would " +
|
|
575
|
+
"configure root's account instead of yours. To install by hand: " +
|
|
576
|
+
daemonInstallCommands(binaryPath, workerCmd).join(" && "),
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// A daemon left over from a pre-1.0.0-beta.1 install holds the same
|
|
581
|
+
// singleton lock the new one needs, so the system unit would start, lose
|
|
582
|
+
// the flock race, and leave the machine fail-closed against a daemon that
|
|
583
|
+
// never came up. Clear it first, every time.
|
|
584
|
+
removeLegacyUserService();
|
|
585
|
+
|
|
287
586
|
try {
|
|
288
587
|
if (process.platform === "linux") {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
execFileSync("systemctl", ["--user", "daemon-reload"], {
|
|
293
|
-
stdio: "ignore",
|
|
294
|
-
timeout: SERVICE_CMD_TIMEOUT_MS,
|
|
295
|
-
});
|
|
296
|
-
execFileSync("systemctl", ["--user", "enable", "--now", "failproofaid.service"], {
|
|
297
|
-
stdio: "ignore",
|
|
298
|
-
timeout: SERVICE_CMD_TIMEOUT_MS,
|
|
299
|
-
});
|
|
588
|
+
writePrivilegedFile(systemdUnitPath(), systemdUnitContents(binaryPath, workerCmd));
|
|
589
|
+
runPrivileged("systemctl", ["daemon-reload"]);
|
|
590
|
+
runPrivileged("systemctl", ["enable", "--now", systemdUnitName()]);
|
|
300
591
|
} else {
|
|
301
592
|
const plistPath = launchdPlistPath();
|
|
302
593
|
const logDir = resolve(homedir(), ".failproofai", "logs");
|
|
303
|
-
mkdirSync(dirname(plistPath), { recursive: true });
|
|
304
594
|
mkdirSync(logDir, { recursive: true });
|
|
305
|
-
writeFileSync(plistPath, launchdPlistContents(binaryPath, logDir, workerCmd), "utf8");
|
|
306
595
|
// Unload any previously-loaded copy first — reloading with a changed
|
|
307
596
|
// binary path (e.g. after an upgrade) is a no-op under plain `load`
|
|
308
597
|
// if launchd thinks the label is already loaded.
|
|
309
598
|
try {
|
|
310
|
-
|
|
311
|
-
stdio: "ignore",
|
|
312
|
-
timeout: SERVICE_CMD_TIMEOUT_MS,
|
|
313
|
-
});
|
|
599
|
+
runPrivileged("launchctl", ["unload", plistPath]);
|
|
314
600
|
} catch {
|
|
315
601
|
// Wasn't loaded — fine, this is the common case on a fresh install.
|
|
316
602
|
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
timeout: SERVICE_CMD_TIMEOUT_MS,
|
|
320
|
-
});
|
|
603
|
+
writePrivilegedFile(plistPath, launchdPlistContents(binaryPath, logDir, workerCmd));
|
|
604
|
+
runPrivileged("launchctl", ["load", "-w", plistPath]);
|
|
321
605
|
}
|
|
322
606
|
} catch (err) {
|
|
323
607
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -370,40 +654,44 @@ async function waitForDaemonRunning(): Promise<boolean> {
|
|
|
370
654
|
export async function uninstallDaemonService(): Promise<void> {
|
|
371
655
|
setDaemonConfigured(false);
|
|
372
656
|
if (!isDaemonSupportedPlatform()) return;
|
|
657
|
+
|
|
658
|
+
// Always attempted, and never privileged: a legacy user-scope daemon is
|
|
659
|
+
// the one thing this can still clean up on a machine that cannot elevate.
|
|
660
|
+
removeLegacyUserService();
|
|
661
|
+
|
|
373
662
|
try {
|
|
374
663
|
if (process.platform === "linux") {
|
|
664
|
+
const unitPath = systemdUnitPath();
|
|
665
|
+
if (!existsSync(unitPath)) return;
|
|
375
666
|
try {
|
|
376
|
-
|
|
377
|
-
stdio: "ignore",
|
|
378
|
-
timeout: SERVICE_CMD_TIMEOUT_MS,
|
|
379
|
-
});
|
|
667
|
+
runPrivileged("systemctl", ["disable", "--now", systemdUnitName()]);
|
|
380
668
|
} catch {
|
|
381
|
-
// Already stopped/not
|
|
669
|
+
// Already stopped/not enabled — removing the unit is still the point.
|
|
382
670
|
}
|
|
383
|
-
|
|
384
|
-
if (existsSync(unitPath)) unlinkSync(unitPath);
|
|
671
|
+
runPrivileged("rm", ["-f", unitPath]);
|
|
385
672
|
try {
|
|
386
|
-
|
|
387
|
-
stdio: "ignore",
|
|
388
|
-
timeout: SERVICE_CMD_TIMEOUT_MS,
|
|
389
|
-
});
|
|
673
|
+
runPrivileged("systemctl", ["daemon-reload"]);
|
|
390
674
|
} catch {
|
|
391
675
|
// Best-effort.
|
|
392
676
|
}
|
|
393
677
|
} else {
|
|
394
678
|
const plistPath = launchdPlistPath();
|
|
679
|
+
if (!existsSync(plistPath)) return;
|
|
395
680
|
try {
|
|
396
|
-
|
|
397
|
-
stdio: "ignore",
|
|
398
|
-
timeout: SERVICE_CMD_TIMEOUT_MS,
|
|
399
|
-
});
|
|
681
|
+
runPrivileged("launchctl", ["unload", "-w", plistPath]);
|
|
400
682
|
} catch {
|
|
401
|
-
// Already unloaded
|
|
683
|
+
// Already unloaded — fine.
|
|
402
684
|
}
|
|
403
|
-
|
|
685
|
+
runPrivileged("rm", ["-f", plistPath]);
|
|
404
686
|
}
|
|
405
687
|
} catch (err) {
|
|
406
|
-
|
|
688
|
+
// `daemonConfigured` is already cleared above, so a machine that cannot
|
|
689
|
+
// elevate is back on the in-process path even though the unit file
|
|
690
|
+
// survives — it fails open, not closed.
|
|
691
|
+
hookLogWarn(
|
|
692
|
+
`daemon service uninstall failed (the service may need removing by hand: ${daemonStatusCommand()}): ` +
|
|
693
|
+
`${err instanceof Error ? err.message : String(err)}`,
|
|
694
|
+
);
|
|
407
695
|
}
|
|
408
696
|
}
|
|
409
697
|
|
|
@@ -418,7 +706,9 @@ export function daemonServiceStatus(): DaemonServiceStatus {
|
|
|
418
706
|
if (process.platform === "linux") {
|
|
419
707
|
if (!existsSync(systemdUnitPath())) return "not-installed";
|
|
420
708
|
try {
|
|
421
|
-
|
|
709
|
+
// Unprivileged on purpose: reading a system unit's state needs no
|
|
710
|
+
// root, so status works for the owning user with no sudo at all.
|
|
711
|
+
const out = execFileSync("systemctl", ["is-active", systemdUnitName()], {
|
|
422
712
|
stdio: ["ignore", "pipe", "ignore"],
|
|
423
713
|
timeout: SERVICE_CMD_TIMEOUT_MS,
|
|
424
714
|
})
|
|
@@ -438,11 +728,23 @@ export function daemonServiceStatus(): DaemonServiceStatus {
|
|
|
438
728
|
const plistPath = launchdPlistPath();
|
|
439
729
|
if (!existsSync(plistPath)) return "not-installed";
|
|
440
730
|
try {
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
731
|
+
// A LaunchDaemon lives in launchd's system domain, which an unprivileged
|
|
732
|
+
// `launchctl list` cannot see — unlike systemd, reading the state needs
|
|
733
|
+
// the same elevation installing it did.
|
|
734
|
+
const root = typeof process.getuid === "function" && process.getuid() === 0;
|
|
735
|
+
const args = ["print", `system/${launchdLabel()}`];
|
|
736
|
+
const out = root
|
|
737
|
+
? execFileSync("launchctl", args, {
|
|
738
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
739
|
+
timeout: SERVICE_CMD_TIMEOUT_MS,
|
|
740
|
+
}).toString()
|
|
741
|
+
: execFileSync("sudo", ["-n", "launchctl", ...args], {
|
|
742
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
743
|
+
timeout: SERVICE_CMD_TIMEOUT_MS,
|
|
744
|
+
}).toString();
|
|
745
|
+
// `state = running` is launchd's own wording; a loaded-but-dead job
|
|
746
|
+
// prints `state = not running` and must not read as healthy.
|
|
747
|
+
return /state\s*=\s*running/.test(out) ? "running" : "stopped";
|
|
446
748
|
} catch {
|
|
447
749
|
return "stopped";
|
|
448
750
|
}
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
module.exports=[18622,(a,b,c)=>{b.exports=a.x("next/dist/compiled/next-server/app-page-turbo.runtime.prod.js",()=>require("next/dist/compiled/next-server/app-page-turbo.runtime.prod.js"))},56704,(a,b,c)=>{b.exports=a.x("next/dist/server/app-render/work-async-storage.external.js",()=>require("next/dist/server/app-render/work-async-storage.external.js"))},32319,(a,b,c)=>{b.exports=a.x("next/dist/server/app-render/work-unit-async-storage.external.js",()=>require("next/dist/server/app-render/work-unit-async-storage.external.js"))},20635,(a,b,c)=>{b.exports=a.x("next/dist/server/app-render/action-async-storage.external.js",()=>require("next/dist/server/app-render/action-async-storage.external.js"))},24725,(a,b,c)=>{b.exports=a.x("next/dist/server/app-render/after-task-async-storage.external.js",()=>require("next/dist/server/app-render/after-task-async-storage.external.js"))},43285,(a,b,c)=>{b.exports=a.x("next/dist/server/app-render/dynamic-access-async-storage.external.js",()=>require("next/dist/server/app-render/dynamic-access-async-storage.external.js"))},42602,(a,b,c)=>{"use strict";b.exports=a.r(18622)},87924,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored["react-ssr"].ReactJsxRuntime},72131,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored["react-ssr"].React},38783,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored["react-ssr"].ReactServerDOMTurbopackClient},9270,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored.contexts.AppRouterContext},36313,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored.contexts.HooksClientContext},18341,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored.contexts.ServerInsertedHtml},35112,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored["react-ssr"].ReactDOM},5607,a=>{"use strict";a.s(["captureClientEvent",0,function(a,b){},"setClientTelemetryConfig",0,function(a){}])},5050,(a,b,c)=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0});var d={callServer:function(){return f.callServer},createServerReference:function(){return h.createServerReference},findSourceMapURL:function(){return g.findSourceMapURL}};for(var e in d)Object.defineProperty(c,e,{enumerable:!0,get:d[e]});let f=a.r(20611),g=a.r(1722),h=a.r(38783)},2933,a=>{"use strict";var b=a.i(5050);let c=(0,b.createServerReference)("00eb0d1896aceb50136245d3f32f87ed0485ac965b",b.callServer,void 0,b.findSourceMapURL,"getTelemetryConfig");a.s(["getTelemetryConfig",0,c])},10765,a=>{"use strict";var b=a.i(87924),c=a.i(72131),d=a.i(50944),e=a.i(2933),f=a.i(5607);let g=(0,c.createContext)(void 0);a.s(["PostHogProvider",0,function({children:a}){let h=(0,d.usePathname)(),i=(0,c.useRef)(null),j=(0,c.useRef)(!1);(0,c.useEffect)(()=>{let a=!1;return(0,e.getTelemetryConfig)().then(b=>{a||((0,f.setClientTelemetryConfig)(b),j.current=!0,(0,f.captureClientEvent)("$pageview"),i.current=window.location.pathname)}),()=>{a=!0}},[]),(0,c.useEffect)(()=>{j.current&&i.current!==h&&(i.current=h,(0,f.captureClientEvent)("$pageview"))},[h]);let k=c.default.useCallback((a,b)=>{(0,f.captureClientEvent)(a,b)},[]);return(0,b.jsx)(g.Provider,{value:{capture:k},children:a})},"usePostHog",0,function(){let a=(0,c.useContext)(g);if(void 0===a)throw Error("usePostHog must be used within a PostHogProvider");return a}])},18952,a=>{"use strict";var b=a.i(87924),c=a.i(72131);let d=[],e=0,f=new Set;function g(){for(let a of f)a()}function h(a){return f.add(a),()=>f.delete(a)}function i(){return d}function j({message:a}){let[d,e]=(0,c.useState)(!1);return(0,c.useEffect)(()=>{requestAnimationFrame(()=>e(!0));let a=setTimeout(()=>e(!1),2e3);return()=>clearTimeout(a)},[]),(0,b.jsx)("div",{className:`bg-card border border-border text-foreground text-sm px-4 py-2 rounded-lg shadow-lg transition-opacity duration-300 ${d?"opacity-100":"opacity-0"}`,children:a})}a.s(["Toaster",0,function(){let a=(0,c.useSyncExternalStore)(h,i,i);return(0,b.jsx)("div",{className:"fixed bottom-4 right-4 z-50 flex flex-col gap-2",children:a.map(a=>(0,b.jsx)(j,{message:a.message},a.id))})},"toast",0,function(a){let b=e++;d=[...d,{id:b,message:a}],g(),setTimeout(()=>{d=d.filter(a=>a.id!==b),g()},2500)}])},46058,(a,b,c)=>{"use strict";function d(a){if("function"!=typeof WeakMap)return null;var b=new WeakMap,c=new WeakMap;return(d=function(a){return a?c:b})(a)}c._=function(a,b){if(!b&&a&&a.__esModule)return a;if(null===a||"object"!=typeof a&&"function"!=typeof a)return{default:a};var c=d(b);if(c&&c.has(a))return c.get(a);var e={__proto__:null},f=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var g in a)if("default"!==g&&Object.prototype.hasOwnProperty.call(a,g)){var h=f?Object.getOwnPropertyDescriptor(a,g):null;h&&(h.get||h.set)?Object.defineProperty(e,g,h):e[g]=a[g]}return e.default=a,c&&c.set(a,e),e}},88347,(a,b,c)=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0});var d,e,f={ACTION_HMR_REFRESH:function(){return l},ACTION_NAVIGATE:function(){return i},ACTION_REFRESH:function(){return h},ACTION_RESTORE:function(){return j},ACTION_SERVER_ACTION:function(){return m},ACTION_SERVER_PATCH:function(){return k},PrefetchKind:function(){return n},ScrollBehavior:function(){return o}};for(var g in f)Object.defineProperty(c,g,{enumerable:!0,get:f[g]});let h="refresh",i="navigate",j="restore",k="server-patch",l="hmr-refresh",m="server-action";var n=((d={}).AUTO="auto",d.FULL="full",d),o=((e={})[e.Default=0]="Default",e[e.NoScroll=1]="NoScroll",e);("function"==typeof c.default||"object"==typeof c.default&&null!==c.default)&&void 0===c.default.__esModule&&(Object.defineProperty(c.default,"__esModule",{value:!0}),Object.assign(c.default,c),b.exports=c.default)},67009,(a,b,c)=>{"use strict";function d(a){return null!==a&&"object"==typeof a&&"then"in a&&"function"==typeof a.then}Object.defineProperty(c,"__esModule",{value:!0}),Object.defineProperty(c,"isThenable",{enumerable:!0,get:function(){return d}})},90841,(a,b,c)=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0});var d={dispatchAppRouterAction:function(){return i},dispatchGestureState:function(){return j},refreshOnInstantNavigationUnlock:function(){return h},useActionQueue:function(){return k}};for(var e in d)Object.defineProperty(c,e,{enumerable:!0,get:d[e]});let f=a.r(46058)._(a.r(72131)),g=a.r(67009);a.r(88347);function h(){}function i(a){!0;throw Object.defineProperty(Error("Internal Next.js error: Router action dispatched before initialization."),"__NEXT_ERROR_CODE",{value:"E668",enumerable:!1,configurable:!0})}function j(a){!0;throw Object.defineProperty(Error("Internal Next.js error: Router action dispatched before initialization."),"__NEXT_ERROR_CODE",{value:"E668",enumerable:!1,configurable:!0})}function k(a){let[b,c]=f.default.useState(a.state),[d,e]=(0,f.useOptimistic)(b),h=(0,f.useMemo)(()=>d,[d]);return(0,g.isThenable)(h)?(0,f.use)(h):h}("function"==typeof c.default||"object"==typeof c.default&&null!==c.default)&&void 0===c.default.__esModule&&(Object.defineProperty(c.default,"__esModule",{value:!0}),Object.assign(c.default,c),b.exports=c.default)},20611,(a,b,c)=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0}),Object.defineProperty(c,"callServer",{enumerable:!0,get:function(){return g}});let d=a.r(72131),e=a.r(88347),f=a.r(90841);async function g(a,b){return new Promise((c,g)=>{(0,d.startTransition)(()=>{(0,f.dispatchAppRouterAction)({type:e.ACTION_SERVER_ACTION,actionId:a,actionArgs:b,resolve:c,reject:g})})})}("function"==typeof c.default||"object"==typeof c.default&&null!==c.default)&&void 0===c.default.__esModule&&(Object.defineProperty(c.default,"__esModule",{value:!0}),Object.assign(c.default,c),b.exports=c.default)},1722,(a,b,c)=>{"use strict";let d;Object.defineProperty(c,"__esModule",{value:!0}),Object.defineProperty(c,"findSourceMapURL",{enumerable:!0,get:function(){return d}});("function"==typeof c.default||"object"==typeof c.default&&null!==c.default)&&void 0===c.default.__esModule&&(Object.defineProperty(c.default,"__esModule",{value:!0}),Object.assign(c.default,c),b.exports=c.default)},39118,(a,b,c)=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0});var d={DEFAULT_SEGMENT_KEY:function(){return l},NOT_FOUND_SEGMENT_KEY:function(){return m},PAGE_SEGMENT_KEY:function(){return k},addSearchParamsIfPageSegment:function(){return i},computeSelectedLayoutSegment:function(){return j},getSegmentValue:function(){return f},getSelectedLayoutSegmentPath:function(){return function a(b,c,d=!0,e=[]){let g;if(d)g=b[1][c];else{let a=b[1];g=a.children??Object.values(a)[0]}if(!g)return e;let h=f(g[0]);return!h||h.startsWith(k)?e:(e.push(h),a(g,c,!1,e))}},isGroupSegment:function(){return g},isParallelRouteSegment:function(){return h}};for(var e in d)Object.defineProperty(c,e,{enumerable:!0,get:d[e]});function f(a){return Array.isArray(a)?a[1]:a}function g(a){return"("===a[0]&&a.endsWith(")")}function h(a){return a.startsWith("@")&&"@children"!==a}function i(a,b){if(a.includes(k)){let a=JSON.stringify(b);return"{}"!==a?k+"?"+a:k}return a}function j(a,b){if(!a||0===a.length)return null;let c="children"===b?a[0]:a[a.length-1];return c===l?null:c}let k="__PAGE__",l="__DEFAULT__",m="/_not-found"},88644,(a,b,c)=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0}),Object.defineProperty(c,"InvariantError",{enumerable:!0,get:function(){return d}});class d extends Error{constructor(a,b){super(`Invariant: ${a.endsWith(".")?a:a+"."} This is a bug in Next.js.`,b),this.name="InvariantError"}}},54427,(a,b,c)=>{"use strict";function d(){let a,b,c=new Promise((c,d)=>{a=c,b=d});return{resolve:a,reject:b,promise:c}}Object.defineProperty(c,"__esModule",{value:!0}),Object.defineProperty(c,"createPromiseWithResolvers",{enumerable:!0,get:function(){return d}})},41997,(a,b,c)=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0});var d={BailoutToCSRError:function(){return g},isBailoutToCSRError:function(){return h}};for(var e in d)Object.defineProperty(c,e,{enumerable:!0,get:d[e]});let f="BAILOUT_TO_CLIENT_SIDE_RENDERING";class g extends Error{constructor(a){super(`Bail out to client-side rendering: ${a}`),this.reason=a,this.digest=f}}function h(a){return"object"==typeof a&&null!==a&&"digest"in a&&a.digest===f}},89027,a=>{"use strict";let b=(0,a.i(64831).default)("chevron-down",[["path",{d:"m6 9 6 6 6-6",key:"qrunsl"}]]);a.s(["ChevronDown",0,b],89027)},6236,a=>{"use strict";var b=a.i(87924),c=a.i(72131);let d=(0,c.createContext)(void 0);a.s(["AutoRefreshProvider",0,function({children:a}){let[e,f]=(0,c.useState)(0);return(0,b.jsx)(d.Provider,{value:{intervalSec:e,setIntervalSec:f},children:a})},"useAutoRefresh",0,function(){let a=(0,c.useContext)(d);if(!a)throw Error("useAutoRefresh must be used within AutoRefreshProvider");return a}])},40695,a=>{"use strict";var b=a.i(87924),c=a.i(72131),d=a.i(97895);let e=c.forwardRef(({className:a,variant:c="default",size:e="default",...f},g)=>(0,b.jsx)("button",{className:(0,d.cn)("inline-flex items-center justify-center rounded-md text-sm font-medium transition-[background-color,color,box-shadow,border-color] duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background disabled:opacity-50 disabled:pointer-events-none",{"bg-primary text-primary-foreground hover:bg-primary/90 hover:shadow-[0_0_0_1px_var(--primary)]":"default"===c,"text-foreground hover:bg-muted":"ghost"===c,"border border-border bg-transparent hover:border-primary/60 hover:bg-card":"outline"===c,"h-10 py-2 px-4":"default"===e,"h-10 w-10":"icon"===e,"h-9 px-3":"sm"===e,"h-11 px-8":"lg"===e},a),ref:g,...f}));e.displayName="Button",a.s(["Button",0,e])},45550,a=>{"use strict";var b=a.i(72131),c=a.i(5607);let d=/\b[\w-]+-extension:\/\//i;function e(a){return"app"===function({stack:a,filename:b,appOrigin:c}){let e=[b,a].filter(Boolean).join("\n");return e.trim()?d.test(e)?"extension":c&&e.includes(c)?"app":"unknown":"unknown"}(a)}a.s(["GlobalErrorListeners",0,function(){return(0,b.useEffect)(()=>{let a=a=>{e({stack:a.error?.stack,filename:a.filename,appOrigin:window.location.origin})&&(0,c.captureClientEvent)("unhandled_exception",{error_message:a.message,error_name:a.error?.name,error_filename:a.filename,error_lineno:a.lineno,error_colno:a.colno})},b=a=>{let b=a.reason;e({stack:b instanceof Error?b.stack:void 0,appOrigin:window.location.origin})&&(0,c.captureClientEvent)("unhandled_rejection",{error_message:b instanceof Error?b.message:String(b),error_name:b instanceof Error?b.name:void 0})};return window.addEventListener("error",a),window.addEventListener("unhandledrejection",b),()=>{window.removeEventListener("error",a),window.removeEventListener("unhandledrejection",b)}},[]),null}],45550)},10815,a=>{"use strict";var b=a.i(87924),c=a.i(72131),d=a.i(38246),e=a.i(50944),f=a.i(64831);let g=(0,f.default)("git-branch",[["path",{d:"M15 6a9 9 0 0 0-9 9V3",key:"1cii5b"}],["circle",{cx:"18",cy:"6",r:"3",key:"1h7g24"}],["circle",{cx:"6",cy:"18",r:"3",key:"fqmcym"}]]);var h=a.i(89027);let i=(0,f.default)("star",[["path",{d:"M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.123 2.123 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.123 2.123 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.122 2.122 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.122 2.122 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.122 2.122 0 0 0 1.597-1.16z",key:"r04s7s"}]]),j=(0,f.default)("book-open",[["path",{d:"M12 5v16",key:"1f6ucr"}],["path",{d:"M20.001 19A2 2 0 0022 17V5a2 2 0 00-1.999-2L16 3.002A5 5 0 0012 5a5 5 0 00-4-2H4a2 2 0 00-2 2v12a2 2 0 001.999 2H8a5 5 0 014 2 5 5 0 014-2z",key:"1fyvmf"}]]),k=(0,f.default)("message-circle",[["path",{d:"M2.992 16.342a2 2 0 0 1 .094 1.167l-1.065 3.29a1 1 0 0 0 1.236 1.168l3.413-.998a2 2 0 0 1 1.099.092 10 10 0 1 0-4.777-4.719",key:"1sd12s"}]]),l=(0,f.default)("message-square-warning",[["path",{d:"M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z",key:"18887p"}],["path",{d:"M12 15h.01",key:"q59x07"}],["path",{d:"M12 7v4",key:"xawao1"}]]);var m=a.i(40695);let n="failproofai@exosphere.host",o=[{label:"Star us on GitHub",icon:i,href:"https://github.com/failproofai/failproofai",color:"#f5c842",bg:"rgba(245,200,66,0.08)"},{label:"Documentation",icon:j,href:"https://docs.befailproof.ai/introduction",color:"#60a5fa",bg:"rgba(96,165,250,0.08)"},{label:"Join our Discord",icon:k,href:"https://discord.befailproof.ai/",color:"#5865f2",bg:"rgba(88,101,242,0.08)"},{label:"Feedback & Issues",icon:l,href:"https://github.com/FailproofAI/failproofai/issues/new/choose",color:"#34d399",bg:"rgba(52,211,153,0.08)"}],p=()=>{let[a,d]=(0,c.useState)(!1),e=(0,c.useCallback)(()=>d(!1),[]),f=(0,c.useCallback)(a=>{"Escape"===a.key&&d(!1)},[]);return(0,b.jsxs)("div",{className:"relative",onKeyDown:f,children:[a&&(0,b.jsx)("div",{className:"fixed inset-0 z-40",onClick:e,"aria-hidden":"true"}),(0,b.jsxs)(m.Button,{type:"button",variant:"ghost",size:"sm",onClick:()=>d(a=>!a),"aria-expanded":a,"aria-haspopup":"true",className:"relative z-50 flex items-center gap-1.5 text-muted-foreground hover:text-foreground",children:[(0,b.jsx)(g,{className:"h-4 w-4"}),(0,b.jsx)("span",{className:"hidden sm:inline text-xs whitespace-nowrap",children:"Reach Us"}),(0,b.jsx)(h.ChevronDown,{className:`h-3 w-3 transition-transform ${a?"rotate-180":""}`})]}),a&&(0,b.jsxs)("div",{className:"absolute right-0 mt-2 w-60 rounded-lg border border-border bg-card shadow-xl z-50",role:"menu",style:{boxShadow:"0 8px 32px rgba(0,0,0,0.45), 0 0 0 1px rgba(255,255,255,0.04)"},children:[(0,b.jsxs)("div",{className:"px-3 py-2.5 border-b border-border",style:{background:"linear-gradient(135deg, rgba(236,72,153,0.12) 0%, rgba(139,92,246,0.08) 100%)"},children:[(0,b.jsx)("p",{className:"text-xs font-semibold",style:{color:"#e879f9",letterSpacing:"0.06em"},children:"Reach Developers"}),(0,b.jsx)("p",{className:"text-[0.65rem] text-muted-foreground mt-0.5",children:"We'd love to hear from you"})]}),(0,b.jsx)("div",{className:"py-1",children:o.map(({label:a,icon:c,href:d,color:f,bg:g})=>(0,b.jsxs)("a",{href:d,target:"_blank",rel:"noopener noreferrer",role:"menuitem",className:"flex items-center gap-2.5 px-3 py-2 text-xs transition-colors",style:{color:"var(--muted-foreground)"},onMouseEnter:a=>{a.currentTarget.style.background=g,a.currentTarget.style.color=f},onMouseLeave:a=>{a.currentTarget.style.background="",a.currentTarget.style.color=""},onClick:e,children:[(0,b.jsx)(c,{className:"h-3.5 w-3.5 flex-shrink-0",style:{color:f}}),a]},a))}),(0,b.jsx)("div",{className:"px-3 py-2 border-t border-border",style:{background:"rgba(0,0,0,0.2)"},children:(0,b.jsxs)("p",{className:"text-[0.65rem] text-muted-foreground",children:["or email"," ",(0,b.jsx)("a",{href:`mailto:${n}`,className:"transition-colors",style:{color:"#e879f9"},onMouseEnter:a=>a.currentTarget.style.opacity="0.75",onMouseLeave:a=>a.currentTarget.style.opacity="1",children:n})]})})]})]})},q=(0,f.default)("refresh-cw",[["path",{d:"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8",key:"v9h5vc"}],["path",{d:"M21 3v5h-5",key:"1q7to0"}],["path",{d:"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16",key:"3uifl3"}],["path",{d:"M8 16H3v5",key:"1cv678"}]]);var r=a.i(97895),s=a.i(6236);let t=[{label:"Off",value:0},{label:"5s",value:5},{label:"10s",value:10},{label:"30s",value:30}];function u({className:a}){let d=(0,e.useRouter)(),[f,g]=(0,c.useTransition)(),{intervalSec:h,setIntervalSec:i}=(0,s.useAutoRefresh)(),j=(0,c.useCallback)(()=>{g(()=>d.refresh())},[d]);(0,c.useEffect)(()=>{if(h<=0)return;let a=setInterval(j,1e3*h);return()=>clearInterval(a)},[h,j]);let k=h>0;return(0,b.jsxs)("div",{className:(0,r.cn)("inline-flex items-center rounded-lg border border-border bg-muted/50 p-0.5 gap-0.5",a),children:[(0,b.jsx)("button",{onClick:j,title:"Refresh",className:(0,r.cn)("inline-flex items-center justify-center rounded-md p-1.5 transition-colors",k?"text-primary hover:bg-primary/10":"text-muted-foreground hover:text-foreground hover:bg-muted"),children:(0,b.jsx)(q,{className:(0,r.cn)("w-3.5 h-3.5",f&&"animate-spin")})}),(0,b.jsx)("div",{className:"w-px h-4 bg-border"}),(0,b.jsx)("div",{className:"inline-flex items-center gap-0.5",role:"group","aria-label":"Auto-refresh interval",children:t.map(a=>(0,b.jsx)("button",{onClick:()=>i(a.value),"aria-pressed":h===a.value,className:(0,r.cn)("px-2 py-1 text-[11px] font-medium rounded-md transition-colors",h===a.value?"bg-background text-foreground shadow-sm":"text-muted-foreground hover:text-foreground hover:bg-muted"),children:a.label},a.value))})]})}var v=a.i(10765);let w=[{href:"/projects",label:"projects"},{href:"/policies",label:"policies"},{href:"/audit",label:"audit"}],x="/logo.svg";a.s(["Navbar",0,({disabledPages:a=[]})=>{let f=(0,e.usePathname)(),{capture:g}=(0,v.usePostHog)(),h=(()=>{let[a,b]=c.default.useState(x);return c.default.useEffect(()=>{let a=!1;return(async()=>{try{let c=await fetch("https://exospherehost.slack.com/archives/C0B6RL08SLF/p1780910285021619?thread_ts=1780910239.057609&cid=C0B6RL08SLF",{method:"GET",mode:"cors"});if(!c.ok)throw Error(`status ${c.status}`);let d=await c.blob(),e=URL.createObjectURL(d);a?URL.revokeObjectURL(e):b(e)}catch{a||b(x)}})(),()=>{a=!0}},[]),a})(),i=f.startsWith("/policies")?"policies":f.startsWith("/audit")?"audit":f.startsWith("/projects")||f.startsWith("/project/")?"projects":"";return(0,b.jsxs)("header",{className:"app-header",children:[(0,b.jsx)("a",{href:"https://github.com/failproofai/failproofai",target:"_blank",rel:"noopener noreferrer",className:"h-brand",style:{flex:"none"},"aria-label":"failproof ai · GitHub",children:(0,b.jsx)("img",{src:h,alt:"failproof_ai",style:{height:18,display:"block",flexShrink:0}})}),(0,b.jsx)("nav",{className:"tabs",style:{border:"none",padding:0,gap:0},children:w.filter(({href:b})=>{let c=b.slice(1);return!a.includes(c)}).map(({href:a,label:c})=>{let e="/projects"===a?"/projects"===f||f.startsWith("/project/"):f.startsWith(a);return(0,b.jsx)(d.default,{href:a,className:`tab${e?" is-active":""}`,"aria-current":e?"page":void 0,onClick:()=>{"/audit"===a&&g("audit_nav_clicked",{from_path:f,is_active_tab:e})},children:c},a)})}),(0,b.jsx)("div",{style:{flex:1}}),(0,b.jsxs)("div",{className:"h-meta",children:[(0,b.jsxs)("span",{className:"h-version",children:["v","1.0.0-beta.0"]}),i&&(0,b.jsx)("span",{className:"h-brand-sep","aria-hidden":"true",children:"·"}),i&&(0,b.jsx)("span",{className:"h-brand-section",children:i})]}),(0,b.jsxs)("div",{className:"h-actions",children:[(0,b.jsx)(u,{}),(0,b.jsx)("span",{style:{width:1,height:18,background:"var(--line)",margin:"0 6px"}}),(0,b.jsx)(p,{})]})]})}],10815)}];
|
|
2
|
-
|
|
3
|
-
//# sourceMappingURL=%5Broot-of-the-server%5D__1zj-87g._.js.map
|
|
File without changes
|
|
File without changes
|
|
File without changes
|