failproofai 1.0.0-beta.0 → 1.0.0-beta.1
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]__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]__1ig795d._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0259h38._.js → [root-of-the-server]__1yo1gyz._.js} +2 -2
- 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_14q3-7b.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 → 07ix3lb4gib0x.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0u9hc6oxx-gpj.js → 0fdx7ikab08-g.js} +1 -1
- package/.next/standalone/.next/static/chunks/{29k6ef49zppxj.js → 191njgoik03fm.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1grd7y94-igz9.js → 1b3a1scouf40o.js} +1 -1
- package/.next/standalone/.next/static/chunks/{3wrgbih-516uj.js → 1d71o1_tav721.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1d6ncspjagm53.js → 24gs8ip6_spcb.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2vr7aazw6k2-n.js → 2ovmv4esqalpb.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0ip1n8xmgxarv.js → 30is-6r00sbd7.js} +1 -1
- package/.next/standalone/.next/static/chunks/{29ydr-8q0zis6.js → 35584kn409ad1.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 +135 -35
- package/dist/worker.mjs +1 -1
- package/package.json +1 -1
- package/src/hooks/configure-wizard.ts +4 -0
- package/src/hooks/daemon-service.ts +259 -58
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1zj-87g._.js +0 -3
- /package/.next/standalone/.next/static/{7Lxtd352rSaHB-pmm6G8M → avJMC2AXvrg13Pbm4QH8F}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{7Lxtd352rSaHB-pmm6G8M → avJMC2AXvrg13Pbm4QH8F}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{7Lxtd352rSaHB-pmm6G8M → avJMC2AXvrg13Pbm4QH8F}/_ssgManifest.js +0 -0
package/dist/worker.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "failproofai",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.1",
|
|
4
4
|
"description": "The easiest way to manage policies that keep your AI agents reliable, on-task, and running autonomously — for Claude Code & the Agents SDK",
|
|
5
5
|
"bin": {
|
|
6
6
|
"failproofai": "./dist/cli.mjs",
|
|
@@ -289,6 +289,10 @@ export function classifyDaemonInstallFailure(reason: string | undefined): string
|
|
|
289
289
|
// signal, "disabled" is a deliberate air-gapped opt-out, and a plain fetch
|
|
290
290
|
// failure is usually a proxy or an offline box — three very different
|
|
291
291
|
// stories that would otherwise all land in `service_manager_error`.
|
|
292
|
+
// The service is installed system-wide now, so "couldn't become root" is a
|
|
293
|
+
// first-class outcome with a specific remedy (re-run under sudo) rather
|
|
294
|
+
// than an opaque service_manager_error.
|
|
295
|
+
if (/root privileges are required/.test(reason)) return "needs_root";
|
|
292
296
|
if (/checksum mismatch|has no entry for/.test(reason)) return "checksum_mismatch";
|
|
293
297
|
if (/downloads are disabled/.test(reason)) return "downloads_disabled";
|
|
294
298
|
if (/failed to download/.test(reason)) return "download_failed";
|
|
@@ -8,8 +8,8 @@
|
|
|
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 { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "node:fs";
|
|
12
|
-
import { homedir } from "node:os";
|
|
11
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync, rmSync } from "node:fs";
|
|
12
|
+
import { homedir, tmpdir, userInfo } from "node:os";
|
|
13
13
|
import { resolve, dirname } from "node:path";
|
|
14
14
|
import { execFileSync } from "node:child_process";
|
|
15
15
|
import { hookLogWarn } from "./hook-logger";
|
|
@@ -168,22 +168,71 @@ export async function ensureFailproofaidBinary(): Promise<{ path?: string; reaso
|
|
|
168
168
|
* here, once, at install time, and handing it to the daemon via the
|
|
169
169
|
* environment closes that gap entirely.
|
|
170
170
|
*/
|
|
171
|
-
function resolveWorkerCommand(): string | null {
|
|
171
|
+
export function resolveWorkerCommand(): string | null {
|
|
172
172
|
if (process.env.FAILPROOFAI_WORKER_CMD) return process.env.FAILPROOFAI_WORKER_CMD;
|
|
173
173
|
|
|
174
174
|
const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT;
|
|
175
175
|
if (!packageRoot) return null;
|
|
176
176
|
const workerScript = resolve(packageRoot, "dist", "worker.mjs");
|
|
177
177
|
if (!existsSync(workerScript)) return null;
|
|
178
|
-
|
|
178
|
+
// `process.execPath`, not a bare `node`. A system-scope service does not
|
|
179
|
+
// inherit a login environment, so its PATH is the system default — and the
|
|
180
|
+
// single most common way to install Node is nvm, which puts it under
|
|
181
|
+
// ~/.nvm/versions/node/*/bin and nowhere on that PATH. A bare `node` would
|
|
182
|
+
// resolve fine when the wizard runs it and then fail inside the service,
|
|
183
|
+
// silently, on exactly the machines least likely to notice. execPath is
|
|
184
|
+
// whatever runtime is executing this CLI right now (node for the published
|
|
185
|
+
// bin, bun in a source checkout), absolute either way.
|
|
186
|
+
return `${process.execPath} ${workerScript}`;
|
|
179
187
|
}
|
|
180
188
|
|
|
181
|
-
|
|
189
|
+
/**
|
|
190
|
+
* The account the daemon runs as. The service is root-*installed* but never
|
|
191
|
+
* root-*run*: everything it touches (the socket, the lock, the policy config)
|
|
192
|
+
* lives in one user's home and is peer-checked against that user's uid.
|
|
193
|
+
*/
|
|
194
|
+
function serviceUser(): string {
|
|
195
|
+
return userInfo().username;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* `/etc/systemd/system/failproofaid@<user>.service`.
|
|
200
|
+
*
|
|
201
|
+
* The `@<user>` suffix is systemd's convention for a per-user instance, but
|
|
202
|
+
* this is a concrete unit file rather than an instance of a template: every
|
|
203
|
+
* field that matters is user-specific (the ExecStart path is under the user's
|
|
204
|
+
* own ~/.failproofai/bin, so is HOME, so is the worker command), so a shared
|
|
205
|
+
* template would need a per-instance drop-in for all of them and buy nothing.
|
|
206
|
+
* Naming it per-user is what keeps a second user's install from silently
|
|
207
|
+
* stealing the first's unit — a single `failproofaid.service` would.
|
|
208
|
+
*/
|
|
209
|
+
function systemdUnitName(user: string = serviceUser()): string {
|
|
210
|
+
return `failproofaid@${user}.service`;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function systemdUnitPath(user: string = serviceUser()): string {
|
|
214
|
+
return resolve("/etc/systemd/system", systemdUnitName(user));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* The pre-1.0.0-beta.1 user-scope unit. Still removed on install and
|
|
219
|
+
* uninstall: it holds the same flock the new service needs, so leaving one
|
|
220
|
+
* behind means the system unit starts, loses the singleton race, and the
|
|
221
|
+
* machine sits fail-closed against a daemon that never came up.
|
|
222
|
+
*/
|
|
223
|
+
function legacySystemdUserUnitPath(): string {
|
|
182
224
|
return resolve(homedir(), ".config", "systemd", "user", "failproofaid.service");
|
|
183
225
|
}
|
|
184
226
|
|
|
227
|
+
const LAUNCHD_LABEL = "ai.failproof.failproofaid";
|
|
228
|
+
|
|
185
229
|
function launchdPlistPath(): string {
|
|
186
|
-
return
|
|
230
|
+
return `/Library/LaunchDaemons/${LAUNCHD_LABEL}.plist`;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/** The pre-1.0.0-beta.1 LaunchAgent — same migration problem as above. */
|
|
234
|
+
function legacyLaunchAgentPlistPath(): string {
|
|
235
|
+
return resolve(homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
|
|
187
236
|
}
|
|
188
237
|
|
|
189
238
|
/**
|
|
@@ -197,23 +246,129 @@ export function daemonServiceFilePath(): string | null {
|
|
|
197
246
|
return process.platform === "linux" ? systemdUnitPath() : launchdPlistPath();
|
|
198
247
|
}
|
|
199
248
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
249
|
+
/**
|
|
250
|
+
* The command a user runs to inspect their own daemon — surfaced so the
|
|
251
|
+
* wizard can print it instead of leaving people to discover which service
|
|
252
|
+
* manager, and which scope, is involved.
|
|
253
|
+
*/
|
|
254
|
+
export function daemonStatusCommand(): string | null {
|
|
255
|
+
if (!isDaemonSupportedPlatform()) return null;
|
|
256
|
+
return process.platform === "linux"
|
|
257
|
+
? `systemctl status ${systemdUnitName()}`
|
|
258
|
+
: `sudo launchctl print system/${LAUNCHD_LABEL}`;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/** True when privileged commands can run without prompting for a password. */
|
|
262
|
+
function canElevate(): boolean {
|
|
263
|
+
if (typeof process.getuid === "function" && process.getuid() === 0) return true;
|
|
264
|
+
try {
|
|
265
|
+
execFileSync("sudo", ["-n", "true"], { stdio: "ignore", timeout: SERVICE_CMD_TIMEOUT_MS });
|
|
266
|
+
return true;
|
|
267
|
+
} catch {
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Runs one privileged command, non-interactively.
|
|
274
|
+
*
|
|
275
|
+
* `sudo -n` on purpose: the wizard owns the terminal, and a sudo password
|
|
276
|
+
* prompt fired from underneath a TUI is unreadable at best. A machine that
|
|
277
|
+
* cannot elevate silently gets a clear reason and the manual commands
|
|
278
|
+
* instead — it keeps working exactly as it did, on the in-process path.
|
|
279
|
+
*/
|
|
280
|
+
function runPrivileged(command: string, args: string[]): void {
|
|
281
|
+
const root = typeof process.getuid === "function" && process.getuid() === 0;
|
|
282
|
+
const [cmd, argv] = root ? [command, args] : ["sudo", ["-n", command, ...args]];
|
|
283
|
+
execFileSync(cmd, argv, { stdio: "ignore", timeout: SERVICE_CMD_TIMEOUT_MS });
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Installs a file into a root-owned location via a temp file, because the
|
|
288
|
+
* caller is not root and cannot write there directly. `install -m` sets the
|
|
289
|
+
* mode in the same step, so the file is never briefly world-writable.
|
|
290
|
+
*/
|
|
291
|
+
function writePrivilegedFile(destination: string, contents: string, mode = "0644"): void {
|
|
292
|
+
const staging = resolve(tmpdir(), `failproofaid-${process.pid}-${Date.now()}.tmp`);
|
|
293
|
+
try {
|
|
294
|
+
writeFileSync(staging, contents, "utf8");
|
|
295
|
+
runPrivileged("install", ["-m", mode, staging, destination]);
|
|
296
|
+
} finally {
|
|
297
|
+
rmSync(staging, { force: true });
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Removes a daemon installed by an earlier version into the user's own
|
|
303
|
+
* session scope. Best-effort and never privileged — these paths are all
|
|
304
|
+
* inside the user's home.
|
|
305
|
+
*/
|
|
306
|
+
function removeLegacyUserService(): void {
|
|
307
|
+
try {
|
|
308
|
+
if (process.platform === "linux") {
|
|
309
|
+
const legacy = legacySystemdUserUnitPath();
|
|
310
|
+
if (!existsSync(legacy)) return;
|
|
311
|
+
try {
|
|
312
|
+
execFileSync("systemctl", ["--user", "disable", "--now", "failproofaid.service"], {
|
|
313
|
+
stdio: "ignore",
|
|
314
|
+
timeout: SERVICE_CMD_TIMEOUT_MS,
|
|
315
|
+
});
|
|
316
|
+
} catch {
|
|
317
|
+
// Not loaded — removing the file is still the point.
|
|
318
|
+
}
|
|
319
|
+
unlinkSync(legacy);
|
|
320
|
+
try {
|
|
321
|
+
execFileSync("systemctl", ["--user", "daemon-reload"], {
|
|
322
|
+
stdio: "ignore",
|
|
323
|
+
timeout: SERVICE_CMD_TIMEOUT_MS,
|
|
324
|
+
});
|
|
325
|
+
} catch {
|
|
326
|
+
/* best-effort */
|
|
327
|
+
}
|
|
328
|
+
} else {
|
|
329
|
+
const legacy = legacyLaunchAgentPlistPath();
|
|
330
|
+
if (!existsSync(legacy)) return;
|
|
331
|
+
try {
|
|
332
|
+
execFileSync("launchctl", ["unload", "-w", legacy], {
|
|
333
|
+
stdio: "ignore",
|
|
334
|
+
timeout: SERVICE_CMD_TIMEOUT_MS,
|
|
335
|
+
});
|
|
336
|
+
} catch {
|
|
337
|
+
/* not loaded */
|
|
338
|
+
}
|
|
339
|
+
unlinkSync(legacy);
|
|
340
|
+
}
|
|
341
|
+
} catch (err) {
|
|
342
|
+
hookLogWarn(`could not remove the legacy user-scope daemon: ${err instanceof Error ? err.message : String(err)}`);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
export function systemdUnitContents(binaryPath: string, workerCmd: string | null): string {
|
|
347
|
+
// Quoted because both values contain a space or a path — systemd's
|
|
348
|
+
// Environment= requires quoting whenever the value does.
|
|
204
349
|
const envLine = workerCmd ? `Environment="FAILPROOFAI_WORKER_CMD=${workerCmd}"\n` : "";
|
|
350
|
+
const user = serviceUser();
|
|
205
351
|
return `[Unit]
|
|
206
|
-
Description=failproofai background daemon (failproofaid)
|
|
352
|
+
Description=failproofai background daemon (failproofaid) for ${user}
|
|
207
353
|
After=network.target
|
|
208
354
|
|
|
209
355
|
[Service]
|
|
210
356
|
Type=simple
|
|
357
|
+
User=${user}
|
|
358
|
+
# Set explicitly rather than relying on systemd deriving it from User=:
|
|
359
|
+
# failproofaid is user-scope by construction and refuses to start without
|
|
360
|
+
# HOME ("HOME is not set; failproofaid is user-scope only"), so the one
|
|
361
|
+
# variable it cannot do without is not left to a version-dependent default.
|
|
362
|
+
Environment="HOME=${homedir()}"
|
|
211
363
|
${envLine}ExecStart=${binaryPath}
|
|
212
364
|
Restart=on-failure
|
|
213
365
|
RestartSec=2
|
|
214
366
|
|
|
215
367
|
[Install]
|
|
216
|
-
|
|
368
|
+
# multi-user.target, not default.target: this is the whole point of a
|
|
369
|
+
# system unit — it starts at boot, with no login and no lingering, and
|
|
370
|
+
# keeps running after the installing user logs out.
|
|
371
|
+
WantedBy=multi-user.target
|
|
217
372
|
`;
|
|
218
373
|
}
|
|
219
374
|
|
|
@@ -235,7 +390,7 @@ function launchdPlistContents(binaryPath: string, logDir: string, workerCmd: str
|
|
|
235
390
|
<plist version="1.0">
|
|
236
391
|
<dict>
|
|
237
392
|
<key>Label</key>
|
|
238
|
-
<string
|
|
393
|
+
<string>${LAUNCHD_LABEL}</string>
|
|
239
394
|
<key>ProgramArguments</key>
|
|
240
395
|
<array>
|
|
241
396
|
<string>${escapeXml(binaryPath)}</string>
|
|
@@ -244,6 +399,12 @@ function launchdPlistContents(binaryPath: string, logDir: string, workerCmd: str
|
|
|
244
399
|
<true/>
|
|
245
400
|
<key>KeepAlive</key>
|
|
246
401
|
<true/>
|
|
402
|
+
<!-- A LaunchDaemon runs as root unless told otherwise. This is the
|
|
403
|
+
launchd half of the systemd unit's User=: loaded at boot by the
|
|
404
|
+
system, executed as the one user whose home, socket and lock it
|
|
405
|
+
is allowed to touch. -->
|
|
406
|
+
<key>UserName</key>
|
|
407
|
+
<string>${escapeXml(serviceUser())}</string>
|
|
247
408
|
${envBlock} <key>StandardOutPath</key>
|
|
248
409
|
<string>${escapeXml(resolve(logDir, "failproofaid.log"))}</string>
|
|
249
410
|
<key>StandardErrorPath</key>
|
|
@@ -258,6 +419,25 @@ export interface DaemonInstallResult {
|
|
|
258
419
|
reason?: string;
|
|
259
420
|
}
|
|
260
421
|
|
|
422
|
+
/**
|
|
423
|
+
* The privileged commands an install performs — returned verbatim so a
|
|
424
|
+
* machine that cannot elevate can be told exactly what to run rather than
|
|
425
|
+
* just that something failed.
|
|
426
|
+
*/
|
|
427
|
+
function daemonInstallCommands(binaryPath: string, workerCmd: string | null): string[] {
|
|
428
|
+
if (process.platform === "linux") {
|
|
429
|
+
return [
|
|
430
|
+
`sudo tee ${systemdUnitPath()} <<'EOF'\n${systemdUnitContents(binaryPath, workerCmd)}EOF`,
|
|
431
|
+
"sudo systemctl daemon-reload",
|
|
432
|
+
`sudo systemctl enable --now ${systemdUnitName()}`,
|
|
433
|
+
];
|
|
434
|
+
}
|
|
435
|
+
return [
|
|
436
|
+
`sudo tee ${launchdPlistPath()} < the plist failproofai config would write`,
|
|
437
|
+
`sudo launchctl load -w ${launchdPlistPath()}`,
|
|
438
|
+
];
|
|
439
|
+
}
|
|
440
|
+
|
|
261
441
|
/**
|
|
262
442
|
* Writes and enables the service unit, starting it immediately. Safe to
|
|
263
443
|
* call repeatedly — re-running replaces the unit file (picking up a
|
|
@@ -284,40 +464,43 @@ export async function installDaemonService(): Promise<DaemonInstallResult> {
|
|
|
284
464
|
// worker regardless of what cwd the service manager starts it from.
|
|
285
465
|
const workerCmd = resolveWorkerCommand();
|
|
286
466
|
|
|
467
|
+
// The service is installed system-wide, which needs root. Check before
|
|
468
|
+
// writing anything, so a machine that cannot elevate gets the exact
|
|
469
|
+
// commands to run instead of a half-installed service.
|
|
470
|
+
if (!canElevate()) {
|
|
471
|
+
return {
|
|
472
|
+
installed: false,
|
|
473
|
+
reason:
|
|
474
|
+
"root privileges are required to install the failproofaid system service, and sudo is not available without a password. " +
|
|
475
|
+
`Run: sudo failproofai config (or install manually: ${daemonInstallCommands(binaryPath, workerCmd).join(" && ")})`,
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// A daemon left over from a pre-1.0.0-beta.1 install holds the same
|
|
480
|
+
// singleton lock the new one needs, so the system unit would start, lose
|
|
481
|
+
// the flock race, and leave the machine fail-closed against a daemon that
|
|
482
|
+
// never came up. Clear it first, every time.
|
|
483
|
+
removeLegacyUserService();
|
|
484
|
+
|
|
287
485
|
try {
|
|
288
486
|
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
|
-
});
|
|
487
|
+
writePrivilegedFile(systemdUnitPath(), systemdUnitContents(binaryPath, workerCmd));
|
|
488
|
+
runPrivileged("systemctl", ["daemon-reload"]);
|
|
489
|
+
runPrivileged("systemctl", ["enable", "--now", systemdUnitName()]);
|
|
300
490
|
} else {
|
|
301
491
|
const plistPath = launchdPlistPath();
|
|
302
492
|
const logDir = resolve(homedir(), ".failproofai", "logs");
|
|
303
|
-
mkdirSync(dirname(plistPath), { recursive: true });
|
|
304
493
|
mkdirSync(logDir, { recursive: true });
|
|
305
|
-
writeFileSync(plistPath, launchdPlistContents(binaryPath, logDir, workerCmd), "utf8");
|
|
306
494
|
// Unload any previously-loaded copy first — reloading with a changed
|
|
307
495
|
// binary path (e.g. after an upgrade) is a no-op under plain `load`
|
|
308
496
|
// if launchd thinks the label is already loaded.
|
|
309
497
|
try {
|
|
310
|
-
|
|
311
|
-
stdio: "ignore",
|
|
312
|
-
timeout: SERVICE_CMD_TIMEOUT_MS,
|
|
313
|
-
});
|
|
498
|
+
runPrivileged("launchctl", ["unload", plistPath]);
|
|
314
499
|
} catch {
|
|
315
500
|
// Wasn't loaded — fine, this is the common case on a fresh install.
|
|
316
501
|
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
timeout: SERVICE_CMD_TIMEOUT_MS,
|
|
320
|
-
});
|
|
502
|
+
writePrivilegedFile(plistPath, launchdPlistContents(binaryPath, logDir, workerCmd));
|
|
503
|
+
runPrivileged("launchctl", ["load", "-w", plistPath]);
|
|
321
504
|
}
|
|
322
505
|
} catch (err) {
|
|
323
506
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -370,40 +553,44 @@ async function waitForDaemonRunning(): Promise<boolean> {
|
|
|
370
553
|
export async function uninstallDaemonService(): Promise<void> {
|
|
371
554
|
setDaemonConfigured(false);
|
|
372
555
|
if (!isDaemonSupportedPlatform()) return;
|
|
556
|
+
|
|
557
|
+
// Always attempted, and never privileged: a legacy user-scope daemon is
|
|
558
|
+
// the one thing this can still clean up on a machine that cannot elevate.
|
|
559
|
+
removeLegacyUserService();
|
|
560
|
+
|
|
373
561
|
try {
|
|
374
562
|
if (process.platform === "linux") {
|
|
563
|
+
const unitPath = systemdUnitPath();
|
|
564
|
+
if (!existsSync(unitPath)) return;
|
|
375
565
|
try {
|
|
376
|
-
|
|
377
|
-
stdio: "ignore",
|
|
378
|
-
timeout: SERVICE_CMD_TIMEOUT_MS,
|
|
379
|
-
});
|
|
566
|
+
runPrivileged("systemctl", ["disable", "--now", systemdUnitName()]);
|
|
380
567
|
} catch {
|
|
381
|
-
// Already stopped/not
|
|
568
|
+
// Already stopped/not enabled — removing the unit is still the point.
|
|
382
569
|
}
|
|
383
|
-
|
|
384
|
-
if (existsSync(unitPath)) unlinkSync(unitPath);
|
|
570
|
+
runPrivileged("rm", ["-f", unitPath]);
|
|
385
571
|
try {
|
|
386
|
-
|
|
387
|
-
stdio: "ignore",
|
|
388
|
-
timeout: SERVICE_CMD_TIMEOUT_MS,
|
|
389
|
-
});
|
|
572
|
+
runPrivileged("systemctl", ["daemon-reload"]);
|
|
390
573
|
} catch {
|
|
391
574
|
// Best-effort.
|
|
392
575
|
}
|
|
393
576
|
} else {
|
|
394
577
|
const plistPath = launchdPlistPath();
|
|
578
|
+
if (!existsSync(plistPath)) return;
|
|
395
579
|
try {
|
|
396
|
-
|
|
397
|
-
stdio: "ignore",
|
|
398
|
-
timeout: SERVICE_CMD_TIMEOUT_MS,
|
|
399
|
-
});
|
|
580
|
+
runPrivileged("launchctl", ["unload", "-w", plistPath]);
|
|
400
581
|
} catch {
|
|
401
|
-
// Already unloaded
|
|
582
|
+
// Already unloaded — fine.
|
|
402
583
|
}
|
|
403
|
-
|
|
584
|
+
runPrivileged("rm", ["-f", plistPath]);
|
|
404
585
|
}
|
|
405
586
|
} catch (err) {
|
|
406
|
-
|
|
587
|
+
// `daemonConfigured` is already cleared above, so a machine that cannot
|
|
588
|
+
// elevate is back on the in-process path even though the unit file
|
|
589
|
+
// survives — it fails open, not closed.
|
|
590
|
+
hookLogWarn(
|
|
591
|
+
`daemon service uninstall failed (the service may need removing by hand: ${daemonStatusCommand()}): ` +
|
|
592
|
+
`${err instanceof Error ? err.message : String(err)}`,
|
|
593
|
+
);
|
|
407
594
|
}
|
|
408
595
|
}
|
|
409
596
|
|
|
@@ -418,7 +605,9 @@ export function daemonServiceStatus(): DaemonServiceStatus {
|
|
|
418
605
|
if (process.platform === "linux") {
|
|
419
606
|
if (!existsSync(systemdUnitPath())) return "not-installed";
|
|
420
607
|
try {
|
|
421
|
-
|
|
608
|
+
// Unprivileged on purpose: reading a system unit's state needs no
|
|
609
|
+
// root, so status works for the owning user with no sudo at all.
|
|
610
|
+
const out = execFileSync("systemctl", ["is-active", systemdUnitName()], {
|
|
422
611
|
stdio: ["ignore", "pipe", "ignore"],
|
|
423
612
|
timeout: SERVICE_CMD_TIMEOUT_MS,
|
|
424
613
|
})
|
|
@@ -438,11 +627,23 @@ export function daemonServiceStatus(): DaemonServiceStatus {
|
|
|
438
627
|
const plistPath = launchdPlistPath();
|
|
439
628
|
if (!existsSync(plistPath)) return "not-installed";
|
|
440
629
|
try {
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
630
|
+
// A LaunchDaemon lives in launchd's system domain, which an unprivileged
|
|
631
|
+
// `launchctl list` cannot see — unlike systemd, reading the state needs
|
|
632
|
+
// the same elevation installing it did.
|
|
633
|
+
const root = typeof process.getuid === "function" && process.getuid() === 0;
|
|
634
|
+
const args = ["print", `system/${LAUNCHD_LABEL}`];
|
|
635
|
+
const out = root
|
|
636
|
+
? execFileSync("launchctl", args, {
|
|
637
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
638
|
+
timeout: SERVICE_CMD_TIMEOUT_MS,
|
|
639
|
+
}).toString()
|
|
640
|
+
: execFileSync("sudo", ["-n", "launchctl", ...args], {
|
|
641
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
642
|
+
timeout: SERVICE_CMD_TIMEOUT_MS,
|
|
643
|
+
}).toString();
|
|
644
|
+
// `state = running` is launchd's own wording; a loaded-but-dead job
|
|
645
|
+
// prints `state = not running` and must not read as healthy.
|
|
646
|
+
return /state\s*=\s*running/.test(out) ? "running" : "stopped";
|
|
446
647
|
} catch {
|
|
447
648
|
return "stopped";
|
|
448
649
|
}
|
|
@@ -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
|